diff --git a/jzy3d-core/src/main/java/org/jzy3d/chart/Chart.java b/jzy3d-core/src/main/java/org/jzy3d/chart/Chart.java index 71920c3a..4e2c3dc0 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/chart/Chart.java +++ b/jzy3d-core/src/main/java/org/jzy3d/chart/Chart.java @@ -405,6 +405,13 @@ public class Chart { public void remove(Drawable drawable, boolean updateViews) { getScene().getGraph().remove(drawable, updateViews); } + + public void remove(List drawables) { + for (Drawable drawable : drawables) { + remove(drawable, false); + } + getView().updateBounds(); + } /* ADDING LIGHTS */ diff --git a/jzy3d-core/src/main/java/org/jzy3d/colors/Color.java b/jzy3d-core/src/main/java/org/jzy3d/colors/Color.java index f23ebf04..6affdf2b 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/colors/Color.java +++ b/jzy3d-core/src/main/java/org/jzy3d/colors/Color.java @@ -12,26 +12,19 @@ import java.util.Random; */ public class Color { - /** Black color. */ public static final Color BLACK = new Color(0.0f, 0.0f, 0.0f); - /** White color. */ public static final Color WHITE = new Color(1.0f, 1.0f, 1.0f); - /** Gray color. */ public static final Color GRAY = new Color(0.5f, 0.5f, 0.5f); - /** Red color. */ public static final Color RED = new Color(1.0f, 0.0f, 0.0f); - /** Green color. */ public static final Color GREEN = new Color(0.0f, 1.0f, 0.0f); - /** Blue color. */ public static final Color BLUE = new Color(0.0f, 0.0f, 1.0f); - /** Yellow color. */ public static final Color YELLOW = new Color(1.0f, 1.0f, 0.0f); - /** Magenta color. */ public static final Color MAGENTA = new Color(1.0f, 0.0f, 1.0f); - /** Cyan color. */ public static final Color CYAN = new Color(0.0f, 1.0f, 1.0f); + + public static final Color ORANGE = new Color(1.0f, 165.0f/255.0f, 0); public static final Color[] COLORS = {RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN}; diff --git a/jzy3d-core/src/main/java/org/jzy3d/maths/Angle3d.java b/jzy3d-core/src/main/java/org/jzy3d/maths/Angle3d.java index b254545b..faba7f46 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/maths/Angle3d.java +++ b/jzy3d-core/src/main/java/org/jzy3d/maths/Angle3d.java @@ -1,10 +1,29 @@ package org.jzy3d.maths; +import java.util.List; +import org.jzy3d.plot3d.primitives.Point; + /** * An Angle3d stores three 3d points, considering the angle is on the second one. An instance may * return angle(), cos() and sin(). */ public class Angle3d { + public static final double DEGREE_90_D = Math.PI / 2; + public static final float DEGREE_90 = (float) DEGREE_90_D; + + public static final double DEGREE_45_D = Math.PI / 4; + public static final float DEGREE_45 = (float) DEGREE_45_D; + + protected float x1; + protected float x2; + protected float x3; + protected float y1; + protected float y2; + protected float y3; + protected float z1; + protected float z2; + protected float z3; + /** * Create an angle, described by three points. The angle is supposed to be on p2 @@ -68,23 +87,96 @@ public class Angle3d { /** Computes the angle at vertex p2 between rays p1,p2 and p3,p2. Returns 0 to PI radians. */ public float angle() { + return (float) angleD(); + } + + /** Computes the angle at vertex p2 between rays p1,p2 and p3,p2. Returns 0 to PI radians. */ + public double angleD() { double lenP1P3 = Math.sqrt(Math.pow(x1 - x3, 2) + Math.pow(y1 - y3, 2) + Math.pow(z1 - z3, 2)); double lenP1P2 = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2)); double lenP3P2 = Math.sqrt(Math.pow(x3 - x2, 2) + Math.pow(y3 - y2, 2) + Math.pow(z3 - z2, 2)); double numerator = Math.pow(lenP1P2, 2) + Math.pow(lenP3P2, 2) - Math.pow(lenP1P3, 2); double denominator = 2 * lenP1P2 * lenP3P2; - return (float) Math.acos(numerator / denominator); + return Math.acos(numerator / denominator); } + + + /** + * Compute the sum of all angles in the input coordinate list. + * + * If input contains point A, B, C, this method will compute angles ABC, BCA, CAB. + * + * An error is thrown if there are less than 3 points. + * + * + * + */ + public static double angleSum(List coords) { + if(coords.size()<3) { + throw new IllegalArgumentException("Can not compute an angle with less than 3 points"); + } + + Angle3d[] angles = new Angle3d[coords.size()]; + + for (int i = 0; i < coords.size()-2; i++) { + angles[i] = new Angle3d(coords.get(i), coords.get(i+1), coords.get(i+2)); + } + angles[coords.size()-2] = new Angle3d(coords.get(coords.size()-2), coords.get(coords.size()-1), coords.get(0)); + angles[coords.size()-1] = new Angle3d(coords.get(coords.size()-1), coords.get(0), coords.get(1)); - /***********************************************************/ + + double cumulated = 0; + for(Angle3d angle: angles) { + cumulated+=angle.angleD(); + } + return cumulated; + } + + public static double angleSumFromPoints(List coords) { + if(coords.size()<3) { + throw new IllegalArgumentException("Can not compute an angle with less than 3 points"); + } + + Angle3d[] angles = new Angle3d[coords.size()]; + + for (int i = 0; i < coords.size()-2; i++) { + angles[i] = new Angle3d(coords.get(i).xyz, coords.get(i+1).xyz, coords.get(i+2).xyz); + } + angles[coords.size()-2] = new Angle3d(coords.get(coords.size()-2).xyz, coords.get(coords.size()-1).xyz, coords.get(0).xyz); + angles[coords.size()-1] = new Angle3d(coords.get(coords.size()-1).xyz, coords.get(0).xyz, coords.get(1).xyz); - private float x1; - private float x2; - private float x3; - private float y1; - private float y2; - private float y3; - private float z1; - private float z2; - private float z3; + + double cumulated = 0; + for(Angle3d angle: angles) { + cumulated+=angle.angleD(); + } + return cumulated; + } + + /** + * Returns the expected sum of all angles of a polygon given its number of points. + * + * The polygon is supposed to not have any edge crossing another edge. + * + * @param n + * @return + */ + public static double angleSumOfNonIntersectingPolygon(int n) { + if(n<3) + return 0; + else + return (n-2)*180; + } + + public static double angleSumOfNonIntersectingPolygonRad(int n) { + if(n<3) + return 0; + else + return (n-2)*Math.PI; + } + + public static boolean angleSumFromPointsOfNonIntersectingPolygon(List coords) { + double diff= Math.abs(angleSumFromPoints(coords)-angleSumOfNonIntersectingPolygonRad(coords.size())); + return diff<0.0001; + } } diff --git a/jzy3d-core/src/main/java/org/jzy3d/maths/Coord3d.java b/jzy3d-core/src/main/java/org/jzy3d/maths/Coord3d.java index 013ef2d2..58c47d51 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/maths/Coord3d.java +++ b/jzy3d-core/src/main/java/org/jzy3d/maths/Coord3d.java @@ -21,6 +21,10 @@ public class Coord3d implements Serializable { */ private static final long serialVersionUID = -1636927109633279805L; + public float x; + public float y; + public float z; + public static List list(int size) { return new ArrayList(size); } @@ -254,6 +258,13 @@ public class Coord3d implements Serializable { z /= c2.z; } + public void divSelf(float value) { + x /= value; + y /= value; + z /= value; + } + + /** * Divise all components of the current Coord by the same value and return the result in a new * Coord3d. @@ -376,6 +387,7 @@ public class Coord3d implements Serializable { return Math.sqrt(distanceSq(c)); } + /** Compute the square distance between two coordinates. */ public double distanceSq(Coord3d c) { return Math.pow(x - c.x, 2) + Math.pow(y - c.y, 2) + Math.pow(z - c.z, 2); } @@ -434,7 +446,7 @@ public class Coord3d implements Serializable { public final Coord3d interpolateTo(Coord3d v, float f) { return new Coord3d(x + (v.x - x) * f, y + (v.y - y) * f, z + (v.z - z) * f); } - + /**************************************************************/ /** Return a string representation of this coordinate. */ @@ -605,9 +617,4 @@ public class Coord3d implements Serializable { return clone; } - /**************************************************************/ - - public float x; - public float y; - public float z; } diff --git a/jzy3d-core/src/main/java/org/jzy3d/maths/Permutations.java b/jzy3d-core/src/main/java/org/jzy3d/maths/Permutations.java new file mode 100644 index 00000000..04debe37 --- /dev/null +++ b/jzy3d-core/src/main/java/org/jzy3d/maths/Permutations.java @@ -0,0 +1,35 @@ +package org.jzy3d.maths; + +import java.util.ArrayList; +import java.util.List; + +/** + * Generate all possible permutations of the input list + * + * May study https://www.quickperm.org/ as an faster alternative. + * + * @author martin + * + */ +public class Permutations { + public static List> of(List original) { + if (original.isEmpty()) { + List> result = new ArrayList<>(); + result.add(new ArrayList<>()); + return result; + } + else { + E firstElement = original.remove(0); + List> returnValue = new ArrayList<>(); + List> permutations = of(original); + for (List smallerPermutated : permutations) { + for (int index = 0; index <= smallerPermutated.size(); index++) { + List temp = new ArrayList<>(smallerPermutated); + temp.add(index, firstElement); + returnValue.add(temp); + } + } + return returnValue; + } + } +} diff --git a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Geometry.java b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Geometry.java index 1ca67137..83ecf45a 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Geometry.java +++ b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Geometry.java @@ -34,6 +34,11 @@ public abstract class Geometry extends Wireframeable implements ISingleColorable add(points); } + public Geometry(List points) { + this(); + add(points); + } + public Geometry(Color wireframeColor, Point... points) { this(points); setWireframeColor(wireframeColor); @@ -188,6 +193,13 @@ public abstract class Geometry extends Wireframeable implements ISingleColorable updateBounds(); } + public void add(List points) { + for(Point p: points) { + add(p, false); + } + updateBounds(); + } + @Override public void applyGeometryTransform(Transform transform) { diff --git a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Polygon.java b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Polygon.java index 87b4f65f..c195a1b4 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Polygon.java +++ b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Polygon.java @@ -1,5 +1,6 @@ package org.jzy3d.plot3d.primitives; +import java.util.List; import org.jzy3d.colors.Color; import org.jzy3d.painters.IPainter; @@ -22,6 +23,10 @@ public class Polygon extends Geometry { super(points); } + public Polygon(List points) { + super(points); + } + public Polygon(Color wire, Point... points) { super(wire, points); } diff --git a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Scatter.java b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Scatter.java index bd0d00da..c87da5f2 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Scatter.java +++ b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/Scatter.java @@ -11,7 +11,9 @@ import org.jzy3d.painters.IPainter; import org.jzy3d.plot3d.transform.Transform; /** - * Experimental 3d object. + * A collection of coordinates rendered as dots. + * + * Warning : dots having a width of 1 may not be visible in case HiDPI is ON. * * @author Martin Pernollet * @@ -28,17 +30,33 @@ public class Scatter extends Drawable implements ISingleColorable { this(coordinates, Color.BLACK); } + public Scatter(List coordinates) { + this(coordinates, Color.BLACK); + } + public Scatter(Coord3d[] coordinates, Color rgb) { this(coordinates, rgb, 1.0f); } + public Scatter(List coordinates, Color rgb) { + this(coordinates, rgb, 1.0f); + } + public Scatter(Coord3d[] coordinates, Color rgb, float width) { - bbox = new BoundingBox3d(); + this(); setData(coordinates); setWidth(width); setColor(rgb); } + public Scatter(List coordinates, Color rgb, float width) { + this(); + setData(coordinates); + setWidth(width); + setColor(rgb); + } + + public Scatter(Coord3ds coords) { this(coords.coordsArray(), coords.colorsArray()); } diff --git a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/axis/layout/renderers/IntegerTickRenderer.java b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/axis/layout/renderers/IntegerTickRenderer.java index 2dcddb97..1572b85e 100644 --- a/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/axis/layout/renderers/IntegerTickRenderer.java +++ b/jzy3d-core/src/main/java/org/jzy3d/plot3d/primitives/axis/layout/renderers/IntegerTickRenderer.java @@ -1,10 +1,26 @@ package org.jzy3d.plot3d.primitives.axis.layout.renderers; +import java.text.NumberFormat; +import java.util.Locale; + public class IntegerTickRenderer implements ITickRenderer { - public IntegerTickRenderer() {} + boolean separator = false; + + public IntegerTickRenderer() { + this(false); + } + + public IntegerTickRenderer(boolean separator) { + this.separator = separator; + } + + @Override public String format(double value) { - return "" + (int) value; + if(separator) + return NumberFormat.getNumberInstance(Locale.US).format((int) value); + else + return ""+(int) value; } } diff --git a/jzy3d-core/src/test/java/org/jzy3d/maths/TestAngle3d.java b/jzy3d-core/src/test/java/org/jzy3d/maths/TestAngle3d.java new file mode 100644 index 00000000..14a928e6 --- /dev/null +++ b/jzy3d-core/src/test/java/org/jzy3d/maths/TestAngle3d.java @@ -0,0 +1,110 @@ +package org.jzy3d.maths; + +import org.junit.Assert; +import org.junit.Test; +import org.jzy3d.colors.Color; +import org.jzy3d.plot3d.primitives.Point; +import org.jzy3d.plot3d.primitives.Polygon; + +public class TestAngle3d { + @Test + public void testAngle90and45_Vertical() { + + // Given on horizontal square + Polygon p1 = new Polygon(); + p1.add(new Point(new Coord3d(0, 0, 0), Color.BLUE)); // P1 left bottom + p1.add(new Point(new Coord3d(1, 0, 0), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(1, 1, 0), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(0, 1, 0), Color.BLUE)); // P1 left top + + + // When computing angle + + Angle3d a1 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(2).xyz); + + + Angle3d a2 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(3).xyz); + + // Then + Assert.assertEquals(Angle3d.DEGREE_90, a1.angle(), 0.0001); + Assert.assertEquals(Angle3d.DEGREE_45, a2.angle(), 0.0001); + + } + + @Test + public void testAngle90and45_Horizontal() { + + // Given on horizontal square + Polygon p1 = new Polygon(); + p1.add(new Point(new Coord3d(0, 0, 0), Color.BLUE)); // P1 left bottom + p1.add(new Point(new Coord3d(0, 0, 1), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(0, 1, 1), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(0, 1, 0), Color.BLUE)); // P1 left top + + + // When computing angle + + Angle3d a1 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(2).xyz); + + + Angle3d a2 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(3).xyz); + + // Then + Assert.assertEquals(Angle3d.DEGREE_90, a1.angle(), 0.0001); + Assert.assertEquals(Angle3d.DEGREE_45, a2.angle(), 0.0001); + + } + + @Test + public void whenSumingAnglesOfPolygon_TriangleIs180_SquareIs360() { + // square + Coord3d c1 = new Coord3d(0,0,0); // bottom left + Coord3d c2 = new Coord3d(1,0,0); // bottom right + Coord3d c3 = new Coord3d(1,1,0); // top right + Coord3d c4 = new Coord3d(0,1,0); + + Assert.assertEquals(2*Math.PI, Angle3d.angleSum(Coord3d.list(c1,c2,c3,c4)), 0.0001); + Assert.assertEquals(Math.PI, Angle3d.angleSum(Coord3d.list(c1,c2,c3)), 0.0001); + } + + + @Test(expected=IllegalArgumentException.class) + public void sumOfIncompleteAngles() { + Angle3d.angleSum(Coord3d.list(Coord3d.IDENTITY)); + } + + @Test(expected=IllegalArgumentException.class) + public void sumOfIncompleteAngles2() { + Angle3d.angleSum(Coord3d.list(Coord3d.IDENTITY, Coord3d.IDENTITY)); + } + + @Test + public void sumOfAnglesExpect() { + Assert.assertEquals(180, Angle3d.angleSumOfNonIntersectingPolygon(3), 0.0001); + Assert.assertEquals(360, Angle3d.angleSumOfNonIntersectingPolygon(4), 0.0001); + } + + /*@Test + public void testAngle90and45_nonPlanar() { + + // Given + Polygon p1 = new Polygon(); + p1.add(new Point(new Coord3d(0, 0, 0), Color.BLUE)); // P1 left bottom + p1.add(new Point(new Coord3d(1, 0, 1), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(1, 1, 0), Color.BLUE)); // neighbor edge will be dropped + p1.add(new Point(new Coord3d(0, 1, 0), Color.BLUE)); // P1 left top + + + // When computing angle + + Angle3d a1 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(2).xyz); + + + Angle3d a2 = new Angle3d(p1.get(0).xyz, p1.get(1).xyz, p1.get(3).xyz); + + // Then + Assert.assertEquals(Angle3d.DEGREE_90, a1.angle(), 0.0001); + Assert.assertEquals(Angle3d.DEGREE_45, a2.angle(), 0.0001); + + }*/ +} diff --git a/jzy3d-core/src/test/java/org/jzy3d/maths/TestCoord3d.java b/jzy3d-core/src/test/java/org/jzy3d/maths/TestCoord3d.java index 59aced4d..56bd4d82 100644 --- a/jzy3d-core/src/test/java/org/jzy3d/maths/TestCoord3d.java +++ b/jzy3d-core/src/test/java/org/jzy3d/maths/TestCoord3d.java @@ -91,4 +91,6 @@ public class TestCoord3d { Assert.assertFalse(new Coord3d(Float.POSITIVE_INFINITY, 2, 3) .equals(new Coord3d(Float.NEGATIVE_INFINITY, 2, 3))); } + + } diff --git a/jzy3d-tests-java9/data/bench-2-100-emulgl-hidpi.xlsx b/jzy3d-tests-java9/data/bench-2-100-emulgl-hidpi.xlsx new file mode 100644 index 00000000..61c1b955 Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-100-emulgl-hidpi.xlsx differ diff --git a/jzy3d-tests-java9/data/bench-2-100-native.xlsx b/jzy3d-tests-java9/data/bench-2-100-native.xlsx new file mode 100644 index 00000000..a9d29a10 Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-100-native.xlsx differ diff --git a/jzy3d-tests-java9/data/bench-2-200-emulgl-hidpi.xlsx b/jzy3d-tests-java9/data/bench-2-200-emulgl-hidpi.xlsx new file mode 100644 index 00000000..7513f5b6 Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-200-emulgl-hidpi.xlsx differ diff --git a/jzy3d-tests-java9/data/bench-2-250-native.xlsx b/jzy3d-tests-java9/data/bench-2-250-native.xlsx new file mode 100644 index 00000000..613bbdca Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-250-native.xlsx differ diff --git a/jzy3d-tests-java9/data/bench-2-30-emulgl-hidpi.xlsx b/jzy3d-tests-java9/data/bench-2-30-emulgl-hidpi.xlsx new file mode 100644 index 00000000..f74d7a71 Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-30-emulgl-hidpi.xlsx differ diff --git a/jzy3d-tests-java9/data/bench-2-500-emulgl-hidpi.xlsx b/jzy3d-tests-java9/data/bench-2-500-emulgl-hidpi.xlsx new file mode 100644 index 00000000..be9238dc Binary files /dev/null and b/jzy3d-tests-java9/data/bench-2-500-emulgl-hidpi.xlsx differ diff --git a/jzy3d-tests-java9/pom.xml b/jzy3d-tests-java9/pom.xml index 2d5b9f97..cf43c2d0 100644 --- a/jzy3d-tests-java9/pom.xml +++ b/jzy3d-tests-java9/pom.xml @@ -45,6 +45,12 @@ jzy3d-tester-native ${project.version} + + + ${project.groupId} + jzy3d-io-xls + ${project.version} + diff --git a/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkPlot.java b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkPlot.java new file mode 100644 index 00000000..ed253b38 --- /dev/null +++ b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkPlot.java @@ -0,0 +1,106 @@ +package org.jzy3d.performance.polygons; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.apache.poi.ss.usermodel.Cell; +import org.jzy3d.chart.Chart; +import org.jzy3d.chart.factories.AWTChartFactory; +import org.jzy3d.chart.factories.ChartFactory; +import org.jzy3d.colors.Color; +import org.jzy3d.io.xls.ExcelBuilder; +import org.jzy3d.maths.BoundingBox3d; +import org.jzy3d.maths.Coord3d; +import org.jzy3d.painters.Font; +import org.jzy3d.plot3d.primitives.LineStrip; +import org.jzy3d.plot3d.primitives.Scatter; +import org.jzy3d.plot3d.primitives.axis.layout.LabelOrientation; +import org.jzy3d.plot3d.primitives.axis.layout.renderers.IntegerTickRenderer; +import org.jzy3d.plot3d.rendering.canvas.Quality; + +public class BenchmarkPlot implements BenchmarkXLS{ + + public static void main(String[] args) throws IOException { + + // ------------------------------- + // Chart configuration for plotting + ChartFactory f = new AWTChartFactory(); + //ChartFactory f = new EmulGLChartFactory(); + Quality q = Quality.Advanced(); + //q.setHiDPI(HiDPI.OFF); + + + // ------------------------------- + // Collect benchmark data + + int stepMin = 2; // min number of steps for surface + int stepMax = 100; // max number of steps for surface + String info = "emulgl-hidpi"; // HiDPI setting during benchmark + + + String file = BenchmarkUtils.getExcelFilename(outputFolder, stepMin, stepMax, info); + ExcelBuilder xls = new ExcelBuilder(file); + xls.setCurrentSheet(SHEET_BENCHMARK); + + int line = 0; + + + List data = new ArrayList<>(); + + Cell cellTime = xls.getCell(line, TIME); + Cell cellPoly = xls.getCell(line, POLYGONS); + + double maxX = 0; + + while(cellTime!=null && cellPoly!=null) { + double x = cellPoly.getNumericCellValue(); + double y = cellTime.getNumericCellValue(); + + if(x>maxX) + maxX = x; + + data.add(new Coord3d(x, y, 0)); + + cellTime = xls.getCell(line, TIME); + cellPoly = xls.getCell(line, POLYGONS); + + line++; + } + + System.out.println("loaded " + line + " XLS lines"); + + + // ------------------------------- + // Plot benchmark data + + + + Chart c = f.newChart(q); + + c.view2d(); + c.add(new Scatter(data, Color.BLUE, 2)); + c.add(line(40, maxX, Color.GREEN, 2)); + c.add(line(60, maxX, Color.ORANGE, 2)); + c.add(line(80, maxX, Color.RED, 2)); + c.getAxisLayout().setXAxisLabel("Number of polygons (polygons all together cover the same surface)"); + c.getAxisLayout().setYAxisLabel("Rendering time (ms)"); + c.getAxisLayout().setYAxisLabelOrientation(LabelOrientation.PARALLEL_TO_AXIS); + c.getAxisLayout().setFont(Font.Helvetica_18); + c.getAxisLayout().setXTickRenderer(new IntegerTickRenderer(true)); + + c.getView().setBoundManual(new BoundingBox3d(0, (float)maxX, 0, 400, -10, 10)); + + c.open(file, 1024, 768); + + + //DebugGLChart3d debugChart = new DebugGLChart3d(c, new AWTChartFactory()); + //debugChart.open(new Rectangle(0, 0, 300, 300)); + + } + + private static LineStrip line(int ms, double maxX, Color lineColor, int width) { + LineStrip line = new LineStrip(lineColor, new Coord3d(0, ms, 0), new Coord3d(maxX, ms, 0)); + line.setWireframeWidth(width); + return line; + } +} diff --git a/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkRun.java b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkRun.java new file mode 100644 index 00000000..168ebc6e --- /dev/null +++ b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkRun.java @@ -0,0 +1,188 @@ +package org.jzy3d.performance.polygons; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.Map; +import java.util.Set; +import org.jzy3d.chart.Chart; +import org.jzy3d.chart.EmulGLSkin; +import org.jzy3d.chart.factories.AWTChartFactory; +import org.jzy3d.chart.factories.ChartFactory; +import org.jzy3d.chart.factories.EmulGLChartFactory; +import org.jzy3d.colors.Color; +import org.jzy3d.colors.ColorMapper; +import org.jzy3d.colors.colormaps.ColorMapRainbow; +import org.jzy3d.io.xls.ExcelBuilder; +import org.jzy3d.io.xls.ExcelBuilder.Type; +import org.jzy3d.maths.Range; +import org.jzy3d.maths.Statistics; +import org.jzy3d.maths.TicToc; +import org.jzy3d.plot3d.builder.Mapper; +import org.jzy3d.plot3d.builder.SurfaceBuilder; +import org.jzy3d.plot3d.builder.concrete.OrthonormalGrid; +import org.jzy3d.plot3d.primitives.Shape; +import org.jzy3d.plot3d.rendering.canvas.Quality; +import org.jzy3d.plot3d.rendering.view.HiDPI; + +public class BenchmarkRun implements BenchmarkXLS { + + public static void main(String[] args) throws IOException, InterruptedException { + int width = 1024; + int height = 768; + HiDPI hidpi = HiDPI.ON; + boolean alphaEnabled = false; + String info = "native"; // for building file name + + boolean offscreen = false; + int stepMax = 250; // max number of steps for surface + int stepMin = 2; // min number of steps for surface + int shoots = 30; // number of trials + + boolean saveImages = false; + + + ExcelBuilder xls = new ExcelBuilder(Type.XLSX, SHEET_CONFIG); + + RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); + + Map systemProperties = runtimeBean.getSystemProperties(); + Set keys = systemProperties.keySet(); + + int line = 0; + for (String key : keys) { + String value = systemProperties.get(key); + + xls.setCell(line, 0, key); + xls.setCell(line, 1, value); + line++; + } + + + + // -------------------------------------------------------- + // Configure base quality for standard case + + // ChartFactory factory = new EmulGLChartFactory(); + ChartFactory factory = new AWTChartFactory(); + + if (offscreen) + factory.getPainterFactory().setOffscreen(width, height); + + Quality q = Quality.Advanced(); + q.setAnimated(factory instanceof AWTChartFactory); // RESOLVE ME : CHART DOES NOT SHOW A NON + // ANIMATED AWT + q.setAlphaActivated(alphaEnabled); + q.setHiDPI(hidpi); + + Chart chart = factory.newChart(q); + if (!offscreen) + chart.open(width, height); + + // EmulGLSkin skin = EmulGLSkin.on(chart); + // chart.addMouseCameraController(); + + + Shape surface = null; + + xls.setCurrentSheet(xls.newSheet(SHEET_BENCHMARK)); + + + line = 0; + + + for (int i = stepMax; i >= stepMin; i--) { + + + if (surface != null) { + chart.remove(surface); + chart.render(); + } + + surface = surface(i); + chart.add(surface); + + int polygonNumber = surface.size(); + + + int minPoly = Integer.MAX_VALUE; + int maxPoly = 0; + + // Measure perf + double[] renderingTime = new double[shoots]; + + TicToc t = new TicToc(); + + for (int s = 0; s < shoots; s++) { + + t.tic(); + chart.render(); + t.toc(); + + renderingTime[s] = t.elapsedMilisecond(); + + if (renderingTime[s] < 40) { + Thread.sleep(50); + } + + xls.setCell(line, TIME, renderingTime[s]); + xls.setCell(line, POLYGONS, polygonNumber); + xls.setCell(line, WIDTH, width); + xls.setCell(line, HEIGHT, height); + line++; + + if (polygonNumber > maxPoly) { + maxPoly = polygonNumber; + } + if (polygonNumber < minPoly) { + minPoly = polygonNumber; + } + } + + + // System.out.println("Median perf for " + n + " polygons : " + // + Statistics.median(perf, false) + " after " + shoots + " shoots"); + // Array.print(perf); + + System.out.println(polygonNumber + ", " + (width * height) + ", " + + Statistics.median(renderingTime, false) + ", " + shoots); + + if (saveImages) + chart.screenshot(new File("target/bench-" + polygonNumber + ".png")); + + } + + // finish + String file = BenchmarkUtils.getExcelFilename(outputFolder, stepMin, stepMax, info); + xls.save(file); + + System.out.println("Wrote " + file + " with " + line + " lines"); + } + + + + + private static Shape surface(int steps) { + SurfaceBuilder builder = new SurfaceBuilder(); + + Mapper mapper = new Mapper() { + @Override + public double f(double x, double y) { + return 0;// x * Math.sin(x * y); + } + }; + + Range range = new Range(-3, 3); + + Shape surface = builder.orthonormal(new OrthonormalGrid(range, steps), mapper); + + ColorMapper colorMapper = new ColorMapper(new ColorMapRainbow(), surface); + surface.setColorMapper(colorMapper); + surface.setFaceDisplayed(true); + surface.setWireframeDisplayed(false); + surface.setWireframeColor(Color.BLACK); + surface.setWireframeWidth(1); + return surface; + } +} diff --git a/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkUtils.java b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkUtils.java new file mode 100644 index 00000000..aef4a49f --- /dev/null +++ b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkUtils.java @@ -0,0 +1,8 @@ +package org.jzy3d.performance.polygons; + +public class BenchmarkUtils { + public static String getExcelFilename(String outputFolder, int stepMin, int stepMax, + String info) { + return outputFolder + "/bench-" + stepMin + "-" + stepMax + "-" + info + ".xlsx"; + } +} diff --git a/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkXLS.java b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkXLS.java new file mode 100644 index 00000000..01ff92e3 --- /dev/null +++ b/jzy3d-tests-java9/src/test/java/org/jzy3d/performance/polygons/BenchmarkXLS.java @@ -0,0 +1,14 @@ +package org.jzy3d.performance.polygons; + +public interface BenchmarkXLS { + int TIME = 0; + int POLYGONS = 1; + int WIDTH = 2; + int HEIGHT = 3; + + String SHEET_CONFIG = "config"; + String SHEET_BENCHMARK = "benchmark"; + + String outputFolder = "./data"; + +}