kopia lustrzana https://github.com/jameshball/osci-render
				
				
				
			
						commit
						f2420e8563
					
				
							
								
								
									
										2
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										2
									
								
								pom.xml
								
								
								
								
							|  | @ -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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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) { | ||||
|   } | ||||
| } | ||||
		Ładowanie…
	
		Reference in New Issue
	
	 James H Ball
						James H Ball