public AdvancingFrontNode( TriangulationPoint point ) { this.point = point; value = point.getX(); key = Double.valueOf( value ); // XXX: BST }
@Override public String toString() { return "[" + getX() + "," + getY() + "]"; }
/** * We use a balancing tree to locate a node smaller or equal to * given key value * * @param x * @return */ public AdvancingFrontNode locateNode( TriangulationPoint point ) { return locateNode( point.getX() ); }
public int hashCode() { long bits = java.lang.Double.doubleToLongBits(getX()); bits ^= java.lang.Double.doubleToLongBits(getY()) * 31; return (((int) bits) ^ ((int) (bits >> 32))); }
/** * Forumla to calculate signed area<br> * Positive if CCW<br> * Negative if CW<br> * 0 if collinear<br> * <pre> * A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1) * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) * </pre> */ public static Orientation orient2d( TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc ) { double detleft = ( pa.getX() - pc.getX() ) * ( pb.getY() - pc.getY() ); double detright = ( pa.getY() - pc.getY() ) * ( pb.getX() - pc.getX() ); double val = detleft - detright; if( val > -EPSILON && val < EPSILON ) { return Orientation.Collinear; } else if( val > 0 ) { return Orientation.CCW; } return Orientation.CW; }
/** * * @param node - middle node * @return the angle between p-a and p-b in range [-pi,pi] */ private static double angle( TriangulationPoint p, TriangulationPoint a, TriangulationPoint b ) { // XXX: do we really need a signed angle for holeAngle? // could possible save some cycles here /* Complex plane * ab = cosA +i*sinA * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) * atan2(y,x) computes the principal value of the argument function * applied to the complex number x+iy * Where x = ax*bx + ay*by * y = ax*by - ay*bx */ final double px = p.getX(); final double py = p.getY(); final double ax = a.getX() - px; final double ay = a.getY() - py; final double bx = b.getX() - px; final double by = b.getY() - py; return Math.atan2( ax*by - ay*bx, ax*bx + ay*by ); }
private static void fillRightAboveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) { while( node.next.point.getX() < edge.p.getX() ) { if( tcx.isDebugEnabled() ) { tcx.getDebugContext().setActiveNode( node ); } // Check if next node is below the edge Orientation o1 = orient2d( edge.q, node.next.point, edge.p ); if( o1 == Orientation.CCW ) { fillRightBelowEdgeEvent( tcx, edge, node ); } else { node = node.next; } } }
private static void fillLeftAboveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) { while( node.prev.point.getX() > edge.p.getX() ) { if( tcx.isDebugEnabled() ) { tcx.getDebugContext().setActiveNode( node ); } // Check if next node is below the edge Orientation o1 = orient2d( edge.q, node.prev.point, edge.p ); if( o1 == Orientation.CW ) { fillLeftBelowEdgeEvent( tcx, edge, node ); } else { node = node.prev; } } }
private static List<Triangle2d> convert(Polygon p) { List<DelaunayTriangle> triangles = p.getTriangles(); if (triangles == null || triangles.size() == 0) { return null; } List<Triangle2d> out = new ArrayList<Triangle2d>(); for (DelaunayTriangle t : triangles) { Triangle2d triangle = new Triangle2d( new Point2d(t.points[0].getX(), t.points[0].getY()), new Point2d(t.points[1].getX(), t.points[1].getY()), new Point2d(t.points[2].getX(), t.points[2].getY())); out.add(triangle); } return out; }
private static void fillLeftBelowEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) { if( tcx.isDebugEnabled() ) { tcx.getDebugContext().setActiveNode( node ); } if( node.point.getX() > edge.p.getX() ) { if( orient2d( node.point, node.prev.point, node.prev.prev.point ) == Orientation.CW ) { // Concave fillLeftConcaveEdgeEvent( tcx, edge, node ); } else { // Convex fillLeftConvexEdgeEvent( tcx, edge, node ); // Retry this one fillLeftBelowEdgeEvent( tcx, edge, node ); } } }
private static void fillRightBelowEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) { if( tcx.isDebugEnabled() ) { tcx.getDebugContext().setActiveNode( node ); } if( node.point.getX() < edge.p.getX() ) // needed? { if( orient2d( node.point, node.next.point, node.next.next.point ) == Orientation.CCW ) { // Concave fillRightConcaveEdgeEvent( tcx, edge, node ); } else { // Convex fillRightConvexEdgeEvent( tcx, edge, node ); // Retry this one fillRightBelowEdgeEvent( tcx, edge, node ); } } }
/** * Find closes node to the left of the new point and * create a new triangle. If needed new holes and basins * will be filled to. * * @param tcx * @param point * @return */ private static AdvancingFrontNode pointEvent( DTSweepContext tcx, TriangulationPoint point ) { AdvancingFrontNode node,newNode; node = tcx.locateNode( point ); if( tcx.isDebugEnabled() ) { tcx.getDebugContext().setActiveNode( node ); } newNode = newFrontTriangle( tcx, point, node ); // Only need to check +epsilon since point never have smaller // x value than node due to how we fetch nodes from the front if( point.getX() <= node.point.getX() + EPSILON ) { fill( tcx, node ); } tcx.addNode( newNode ); fillAdvancingFront( tcx, newNode ); return newNode; }