Merge pull request #17 from jameshball/master

Version 1.0.1
pull/35/head
James H Ball 2021-05-05 21:01:20 +01:00 zatwierdzone przez GitHub
commit f2420e8563
9 zmienionych plików z 210 dodań i 48 usunięć

Wyświetl plik

@ -6,7 +6,7 @@
<groupId>sh.ball</groupId>
<artifactId>osci-render</artifactId>
<version>1.0</version>
<version>1.0.1</version>
<name>osci-render</name>

Wyświetl plik

@ -11,6 +11,7 @@ module oscirender {
requires java.data.front;
requires org.jgrapht.core;
requires org.unbescape.html;
requires java.desktop;
opens sh.ball.gui;
}

Wyświetl plik

@ -1,15 +1,8 @@
package sh.ball.audio;
import org.xml.sax.SAXException;
import sh.ball.FrameSet;
import sh.ball.Renderer;
import java.io.IOException;
import sh.ball.parser.FileParser;
import javax.xml.parsers.ParserConfigurationException;
public class FrameProducer<T> implements Runnable {
private final Renderer<T> renderer;
@ -17,9 +10,9 @@ public class FrameProducer<T> implements Runnable {
private boolean running;
public FrameProducer(Renderer<T> renderer, FileParser<FrameSet<T>> parser) throws IOException, SAXException, ParserConfigurationException {
public FrameProducer(Renderer<T> renderer, FrameSet<T> frames) {
this.renderer = renderer;
this.frames = parser.parse();
this.frames = frames;
}
@Override

Wyświetl plik

@ -4,6 +4,7 @@ import sh.ball.MovableRenderer;
import sh.ball.audio.AudioPlayer;
import sh.ball.audio.FrameProducer;
import java.awt.geom.RoundRectangle2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -29,6 +30,7 @@ import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sh.ball.engine.Vector3;
import sh.ball.shapes.ShapeFrameSet;
import sh.ball.parser.obj.ObjFrameSettings;
import sh.ball.parser.obj.ObjParser;
import sh.ball.parser.ParserFactory;
@ -45,7 +47,7 @@ public class Controller implements Initializable {
private FrameProducer<List<Shape>> producer = new FrameProducer<>(
renderer,
new ObjParser(DEFAULT_OBJ)
new ObjParser(DEFAULT_OBJ).parse()
);
private Stage stage;
@ -87,7 +89,7 @@ public class Controller implements Initializable {
@FXML
private TextField cameraZTextField;
public Controller() throws ParserConfigurationException, SAXException, IOException {
public Controller() throws IOException {
}
private Map<Slider, SliderUpdater<Double>> initializeSliderMap() {
@ -164,7 +166,7 @@ public class Controller implements Initializable {
String path = file.getAbsolutePath();
producer = new FrameProducer<>(
renderer,
ParserFactory.getParser(path)
ParserFactory.getParser(path).parse()
);
executor.submit(producer);

Wyświetl plik

@ -1,28 +1,136 @@
package sh.ball.parser.svg;
import java.awt.geom.Arc2D;
import java.util.ArrayList;
import java.util.List;
import sh.ball.shapes.Line;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
class EllipticalArcTo {
private static List<Shape> parseEllipticalArc(SvgState state, List<Float> args, boolean isAbsolute) {
// TODO: Properly implement
private static final int EXPECTED_ARGS = 7;
if (args.size() % 7 != 0 || args.size() < 7) {
private static List<Shape> parseEllipticalArc(SvgState state, List<Float> args, boolean isAbsolute) {
if (args.size() % EXPECTED_ARGS != 0 || args.size() < EXPECTED_ARGS) {
throw new IllegalArgumentException(
"SVG elliptical arc command has incorrect number of arguments.");
}
List<Float> lineToArgs = new ArrayList<>();
List<Shape> arcs = new ArrayList<>();
for (int i = 0; i < args.size(); i += 7) {
lineToArgs.add(args.get(i + 5));
lineToArgs.add(args.get(i + 6));
for (int i = 0; i < args.size(); i += EXPECTED_ARGS) {
Vector2 newPoint = new Vector2(args.get(i + 5), args.get(i + 6));
newPoint = isAbsolute ? newPoint : state.currPoint.translate(newPoint);
System.out.println(args.get(i + 2));
arcs.addAll(createArc(
state.currPoint,
args.get(i),
args.get(i + 1),
args.get(i + 2),
args.get(i + 3) == 1,
args.get(i + 4) == 1,
newPoint
));
state.currPoint = newPoint;
}
return isAbsolute ? LineTo.absolute(state, lineToArgs) : LineTo.relative(state, lineToArgs);
return arcs;
}
// The following algorithm is completely based on https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
private static List<Shape> createArc(Vector2 start, double rx, double ry, float theta, boolean largeArcFlag, boolean sweepFlag, Vector2 end) {
double x2 = end.getX();
double y2 = end.getY();
// Ensure radii are valid
if (rx == 0 || ry == 0) {
return List.of(new Line(start, end));
}
double x0 = start.getX();
double y0 = start.getY();
// Compute the half distance between the current and the final point
double dx2 = (x0 - x2) / 2.0f;
double dy2 = (y0 - y2) / 2.0f;
// Convert theta from degrees to radians
theta = (float) Math.toRadians(theta % 360);
//
// Step 1 : Compute (x1', y1')
//
double x1prime = Math.cos(theta) * dx2 + Math.sin(theta) * dy2;
double y1prime = -Math.sin(theta) * dx2 + Math.cos(theta) * dy2;
// Ensure radii are large enough
rx = Math.abs(rx);
ry = Math.abs(ry);
double Prx = rx * rx;
double Pry = ry * ry;
double Px1 = x1prime * x1prime;
double Py1 = y1prime * y1prime;
double d = Px1 / Prx + Py1 / Pry;
if (d > 1) {
rx = Math.abs(Math.sqrt(d) * rx);
ry = Math.abs(Math.sqrt(d) * ry);
Prx = rx * rx;
Pry = ry * ry;
}
//
// Step 2 : Compute (cx', cy')
//
double sign = (largeArcFlag == sweepFlag) ? -1.0 : 1.0;
double coef = sign *
Math.sqrt(((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)));
double cxprime = coef * ((rx * y1prime) / ry);
double cyprime = coef * -((ry * x1prime) / rx);
//
// Step 3 : Compute (cx, cy) from (cx', cy')
//
double sx2 = (x0 + x2) / 2.0f;
double sy2 = (y0 + y2) / 2.0f;
double cx = sx2 + Math.cos(theta) * cxprime - Math.sin(theta) * cyprime;
double cy = sy2 + Math.sin(theta) * cxprime + Math.cos(theta) * cyprime;
//
// Step 4 : Compute the angleStart (theta1) and the angleExtent (dtheta)
//
double ux = (x1prime - cxprime) / rx;
double uy = (y1prime - cyprime) / ry;
double vx = (-x1prime - cxprime) / rx;
double vy = (-y1prime - cyprime) / ry;
double p, n;
// Compute the angle start
n = Math.sqrt((ux * ux) + (uy * uy));
p = ux; // (1 * ux) + (0 * uy)
sign = (uy < 0) ? -1.0 : 1.0;
double angleStart = Math.toDegrees(sign * Math.acos(p / n));
// Compute the angle extent
n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
p = ux * vx + uy * vy;
sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
double angleExtent = Math.toDegrees(sign * Math.acos(p / n));
if (!sweepFlag && angleExtent > 0) {
angleExtent -= 360;
} else if (sweepFlag && angleExtent < 0) {
angleExtent += 360;
}
angleExtent %= 360;
angleStart %= 360;
Arc2D.Float arc = new Arc2D.Float();
arc.x = (float) (cx - rx);
arc.y = (float) (cy - ry);
arc.width = (float) (rx * 2.0f);
arc.height = (float) (ry * 2.0f);
arc.start = (float) -angleStart;
arc.extent = (float) -angleExtent;
return Shape.convert(arc);
}
static List<Shape> absolute(SvgState state, List<Float> args) {

Wyświetl plik

@ -25,7 +25,7 @@ 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.ShapeFrameSet;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;

Wyświetl plik

@ -12,7 +12,7 @@ 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.shapes.ShapeFrameSet;
import sh.ball.parser.svg.SvgParser;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;

Wyświetl plik

@ -1,9 +1,13 @@
package sh.ball.shapes;
import sh.ball.FrameSet;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.util.ArrayList;
import java.util.List;
public abstract class Shape {
public abstract class Shape implements FrameSet<List<Shape>> {
public static final int DEFAULT_WEIGHT = 80;
@ -30,8 +34,62 @@ public abstract class Shape {
return length;
}
@Override
public List<Shape> next() {
return List.of(this);
}
@Override
public void setFrameSettings(Object settings) {}
/* SHAPE HELPER FUNCTIONS */
public static List<Shape> convert(java.awt.Shape shape) {
List<Shape> shapes = new ArrayList<>();
Vector2 moveToPoint = new Vector2();
Vector2 currentPoint = new Vector2();
float[] coords = new float[6];
PathIterator pathIterator = shape.getPathIterator(new AffineTransform());
while (!pathIterator.isDone()) {
switch (pathIterator.currentSegment(coords)) {
case PathIterator.SEG_MOVETO -> {
currentPoint = new Vector2(coords[0], coords[1]);
moveToPoint = currentPoint;
}
case PathIterator.SEG_LINETO -> {
shapes.add(new Line(
currentPoint,
new Vector2(coords[0], coords[1])
));
currentPoint = new Vector2(coords[0], coords[1]);
}
case PathIterator.SEG_QUADTO -> {
shapes.add(new QuadraticBezierCurve(
currentPoint,
new Vector2(coords[0], coords[1]),
new Vector2(coords[2], coords[3])
));
currentPoint = new Vector2(coords[2], coords[3]);
}
case PathIterator.SEG_CUBICTO -> {
shapes.add(new CubicBezierCurve(
currentPoint,
new Vector2(coords[0], coords[1]),
new Vector2(coords[2], coords[3]),
new Vector2(coords[4], coords[5])
));
currentPoint = new Vector2(coords[4], coords[5]);
}
case PathIterator.SEG_CLOSE ->
shapes.add(new Line(currentPoint, moveToPoint));
}
pathIterator.next();
}
return shapes;
}
// Normalises sh.ball.shapes between the coords -1 and 1 for proper scaling on an oscilloscope. May not
// work perfectly with curves that heavily deviate from their start and end points.
public static List<Shape> normalize(List<Shape> shapes) {

Wyświetl plik

@ -1,24 +1,24 @@
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;
}
@Override
public void setFrameSettings(Object settings) {
}
}
package sh.ball.shapes;
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;
}
@Override
public void setFrameSettings(Object settings) {
}
}