package com.revolsys.geometry.test.old.perf.operation.overlay; import java.util.Random; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.geometry.operation.overlay.snap.SnapIfNeededOverlayOp; import junit.framework.TestCase; /** * Tests Noding checking during overlay. * Intended to show that noding check failures due to robustness * problems do not occur very often (i.e. that the heuristic is * not triggering so often that a large performance penalty would be incurred.) * * The class generates test geometries for input to overlay which contain almost parallel lines * - this should cause noding failures relatively frequently. * * Can also be used to check that the cross-snapping heuristic fix for robustness * failures works well. If snapping ever fails to fix a case, * an exception is thrown. It is expected (and has been observed) * that cross-snapping works extremely well on this dataset. * * @version 1.7 */ public class OverlayNodingStressTest extends TestCase { private static final int BATCH_SIZE = 20; private static final int ITER_LIMIT = 10000; private static final double MAX_DISPLACEMENT = 60; public static void main(final String[] args) { junit.textui.TestRunner.run(OverlayNodingStressTest.class); } private Geometry baseAccum = null; private int geomCount = 0; private final Random rand = new Random((long)(Math.PI * 10e8)); public OverlayNodingStressTest(final String name) { super(name); } public void checkIntersection(final Geometry base, final Geometry testGeom) { // this line can be used to test for the presence of noding failures for // non-tricky cases // Geometry star = rr2; // System.out.println("Star:"); // System.out.println(base); // System.out.println("Rectangle:"); // System.out.println(testGeom); // test to see whether the basic overlay code fails try { final Geometry intTrial = base.intersection(testGeom); } catch (final Exception ex) { } // this will throw an intersection if a robustness error occurs, // stopping the run final Geometry intersection = SnapIfNeededOverlayOp.intersection(base, testGeom); // System.out.println("Intersection:"); // System.out.println(intersection); } public Geometry[] generateGeometryAccum(final double angle1, final double angle2) { final RotatedRectangleFactory rrFact = new RotatedRectangleFactory(); final double basex = angle2 * MAX_DISPLACEMENT - MAX_DISPLACEMENT / 2; final Point base = new PointDoubleXY(basex, basex); final Polygon rr1 = rrFact.newRectangle(100, 20, angle1, base); // limit size of accumulated star this.geomCount++; if (this.geomCount >= BATCH_SIZE) { this.geomCount = 0; } if (this.geomCount == 0) { this.baseAccum = null; } if (this.baseAccum == null) { this.baseAccum = rr1; } else { // this line can be used to test for the presence of noding failures for // non-tricky cases // Geometry star = rr2; this.baseAccum = rr1.union(this.baseAccum); } return new Geometry[] { this.baseAccum, rr1 }; } public Geometry[] generateGeometryStar(final double angle1, final double angle2) { final RotatedRectangleFactory rrFact = new RotatedRectangleFactory(); final Polygon rr1 = rrFact.newRectangle(100, 20, angle1); final Polygon rr2 = rrFact.newRectangle(100, 20, angle2); // this line can be used to test for the presence of noding failures for // non-tricky cases // Geometry star = rr2; final Geometry star = rr1.union(rr2); return new Geometry[] { star, rr1 }; } private double getRand() { final double r = this.rand.nextDouble(); return r; } public void testNoding() { final int iterLimit = ITER_LIMIT; for (int i = 0; i < iterLimit; i++) { // System.out.println("Iter: " + i + " Noding failure count = " // + this.failureCount); final double ang1 = getRand() * Math.PI; final double ang2 = getRand() * Math.PI; // Geometry[] geom = generateGeometryStar(ang1, ang2); final Geometry[] geom = generateGeometryAccum(ang1, ang2); checkIntersection(geom[0], geom[1]); } // System.out.println("Test count = " + iterLimit // + " Noding failure count = " + this.failureCount); } } class RotatedRectangleFactory { private static double PI_OVER_2 = Math.PI / 2; private final GeometryFactory fact = GeometryFactory.DEFAULT_3D; public RotatedRectangleFactory() { } public Polygon newRectangle(final double length, final double width, final double angle) { return newRectangle(length, width, angle, new PointDoubleXY(0, 0)); } public Polygon newRectangle(final double length, final double width, final double angle, final Point base) { final double posx = length / 2 * Math.cos(angle); final double posy = length / 2 * Math.sin(angle); final double negx = -posx; final double negy = -posy; final double widthOffsetx = width / 2 * Math.cos(angle + PI_OVER_2); final double widthOffsety = width / 2 * Math.sin(angle + PI_OVER_2); final double x1 = base.getX() + posx + widthOffsetx; final double y1 = base.getY() + posy + widthOffsety; final double[] coordinates = new double[] { x1, y1, // base.getX() + posx - widthOffsetx, base.getY() + posy - widthOffsety, // base.getX() + negx - widthOffsetx, base.getY() + negy - widthOffsety, // base.getX() + negx + widthOffsetx, base.getY() + negy + widthOffsety, // x1, y1 }; final Polygon poly = this.fact.polygon(this.fact.linearRing(2, coordinates)); return poly; } }