Complete bulk of FrameProducer refactor

pull/35/head
James Ball 2021-04-17 00:03:43 +01:00
rodzic cee2b19165
commit 67fb81dea8
12 zmienionych plików z 199 dodań i 136 usunięć

Wyświetl plik

@ -24,6 +24,7 @@
<configuration>
<source>15</source>
<target>15</target>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
</plugins>

Wyświetl plik

@ -0,0 +1,5 @@
package sh.ball;
public interface FrameSet<T> {
T next();
}

Wyświetl plik

@ -1,59 +1,35 @@
package sh.ball.audio;
import sh.ball.Renderer;
import sh.ball.engine.Vector3;
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sh.ball.FrameSet;
import sh.ball.Renderer;
import java.io.IOException;
import sh.ball.parser.FileParser;
import sh.ball.parser.ObjParser;
import sh.ball.parser.TextParser;
import sh.ball.parser.svg.SvgParser;
import sh.ball.shapes.Shape;
public class FrameProducer implements Runnable {
import javax.xml.parsers.ParserConfigurationException;
private final Renderer<List<Shape>> renderer;
public class FrameProducer<T> implements Runnable {
private ObjParser objParser;
private SvgParser svgParser;
private TextParser textParser;
private FileParser parser;
private final Renderer<T> renderer;
private final FrameSet<T> frames;
public FrameProducer(Renderer<List<Shape>> renderer) {
private boolean running;
public FrameProducer(Renderer<T> renderer, FileParser<FrameSet<T>> parser) throws IOException, SAXException, ParserConfigurationException {
this.renderer = renderer;
this.frames = parser.parse();
}
@Override
public void run() {
while (true) {
renderer.addFrame(parser.nextFrame());
running = true;
while (running) {
renderer.addFrame(frames.next());
}
}
public void setParser(String filePath)
throws IOException, ParserConfigurationException, SAXException {
if (ObjParser.isObjFile(filePath)) {
objParser = new ObjParser(filePath, 1);
parser = objParser;
} else if (SvgParser.isSvgFile(filePath)) {
svgParser = new SvgParser(filePath);
parser = svgParser;
} else if (TextParser.isTxtFile(filePath)) {
textParser = new TextParser(filePath);
parser = textParser;
} else {
throw new IllegalArgumentException(
"Provided file extension in file " + filePath + " not supported.");
}
}
public void setFocalLength(Double focalLength) {
objParser.setFocalLength(focalLength);
}
public void setCameraPos(Vector3 vector) {
objParser.setCameraPos(vector);
public void stop() {
running = false;
}
}

Wyświetl plik

@ -3,13 +3,17 @@ package sh.ball.gui;
import sh.ball.MovableRenderer;
import sh.ball.audio.AudioPlayer;
import sh.ball.audio.FrameProducer;
import sh.ball.engine.Vector3;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.beans.InvalidationListener;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
@ -20,20 +24,28 @@ import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sh.ball.parser.ObjParser;
import sh.ball.parser.obj.ObjParser;
import sh.ball.parser.ParserFactory;
import sh.ball.parser.txt.TextParser;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
public class Controller implements Initializable {
private static final String DEFAULT_FILE = "src/main/resources/models/cube.obj";
private static final String DEFAULT_FILE = TextParser.class.getResource("/models/cube.obj").getPath();
private final FileChooser fileChooser = new FileChooser();
private final MovableRenderer<List<Shape>, Vector2> renderer = new AudioPlayer();
private final FrameProducer producer = new FrameProducer(renderer);
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private FrameProducer<List<Shape>> producer = new FrameProducer<>(
renderer,
ParserFactory.getParser(DEFAULT_FILE).orElseThrow(FileNotFoundException::new)
);
private Stage stage;
@ -74,52 +86,53 @@ public class Controller implements Initializable {
@FXML
private TextField cameraZTextField;
public Controller() throws ParserConfigurationException, SAXException, IOException {
}
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
return Map.of(
weightSlider,
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
rotateSpeedSlider,
new SliderUpdater<>(rotateSpeedLabel::setText, renderer::setRotationSpeed),
translationSpeedSlider,
new SliderUpdater<>(translationSpeedLabel::setText, renderer::setTranslationSpeed),
scaleSlider,
new SliderUpdater<>(scaleLabel::setText, renderer::setScale),
focalLengthSlider,
new SliderUpdater<>(focalLengthLabel::setText, producer::setFocalLength)
weightSlider,
new SliderUpdater<>(weightLabel::setText, renderer::setQuality),
rotateSpeedSlider,
new SliderUpdater<>(rotateSpeedLabel::setText, renderer::setRotationSpeed),
translationSpeedSlider,
new SliderUpdater<>(translationSpeedLabel::setText, renderer::setTranslationSpeed),
scaleSlider,
new SliderUpdater<>(scaleLabel::setText, renderer::setScale)
// focalLengthSlider,
// new SliderUpdater<>(focalLengthLabel::setText, producer::setFocalLength)
);
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
chooseFile(new File(DEFAULT_FILE));
Map<Slider, SliderUpdater<Double>> sliders = initializeSliderMap();
for (Slider slider : sliders.keySet()) {
slider.valueProperty().addListener((source, oldValue, newValue) ->
sliders.get(slider).update(slider.getValue())
sliders.get(slider).update(slider.getValue())
);
}
InvalidationListener translationUpdate = observable ->
renderer.setTranslation(new Vector2(
tryParse(translationXTextField.getText()),
tryParse(translationYTextField.getText())
));
renderer.setTranslation(new Vector2(
tryParse(translationXTextField.getText()),
tryParse(translationYTextField.getText())
));
translationXTextField.textProperty().addListener(translationUpdate);
translationYTextField.textProperty().addListener(translationUpdate);
InvalidationListener cameraPosUpdate = observable ->
producer.setCameraPos(new Vector3(
tryParse(cameraXTextField.getText()),
tryParse(cameraYTextField.getText()),
tryParse(cameraZTextField.getText())
));
cameraXTextField.textProperty().addListener(cameraPosUpdate);
cameraYTextField.textProperty().addListener(cameraPosUpdate);
cameraZTextField.textProperty().addListener(cameraPosUpdate);
// InvalidationListener cameraPosUpdate = observable ->
// producer.setCameraPos(new Vector3(
// tryParse(cameraXTextField.getText()),
// tryParse(cameraYTextField.getText()),
// tryParse(cameraZTextField.getText())
// ));
//
// cameraXTextField.textProperty().addListener(cameraPosUpdate);
// cameraYTextField.textProperty().addListener(cameraPosUpdate);
// cameraZTextField.textProperty().addListener(cameraPosUpdate);
chooseFileButton.setOnAction(e -> {
File file = null;
@ -129,7 +142,7 @@ public class Controller implements Initializable {
chooseFile(file);
});
new Thread(producer).start();
executor.submit(producer);
new Thread(renderer).start();
}
@ -143,8 +156,15 @@ public class Controller implements Initializable {
private void chooseFile(File file) {
try {
producer.stop();
String path = file.getAbsolutePath();
producer.setParser(path);
producer = new FrameProducer<>(
renderer,
ParserFactory.getParser(path)
.orElseThrow(FileNotFoundException::new)
);
executor.submit(producer);
if (file.exists() && !file.isDirectory()) {
fileLabel.setText(path);
objTitledPane.setDisable(!ObjParser.isObjFile(path));

Wyświetl plik

@ -6,7 +6,7 @@ import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sh.ball.shapes.Shape;
public abstract class FileParser {
public abstract class FileParser<T> {
public abstract String getFileExtension();
@ -21,9 +21,8 @@ public abstract class FileParser {
return path.matches(".*\\." + getFileExtension());
}
protected abstract void parseFile(String path)
public abstract T parse()
throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException;
public abstract List<Shape> nextFrame();
public abstract String getFilePath();
}

Wyświetl plik

@ -0,0 +1,28 @@
package sh.ball.parser;
import org.xml.sax.SAXException;
import sh.ball.FrameSet;
import sh.ball.parser.obj.ObjParser;
import sh.ball.parser.svg.SvgParser;
import sh.ball.parser.txt.TextParser;
import sh.ball.shapes.Shape;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
public class ParserFactory {
public static Optional<FileParser<FrameSet<List<Shape>>>> getParser(String filePath) throws IOException, ParserConfigurationException, SAXException {
if (ObjParser.isObjFile(filePath)) {
return Optional.of(new ObjParser(filePath, 1));
} else if (SvgParser.isSvgFile(filePath)) {
return Optional.of(new SvgParser(filePath));
} else if (TextParser.isTxtFile(filePath)) {
return Optional.of(new TextParser(filePath));
}
return Optional.empty();
}
}

Wyświetl plik

@ -0,0 +1,20 @@
package sh.ball.parser;
import sh.ball.FrameSet;
import sh.ball.shapes.Shape;
import java.util.List;
public class ShapeFrameSet implements FrameSet<List<Shape>> {
private final List<Shape> shapes;
public ShapeFrameSet(List<Shape> shapes) {
this.shapes = shapes;
}
@Override
public List<Shape> next() {
return shapes;
}
}

Wyświetl plik

@ -0,0 +1,28 @@
package sh.ball.parser.obj;
import sh.ball.FrameSet;
import sh.ball.engine.Camera;
import sh.ball.engine.Vector3;
import sh.ball.engine.WorldObject;
import sh.ball.shapes.Shape;
import java.util.List;
public class ObjFrameSet implements FrameSet<List<Shape>> {
private final WorldObject object;
private final Camera camera;
private final Vector3 rotation;
public ObjFrameSet(WorldObject object, Camera camera, Vector3 rotation) {
this.object = object;
this.camera = camera;
this.rotation = rotation;
}
@Override
public List<Shape> next() {
object.rotate(rotation);
return camera.draw(object);
}
}

Wyświetl plik

@ -1,35 +1,35 @@
package sh.ball.parser;
package sh.ball.parser.obj;
import sh.ball.FrameSet;
import sh.ball.engine.Camera;
import sh.ball.engine.Vector3;
import sh.ball.engine.WorldObject;
import java.io.IOException;
import java.util.List;
import sh.ball.parser.FileParser;
import sh.ball.shapes.Shape;
public class ObjParser extends FileParser {
public class ObjParser extends FileParser<FrameSet<List<Shape>>> {
private static final float DEFAULT_ROTATE_SPEED = 3;
private final Vector3 cameraPos;
private final Vector3 rotation;
private final boolean isDefaultPosition;
private final String filePath;
private final Camera camera;
private WorldObject object;
private Camera camera;
private double focalLength;
public ObjParser(String path, float rotateSpeed, float cameraX, float cameraY, float cameraZ,
float focalLength, boolean isDefaultPosition) throws IOException {
float focalLength, boolean isDefaultPosition) {
rotateSpeed *= Math.PI / 1000;
checkFileExtension(path);
this.filePath = path;
this.cameraPos = new Vector3(cameraX, cameraY, cameraZ);
this.isDefaultPosition = isDefaultPosition;
this.focalLength = focalLength;
Vector3 cameraPos = new Vector3(cameraX, cameraY, cameraZ);
this.camera = new Camera(focalLength, cameraPos);
this.rotation = new Vector3(0, rotateSpeed, rotateSpeed);
parseFile(path);
}
public ObjParser(String path, float focalLength) throws IOException {
@ -42,19 +42,14 @@ public class ObjParser extends FileParser {
}
@Override
protected void parseFile(String path) throws IllegalArgumentException, IOException {
object = new WorldObject(path);
camera = new Camera(focalLength, cameraPos);
public FrameSet<List<Shape>> parse() throws IllegalArgumentException, IOException {
object = new WorldObject(filePath);
if (isDefaultPosition) {
camera.findZPos(object);
}
}
@Override
public List<Shape> nextFrame() {
object.rotate(rotation);
return camera.draw(object);
return new ObjFrameSet(object, camera, rotation);
}
@Override

Wyświetl plik

@ -19,27 +19,26 @@ import org.jsoup.Jsoup;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import sh.ball.FrameSet;
import sh.ball.parser.FileParser;
import sh.ball.parser.ShapeFrameSet;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
public class SvgParser extends FileParser {
public class SvgParser extends FileParser<FrameSet<List<Shape>>> {
private final Map<Character, Function<List<Float>, List<Shape>>> commandMap;
private final SvgState state;
private final String filePath;
private List<Shape> shapes;
private Document svg;
public SvgParser(String path) throws IOException, SAXException, ParserConfigurationException {
public SvgParser(String path) {
checkFileExtension(path);
this.filePath = path;
this.shapes = new ArrayList<>();
this.state = new SvgState();
this.commandMap = new HashMap<>();
initialiseCommandMap();
parseFile(path);
}
// Map command chars to function calls.
@ -118,10 +117,11 @@ public class SvgParser extends FileParser {
}
@Override
protected void parseFile(String filePath)
public FrameSet<List<Shape>> parse()
throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException {
this.svg = getXMLDocument(filePath);
List<Node> svgElem = asList(svg.getElementsByTagName("svg"));
List<Shape> shapes = new ArrayList<>();
if (svgElem.size() != 1) {
throw new IllegalArgumentException("SVG has either zero or more than one svg element.");
@ -132,7 +132,7 @@ public class SvgParser extends FileParser {
shapes.addAll(parsePath(node.getNodeValue()));
}
shapes = Shape.normalize(shapes);
return new ShapeFrameSet(Shape.normalize(shapes));
}
/* Given a character, will return the glyph associated with it.
@ -196,11 +196,6 @@ public class SvgParser extends FileParser {
return svgShapes;
}
@Override
public List<Shape> nextFrame() {
return shapes;
}
@Override
public String getFilePath() {
return filePath;

Wyświetl plik

@ -1,4 +1,4 @@
package sh.ball.parser;
package sh.ball.parser.txt;
import java.io.IOException;
import java.nio.charset.Charset;
@ -11,34 +11,31 @@ import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sh.ball.FrameSet;
import sh.ball.parser.FileParser;
import sh.ball.parser.ShapeFrameSet;
import sh.ball.parser.svg.SvgParser;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
public class TextParser extends FileParser {
public class TextParser extends FileParser<FrameSet<List<Shape>>> {
private static final char WIDE_CHAR = 'W';
private static final double HEIGHT_SCALAR = 1.6;
private static final String DEFAULT_FONT = TextParser.class.getResource("/fonts/SourceCodePro-ExtraLight.svg").getPath();
private final Map<Character, List<Shape>> charToShape;
private final List<String> text;
private final String filePath;
private final String fontPath;
private List<Shape> shapes;
public TextParser(String path, String font)
throws IOException, SAXException, ParserConfigurationException {
public TextParser(String path, String font) {
checkFileExtension(path);
this.filePath = path;
this.fontPath = font;
this.charToShape = new HashMap<>();
this.shapes = new ArrayList<>();
this.text = Files.readAllLines(Paths.get(path), Charset.defaultCharset());
parseFile(font);
}
public TextParser(String path)
throws IOException, SAXException, ParserConfigurationException {
public TextParser(String path) {
this(path, DEFAULT_FONT);
}
@ -48,9 +45,11 @@ public class TextParser extends FileParser {
}
@Override
protected void parseFile(String path)
throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException {
SvgParser parser = new SvgParser(path);
public FrameSet<List<Shape>> parse() throws IllegalArgumentException, IOException, ParserConfigurationException, SAXException {
List<String> text = Files.readAllLines(Paths.get(filePath), Charset.defaultCharset());
SvgParser parser = new SvgParser(fontPath);
parser.parse();
List<Shape> shapes = new ArrayList<>();
/* WIDE_CHAR used as an example character that will be wide in most languages.
* This helps determine the correct character width for the font chosen. */
@ -78,12 +77,7 @@ public class TextParser extends FileParser {
}
}
shapes = Shape.flip(Shape.normalize(shapes));
}
@Override
public List<Shape> nextFrame() {
return shapes;
return new ShapeFrameSet(Shape.flip(Shape.normalize(shapes)));
}
@Override

Wyświetl plik

@ -4,11 +4,13 @@ import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.junit.Test;
import org.xml.sax.SAXException;
import sh.ball.parser.svg.SvgParser;
import sh.ball.shapes.Line;
import sh.ball.shapes.Shape;
import sh.ball.util.ClassLoaderUtil;
public class SvgParserTest {
@ -20,28 +22,28 @@ public class SvgParserTest {
@Test
public void lineToGeneratesALineShape()
throws ParserConfigurationException, SAXException, IOException, URISyntaxException {
SvgParser svgParser = new SvgParser(getResource("images/line-to.svg"));
assertEquals(svgParser.nextFrame(), Line.pathToLines(0.5, 0.5, 0.75, 1, 0, 0, 0.5, 0.5));
FrameSet<List<Shape>> frames = new SvgParser(getResource("images/line-to.svg")).parse();
assertEquals(frames.next(), Line.pathToLines(0.5, 0.5, 0.75, 1, 0, 0, 0.5, 0.5));
}
@Test
public void horizontalLineToGeneratesAHorizontalLineShape()
throws ParserConfigurationException, SAXException, IOException, URISyntaxException {
SvgParser svgParser = new SvgParser(getResource("images/horizontal-line-to.svg"));
assertEquals(svgParser.nextFrame(), Line.pathToLines(0.5, 0.5, 0.75, 0.5, 0, 0.5, 0.5, 0.5));
FrameSet<List<Shape>> frames = new SvgParser(getResource("images/horizontal-line-to.svg")).parse();
assertEquals(frames.next(), Line.pathToLines(0.5, 0.5, 0.75, 0.5, 0, 0.5, 0.5, 0.5));
}
@Test
public void verticalLineToGeneratesAVerticalLineShape()
throws ParserConfigurationException, SAXException, IOException, URISyntaxException {
SvgParser svgParser = new SvgParser(getResource("images/vertical-line-to.svg"));
assertEquals(svgParser.nextFrame(), Line.pathToLines(0.5, 0.5, 0.5, 0.75, 0.5, 0, 0.5, 0.5));
FrameSet<List<Shape>> frames = new SvgParser(getResource("images/vertical-line-to.svg")).parse();
assertEquals(frames.next(), Line.pathToLines(0.5, 0.5, 0.5, 0.75, 0.5, 0, 0.5, 0.5));
}
@Test
public void closingASubPathDrawsLineToInitialPoint()
throws ParserConfigurationException, SAXException, IOException, URISyntaxException {
SvgParser svgParser = new SvgParser(getResource("images/closing-subpath.svg"));
assertEquals(svgParser.nextFrame(), Line.pathToLines(0.5, 0.5, 0.75, 0.5, 0.75, 0.75, 0.5, 0.75, 0.5, 0.5));
FrameSet<List<Shape>> frames = new SvgParser(getResource("images/closing-subpath.svg")).parse();
assertEquals(frames.next(), Line.pathToLines(0.5, 0.5, 0.75, 0.5, 0.75, 0.75, 0.5, 0.75, 0.5, 0.5));
}
}