Node p = outerNode; double qx = Double.NEGATIVE_INFINITY; final double hx = holeNode.getX(); final double hy = holeNode.getY(); Node connection = null; if (hy <= p.getY() && hy >= p.next.getY() && p.next.getY() != p.getY()) { final double x = p.getX() + (hy - p.getY()) * (p.next.getX() - p.getX()) / (p.next.getY() - p.getY()); if (x <= hx && x > qx) { qx = x; if (x == hx) { if (hy == p.getY()) return p; if (hy == p.next.getY()) return p.next; connection = p.getX() < p.next.getX() ? p : p.next; final double mx = connection.getX(); final double my = connection.getY(); double tanMin = Double.POSITIVE_INFINITY; double tan; if (hx >= p.getX() && p.getX() >= mx && hx != p.getX() && pointInEar(p.getX(), p.getY(), hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy)) { tan = Math.abs(hy - p.getY()) / (hx - p.getX()); // tangential if ((tan < tanMin || (tan == tanMin && p.getX() > connection.getX())) && isLocallyInside(p, holeNode)) { connection = p; tanMin = tan;
&& n != null && Long.compareUnsigned(n.morton, maxZ) <= 0) { if (p.idx != ear.previous.idx && p.idx != ear.next.idx && pointInEar(p.getX(), p.getY(), ear.previous.getX(), ear.previous.getY(), ear.getX(), ear.getY(), ear.next.getX(), ear.next.getY()) && area(p.previous.getX(), p.previous.getY(), p.getX(), p.getY(), p.next.getX(), p.next.getY()) >= 0) return false; p = p.previousZ; pointInEar(n.getX(), n.getY(), ear.previous.getX(), ear.previous.getY(), ear.getX(), ear.getY(), ear.next.getX(), ear.next.getY()) && area(n.previous.getX(), n.previous.getY(), n.getX(), n.getY(), n.next.getX(), n.next.getY()) >= 0) return false; n = n.nextZ; && pointInEar(p.getX(), p.getY(), ear.previous.getX(), ear.previous.getY(), ear.getX(), ear.getY(), ear.next.getX(), ear.next.getY()) && area(p.previous.getX(), p.previous.getY(), p.getX(), p.getY(), p.next.getX(), p.next.getY()) >= 0) { return false; Long.compareUnsigned(n.morton, maxZ) <= 0) { if (n.idx != ear.previous.idx && n.idx != ear.next.idx && pointInEar(n.getX(), n.getY(), ear.previous.getX(), ear.previous.getY(), ear.getX(), ear.getY(), ear.next.getX(), ear.next.getY()) && area(n.previous.getX(), n.previous.getY(), n.getX(), n.getY(), n.next.getX(), n.next.getY()) >= 0) { return false;
/** Iterate through all polygon nodes and remove small local self-intersections **/ private static Node cureLocalIntersections(Node startNode, final List<Triangle> tessellation) { Node node = startNode; Node nextNode; do { nextNode = node.next; Node a = node.previous; Node b = nextNode.next; // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) if (isVertexEquals(a, b) == false && isIntersectingPolygon(a, a.getX(), a.getY(), b.getX(), b.getY()) == false && linesIntersect(a.getX(), a.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY(), b.getX(), b.getY()) && isLocallyInside(a, b) && isLocallyInside(b, a)) { // Return the triangulated vertices to the tessellation tessellation.add(new Triangle(a, node, b)); // remove two nodes involved removeNode(node); removeNode(node.next); node = startNode = b; } node = node.next; } while (node != startNode); return node; }
/** Links two polygon vertices using a bridge. **/ private static Node splitPolygon(final Node a, final Node b) { final Node a2 = new Node(a); final Node b2 = new Node(b); final Node an = a.next; final Node bp = b.previous; a.next = b; a.nextZ = b; b.previous = a; b.previousZ = a; a2.next = an; a2.nextZ = an; an.previous = a2; an.previousZ = a2; b2.next = a2; b2.nextZ = a2; a2.previous = b2; a2.previousZ = b2; bp.next = b2; bp.nextZ = b2; return b2; }
/** Creates a node and optionally links it with a previous node in a circular doubly-linked list */ private static Node insertNode(final Polygon polygon, int index, int vertexIndex, final Node lastNode) { final Node node = new Node(polygon, index, vertexIndex); if(lastNode == null) { node.previous = node; node.previousZ = node; node.next = node; node.nextZ = node; } else { node.next = lastNode.next; node.nextZ = lastNode.next; node.previous = lastNode; node.previousZ = lastNode; lastNode.next.previous = node; lastNode.nextZ.previousZ = node; lastNode.next = node; lastNode.nextZ = node; } return node; }
/** Determine whether the middle point of a polygon diagonal is contained within the polygon */ private static boolean middleInsert(final Node start, final double x0, final double y0, final double x1, final double y1) { Node node = start; Node nextNode; boolean lIsInside = false; final double lDx = (x0 + x1) / 2.0f; final double lDy = (y0 + y1) / 2.0f; do { nextNode = node.next; if (node.getY() > lDy != nextNode.getY() > lDy && lDx < (nextNode.getX() - node.getX()) * (lDy - node.getY()) / (nextNode.getY() - node.getY()) + node.getX()) { lIsInside = !lIsInside; } node = node.next; } while (node != start); return lIsInside; }
/** Finds the left-most hole of a polygon ring. **/ private static Node fetchLeftmost(final Node start) { Node node = start; Node leftMost = start; do { // Determine if the current node possesses a lesser X position. if (node.getX() < leftMost.getX()) { // Maintain a reference to this Node. leftMost = node; } // Progress the search to the next node in the doubly-linked list. node = node.next; } while (node != start); // Return the node with the smallest X value. return leftMost; }
/** Determines whether a polygon node forms a valid ear with adjacent nodes. **/ private static boolean isEar(final Node ear, final boolean mortonOptimized) { if (mortonOptimized == true) { return mortonIsEar(ear); } // make sure there aren't other points inside the potential ear Node node = ear.next.next; while (node != ear.previous) { if (pointInEar(node.getX(), node.getY(), ear.previous.getX(), ear.previous.getY(), ear.getX(), ear.getY(), ear.next.getX(), ear.next.getY()) && area(node.previous.getX(), node.previous.getY(), node.getX(), node.getY(), node.next.getX(), node.next.getY()) >= 0) { return false; } node = node.next; } return true; }
/** Determines if the diagonal of a polygon is intersecting with any polygon elements. **/ private static boolean isIntersectingPolygon(final Node start, final double x0, final double y0, final double x1, final double y1) { Node node = start; Node nextNode; do { nextNode = node.next; if(isVertexEquals(node, x0, y0) == false && isVertexEquals(node, x1, y1) == false) { if (linesIntersect(node.getX(), node.getY(), nextNode.getX(), nextNode.getY(), x0, y0, x1, y1)) { return true; } } node = nextNode; } while (node != start); return false; }
/** get longitude value for the given vertex */ public double getLon(int vertex) { return this.vertex[vertex].getLon(); }
/** get latitude value for the given vertex */ public double getLat(int vertex) { return this.vertex[vertex].getLat(); }
private static boolean isLocallyInside(final Node a, final Node b) { // if a is cw if (area(a.previous.getX(), a.previous.getY(), a.getX(), a.getY(), a.next.getX(), a.next.getY()) < 0) { return area(a.getX(), a.getY(), b.getX(), b.getY(), a.next.getX(), a.next.getY()) >= 0 && area(a.getX(), a.getY(), a.previous.getX(), a.previous.getY(), b.getX(), b.getY()) >= 0; } // ccw return area(a.getX(), a.getY(), b.getX(), b.getY(), a.previous.getX(), a.previous.getY()) < 0 || area(a.getX(), a.getY(), a.next.getX(), a.next.getY(), b.getX(), b.getY()) < 0; }
/** Determines whether a diagonal between two polygon nodes lies within a polygon interior. * (This determines the validity of the ray.) **/ private static boolean isValidDiagonal(final Node a, final Node b) { return a.next.idx != b.idx && a.previous.idx != b.idx && isIntersectingPolygon(a, a.getX(), a.getY(), b.getX(), b.getY()) == false && isLocallyInside(a, b) && isLocallyInside(b, a) && middleInsert(a, a.getX(), a.getY(), b.getX(), b.getY()); }