/** Determines whether two line segments intersect. **/ public static final boolean linesIntersect(final double aX0, final double aY0, final double aX1, final double aY1, final double bX0, final double bY0, final double bX1, final double bY1) { return (area(aX0, aY0, aX1, aY1, bX0, bY0) > 0) != (area(aX0, aY0, aX1, aY1, bX1, bY1) > 0) && (area(bX0, bY0, bX1, bY1, aX0, aY0) > 0) != (area(bX0, bY0, bX1, bY1, aX1, aY1) > 0); }
Node diagonal = nextNode.next; while (diagonal != searchNode.previous) { if(isValidDiagonal(searchNode, diagonal)) { Node splitNode = splitPolygon(searchNode, diagonal); searchNode = filterPoints(searchNode, searchNode.next); splitNode = filterPoints(splitNode, splitNode.next); sortByMortonWithReset(searchNode); sortByMortonWithReset(splitNode); earcutLinkedList(searchNode, tessellation, State.INIT, mortonIndexed); earcutLinkedList(splitNode, tessellation, State.INIT, mortonIndexed);
/** Finds a bridge between vertices that connects a hole with an outer ring, and links it */ private static final void eliminateHole(final Node holeNode, Node outerNode) { // Attempt to find a logical bridge between the HoleNode and OuterNode. outerNode = fetchHoleBridge(holeNode, outerNode); // Determine whether a hole bridge could be fetched. if(outerNode != null) { // Split the resulting polygon. Node node = splitPolygon(outerNode, holeNode); // Filter the split nodes. filterPoints(node, node.next); } }
/** Creates a circular doubly linked list using polygon points. The order is governed by the specified winding order */ private static final Node createDoublyLinkedList(final Polygon polygon, int startIndex, final WindingOrder windingOrder) { Node lastNode = null; // Link points into the circular doubly-linked list in the specified winding order if (windingOrder == polygon.getWindingOrder()) { for (int i = 0; i < polygon.numPoints(); ++i) { lastNode = insertNode(polygon, startIndex++, i, lastNode); } } else { for (int i = polygon.numPoints() - 1; i >= 0; --i) { lastNode = insertNode(polygon, startIndex++, i, lastNode); } } // if first and last node are the same then remove the end node and set lastNode to the start if (lastNode != null && isVertexEquals(lastNode, lastNode.next)) { removeNode(lastNode); lastNode = lastNode.next; } // Return the last node in the Doubly-Linked List return filterPoints(lastNode, null); }
nextNode = currEar.next; final boolean isReflex = area(prevNode.getX(), prevNode.getY(), currEar.getX(), currEar.getY(), nextNode.getX(), nextNode.getY()) >= 0; if (isReflex == false && isEar(currEar, mortonOptimized) == true) { removeNode(currEar); case INIT: currEar = filterPoints(currEar, null); state = State.CURE; continue earcut; case CURE: currEar = cureLocalIntersections(currEar, tessellation); state = State.SPLIT; continue earcut; case SPLIT: if (splitEarcut(currEar, tessellation, mortonOptimized) == false) {
Node outerNode = createDoublyLinkedList(polygon, 0, WindingOrder.CW); outerNode = eliminateHoles(polygon, outerNode); sortByMorton(outerNode); List<Triangle> result = earcutLinkedList(outerNode, new ArrayList<>(), State.INIT, mortonOptimized); if (result.size() == 0) { throw new IllegalArgumentException("Unable to Tessellate shape [" + polygon + "]. Possible malformed shape detected.");
for(int i = 0; i < polygon.numHoles(); ++i) { Node list = createDoublyLinkedList(holes[i], nodeIndex, WindingOrder.CCW); if (list == list.next) { list.isSteiner = true; holeList.add(fetchLeftmost(list)); eliminateHole(holeNode, outerNode); outerNode = filterPoints(outerNode, outerNode.next);
nextNode = node.next; prevNode = node.previous; if (node.isSteiner == false && isVertexEquals(node, nextNode) || area(prevNode.getX(), prevNode.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY()) == 0) { removeNode(node); node = end = prevNode;
/** Determines whether a polygon node forms a valid ear with adjacent nodes. **/ private static final 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; }
&& 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;
private static final 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; }