Merge branch 'main' into tasks
# Conflicts: # src/main/java/schule/ngb/zm/tasks/DelayedTask.java # src/main/java/schule/ngb/zm/tasks/FrameSynchronizedTask.java # src/main/java/schule/ngb/zm/tasks/FramerateLimitedTask.java # src/main/java/schule/ngb/zm/tasks/RateLimitedTask.java # src/main/java/schule/ngb/zm/tasks/Task.java
16
.gitignore
vendored
@@ -32,3 +32,19 @@ hs_err_pid*
|
||||
.DS_Store
|
||||
._*
|
||||
Thumbs.db
|
||||
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Avoid ignore Gradle wrappper properties
|
||||
!gradle-wrapper.properties
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
46
build.gradle
Normal file
@@ -0,0 +1,46 @@
|
||||
plugins {
|
||||
id 'idea'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
/*properties {
|
||||
zmVersion {
|
||||
major = 0;
|
||||
minor = 0;
|
||||
rev = 21;
|
||||
}
|
||||
}*/
|
||||
|
||||
group 'schule.ngb'
|
||||
version '0.0.21-SNAPSHOT'
|
||||
//version '{$zmVersion.major}.{$zmVersion.minor}.{$zmVersion.rev}-SNAPSHOT'
|
||||
|
||||
compileJava {
|
||||
options.release = 11
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
runtimeOnly 'com.googlecode.soundlibs:jlayer:1.0.1.4'
|
||||
runtimeOnly 'com.googlecode.soundlibs:tritonus-share:0.3.7.4'
|
||||
runtimeOnly 'com.googlecode.soundlibs:mp3spi:1.9.5.4'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.register('jarMP3SPI', Jar) {
|
||||
archiveClassifier = 'all'
|
||||
duplicatesStrategy = 'exclude'
|
||||
archivesBaseName = 'zeichenmaschine-mp3spi'
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
2
settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'zeichenmaschine'
|
||||
|
||||
@@ -3,12 +3,12 @@ package schule.ngb.zm;
|
||||
/**
|
||||
* Repräsentiert eine Farbe in der Zeichenmaschine.
|
||||
* <p>
|
||||
* Farben bestehen entweder aus einem Grauwert (zwischen <code>0</code> und
|
||||
* <code>255</code>) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
|
||||
* <code>0</code> und <code>255</code>).
|
||||
* Farben bestehen entweder aus einem Grauwert (zwischen 0 und
|
||||
* 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
|
||||
* 0 und 255).
|
||||
* <p>
|
||||
* Eine Farbe hat außerdem einen Transparenzwert zwischen <code>0</code>
|
||||
* (unsichtbar) und <code>255</code> (deckend).
|
||||
* Eine Farbe hat außerdem einen Transparenzwert zwischen 0
|
||||
* (unsichtbar) und 255 (deckend).
|
||||
*/
|
||||
public class Color {
|
||||
|
||||
@@ -106,7 +106,7 @@ public class Color {
|
||||
/**
|
||||
* Erstellt eine graue Farbe entsprechend des Grauwertes <var>gray</var>.
|
||||
*
|
||||
* @param gray Ein Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int gray ) {
|
||||
this(gray, gray, gray, 255);
|
||||
@@ -116,7 +116,7 @@ public class Color {
|
||||
* Erstellt eine graue Farbe entsprechend des Grauwertes <var>gray</var> und
|
||||
* des Transparentwertes <var>alpha</var>.
|
||||
*
|
||||
* @param gray Ein Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int gray, int alpha ) {
|
||||
this(gray, gray, gray, alpha);
|
||||
@@ -125,12 +125,11 @@ public class Color {
|
||||
/**
|
||||
* Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und
|
||||
* <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
|
||||
* liegen zwischen <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int red, int green, int blue ) {
|
||||
this(red, green, blue, 255);
|
||||
@@ -139,17 +138,16 @@ public class Color {
|
||||
/**
|
||||
* Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und
|
||||
* <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
|
||||
* liegen zwischen <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
* <var>alpha</var> gibt den den Transparentwert an (auch zwischen
|
||||
* code>0</code> und <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* 0 und 255), wobei
|
||||
* 0 komplett durchsichtig ist und 255 komplett
|
||||
* deckend.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparentwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
* @param alpha Transparentwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int red, int green, int blue, int alpha ) {
|
||||
rgba = (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
@@ -239,7 +237,7 @@ public class Color {
|
||||
/**
|
||||
* Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann
|
||||
* sechs- oder achtstellig sein (wenn ein Transparentwert vorhanden ist).
|
||||
* Dem Code kann ein <code>#</code> Zeichen vorangestellt sein.
|
||||
* Dem Code kann ein {@code #} Zeichen vorangestellt sein.
|
||||
*
|
||||
* @param hexcode
|
||||
* @return
|
||||
@@ -387,10 +387,10 @@ public class Constants {
|
||||
|
||||
/**
|
||||
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
|
||||
* zwischen <code>0</code> und <code>255</code> an, wobei
|
||||
* <code>0</code> schwarz und <code>255</code> weiß ist.
|
||||
* zwischen 0 und 255 an, wobei
|
||||
* 0 schwarz und 255 weiß ist.
|
||||
*
|
||||
* @param gray Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Grauwert zwischen 0 und 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int gray ) {
|
||||
@@ -399,16 +399,16 @@ public class Constants {
|
||||
|
||||
/**
|
||||
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
|
||||
* zwischen <code>0</code> und <code>255</code> an, wobei
|
||||
* <code>0</code> schwarz und <code>255</code> weiß ist.
|
||||
* zwischen 0 und 255 an, wobei
|
||||
* 0 schwarz und 255 weiß ist.
|
||||
* {@code alpha} gibt den den Transparentwert an (auch zwischen
|
||||
* <code>0</code> und <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* 0 und 255), wobei
|
||||
* 0 komplett durchsichtig ist und 255 komplett
|
||||
* deckend.
|
||||
*
|
||||
* @param gray Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparentwert zwischen <code>0</code> und
|
||||
* <code>255</code>.
|
||||
* @param gray Grauwert zwischen 0 und 255.
|
||||
* @param alpha Transparentwert zwischen 0 und
|
||||
* 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int gray, int alpha ) {
|
||||
@@ -418,11 +418,11 @@ public class Constants {
|
||||
/**
|
||||
* Erstellt eine Farbe. Die Parameter {@code red}, {@code green} und
|
||||
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
|
||||
* liegen zwischen <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int red, int green, int blue ) {
|
||||
@@ -432,17 +432,17 @@ public class Constants {
|
||||
/**
|
||||
* Erstellt eine Farbe. Die Parameter {@code red}, {@code green} und
|
||||
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
|
||||
* liegen zwischen <code>0</code> und <code>255</code>. {@code alpha} gibt
|
||||
* den den Transparentwert an (auch zwischen code>0</code> und
|
||||
* <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* liegen zwischen 0 und 255. {@code alpha} gibt
|
||||
* den den Transparentwert an (auch zwischen 0 und
|
||||
* 255), wobei
|
||||
* 0 komplett durchsichtig ist und 255 komplett
|
||||
* deckend.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparenzwert zwischen <code>0</code> und
|
||||
* <code>255</code>.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
* @param alpha Transparenzwert zwischen 0 und
|
||||
* 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int red, int green, int blue, int alpha ) {
|
||||
@@ -589,7 +589,7 @@ public class Constants {
|
||||
* Ermittelt das Vorzeichen der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>-1</code>, <code>1</code> oder <code>0</code>.
|
||||
* @return -1, 1 oder 0.
|
||||
*/
|
||||
public static final double sign( double x ) {
|
||||
return Math.signum(x);
|
||||
@@ -670,7 +670,7 @@ public class Constants {
|
||||
* Ermittelt den Sinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>sin(x)</code>.
|
||||
* @return {@code sin(x)}.
|
||||
*/
|
||||
public static final double sin( double x ) {
|
||||
return Math.sin(x);
|
||||
@@ -680,7 +680,7 @@ public class Constants {
|
||||
* Ermittelt den Kosinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>cos(x)</code>.
|
||||
* @return {@code cos(x)}.
|
||||
*/
|
||||
public static final double cos( double x ) {
|
||||
return Math.cos(x);
|
||||
@@ -690,7 +690,7 @@ public class Constants {
|
||||
* Ermittelt den Tangens der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>tan(x)</code>.
|
||||
* @return {@code tan(x)}.
|
||||
*/
|
||||
public static final double tan( double x ) {
|
||||
return Math.tan(x);
|
||||
@@ -700,7 +700,7 @@ public class Constants {
|
||||
* Ermittelt den Arkussinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>asin(x)</code>.
|
||||
* @return {@code asin(x)}.
|
||||
*/
|
||||
public static final double arcsin( double x ) {
|
||||
return Math.asin(x);
|
||||
@@ -710,7 +710,7 @@ public class Constants {
|
||||
* Ermittelt den Arkuskosinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>acos(x)</code>.
|
||||
* @return {@code acos(x)}.
|
||||
*/
|
||||
public static final double arccos( double x ) {
|
||||
return Math.acos(x);
|
||||
@@ -720,20 +720,20 @@ public class Constants {
|
||||
* Ermittelt den Arkusktangens der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>atan(x)</code>.
|
||||
* @return {@code atan(x)}.
|
||||
*/
|
||||
public static final double arctan( double x ) {
|
||||
return Math.atan(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall <code>[min, max]</code>.
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
|
||||
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
|
||||
* zurückgegeben.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @param max Das Maximum des Intervalls.
|
||||
* @return Eine Zahl im Intervall <code>[min, max]</code>.
|
||||
* @return Eine Zahl im Intervall {@code [min, max]}.
|
||||
*/
|
||||
public static final double limit( double x, double min, double max ) {
|
||||
if( x > max ) {
|
||||
@@ -746,13 +746,13 @@ public class Constants {
|
||||
}
|
||||
|
||||
/**
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall <code>[min, max]</code>.
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
|
||||
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
|
||||
* zurückgegeben.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @param max Das Maximum des Intervalls.
|
||||
* @return Eine Zahl im Intervall <code>[min, max]</code>.
|
||||
* @return Eine Zahl im Intervall {@code [min, max]}.
|
||||
*/
|
||||
public static final int limit( int x, int min, int max ) {
|
||||
if( x > max ) {
|
||||
@@ -774,7 +774,7 @@ public class Constants {
|
||||
*
|
||||
* @param from Startwert
|
||||
* @param to Zielwert
|
||||
* @param t Wert zwischen <code>0</code> und <code>1</code>.
|
||||
* @param t Wert zwischen 0 und 1.
|
||||
* @return Das Ergebnis der linearen Interpolation.
|
||||
*/
|
||||
public static final double interpolate( double from, double to, double t ) {
|
||||
@@ -885,8 +885,8 @@ public class Constants {
|
||||
* Erzeugt einen zufälligen Wahrheitswert. {@code true} wird mit der
|
||||
* Wahrscheinlichkeit {@code percent} Prozent erzeugt.
|
||||
*
|
||||
* @param percent Eine Prozentzahl zwischen <code>0</code> und
|
||||
* <code>100</code>.
|
||||
* @param percent Eine Prozentzahl zwischen 0 und
|
||||
* 100.
|
||||
* @return Ein Wahrheitswert.
|
||||
*/
|
||||
public static final boolean randomBool( int percent ) {
|
||||
@@ -12,7 +12,7 @@ public interface Drawable {
|
||||
* Gibt an, ob das Objekt derzeit sichtbar ist (also gezeichnet werden
|
||||
* muss).
|
||||
*
|
||||
* @return <code>true</code>, wenn das Objekt sichtbar ist.
|
||||
* @return {@code true}, wenn das Objekt sichtbar ist.
|
||||
*/
|
||||
boolean isVisible();
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface Updatable {
|
||||
|
||||
/**
|
||||
* Gibt an, ob das Objekt gerade auf Aktualisierungen reagiert.
|
||||
* @return <code>true</code>, wenn das Objekt aktiv ist.
|
||||
* @return {@code true}, wenn das Objekt aktiv ist.
|
||||
*/
|
||||
public boolean isActive();
|
||||
|
||||
@@ -90,7 +90,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
* als letzte eingefügt. Die aufrufende Methode kann also nicht sicher sein,
|
||||
* dass die neue Ebene am Ende wirklich am Index {@code i} steht.
|
||||
*
|
||||
* @param i Index der Ebene, beginnend mit <code>0</code>.
|
||||
* @param i Index der Ebene, beginnend mit 0.
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public void addLayer( int i, Layer layer ) {
|
||||
@@ -125,10 +125,10 @@ public class Zeichenleinwand extends Canvas {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt die Ebene am Index <var>i</var> (beginnend bei <code>0</code>).
|
||||
* Holt die Ebene am Index <var>i</var> (beginnend bei 0).
|
||||
*
|
||||
* @param i Index der Ebene (beginnend bei <code>0</code>).
|
||||
* @return Die Ebene am Index <var>i</var> oder <code>null</code>.
|
||||
* @param i Index der Ebene (beginnend bei 0).
|
||||
* @return Die Ebene am Index <var>i</var> oder {@code null}.
|
||||
* @throws IndexOutOfBoundsException Falls der Index nicht existiert.
|
||||
*/
|
||||
public Layer getLayer( int i ) {
|
||||
@@ -141,7 +141,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
|
||||
/**
|
||||
* Sucht die erste Ebene des angegebenen Typs aus der Liste der Ebenen.
|
||||
* Existiert keine solche Ebene, wird <code>null</code> zurückgegeben.
|
||||
* Existiert keine solche Ebene, wird {@code null} zurückgegeben.
|
||||
*
|
||||
* @param clazz Typ der Ebene.
|
||||
* @param <L>
|
||||
@@ -531,6 +531,7 @@ public class Zeichenmaschine extends Constants {
|
||||
* vor {@code cleanup()} aufgerufen wird.
|
||||
*/
|
||||
private void cleanup() {
|
||||
LOG.debug("%s shutting down.", APP_NAME);
|
||||
// Alle noch nicht ausgelösten Events werden entfernt
|
||||
eventQueue.clear();
|
||||
// Alle noch nicht ausgeführten Tasks werden entfernt
|
||||
@@ -18,8 +18,8 @@ public interface Audio {
|
||||
* {@code isLooping() == true}, dann muss auch immer
|
||||
* {@code isPlaying() == true} gelten.
|
||||
*
|
||||
* @return @return {@code true}, wenn das Medium in einer Schleife
|
||||
* abgespielt wird, {@code false} sonst.
|
||||
* @return {@code true}, wenn das Medium in einer Schleife abgespielt wird,
|
||||
* {@code false} sonst.
|
||||
*/
|
||||
boolean isLooping();
|
||||
|
||||
@@ -36,17 +36,28 @@ public interface Audio {
|
||||
*/
|
||||
void setVolume( double volume );
|
||||
|
||||
/**
|
||||
* Gibt die aktuelle Lautstärkeeinstellung dieses Mediums zurück.
|
||||
* <p>
|
||||
* Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton
|
||||
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
|
||||
* Mediums.
|
||||
*
|
||||
* @return Die Lautstärke als linear skalierter Wert.
|
||||
*/
|
||||
double getVolume();
|
||||
|
||||
/**
|
||||
* Startet die Wiedergabe des Mediums und beendet die Methode. Das
|
||||
* Audio-Medium wird einmal abgespielt und stoppt dann.
|
||||
*/
|
||||
void playOnce();
|
||||
void play();
|
||||
|
||||
/**
|
||||
* Startet die Wiedergabe des Mediums und blockiert das Programm, bis die
|
||||
* Wiedergabe beendet ist.
|
||||
*/
|
||||
void playOnceAndWait();
|
||||
void playAndWait();
|
||||
|
||||
/**
|
||||
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
|
||||
@@ -50,30 +50,53 @@ public class Mixer implements Audio {
|
||||
pAudio.setVolume(pVolumeFactor * volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return audios.stream().anyMatch(aw -> aw.audio.isPlaying());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
return audios.stream().anyMatch(aw -> aw.audio.isLooping());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setVolume( double pVolume ) {
|
||||
volume = (float) pVolume;
|
||||
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * pVolume));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playOnce() {
|
||||
audios.stream().forEach(aw -> aw.audio.playOnce());
|
||||
public double getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playOnceAndWait() {
|
||||
audios.stream().forEach(aw -> aw.audio.playOnce());
|
||||
public void play() {
|
||||
audios.stream().forEach(aw -> aw.audio.play());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playAndWait() {
|
||||
audios.stream().forEach(aw -> aw.audio.play());
|
||||
while( audios.stream().anyMatch(aw -> aw.audio.isPlaying()) ) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
@@ -83,16 +106,25 @@ public class Mixer implements Audio {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void loop() {
|
||||
audios.stream().forEach(aw -> aw.audio.loop());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
audios.stream().forEach(aw -> aw.audio.stop());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if( isPlaying() ) {
|
||||
@@ -101,6 +133,18 @@ public class Mixer implements Audio {
|
||||
audios.stream().forEach(aw -> aw.audio.dispose());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ändert die Lautstärke aller hinzugefügten Audiomedien in der angegebenen
|
||||
* Zeit schrittweise, bis die angegebene Lautstärke erreicht ist.
|
||||
* <p>
|
||||
* Zu beachten ist, dass die Lautstärke des Mixers angepasst wird. Das
|
||||
* bedeutet, dass die Lautstärke der hinzugefügten Medien mit ihrem
|
||||
* Lautstärkefaktor multipliziert werden. Die Medien haben am Ende also
|
||||
* nicht unbedingt die Lautstärke {@code to}.
|
||||
*
|
||||
* @param to Der Zielwert für die Lautstärke.
|
||||
* @param time Die Zeit, nach der die Änderung abgeschlossen sein soll.
|
||||
*/
|
||||
public void fade( final double to, final int time ) {
|
||||
TaskRunner.run(new Runnable() {
|
||||
@Override
|
||||
@@ -108,9 +152,6 @@ public class Mixer implements Audio {
|
||||
final long start = System.currentTimeMillis();
|
||||
double t = 0.0;
|
||||
double from = volume;
|
||||
if( !isPlaying() ) {
|
||||
playOnce();
|
||||
}
|
||||
do {
|
||||
setVolume(Constants.interpolate(from, to, t));
|
||||
t = (double) (System.currentTimeMillis() - start) / (double) time;
|
||||
@@ -1,41 +1,82 @@
|
||||
package schule.ngb.zm.media;
|
||||
|
||||
import schule.ngb.zm.tasks.TaskRunner;
|
||||
import schule.ngb.zm.util.Log;
|
||||
import schule.ngb.zm.util.ResourceStreamProvider;
|
||||
import schule.ngb.zm.util.Validator;
|
||||
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Ein Musikstück, dass im Projekt abgespielt werden soll.
|
||||
* <p>
|
||||
* Im gegensatz zu einem {@link Sound} sind Musikstücke längere Audiodateien,
|
||||
* die zum Beispiel als Hintergrundmusik ablaufen sollen. Die Musik wird daher
|
||||
* nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle
|
||||
* gestreamt und wiedergegeben.
|
||||
*/
|
||||
public class Music implements Audio {
|
||||
|
||||
// size of the byte buffer used to read/write the audio stream
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
|
||||
/**
|
||||
* Ob der Sound gerade abgespielt wird.
|
||||
*/
|
||||
private boolean playing = false;
|
||||
|
||||
/**
|
||||
* Ob der Sound gerade in einer Schleife abgespielt wird.
|
||||
*/
|
||||
private boolean looping = false;
|
||||
|
||||
/**
|
||||
* Die Quelle des Musikstücks.
|
||||
*/
|
||||
private String audioSource;
|
||||
|
||||
/**
|
||||
* Der AudioStream, um die AUdiosdaten zulsen, falls dieser schon geöffnet
|
||||
* wurde. Sonst {@code null}.
|
||||
*/
|
||||
private AudioInputStream audioStream;
|
||||
|
||||
/**
|
||||
* Die Line für die Ausgabe, falls diese schon geöffnet wurde. Sonst
|
||||
* {@code null}.
|
||||
*/
|
||||
private SourceDataLine audioLine;
|
||||
|
||||
/**
|
||||
* Die Lautstärke der Musik.
|
||||
*/
|
||||
private float volume = 0.8f;
|
||||
|
||||
/**
|
||||
* Erstellt eine Musik aus der angegebenen Datei oder Webadresse.
|
||||
*
|
||||
* @param source Ein Dateipfad oder eine Webadresse.
|
||||
* @throws NullPointerException Falls die Quelle {@code null} ist.
|
||||
*/
|
||||
public Music( String source ) {
|
||||
Validator.requireNotNull(source);
|
||||
this.audioSource = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
if( !playing ) {
|
||||
@@ -44,6 +85,9 @@ public class Music implements Audio {
|
||||
return looping;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setVolume( double volume ) {
|
||||
this.volume = (float) volume;
|
||||
@@ -52,6 +96,18 @@ public class Music implements Audio {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public double getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
|
||||
* anzuwenden.
|
||||
*/
|
||||
private void applyVolume() {
|
||||
FloatControl gainControl =
|
||||
(FloatControl) audioLine.getControl(FloatControl.Type.MASTER_GAIN);
|
||||
@@ -61,8 +117,11 @@ public class Music implements Audio {
|
||||
gainControl.setValue(vol);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playOnce() {
|
||||
public void play() {
|
||||
if( openLine() ) {
|
||||
TaskRunner.run(new Runnable() {
|
||||
@Override
|
||||
@@ -73,19 +132,28 @@ public class Music implements Audio {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playOnceAndWait() {
|
||||
public void playAndWait() {
|
||||
if( openLine() ) {
|
||||
stream();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void loop() {
|
||||
looping = true;
|
||||
playOnce();
|
||||
play();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
playing = false;
|
||||
@@ -93,6 +161,9 @@ public class Music implements Audio {
|
||||
dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if( audioLine != null ) {
|
||||
@@ -110,7 +181,8 @@ public class Music implements Audio {
|
||||
if( audioStream != null ) {
|
||||
audioStream.close();
|
||||
}
|
||||
} catch( IOException ex ) {}
|
||||
} catch( IOException ex ) {
|
||||
}
|
||||
|
||||
audioLine = null;
|
||||
audioStream = null;
|
||||
@@ -124,15 +196,14 @@ public class Music implements Audio {
|
||||
int bytesRead = -1;
|
||||
|
||||
try {
|
||||
while (playing && (bytesRead = audioStream.read(bytesBuffer)) != -1) {
|
||||
while( playing && (bytesRead = audioStream.read(bytesBuffer)) != -1 ) {
|
||||
audioLine.write(bytesBuffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
audioLine.drain();
|
||||
audioLine.stop();
|
||||
} catch( IOException ex ) {
|
||||
LOGGER.warning("Error while playing Music source <" + audioSource + ">");
|
||||
LOGGER.throwing("Music", "stream", ex);
|
||||
LOG.warn(ex, "Error while playing Music source <%s>", audioSource);
|
||||
}
|
||||
|
||||
// Wait for the remaining audio to play
|
||||
@@ -154,14 +225,14 @@ public class Music implements Audio {
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
|
||||
if( in != null ) {
|
||||
final AudioInputStream inStream = AudioSystem.getAudioInputStream(in);
|
||||
URL url = ResourceStreamProvider.getResourceURL(audioSource);
|
||||
if( url != null ) {
|
||||
final AudioInputStream inStream = AudioSystem.getAudioInputStream(url);
|
||||
AudioFormat format = inStream.getFormat();
|
||||
|
||||
final int ch = format.getChannels();
|
||||
final float rate = format.getSampleRate();
|
||||
AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch*2, rate, false);
|
||||
AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false);
|
||||
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat);
|
||||
|
||||
@@ -172,28 +243,33 @@ public class Music implements Audio {
|
||||
audioStream = AudioSystem.getAudioInputStream(outFormat, inStream);
|
||||
return true;
|
||||
} else {
|
||||
LOGGER.warning("Sound source <" + audioSource + "> could not be played: No audio source found.");
|
||||
LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
|
||||
}
|
||||
} catch( UnsupportedAudioFileException ex ) {
|
||||
LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: The specified audio file is not supported.", ex);
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
|
||||
} catch( LineUnavailableException ex ) {
|
||||
LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Audio line for playing back is unavailable.", ex);
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
|
||||
} catch( IOException ex ) {
|
||||
LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Error playing the audio file.", ex);
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void streamingStopped() {
|
||||
playing = false;
|
||||
dispose();
|
||||
|
||||
if( looping ) {
|
||||
playOnce();
|
||||
if( openLine() ) {
|
||||
stream();
|
||||
} else {
|
||||
playing = false;
|
||||
looping = false;
|
||||
}
|
||||
} else {
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
//private static final Logger LOGGER = Logger.getLogger("schule.ngb.zm.media.Music");
|
||||
private static final Logger LOGGER = Logger.getLogger(Music.class.getName());
|
||||
private static final Log LOG = Log.getLogger(Music.class);
|
||||
|
||||
}
|
||||
309
src/main/java/schule/ngb/zm/media/Sound.java
Normal file
@@ -0,0 +1,309 @@
|
||||
package schule.ngb.zm.media;
|
||||
|
||||
import schule.ngb.zm.util.Log;
|
||||
import schule.ngb.zm.util.ResourceStreamProvider;
|
||||
import schule.ngb.zm.util.Validator;
|
||||
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Wiedergabe kurzer Soundclips, die mehrmals wiederverwendet werden.
|
||||
* <p>
|
||||
* In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer Sounds,
|
||||
* die zusammen mit bestimmten Aktionen wiedergegeben werden (zum Beispiel, wenn
|
||||
* die Spielfigur springt, wenn zwei Objekte kollidieren, usw.). Sounds werden
|
||||
* komplett in den Speicher geladen und können dadurch immer wieder, als
|
||||
* Schleife oder auch nur Abschnittsweise abgespielt werden.
|
||||
* <p>
|
||||
* Für längre Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher die
|
||||
* KLasse {@link Music} an.
|
||||
*/
|
||||
@SuppressWarnings( "unused" )
|
||||
public class Sound implements Audio {
|
||||
|
||||
/**
|
||||
* Ob der Sound gerade abgespielt wird.
|
||||
*/
|
||||
private boolean playing = false;
|
||||
|
||||
/**
|
||||
* Ob der Sound gerade in einer Schleife abgespielt wird.
|
||||
*/
|
||||
private boolean looping = false;
|
||||
|
||||
/**
|
||||
* Die Quelle des Musikstücks.
|
||||
*/
|
||||
private String audioSource;
|
||||
|
||||
/**
|
||||
* Der Clip, falls er schon geladen wurde, sonst {@code null}.
|
||||
*/
|
||||
private Clip audioClip;
|
||||
|
||||
/**
|
||||
* Ob die Resourcen des Clips im Speicher nach dem nächsten Abspielen
|
||||
* freigegeben werden sollen.
|
||||
*/
|
||||
private boolean disposeAfterPlay = false;
|
||||
|
||||
/**
|
||||
* Die Lautstärke des Clips.
|
||||
*/
|
||||
private float volume = 0.8f;
|
||||
|
||||
/**
|
||||
* Erstellt einen Sound aus der angegebene Quelle.
|
||||
*
|
||||
* @param source Ein Dateipfad oder eine Webadresse.
|
||||
* @throws NullPointerException Falls die Quelle {@code null} ist.
|
||||
*/
|
||||
public Sound( String source ) {
|
||||
Validator.requireNotNull(source);
|
||||
this.audioSource = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
// return audioClip != null && audioClip.isRunning();
|
||||
return playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
if( !playing ) {
|
||||
looping = false;
|
||||
}
|
||||
return looping;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setVolume( double volume ) {
|
||||
this.volume = (float) volume;
|
||||
if( audioClip != null ) {
|
||||
applyVolume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public double getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
|
||||
* anzuwenden.
|
||||
*/
|
||||
private void applyVolume() {
|
||||
FloatControl gainControl =
|
||||
(FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||
|
||||
float vol = 20f * (float) Math.log10(volume);
|
||||
// vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum());
|
||||
gainControl.setValue(vol);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
looping = false;
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
playing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void play() {
|
||||
if( this.openClip() ) {
|
||||
audioClip.start();
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playAndWait() {
|
||||
this.play();
|
||||
|
||||
long audioLen = audioClip.getMicrosecondLength();
|
||||
while( playing ) {
|
||||
try {
|
||||
long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L;
|
||||
Thread.sleep(ms);
|
||||
} catch( InterruptedException ex ) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
audioClip.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
|
||||
* frei.
|
||||
* <p>
|
||||
* Der Aufruf ist effektiv gleich zu
|
||||
* <pre><code>
|
||||
* clip.playAndWait();
|
||||
* clip.dispose();
|
||||
* </code></pre>
|
||||
* allerdings wird der aufrufende Thread nicht blockiert und
|
||||
* {@link #dispose()} automatisch am Ende aufgerufen.
|
||||
*/
|
||||
public void playOnce() {
|
||||
disposeAfterPlay = true;
|
||||
play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
|
||||
* frei.
|
||||
* <p>
|
||||
* Der Aufruf entspricht
|
||||
* <pre><code>
|
||||
* clip.playAndWait();
|
||||
* clip.dispose();
|
||||
* </code></pre>
|
||||
*/
|
||||
public void playOnceAndWait() {
|
||||
disposeAfterPlay = true;
|
||||
playAndWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void loop() {
|
||||
loop(Clip.LOOP_CONTINUOUSLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wiederholt den Sound die angegebene Anzahl an Wiederholungen ab und stoppt
|
||||
* die Wiedergabe dann.
|
||||
* @param count Anzahl der Wiederholungen.
|
||||
*/
|
||||
public void loop( int count ) {
|
||||
if( count > 0 ) {
|
||||
int loopCount = count;
|
||||
if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
|
||||
if( loopCount <= 0 ) {
|
||||
return;
|
||||
}
|
||||
// Adjust Number of loops
|
||||
loopCount -= 1;
|
||||
}
|
||||
if( openClip() ) {
|
||||
looping = true;
|
||||
audioClip.loop(loopCount);
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if( audioClip != null ) {
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
audioClip.close();
|
||||
audioClip = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean openClip() {
|
||||
if( audioClip != null ) {
|
||||
audioClip.setFramePosition(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
|
||||
if( in != null ) {
|
||||
AudioInputStream audioStream = AudioSystem.getAudioInputStream(in);
|
||||
AudioFormat format = audioStream.getFormat();
|
||||
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||
|
||||
audioClip = (Clip) AudioSystem.getLine(info);
|
||||
audioClip.addLineListener(new LineListener() {
|
||||
@Override
|
||||
public void update( LineEvent event ) {
|
||||
if( event.getType() == LineEvent.Type.STOP ) {
|
||||
playbackStopped();
|
||||
}
|
||||
}
|
||||
});
|
||||
audioClip.open(audioStream);
|
||||
applyVolume();
|
||||
return true;
|
||||
} else {
|
||||
LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
|
||||
}
|
||||
} catch( UnsupportedAudioFileException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
|
||||
} catch( LineUnavailableException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
|
||||
} catch( IOException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void update( LineEvent event ) {
|
||||
LineEvent.Type type = event.getType();
|
||||
|
||||
if( type == LineEvent.Type.START ) {
|
||||
playing = true;
|
||||
} else if( type == LineEvent.Type.STOP ) {
|
||||
playing = false;
|
||||
if( disposeAfterPlay ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Interne Methode, die aufgerufen wird, wenn die Wiedergabe gestoppt wird.
|
||||
* Entweder durch einen Aufruf von {@link #stop()} oder, weil die Wiedergabe
|
||||
* nach {@link #playOnce()} beendet wurde.
|
||||
*/
|
||||
private void playbackStopped() {
|
||||
playing = false;
|
||||
if( disposeAfterPlay ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Log LOG = Log.getLogger(Sound.class);
|
||||
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public abstract class Shape extends FilledShape {
|
||||
* Unterklassen sollten diese Methode überschreiben, um weitere
|
||||
* Eigenschaften zu kopieren (zum Beispiel den Radius eines Kreises). Mit
|
||||
* dem Aufruf
|
||||
* <code>super.copyFrom(shape)</code> sollten die Basiseigenschaften
|
||||
* {@code super.copyFrom(shape)} sollten die Basiseigenschaften
|
||||
* kopiert werden.
|
||||
*
|
||||
* @param shape
|
||||
@@ -222,7 +222,7 @@ public abstract class Shape extends FilledShape {
|
||||
* {@code buff} nach oben verschoben.
|
||||
*
|
||||
* @param shape
|
||||
* @param anchor
|
||||
* @param dir
|
||||
* @param buff
|
||||
*/
|
||||
public void moveTo( Shape shape, Options.Direction dir, double buff ) {
|
||||
@@ -47,7 +47,7 @@ public class ImageLoader {
|
||||
|
||||
/**
|
||||
* Lädt ein Bild von der angegebenen Quelle <var>source</var> und gibt das
|
||||
* Bild zurück oder <code>null</code>, wenn das Bild nicht geladen werden
|
||||
* Bild zurück oder {@code null}, wenn das Bild nicht geladen werden
|
||||
* konnte. Ist ein Bild mit der angegebenen Quelle im Cache, wird das
|
||||
* gespeicherte Bild zurückgegeben. Dies kann mit {@code caching = false}
|
||||
* verhindert werden.
|
||||
@@ -1,5 +1,11 @@
|
||||
package schule.ngb.zm.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
@@ -30,6 +36,10 @@ public final class Log {
|
||||
|
||||
private static final String ROOT_LOGGER = "schule.ngb.zm";
|
||||
|
||||
private static final String DEFAULT_LOG_FORMAT = "[%1$tT] [%4$11s] %5$s %6$s%n";
|
||||
|
||||
private static final String DEFAULT_DEBUG_FORMAT = "[%1$tT] [%4$11s] (%2$s) %5$s %6$s%n";
|
||||
|
||||
private static boolean LOGGING_INIT = false;
|
||||
|
||||
/**
|
||||
@@ -43,7 +53,7 @@ public final class Log {
|
||||
*
|
||||
* @see #enableGlobalLevel(Level)
|
||||
*/
|
||||
public static final void enableGlobalDebugging() {
|
||||
public static void enableGlobalDebugging() {
|
||||
enableGlobalLevel(ALL);
|
||||
}
|
||||
|
||||
@@ -68,32 +78,18 @@ public final class Log {
|
||||
* @param level Das Level, auf das alle {@code Logger} und {@code Handler}
|
||||
* mindestens herabgesenkt werden sollen.
|
||||
*/
|
||||
public static final void xenableGlobalLevel( Level level ) {
|
||||
public static void enableGlobalLevel( Level level ) {
|
||||
int lvl = Validator.requireNotNull(level).intValue();
|
||||
ensureRootLoggerIntialized();
|
||||
|
||||
Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
|
||||
rootLogger.setLevel(level);
|
||||
|
||||
for( Handler handler : rootLogger.getHandlers() ) {
|
||||
if( handler instanceof ConsoleHandler ) {
|
||||
Level handlerLevel = handler.getLevel();
|
||||
if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
|
||||
handler.setLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final void enableGlobalLevel( Level level ) {
|
||||
int lvl = Validator.requireNotNull(level).intValue();
|
||||
|
||||
// Decrease level of root level ConsoleHandlers for outpu
|
||||
// Decrease level of root level ConsoleHandlers for output
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
for( Handler handler : rootLogger.getHandlers() ) {
|
||||
if( handler instanceof ConsoleHandler ) {
|
||||
Level handlerLevel = handler.getLevel();
|
||||
if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
|
||||
handler.setLevel(level);
|
||||
handler.setFormatter(new LogFormatter());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,16 +113,42 @@ public final class Log {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger LOG = Logger.getLogger(ROOT_LOGGER);
|
||||
LOG.fine("Debug logging enabled.");
|
||||
}
|
||||
|
||||
public static final Log getLogger( Class<?> clazz ) {
|
||||
if( !LOGGING_INIT ) {
|
||||
Logger.getLogger(ROOT_LOGGER);
|
||||
LOGGING_INIT = true;
|
||||
}
|
||||
public static Log getLogger( Class<?> clazz ) {
|
||||
ensureRootLoggerIntialized();
|
||||
return new Log(clazz);
|
||||
}
|
||||
|
||||
private static void ensureRootLoggerIntialized() {
|
||||
if( LOGGING_INIT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
|
||||
rootLogger.setLevel(Level.INFO);
|
||||
|
||||
if( System.getProperty("java.util.logging.SimpleFormatter.format") == null
|
||||
&& LogManager.getLogManager().getProperty("java.util.logging.SimpleFormatter.format") == null ) {
|
||||
System.setProperty("java.util.logging.SimpleFormatter.format", DEFAULT_LOG_FORMAT);
|
||||
rootLogger.addHandler(new StreamHandler(System.err, new LogFormatter()));
|
||||
rootLogger.setUseParentHandlers(false);
|
||||
}
|
||||
if( rootLogger.getUseParentHandlers() ) {
|
||||
// This logger was not configured somewhere else
|
||||
// Add a Handler and Formatter
|
||||
|
||||
//StreamHandler rootHandler = new StreamHandler(System.out, new SimpleFormatter());
|
||||
//rootLogger.addHandler(rootHandler);
|
||||
//rootLogger.setUseParentHandlers(false);
|
||||
}
|
||||
|
||||
LOGGING_INIT = true;
|
||||
}
|
||||
|
||||
private final Logger LOGGER;
|
||||
|
||||
private final Class<?> sourceClass;
|
||||
@@ -315,4 +337,40 @@ public final class Log {
|
||||
this.log(FINER, throwable, msgSupplier);
|
||||
}
|
||||
|
||||
static final class LogFormatter extends Formatter {
|
||||
|
||||
@Override
|
||||
public String format( LogRecord record ) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(
|
||||
record.getInstant(), ZoneId.systemDefault());
|
||||
String source;
|
||||
if (record.getSourceClassName() != null) {
|
||||
source = record.getSourceClassName();
|
||||
if (record.getSourceMethodName() != null) {
|
||||
source += " " + record.getSourceMethodName();
|
||||
}
|
||||
} else {
|
||||
source = record.getLoggerName();
|
||||
}
|
||||
String message = formatMessage(record);
|
||||
String throwable = "";
|
||||
if (record.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
pw.println();
|
||||
record.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
throwable = sw.toString();
|
||||
}
|
||||
return String.format(DEFAULT_LOG_FORMAT,
|
||||
zdt,
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getLocalizedName(),
|
||||
message,
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -1,204 +0,0 @@
|
||||
package schule.ngb.zm.media;
|
||||
|
||||
import schule.ngb.zm.util.ResourceStreamProvider;
|
||||
|
||||
import javax.sound.sampled.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Sound implements Audio {
|
||||
|
||||
private boolean playing = false;
|
||||
|
||||
private boolean looping = false;
|
||||
|
||||
private String audioSource;
|
||||
|
||||
private Clip audioClip;
|
||||
|
||||
private boolean disposeAfterPlay = false;
|
||||
|
||||
private float volume = 0.8f;
|
||||
|
||||
public Sound( String source ) {
|
||||
this.audioSource = source;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
// return audioClip != null && audioClip.isRunning();
|
||||
return playing;
|
||||
}
|
||||
|
||||
public boolean isLooping() {
|
||||
if( !playing ) {
|
||||
looping = false;
|
||||
}
|
||||
return looping;
|
||||
}
|
||||
|
||||
public void setVolume( double volume ) {
|
||||
this.volume = (float) volume;
|
||||
if( audioClip != null ) {
|
||||
applyVolume();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyVolume() {
|
||||
FloatControl gainControl =
|
||||
(FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||
|
||||
float vol = 20f * (float) Math.log10(volume);
|
||||
// vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum());
|
||||
gainControl.setValue(vol);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
looping = false;
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
playing = false;
|
||||
}
|
||||
|
||||
public void play() {
|
||||
if( this.openClip() ) {
|
||||
audioClip.start();
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void playOnce() {
|
||||
disposeAfterPlay = true;
|
||||
play();
|
||||
}
|
||||
|
||||
public void playOnceAndWait() {
|
||||
disposeAfterPlay = true;
|
||||
playAndWait();
|
||||
}
|
||||
|
||||
public void playAndWait() {
|
||||
this.play();
|
||||
|
||||
long audioLen = audioClip.getMicrosecondLength();
|
||||
while( playing ) {
|
||||
try {
|
||||
long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L;
|
||||
Thread.sleep(ms);
|
||||
} catch( InterruptedException ex ) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
audioClip.close();
|
||||
}
|
||||
|
||||
public void loop() {
|
||||
loop(Clip.LOOP_CONTINUOUSLY);
|
||||
}
|
||||
|
||||
public void loop( int count ) {
|
||||
int loopCount = count;
|
||||
if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
|
||||
if( loopCount <= 0 ) {
|
||||
return;
|
||||
}
|
||||
// Adjust Number of loops
|
||||
loopCount -= 1;
|
||||
}
|
||||
if( openClip() ) {
|
||||
looping = true;
|
||||
audioClip.loop(loopCount);
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if( audioClip != null ) {
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
audioClip.close();
|
||||
audioClip = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean openClip() {
|
||||
if( audioClip != null ) {
|
||||
audioClip.setFramePosition(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
|
||||
if( in != null ) {
|
||||
AudioInputStream audioStream = AudioSystem.getAudioInputStream(in);
|
||||
AudioFormat format = audioStream.getFormat();
|
||||
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||
|
||||
audioClip = (Clip) AudioSystem.getLine(info);
|
||||
audioClip.addLineListener(new LineListener() {
|
||||
@Override
|
||||
public void update( LineEvent event ) {
|
||||
if( event.getType() == LineEvent.Type.STOP ) {
|
||||
playbackStopped();
|
||||
}
|
||||
}
|
||||
});
|
||||
audioClip.open(audioStream);
|
||||
applyVolume();
|
||||
return true;
|
||||
} else {
|
||||
LOGGER.warning("Sound source " + audioSource + " could not be played: No audio source found.");
|
||||
}
|
||||
} catch( UnsupportedAudioFileException ex ) {
|
||||
LOGGER.warning("Sound source " + audioSource + " could not be played: The specified audio file is not supported.");
|
||||
LOGGER.throwing("Sound", "openClip", ex);
|
||||
} catch( LineUnavailableException ex ) {
|
||||
LOGGER.warning("Sound source " + audioSource + " could not be played: Audio line for playing back is unavailable.");
|
||||
LOGGER.throwing("Sound", "openClip", ex);
|
||||
} catch( IOException ex ) {
|
||||
LOGGER.warning("Sound source " + audioSource + " could not be played: Error playing the audio file.");
|
||||
LOGGER.throwing("Sound", "openClip", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void update( LineEvent event ) {
|
||||
LineEvent.Type type = event.getType();
|
||||
|
||||
if( type == LineEvent.Type.START ) {
|
||||
playing = true;
|
||||
} else if( type == LineEvent.Type.STOP ) {
|
||||
playing = false;
|
||||
if( disposeAfterPlay ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
private void playbackStopped() {
|
||||
playing = false;
|
||||
if( disposeAfterPlay ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void addLineListener( LineListener listener ) {
|
||||
if( audioClip == null ) {
|
||||
openClip();
|
||||
}
|
||||
if( audioClip != null ) {
|
||||
audioClip.addLineListener(listener);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Sound.class.getName());
|
||||
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestColor {
|
||||
class ColorTest {
|
||||
|
||||
@Test
|
||||
public void colors() {
|
||||
public void init() {
|
||||
Color c;
|
||||
|
||||
c = new Color();
|
||||
@@ -39,7 +39,7 @@ public class TestColor {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseColors() {
|
||||
public void parse() {
|
||||
Color c;
|
||||
|
||||
c = Color.getRGBColor(0x00FF00FF);
|
||||
@@ -86,7 +86,74 @@ public class TestColor {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hsl() {
|
||||
void testEquals() {
|
||||
Color c1 = new Color(254, 98, 12);
|
||||
Color c2 = new Color(254, 98, 12);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
assertEquals(c1, c1);
|
||||
c2 = new Color(c1);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
|
||||
Color yellow = new Color(255, 255, 0);
|
||||
assertEquals(java.awt.Color.YELLOW, yellow);
|
||||
assertNotEquals(java.awt.Color.YELLOW, Color.YELLOW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void construct() {
|
||||
Color c;
|
||||
// Empty color is white
|
||||
c = new Color();
|
||||
assertEquals(Color.BLACK, c);
|
||||
// One arg is shade of gray
|
||||
c = new Color(255);
|
||||
assertEquals(Color.WHITE, c);
|
||||
c = new Color(0);
|
||||
assertEquals(Color.BLACK, c);
|
||||
c = new Color(64);
|
||||
assertEquals(Color.DARKGRAY, c);
|
||||
c = new Color(192);
|
||||
assertEquals(Color.LIGHTGRAY, c);
|
||||
// RGB colors
|
||||
c = new Color(0,0,0);
|
||||
assertEquals(Color.BLACK, c);
|
||||
c = new Color(255,0,0);
|
||||
assertEquals(java.awt.Color.RED, c.getJavaColor());
|
||||
c = new Color(0,255,0);
|
||||
assertEquals(java.awt.Color.GREEN, c.getJavaColor());
|
||||
c = new Color(0,0,255);
|
||||
assertEquals(java.awt.Color.BLUE, c.getJavaColor());
|
||||
// From java.awt.Color
|
||||
c = new Color(java.awt.Color.YELLOW);
|
||||
assertEquals(java.awt.Color.YELLOW, c.getJavaColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBColor() {
|
||||
Color c1 = Color.getRGBColor(0xFFFF0000);
|
||||
assertEquals(Color.RED, c1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSBColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSLColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseHexcode() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void morph() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void RGBtoHSL() {
|
||||
Color c;
|
||||
float[] hsl;
|
||||
|
||||
@@ -101,45 +168,60 @@ public class TestColor {
|
||||
assertEquals(.5647f, hsl[2], .0001f);
|
||||
}
|
||||
|
||||
public static void main( String[] args ) {
|
||||
new ColorPalette();
|
||||
@Test
|
||||
void HSLtoRGB() {
|
||||
}
|
||||
|
||||
static class ColorPalette extends Zeichenmaschine {
|
||||
public static final int SIZE = 10, COLORS = 16;
|
||||
|
||||
public void setup() {
|
||||
setSize(SIZE*COLORS, SIZE*COLORS);
|
||||
setTitle("Colors");
|
||||
|
||||
drawing.noStroke();
|
||||
drawing.setAnchor(NORTHWEST);
|
||||
|
||||
int steps = (int) (255.0/COLORS);
|
||||
Color c;
|
||||
for( int i = 0; i < COLORS; i++ ) {
|
||||
for( int j = 0; j < COLORS; j++ ) {
|
||||
c = new Color(i * steps, j * steps, (i+j)/2 * steps);
|
||||
drawing.setFillColor(c);
|
||||
drawing.rect(i*SIZE, j*SIZE, SIZE, SIZE);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void testHSLtoRGB() {
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
Color c = Color.HGGREEN;
|
||||
drawing.setFillColor(c);
|
||||
drawing.rect(0, 0, width/2, height);
|
||||
|
||||
for( int p = 10; p < 100; p += 10 ) {
|
||||
drawing.setFillColor(c.brighter(p));
|
||||
drawing.rect(width / 2, 0, width / 2, height / 2);
|
||||
drawing.setFillColor(c.darker(p));
|
||||
drawing.rect(width / 2, height / 2, width / 2, height / 2);
|
||||
|
||||
delay(1000);
|
||||
@Test
|
||||
void copy() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBA() {
|
||||
Color yellow = new Color(255, 255, 0);
|
||||
assertEquals(java.awt.Color.YELLOW.getRGB(), Color.YELLOW.getRGBA());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGreen() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBlue() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlpha() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaColor() {
|
||||
assertEquals(java.awt.Color.YELLOW, Color.YELLOW.getJavaColor());
|
||||
assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void brighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void darker() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDarker() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,79 @@ package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class VectorTest {
|
||||
|
||||
@Test
|
||||
public void init() {
|
||||
Vector vec;
|
||||
|
||||
vec = new Vector();
|
||||
assertEquals(0.0, vec.x, 0.0);
|
||||
assertEquals(0.0, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(4.5, 5.1);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(vec);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(Double.MAX_VALUE, Double.MIN_VALUE);
|
||||
assertEquals(Double.MAX_VALUE, vec.x, 0.0);
|
||||
assertEquals(Double.MIN_VALUE, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Double(7.2, 3.5677));
|
||||
assertEquals(7.2, vec.x, 0.0);
|
||||
assertEquals(3.5677, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Float(7.2f, 3.5677f));
|
||||
assertEquals(7.2, vec.x, 0.0001);
|
||||
assertEquals(3.5677, vec.y, 0.0001);
|
||||
|
||||
vec.set(new Vector(5.1, 8.9));
|
||||
assertEquals(5.1, vec.x, 0.0001);
|
||||
assertEquals(8.9, vec.y, 0.0001);
|
||||
|
||||
Vector vec2 = vec.copy();
|
||||
assertNotSame(vec, vec2);
|
||||
assertEquals(vec, vec2);
|
||||
assertEquals(5.1, vec2.x, 0.0001);
|
||||
assertEquals(8.9, vec2.y, 0.0001);
|
||||
|
||||
assertEquals(vec, vec.set(100, 100));
|
||||
assertEquals(200, vec.set(200, 200).x, 0.0001);
|
||||
assertEquals(8.9, vec.set(vec2).y, 0.0001);
|
||||
|
||||
for( int i = 0; i < 50; i++ ) {
|
||||
vec = Vector.random();
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < 100);
|
||||
assertTrue(vec.y < 100);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < i*10);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10, (i+1)*10, (i+2)*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= (i+1)*10);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < (i+2)*10);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLength() {
|
||||
Vector vec = new Vector(2.0, 5.4);
|
||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
@@ -1,34 +0,0 @@
|
||||
|
||||
import schule.ngb.zm.Zeichenmaschine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Beschreiben Sie hier die Klasse BluejTest.
|
||||
*
|
||||
* @author (Ihr Name)
|
||||
* @version (eine Versionsnummer oder ein Datum)
|
||||
*/
|
||||
public class BluejTest extends Zeichenmaschine
|
||||
{
|
||||
public void setup() {
|
||||
if( !IN_BLUEJ ) {
|
||||
drawing.clear(schule.ngb.zm.Color.HGRED);
|
||||
} else {
|
||||
drawing.clear(schule.ngb.zm.Color.HGGREEN);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void listClasses() {
|
||||
// find all classes in classpath
|
||||
List<String> allClasses = ClasspathInspector.getAllKnownClassNames();
|
||||
System.out.printf("There are %s classes available in the classpath\n", allClasses.size());
|
||||
|
||||
for (String clazz : allClasses) {
|
||||
if( clazz.contains("Boot") || clazz.contains("Main") ) {
|
||||
System.out.printf("%s\n", clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Find classes in the classpath (reads JARs and classpath folders).
|
||||
*
|
||||
* @author Pål Brattberg, brattberg@gmail.com
|
||||
* @see http://gist.github.com/pal
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ClasspathInspector {
|
||||
static boolean DEBUG = false;
|
||||
|
||||
|
||||
public static List<String> getAllKnownClassNames() {
|
||||
List<String> classNames = new ArrayList<String>();
|
||||
List<File> classLocations = getClassLocationsForCurrentClasspath();
|
||||
for (File file : classLocations) {
|
||||
classNames.addAll(getClassNamesFromPath(file));
|
||||
}
|
||||
return classNames;
|
||||
}
|
||||
|
||||
public static List<Class> getAllKnownClasses() {
|
||||
List<Class> classFiles = new ArrayList<Class>();
|
||||
List<File> classLocations = getClassLocationsForCurrentClasspath();
|
||||
for (File file : classLocations) {
|
||||
classFiles.addAll(getClassesFromPath(file));
|
||||
}
|
||||
return classFiles;
|
||||
}
|
||||
|
||||
public static List<Class> getMatchingClasses(Class interfaceOrSuperclass) {
|
||||
List<Class> matchingClasses = new ArrayList<Class>();
|
||||
List<Class> classes = getAllKnownClasses();
|
||||
log("checking %s classes", classes.size());
|
||||
for (Class clazz : classes) {
|
||||
if (interfaceOrSuperclass.isAssignableFrom(clazz)) {
|
||||
matchingClasses.add(clazz);
|
||||
log("class %s is assignable from %s", interfaceOrSuperclass, clazz);
|
||||
}
|
||||
}
|
||||
return matchingClasses;
|
||||
}
|
||||
|
||||
public static List<Class> getMatchingClasses(String validPackagePrefix, Class interfaceOrSuperclass) {
|
||||
throw new IllegalStateException("Not yet implemented!");
|
||||
}
|
||||
|
||||
public static List<Class> getMatchingClasses(String validPackagePrefix) {
|
||||
throw new IllegalStateException("Not yet implemented!");
|
||||
}
|
||||
|
||||
private static Collection<? extends Class> getClassesFromPath(File path) {
|
||||
if (path.isDirectory()) {
|
||||
return getClassesFromDirectory(path);
|
||||
} else {
|
||||
return getClassesFromJarFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<String> getClassNamesFromPath(File path) {
|
||||
if (path.isDirectory()) {
|
||||
return getClassNamesFromDirectory(path);
|
||||
} else {
|
||||
return getClassNamesFromJarFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static String fromFileToClassName(final String fileName) {
|
||||
return fileName.substring(0, fileName.length() - 6).replaceAll("/|\\\\", "\\.");
|
||||
}
|
||||
|
||||
private static List<Class> getClassesFromJarFile(File path) {
|
||||
List<Class> classes = new ArrayList<Class>();
|
||||
log("getClassesFromJarFile: Getting classes for " + path);
|
||||
|
||||
try {
|
||||
if (path.canRead()) {
|
||||
JarFile jar = new JarFile(path);
|
||||
Enumeration<JarEntry> en = jar.entries();
|
||||
while (en.hasMoreElements()) {
|
||||
JarEntry entry = en.nextElement();
|
||||
if (entry.getName().endsWith("class")) {
|
||||
String className = fromFileToClassName(entry.getName());
|
||||
log("\tgetClassesFromJarFile: found " + className);
|
||||
Class claz = Class.forName(className);
|
||||
classes.add(claz);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read classes from jar file: " + path, e);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static List<String> getClassNamesFromJarFile(File path) {
|
||||
List<String> classes = new ArrayList<>();
|
||||
log("getClassNamesFromJarFile: Getting classes for " + path);
|
||||
|
||||
try {
|
||||
if (path.canRead()) {
|
||||
JarFile jar = new JarFile(path);
|
||||
Enumeration<JarEntry> en = jar.entries();
|
||||
while (en.hasMoreElements()) {
|
||||
JarEntry entry = en.nextElement();
|
||||
if (entry.getName().endsWith("class")) {
|
||||
String className = fromFileToClassName(entry.getName());
|
||||
classes.add(className);
|
||||
log("\tgetClassesFromJarFile: found " + className);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read classnames from jar file: " + path, e);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static List<Class> getClassesFromDirectory(File path) {
|
||||
List<Class> classes = new ArrayList<Class>();
|
||||
log("getClassesFromDirectory: Getting classes for " + path);
|
||||
|
||||
// get jar files from top-level directory
|
||||
List<File> jarFiles = listFiles(path, new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".jar");
|
||||
}
|
||||
}, false);
|
||||
for (File file : jarFiles) {
|
||||
classes.addAll(getClassesFromJarFile(file));
|
||||
}
|
||||
|
||||
// get all class-files
|
||||
List<File> classFiles = listFiles(path, new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".class");
|
||||
}
|
||||
}, true);
|
||||
|
||||
// List<URL> urlList = new ArrayList<URL>();
|
||||
// List<String> classNameList = new ArrayList<String>();
|
||||
int substringBeginIndex = path.getAbsolutePath().length() + 1;
|
||||
for (File classfile : classFiles) {
|
||||
String className = classfile.getAbsolutePath().substring(substringBeginIndex);
|
||||
className = fromFileToClassName(className);
|
||||
log("Found class %s in path %s: ", className, path);
|
||||
try {
|
||||
classes.add(Class.forName(className));
|
||||
} catch (Throwable e) {
|
||||
log("Couldn't create class %s. %s: ", className, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static List<String> getClassNamesFromDirectory(File path) {
|
||||
List<String> classes = new ArrayList<>();
|
||||
log("getClassNamesFromDirectory: Getting classes for " + path);
|
||||
|
||||
// get jar files from top-level directory
|
||||
List<File> jarFiles = listFiles(path, new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".jar");
|
||||
}
|
||||
}, false);
|
||||
for (File file : jarFiles) {
|
||||
classes.addAll(getClassNamesFromJarFile(file));
|
||||
}
|
||||
|
||||
// get all class-files
|
||||
List<File> classFiles = listFiles(path, new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".class");
|
||||
}
|
||||
}, true);
|
||||
|
||||
// List<URL> urlList = new ArrayList<URL>();
|
||||
// List<String> classNameList = new ArrayList<String>();
|
||||
int substringBeginIndex = path.getAbsolutePath().length() + 1;
|
||||
for (File classfile : classFiles) {
|
||||
String className = classfile.getAbsolutePath().substring(substringBeginIndex);
|
||||
className = fromFileToClassName(className);
|
||||
log("Found class %s in path %s: ", className, path);
|
||||
classes.add(className);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static List<File> listFiles(File directory, FilenameFilter filter, boolean recurse) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
File[] entries = directory.listFiles();
|
||||
|
||||
// Go over entries
|
||||
for (File entry : entries) {
|
||||
// If there is no filter or the filter accepts the
|
||||
// file / directory, add it to the list
|
||||
if (filter == null || filter.accept(directory, entry.getName())) {
|
||||
files.add(entry);
|
||||
}
|
||||
|
||||
// If the file is a directory and the recurse flag
|
||||
// is set, recurse into the directory
|
||||
if (recurse && entry.isDirectory()) {
|
||||
files.addAll(listFiles(entry, filter, recurse));
|
||||
}
|
||||
}
|
||||
|
||||
// Return collection of files
|
||||
return files;
|
||||
}
|
||||
|
||||
public static List<File> getClassLocationsForCurrentClasspath() {
|
||||
List<File> urls = new ArrayList<File>();
|
||||
String javaClassPath = System.getProperty("java.class.path");
|
||||
if (javaClassPath != null) {
|
||||
for (String path : javaClassPath.split(File.pathSeparator)) {
|
||||
urls.add(new File(path));
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
// todo: this is only partial, probably
|
||||
public static URL normalize(URL url) throws MalformedURLException {
|
||||
String spec = url.getFile();
|
||||
|
||||
// get url base - remove everything after ".jar!/??" , if exists
|
||||
final int i = spec.indexOf("!/");
|
||||
if (i != -1) {
|
||||
spec = spec.substring(0, spec.indexOf("!/"));
|
||||
}
|
||||
|
||||
// uppercase windows drive
|
||||
url = new URL(url, spec);
|
||||
final String file = url.getFile();
|
||||
final int i1 = file.indexOf(':');
|
||||
if (i1 != -1) {
|
||||
String drive = file.substring(i1 - 1, 2).toUpperCase();
|
||||
url = new URL(url, file.substring(0, i1 - 1) + drive + file.substring(i1));
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private static void log(String pattern, final Object... args) {
|
||||
if (DEBUG)
|
||||
System.out.printf(pattern + "\n", args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// find all classes in classpath
|
||||
List<Class> allClasses = ClasspathInspector.getAllKnownClasses();
|
||||
System.out.printf("There are %s classes available in the classpath\n", allClasses.size());
|
||||
|
||||
// find all classes that implement/subclass an interface/superclass
|
||||
List<Class> serializableClasses = ClasspathInspector.getMatchingClasses(Serializable.class);
|
||||
for (Class clazz : serializableClasses) {
|
||||
System.out.printf("%s is Serializable\n", clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=BluejTest
|
||||
dependency1.to=ClasspathInspector
|
||||
dependency1.type=UsesDependency
|
||||
editor.fx.0.height=0
|
||||
editor.fx.0.width=0
|
||||
editor.fx.0.x=0
|
||||
editor.fx.0.y=0
|
||||
objectbench.height=94
|
||||
objectbench.width=776
|
||||
package.divider.horizontal=0.6
|
||||
package.divider.vertical=0.8305369127516778
|
||||
package.editor.height=488
|
||||
package.editor.width=661
|
||||
package.editor.x=374
|
||||
package.editor.y=158
|
||||
package.frame.height=660
|
||||
package.frame.width=800
|
||||
package.numDependencies=1
|
||||
package.numTargets=2
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.height=60
|
||||
readme.name=@README
|
||||
readme.width=48
|
||||
readme.x=10
|
||||
readme.y=10
|
||||
target1.height=70
|
||||
target1.name=BluejTest
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.width=120
|
||||
target1.x=70
|
||||
target1.y=10
|
||||
target2.height=70
|
||||
target2.name=ClasspathInspector
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.width=130
|
||||
target2.x=380
|
||||
target2.y=30
|
||||
@@ -1,135 +0,0 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ColorTest {
|
||||
|
||||
|
||||
@Test
|
||||
void testEquals() {
|
||||
Color c1 = new Color(254, 98, 12);
|
||||
Color c2 = new Color(254, 98, 12);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
assertEquals(c1, c1);
|
||||
c2 = new Color(c1);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
|
||||
assertEquals(Color.YELLOW, java.awt.Color.YELLOW);
|
||||
assertNotEquals(java.awt.Color.YELLOW, Color.YELLOW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void construct() {
|
||||
Color c;
|
||||
// Empty color is white
|
||||
c = new Color();
|
||||
assertEquals(c, Color.BLACK);
|
||||
// One arg is shade of gray
|
||||
c = new Color(255);
|
||||
assertEquals(c, Color.WHITE);
|
||||
c = new Color(0);
|
||||
assertEquals(c, Color.BLACK);
|
||||
c = new Color(64);
|
||||
assertEquals(c, Color.DARKGRAY);
|
||||
c = new Color(192);
|
||||
assertEquals(c, Color.LIGHTGRAY);
|
||||
// RGB colors
|
||||
c = new Color(0,0,0);
|
||||
assertEquals(c, Color.BLACK);
|
||||
c = new Color(255,0,0);
|
||||
assertEquals(c, Color.RED);
|
||||
c = new Color(0,255,0);
|
||||
assertEquals(c, Color.GREEN);
|
||||
c = new Color(0,0,255);
|
||||
assertEquals(c, Color.BLUE);
|
||||
// From java.awt.Color
|
||||
c = new Color(java.awt.Color.YELLOW);
|
||||
assertEquals(c, Color.YELLOW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBColor() {
|
||||
Color c1 = Color.getRGBColor(0xFFFF0000);
|
||||
assertEquals(Color.RED, c1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSBColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSLColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseHexcode() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void morph() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void RGBtoHSL() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void HSLtoRGB() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHSLtoRGB() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void copy() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBA() {
|
||||
assertEquals(java.awt.Color.YELLOW.getRGB(), Color.YELLOW.getRGBA());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGreen() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBlue() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlpha() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getColor() {
|
||||
assertEquals(java.awt.Color.YELLOW, Color.YELLOW.getJavaColor());
|
||||
assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void brighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void darker() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDarker() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestVector {
|
||||
|
||||
@Test
|
||||
public void testInit() {
|
||||
Vector vec;
|
||||
|
||||
vec = new Vector();
|
||||
assertEquals(0.0, vec.x, 0.0);
|
||||
assertEquals(0.0, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(4.5, 5.1);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(vec);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(Double.MAX_VALUE, Double.MIN_VALUE);
|
||||
assertEquals(Double.MAX_VALUE, vec.x, 0.0);
|
||||
assertEquals(Double.MIN_VALUE, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Double(7.2, 3.5677));
|
||||
assertEquals(7.2, vec.x, 0.0);
|
||||
assertEquals(3.5677, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Float(7.2f, 3.5677f));
|
||||
assertEquals(7.2, vec.x, 0.0001);
|
||||
assertEquals(3.5677, vec.y, 0.0001);
|
||||
|
||||
vec.set(new Vector(5.1, 8.9));
|
||||
assertEquals(5.1, vec.x, 0.0001);
|
||||
assertEquals(8.9, vec.y, 0.0001);
|
||||
|
||||
Vector vec2 = vec.copy();
|
||||
assertNotSame(vec, vec2);
|
||||
assertEquals(vec, vec2);
|
||||
assertEquals(5.1, vec2.x, 0.0001);
|
||||
assertEquals(8.9, vec2.y, 0.0001);
|
||||
|
||||
assertEquals(vec, vec.set(100, 100));
|
||||
assertEquals(200, vec.set(200, 200).x, 0.0001);
|
||||
assertEquals(8.9, vec.set(vec2).y, 0.0001);
|
||||
|
||||
for( int i = 0; i < 50; i++ ) {
|
||||
vec = Vector.random();
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < 100);
|
||||
assertTrue(vec.y < 100);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < i*10);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10, (i+1)*10, (i+2)*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= (i+1)*10);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < (i+2)*10);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMath() {
|
||||
Vector vec1, vec2;
|
||||
|
||||
vec1 = new Vector(100, 0);
|
||||
vec1.scale(0.0);
|
||||
vec2 = new Vector(0, 0);
|
||||
assertEquals(vec2, vec1);
|
||||
|
||||
vec1.add(100, 50);
|
||||
assertEquals(100, vec1.x, 0.0);
|
||||
assertEquals(50, vec1.y, 0.0);
|
||||
|
||||
vec2.sub(50, 100);
|
||||
assertEquals(-50, vec2.x, 0.0);
|
||||
assertEquals(-100, vec2.y, 0.0);
|
||||
|
||||
vec2.scale(1.5);
|
||||
assertEquals(-75, vec2.x, 0.0001);
|
||||
assertEquals(-150, vec2.y, 0.0001);
|
||||
|
||||
assertEquals(100, vec1.set(100,0).length(), 0.0001);
|
||||
assertEquals(10000, vec1.lengthSq(), 0.0001);
|
||||
assertEquals(Math.sqrt(50*50 + 60*60), vec1.set(50,60).length(), 0.0001);
|
||||
assertEquals(1.0, vec1.set(100,0).normalize().length(), 0.0001);
|
||||
assertEquals(1.0, Vector.random().normalize().length(), 0.0001);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStaticMath() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||