protected void reset() { currentLM = minimaList; if (currentLM == null) { return; // ie nothing to process } // reset all edges ... LocalMinima lm = minimaList; while (lm != null) { Edge e = lm.leftBound; if (e != null) { e.setCurrent(new LongPoint(e.getBot())); e.side = Edge.Side.LEFT; e.outIdx = Edge.UNASSIGNED; } e = lm.rightBound; if (e != null) { e.setCurrent(new LongPoint(e.getBot())); e.side = Edge.Side.RIGHT; e.outIdx = Edge.UNASSIGNED; } lm = lm.next; } }
private Edge processBound(Edge e, boolean LeftBoundIsForward) { Edge EStart, result = e; Edge Horz; if (result.outIdx == Edge.SKIP) { // check if there are edges beyond the skip edge in the bound and if so // create another LocMin and calling ProcessBound once more ... e = result; if (LeftBoundIsForward) { while (e.getTop().getY() == e.next.getBot().getY()) { e = e.next; } while (e != result && e.deltaX == Edge.HORIZONTAL) { e = e.prev; } } else { while (e.getTop().getY() == e.prev.getBot().getY()) { e = e.prev; } while (e != result && e.deltaX == Edge.HORIZONTAL) { e = e.next; } } if (e == result) { if (LeftBoundIsForward) { result = e.next; } else { result = e.prev; } } else { // there are more edges in the bound beyond result starting with E if (LeftBoundIsForward) { e = result.next; } else { e = result.prev; } final LocalMinima locMin = new LocalMinima(); locMin.next = null; locMin.y = e.getBot().getY(); locMin.leftBound = null; locMin.rightBound = e; e.windDelta = 0; result = processBound(e, LeftBoundIsForward); insertLocalMinima(locMin); } return result; } if (e.deltaX == Edge.HORIZONTAL) { // We need to be careful with open paths because this may not be a // true local minima (ie E may be following a skip edge). // Also, consecutive horz. edges may start heading left before going right. if (LeftBoundIsForward) { EStart = e.prev; } else { EStart = e.next; } if (EStart.deltaX == Edge.HORIZONTAL) // ie an adjoining horizontal skip edge { if (EStart.getBot().getX() != e.getBot().getX() && EStart.getTop().getX() != e.getBot().getX()) e.reverseHorizontal(); } else if (EStart.getBot().getX() != e.getBot().getX()) e.reverseHorizontal(); } EStart = e; if (LeftBoundIsForward) { while (result.getTop().getY() == result.next.getBot().getY() && result.next.outIdx != Edge.SKIP) { result = result.next; } if (result.deltaX == Edge.HORIZONTAL && result.next.outIdx != Edge.SKIP) { // nb: at the top of a bound, horizontals are added to the bound // only when the preceding edge attaches to the horizontal's left vertex // unless a Skip edge is encountered when that becomes the top divide Horz = result; while (Horz.prev.deltaX == Edge.HORIZONTAL) { Horz = Horz.prev; } if (Horz.prev.getTop().getX() > result.next.getTop().getX()) result = Horz.prev; } while (e != result) { e.nextInLML = e.next; if (e.deltaX == Edge.HORIZONTAL && e != EStart && e.getBot().getX() != e.prev.getTop().getX()) { e.reverseHorizontal(); } e = e.next; } if (e.deltaX == Edge.HORIZONTAL && e != EStart && e.getBot().getX() != e.prev.getTop().getX()) { e.reverseHorizontal(); } result = result.next; // move to the edge just beyond current bound } else { while (result.getTop().getY() == result.prev.getBot().getY() && result.prev.outIdx != Edge.SKIP) { result = result.prev; } if (result.deltaX == Edge.HORIZONTAL && result.prev.outIdx != Edge.SKIP) { Horz = result; while (Horz.next.deltaX == Edge.HORIZONTAL) { Horz = Horz.next; } if (Horz.next.getTop().getX() == result.prev.getTop().getX() || Horz.next.getTop().getX() > result.prev.getTop().getX()) result = Horz.next; } while (e != result) { e.nextInLML = e.prev; if (e.deltaX == Edge.HORIZONTAL && e != EStart && e.getBot().getX() != e.next.getTop().getX()) { e.reverseHorizontal(); } e = e.prev; } if (e.deltaX == Edge.HORIZONTAL && e != EStart && e.getBot().getX() != e.next.getTop().getX()) { e.reverseHorizontal(); } result = result.prev; // move to the edge just beyond current bound } return result; }
public boolean addPath(Path pg, PolyType polyType, boolean Closed) { if (!Closed && polyType == PolyType.CLIP) { throw new IllegalStateException("AddPath: Open paths must be subject."); } int highI = pg.size() - 1; if (Closed) { while (highI > 0 && pg.get(highI).equals(pg.get(0))) { --highI; } } while (highI > 0 && pg.get(highI).equals(pg.get(highI - 1))) { --highI; } if (Closed && highI < 2 || !Closed && highI < 1) { return false; } // create a new edge array ... final List<Edge> edges = new ArrayList<Edge>(highI + 1); for (int i = 0; i <= highI; i++) { edges.add(new Edge()); } boolean IsFlat = true; // 1. Basic (first) edge initialization ... edges.get(1).setCurrent(new LongPoint(pg.get(1))); rangeTest(pg.get(0)); rangeTest(pg.get(highI)); initEdge(edges.get(0), edges.get(1), edges.get(highI), pg.get(0)); initEdge(edges.get(highI), edges.get(0), edges.get(highI - 1), pg.get(highI)); for (int i = highI - 1; i >= 1; --i) { rangeTest(pg.get(i)); initEdge(edges.get(i), edges.get(i + 1), edges.get(i - 1), pg.get(i)); } Edge eStart = edges.get(0); // 2. Remove duplicate vertices, and (when closed) collinear edges ... Edge e = eStart, eLoopStop = eStart; for (; ; ) { // nb: allows matching start and end points when not Closed ... if (e.getCurrent().equals(e.next.getCurrent()) && (Closed || !e.next.equals(eStart))) { if (e == e.next) { break; } if (e == eStart) { eStart = e.next; } e = removeEdge(e); eLoopStop = e; continue; } if (e.prev == e.next) { break; // only two vertices } else if (Closed && Point.slopesEqual(e.prev.getCurrent(), e.getCurrent(), e.next.getCurrent()) && (!isPreserveCollinear() || !Point.isPt2BetweenPt1AndPt3( e.prev.getCurrent(), e.getCurrent(), e.next.getCurrent()))) { // Collinear edges are allowed for open paths but in closed paths // the default is to merge adjacent collinear edges into a single edge. // However, if the PreserveCollinear property is enabled, only overlapping // collinear edges (ie spikes) will be removed from closed paths. if (e == eStart) { eStart = e.next; } e = removeEdge(e); e = e.prev; eLoopStop = e; continue; } e = e.next; if (e == eLoopStop || !Closed && e.next == eStart) { break; } } if (!Closed && e == e.next || Closed && e.prev == e.next) { return false; } if (!Closed) { hasOpenPaths = true; eStart.prev.outIdx = Edge.SKIP; } // 3. Do second stage of edge initialization ... e = eStart; do { initEdge2(e, polyType); e = e.next; if (IsFlat && e.getCurrent().getY() != eStart.getCurrent().getY()) { IsFlat = false; } } while (e != eStart); // 4. Finally, add edge bounds to LocalMinima list ... // Totally flat paths must be handled differently when adding them // to LocalMinima list to avoid endless loops etc ... if (IsFlat) { if (Closed) { return false; } e.prev.outIdx = Edge.SKIP; final LocalMinima locMin = new LocalMinima(); locMin.next = null; locMin.y = e.getBot().getY(); locMin.leftBound = null; locMin.rightBound = e; locMin.rightBound.side = Edge.Side.RIGHT; locMin.rightBound.windDelta = 0; for (; ; ) { if (e.getBot().getX() != e.prev.getTop().getX()) e.reverseHorizontal(); if (e.next.outIdx == Edge.SKIP) break; e.nextInLML = e.next; e = e.next; } insertLocalMinima(locMin); this.edges.add(edges); return true; } this.edges.add(edges); boolean leftBoundIsForward; Edge EMin = null; // workaround to avoid an endless loop in the while loop below when // open paths have matching start and end points ... if (e.prev.getBot().equals(e.prev.getTop())) { e = e.next; } for (; ; ) { e = e.findNextLocMin(); if (e == EMin) { break; } else if (EMin == null) { EMin = e; } // E and E.Prev now share a local minima (left aligned if horizontal). // Compare their slopes to find which starts which bound ... final LocalMinima locMin = new LocalMinima(); locMin.next = null; locMin.y = e.getBot().getY(); if (e.deltaX < e.prev.deltaX) { locMin.leftBound = e.prev; locMin.rightBound = e; leftBoundIsForward = false; // Q.nextInLML = Q.prev } else { locMin.leftBound = e; locMin.rightBound = e.prev; leftBoundIsForward = true; // Q.nextInLML = Q.next } locMin.leftBound.side = Edge.Side.LEFT; locMin.rightBound.side = Edge.Side.RIGHT; if (!Closed) { locMin.leftBound.windDelta = 0; } else if (locMin.leftBound.next == locMin.rightBound) { locMin.leftBound.windDelta = -1; } else { locMin.leftBound.windDelta = 1; } locMin.rightBound.windDelta = -locMin.leftBound.windDelta; e = processBound(locMin.leftBound, leftBoundIsForward); if (e.outIdx == Edge.SKIP) { e = processBound(e, leftBoundIsForward); } Edge E2 = processBound(locMin.rightBound, !leftBoundIsForward); if (E2.outIdx == Edge.SKIP) { E2 = processBound(E2, !leftBoundIsForward); } if (locMin.leftBound.outIdx == Edge.SKIP) { locMin.leftBound = null; } else if (locMin.rightBound.outIdx == Edge.SKIP) { locMin.rightBound = null; } insertLocalMinima(locMin); if (!leftBoundIsForward) { e = E2; } } return true; }