import javafx.application.*; import javafx.animation.*; import javafx.util.*; import javafx.stage.*; import javafx.scene.*; import javafx.scene.shape.*; import javafx.scene.paint.*; import javafx.scene.transform.*; import javafx.event.*; import javafx.scene.shape.StrokeType; import com.sun.javafx.perf.PerformanceTracker; public class BenchShapes extends Application { public static enum ShapeType { RECT, RRECT, CIRCLE, OVAL, TRIANGLE, TRI_QUAD, TRI_CUBIC, RECT_PATH, RECT_QUAD, RECT_CUBIC, POLYGON, POLY_QUAD, POLY_CUBIC, SUPER_POLY, }; static int NSHAPES = 200; static double DIM = 200; static double HDIM = DIM/2.0; static ShapeType shapetype = ShapeType.RECT; static StrokeType stroketype = StrokeType.CENTERED; static boolean domarkup = false; static boolean dograd = false; static boolean dodashes = false; static boolean randomize = false; static double degoff = 0.0; Group root; Rotate rotator; public void start(Stage stage) { stage.setTitle("Inner/Outer stroke test"); root = new Group(); double dist = (900 - DIM*1.5) / 2.0; if (dist < 0) dist = 0; double sdim = dist*2 + DIM * 1.5; if (sdim < 900) sdim = 900; Scene scene = new Scene(root, sdim, sdim); scene.setFill(Color.WHITE); for (int i = 0; i < NSHAPES; i++) { double deg = degoff + i * 360f / NSHAPES; double rad = Math.toRadians(deg); double cx = sdim/2.0 + Math.cos(rad) * dist; double cy = sdim/2.0 + Math.sin(rad) * dist; Paint p; if (dograd) { Color c1 = Color.hsb(deg, 1.0, 0.2); Color c2 = Color.hsb(deg, 1.0, 1.0); Color c3 = Color.hsb(deg, 1.0, 0.2); p = new LinearGradient(0, 0, 1, 1, true, CycleMethod.REFLECT, new Stop[] { new Stop(0.0f, c1), new Stop(0.5f, c2), new Stop(1.0f, c3), }); } else { p = Color.hsb(deg, 1.0, 1.0); } double dim = randomize ? DIM + Math.random() : DIM; double hdim = dim/2.0; Node n = makeShape(cx, cy, hdim, dim, 10, p); n.setRotate(-deg); root.getChildren().add(n); } rotator = Transform.rotate(0, sdim/2.0, sdim/2.0); root.getTransforms().add(rotator); stage.setScene(scene); stage.setVisible(true); perfTracker = PerformanceTracker.getSceneTracker(scene); initTimelines(); } public void initTimelines() { new AnimationTimer() { public void handle(long now) { rotator.setAngle(rotator.getAngle()+1); } }.start(); Timeline fpsTimeline = new Timeline(); fpsTimeline.setCycleCount(Timeline.INDEFINITE); EventHandler<ActionEvent> handler = new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { updateFPS(); } }; KeyFrame fpsKeyFrame = new KeyFrame(Duration.valueOf(2000), handler); fpsTimeline.getKeyFrames().add(fpsKeyFrame); fpsTimeline.play(); } private PerformanceTracker perfTracker; void updateFPS() { String fpsText = String.format("FPS: %.2f", perfTracker.getInstantFPS()); //fpsLabel.setText(fpsText); System.out.println(fpsText); } public Node makeShape(double cx, double cy, double hdim, double dim, float lw, Paint p) { switch (shapetype) { case RECT: return makeRect(cx-hdim, cy-hdim, dim, dim, 0, 0, lw, p); case RRECT: return makeRect(cx-hdim, cy-hdim, dim, dim, hdim, hdim, lw, p); case CIRCLE: return makeCircle(cx, cy, hdim, lw, p); case OVAL: return makeEllipse(cx, cy, hdim, hdim, lw, p); case RECT_PATH: return makeRectPath(cx-hdim, cy-hdim, dim, dim, lw, p); case RECT_QUAD: return makeQuadRect(cx-hdim, cy-hdim, dim, dim, lw, p); case RECT_CUBIC: return makeCubicRect(cx-hdim, cy-hdim, dim, dim, lw, p); case TRIANGLE: return makeTriangle(cx-hdim, cy-hdim, dim, dim, lw, p); case TRI_QUAD: return makeQuadTriangle(cx-hdim, cy-hdim, dim, dim, lw, p); case TRI_CUBIC: return makeCubicTriangle(cx-hdim, cy-hdim, dim, dim, lw, p); case POLYGON: return makePolygon(cx-hdim, cy-hdim, dim, dim, lw, p, 90 + 9); case POLY_QUAD: return makeQuadPoly(cx-hdim, cy-hdim, dim, dim, lw, p); case POLY_CUBIC: return makeCubicPoly(cx-hdim, cy-hdim, dim, dim, lw, p); case SUPER_POLY: return makePolygon(cx-hdim, cy-hdim, dim, dim, lw, p, 131); default: throw new IllegalArgumentException("Unrecognized shape type: "+ shapetype); } } public Node makeRect(double x, double y, double w, double h, double aw, double ah, float lw, Paint p) { Rectangle r = new Rectangle(); r.setX(x); r.setY(y); r.setWidth(w); r.setHeight(h); if (aw > 0 && ah > 0) { r.setArcWidth(aw); r.setArcHeight(ah); } return finish(r, x, y, w, h, lw, p); } public Node makeCircle(double cx, double cy, double r, float lw, Paint p) { Circle c = new Circle(); c.setCenterX(cx); c.setCenterY(cy); c.setRadius(r); return finish(c, cx - r, cy - r, r*2, r*2, lw, p); } public Node makeEllipse(double cx, double cy, double rx, double ry, float lw, Paint p) { Ellipse e = new Ellipse(); e.setCenterX(cx); e.setCenterY(cy); e.setRadiusX(rx); e.setRadiusY(ry); return finish(e, cx - rx, cy - ry, rx*2, ry*2, lw, p); } public Node makeRectPath(double x, double y, double w, double h, float lw, Paint p) { double hw = w / 2.0; double hh = h / 2.0; double qw = hw / 2.0; double qh = hh / 2.0; Path path = new Path(); path.getElements().add(new MoveTo(x , y )); path.getElements().add(new LineTo(x+hw , y+qh )); path.getElements().add(new LineTo(x+w , y )); path.getElements().add(new LineTo(x+w-qw, y+hh )); path.getElements().add(new LineTo(x+w , y+h )); path.getElements().add(new LineTo(x+hw , y+h-qh)); path.getElements().add(new LineTo(x , y+h )); path.getElements().add(new LineTo(x+qw , y+hh )); path.getElements().add(new ClosePath()); return finish(path, x, y, w, h, lw, p); } public Node makeQuadRect(double x, double y, double w, double h, float lw, Paint p) { double hw = w / 2.0; double hh = h / 2.0; double qw = hw / 2.0; double qh = hh / 2.0; Path curve = new Path(); curve.getElements().add(new MoveTo(x, y)); curve.getElements().add(new QuadCurveTo(x +hw, y +qh, x+w, y )); curve.getElements().add(new QuadCurveTo(x+w-qw, y +hh, x+w, y+h)); curve.getElements().add(new QuadCurveTo(x +hw, y+h-qh, x , y+h)); curve.getElements().add(new QuadCurveTo(x +qw, y +hh, x , y )); curve.getElements().add(new ClosePath()); return finish(curve, x, y, w, h, lw, p); } public Node makeCubicRect(double x, double y, double w, double h, float lw, Paint p) { double hw = w / 2.0; double hh = h / 2.0; double qw = hw / 2.0; double qh = hh / 2.0; Path curve = new Path(); curve.getElements().add(new MoveTo(x, y)); curve.getElements().add(new CubicCurveTo(x +qw, y +qh, x+w-qw, y +qh, x+w, y )); curve.getElements().add(new CubicCurveTo(x+w-qw, y +qh, x+w-qw, y+h-qh, x+w, y+h)); curve.getElements().add(new CubicCurveTo(x+w-qw, y+h-qh, x +qw, y+h-qh, x , y+h)); curve.getElements().add(new CubicCurveTo(x +qw, y+h-qh, x +qw, y +qh, x , y )); curve.getElements().add(new ClosePath()); return finish(curve, x, y, w, h, lw, p); } public Node makeTriangle(double x, double y, double w, double h, float lw, Paint p) { Path tri = new Path(); tri.getElements().add(new MoveTo(x + w/2.0, y)); tri.getElements().add(new LineTo(x + w, y + h)); tri.getElements().add(new LineTo(x, y + h)); tri.getElements().add(new ClosePath()); return finish(tri, x, y, w, h, lw, p); } public Node makeQuadTriangle(double x, double y, double w, double h, float lw, Paint p) { Path tri = new Path(); double cx = x + w/2.0; double cy = y + h/2.0; tri.getElements().add(new MoveTo(cx, y)); tri.getElements().add(new QuadCurveTo(cx, cy, x + w, y + h)); tri.getElements().add(new QuadCurveTo(cx, cy, x, y + h)); tri.getElements().add(new QuadCurveTo(cx, cy, cx, y)); tri.getElements().add(new ClosePath()); return finish(tri, x, y, w, h, lw, p); } public Node makeCubicTriangle(double x, double y, double w, double h, float lw, Paint p) { Path tri = new Path(); double cx = x + w/2.0; double cy = y + h/2.0; double qw = w / 4.0; double qh = h / 4.0; tri.getElements().add(new MoveTo(cx, y)); tri.getElements().add(new CubicCurveTo(x+w, y, cx, cy, x + w, y + h)); tri.getElements().add(new CubicCurveTo(x+w-qw, y+h-qh, x+qw, y+h-qw, x, y + h)); tri.getElements().add(new CubicCurveTo(cx, cy, x, y, cx, y)); tri.getElements().add(new ClosePath()); return finish(tri, x, y, w, h, lw, p); } public Node makePolygon(double x, double y, double w, double h, float lw, Paint p, int inc) { Path poly = new Path(); double hw = w/2.0; double hh = h/2.0; double cx = x + hw; double cy = y + hh; int angle = inc; poly.getElements().add(new MoveTo(cx+hw, cy)); while (true) { double cos = Math.cos(Math.toRadians(angle)); double sin = Math.sin(Math.toRadians(angle)); double vx = cx + cos * hw; double vy = cy + sin * hh; poly.getElements().add(new LineTo(vx, vy)); angle += inc; if (angle >= 360) { angle -= 360; if (angle == 0) { break; } } } poly.getElements().add(new ClosePath()); return finish(poly, x, y, w, h, lw, p); } public Node makeQuadPoly(double x, double y, double w, double h, float lw, Paint p) { Path poly = new Path(); double hw = w/2.0; double hh = h/2.0; double cx = x + hw; double cy = y + hh; int inc = 90+9; int angle = inc; poly.getElements().add(new MoveTo(cx+hw, cy)); while (true) { double cos = Math.cos(Math.toRadians(angle)); double sin = Math.sin(Math.toRadians(angle)); double vx = cx + cos * hw; double vy = cy + sin * hh; poly.getElements().add(new QuadCurveTo(cx, cy, vx, vy)); if (angle == 0) { break; } angle += inc; if (angle >= 360) { angle -= 360; } } poly.getElements().add(new ClosePath()); return finish(poly, x, y, w, h, lw, p); } public Node makeCubicPoly(double x, double y, double w, double h, float lw, Paint p) { Path poly = new Path(); double hw = w/2.0; double hh = h/2.0; double cx = x + hw; double cy = y + hh; int inc = 90+9; int angle = inc; poly.getElements().add(new MoveTo(cx+hw, cy)); double odx = hw; double ody = 0; while (true) { double cos = Math.cos(Math.toRadians(angle)); double sin = Math.sin(Math.toRadians(angle)); double dx = cos * hw; double dy = sin * hh; poly.getElements().add(new CubicCurveTo(cx+odx/2.0, cy+ody/2.0, cx+ dx/2.0, cy+ dy/2.0, cx+ dx, cy+ dy)); odx = dx; ody = dy; if (angle == 0) { break; } angle += inc; if (angle >= 360) { angle -= 360; } } poly.getElements().add(new ClosePath()); return finish(poly, x, y, w, h, lw, p); } public Node finish(Shape s, double x, double y, double w, double h, float lw, Paint p) { if (stroketype == null) { s.setFill(p); s.setStroke(null); } else { s.setFill(null); s.setStroke(p); s.setStrokeWidth(lw); s.setStrokeType(stroketype); if (dodashes) { s.getStrokeDashArray().add(10.0); s.getStrokeDashArray().add(10.0); s.setStrokeLineCap(StrokeLineCap.BUTT); } } return markup(s, x, y, w, h, lw); } public Node markup(Node n, double x, double y, double w, double h, float lw) { if (!domarkup) { return n; } float off1, off2; if (stroketype == StrokeType.INSIDE) { off1 = -1.0f; off2 = -0.5f; } else if (stroketype == StrokeType.CENTERED) { off1 = -0.5f; off2 = +0.5f; } else if (stroketype == StrokeType.OUTSIDE) { off1 = +0.5f; off2 = +1.0f; } else if (stroketype == null) { off1 = off2 = 0f; } else { throw new IllegalArgumentException("Unrecognized StrokeType: "+ stroketype); } off1 *= lw; off2 *= lw; Rectangle r0 = new Rectangle(); r0.setX(x); r0.setY(y); r0.setWidth(w); r0.setHeight(h); r0.setFill(null); r0.setStroke(Color.GREEN); r0.setStrokeWidth(2); Group g = new Group(); g.getChildren().add(n); g.getChildren().add(r0); if (stroketype != null) { Rectangle r1 = new Rectangle(); r1.setX(x-off1); r1.setY(y-off1); r1.setWidth(w+off1+off1); r1.setHeight(h+off1+off1); r1.setFill(null); r1.setStroke(Color.RED); r1.setStrokeWidth(2); Rectangle r2 = new Rectangle(); r2.setX(x-off2); r2.setY(y-off2); r2.setWidth(w+off2+off2); r2.setHeight(h+off2+off2); r2.setFill(null); r2.setStroke(Color.RED); r2.setStrokeWidth(2); g.getChildren().add(r1); g.getChildren().add(r2); } return g; } public static void main(String argv[]) { for (String arg: argv) { if (arg.startsWith("out")) { stroketype = StrokeType.OUTSIDE; } else if (arg.startsWith("in")) { stroketype = StrokeType.INSIDE; } else if (arg.startsWith("center")) { stroketype = StrokeType.CENTERED; } else if (arg.startsWith("fill")) { stroketype = null; } else if (arg.startsWith("dash")) { dodashes = true; } else if (arg.startsWith("grad")) { dograd = true; } else if (arg.startsWith("rect")) { shapetype = ShapeType.RECT; } else if (arg.startsWith("rrect") || arg.startsWith("round")) { shapetype = ShapeType.RRECT; } else if (arg.startsWith("circle")) { shapetype = ShapeType.CIRCLE; } else if (arg.startsWith("oval") || arg.startsWith("ellipse")) { shapetype = ShapeType.OVAL; } else if (arg.startsWith("path")) { shapetype = ShapeType.RECT_PATH; } else if (arg.startsWith("quadpath")) { shapetype = ShapeType.RECT_QUAD; } else if (arg.startsWith("cubicpath")) { shapetype = ShapeType.RECT_CUBIC; } else if (arg.startsWith("tri")) { shapetype = ShapeType.TRIANGLE; } else if (arg.startsWith("quadtri")) { shapetype = ShapeType.TRI_QUAD; } else if (arg.startsWith("cubictri")) { shapetype = ShapeType.TRI_CUBIC; } else if (arg.startsWith("poly")) { shapetype = ShapeType.POLYGON; } else if (arg.startsWith("quadpoly")) { shapetype = ShapeType.POLY_QUAD; } else if (arg.startsWith("cubicpoly")) { shapetype = ShapeType.POLY_CUBIC; } else if (arg.startsWith("superpoly")) { shapetype = ShapeType.SUPER_POLY; } else if (arg.startsWith("rand")) { randomize = true; } else if (arg.startsWith("num=")) { NSHAPES = Integer.parseInt(arg.substring(4)); } else if (arg.startsWith("dim=")) { DIM = Integer.parseInt(arg.substring(4)); HDIM = DIM/2.0; } else if (arg.startsWith("deg=")) { degoff = Double.parseDouble(arg.substring(4)); } else { throw new IllegalArgumentException("Unrecognized argument: "+arg); } } Launcher.launch(BenchShapes.class, argv); } }