Implement working basic UI that controls focal length

pull/35/head
James Ball 2020-11-22 21:07:48 +00:00
rodzic e358df1c34
commit be845393cf
11 zmienionych plików z 194 dodań i 25 usunięć

Wyświetl plik

@ -31,7 +31,7 @@ public class AudioClient {
// example: // example:
// osci-render models/cube.obj 3 // osci-render models/cube.obj 3
public static void main(String[] programArgs) public static void main(String[] programArgs)
throws IOException, ParserConfigurationException, SAXException, InterruptedException { throws IOException, ParserConfigurationException, SAXException {
// TODO: Calculate weight of lines using depth. // TODO: Calculate weight of lines using depth.
// Reduce weight of lines drawn multiple times. // Reduce weight of lines drawn multiple times.
// Find intersections of lines to (possibly) improve line cleanup. // Find intersections of lines to (possibly) improve line cleanup.

Wyświetl plik

@ -10,6 +10,7 @@ import com.xtaudio.xt.XtService;
import com.xtaudio.xt.XtSetup; import com.xtaudio.xt.XtSetup;
import com.xtaudio.xt.XtStream; import com.xtaudio.xt.XtStream;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import shapes.Shape; import shapes.Shape;
import shapes.Vector2; import shapes.Vector2;
@ -18,7 +19,7 @@ import java.util.List;
public class AudioPlayer implements Runnable { public class AudioPlayer implements Runnable {
private final XtFormat FORMAT; private final XtFormat FORMAT;
private final ArrayBlockingQueue<List<Shape>> frameQueue; private final BlockingQueue<List<Shape>> frameQueue;
private List<Shape> frame; private List<Shape> frame;
private int currentShape = 0; private int currentShape = 0;
@ -34,7 +35,7 @@ public class AudioPlayer implements Runnable {
private volatile boolean stopped; private volatile boolean stopped;
public AudioPlayer(int sampleRate, ArrayBlockingQueue<List<Shape>> frameQueue) { public AudioPlayer(int sampleRate, BlockingQueue<List<Shape>> frameQueue) {
this.FORMAT = new XtFormat(new XtMix(sampleRate, XtSample.FLOAT32), 0, 0, 2, 0); this.FORMAT = new XtFormat(new XtMix(sampleRate, XtSample.FLOAT32), 0, 0, 2, 0);
this.frameQueue = frameQueue; this.frameQueue = frameQueue;
} }

Wyświetl plik

@ -0,0 +1,63 @@
package audio;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import parser.FileParser;
import parser.ObjParser;
import parser.TextParser;
import parser.svg.SvgParser;
import shapes.Shape;
public class FrameProducer implements Runnable {
private static final String DEFAULT_FILE = "models/cube.obj";
private final BlockingQueue<List<Shape>> frameQueue;
private ObjParser objParser;
private SvgParser svgParser;
private TextParser textParser;
private FileParser parser;
public FrameProducer(BlockingQueue<List<Shape>> frameQueue)
throws ParserConfigurationException, SAXException, IOException {
this.frameQueue = frameQueue;
setParser(DEFAULT_FILE);
}
@Override
public void run() {
while (true) {
try {
frameQueue.put(parser.nextFrame());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Frame missed.");
}
}
}
public void setFocalLength(float focalLength) {
objParser.setFocalLength(focalLength);
}
public void setParser(String filePath)
throws IOException, ParserConfigurationException, SAXException {
if (filePath.matches(".*\\.obj")) {
objParser = new ObjParser(filePath, 1);
parser = objParser;
} else if (filePath.matches(".*\\.svg")) {
svgParser = new SvgParser(filePath);
parser = svgParser;
} else if (filePath.matches(".*\\.txt")) {
textParser = new TextParser(filePath);
parser = textParser;
} else {
throw new IllegalArgumentException(
"Provided file extension in file " + filePath + " not supported.");
}
}
}

Wyświetl plik

@ -20,7 +20,7 @@ public class Camera {
private static final int SAMPLE_RENDER_SAMPLES = 50; private static final int SAMPLE_RENDER_SAMPLES = 50;
private static final double EPSILON = 0.001; private static final double EPSILON = 0.001;
private final double focalLength; private double focalLength;
private Vector3 pos; private Vector3 pos;
@ -99,9 +99,11 @@ public class Camera {
return new Vector2(); return new Vector2();
} }
double oldFocalLength = focalLength;
return new Vector2( return new Vector2(
vertex.getX() * focalLength / (vertex.getZ() - pos.getZ()) + pos.getX(), vertex.getX() * oldFocalLength / (vertex.getZ() - pos.getZ()) + pos.getX(),
vertex.getY() * focalLength / (vertex.getZ() - pos.getZ()) + pos.getY() vertex.getY() * oldFocalLength / (vertex.getZ() - pos.getZ()) + pos.getY()
); );
} }
@ -117,4 +119,8 @@ public class Camera {
return lines; return lines;
} }
public void setFocalLength(float focalLength) {
this.focalLength = focalLength;
}
} }

Wyświetl plik

@ -1,22 +1,76 @@
package gui; package gui;
import audio.AudioPlayer;
import audio.FrameProducer;
import java.io.File;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathEvaluationResult;
import org.xml.sax.SAXException;
import parser.FileParser;
import parser.ObjParser;
import parser.TextParser;
import parser.svg.SvgParser;
import shapes.Shape;
import shapes.Vector2;
public class Controller implements Initializable { public class Controller implements Initializable {
private static final int BUFFER_SIZE = 20;
private static final int SAMPLE_RATE = 192000;
private final FileChooser fileChooser = new FileChooser();
private final BlockingQueue<List<Shape>> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE);
private final AudioPlayer player = new AudioPlayer(SAMPLE_RATE, frameQueue);
/* Default parser. */
private final FrameProducer producer = new FrameProducer(frameQueue);
private Stage stage;
@FXML
private Button btnBrowse;
@FXML @FXML
private Text sliderOutput; private Text sliderOutput;
@FXML @FXML
private Slider slider; private Slider slider;
public Controller() throws IOException, ParserConfigurationException, SAXException {
}
@Override @Override
public void initialize(URL url, ResourceBundle resourceBundle) { public void initialize(URL url, ResourceBundle resourceBundle) {
slider.valueProperty().addListener( slider.valueProperty().addListener((source, oldValue, newValue) -> {
(source, oldValue, newValue) -> sliderOutput.setText(String.valueOf(slider.getValue()))); sliderOutput.setText(String.valueOf(slider.getValue()));
producer.setFocalLength((float) slider.getValue());
});
btnBrowse.setOnAction(e -> {
File file = fileChooser.showOpenDialog(stage);
try {
producer.setParser(file.getAbsolutePath());
} catch (IOException | ParserConfigurationException | SAXException ioException) {
ioException.printStackTrace();
}
});
new Thread(producer).start();
new Thread(new AudioPlayer(SAMPLE_RATE, frameQueue)).start();
}
public void setStage(Stage stage) {
this.stage = stage;
} }
} }

Wyświetl plik

@ -1,6 +1,7 @@
package gui; package gui;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
@ -10,10 +11,22 @@ public class Main extends Application {
@Override @Override
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("osci-render.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("osci-render.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
controller.setStage(primaryStage);
primaryStage.setTitle("osci-render"); primaryStage.setTitle("osci-render");
primaryStage.setScene(new Scene(root)); primaryStage.setScene(new Scene(root));
controller.setStage(primaryStage);
primaryStage.show(); primaryStage.show();
primaryStage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
} }
public static void main(String[] args) { public static void main(String[] args) {

Wyświetl plik

@ -8,8 +8,9 @@
<children> <children>
<AnchorPane prefHeight="400.0" prefWidth="400.0"> <AnchorPane prefHeight="400.0" prefWidth="400.0">
<children> <children>
<Slider fx:id="slider" layoutX="44.0" layoutY="54.0" /> <Slider fx:id="slider" blockIncrement="2.0" layoutX="45.0" layoutY="238.0" max="2.0" min="1.0E-5" prefHeight="14.0" prefWidth="313.0" />
<Text fx:id="sliderOutput" layoutX="109.0" layoutY="39.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0" /> <Text fx:id="sliderOutput" layoutX="196.0" layoutY="219.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0" />
<Button fx:id="btnBrowse" layoutX="23.0" layoutY="22.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="119.0" text="Browse" />
</children> </children>
</AnchorPane> </AnchorPane>
</children> </children>

Wyświetl plik

@ -25,4 +25,5 @@ public abstract class FileParser {
throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException; throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException;
public abstract List<Shape> nextFrame(); public abstract List<Shape> nextFrame();
public abstract String getFilePath();
} }

Wyświetl plik

@ -4,36 +4,38 @@ import engine.Camera;
import engine.Vector3; import engine.Vector3;
import engine.WorldObject; import engine.WorldObject;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import shapes.Shape; import shapes.Shape;
public class ObjParser extends FileParser { public class ObjParser extends FileParser {
private static double OBJ_ROTATE_SPEED = Math.PI / 1000; private static final float DEFAULT_ROTATE_SPEED = 3;
private List<List<Shape>> shapes;
private final Vector3 cameraPos; private final Vector3 cameraPos;
private final Vector3 rotation; private final Vector3 rotation;
private final double focalLength;
private final boolean isDefaultPosition; private final boolean isDefaultPosition;
private final String filePath;
private WorldObject object; private WorldObject object;
private Camera camera; private Camera camera;
private double focalLength;
public ObjParser(String path, float rotateSpeed, float cameraX, float cameraY, float cameraZ, public ObjParser(String path, float rotateSpeed, float cameraX, float cameraY, float cameraZ,
float focalLength, boolean isDefaultPosition) throws IOException { float focalLength, boolean isDefaultPosition) throws IOException {
ObjParser.OBJ_ROTATE_SPEED *= rotateSpeed; rotateSpeed *= Math.PI / 1000;
checkFileExtension(path); checkFileExtension(path);
this.shapes = new ArrayList<>(); this.filePath = path;
this.cameraPos = new Vector3(cameraX, cameraY, cameraZ); this.cameraPos = new Vector3(cameraX, cameraY, cameraZ);
this.isDefaultPosition = isDefaultPosition; this.isDefaultPosition = isDefaultPosition;
this.focalLength = focalLength; this.focalLength = focalLength;
this.rotation = new Vector3(0, OBJ_ROTATE_SPEED, OBJ_ROTATE_SPEED); this.rotation = new Vector3(0, rotateSpeed, rotateSpeed);
parseFile(path); parseFile(path);
} }
public ObjParser(String path, float focalLength) throws IOException {
this(path, DEFAULT_ROTATE_SPEED, 0, 0, 0, focalLength, true);
}
@Override @Override
protected String getFileExtension() { protected String getFileExtension() {
return "obj"; return "obj";
@ -43,8 +45,7 @@ public class ObjParser extends FileParser {
protected void parseFile(String path) throws IllegalArgumentException, IOException { protected void parseFile(String path) throws IllegalArgumentException, IOException {
object = new WorldObject(path); object = new WorldObject(path);
camera = new Camera(focalLength, cameraPos); camera = new Camera(focalLength, cameraPos);
// If camera position arguments haven't been specified, automatically work out the position of
// the camera based on the size of the object in the camera's view.
if (isDefaultPosition) { if (isDefaultPosition) {
camera.findZPos(object); camera.findZPos(object);
} }
@ -55,4 +56,18 @@ public class ObjParser extends FileParser {
object.rotate(rotation); object.rotate(rotation);
return camera.draw(object); return camera.draw(object);
} }
@Override
public String getFilePath() {
return filePath;
}
// If camera position arguments haven't been specified, automatically work out the position of
// the camera based on the size of the object in the camera's view.
public void setFocalLength(float focalLength) {
camera.setFocalLength(focalLength);
if (isDefaultPosition) {
camera.findZPos(object);
}
}
} }

Wyświetl plik

@ -22,11 +22,14 @@ public class TextParser extends FileParser {
private final Map<Character, List<Shape>> charToShape; private final Map<Character, List<Shape>> charToShape;
private final List<String> text; private final List<String> text;
private final String filePath;
private List<Shape> shapes; private List<Shape> shapes;
public TextParser(String path, String font) public TextParser(String path, String font)
throws IOException, SAXException, ParserConfigurationException { throws IOException, SAXException, ParserConfigurationException {
checkFileExtension(path);
this.filePath = path;
this.charToShape = new HashMap<>(); this.charToShape = new HashMap<>();
this.shapes = new ArrayList<>(); this.shapes = new ArrayList<>();
this.text = Files.readAllLines(Paths.get(path), Charset.defaultCharset()); this.text = Files.readAllLines(Paths.get(path), Charset.defaultCharset());
@ -40,7 +43,7 @@ public class TextParser extends FileParser {
@Override @Override
protected String getFileExtension() { protected String getFileExtension() {
return ".txt"; return "txt";
} }
@Override @Override
@ -81,4 +84,9 @@ public class TextParser extends FileParser {
public List<Shape> nextFrame() { public List<Shape> nextFrame() {
return shapes; return shapes;
} }
@Override
public String getFilePath() {
return filePath;
}
} }

Wyświetl plik

@ -27,15 +27,17 @@ public class SvgParser extends FileParser {
private final Map<Character, Function<List<Float>, List<Shape>>> commandMap; private final Map<Character, Function<List<Float>, List<Shape>>> commandMap;
private final SvgState state; private final SvgState state;
private final String filePath;
private List<Shape> shapes; private List<Shape> shapes;
private Document svg; private Document svg;
public SvgParser(String path) throws IOException, SAXException, ParserConfigurationException { public SvgParser(String path) throws IOException, SAXException, ParserConfigurationException {
checkFileExtension(path); checkFileExtension(path);
shapes = new ArrayList<>(); this.filePath = path;
state = new SvgState(); this.shapes = new ArrayList<>();
commandMap = new HashMap<>(); this.state = new SvgState();
this.commandMap = new HashMap<>();
initialiseCommandMap(); initialiseCommandMap();
parseFile(path); parseFile(path);
} }
@ -198,4 +200,9 @@ public class SvgParser extends FileParser {
public List<Shape> nextFrame() { public List<Shape> nextFrame() {
return shapes; return shapes;
} }
@Override
public String getFilePath() {
return filePath;
}
} }