kopia lustrzana https://github.com/jameshball/osci-render
Refactor legacy AudioPlayer effects and reduce coupling of Controller
rodzic
954b8838d2
commit
46d7868c9f
2
pom.xml
2
pom.xml
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<groupId>sh.ball</groupId>
|
<groupId>sh.ball</groupId>
|
||||||
<artifactId>osci-render</artifactId>
|
<artifactId>osci-render</artifactId>
|
||||||
<version>1.1.1</version>
|
<version>1.1.2</version>
|
||||||
|
|
||||||
<name>osci-render</name>
|
<name>osci-render</name>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package sh.ball.audio;
|
package sh.ball.audio;
|
||||||
|
|
||||||
import sh.ball.audio.effect.Effect;
|
import sh.ball.audio.effect.Effect;
|
||||||
|
import xt.audio.*;
|
||||||
import xt.audio.Enums.XtSample;
|
import xt.audio.Enums.XtSample;
|
||||||
import xt.audio.Enums.XtSetup;
|
import xt.audio.Enums.XtSetup;
|
||||||
import xt.audio.Enums.XtSystem;
|
import xt.audio.Enums.XtSystem;
|
||||||
|
@ -11,12 +12,6 @@ import xt.audio.Structs.XtDeviceStreamParams;
|
||||||
import xt.audio.Structs.XtFormat;
|
import xt.audio.Structs.XtFormat;
|
||||||
import xt.audio.Structs.XtMix;
|
import xt.audio.Structs.XtMix;
|
||||||
import xt.audio.Structs.XtStreamParams;
|
import xt.audio.Structs.XtStreamParams;
|
||||||
import xt.audio.XtAudio;
|
|
||||||
import xt.audio.XtDevice;
|
|
||||||
import xt.audio.XtPlatform;
|
|
||||||
import xt.audio.XtSafeBuffer;
|
|
||||||
import xt.audio.XtService;
|
|
||||||
import xt.audio.XtStream;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -30,38 +25,24 @@ import java.util.List;
|
||||||
|
|
||||||
public class AudioPlayer implements Renderer<List<Shape>> {
|
public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
|
|
||||||
private static final int SAMPLE_RATE = 192000;
|
|
||||||
private static final int BUFFER_SIZE = 20;
|
private static final int BUFFER_SIZE = 20;
|
||||||
|
|
||||||
private final XtMix MIX = new XtMix(SAMPLE_RATE, XtSample.FLOAT32);
|
private final XtFormat format;
|
||||||
private final XtChannels CHANNELS = new XtChannels(0, 0, 2, 0);
|
|
||||||
private final XtFormat FORMAT = new XtFormat(MIX, CHANNELS);
|
|
||||||
private final BlockingQueue<List<Shape>> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE);
|
private final BlockingQueue<List<Shape>> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE);
|
||||||
|
private final Map<Object, Effect> effects = new HashMap<>();
|
||||||
|
|
||||||
private Map<Object, Effect> effects = new HashMap<>();
|
|
||||||
private List<Shape> frame;
|
private List<Shape> frame;
|
||||||
private int currentShape = 0;
|
private int currentShape = 0;
|
||||||
private int audioFramesDrawn = 0;
|
private int audioFramesDrawn = 0;
|
||||||
|
|
||||||
private double translateSpeed = 0;
|
|
||||||
private Vector2 translateVector = new Vector2();
|
|
||||||
private final Phase translatePhase = new Phase();
|
|
||||||
private double rotateSpeed = 0;
|
|
||||||
private final Phase rotatePhase = new Phase();
|
|
||||||
private double scale = 1;
|
|
||||||
private double weight = Shape.DEFAULT_WEIGHT;
|
private double weight = Shape.DEFAULT_WEIGHT;
|
||||||
|
|
||||||
private volatile boolean stopped;
|
private volatile boolean stopped;
|
||||||
|
|
||||||
public AudioPlayer() {
|
public AudioPlayer(int sampleRate) {
|
||||||
}
|
XtMix mix = new XtMix(sampleRate, XtSample.FLOAT32);
|
||||||
|
XtChannels channels = new XtChannels(0, 0, 2, 0);
|
||||||
public AudioPlayer(double rotateSpeed, double translateSpeed, Vector2 translateVector, double scale, double weight) {
|
this.format = new XtFormat(mix, channels);
|
||||||
setRotationSpeed(rotateSpeed);
|
|
||||||
setTranslationSpeed(translateSpeed);
|
|
||||||
setTranslation(translateVector);
|
|
||||||
setScale(scale);
|
|
||||||
setQuality(weight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int render(XtStream stream, XtBuffer buffer, Object user) throws InterruptedException {
|
private int render(XtStream stream, XtBuffer buffer, Object user) throws InterruptedException {
|
||||||
|
@ -73,16 +54,13 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
Shape shape = getCurrentShape();
|
Shape shape = getCurrentShape();
|
||||||
|
|
||||||
shape = shape.setWeight(weight);
|
shape = shape.setWeight(weight);
|
||||||
shape = scale(shape);
|
|
||||||
shape = rotate(shape, FORMAT.mix.rate);
|
|
||||||
shape = translate(shape, FORMAT.mix.rate);
|
|
||||||
|
|
||||||
double totalAudioFrames = shape.getWeight() * shape.getLength();
|
double totalAudioFrames = shape.getWeight() * shape.getLength();
|
||||||
double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames;
|
double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames;
|
||||||
Vector2 nextVector = applyEffects(f, shape.nextVector(drawingProgress));
|
Vector2 nextVector = applyEffects(f, shape.nextVector(drawingProgress));
|
||||||
|
|
||||||
output[f * FORMAT.channels.outputs] = (float) nextVector.getX();
|
output[f * format.channels.outputs] = (float) nextVector.getX();
|
||||||
output[f * FORMAT.channels.outputs + 1] = (float) nextVector.getY();
|
output[f * format.channels.outputs + 1] = (float) nextVector.getY();
|
||||||
|
|
||||||
audioFramesDrawn++;
|
audioFramesDrawn++;
|
||||||
|
|
||||||
|
@ -107,60 +85,6 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Shape rotate(Shape shape, double sampleRate) {
|
|
||||||
if (rotateSpeed != 0) {
|
|
||||||
shape = shape.rotate(
|
|
||||||
nextTheta(sampleRate, rotateSpeed, translatePhase)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Shape translate(Shape shape, double sampleRate) {
|
|
||||||
if (translateSpeed != 0 && !translateVector.equals(new Vector2())) {
|
|
||||||
return shape.translate(translateVector.scale(
|
|
||||||
Math.sin(nextTheta(sampleRate, translateSpeed, rotatePhase))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double nextTheta(double sampleRate, double frequency, Phase phase) {
|
|
||||||
phase.value += frequency / sampleRate;
|
|
||||||
|
|
||||||
if (phase.value >= 1.0) {
|
|
||||||
phase.value = -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return phase.value * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Shape scale(Shape shape) {
|
|
||||||
if (scale != 1) {
|
|
||||||
return shape.scale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRotationSpeed(double speed) {
|
|
||||||
this.rotateSpeed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTranslation(Vector2 translation) {
|
|
||||||
this.translateVector = translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTranslationSpeed(double speed) {
|
|
||||||
translateSpeed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScale(double scale) {
|
|
||||||
this.scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setQuality(double quality) {
|
public void setQuality(double quality) {
|
||||||
this.weight = quality;
|
this.weight = quality;
|
||||||
|
@ -191,11 +115,11 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
if (defaultOutput == null) return;
|
if (defaultOutput == null) return;
|
||||||
|
|
||||||
try (XtDevice device = service.openDevice(defaultOutput)) {
|
try (XtDevice device = service.openDevice(defaultOutput)) {
|
||||||
if (device.supportsFormat(FORMAT)) {
|
if (device.supportsFormat(format)) {
|
||||||
|
|
||||||
XtBufferSize size = device.getBufferSize(FORMAT);
|
XtBufferSize size = device.getBufferSize(format);
|
||||||
XtStreamParams streamParams = new XtStreamParams(true, this::render, null, null);
|
XtStreamParams streamParams = new XtStreamParams(true, this::render, null, null);
|
||||||
XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, FORMAT, size.current);
|
XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, format, size.current);
|
||||||
try (XtStream stream = device.openStream(deviceParams, null);
|
try (XtStream stream = device.openStream(deviceParams, null);
|
||||||
XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) {
|
XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) {
|
||||||
stream.start();
|
stream.start();
|
||||||
|
@ -239,9 +163,4 @@ public class AudioPlayer implements Renderer<List<Shape>> {
|
||||||
effects.remove(identifier);
|
effects.remove(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Phase {
|
|
||||||
|
|
||||||
private double value = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,8 @@ package sh.ball.audio.effect;
|
||||||
|
|
||||||
public enum EffectType {
|
public enum EffectType {
|
||||||
VECTOR_CANCELLING,
|
VECTOR_CANCELLING,
|
||||||
BIT_CRUSH
|
BIT_CRUSH,
|
||||||
|
SCALE,
|
||||||
|
ROTATE,
|
||||||
|
TRANSLATE
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
public abstract class PhaseEffect implements Effect {
|
||||||
|
|
||||||
|
protected final int sampleRate;
|
||||||
|
|
||||||
|
protected double speed;
|
||||||
|
private double phase;
|
||||||
|
|
||||||
|
protected PhaseEffect(int sampleRate, double speed) {
|
||||||
|
this.sampleRate = sampleRate;
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double nextTheta() {
|
||||||
|
phase += speed / sampleRate;
|
||||||
|
|
||||||
|
if (phase >= 1.0) {
|
||||||
|
phase = -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return phase * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(double speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class RotateEffect extends PhaseEffect {
|
||||||
|
|
||||||
|
public RotateEffect(int sampleRate, double speed) {
|
||||||
|
super(sampleRate, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RotateEffect(int sampleRate) {
|
||||||
|
this(sampleRate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
if (speed != 0) {
|
||||||
|
return vector.rotate(nextTheta());
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class ScaleEffect implements Effect {
|
||||||
|
|
||||||
|
private double scale;
|
||||||
|
|
||||||
|
public ScaleEffect(double scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScaleEffect() {
|
||||||
|
this(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScale(double scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
return vector.scale(scale);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package sh.ball.audio.effect;
|
||||||
|
|
||||||
|
import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
|
public class TranslateEffect extends PhaseEffect {
|
||||||
|
|
||||||
|
private Vector2 translation;
|
||||||
|
|
||||||
|
public TranslateEffect(int sampleRate, double speed, Vector2 translation) {
|
||||||
|
super(sampleRate, speed);
|
||||||
|
this.translation = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslateEffect(int sampleRate) {
|
||||||
|
this(sampleRate, 0, new Vector2());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector2 apply(int count, Vector2 vector) {
|
||||||
|
if (speed != 0 && !translation.equals(new Vector2())) {
|
||||||
|
return vector.translate(translation.scale(Math.sin(nextTheta())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTranslation(Vector2 translation) {
|
||||||
|
this.translation = translation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package sh.ball.gui;
|
package sh.ball.gui;
|
||||||
|
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import sh.ball.audio.AudioPlayer;
|
import sh.ball.audio.Renderer;
|
||||||
import sh.ball.audio.effect.Effect;
|
import sh.ball.audio.effect.*;
|
||||||
import sh.ball.audio.FrameProducer;
|
import sh.ball.audio.FrameProducer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -24,8 +24,6 @@ import javafx.stage.Stage;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import sh.ball.audio.effect.EffectType;
|
|
||||||
import sh.ball.audio.effect.EventFactory;
|
|
||||||
import sh.ball.engine.Vector3;
|
import sh.ball.engine.Vector3;
|
||||||
import sh.ball.parser.obj.ObjFrameSettings;
|
import sh.ball.parser.obj.ObjFrameSettings;
|
||||||
import sh.ball.parser.obj.ObjParser;
|
import sh.ball.parser.obj.ObjParser;
|
||||||
|
@ -35,17 +33,18 @@ import sh.ball.shapes.Vector2;
|
||||||
|
|
||||||
public class Controller implements Initializable {
|
public class Controller implements Initializable {
|
||||||
|
|
||||||
|
private static final int SAMPLE_RATE = 192000;
|
||||||
private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
|
private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
|
||||||
|
|
||||||
private final FileChooser fileChooser = new FileChooser();
|
private final FileChooser fileChooser = new FileChooser();
|
||||||
// TODO: Reduce coupling on AudioPlayer
|
private final Renderer<List<Shape>> renderer;
|
||||||
private final AudioPlayer renderer = new AudioPlayer();
|
|
||||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
private FrameProducer<List<Shape>> producer = new FrameProducer<>(
|
private final RotateEffect rotateEffect = new RotateEffect(SAMPLE_RATE);
|
||||||
renderer,
|
private final TranslateEffect translateEffect = new TranslateEffect(SAMPLE_RATE);
|
||||||
new ObjParser(DEFAULT_OBJ).parse()
|
private final ScaleEffect scaleEffect = new ScaleEffect();
|
||||||
);
|
|
||||||
|
private FrameProducer<List<Shape>> producer;
|
||||||
|
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
|
|
||||||
|
@ -94,7 +93,12 @@ public class Controller implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private Slider bitCrushSlider;
|
private Slider bitCrushSlider;
|
||||||
|
|
||||||
public Controller() throws IOException {
|
public Controller(Renderer<List<Shape>> renderer) throws IOException {
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.producer = new FrameProducer<>(
|
||||||
|
renderer,
|
||||||
|
new ObjParser(DEFAULT_OBJ).parse()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
|
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
|
||||||
|
@ -102,11 +106,11 @@ public class Controller implements Initializable {
|
||||||
weightSlider,
|
weightSlider,
|
||||||
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
|
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
|
||||||
rotateSpeedSlider,
|
rotateSpeedSlider,
|
||||||
new SliderUpdater<>(rotateSpeedLabel::setText, renderer::setRotationSpeed),
|
new SliderUpdater<>(rotateSpeedLabel::setText, rotateEffect::setSpeed),
|
||||||
translationSpeedSlider,
|
translationSpeedSlider,
|
||||||
new SliderUpdater<>(translationSpeedLabel::setText, renderer::setTranslationSpeed),
|
new SliderUpdater<>(translationSpeedLabel::setText, translateEffect::setSpeed),
|
||||||
scaleSlider,
|
scaleSlider,
|
||||||
new SliderUpdater<>(scaleLabel::setText, renderer::setScale),
|
new SliderUpdater<>(scaleLabel::setText, scaleEffect::setScale),
|
||||||
focalLengthSlider,
|
focalLengthSlider,
|
||||||
new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength)
|
new SliderUpdater<>(focalLengthLabel::setText, this::setFocalLength)
|
||||||
);
|
);
|
||||||
|
@ -135,7 +139,7 @@ public class Controller implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidationListener translationUpdate = observable ->
|
InvalidationListener translationUpdate = observable ->
|
||||||
renderer.setTranslation(new Vector2(
|
translateEffect.setTranslation(new Vector2(
|
||||||
tryParse(translationXTextField.getText()),
|
tryParse(translationXTextField.getText()),
|
||||||
tryParse(translationYTextField.getText())
|
tryParse(translationYTextField.getText())
|
||||||
));
|
));
|
||||||
|
@ -150,7 +154,6 @@ public class Controller implements Initializable {
|
||||||
tryParse(cameraZTextField.getText())
|
tryParse(cameraZTextField.getText())
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
||||||
cameraXTextField.textProperty().addListener(cameraPosUpdate);
|
cameraXTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
cameraYTextField.textProperty().addListener(cameraPosUpdate);
|
cameraYTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
cameraZTextField.textProperty().addListener(cameraPosUpdate);
|
cameraZTextField.textProperty().addListener(cameraPosUpdate);
|
||||||
|
@ -175,6 +178,10 @@ public class Controller implements Initializable {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
renderer.addEffect(EffectType.SCALE, scaleEffect);
|
||||||
|
renderer.addEffect(EffectType.ROTATE, rotateEffect);
|
||||||
|
renderer.addEffect(EffectType.TRANSLATE, translateEffect);
|
||||||
|
|
||||||
executor.submit(producer);
|
executor.submit(producer);
|
||||||
new Thread(renderer).start();
|
new Thread(renderer).start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,20 @@ import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import sh.ball.audio.AudioPlayer;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Gui extends Application {
|
public class Gui extends Application {
|
||||||
|
|
||||||
|
private static final int SAMPLE_RATE = 192000;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/osci-render.fxml"));
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/osci-render.fxml"));
|
||||||
|
Controller controller = new Controller(new AudioPlayer(SAMPLE_RATE));
|
||||||
|
loader.setController(controller);
|
||||||
Parent root = loader.load();
|
Parent root = loader.load();
|
||||||
Controller controller = loader.getController();
|
|
||||||
controller.setStage(stage);
|
|
||||||
|
|
||||||
stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/icons/icon.png"))));
|
stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/icons/icon.png"))));
|
||||||
stage.setTitle("osci-render");
|
stage.setTitle("osci-render");
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<?import javafx.scene.layout.GridPane?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
|
||||||
<GridPane alignment="center" hgap="10" prefWidth="400.0" vgap="10" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sh.ball.gui.Controller">
|
<GridPane alignment="center" hgap="10" prefWidth="400.0" vgap="10" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints />
|
<ColumnConstraints />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
|
@ -26,9 +26,9 @@
|
||||||
<Slider fx:id="rotateSpeedSlider" layoutX="116.0" layoutY="79.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
<Slider fx:id="rotateSpeedSlider" layoutX="116.0" layoutY="79.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
||||||
<Label layoutX="37.0" layoutY="77.0" text="Rotate speed" />
|
<Label layoutX="37.0" layoutY="77.0" text="Rotate speed" />
|
||||||
<Label fx:id="rotateSpeedLabel" layoutX="316.0" layoutY="77.0" maxWidth="40.0" text="0" />
|
<Label fx:id="rotateSpeedLabel" layoutX="316.0" layoutY="77.0" maxWidth="40.0" text="0" />
|
||||||
<Slider fx:id="translationSpeedSlider" layoutX="116.0" layoutY="105.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
<Slider fx:id="translationSpeedSlider" layoutX="116.0" layoutY="105.0" max="10.0" prefHeight="14.0" prefWidth="198.0" />
|
||||||
<Label layoutX="14.0" layoutY="103.0" text="Translation speed" />
|
<Label layoutX="14.0" layoutY="103.0" text="Translation speed" />
|
||||||
<Label fx:id="translationSpeedLabel" layoutX="316.0" layoutY="103.0" maxWidth="40.0" text="1" />
|
<Label fx:id="translationSpeedLabel" layoutX="316.0" layoutY="103.0" maxWidth="40.0" text="0" />
|
||||||
<Slider fx:id="scaleSlider" layoutX="116.0" layoutY="130.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
<Slider fx:id="scaleSlider" layoutX="116.0" layoutY="130.0" max="10.0" prefHeight="14.0" prefWidth="198.0" value="1.0" />
|
||||||
<Label layoutX="80.0" layoutY="128.0" text="Scale" />
|
<Label layoutX="80.0" layoutY="128.0" text="Scale" />
|
||||||
<Label fx:id="scaleLabel" layoutX="316.0" layoutY="128.0" maxWidth="40.0" text="1" />
|
<Label fx:id="scaleLabel" layoutX="316.0" layoutY="128.0" maxWidth="40.0" text="1" />
|
||||||
|
|
Ładowanie…
Reference in New Issue