diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/base/PlateRecognition.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/base/PlateRecognition.java index 729e1ed..f41d84c 100644 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/base/PlateRecognition.java +++ b/open-anpr-core/src/main/java/com/visual/open/anpr/core/base/PlateRecognition.java @@ -6,5 +6,5 @@ import com.visual.open.anpr.core.domain.PlateInfo.ParseInfo; public interface PlateRecognition { - ParseInfo inference(ImageMat image, boolean single, Map params); + ParseInfo inference(ImageMat image, Boolean single, Map params); } diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Clipper.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Clipper.java deleted file mode 100644 index 9a6b9d6..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Clipper.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -public interface Clipper { - enum ClipType { - INTERSECTION, UNION, DIFFERENCE, XOR - } - - enum Direction { - RIGHT_TO_LEFT, LEFT_TO_RIGHT - } - - enum EndType { - CLOSED_POLYGON, CLOSED_LINE, OPEN_BUTT, OPEN_SQUARE, OPEN_ROUND - } - - enum JoinType { - SQUARE, ROUND, MITER - } - - enum PolyFillType { - EVEN_ODD, NON_ZERO, POSITIVE, NEGATIVE - } - - enum PolyType { - SUBJECT, CLIP - } - - interface ZFillCallback { - void zFill(LongPoint bot1, LongPoint top1, LongPoint bot2, LongPoint top2, LongPoint pt); - } - - //InitOptions that can be passed to the constructor ... - int REVERSE_SOLUTION = 1; - - int STRICTLY_SIMPLE = 2; - - int PRESERVE_COLINEAR = 4; - - boolean addPath(Path pg, PolyType polyType, boolean Closed); - - boolean addPaths(Paths ppg, PolyType polyType, boolean closed); - - void clear(); - - boolean execute(ClipType clipType, Paths solution); - - boolean execute(ClipType clipType, Paths solution, PolyFillType subjFillType, PolyFillType clipFillType); - - boolean execute(ClipType clipType, PolyTree polytree); - - boolean execute(ClipType clipType, PolyTree polytree, PolyFillType subjFillType, PolyFillType clipFillType); -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperBase.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperBase.java deleted file mode 100644 index 5296a2c..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperBase.java +++ /dev/null @@ -1,691 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Path.OutRec; -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -public abstract class ClipperBase implements Clipper { - protected class LocalMinima { - long y; - Edge leftBound; - Edge rightBound; - LocalMinima next; - } - - protected class Scanbeam { - long y; - Scanbeam next; - } - - protected class Maxima { - long x; - Maxima next; - Maxima prev; - } - - private static void initEdge(Edge e, Edge eNext, Edge ePrev, LongPoint pt ) { - e.next = eNext; - e.prev = ePrev; - e.setCurrent( new LongPoint( pt ) ); - e.outIdx = Edge.UNASSIGNED; - } - - private static void initEdge2(Edge e, PolyType polyType ) { - if (e.getCurrent().getY() >= e.next.getCurrent().getY()) { - e.setBot( new LongPoint( e.getCurrent() ) ); - e.setTop( new LongPoint( e.next.getCurrent() ) ); - } - else { - e.setTop( new LongPoint( e.getCurrent() ) ); - e.setBot( new LongPoint( e.next.getCurrent() ) ); - } - e.updateDeltaX(); - e.polyTyp = polyType; - } - - private static void rangeTest( LongPoint Pt ) { - - if (Pt.getX() > LOW_RANGE || Pt.getY() > LOW_RANGE || -Pt.getX() > LOW_RANGE || -Pt.getY() > LOW_RANGE) { - if (Pt.getX() > HI_RANGE || Pt.getY() > HI_RANGE || -Pt.getX() > HI_RANGE || -Pt.getY() > HI_RANGE) { - throw new IllegalStateException( "Coordinate outside allowed range" ); - } - } - } - - private static Edge removeEdge(Edge e ) { - //removes e from double_linked_list (but without removing from memory) - e.prev.next = e.next; - e.next.prev = e.prev; - final Edge result = e.next; - e.prev = null; //flag as removed (see ClipperBase.Clear) - return result; - } - - private final static long LOW_RANGE = 0x3FFFFFFF; - - private final static long HI_RANGE = 0x3FFFFFFFFFFFFFFFL; - - protected LocalMinima minimaList; - - protected LocalMinima currentLM; - - protected Scanbeam scanbeam; - - protected final List polyOuts = new ArrayList<>(); - - protected Edge activeEdges; - - protected boolean hasOpenPaths; - - protected final boolean preserveCollinear; - - private final static Logger LOGGER = Logger.getLogger( Clipper.class.getName() ); - - protected ClipperBase( boolean preserveCollinear ) //constructor (nb: no external instantiation) - { - this.preserveCollinear = preserveCollinear; - minimaList = null; - currentLM = null; - hasOpenPaths = false; - } - - @Override - 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 edges = new ArrayList<>( 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 ); - return true; - } - - 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; - - } - - @Override - public boolean addPaths(Paths paths, PolyType polyType, boolean closed ) { - boolean result = false; - for (Path path : paths) { - if (addPath(path, polyType, closed)) { - result = true; - } - } - return result; - } - - @Override - public void clear() { - disposeLocalMinimaList(); - hasOpenPaths = false; - } - - private void disposeLocalMinimaList() { - while (minimaList != null) { - final LocalMinima tmpLm = minimaList.next; - minimaList = null; - minimaList = tmpLm; - } - currentLM = null; - } - - private void insertLocalMinima( LocalMinima newLm ) { - if (minimaList == null) { - minimaList = newLm; - } - else if (newLm.y >= minimaList.y) { - newLm.next = minimaList; - minimaList = newLm; - } - else { - LocalMinima tmpLm = minimaList; - while (tmpLm.next != null && newLm.y < tmpLm.next.y) { - tmpLm = tmpLm.next; - } - newLm.next = tmpLm.next; - tmpLm.next = newLm; - } - } - private boolean isPreserveCollinear() { - return preserveCollinear; - } - - protected boolean popLocalMinima( long y, LocalMinima[] current ) { - LOGGER.entering( ClipperBase.class.getName(), "popLocalMinima" ); - current[0] = currentLM; - if (currentLM != null && currentLM.y == y) { - currentLM = currentLM.next; - return true; - } - return false; - } - - 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; - } - - protected void reset() { - currentLM = minimaList; - if (currentLM == null) { - return; //ie nothing to process - } - - //reset all edges ... - scanbeam = null; - LocalMinima lm = minimaList; - while (lm != null) { - insertScanbeam(lm.y); - Edge e = lm.leftBound; - if (e != null) { - e.setCurrent( new LongPoint( e.getBot() ) ); - e.outIdx = Edge.UNASSIGNED; - } - e = lm.rightBound; - if (e != null) { - e.setCurrent( new LongPoint( e.getBot() ) ); - e.outIdx = Edge.UNASSIGNED; - } - lm = lm.next; - } - activeEdges = null; - } - - protected void insertScanbeam( long y ) { - LOGGER.entering( ClipperBase.class.getName(), "insertScanbeam" ); - - //single-linked list: sorted descending, ignoring dups. - if (scanbeam == null) { - scanbeam = new Scanbeam(); - scanbeam.next = null; - scanbeam.y = y; - } - else if (y > scanbeam.y) { - final Scanbeam newSb = new Scanbeam(); - newSb.y = y; - newSb.next = scanbeam; - scanbeam = newSb; - } - else { - Scanbeam sb2 = scanbeam; - while (sb2.next != null && (y <= sb2.next.y)) { - sb2 = sb2.next; - } - if (y == sb2.y) { - return; //ie ignores duplicates - } - final Scanbeam newSb = new Scanbeam(); - newSb.y = y; - newSb.next = sb2.next; - sb2.next = newSb; - } - } - - protected boolean popScanbeam( long[] y ) { - if (scanbeam == null) { - y[0] = 0; - return false; - } - y[0] = scanbeam.y; - scanbeam = scanbeam.next; - return true; - } - - protected final boolean localMinimaPending() { - return currentLM != null; - } - - protected OutRec createOutRec() { - OutRec result = new OutRec(); - result.Idx = Edge.UNASSIGNED; - result.isHole = false; - result.isOpen = false; - result.firstLeft = null; - result.setPoints( null ); - result.bottomPt = null; - result.polyNode = null; - polyOuts.add( result ); - result.Idx = polyOuts.size() - 1; - return result; - } - - protected void disposeOutRec( int index ) { - OutRec outRec = polyOuts.get( index ); - outRec.setPoints( null ); - outRec = null; - polyOuts.set( index, null ); - } - - protected void updateEdgeIntoAEL( Edge e ) { - if (e.nextInLML == null) { - throw new IllegalStateException("UpdateEdgeIntoAEL: invalid call"); - } - final Edge aelPrev = e.prevInAEL; - final Edge aelNext = e.nextInAEL; - e.nextInLML.outIdx = e.outIdx; - if (aelPrev != null) { - aelPrev.nextInAEL = e.nextInLML; - } - else { - activeEdges = e.nextInLML; - } - if (aelNext != null) { - aelNext.prevInAEL = e.nextInLML; - } - e.nextInLML.side = e.side; - e.nextInLML.windDelta = e.windDelta; - e.nextInLML.windCnt = e.windCnt; - e.nextInLML.windCnt2 = e.windCnt2; - e = e.nextInLML; - e.setCurrent(e.getBot()); - e.prevInAEL = aelPrev; - e.nextInAEL = aelNext; - if (e.isHorizontal()) { - insertScanbeam(e.getTop().getY()); - } - } - - protected void swapPositionsInAEL(Edge edge1, Edge edge2 ) { - LOGGER.entering( ClipperBase.class.getName(), "swapPositionsInAEL" ); - - //check that one or other edge hasn't already been removed from AEL ... - if (edge1.nextInAEL == edge1.prevInAEL || edge2.nextInAEL == edge2.prevInAEL) { - return; - } - - if (edge1.nextInAEL == edge2) { - final Edge next = edge2.nextInAEL; - if (next != null) { - next.prevInAEL = edge1; - } - final Edge prev = edge1.prevInAEL; - if (prev != null) { - prev.nextInAEL = edge2; - } - edge2.prevInAEL = prev; - edge2.nextInAEL = edge1; - edge1.prevInAEL = edge2; - edge1.nextInAEL = next; - } - else if (edge2.nextInAEL == edge1) { - final Edge next = edge1.nextInAEL; - if (next != null) { - next.prevInAEL = edge2; - } - final Edge prev = edge2.prevInAEL; - if (prev != null) { - prev.nextInAEL = edge1; - } - edge1.prevInAEL = prev; - edge1.nextInAEL = edge2; - edge2.prevInAEL = edge1; - edge2.nextInAEL = next; - } - else { - final Edge next = edge1.nextInAEL; - final Edge prev = edge1.prevInAEL; - edge1.nextInAEL = edge2.nextInAEL; - if (edge1.nextInAEL != null) { - edge1.nextInAEL.prevInAEL = edge1; - } - edge1.prevInAEL = edge2.prevInAEL; - if (edge1.prevInAEL != null) { - edge1.prevInAEL.nextInAEL = edge1; - } - edge2.nextInAEL = next; - if (edge2.nextInAEL != null) { - edge2.nextInAEL.prevInAEL = edge2; - } - edge2.prevInAEL = prev; - if (edge2.prevInAEL != null) { - edge2.prevInAEL.nextInAEL = edge2; - } - } - - if (edge1.prevInAEL == null) { - activeEdges = edge1; - } - else if (edge2.prevInAEL == null) { - activeEdges = edge2; - } - - LOGGER.exiting( ClipperBase.class.getName(), "swapPositionsInAEL" ); - } - - protected void deleteFromAEL( Edge e ) { - LOGGER.entering( ClipperBase.class.getName(), "deleteFromAEL" ); - - Edge aelPrev = e.prevInAEL; - Edge aelNext = e.nextInAEL; - if (aelPrev == null && aelNext == null && (e != activeEdges)) { - return; //already deleted - } - if (aelPrev != null) { - aelPrev.nextInAEL = aelNext; - } - else { - activeEdges = aelNext; - } - if (aelNext != null) { - aelNext.prevInAEL = aelPrev; - } - e.nextInAEL = null; - e.prevInAEL = null; - - LOGGER.exiting( ClipperBase.class.getName(), "deleteFromAEL" ); - } -} \ No newline at end of file diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperOffset.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperOffset.java deleted file mode 100644 index caf4a98..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/ClipperOffset.java +++ /dev/null @@ -1,481 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Clipper.*; -import com.visual.open.anpr.core.clipper.Point.DoublePoint; -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class ClipperOffset { - private static boolean nearZero( double val ) { - return val > -TOLERANCE && val < TOLERANCE; - } - - private Paths destPolys; - private Path srcPoly; - private Path destPoly; - - private final List normals; - private double delta, inA, sin, cos; - - private double miterLim, stepsPerRad; - private LongPoint lowest; - - private final PolyNode polyNodes; - private final double arcTolerance; - - private final double miterLimit; - private final static double TWO_PI = Math.PI * 2; - - private final static double DEFAULT_ARC_TOLERANCE = 0.25; - - private final static double TOLERANCE = 1.0E-20; - - public ClipperOffset() { - this( 2, DEFAULT_ARC_TOLERANCE ); - } - - public ClipperOffset( double miterLimit, double arcTolerance ) { - this.miterLimit = miterLimit; - this.arcTolerance = arcTolerance; - lowest = new LongPoint(); - lowest.setX( -1L ); - polyNodes = new PolyNode(); - normals = new ArrayList<>(); - } - - public void addPath(Path path, JoinType joinType, EndType endType ) { - int highI = path.size() - 1; - if (highI < 0) { - return; - } - final PolyNode newNode = new PolyNode(); - newNode.setJoinType( joinType ); - newNode.setEndType( endType ); - - //strip duplicate points from path and also get index to the lowest point ... - if (endType == EndType.CLOSED_LINE || endType == EndType.CLOSED_POLYGON) { - while (highI > 0 && path.get( 0 ) == path.get( highI )) { - highI--; - } - } - - newNode.getPolygon().add( path.get( 0 ) ); - int j = 0, k = 0; - for (int i = 1; i <= highI; i++) { - if (newNode.getPolygon().get( j ) != path.get( i )) { - j++; - newNode.getPolygon().add( path.get( i ) ); - if (path.get( i ).getY() > newNode.getPolygon().get( k ).getY() || path.get( i ).getY() == newNode.getPolygon().get( k ).getY() - && path.get( i ).getX() < newNode.getPolygon().get( k ).getX()) { - k = j; - } - } - } - if (endType == EndType.CLOSED_POLYGON && j < 2) { - return; - } - - polyNodes.addChild( newNode ); - - //if this path's lowest pt is lower than all the others then update m_lowest - if (endType != EndType.CLOSED_POLYGON) { - return; - } - if (lowest.getX() < 0) { - lowest = new LongPoint( polyNodes.getChildCount() - 1, k ); - } - else { - final LongPoint ip = polyNodes.getChilds().get( (int) lowest.getX() ).getPolygon().get( (int) lowest.getY() ); - if (newNode.getPolygon().get( k ).getY() > ip.getY() || newNode.getPolygon().get( k ).getY() == ip.getY() - && newNode.getPolygon().get( k ).getX() < ip.getX()) { - lowest = new LongPoint( polyNodes.getChildCount() - 1, k ); - } - } - } - - public void addPaths(Paths paths, JoinType joinType, EndType endType ) { - for (final Path p : paths) { - addPath( p, joinType, endType ); - } - } - - public void clear() { - polyNodes.getChilds().clear(); - lowest.setX( -1L ); - } - - private void doMiter( int j, int k, double r ) { - final double q = delta / r; - destPoly.add( new LongPoint( Math.round( srcPoly.get( j ).getX() + (normals.get( k ).getX() + normals.get( j ).getX()) * q ), Math - .round( srcPoly.get( j ).getY() + (normals.get( k ).getY() + normals.get( j ).getY()) * q ) ) ); - } - - private void doOffset( double delta ) { - destPolys = new Paths(); - this.delta = delta; - - //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (nearZero( delta )) { - for (int i = 0; i < polyNodes.getChildCount(); i++) { - final PolyNode node = polyNodes.getChilds().get( i ); - if (node.getEndType() == EndType.CLOSED_POLYGON) { - destPolys.add( node.getPolygon() ); - } - } - return; - } - - //see offset_triginometry3.svg in the documentation folder ... - if (miterLimit > 2) { - miterLim = 2 / (miterLimit * miterLimit); - } - else { - miterLim = 0.5; - } - - double y; - if (arcTolerance <= 0.0) { - y = DEFAULT_ARC_TOLERANCE; - } - else if (arcTolerance > Math.abs( delta ) * DEFAULT_ARC_TOLERANCE) { - y = Math.abs( delta ) * DEFAULT_ARC_TOLERANCE; - } - else { - y = arcTolerance; - } - //see offset_triginometry2.svg in the documentation folder ... - final double steps = Math.PI / Math.acos( 1 - y / Math.abs( delta ) ); - sin = Math.sin( TWO_PI / steps ); - cos = Math.cos( TWO_PI / steps ); - stepsPerRad = steps / TWO_PI; - if (delta < 0.0) { - sin = -sin; - } - - for (int i = 0; i < polyNodes.getChildCount(); i++) { - final PolyNode node = polyNodes.getChilds().get( i ); - srcPoly = node.getPolygon(); - - final int len = srcPoly.size(); - - if (len == 0 || delta <= 0 && (len < 3 || node.getEndType() != EndType.CLOSED_POLYGON)) { - continue; - } - - destPoly = new Path(); - - if (len == 1) { - if (node.getJoinType() == JoinType.ROUND) { - double X = 1.0, Y = 0.0; - for (int j = 1; j <= steps; j++) { - destPoly.add( new LongPoint( Math.round( srcPoly.get( 0 ).getX() + X * delta ), Math.round( srcPoly.get( 0 ).getY() + Y - * delta ) ) ); - final double X2 = X; - X = X * cos - sin * Y; - Y = X2 * sin + Y * cos; - } - } - else { - double X = -1.0, Y = -1.0; - for (int j = 0; j < 4; ++j) { - destPoly.add( new LongPoint( Math.round( srcPoly.get( 0 ).getX() + X * delta ), Math.round( srcPoly.get( 0 ).getY() + Y - * delta ) ) ); - if (X < 0) { - X = 1; - } - else if (Y < 0) { - Y = 1; - } - else { - X = -1; - } - } - } - destPolys.add( destPoly ); - continue; - } - - //build m_normals ... - normals.clear(); - for (int j = 0; j < len - 1; j++) { - normals.add( Point.getUnitNormal( srcPoly.get( j ), srcPoly.get( j + 1 ) ) ); - } - if (node.getEndType() == EndType.CLOSED_LINE || node.getEndType() == EndType.CLOSED_POLYGON) { - normals.add( Point.getUnitNormal( srcPoly.get( len - 1 ), srcPoly.get( 0 ) ) ); - } - else { - normals.add( new DoublePoint( normals.get( len - 2 ) ) ); - } - - if (node.getEndType() == EndType.CLOSED_POLYGON) { - final int[] k = new int[] { len - 1 }; - for (int j = 0; j < len; j++) { - offsetPoint( j, k, node.getJoinType() ); - } - destPolys.add( destPoly ); - } - else if (node.getEndType() == EndType.CLOSED_LINE) { - final int[] k = new int[] { len - 1 }; - for (int j = 0; j < len; j++) { - offsetPoint( j, k, node.getJoinType() ); - } - destPolys.add( destPoly ); - destPoly = new Path(); - //re-build m_normals ... - final DoublePoint n = normals.get( len - 1 ); - for (int j = len - 1; j > 0; j--) { - normals.set( j, new DoublePoint( -normals.get( j - 1 ).getX(), -normals.get( j - 1 ).getY() ) ); - } - normals.set( 0, new DoublePoint( -n.getX(), -n.getY(), 0 ) ); - k[0] = 0; - for (int j = len - 1; j >= 0; j--) { - offsetPoint( j, k, node.getJoinType() ); - } - destPolys.add( destPoly ); - } - else { - final int[] k = new int[1]; - for (int j = 1; j < len - 1; ++j) { - offsetPoint( j, k, node.getJoinType() ); - } - - LongPoint pt1; - if (node.getEndType() == EndType.OPEN_BUTT) { - final int j = len - 1; - pt1 = new LongPoint( Math.round( srcPoly.get( j ).getX() + normals.get( j ).getX() * delta ), Math.round( srcPoly.get( j ) - .getY() + normals.get( j ).getY() * delta ), 0 ); - destPoly.add( pt1 ); - pt1 = new LongPoint( Math.round( srcPoly.get( j ).getX() - normals.get( j ).getX() * delta ), Math.round( srcPoly.get( j ) - .getY() - normals.get( j ).getY() * delta ), 0 ); - destPoly.add( pt1 ); - } - else { - final int j = len - 1; - k[0] = len - 2; - inA = 0; - normals.set( j, new DoublePoint( -normals.get( j ).getX(), -normals.get( j ).getY() ) ); - if (node.getEndType() == EndType.OPEN_SQUARE) { - doSquare( j, k[0] ); - } - else { - doRound( j, k[0] ); - } - } - - //re-build m_normals ... - for (int j = len - 1; j > 0; j--) { - normals.set( j, new DoublePoint( -normals.get( j - 1 ).getX(), -normals.get( j - 1 ).getY() ) ); - } - - normals.set( 0, new DoublePoint( -normals.get( 1 ).getX(), -normals.get( 1 ).getY() ) ); - - k[0] = len - 1; - for (int j = k[0] - 1; j > 0; --j) { - offsetPoint( j, k, node.getJoinType() ); - } - - if (node.getEndType() == EndType.OPEN_BUTT) { - pt1 = new LongPoint( Math.round( srcPoly.get( 0 ).getX() - normals.get( 0 ).getX() * delta ), Math.round( srcPoly.get( 0 ) - .getY() - normals.get( 0 ).getY() * delta ) ); - destPoly.add( pt1 ); - pt1 = new LongPoint( Math.round( srcPoly.get( 0 ).getX() + normals.get( 0 ).getX() * delta ), Math.round( srcPoly.get( 0 ) - .getY() + normals.get( 0 ).getY() * delta ) ); - destPoly.add( pt1 ); - } - else { - k[0] = 1; - inA = 0; - if (node.getEndType() == EndType.OPEN_SQUARE) { - doSquare( 0, 1 ); - } - else { - doRound( 0, 1 ); - } - } - destPolys.add( destPoly ); - } - } - } - - private void doRound( int j, int k ) { - final double a = Math.atan2( inA, normals.get( k ).getX() * normals.get( j ).getX() + normals.get( k ).getY() * normals.get( j ).getY() ); - final int steps = Math.max( (int) Math.round( stepsPerRad * Math.abs( a ) ), 1 ); - - double X = normals.get( k ).getX(), Y = normals.get( k ).getY(), X2; - for (int i = 0; i < steps; ++i) { - destPoly.add( new LongPoint( Math.round( srcPoly.get( j ).getX() + X * delta ), Math.round( srcPoly.get( j ).getY() + Y * delta ) ) ); - X2 = X; - X = X * cos - sin * Y; - Y = X2 * sin + Y * cos; - } - destPoly.add( new LongPoint( Math.round( srcPoly.get( j ).getX() + normals.get( j ).getX() * delta ), Math.round( srcPoly.get( j ).getY() - + normals.get( j ).getY() * delta ) ) ); - } - - private void doSquare( int j, int k ) { - final double nkx = normals.get( k ).getX(); - final double nky = normals.get( k ).getY(); - final double njx = normals.get( j ).getX(); - final double njy = normals.get( j ).getY(); - final double sjx = srcPoly.get( j ).getX(); - final double sjy = srcPoly.get( j ).getY(); - final double dx = Math.tan( Math.atan2( inA, nkx * njx + nky * njy ) / 4 ); - destPoly.add( new LongPoint( Math.round( sjx + delta * (nkx - nky * dx) ), Math.round( sjy + delta * (nky + nkx * dx) ), 0 ) ); - destPoly.add( new LongPoint( Math.round( sjx + delta * (njx + njy * dx) ), Math.round( sjy + delta * (njy - njx * dx) ), 0 ) ); - } - - //------------------------------------------------------------------------------ - - public void execute(Paths solution, double delta ) { - solution.clear(); - fixOrientations(); - doOffset( delta ); - //now clean up 'corners' ... - final DefaultClipper clpr = new DefaultClipper( Clipper.REVERSE_SOLUTION ); - clpr.addPaths( destPolys, PolyType.SUBJECT, true ); - if (delta > 0) { - clpr.execute( ClipType.UNION, solution, PolyFillType.POSITIVE, PolyFillType.POSITIVE ); - } - else { - final LongRect r = destPolys.getBounds(); - final Path outer = new Path( 4 ); - - outer.add( new LongPoint( r.left - 10, r.bottom + 10, 0 ) ); - outer.add( new LongPoint( r.right + 10, r.bottom + 10, 0 ) ); - outer.add( new LongPoint( r.right + 10, r.top - 10, 0 ) ); - outer.add( new LongPoint( r.left - 10, r.top - 10, 0 ) ); - - clpr.addPath( outer, PolyType.SUBJECT, true ); - - clpr.execute( ClipType.UNION, solution, PolyFillType.NEGATIVE, PolyFillType.NEGATIVE ); - if (solution.size() > 0) { - solution.remove( 0 ); - } - } - } - - //------------------------------------------------------------------------------ - - public void execute(PolyTree solution, double delta ) { - solution.Clear(); - fixOrientations(); - doOffset( delta ); - - //now clean up 'corners' ... - final DefaultClipper clpr = new DefaultClipper( Clipper.REVERSE_SOLUTION ); - clpr.addPaths( destPolys, PolyType.SUBJECT, true ); - if (delta > 0) { - clpr.execute( ClipType.UNION, solution, PolyFillType.POSITIVE, PolyFillType.POSITIVE ); - } - else { - final LongRect r = destPolys.getBounds(); - final Path outer = new Path( 4 ); - - outer.add( new LongPoint( r.left - 10, r.bottom + 10, 0 ) ); - outer.add( new LongPoint( r.right + 10, r.bottom + 10, 0 ) ); - outer.add( new LongPoint( r.right + 10, r.top - 10, 0 ) ); - outer.add( new LongPoint( r.left - 10, r.top - 10, 0 ) ); - - clpr.addPath( outer, PolyType.SUBJECT, true ); - - clpr.execute( ClipType.UNION, solution, PolyFillType.NEGATIVE, PolyFillType.NEGATIVE ); - //remove the outer PolyNode rectangle ... - if (solution.getChildCount() == 1 && solution.getChilds().get( 0 ).getChildCount() > 0) { - final PolyNode outerNode = solution.getChilds().get( 0 ); - solution.getChilds().set( 0, outerNode.getChilds().get( 0 ) ); - solution.getChilds().get( 0 ).setParent( solution ); - for (int i = 1; i < outerNode.getChildCount(); i++) { - solution.addChild( outerNode.getChilds().get( i ) ); - } - } - else { - solution.Clear(); - } - } - } - - //------------------------------------------------------------------------------ - - private void fixOrientations() { - //fixup orientations of all closed paths if the orientation of the - //closed path with the lowermost vertex is wrong ... - if (lowest.getX() >= 0 && !polyNodes.childs.get( (int) lowest.getX() ).getPolygon().orientation()) { - for (int i = 0; i < polyNodes.getChildCount(); i++) { - final PolyNode node = polyNodes.childs.get( i ); - if (node.getEndType() == EndType.CLOSED_POLYGON || node.getEndType() == EndType.CLOSED_LINE && node.getPolygon().orientation()) { - Collections.reverse( node.getPolygon() ); - - } - } - } - else { - for (int i = 0; i < polyNodes.getChildCount(); i++) { - final PolyNode node = polyNodes.childs.get( i ); - if (node.getEndType() == EndType.CLOSED_LINE && !node.getPolygon().orientation()) { - Collections.reverse( node.getPolygon() ); - } - } - } - } - - private void offsetPoint( int j, int[] kV, JoinType jointype ) { - //cross product ... - final int k = kV[0]; - final double nkx = normals.get( k ).getX(); - final double nky = normals.get( k ).getY(); - final double njy = normals.get( j ).getY(); - final double njx = normals.get( j ).getX(); - final long sjx = srcPoly.get( j ).getX(); - final long sjy = srcPoly.get( j ).getY(); - inA = nkx * njy - njx * nky; - - if (Math.abs( inA * delta ) < 1.0) { - //dot product ... - - final double cosA = nkx * njx + njy * nky; - if (cosA > 0) // angle ==> 0 degrees - { - destPoly.add( new LongPoint( Math.round( sjx + nkx * delta ), Math.round( sjy + nky * delta ), 0 ) ); - return; - } - //else angle ==> 180 degrees - } - else if (inA > 1.0) { - inA = 1.0; - } - else if (inA < -1.0) { - inA = -1.0; - } - - if (inA * delta < 0) { - destPoly.add( new LongPoint( Math.round( sjx + nkx * delta ), Math.round( sjy + nky * delta ) ) ); - destPoly.add( srcPoly.get( j ) ); - destPoly.add( new LongPoint( Math.round( sjx + njx * delta ), Math.round( sjy + njy * delta ) ) ); - } - else { - switch (jointype) { - case MITER: { - final double r = 1 + njx * nkx + njy * nky; - if (r >= miterLim) { - doMiter( j, k, r ); - } - else { - doSquare( j, k ); - } - break; - } - case SQUARE: - doSquare( j, k ); - break; - case ROUND: - doRound( j, k ); - break; - } - } - kV[0] = j; - } - //------------------------------------------------------------------------------ -} \ No newline at end of file diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/DefaultClipper.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/DefaultClipper.java deleted file mode 100644 index b9a519e..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/DefaultClipper.java +++ /dev/null @@ -1,2518 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Path.Join; -import com.visual.open.anpr.core.clipper.Path.OutRec; -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class DefaultClipper extends ClipperBase { - private class IntersectNode { - Edge edge1; - Edge Edge2; - private LongPoint pt; - - LongPoint getPt() { - return pt; - } - - void setPt( LongPoint pt ) { - this.pt = pt; - } - - } - - private static void getHorzDirection(Edge HorzEdge, Direction[] Dir, long[] Left, long[] Right ) { - if (HorzEdge.getBot().getX() < HorzEdge.getTop().getX()) { - Left[0] = HorzEdge.getBot().getX(); - Right[0] = HorzEdge.getTop().getX(); - Dir[0] = Direction.LEFT_TO_RIGHT; - } - else { - Left[0] = HorzEdge.getTop().getX(); - Right[0] = HorzEdge.getBot().getX(); - Dir[0] = Direction.RIGHT_TO_LEFT; - } - } - - private static boolean getOverlap( long a1, long a2, long b1, long b2, long[] Left, long[] Right ) { - if (a1 < a2) { - if (b1 < b2) { - Left[0] = Math.max( a1, b1 ); - Right[0] = Math.min( a2, b2 ); - } - else { - Left[0] = Math.max( a1, b2 ); - Right[0] = Math.min( a2, b1 ); - } - } - else { - if (b1 < b2) { - Left[0] = Math.max( a2, b1 ); - Right[0] = Math.min( a1, b2 ); - } - else { - Left[0] = Math.max( a2, b2 ); - Right[0] = Math.min( a1, b1 ); - } - } - return Left[0] < Right[0]; - } - - private static boolean isOutRec1RightOfOutRec2(OutRec outRec1, OutRec outRec2 ) { - do { - outRec1 = outRec1.firstLeft; - if (outRec1 == outRec2) { - return true; - } - } - while (outRec1 != null); - return false; - } - - //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos - //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf - private static int isPointInPolygon(LongPoint pt, Path.OutPt op ) { - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - final Path.OutPt startOp = op; - final long ptx = pt.getX(), pty = pt.getY(); - long poly0x = op.getPt().getX(), poly0y = op.getPt().getY(); - do { - op = op.next; - final long poly1x = op.getPt().getX(), poly1y = op.getPt().getY(); - - if (poly1y == pty) { - if (poly1x == ptx || poly0y == pty && poly1x > ptx == poly0x < ptx) { - return -1; - } - } - if (poly0y < pty != poly1y < pty) { - if (poly0x >= ptx) { - if (poly1x > ptx) { - result = 1 - result; - } - else { - final double d = (double) (poly0x - ptx) * (poly1y - pty) - (double) (poly1x - ptx) * (poly0y - pty); - if (d == 0) { - return -1; - } - if (d > 0 == poly1y > poly0y) { - result = 1 - result; - } - } - } - else { - if (poly1x > ptx) { - final double d = (double) (poly0x - ptx) * (poly1y - pty) - (double) (poly1x - ptx) * (poly0y - pty); - if (d == 0) { - return -1; - } - if (d > 0 == poly1y > poly0y) { - result = 1 - result; - } - } - } - } - poly0x = poly1x; - poly0y = poly1y; - } - while (startOp != op); - - return result; - } - - //------------------------------------------------------------------------------ - private static boolean joinHorz(Path.OutPt op1, Path.OutPt op1b, Path.OutPt op2, Path.OutPt op2b, LongPoint Pt, boolean DiscardLeft ) { - final Direction Dir1 = op1.getPt().getX() > op1b.getPt().getX() ? Direction.RIGHT_TO_LEFT : Direction.LEFT_TO_RIGHT; - final Direction Dir2 = op2.getPt().getX() > op2b.getPt().getX() ? Direction.RIGHT_TO_LEFT : Direction.LEFT_TO_RIGHT; - if (Dir1 == Dir2) { - return false; - } - - //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - //So, to facilitate this while inserting Op1b and Op2b ... - //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == Direction.LEFT_TO_RIGHT) { - while (op1.next.getPt().getX() <= Pt.getX() && op1.next.getPt().getX() >= op1.getPt().getX() && op1.next.getPt().getY() == Pt.getY()) { - op1 = op1.next; - } - if (DiscardLeft && op1.getPt().getX() != Pt.getX()) { - op1 = op1.next; - } - op1b = op1.duplicate( !DiscardLeft ); - if (!op1b.getPt().equals( Pt )) { - op1 = op1b; - op1.setPt( new LongPoint( Pt ) ); - op1b = op1.duplicate( !DiscardLeft ); - } - } - else { - while (op1.next.getPt().getX() >= Pt.getX() && op1.next.getPt().getX() <= op1.getPt().getX() && op1.next.getPt().getY() == Pt.getY()) { - op1 = op1.next; - } - if (!DiscardLeft && op1.getPt().getX() != Pt.getX()) { - op1 = op1.next; - } - op1b = op1.duplicate( DiscardLeft ); - if (!op1b.getPt().equals( Pt )) { - op1 = op1b; - op1.setPt( new LongPoint( Pt ) ); - op1b = op1.duplicate( DiscardLeft ); - } - } - - if (Dir2 == Direction.LEFT_TO_RIGHT) { - while (op2.next.getPt().getX() <= Pt.getX() && op2.next.getPt().getX() >= op2.getPt().getX() && op2.next.getPt().getY() == Pt.getY()) { - op2 = op2.next; - } - if (DiscardLeft && op2.getPt().getX() != Pt.getX()) { - op2 = op2.next; - } - op2b = op2.duplicate( !DiscardLeft ); - if (!op2b.getPt().equals( Pt )) { - op2 = op2b; - op2.setPt( new LongPoint( Pt ) ); - op2b = op2.duplicate( !DiscardLeft ); - } - } - else { - while (op2.next.getPt().getX() >= Pt.getX() && op2.next.getPt().getX() <= op2.getPt().getX() && op2.next.getPt().getY() == Pt.getY()) { - op2 = op2.next; - } - if (!DiscardLeft && op2.getPt().getX() != Pt.getX()) { - op2 = op2.next; - } - op2b = op2.duplicate( DiscardLeft ); - if (!op2b.getPt().equals( Pt )) { - op2 = op2b; - op2.setPt( new LongPoint( Pt ) ); - op2b = op2.duplicate( DiscardLeft ); - } - } - - if (Dir1 == Direction.LEFT_TO_RIGHT == DiscardLeft) { - op1.prev = op2; - op2.next = op1; - op1b.next = op2b; - op2b.prev = op1b; - } - else { - op1.next = op2; - op2.prev = op1; - op1b.prev = op2b; - op2b.next = op1b; - } - return true; - } - - private static boolean joinPoints(Join j, OutRec outRec1, OutRec outRec2 ) { - Path.OutPt op1 = j.outPt1, op1b; - Path.OutPt op2 = j.outPt2, op2b; - - //There are 3 kinds of joins for output polygons ... - //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - //location at the Bottom of the overlapping segment (& Join.OffPt is above). - //3. StrictlySimple joins where edges touch but are not collinear and where - //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - final boolean isHorizontal = j.outPt1.getPt().getY() == j.getOffPt().getY(); - - if (isHorizontal && j.getOffPt().equals( j.outPt1.getPt() ) && j.getOffPt().equals( j.outPt2.getPt() )) { - //Strictly Simple join ... - if (outRec1 != outRec2) { - return false; - } - op1b = j.outPt1.next; - while (op1b != op1 && op1b.getPt().equals( j.getOffPt() )) { - op1b = op1b.next; - } - final boolean reverse1 = op1b.getPt().getY() > j.getOffPt().getY(); - op2b = j.outPt2.next; - while (op2b != op2 && op2b.getPt().equals( j.getOffPt() )) { - op2b = op2b.next; - } - final boolean reverse2 = op2b.getPt().getY() > j.getOffPt().getY(); - if (reverse1 == reverse2) { - return false; - } - if (reverse1) { - op1b = op1.duplicate( false ); - op2b = op2.duplicate( true ); - op1.prev = op2; - op2.next = op1; - op1b.next = op2b; - op2b.prev = op1b; - j.outPt1 = op1; - j.outPt2 = op1b; - return true; - } - else { - op1b = op1.duplicate( true ); - op2b = op2.duplicate( false ); - op1.next = op2; - op2.prev = op1; - op1b.prev = op2b; - op2b.next = op1b; - j.outPt1 = op1; - j.outPt2 = op1b; - return true; - } - } - else if (isHorizontal) { - //treat horizontal joins differently to non-horizontal joins since with - //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - //may be anywhere along the horizontal edge. - op1b = op1; - while (op1.prev.getPt().getY() == op1.getPt().getY() && op1.prev != op1b && op1.prev != op2) { - op1 = op1.prev; - } - while (op1b.next.getPt().getY() == op1b.getPt().getY() && op1b.next != op1 && op1b.next != op2) { - op1b = op1b.next; - } - if (op1b.next == op1 || op1b.next == op2) { - return false; - } //a flat 'polygon' - - op2b = op2; - while (op2.prev.getPt().getY() == op2.getPt().getY() && op2.prev != op2b && op2.prev != op1b) { - op2 = op2.prev; - } - while (op2b.next.getPt().getY() == op2b.getPt().getY() && op2b.next != op2 && op2b.next != op1) { - op2b = op2b.next; - } - if (op2b.next == op2 || op2b.next == op1) { - return false; - } //a flat 'polygon' - - final long[] LeftV = new long[1], RightV = new long[1]; - //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges - if (!getOverlap( op1.getPt().getX(), op1b.getPt().getX(), op2.getPt().getX(), op2b.getPt().getX(), LeftV, RightV )) { - return false; - } - final long Left = LeftV[0]; - final long Right = RightV[0]; - - //DiscardLeftSide: when overlapping edges are joined, a spike will created - //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - //on the discard Side as either may still be needed for other joins ... - LongPoint Pt; - boolean DiscardLeftSide; - if (op1.getPt().getX() >= Left && op1.getPt().getX() <= Right) { - Pt = new LongPoint( op1.getPt() ); - DiscardLeftSide = op1.getPt().getX() > op1b.getPt().getX(); - } - else if (op2.getPt().getX() >= Left && op2.getPt().getX() <= Right) { - Pt = new LongPoint( op2.getPt() ); - DiscardLeftSide = op2.getPt().getX() > op2b.getPt().getX(); - } - else if (op1b.getPt().getX() >= Left && op1b.getPt().getX() <= Right) { - Pt = new LongPoint( op1b.getPt() ); - DiscardLeftSide = op1b.getPt().getX() > op1.getPt().getX(); - } - else { - Pt = new LongPoint( op2b.getPt() ); - DiscardLeftSide = op2b.getPt().getX() > op2.getPt().getX(); - } - j.outPt1 = op1; - j.outPt2 = op2; - return joinHorz( op1, op1b, op2, op2b, Pt, DiscardLeftSide ); - } - else { - //nb: For non-horizontal joins ... - // 1. Jr.OutPt1.getPt().getY() == Jr.OutPt2.getPt().getY() - // 2. Jr.OutPt1.Pt > Jr.OffPt.getY() - - //make sure the polygons are correctly oriented ... - op1b = op1.next; - while (op1b.getPt().equals( op1.getPt() ) && op1b != op1) { - op1b = op1b.next; - } - final boolean Reverse1 = op1b.getPt().getY() > op1.getPt().getY() || !Point.slopesEqual( op1.getPt(), op1b.getPt(), j.getOffPt() ); - if (Reverse1) { - op1b = op1.prev; - while (op1b.getPt().equals( op1.getPt() ) && op1b != op1) { - op1b = op1b.prev; - } - if (op1b.getPt().getY() > op1.getPt().getY() || !Point.slopesEqual( op1.getPt(), op1b.getPt(), j.getOffPt() )) { - return false; - } - } - op2b = op2.next; - while (op2b.getPt().equals( op2.getPt() ) && op2b != op2) { - op2b = op2b.next; - } - final boolean Reverse2 = op2b.getPt().getY() > op2.getPt().getY() || !Point.slopesEqual( op2.getPt(), op2b.getPt(), j.getOffPt() ); - if (Reverse2) { - op2b = op2.prev; - while (op2b.getPt().equals( op2.getPt() ) && op2b != op2) { - op2b = op2b.prev; - } - if (op2b.getPt().getY() > op2.getPt().getY() || !Point.slopesEqual( op2.getPt(), op2b.getPt(), j.getOffPt() )) { - return false; - } - } - - if (op1b == op1 || op2b == op2 || op1b == op2b || outRec1 == outRec2 && Reverse1 == Reverse2) { - return false; - } - - if (Reverse1) { - op1b = op1.duplicate( false ); - op2b = op2.duplicate( true ); - op1.prev = op2; - op2.next = op1; - op1b.next = op2b; - op2b.prev = op1b; - j.outPt1 = op1; - j.outPt2 = op1b; - return true; - } - else { - op1b = op1.duplicate( true ); - op2b = op2.duplicate( false ); - op1.next = op2; - op2.prev = op1; - op1b.prev = op2b; - op2b.next = op1b; - j.outPt1 = op1; - j.outPt2 = op1b; - return true; - } - } - } - - private static Paths minkowski(Path pattern, Path path, boolean IsSum, boolean IsClosed ) { - final int delta = IsClosed ? 1 : 0; - final int polyCnt = pattern.size(); - final int pathCnt = path.size(); - final Paths result = new Paths( pathCnt ); - if (IsSum) { - for (int i = 0; i < pathCnt; i++) { - final Path p = new Path( polyCnt ); - for (final LongPoint ip : pattern) { - p.add( new LongPoint( path.get( i ).getX() + ip.getX(), path.get( i ).getY() + ip.getY(), 0 ) ); - } - result.add( p ); - } - } - else { - for (int i = 0; i < pathCnt; i++) { - final Path p = new Path( polyCnt ); - for (final LongPoint ip : pattern) { - p.add( new LongPoint( path.get( i ).getX() - ip.getX(), path.get( i ).getY() - ip.getY(), 0 ) ); - } - result.add( p ); - } - } - - final Paths quads = new Paths( (pathCnt + delta) * (polyCnt + 1) ); - for (int i = 0; i < pathCnt - 1 + delta; i++) { - for (int j = 0; j < polyCnt; j++) { - final Path quad = new Path( 4 ); - quad.add( result.get( i % pathCnt ).get( j % polyCnt ) ); - quad.add( result.get( (i + 1) % pathCnt ).get( j % polyCnt ) ); - quad.add( result.get( (i + 1) % pathCnt ).get( (j + 1) % polyCnt ) ); - quad.add( result.get( i % pathCnt ).get( (j + 1) % polyCnt ) ); - if (!quad.orientation()) { - Collections.reverse( quad ); - } - quads.add( quad ); - } - } - return quads; - } - - public static Paths minkowskiDiff(Path poly1, Path poly2 ) { - final Paths paths = minkowski( poly1, poly2, false, true ); - final DefaultClipper c = new DefaultClipper(); - c.addPaths( paths, PolyType.SUBJECT, true ); - c.execute( ClipType.UNION, paths, PolyFillType.NON_ZERO, PolyFillType.NON_ZERO ); - return paths; - } - - public static Paths minkowskiSum(Path pattern, Path path, boolean pathIsClosed ) { - final Paths paths = minkowski( pattern, path, true, pathIsClosed ); - final DefaultClipper c = new DefaultClipper(); - c.addPaths( paths, PolyType.SUBJECT, true ); - c.execute( ClipType.UNION, paths, PolyFillType.NON_ZERO, PolyFillType.NON_ZERO ); - return paths; - } - - public static Paths minkowskiSum(Path pattern, Paths paths, boolean pathIsClosed ) { - final Paths solution = new Paths(); - final DefaultClipper c = new DefaultClipper(); - for (int i = 0; i < paths.size(); ++i) { - final Paths tmp = minkowski( pattern, paths.get( i ), true, pathIsClosed ); - c.addPaths( tmp, PolyType.SUBJECT, true ); - if (pathIsClosed) { - final Path path = paths.get( i ).TranslatePath( pattern.get( 0 ) ); - c.addPath( path, PolyType.CLIP, true ); - } - } - c.execute( ClipType.UNION, solution, PolyFillType.NON_ZERO, PolyFillType.NON_ZERO ); - return solution; - } - - private static boolean poly2ContainsPoly1(Path.OutPt outPt1, Path.OutPt outPt2 ) { - Path.OutPt op = outPt1; - do { - //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - final int res = isPointInPolygon( op.getPt(), outPt2 ); - if (res >= 0) { - return res > 0; - } - op = op.next; - } - while (op != outPt1); - return true; - } - - //------------------------------------------------------------------------------ - // SimplifyPolygon functions ... - // Convert self-intersecting polygons into simple polygons - //------------------------------------------------------------------------------ - public static Paths simplifyPolygon(Path poly ) { - return simplifyPolygon( poly, PolyFillType.EVEN_ODD ); - } - - public static Paths simplifyPolygon(Path poly, PolyFillType fillType ) { - final Paths result = new Paths(); - final DefaultClipper c = new DefaultClipper( STRICTLY_SIMPLE ); - - c.addPath( poly, PolyType.SUBJECT, true ); - c.execute( ClipType.UNION, result, fillType, fillType ); - return result; - } - - public static Paths simplifyPolygons(Paths polys ) { - return simplifyPolygons( polys, PolyFillType.EVEN_ODD ); - } - - public static Paths simplifyPolygons(Paths polys, PolyFillType fillType ) { - final Paths result = new Paths(); - final DefaultClipper c = new DefaultClipper( STRICTLY_SIMPLE ); - - c.addPaths( polys, PolyType.SUBJECT, true ); - c.execute( ClipType.UNION, result, fillType, fillType ); - return result; - } - - private ClipType clipType; - - private Maxima maxima; - - private Edge sortedEdges; - - private final List intersectList; - - private final Comparator intersectNodeComparer; - - private PolyFillType clipFillType; - - //------------------------------------------------------------------------------ - - private PolyFillType subjFillType; - - //------------------------------------------------------------------------------ - - private final List joins; - - //------------------------------------------------------------------------------ - - private final List ghostJoins; - - private boolean usingPolyTree; - - private ZFillCallback zFillFunction; - - //------------------------------------------------------------------------------ - - private final boolean reverseSolution; - - //------------------------------------------------------------------------------ - - private final boolean strictlySimple; - - private final static Logger LOGGER = Logger.getLogger( DefaultClipper.class.getName() ); - - public DefaultClipper() { - this( 0 ); - } - - public DefaultClipper( int InitOptions ) //constructor - { - super( (PRESERVE_COLINEAR & InitOptions) != 0 ); - scanbeam = null; - maxima = null; - activeEdges = null; - sortedEdges = null; - intersectList = new ArrayList<>(); - intersectNodeComparer = ( node1, node2 ) -> { - final long i = node2.getPt().getY() - node1.getPt().getY(); - if (i > 0) { - return 1; - } - else if (i < 0) { - return -1; - } - else { - return 0; - } - }; - - usingPolyTree = false; - joins = new ArrayList<>(); - ghostJoins = new ArrayList<>(); - reverseSolution = (REVERSE_SOLUTION & InitOptions) != 0; - strictlySimple = (STRICTLY_SIMPLE & InitOptions) != 0; - - zFillFunction = null; - - } - - private void addEdgeToSEL( Edge edge ) { - LOGGER.entering( DefaultClipper.class.getName(), "addEdgeToSEL" ); - - //SEL pointers in PEdge are use to build transient lists of horizontal edges. - //However, since we don't need to worry about processing order, all additions - //are made to the front of the list ... - - if (sortedEdges == null) { - sortedEdges = edge; - edge.prevInSEL = null; - edge.nextInSEL = null; - } - else { - edge.nextInSEL = sortedEdges; - edge.prevInSEL = null; - sortedEdges.prevInSEL = edge; - sortedEdges = edge; - } - } - - private void addGhostJoin(Path.OutPt Op, LongPoint OffPt ) { - final Join j = new Join(); - j.outPt1 = Op; - j.setOffPt( new LongPoint( OffPt ) ); - ghostJoins.add( j ); - } - - //------------------------------------------------------------------------------ - - private void addJoin(Path.OutPt Op1, Path.OutPt Op2, LongPoint OffPt ) { - LOGGER.entering( DefaultClipper.class.getName(), "addJoin" ); - final Join j = new Join(); - j.outPt1 = Op1; - j.outPt2 = Op2; - j.setOffPt( new LongPoint( OffPt ) ); - joins.add( j ); - } - - //------------------------------------------------------------------------------ - - private void addLocalMaxPoly(Edge e1, Edge e2, LongPoint pt ) { - addOutPt( e1, pt ); - if (e2.windDelta == 0) { - addOutPt( e2, pt ); - } - if (e1.outIdx == e2.outIdx) { - e1.outIdx = Edge.UNASSIGNED; - e2.outIdx = Edge.UNASSIGNED; - } - else if (e1.outIdx < e2.outIdx) { - appendPolygon( e1, e2 ); - } - else { - appendPolygon( e2, e1 ); - } - } - - //------------------------------------------------------------------------------ - - private Path.OutPt addLocalMinPoly(Edge e1, Edge e2, LongPoint pt ) { - LOGGER.entering( DefaultClipper.class.getName(), "addLocalMinPoly" ); - Path.OutPt result; - Edge e, prevE; - if (e2.isHorizontal() || e1.deltaX > e2.deltaX) { - result = addOutPt( e1, pt ); - e2.outIdx = e1.outIdx; - e1.side = Edge.Side.LEFT; - e2.side = Edge.Side.RIGHT; - e = e1; - if (e.prevInAEL == e2) { - prevE = e2.prevInAEL; - } - else { - prevE = e.prevInAEL; - } - } - else { - result = addOutPt( e2, pt ); - e1.outIdx = e2.outIdx; - e1.side = Edge.Side.RIGHT; - e2.side = Edge.Side.LEFT; - e = e2; - if (e.prevInAEL == e1) { - prevE = e1.prevInAEL; - } - else { - prevE = e.prevInAEL; - } - } - - if (prevE != null && prevE.outIdx >= 0 && prevE.getTop().getY() < pt.getY() && e.getTop().getY() < pt.getY()) { - long xPrev = Edge.topX( prevE, pt.getY() ); - long xE = Edge.topX( e, pt.getY() ); - if (xPrev == xE && e.windDelta != 0 && prevE.windDelta != 0 && - Point.slopesEqual( new LongPoint( xPrev, pt.getY() ), prevE.getTop(), new LongPoint( xE, pt.getY() ), e.getTop() )) { - final Path.OutPt outPt = addOutPt( prevE, pt ); - addJoin( result, outPt, e.getTop() ); - } - } - return result; - } - - private Path.OutPt addOutPt(Edge e, LongPoint pt ) { - LOGGER.entering( DefaultClipper.class.getName(), "addOutPt" ); - if (e.outIdx < 0) { - final OutRec outRec = createOutRec(); - outRec.isOpen = e.windDelta == 0; - final Path.OutPt newOp = new Path.OutPt(); - outRec.setPoints( newOp ); - newOp.idx = outRec.Idx; - newOp.setPt( new LongPoint( pt ) ); - newOp.next = newOp; - newOp.prev = newOp; - if (!outRec.isOpen) { - setHoleState( e, outRec ); - } - e.outIdx = outRec.Idx; //nb: do this after SetZ ! - return newOp; - } - else { - - final OutRec outRec = polyOuts.get( e.outIdx ); - //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - final Path.OutPt op = outRec.getPoints(); - final boolean ToFront = e.side == Edge.Side.LEFT; - if (LOGGER.isLoggable( Level.FINEST )) { - LOGGER.finest( "op=" + Path.OutPt.getPointCount( op ) ); - LOGGER.finest( ToFront + " " + pt + " " + op.getPt() ); - } - if (ToFront && pt.equals( op.getPt() )) { - return op; - } - else if (!ToFront && pt.equals( op.prev.getPt() )) { - return op.prev; - } - - final Path.OutPt newOp = new Path.OutPt(); - newOp.idx = outRec.Idx; - newOp.setPt( new LongPoint( pt ) ); - newOp.next = op; - newOp.prev = op.prev; - newOp.prev.next = newOp; - op.prev = newOp; - if (ToFront) { - outRec.setPoints( newOp ); - } - return newOp; - } - } - - private Path.OutPt getLastOutPt(Edge e) { - OutRec outRec = polyOuts.get( e.outIdx ); - if (e.side == Edge.Side.LEFT) - return outRec.getPoints(); - else - return outRec.getPoints().prev; - } - - private void appendPolygon(Edge e1, Edge e2 ) { - LOGGER.entering( DefaultClipper.class.getName(), "appendPolygon" ); - - final OutRec outRec1 = polyOuts.get( e1.outIdx ); - final OutRec outRec2 = polyOuts.get( e2.outIdx ); - LOGGER.finest( "" + e1.outIdx ); - LOGGER.finest( "" + e2.outIdx ); - - OutRec holeStateRec; - if (isOutRec1RightOfOutRec2( outRec1, outRec2 )) { - holeStateRec = outRec2; - } - else if (isOutRec1RightOfOutRec2( outRec2, outRec1 )) { - holeStateRec = outRec1; - } - else { - holeStateRec = Path.OutPt.getLowerMostRec( outRec1, outRec2 ); - } - - //get the start and ends of both output polygons and - //join E2 poly onto E1 poly and delete pointers to E2 ... - final Path.OutPt p1_lft = outRec1.getPoints(); - final Path.OutPt p1_rt = p1_lft.prev; - final Path.OutPt p2_lft = outRec2.getPoints(); - final Path.OutPt p2_rt = p2_lft.prev; - - LOGGER.finest( "p1_lft.getPointCount() = " + Path.OutPt.getPointCount( p1_lft ) ); - LOGGER.finest( "p1_rt.getPointCount() = " + Path.OutPt.getPointCount( p1_rt ) ); - LOGGER.finest( "p2_lft.getPointCount() = " + Path.OutPt.getPointCount( p2_lft ) ); - LOGGER.finest( "p2_rt.getPointCount() = " + Path.OutPt.getPointCount( p2_rt ) ); - - //join e2 poly onto e1 poly and delete pointers to e2 ... - if (e1.side == Edge.Side.LEFT) { - if (e2.side == Edge.Side.LEFT) { - //z y x a b c - p2_lft.reversePolyPtLinks(); - p2_lft.next = p1_lft; - p1_lft.prev = p2_lft; - p1_rt.next = p2_rt; - p2_rt.prev = p1_rt; - outRec1.setPoints( p2_rt ); - } - else { - //x y z a b c - p2_rt.next = p1_lft; - p1_lft.prev = p2_rt; - p2_lft.prev = p1_rt; - p1_rt.next = p2_lft; - outRec1.setPoints( p2_lft ); - } - } - else { - if (e2.side == Edge.Side.RIGHT) { - //a b c z y x - p2_lft.reversePolyPtLinks(); - p1_rt.next = p2_rt; - p2_rt.prev = p1_rt; - p2_lft.next = p1_lft; - p1_lft.prev = p2_lft; - } - else { - //a b c x y z - p1_rt.next = p2_lft; - p2_lft.prev = p1_rt; - p1_lft.prev = p2_rt; - p2_rt.next = p1_lft; - } - } - outRec1.bottomPt = null; - if (holeStateRec.equals( outRec2 )) { - if (outRec2.firstLeft != outRec1) { - outRec1.firstLeft = outRec2.firstLeft; - } - outRec1.isHole = outRec2.isHole; - } - outRec2.setPoints( null ); - outRec2.bottomPt = null; - - outRec2.firstLeft = outRec1; - - final int OKIdx = e1.outIdx; - final int ObsoleteIdx = e2.outIdx; - - e1.outIdx = Edge.UNASSIGNED; //nb: safe because we only get here via AddLocalMaxPoly - e2.outIdx = Edge.UNASSIGNED; - - Edge e = activeEdges; - while (e != null) { - if (e.outIdx == ObsoleteIdx) { - e.outIdx = OKIdx; - e.side = e1.side; - break; - } - e = e.nextInAEL; - } - outRec2.Idx = outRec1.Idx; - } - - //------------------------------------------------------------------------------ - - private void buildIntersectList( long topY ) { - if (activeEdges == null) { - return; - } - - //prepare for sorting ... - Edge e = activeEdges; - sortedEdges = e; - while (e != null) { - e.prevInSEL = e.prevInAEL; - e.nextInSEL = e.nextInAEL; - e.getCurrent().setX( Edge.topX( e, topY ) ); - e = e.nextInAEL; - } - - //bubblesort ... - boolean isModified = true; - while (isModified && sortedEdges != null) { - isModified = false; - e = sortedEdges; - while (e.nextInSEL != null) { - final Edge eNext = e.nextInSEL; - final LongPoint[] pt = new LongPoint[1]; - if (e.getCurrent().getX() > eNext.getCurrent().getX()) { - intersectPoint( e, eNext, pt ); - if (pt[0].getY() < topY) { - pt[0] = new LongPoint( Edge.topX( e, topY ), topY ); - } - final IntersectNode newNode = new IntersectNode(); - newNode.edge1 = e; - newNode.Edge2 = eNext; - newNode.setPt( new LongPoint( pt[0] ) ); // TODO is new instance necessary? - intersectList.add( newNode ); - - swapPositionsInSEL( e, eNext ); - isModified = true; - } - else { - e = eNext; - } - } - if (e.prevInSEL != null) { - e.prevInSEL.nextInSEL = null; - } - else { - break; - } - } - sortedEdges = null; - } - - //------------------------------------------------------------------------------ - - private void buildResult( Paths polyg ) { - polyg.clear(); - for (int i = 0; i < polyOuts.size(); i++) { - final OutRec outRec = polyOuts.get( i ); - if (outRec.getPoints() == null) { - continue; - } - Path.OutPt p = outRec.getPoints().prev; - final int cnt = Path.OutPt.getPointCount( p ); - LOGGER.finest( "cnt = " + cnt ); - if (cnt < 2) { - continue; - } - final Path pg = new Path( cnt ); - for (int j = 0; j < cnt; j++) { - pg.add( new LongPoint( p.getPt() ) ); - p = p.prev; - } - polyg.add( pg ); - } - } - - private void buildResult2( PolyTree polytree ) { - polytree.Clear(); - - //add each output polygon/contour to polytree ... - for (int i = 0; i < polyOuts.size(); i++) { - final OutRec outRec = polyOuts.get( i ); - final int cnt = Path.OutPt.getPointCount( outRec.getPoints() ); - if (outRec.isOpen && cnt < 2 || !outRec.isOpen && cnt < 3) { - continue; - } - outRec.fixHoleLinkage(); - final PolyNode pn = new PolyNode(); - polytree.getAllPolys().add( pn ); - outRec.polyNode = pn; - Path.OutPt op = outRec.getPoints().prev; - for (int j = 0; j < cnt; j++) { - pn.getPolygon().add( op.getPt() ); - op = op.prev; - } - } - - //fixup PolyNode links etc ... - for (int i = 0; i < polyOuts.size(); i++) { - final OutRec outRec = polyOuts.get( i ); - if (outRec.polyNode == null) { - continue; - } - else if (outRec.isOpen) { - outRec.polyNode.setOpen( true ); - polytree.addChild( outRec.polyNode ); - } - else if (outRec.firstLeft != null && outRec.firstLeft.polyNode != null) { - outRec.firstLeft.polyNode.addChild( outRec.polyNode ); - } - else { - polytree.addChild( outRec.polyNode ); - } - } - } - - private void copyAELToSEL() { - Edge e = activeEdges; - sortedEdges = e; - while (e != null) { - e.prevInSEL = e.prevInAEL; - e.nextInSEL = e.nextInAEL; - e = e.nextInAEL; - } - } - - private boolean deleteFromSEL( Edge[] e ) { - LOGGER.entering( DefaultClipper.class.getName(), "deleteFromSEL" ); - - //Pop edge from front of SEL (ie SEL is a FILO list) - e[0] = sortedEdges; - if (e[0] == null) { - return false; - } - final Edge oldE = e[0]; - sortedEdges = e[0].nextInSEL; - if (sortedEdges != null) { - sortedEdges.prevInSEL = null; - } - oldE.nextInSEL = null; - oldE.prevInSEL = null; - return true; - } - - private boolean doHorzSegmentsOverlap( long seg1a, long seg1b, long seg2a, long seg2b ) { - if (seg1a > seg1b) { - final long tmp = seg1a; - seg1a = seg1b; - seg1b = tmp; - } - if (seg2a > seg2b) { - final long tmp = seg2a; - seg2a = seg2b; - seg2b = tmp; - } - return seg1a < seg2b && seg2a < seg1b; - } - - private void doMaxima( Edge e ) { - final Edge eMaxPair = e.getMaximaPairEx(); - if (eMaxPair == null) { - if (e.outIdx >= 0) { - addOutPt( e, e.getTop() ); - } - deleteFromAEL( e ); - return; - } - - Edge eNext = e.nextInAEL; - while (eNext != null && eNext != eMaxPair) { - final LongPoint tmp = new LongPoint( e.getTop() ); - intersectEdges( e, eNext, tmp ); - e.setTop( new LongPoint( tmp ) ); - swapPositionsInAEL( e, eNext ); - eNext = e.nextInAEL; - } - - if (e.outIdx == Edge.UNASSIGNED && eMaxPair.outIdx == Edge.UNASSIGNED) { - deleteFromAEL( e ); - deleteFromAEL( eMaxPair ); - } - else if (e.outIdx >= 0 && eMaxPair.outIdx >= 0) { - if (e.outIdx >= 0) { - addLocalMaxPoly( e, eMaxPair, e.getTop() ); - } - deleteFromAEL( e ); - deleteFromAEL( eMaxPair ); - } - - else if (e.windDelta == 0) { - if (e.outIdx >= 0) { - addOutPt( e, e.getTop() ); - e.outIdx = Edge.UNASSIGNED; - } - deleteFromAEL( e ); - - if (eMaxPair.outIdx >= 0) { - addOutPt( eMaxPair, e.getTop() ); - eMaxPair.outIdx = Edge.UNASSIGNED; - } - deleteFromAEL( eMaxPair ); - } - else { - throw new IllegalStateException( "DoMaxima error" ); - } - } - - //------------------------------------------------------------------------------ - - private void doSimplePolygons() { - int i = 0; - while (i < polyOuts.size()) { - final OutRec outrec = polyOuts.get( i++ ); - Path.OutPt op = outrec.getPoints(); - if (op == null || outrec.isOpen) { - continue; - } - do //for each Pt in Polygon until duplicate found do ... - { - Path.OutPt op2 = op.next; - while (op2 != outrec.getPoints()) { - if (op.getPt().equals( op2.getPt() ) && !op2.next.equals( op ) && !op2.prev.equals( op )) { - //split the polygon into two ... - final Path.OutPt op3 = op.prev; - final Path.OutPt op4 = op2.prev; - op.prev = op4; - op4.next = op; - op2.prev = op3; - op3.next = op2; - - outrec.setPoints( op ); - final OutRec outrec2 = createOutRec(); - outrec2.setPoints( op2 ); - updateOutPtIdxs( outrec2 ); - if (poly2ContainsPoly1( outrec2.getPoints(), outrec.getPoints() )) { - //OutRec2 is contained by OutRec1 ... - outrec2.isHole = !outrec.isHole; - outrec2.firstLeft = outrec; - if (usingPolyTree) { - fixupFirstLefts2( outrec2, outrec ); - } - } - else if (poly2ContainsPoly1( outrec.getPoints(), outrec2.getPoints() )) { - //OutRec1 is contained by OutRec2 ... - outrec2.isHole = outrec.isHole; - outrec.isHole = !outrec2.isHole; - outrec2.firstLeft = outrec.firstLeft; - outrec.firstLeft = outrec2; - if (usingPolyTree) { - fixupFirstLefts2( outrec, outrec2 ); - } - } - else { - //the 2 polygons are separate ... - outrec2.isHole = outrec.isHole; - outrec2.firstLeft = outrec.firstLeft; - if (usingPolyTree) { - fixupFirstLefts1( outrec, outrec2 ); - } - } - op2 = op; //ie get ready for the next iteration - } - op2 = op2.next; - } - op = op.next; - } - while (op != outrec.getPoints()); - } - } - - //------------------------------------------------------------------------------ - - private boolean EdgesAdjacent( IntersectNode inode ) { - return inode.edge1.nextInSEL == inode.Edge2 || inode.edge1.prevInSEL == inode.Edge2; - } - - //------------------------------------------------------------------------------ - - @Override - public boolean execute(ClipType clipType, Paths solution ) { - return execute( clipType, solution, PolyFillType.EVEN_ODD, PolyFillType.EVEN_ODD ); - } - - @Override - public boolean execute(ClipType clipType, PolyTree polytree ) { - return execute( clipType, polytree, PolyFillType.EVEN_ODD, PolyFillType.EVEN_ODD ); - } - - @Override - public boolean execute(ClipType clipType, Paths solution, PolyFillType subjFillType, PolyFillType clipFillType ) { - - synchronized (this) { - - if (hasOpenPaths) { - throw new IllegalStateException( "Error: PolyTree struct is needed for open path clipping." ); - } - - solution.clear(); - this.subjFillType = subjFillType; - this.clipFillType = clipFillType; - this.clipType = clipType; - usingPolyTree = false; - boolean succeeded; - try { - succeeded = executeInternal(); - //build the return polygons ... - if (succeeded) { - buildResult( solution ); - } - return succeeded; - } - finally { - polyOuts.clear(); - - } - } - - } - - @Override - public boolean execute(ClipType clipType, PolyTree polytree, PolyFillType subjFillType, PolyFillType clipFillType ) { - synchronized (this) { - this.subjFillType = subjFillType; - this.clipFillType = clipFillType; - this.clipType = clipType; - usingPolyTree = true; - boolean succeeded; - try { - succeeded = executeInternal(); - //build the return polygons ... - if (succeeded) { - buildResult2( polytree ); - } - } - finally { - polyOuts.clear(); - } - return succeeded; - } - } - - //------------------------------------------------------------------------------ - - private boolean executeInternal() { - try { - reset(); - sortedEdges = null; - maxima = null; - - long[] botY = new long[1], topY = new long[1]; - if (!popScanbeam( botY )) return false; - insertLocalMinimaIntoAEL( botY[0] ); - while ( popScanbeam( topY ) || localMinimaPending()) { - processHorizontals(); - ghostJoins.clear(); - if (!processIntersections( topY[0] )) { - return false; - } - processEdgesAtTopOfScanbeam( topY[0] ); - botY[0] = topY[0]; - insertLocalMinimaIntoAEL( botY[0] ); - } - - //fix orientations ... - for (OutRec outRec : polyOuts) { - if (outRec.getPoints() == null || outRec.isOpen) { - continue; - } - if ((outRec.isHole ^ reverseSolution) == outRec.area() > 0) { - outRec.getPoints().reversePolyPtLinks(); - } - } - - joinCommonEdges(); - - for (OutRec outRec : polyOuts) { - if (outRec.getPoints() == null) { - continue; - } - else if (outRec.isOpen) { - fixupOutPolygon( outRec ); - } - else { - fixupOutPolygon( outRec ); - } - } - - if (strictlySimple) { - doSimplePolygons(); - } - return true; - } - //catch { return false; } - finally { - joins.clear(); - ghostJoins.clear(); - } - } - - //------------------------------------------------------------------------------ - - private void fixupFirstLefts1(OutRec OldOutRec, OutRec NewOutRec ) { - for (OutRec outRec : polyOuts) { - final OutRec firstLeft = OutRec.parseFirstLeft( outRec.firstLeft ); - if (outRec.getPoints() != null && firstLeft == OldOutRec) { - if (poly2ContainsPoly1( outRec.getPoints(), NewOutRec.getPoints() )) { - outRec.firstLeft = NewOutRec; - } - } - } - } - //------------------------------------------------------------------------------ - - private void fixupFirstLefts2(OutRec innerOutRec, OutRec outerOutRec ) { - //A polygon has split into two such that one is now the inner of the other. - //It's possible that these polygons now wrap around other polygons, so check - //every polygon that's also contained by OuterOutRec's FirstLeft container - //(including nil) to see if they've become inner to the new inner polygon ... - final OutRec orfl = outerOutRec.firstLeft; - for (OutRec outRec : polyOuts) { - if (outRec.getPoints() == null || outRec == outerOutRec || outRec == innerOutRec) { - continue; - } - final OutRec firstLeft = OutRec.parseFirstLeft( outRec.firstLeft ); - if (firstLeft != orfl && firstLeft != innerOutRec && firstLeft != outerOutRec) { - continue; - } - if (poly2ContainsPoly1( outRec.getPoints(), innerOutRec.getPoints() )) { - outRec.firstLeft = innerOutRec; - } - else if (poly2ContainsPoly1( outRec.getPoints(), outerOutRec.getPoints() )) { - outRec.firstLeft = outerOutRec; - } - else if (outRec.firstLeft == innerOutRec || outRec.firstLeft == outerOutRec) { - outRec.firstLeft = orfl; - } - } - } - //---------------------------------------------------------------------- - - private void fixupFirstLefts3(OutRec oldOutRec, OutRec newOutRec ) { - //same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1() - for (OutRec outRec : polyOuts) { - final OutRec firstLeft = OutRec.parseFirstLeft( outRec.firstLeft ); - if (outRec.getPoints() != null && firstLeft == oldOutRec) { - outRec.firstLeft = newOutRec; - } - } - } - - private boolean fixupIntersectionOrder() { - //pre-condition: intersections are sorted bottom-most first. - //Now it's crucial that intersections are made only between adjacent edges, - //so to ensure this the order of intersections may need adjusting ... - Collections.sort( intersectList, intersectNodeComparer ); - - copyAELToSEL(); - final int cnt = intersectList.size(); - for (int i = 0; i < cnt; i++) { - if (!EdgesAdjacent( intersectList.get( i ) )) { - int j = i + 1; - while (j < cnt && !EdgesAdjacent( intersectList.get( j ) )) { - j++; - } - if (j == cnt) { - return false; - } - - final IntersectNode tmp = intersectList.get( i ); - intersectList.set( i, intersectList.get( j ) ); - intersectList.set( j, tmp ); - - } - swapPositionsInSEL( intersectList.get( i ).edge1, intersectList.get( i ).Edge2 ); - } - return true; - } - - //---------------------------------------------------------------------- - - private void fixupOutPolyline( OutRec outrec ) { - Path.OutPt pp = outrec.getPoints(); - Path.OutPt lastPP = pp.prev; - while (pp != lastPP) { - pp = pp.next; - if (pp.getPt() == pp.prev.getPt()) { - if (pp == lastPP) { - lastPP = pp.prev; - } - Path.OutPt tmpPP = pp.prev; - tmpPP.next = pp.next; - pp.next.prev = tmpPP; - pp = tmpPP; - } - } - if (pp == pp.prev) { - outrec.setPoints( null ); - } - } - - //------------------------------------------------------------------------------ - - private void fixupOutPolygon( OutRec outRec ) { - //FixupOutPolygon() - removes duplicate points and simplifies consecutive - //parallel edges by removing the middle vertex. - Path.OutPt lastOK = null; - outRec.bottomPt = null; - Path.OutPt pp = outRec.getPoints(); - final boolean preserveCol = preserveCollinear || strictlySimple; - for (;;) { - if (pp.prev == pp || pp.prev == pp.next) { - outRec.setPoints( null ); - return; - } - //test for duplicate points and collinear edges ... - if (pp.getPt().equals( pp.next.getPt() ) || pp.getPt().equals( pp.prev.getPt() ) - || Point.slopesEqual( pp.prev.getPt(), pp.getPt(), pp.next.getPt() ) - && (!preserveCol || !Point.isPt2BetweenPt1AndPt3( pp.prev.getPt(), pp.getPt(), pp.next.getPt() ))) { - lastOK = null; - pp.prev.next = pp.next; - pp.next.prev = pp.prev; - pp = pp.prev; - } - else if (pp == lastOK) { - break; - } - else { - if (lastOK == null) { - lastOK = pp; - } - pp = pp.next; - } - } - outRec.setPoints( pp ); - } - - private OutRec getOutRec(int idx ) { - OutRec outrec = polyOuts.get( idx ); - while (outrec != polyOuts.get( outrec.Idx )) { - outrec = polyOuts.get( outrec.Idx ); - } - return outrec; - } - - private void insertEdgeIntoAEL(Edge edge, Edge startEdge ) { - LOGGER.entering( DefaultClipper.class.getName(), "insertEdgeIntoAEL" ); - - if (activeEdges == null) { - edge.prevInAEL = null; - edge.nextInAEL = null; - LOGGER.finest( "Edge " + edge.outIdx + " -> " + null ); - activeEdges = edge; - } - else if (startEdge == null && Edge.doesE2InsertBeforeE1( activeEdges, edge )) { - edge.prevInAEL = null; - edge.nextInAEL = activeEdges; - LOGGER.finest( "Edge " + edge.outIdx + " -> " + edge.nextInAEL.outIdx ); - activeEdges.prevInAEL = edge; - activeEdges = edge; - } - else { - LOGGER.finest( "activeEdges unchanged" ); - if (startEdge == null) { - startEdge = activeEdges; - } - while (startEdge.nextInAEL != null && !Edge.doesE2InsertBeforeE1( startEdge.nextInAEL, edge )) { - startEdge = startEdge.nextInAEL; - } - edge.nextInAEL = startEdge.nextInAEL; - if (startEdge.nextInAEL != null) { - startEdge.nextInAEL.prevInAEL = edge; - } - edge.prevInAEL = startEdge; - startEdge.nextInAEL = edge; - } - } - - //------------------------------------------------------------------------------ - - private void insertLocalMinimaIntoAEL( long botY ) { - LOGGER.entering( DefaultClipper.class.getName(), "insertLocalMinimaIntoAEL" ); - - LocalMinima[] lm = new LocalMinima[1]; - while ( popLocalMinima( botY, lm )) { - final Edge lb = lm[0].leftBound; - final Edge rb = lm[0].rightBound; - - Path.OutPt Op1 = null; - if (lb == null) { - insertEdgeIntoAEL( rb, null ); - updateWindingCount( rb ); - if (rb.isContributing( clipFillType, subjFillType, clipType )) { - Op1 = addOutPt( rb, rb.getBot() ); - } - } - else if (rb == null) { - insertEdgeIntoAEL( lb, null ); - updateWindingCount( lb ); - if (lb.isContributing( clipFillType, subjFillType, clipType )) { - Op1 = addOutPt( lb, lb.getBot() ); - } - insertScanbeam( lb.getTop().getY() ); - } - else { - insertEdgeIntoAEL( lb, null ); - insertEdgeIntoAEL( rb, lb ); - updateWindingCount( lb ); - rb.windCnt = lb.windCnt; - rb.windCnt2 = lb.windCnt2; - if (lb.isContributing( clipFillType, subjFillType, clipType )) { - Op1 = addLocalMinPoly( lb, rb, lb.getBot() ); - } - insertScanbeam( lb.getTop().getY() ); - } - - if (rb != null) { - if (rb.isHorizontal()) { - if (rb.nextInLML != null) { - insertScanbeam( rb.nextInLML.getTop().getY() ); - } - addEdgeToSEL( rb ); - } - else { - insertScanbeam( rb.getTop().getY() ); - } - } - - if (lb == null || rb == null) { - continue; - } - - //if output polygons share an Edge with a horizontal rb, they'll need joining later ... - if (Op1 != null && rb.isHorizontal() && ghostJoins.size() > 0 && rb.windDelta != 0) { - for (int i = 0; i < ghostJoins.size(); i++) { - //if the horizontal Rb and a 'ghost' horizontal overlap, then convert - //the 'ghost' join to a real join ready for later ... - final Join j = ghostJoins.get( i ); - if (doHorzSegmentsOverlap( j.outPt1.getPt().getX(), j.getOffPt().getX(), rb.getBot().getX(), rb.getTop().getX() )) { - addJoin( j.outPt1, Op1, j.getOffPt() ); - } - } - } - - if (lb.outIdx >= 0 && lb.prevInAEL != null && lb.prevInAEL.getCurrent().getX() == lb.getBot().getX() && lb.prevInAEL.outIdx >= 0 - && Point.slopesEqual( lb.prevInAEL.getCurrent(), lb.prevInAEL.getTop(), lb.getCurrent(), lb.getTop() ) && lb.windDelta != 0 && lb.prevInAEL.windDelta != 0) { - final Path.OutPt Op2 = addOutPt( lb.prevInAEL, lb.getBot() ); - addJoin( Op1, Op2, lb.getTop() ); - } - - if (lb.nextInAEL != rb) { - - if (rb.outIdx >= 0 && rb.prevInAEL.outIdx >= 0 && Point.slopesEqual( rb.prevInAEL.getCurrent(), rb.prevInAEL.getTop(), rb.getCurrent(), rb.getTop() ) && rb.windDelta != 0 && rb.prevInAEL.windDelta != 0) { - final Path.OutPt Op2 = addOutPt( rb.prevInAEL, rb.getBot() ); - addJoin( Op1, Op2, rb.getTop() ); - } - - Edge e = lb.nextInAEL; - if (e != null) { - while (e != rb) { - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the right of param2 ABOVE the intersection ... - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the right of param2 ABOVE the intersection ... - intersectEdges( rb, e, lb.getCurrent() ); //order important here - e = e.nextInAEL; - - } - } - } - } - } - - //------------------------------------------------------------------------------ - - private void intersectEdges(Edge e1, Edge e2, LongPoint pt ) { - LOGGER.entering( DefaultClipper.class.getName(), "insersectEdges" ); - - //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before - //e2 in AEL except when e1 is being inserted at the intersection point ... - - final boolean e1Contributing = e1.outIdx >= 0; - final boolean e2Contributing = e2.outIdx >= 0; - - setZ( pt, e1, e2 ); - - //if either edge is on an OPEN path ... - if (e1.windDelta == 0 || e2.windDelta == 0) { - //ignore subject-subject open path intersections UNLESS they - //are both open paths, AND they are both 'contributing maximas' ... - if (e1.windDelta == 0 && e2.windDelta == 0) { - return; - } - else if (e1.polyTyp == e2.polyTyp && e1.windDelta != e2.windDelta && clipType == ClipType.UNION) { - if (e1.windDelta == 0) { - if (e2Contributing) { - addOutPt( e1, pt ); - if (e1Contributing) { - e1.outIdx = Edge.UNASSIGNED; - } - } - } - else { - if (e1Contributing) { - addOutPt( e2, pt ); - if (e2Contributing) { - e2.outIdx = Edge.UNASSIGNED; - } - } - } - } - else if (e1.polyTyp != e2.polyTyp) { - if (e1.windDelta == 0 && Math.abs( e2.windCnt ) == 1 && (clipType != ClipType.UNION || e2.windCnt2 == 0)) { - addOutPt( e1, pt ); - if (e1Contributing) { - e1.outIdx = Edge.UNASSIGNED; - } - } - else if (e2.windDelta == 0 && Math.abs( e1.windCnt ) == 1 && (clipType != ClipType.UNION || e1.windCnt2 == 0)) { - addOutPt( e2, pt ); - if (e2Contributing) { - e2.outIdx = Edge.UNASSIGNED; - } - } - } - return; - } - - //update winding counts... - //assumes that e1 will be to the Right of e2 ABOVE the intersection - if (e1.polyTyp == e2.polyTyp) { - if (e1.isEvenOddFillType( clipFillType, subjFillType )) { - final int oldE1WindCnt = e1.windCnt; - e1.windCnt = e2.windCnt; - e2.windCnt = oldE1WindCnt; - } - else { - if (e1.windCnt + e2.windDelta == 0) { - e1.windCnt = -e1.windCnt; - } - else { - e1.windCnt += e2.windDelta; - } - if (e2.windCnt - e1.windDelta == 0) { - e2.windCnt = -e2.windCnt; - } - else { - e2.windCnt -= e1.windDelta; - } - } - } - else { - if (!e2.isEvenOddFillType( clipFillType, subjFillType )) { - e1.windCnt2 += e2.windDelta; - } - else { - e1.windCnt2 = e1.windCnt2 == 0 ? 1 : 0; - } - if (!e1.isEvenOddFillType( clipFillType, subjFillType )) { - e2.windCnt2 -= e1.windDelta; - } - else { - e2.windCnt2 = e2.windCnt2 == 0 ? 1 : 0; - } - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1.polyTyp == PolyType.SUBJECT) { - e1FillType = subjFillType; - e1FillType2 = clipFillType; - } - else { - e1FillType = clipFillType; - e1FillType2 = subjFillType; - } - if (e2.polyTyp == PolyType.SUBJECT) { - e2FillType = subjFillType; - e2FillType2 = clipFillType; - } - else { - e2FillType = clipFillType; - e2FillType2 = subjFillType; - } - - int e1Wc, e2Wc; - switch (e1FillType) { - case POSITIVE: - e1Wc = e1.windCnt; - break; - case NEGATIVE: - e1Wc = -e1.windCnt; - break; - default: - e1Wc = Math.abs( e1.windCnt ); - break; - } - switch (e2FillType) { - case POSITIVE: - e2Wc = e2.windCnt; - break; - case NEGATIVE: - e2Wc = -e2.windCnt; - break; - default: - e2Wc = Math.abs( e2.windCnt ); - break; - } - - if (e1Contributing && e2Contributing) { - if (e1Wc != 0 && e1Wc != 1 || e2Wc != 0 && e2Wc != 1 || e1.polyTyp != e2.polyTyp && clipType != ClipType.XOR) { - addLocalMaxPoly( e1, e2, pt ); - } - else { - addOutPt( e1, pt ); - addOutPt( e2, pt ); - Edge.swapSides( e1, e2 ); - Edge.swapPolyIndexes( e1, e2 ); - } - } - else if (e1Contributing) { - if (e2Wc == 0 || e2Wc == 1) { - addOutPt( e1, pt ); - Edge.swapSides( e1, e2 ); - Edge.swapPolyIndexes( e1, e2 ); - } - - } - else if (e2Contributing) { - if (e1Wc == 0 || e1Wc == 1) { - addOutPt( e2, pt ); - Edge.swapSides( e1, e2 ); - Edge.swapPolyIndexes( e1, e2 ); - } - } - else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) { - //neither edge is currently contributing ... - int e1Wc2, e2Wc2; - switch (e1FillType2) { - case POSITIVE: - e1Wc2 = e1.windCnt2; - break; - case NEGATIVE: - e1Wc2 = -e1.windCnt2; - break; - default: - e1Wc2 = Math.abs( e1.windCnt2 ); - break; - } - switch (e2FillType2) { - case POSITIVE: - e2Wc2 = e2.windCnt2; - break; - case NEGATIVE: - e2Wc2 = -e2.windCnt2; - break; - default: - e2Wc2 = Math.abs( e2.windCnt2 ); - break; - } - - if (e1.polyTyp != e2.polyTyp) { - addLocalMinPoly( e1, e2, pt ); - } - else if (e1Wc == 1 && e2Wc == 1) { - switch (clipType) { - case INTERSECTION: - if (e1Wc2 > 0 && e2Wc2 > 0) { - addLocalMinPoly( e1, e2, pt ); - } - break; - case UNION: - if (e1Wc2 <= 0 && e2Wc2 <= 0) { - addLocalMinPoly( e1, e2, pt ); - } - break; - case DIFFERENCE: - if (e1.polyTyp == PolyType.CLIP && e1Wc2 > 0 && e2Wc2 > 0 || e1.polyTyp == PolyType.SUBJECT && e1Wc2 <= 0 && e2Wc2 <= 0) { - addLocalMinPoly( e1, e2, pt ); - } - break; - case XOR: - addLocalMinPoly( e1, e2, pt ); - break; - } - } - else { - Edge.swapSides( e1, e2 ); - } - } - } - - private void intersectPoint(Edge edge1, Edge edge2, LongPoint[] ipV ) { - final LongPoint ip = ipV[0] = new LongPoint(); - - double b1, b2; - //nb: with very large coordinate values, it's possible for SlopesEqual() to - //return false but for the edge.Dx value be equal due to double precision rounding. - if (edge1.deltaX == edge2.deltaX) { - ip.setY( edge1.getCurrent().getY() ); - ip.setX( Edge.topX( edge1, ip.getY() ) ); - return; - } - - if (edge1.getDelta().getX() == 0) { - ip.setX( edge1.getBot().getX() ); - if (edge2.isHorizontal()) { - ip.setY( edge2.getBot().getY() ); - } - else { - b2 = edge2.getBot().getY() - edge2.getBot().getX() / edge2.deltaX; - ip.setY( Math.round( ip.getX() / edge2.deltaX + b2 ) ); - } - } - else if (edge2.getDelta().getX() == 0) { - ip.setX( edge2.getBot().getX() ); - if (edge1.isHorizontal()) { - ip.setY( edge1.getBot().getY() ); - } - else { - b1 = edge1.getBot().getY() - edge1.getBot().getX() / edge1.deltaX; - ip.setY( Math.round( ip.getX() / edge1.deltaX + b1 ) ); - } - } - else { - b1 = edge1.getBot().getX() - edge1.getBot().getY() * edge1.deltaX; - b2 = edge2.getBot().getX() - edge2.getBot().getY() * edge2.deltaX; - final double q = (b2 - b1) / (edge1.deltaX - edge2.deltaX); - ip.setY( Math.round( q ) ); - if (Math.abs( edge1.deltaX ) < Math.abs( edge2.deltaX )) { - ip.setX( Math.round( edge1.deltaX * q + b1 ) ); - } - else { - ip.setX( Math.round( edge2.deltaX * q + b2 ) ); - } - } - - if (ip.getY() < edge1.getTop().getY() || ip.getY() < edge2.getTop().getY()) { - if (edge1.getTop().getY() > edge2.getTop().getY()) { - ip.setY( edge1.getTop().getY() ); - } - else { - ip.setY( edge2.getTop().getY() ); - } - if (Math.abs( edge1.deltaX ) < Math.abs( edge2.deltaX )) { - ip.setX( Edge.topX( edge1, ip.getY() ) ); - } - else { - ip.setX( Edge.topX( edge2, ip.getY() ) ); - } - } - //finally, don't allow 'ip' to be BELOW curr.getY() (ie bottom of scanbeam) ... - if (ip.getY() > edge1.getCurrent().getY()) { - ip.setY( edge1.getCurrent().getY() ); - //better to use the more vertical edge to derive X ... - if (Math.abs( edge1.deltaX ) > Math.abs( edge2.deltaX )) { - ip.setX( Edge.topX( edge2, ip.getY() ) ); - } - else { - ip.setX( Edge.topX( edge1, ip.getY() ) ); - } - } - } - - private void joinCommonEdges() { - for (int i = 0; i < joins.size(); i++) { - final Join join = joins.get( i ); - - final OutRec outRec1 = getOutRec( join.outPt1.idx ); - OutRec outRec2 = getOutRec( join.outPt2.idx ); - - if (outRec1.getPoints() == null || outRec2.getPoints() == null) { - continue; - } - if (outRec1.isOpen || outRec2.isOpen) { - continue; - } - - //get the polygon fragment with the correct hole state (FirstLeft) - //before calling JoinPoints() ... - OutRec holeStateRec; - if (outRec1 == outRec2) { - holeStateRec = outRec1; - } - else if (isOutRec1RightOfOutRec2( outRec1, outRec2 )) { - holeStateRec = outRec2; - } - else if (isOutRec1RightOfOutRec2( outRec2, outRec1 )) { - holeStateRec = outRec1; - } - else { - holeStateRec = Path.OutPt.getLowerMostRec( outRec1, outRec2 ); - } - - if (!joinPoints( join, outRec1, outRec2 )) { - continue; - } - - if (outRec1 == outRec2) { - //instead of joining two polygons, we've just created a new one by - //splitting one polygon into two. - outRec1.setPoints( join.outPt1 ); - outRec1.bottomPt = null; - outRec2 = createOutRec(); - outRec2.setPoints( join.outPt2 ); - - //update all OutRec2.Pts Idx's ... - updateOutPtIdxs( outRec2 ); - - if (poly2ContainsPoly1( outRec2.getPoints(), outRec1.getPoints() )) { - //outRec1 contains outRec2 ... - outRec2.isHole = !outRec1.isHole; - outRec2.firstLeft = outRec1; - - if (usingPolyTree) { - fixupFirstLefts2( outRec2, outRec1 ); - } - - if ((outRec2.isHole ^ reverseSolution) == outRec2.area() > 0) { - outRec2.getPoints().reversePolyPtLinks(); - } - - } - else if (poly2ContainsPoly1( outRec1.getPoints(), outRec2.getPoints() )) { - //outRec2 contains outRec1 ... - outRec2.isHole = outRec1.isHole; - outRec1.isHole = !outRec2.isHole; - outRec2.firstLeft = outRec1.firstLeft; - outRec1.firstLeft = outRec2; - - if (usingPolyTree) { - fixupFirstLefts2( outRec1, outRec2 ); - } - - if ((outRec1.isHole ^ reverseSolution) == outRec1.area() > 0) { - outRec1.getPoints().reversePolyPtLinks(); - } - } - else { - //the 2 polygons are completely separate ... - outRec2.isHole = outRec1.isHole; - outRec2.firstLeft = outRec1.firstLeft; - - //fixup FirstLeft pointers that may need reassigning to OutRec2 - if (usingPolyTree) { - fixupFirstLefts1( outRec1, outRec2 ); - } - } - - } - else { - //joined 2 polygons together ... - - outRec2.setPoints( null ); - outRec2.bottomPt = null; - outRec2.Idx = outRec1.Idx; - - outRec1.isHole = holeStateRec.isHole; - if (holeStateRec == outRec2) { - outRec1.firstLeft = outRec2.firstLeft; - } - outRec2.firstLeft = outRec1; - - //fixup FirstLeft pointers that may need reassigning to OutRec1 - if (usingPolyTree) { - fixupFirstLefts3( outRec2, outRec1 ); - } - } - } - } - - private void processEdgesAtTopOfScanbeam( long topY ) { - LOGGER.entering( DefaultClipper.class.getName(), "processEdgesAtTopOfScanbeam" ); - - Edge e = activeEdges; - while (e != null) { - //1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - boolean IsMaximaEdge = e.isMaxima( topY ); - - if (IsMaximaEdge) { - final Edge eMaxPair = e.getMaximaPairEx(); - IsMaximaEdge = eMaxPair == null || !eMaxPair.isHorizontal(); - } - - if (IsMaximaEdge) { - if (strictlySimple) insertMaxima( e.getTop().getX() ); - final Edge ePrev = e.prevInAEL; - doMaxima( e ); - if (ePrev == null) { - e = activeEdges; - } - else { - e = ePrev.nextInAEL; - } - } - else { - //2. promote horizontal edges, otherwise update Curr.getX() and Curr.getY() ... - if (e.isIntermediate( topY ) && e.nextInLML.isHorizontal()) { - final Edge[] t = new Edge[] { e }; - updateEdgeIntoAEL( t ); - e = t[0]; - if (e.outIdx >= 0) { - addOutPt( e, e.getBot() ); - } - addEdgeToSEL( e ); - } - else { - e.getCurrent().setX( Edge.topX( e, topY ) ); - e.getCurrent().setY( topY ); - if (e.getTop().getY() == topY) { - e.getCurrent().setZ( e.getTop().getZ() ); - } - else if (e.getBot().getY() == topY) { - e.getCurrent().setZ( e.getBot().getZ() ); - } - else { - e.getCurrent().setZ( 0L ); - } - } - - //When StrictlySimple and 'e' is being touched by another edge, then - //make sure both edges have a vertex here ... - if (strictlySimple) { - final Edge ePrev = e.prevInAEL; - if (e.outIdx >= 0 && e.windDelta != 0 && ePrev != null && ePrev.outIdx >= 0 && ePrev.getCurrent().getX() == e.getCurrent().getX() - && ePrev.windDelta != 0) { - final LongPoint ip = new LongPoint( e.getCurrent() ); - - setZ( ip, ePrev, e ); - - final Path.OutPt op = addOutPt( ePrev, ip ); - final Path.OutPt op2 = addOutPt( e, ip ); - addJoin( op, op2, ip ); //StrictlySimple (type-3) join - } - } - - e = e.nextInAEL; - } - } - - //3. Process horizontals at the Top of the scanbeam ... - processHorizontals(); - maxima = null; - - //4. Promote intermediate vertices ... - e = activeEdges; - while (e != null) { - if (e.isIntermediate( topY )) { - Path.OutPt op = null; - if (e.outIdx >= 0) { - op = addOutPt( e, e.getTop() ); - } - final Edge[] t = new Edge[] { e }; - updateEdgeIntoAEL( t ); - e = t[0]; - - //if output polygons share an edge, they'll need joining later ... - final Edge ePrev = e.prevInAEL; - final Edge eNext = e.nextInAEL; - if (ePrev != null && ePrev.getCurrent().getX() == e.getBot().getX() && ePrev.getCurrent().getY() == e.getBot().getY() && op != null - && ePrev.outIdx >= 0 && ePrev.getCurrent().getY() > ePrev.getTop().getY() && Point.slopesEqual( e.getCurrent(), e.getTop(), ePrev.getCurrent(), ePrev.getTop() ) && e.windDelta != 0 - && ePrev.windDelta != 0) { - final Path.OutPt op2 = addOutPt( ePrev, e.getBot() ); - addJoin( op, op2, e.getTop() ); - } - else if (eNext != null && eNext.getCurrent().getX() == e.getBot().getX() && eNext.getCurrent().getY() == e.getBot().getY() && op != null - && eNext.outIdx >= 0 && eNext.getCurrent().getY() > eNext.getTop().getY() && Point.slopesEqual( e.getCurrent(), e.getTop(), eNext.getCurrent(), eNext.getTop() ) && e.windDelta != 0 - && eNext.windDelta != 0) { - final Path.OutPt op2 = addOutPt( eNext, e.getBot() ); - addJoin( op, op2, e.getTop() ); - } - } - e = e.nextInAEL; - } - LOGGER.exiting( DefaultClipper.class.getName(), "processEdgesAtTopOfScanbeam" ); - } - - private void processHorizontal( Edge horzEdge ) { - LOGGER.entering( DefaultClipper.class.getName(), "isHorizontal" ); - final Direction[] dir = new Direction[1]; - final long[] horzLeft = new long[1], horzRight = new long[1]; - boolean isOpen = horzEdge.windDelta == 0; - - getHorzDirection( horzEdge, dir, horzLeft, horzRight ); - - Edge eLastHorz = horzEdge, eMaxPair = null; - while (eLastHorz.nextInLML != null && eLastHorz.nextInLML.isHorizontal()) { - eLastHorz = eLastHorz.nextInLML; - } - if (eLastHorz.nextInLML == null) { - eMaxPair = eLastHorz.getMaximaPair(); - } - - Maxima currMax = maxima; - if (currMax != null) { - //get the first maxima in range (X) ... - if (dir[0] == Direction.LEFT_TO_RIGHT) { - while (currMax != null && currMax.x <= horzEdge.getBot().getX()) { - currMax = currMax.next; - } - if (currMax != null && currMax.x >= eLastHorz.getTop().getX()) { - currMax = null; - } - } - else { - while (currMax.next != null && currMax.next.x < horzEdge.getBot().getX()) { - currMax = currMax.next; - } - if (currMax.x <= eLastHorz.getTop().getX()) { - currMax = null; - } - } - } - - Path.OutPt op1; - for (;;) { //loop through consec. horizontal edges - final boolean IsLastHorz = horzEdge == eLastHorz; - Edge e = horzEdge.getNextInAEL( dir[0] ); - while (e != null) { - //this code block inserts extra coords into horizontal edges (in output - //polygons) whereever maxima touch these horizontal edges. This helps - //'simplifying' polygons (ie if the Simplify property is set). - if (currMax != null) { - if (dir[0] == Direction.LEFT_TO_RIGHT) { - while (currMax != null && currMax.x < e.getCurrent().getX()) { - if (horzEdge.outIdx >= 0 && !isOpen) { - addOutPt( horzEdge, new LongPoint( currMax.x, horzEdge.getBot().getY() ) ); - } - currMax = currMax.next; - } - } - else { - while (currMax != null && currMax.x > e.getCurrent().getX()) { - if (horzEdge.outIdx >= 0 && !isOpen) { - addOutPt( horzEdge, new LongPoint( currMax.x, horzEdge.getBot().getY() ) ); - } - currMax = currMax.prev; - } - } - } - - if ((dir[0] == Direction.LEFT_TO_RIGHT && e.getCurrent().getX() > horzRight[0]) || - (dir[0] == Direction.RIGHT_TO_LEFT && e.getCurrent().getX() < horzLeft[0])) break; - - //Also break if we've got to the end of an intermediate horizontal edge ... - //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e.getCurrent().getX() == horzEdge.getTop().getX() && horzEdge.nextInLML != null && e.deltaX < horzEdge.nextInLML.deltaX) { - break; - } - - if (horzEdge.outIdx >= 0 && !isOpen) { //note: may be done multiple times - if (dir[0] == Direction.LEFT_TO_RIGHT) setZ( e.getCurrent(), horzEdge, e ); - else setZ( e.getCurrent(), e, horzEdge ); - - op1 = addOutPt( horzEdge, e.getCurrent() ); - Edge eNextHorz = sortedEdges; - while (eNextHorz != null) { - if (eNextHorz.outIdx >= 0 && - doHorzSegmentsOverlap( horzEdge.getBot().getX(), - horzEdge.getTop().getX(), eNextHorz.getBot().getX(), eNextHorz.getTop().getX() )) - { - Path.OutPt op2 = getLastOutPt( eNextHorz ); - addJoin( op2, op1, eNextHorz.getTop() ); - } - eNextHorz = eNextHorz.nextInSEL; - } - addGhostJoin( op1, horzEdge.getBot() ); - } - - //OK, so far we're still in range of the horizontal Edge but make sure - //we're at the last of consec. horizontals when matching with eMaxPair - if (e == eMaxPair && IsLastHorz) { - if (horzEdge.outIdx >= 0) { - addLocalMaxPoly( horzEdge, eMaxPair, horzEdge.getTop() ); - } - deleteFromAEL( horzEdge ); - deleteFromAEL( eMaxPair ); - return; - } - - if (dir[0] == Direction.LEFT_TO_RIGHT) { - final LongPoint Pt = new LongPoint( e.getCurrent().getX(), horzEdge.getCurrent().getY() ); - intersectEdges( horzEdge, e, Pt ); - } - else { - final LongPoint Pt = new LongPoint( e.getCurrent().getX(), horzEdge.getCurrent().getY() ); - intersectEdges( e, horzEdge, Pt ); - } - final Edge eNext = e.getNextInAEL( dir[0] ); - swapPositionsInAEL( horzEdge, e ); - e = eNext; - } //end while - - //Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (horzEdge.nextInLML == null || !horzEdge.nextInLML.isHorizontal()) break; - - final Edge[] t = new Edge[] { horzEdge }; - updateEdgeIntoAEL( t ); - horzEdge = t[0]; - if (horzEdge.outIdx >= 0) { - addOutPt( horzEdge, horzEdge.getBot() ); - } - getHorzDirection( horzEdge, dir, horzLeft, horzRight ); - - } //end for (;;) - - if (horzEdge.nextInLML != null) { - if (horzEdge.outIdx >= 0) { - op1 = addOutPt( horzEdge, horzEdge.getTop() ); - final Edge[] t = new Edge[] { horzEdge }; - updateEdgeIntoAEL( t ); - horzEdge = t[0]; - - if (horzEdge.windDelta == 0) { - return; - } - //nb: HorzEdge is no longer horizontal here - final Edge ePrev = horzEdge.prevInAEL; - final Edge eNext = horzEdge.nextInAEL; - if (ePrev != null && ePrev.getCurrent().getX() == horzEdge.getBot().getX() && ePrev.getCurrent().getY() == horzEdge.getBot().getY() - && ePrev.windDelta != 0 && ePrev.outIdx >= 0 && ePrev.getCurrent().getY() > ePrev.getTop().getY() - && Edge.slopesEqual( horzEdge, ePrev )) { - final Path.OutPt op2 = addOutPt( ePrev, horzEdge.getBot() ); - addJoin( op1, op2, horzEdge.getTop() ); - } - else if (eNext != null && eNext.getCurrent().getX() == horzEdge.getBot().getX() && eNext.getCurrent().getY() == horzEdge.getBot().getY() - && eNext.windDelta != 0 && eNext.outIdx >= 0 && eNext.getCurrent().getY() > eNext.getTop().getY() - && Edge.slopesEqual( horzEdge, eNext )) { - final Path.OutPt op2 = addOutPt( eNext, horzEdge.getBot() ); - addJoin( op1, op2, horzEdge.getTop() ); - } - } - else { - final Edge[] t = new Edge[] { horzEdge }; - updateEdgeIntoAEL( t ); - horzEdge = t[0]; - } - } - else { - if (horzEdge.outIdx >= 0) { - addOutPt( horzEdge, horzEdge.getTop() ); - } - deleteFromAEL( horzEdge ); - } - } - - //------------------------------------------------------------------------------ - - private void processHorizontals() { - Edge[] horzEdge = new Edge[1]; //m_SortedEdges; - while ( deleteFromSEL( horzEdge) ) { - processHorizontal( horzEdge[0] ); - } - } - - //------------------------------------------------------------------------------ - - private boolean processIntersections( long topY ) { - LOGGER.entering( DefaultClipper.class.getName(), "processIntersections" ); - - if (activeEdges == null) { - return true; - } - try { - buildIntersectList( topY ); - if (intersectList.size() == 0) { - return true; - } - if (intersectList.size() == 1 || fixupIntersectionOrder()) { - processIntersectList(); - } - else { - return false; - } - } - catch (final Exception e) { - sortedEdges = null; - intersectList.clear(); - throw new IllegalStateException( "ProcessIntersections error", e ); - } - sortedEdges = null; - return true; - } - - private void processIntersectList() { - for (int i = 0; i < intersectList.size(); i++) { - final IntersectNode iNode = intersectList.get( i ); - { - intersectEdges( iNode.edge1, iNode.Edge2, iNode.getPt() ); - swapPositionsInAEL( iNode.edge1, iNode.Edge2 ); - } - } - intersectList.clear(); - } - - //------------------------------------------------------------------------------ - - private void insertMaxima( long x ) { - //double-linked list: sorted ascending, ignoring dups. - final Maxima newMax = new Maxima(); - newMax.x = x; - if (maxima == null) { - maxima = newMax; - maxima.next = null; - maxima.prev = null; - } - else if (x < maxima.x) { - newMax.next = maxima; - newMax.prev = null; - maxima = newMax; - } - else { - Maxima m = maxima; - while (m.next != null && (x >= m.next.x)) { - m = m.next; - } - if (x == m.x) { - return; //ie ignores duplicates (& CG to clean up newMax) - } - //insert newMax between m and m.Next ... - newMax.next = m.next; - newMax.prev = m; - if (m.next != null) m.next.prev = newMax; - m.next = newMax; - } - } - - private void setHoleState(Edge e, OutRec outRec ) { - Edge e2 = e.prevInAEL; - Edge eTmp = null; - while (e2 != null) { - if (e2.outIdx >= 0 && e2.windDelta != 0) { - if (eTmp == null) { - eTmp = e2; - } - else if (eTmp.outIdx == e2.outIdx) { - eTmp = null; //paired - } - } - e2 = e2.prevInAEL; - } - if (eTmp == null) { - outRec.firstLeft = null; - outRec.isHole = false; - } - else { - outRec.firstLeft = polyOuts.get( eTmp.outIdx ); - outRec.isHole = !outRec.firstLeft.isHole; - } - } - - private void setZ(LongPoint pt, Edge e1, Edge e2 ) { - if (pt.getZ() != 0 || zFillFunction == null) { - return; - } - else if (pt.equals( e1.getBot() )) { - pt.setZ( e1.getBot().getZ() ); - } - else if (pt.equals( e1.getTop() )) { - pt.setZ( e1.getTop().getZ() ); - } - else if (pt.equals( e2.getBot() )) { - pt.setZ( e2.getBot().getZ() ); - } - else if (pt.equals( e2.getTop() )) { - pt.setZ( e2.getTop().getZ() ); - } - else { - zFillFunction.zFill( e1.getBot(), e1.getTop(), e2.getBot(), e2.getTop(), pt ); - } - } - - //------------------------------------------------------------------------------; - - private void swapPositionsInSEL(Edge edge1, Edge edge2 ) { - if (edge1.nextInSEL == null && edge1.prevInSEL == null) { - return; - } - if (edge2.nextInSEL == null && edge2.prevInSEL == null) { - return; - } - - if (edge1.nextInSEL == edge2) { - final Edge next = edge2.nextInSEL; - if (next != null) { - next.prevInSEL = edge1; - } - final Edge prev = edge1.prevInSEL; - if (prev != null) { - prev.nextInSEL = edge2; - } - edge2.prevInSEL = prev; - edge2.nextInSEL = edge1; - edge1.prevInSEL = edge2; - edge1.nextInSEL = next; - } - else if (edge2.nextInSEL == edge1) { - final Edge next = edge1.nextInSEL; - if (next != null) { - next.prevInSEL = edge2; - } - final Edge prev = edge2.prevInSEL; - if (prev != null) { - prev.nextInSEL = edge1; - } - edge1.prevInSEL = prev; - edge1.nextInSEL = edge2; - edge2.prevInSEL = edge1; - edge2.nextInSEL = next; - } - else { - final Edge next = edge1.nextInSEL; - final Edge prev = edge1.prevInSEL; - edge1.nextInSEL = edge2.nextInSEL; - if (edge1.nextInSEL != null) { - edge1.nextInSEL.prevInSEL = edge1; - } - edge1.prevInSEL = edge2.prevInSEL; - if (edge1.prevInSEL != null) { - edge1.prevInSEL.nextInSEL = edge1; - } - edge2.nextInSEL = next; - if (edge2.nextInSEL != null) { - edge2.nextInSEL.prevInSEL = edge2; - } - edge2.prevInSEL = prev; - if (edge2.prevInSEL != null) { - edge2.prevInSEL.nextInSEL = edge2; - } - } - - if (edge1.prevInSEL == null) { - sortedEdges = edge1; - } - else if (edge2.prevInSEL == null) { - sortedEdges = edge2; - } - } - - private void updateEdgeIntoAEL( Edge[] eV ) { - Edge e = eV[0]; - if (e.nextInLML == null) { - throw new IllegalStateException( "UpdateEdgeIntoAEL: invalid call" ); - } - final Edge AelPrev = e.prevInAEL; - final Edge AelNext = e.nextInAEL; - e.nextInLML.outIdx = e.outIdx; - if (AelPrev != null) { - AelPrev.nextInAEL = e.nextInLML; - } - else { - activeEdges = e.nextInLML; - } - if (AelNext != null) { - AelNext.prevInAEL = e.nextInLML; - } - e.nextInLML.side = e.side; - e.nextInLML.windDelta = e.windDelta; - e.nextInLML.windCnt = e.windCnt; - e.nextInLML.windCnt2 = e.windCnt2; - eV[0] = e = e.nextInLML; - e.setCurrent( new LongPoint( e.getBot() ) ); - e.prevInAEL = AelPrev; - e.nextInAEL = AelNext; - if (!e.isHorizontal()) { - insertScanbeam( e.getTop().getY() ); - } - } - - private void updateOutPtIdxs( OutRec outrec ) { - Path.OutPt op = outrec.getPoints(); - do { - op.idx = outrec.Idx; - op = op.prev; - } - while (op != outrec.getPoints()); - } - - private void updateWindingCount( Edge edge ) { - LOGGER.entering( DefaultClipper.class.getName(), "updateWindingCount" ); - - Edge e = edge.prevInAEL; - //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e != null && (e.polyTyp != edge.polyTyp || e.windDelta == 0)) { - e = e.prevInAEL; - } - if (e == null) { - PolyFillType pft; - pft = (edge.polyTyp == PolyType.SUBJECT ? subjFillType : clipFillType); - if (edge.windDelta == 0) { - edge.windCnt = (pft == PolyFillType.NEGATIVE ? -1 : 1); - } - else { - edge.windCnt = edge.windDelta; - } - edge.windCnt2 = 0; - e = activeEdges; //ie get ready to calc WindCnt2 - } - else if (edge.windDelta == 0 && clipType != ClipType.UNION) { - edge.windCnt = 1; - edge.windCnt2 = e.windCnt2; - e = e.nextInAEL; //ie get ready to calc WindCnt2 - } - else if (edge.isEvenOddFillType( clipFillType, subjFillType )) { - //EvenOdd filling ... - if (edge.windDelta == 0) { - //are we inside a subj polygon ... - boolean Inside = true; - Edge e2 = e.prevInAEL; - while (e2 != null) { - if (e2.polyTyp == e.polyTyp && e2.windDelta != 0) { - Inside = !Inside; - } - e2 = e2.prevInAEL; - } - edge.windCnt = Inside ? 0 : 1; - } - else { - edge.windCnt = edge.windDelta; - } - edge.windCnt2 = e.windCnt2; - e = e.nextInAEL; //ie get ready to calc WindCnt2 - } - else { - //nonZero, Positive or Negative filling ... - if (e.windCnt * e.windDelta < 0) { - //prev edge is 'decreasing' WindCount (WC) toward zero - //so we're outside the previous polygon ... - if (Math.abs( e.windCnt ) > 1) { - //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC - if (e.windDelta * edge.windDelta < 0) { - edge.windCnt = e.windCnt; - } - else { - edge.windCnt = e.windCnt + edge.windDelta; - } - } - else { - //now outside all polys of same polytype so set own WC ... - edge.windCnt = edge.windDelta == 0 ? 1 : edge.windDelta; - } - } - else { - //prev edge is 'increasing' WindCount (WC) away from zero - //so we're inside the previous polygon ... - if (edge.windDelta == 0) { - edge.windCnt = e.windCnt < 0 ? e.windCnt - 1 : e.windCnt + 1; - } - else if (e.windDelta * edge.windDelta < 0) { - edge.windCnt = e.windCnt; - } - else { - edge.windCnt = e.windCnt + edge.windDelta; - } - } - edge.windCnt2 = e.windCnt2; - e = e.nextInAEL; //ie get ready to calc WindCnt2 - } - - //update WindCnt2 ... - if (edge.isEvenOddAltFillType( clipFillType, subjFillType )) { - //EvenOdd filling ... - while (e != edge) { - if (e.windDelta != 0) { - edge.windCnt2 = edge.windCnt2 == 0 ? 1 : 0; - } - e = e.nextInAEL; - } - } - else { - //nonZero, Positive or Negative filling ... - while (e != edge) { - edge.windCnt2 += e.windDelta; - e = e.nextInAEL; - } - } - } - -} //end Clipper - diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Edge.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Edge.java deleted file mode 100644 index 7518a28..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Edge.java +++ /dev/null @@ -1,339 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Clipper.ClipType; -import com.visual.open.anpr.core.clipper.Clipper.Direction; -import com.visual.open.anpr.core.clipper.Clipper.PolyFillType; -import com.visual.open.anpr.core.clipper.Clipper.PolyType; -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.logging.Logger; - -class Edge { - enum Side { - LEFT, RIGHT - } - - static boolean doesE2InsertBeforeE1(Edge e1, Edge e2 ) { - if (e2.current.getX() == e1.current.getX()) { - if (e2.top.getY() > e1.top.getY()) { - return e2.top.getX() < topX( e1, e2.top.getY() ); - } - else { - return e1.top.getX() > topX( e2, e1.top.getY() ); - } - } - else { - return e2.current.getX() < e1.current.getX(); - } - } - - static boolean slopesEqual(Edge e1, Edge e2 ) { - return e1.getDelta().getY() * e2.getDelta().getX() == e1.getDelta().getX() * e2.getDelta().getY(); - - } - - static void swapPolyIndexes(Edge edge1, Edge edge2 ) { - final int outIdx = edge1.outIdx; - edge1.outIdx = edge2.outIdx; - edge2.outIdx = outIdx; - } - - static void swapSides(Edge edge1, Edge edge2 ) { - final Side side = edge1.side; - edge1.side = edge2.side; - edge2.side = side; - } - - static long topX(Edge edge, long currentY ) { - if (currentY == edge.getTop().getY()) { - return edge.getTop().getX(); - } - return edge.getBot().getX() + Math.round( edge.deltaX * (currentY - edge.getBot().getY()) ); - } - - private final LongPoint bot; - - private final LongPoint current; //current (updated for every new scanbeam) - - private final LongPoint top; - - private final LongPoint delta; - double deltaX; - - PolyType polyTyp; - - Side side; //side only refers to current side of solution poly - - int windDelta; //1 or -1 depending on winding direction - - int windCnt; - int windCnt2; //winding count of the opposite polytype - int outIdx; - Edge next; - Edge prev; - Edge nextInLML; - Edge nextInAEL; - Edge prevInAEL; - Edge nextInSEL; - Edge prevInSEL; - - protected final static int SKIP = -2; - - protected final static int UNASSIGNED = -1; - - protected final static double HORIZONTAL = -3.4E+38; - - private final static Logger LOGGER = Logger.getLogger( Edge.class.getName() ); - - public Edge() { - delta = new LongPoint(); - top = new LongPoint(); - bot = new LongPoint(); - current = new LongPoint(); - } - - public Edge findNextLocMin() { - Edge e = this; - Edge e2; - for (;;) { - while (!e.bot.equals( e.prev.bot ) || e.current.equals( e.top )) { - e = e.next; - } - if (e.deltaX != Edge.HORIZONTAL && e.prev.deltaX != Edge.HORIZONTAL) { - break; - } - while (e.prev.deltaX == Edge.HORIZONTAL) { - e = e.prev; - } - e2 = e; - while (e.deltaX == Edge.HORIZONTAL) { - e = e.next; - } - if (e.top.getY() == e.prev.bot.getY()) { - continue; //ie just an intermediate horz. - } - if (e2.prev.bot.getX() < e.bot.getX()) { - e = e2; - } - break; - } - return e; - } - - public LongPoint getBot() { - return bot; - } - - public LongPoint getCurrent() { - return current; - } - - public LongPoint getDelta() { - return delta; - } - - public Edge getMaximaPair() { - if (next.top.equals( top ) && next.nextInLML == null) { - return next; - } - else if (prev.top.equals( top ) && prev.nextInLML == null) { - return prev; - } - return null; - } - - Edge getMaximaPairEx() { - //as above but returns null if MaxPair isn't in AEL (unless it's horizontal) - Edge result = getMaximaPair(); - if (result == null || result.outIdx == Edge.SKIP || - ((result.nextInAEL == result.prevInAEL) && !result.isHorizontal())) { - return null; - } - return result; - } - - public Edge getNextInAEL(Direction direction ) { - return direction == Direction.LEFT_TO_RIGHT ? nextInAEL : prevInAEL; - } - - public LongPoint getTop() { - return top; - } - - public boolean isContributing(PolyFillType clipFillType, PolyFillType subjFillType, ClipType clipType ) { - LOGGER.entering( Edge.class.getName(), "isContributing" ); - - PolyFillType pft, pft2; - if (polyTyp == PolyType.SUBJECT) { - pft = subjFillType; - pft2 = clipFillType; - } - else { - pft = clipFillType; - pft2 = subjFillType; - } - - switch (pft) { - case EVEN_ODD: - //return false if a subj line has been flagged as inside a subj polygon - if (windDelta == 0 && windCnt != 1) { - return false; - } - break; - case NON_ZERO: - if (Math.abs( windCnt ) != 1) { - return false; - } - break; - case POSITIVE: - if (windCnt != 1) { - return false; - } - break; - default: //PolyFillType.pftNegative - if (windCnt != -1) { - return false; - } - break; - } - - switch (clipType) { - case INTERSECTION: - switch (pft2) { - case EVEN_ODD: - case NON_ZERO: - return windCnt2 != 0; - case POSITIVE: - return windCnt2 > 0; - default: - return windCnt2 < 0; - } - case UNION: - switch (pft2) { - case EVEN_ODD: - case NON_ZERO: - return windCnt2 == 0; - case POSITIVE: - return windCnt2 <= 0; - default: - return windCnt2 >= 0; - } - case DIFFERENCE: - if (polyTyp == PolyType.SUBJECT) { - switch (pft2) { - case EVEN_ODD: - case NON_ZERO: - return windCnt2 == 0; - case POSITIVE: - return windCnt2 <= 0; - default: - return windCnt2 >= 0; - } - } - else { - switch (pft2) { - case EVEN_ODD: - case NON_ZERO: - return windCnt2 != 0; - case POSITIVE: - return windCnt2 > 0; - default: - return windCnt2 < 0; - } - } - case XOR: - if (windDelta == 0) { - switch (pft2) { - case EVEN_ODD: - case NON_ZERO: - return windCnt2 == 0; - case POSITIVE: - return windCnt2 <= 0; - default: - return windCnt2 >= 0; - } - } - else { - return true; - } - } - return true; - } - - public boolean isEvenOddAltFillType(PolyFillType clipFillType, PolyFillType subjFillType ) { - if (polyTyp == PolyType.SUBJECT) { - return clipFillType == PolyFillType.EVEN_ODD; - } - else { - return subjFillType == PolyFillType.EVEN_ODD; - } - } - - public boolean isEvenOddFillType(PolyFillType clipFillType, PolyFillType subjFillType ) { - if (polyTyp == PolyType.SUBJECT) { - return subjFillType == PolyFillType.EVEN_ODD; - } - else { - return clipFillType == PolyFillType.EVEN_ODD; - } - } - - public boolean isHorizontal() { - return delta.getY() == 0; - } - - public boolean isIntermediate( double y ) { - return top.getY() == y && nextInLML != null; - } - - public boolean isMaxima( double Y ) { - return top.getY() == Y && nextInLML == null; - } - - public void reverseHorizontal() { - //swap horizontal edges' top and bottom x's so they follow the natural - //progression of the bounds - ie so their xbots will align with the - //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - long temp = top.getX(); - top.setX( bot.getX() ); - bot.setX( temp ); - - temp = top.getZ(); - top.setZ( bot.getZ() ); - bot.setZ( temp ); - - } - - public void setBot( LongPoint bot ) { - this.bot.set( bot ); - } - - public void setCurrent( LongPoint current ) { - this.current.set( current ); - } - - public void setTop( LongPoint top ) { - this.top.set( top ); - } - - @Override - public String toString() { - return "TEdge [Bot=" + bot + ", Curr=" + current + ", Top=" + top + ", Delta=" + delta + ", Dx=" + deltaX + ", PolyTyp=" + polyTyp + ", Side=" + side - + ", WindDelta=" + windDelta + ", WindCnt=" + windCnt + ", WindCnt2=" + windCnt2 + ", OutIdx=" + outIdx + ", Next=" + next + ", Prev=" - + prev + ", NextInLML=" + nextInLML + ", NextInAEL=" + nextInAEL + ", PrevInAEL=" + prevInAEL + ", NextInSEL=" + nextInSEL - + ", PrevInSEL=" + prevInSEL + "]"; - } - - public void updateDeltaX() { - - delta.setX( top.getX() - bot.getX() ); - delta.setY( top.getY() - bot.getY() ); - if (delta.getY() == 0) { - deltaX = Edge.HORIZONTAL; - } - else { - deltaX = (double) delta.getX() / delta.getY(); - } - } - -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/LongRect.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/LongRect.java deleted file mode 100644 index 089331f..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/LongRect.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -public class LongRect { - public long left; - public long top; - public long right; - public long bottom; - - public LongRect() { - - } - - public LongRect( long l, long t, long r, long b ) { - left = l; - top = t; - right = r; - bottom = b; - } - - public LongRect( LongRect ir ) { - left = ir.left; - top = ir.top; - right = ir.right; - bottom = ir.bottom; - } -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Path.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Path.java deleted file mode 100644 index 16cfa57..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Path.java +++ /dev/null @@ -1,414 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.ArrayList; -import java.util.Collections; - -/** - * A pure convenience class to avoid writing List everywhere. - * - * @author Tobias Mahlmann - * - */ -public class Path extends ArrayList { - static class Join { - OutPt outPt1; - OutPt outPt2; - private LongPoint offPt; - - public LongPoint getOffPt() { - return offPt; - } - - public void setOffPt( LongPoint offPt ) { - this.offPt = offPt; - } - - } - - static class OutPt { - public static OutRec getLowerMostRec( OutRec outRec1, OutRec outRec2 ) { - //work out which polygon fragment has the correct hole state ... - if (outRec1.bottomPt == null) { - outRec1.bottomPt = outRec1.pts.getBottomPt(); - } - if (outRec2.bottomPt == null) { - outRec2.bottomPt = outRec2.pts.getBottomPt(); - } - final OutPt bPt1 = outRec1.bottomPt; - final OutPt bPt2 = outRec2.bottomPt; - if (bPt1.getPt().getY() > bPt2.getPt().getY()) { - return outRec1; - } - else if (bPt1.getPt().getY() < bPt2.getPt().getY()) { - return outRec2; - } - else if (bPt1.getPt().getX() < bPt2.getPt().getX()) { - return outRec1; - } - else if (bPt1.getPt().getX() > bPt2.getPt().getX()) { - return outRec2; - } - else if (bPt1.next == bPt1) { - return outRec2; - } - else if (bPt2.next == bPt2) { - return outRec1; - } - else if (isFirstBottomPt( bPt1, bPt2 )) { - return outRec1; - } - else { - return outRec2; - } - } - - private static boolean isFirstBottomPt(OutPt btmPt1, OutPt btmPt2 ) { - OutPt p = btmPt1.prev; - while (p.getPt().equals( btmPt1.getPt() ) && !p.equals( btmPt1 )) { - p = p.prev; - } - final double dx1p = Math.abs( LongPoint.getDeltaX( btmPt1.getPt(), p.getPt() ) ); - p = btmPt1.next; - while (p.getPt().equals( btmPt1.getPt() ) && !p.equals( btmPt1 )) { - p = p.next; - } - final double dx1n = Math.abs( LongPoint.getDeltaX( btmPt1.getPt(), p.getPt() ) ); - - p = btmPt2.prev; - while (p.getPt().equals( btmPt2.getPt() ) && !p.equals( btmPt2 )) { - p = p.prev; - } - final double dx2p = Math.abs( LongPoint.getDeltaX( btmPt2.getPt(), p.getPt() ) ); - p = btmPt2.next; - while (p.getPt().equals( btmPt2.getPt() ) && p.equals( btmPt2 )) { - p = p.next; - } - final double dx2n = Math.abs( LongPoint.getDeltaX( btmPt2.getPt(), p.getPt() ) ); - - if (Math.max( dx1p, dx1n ) == Math.max( dx2p, dx2n ) && Math.min( dx1p, dx1n ) == Math.min( dx2p, dx2n )) { - return btmPt1.area() > 0; //if otherwise identical use orientation - } - else { - return dx1p >= dx2p && dx1p >= dx2n || dx1n >= dx2p && dx1n >= dx2n; - } - } - - int idx; - private LongPoint pt; - OutPt next; - - OutPt prev; - - public OutPt duplicate(boolean InsertAfter ) { - final OutPt result = new OutPt(); - result.setPt( new LongPoint( getPt() ) ); - result.idx = idx; - if (InsertAfter) { - result.next = next; - result.prev = this; - next.prev = result; - next = result; - } - else { - result.prev = prev; - result.next = this; - prev.next = result; - prev = result; - } - return result; - } - - OutPt getBottomPt() { - OutPt dups = null; - OutPt p = next; - OutPt pp = this; - while (p != pp) { - if (p.getPt().getY() > pp.getPt().getY()) { - pp = p; - dups = null; - } - else if (p.getPt().getY() == pp.getPt().getY() && p.getPt().getX() <= pp.getPt().getX()) { - if (p.getPt().getX() < pp.getPt().getX()) { - dups = null; - pp = p; - } - else { - if (p.next != pp && p.prev != pp) { - dups = p; - } - } - } - p = p.next; - } - if (dups != null) { - //there appears to be at least 2 vertices at bottomPt so ... - while (dups != p) { - if (!isFirstBottomPt( p, dups )) { - pp = dups; - } - dups = dups.next; - while (!dups.getPt().equals( pp.getPt() )) { - dups = dups.next; - } - } - } - return pp; - } - - public static int getPointCount( OutPt pts ) { - if (pts == null) return 0; - int result = 0; - OutPt p = pts; - do { - result++; - p = p.next; - } - while (p != pts); - return result; - } - - public LongPoint getPt() { - return pt; - } - - public void reversePolyPtLinks() { - - OutPt pp1; - OutPt pp2; - pp1 = this; - do { - pp2 = pp1.next; - pp1.next = pp1.prev; - pp1.prev = pp2; - pp1 = pp2; - } - while (pp1 != this); - } - - public void setPt( LongPoint pt ) { - this.pt = pt; - } - - private double area() { - OutPt op = this; - double a = 0; - do { - a = a + (double) (op.prev.getPt().getX() + op.getPt().getX()) * (double) (op.prev.getPt().getY() - op.getPt().getY()); - op = op.next; - } while (op != this); - return a * 0.5; - } - } - - /** OutRec: contains a path in the clipping solution. Edges in the AEL will - carry a pointer to an OutRec when they are part of the clipping solution.*/ - static class OutRec { - int Idx; - - boolean isHole; - - boolean isOpen; - OutRec firstLeft; //see comments in clipper.pas - private OutPt pts; - OutPt bottomPt; - PolyNode polyNode; - - public double area() { - return pts.area(); - } - - public void fixHoleLinkage() { - //skip if an outermost polygon or - //already already points to the correct FirstLeft ... - if (firstLeft == null || isHole != firstLeft.isHole && firstLeft.pts != null) { - return; - } - - OutRec orfl = firstLeft; - while (orfl != null && (orfl.isHole == isHole || orfl.pts == null)) { - orfl = orfl.firstLeft; - } - firstLeft = orfl; - } - - public OutPt getPoints() { - return pts; - } - - public static OutRec parseFirstLeft( OutRec firstLeft ) { - while (firstLeft != null && firstLeft.getPoints() == null) { - firstLeft = firstLeft.firstLeft; - } - return firstLeft; - } - - public void setPoints( OutPt pts ) { - this.pts = pts; - } - } - - private static OutPt excludeOp(OutPt op ) { - final OutPt result = op.prev; - result.next = op.next; - op.next.prev = result; - result.idx = 0; - return result; - } - - /** - * - */ - private static final long serialVersionUID = -7120161578077546673L; - - public Path() { - super(); - - } - - public Path( int cnt ) { - super( cnt ); - } - - public double area() { - final int cnt = size(); - if (cnt < 3) { - return 0; - } - double a = 0; - for (int i = 0, j = cnt - 1; i < cnt; ++i) { - a += ((double) get( j ).getX() + get( i ).getX()) * ((double) get( j ).getY() - get( i ).getY()); - j = i; - } - return -a * 0.5; - } - - public Path cleanPolygon() { - return cleanPolygon( 1.415 ); - } - - public Path cleanPolygon(double distance ) { - //distance = proximity in units/pixels below which vertices will be stripped. - //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have - //both x & y coords within 1 unit, then the second vertex will be stripped. - - int cnt = size(); - - if (cnt == 0) { - return new Path(); - } - - OutPt[] outPts = new OutPt[cnt]; - for (int i = 0; i < cnt; ++i) { - outPts[i] = new OutPt(); - } - - for (int i = 0; i < cnt; ++i) { - outPts[i].pt = get( i ); - outPts[i].next = outPts[(i + 1) % cnt]; - outPts[i].next.prev = outPts[i]; - outPts[i].idx = 0; - } - - final double distSqrd = distance * distance; - OutPt op = outPts[0]; - while (op.idx == 0 && op.next != op.prev) { - if (Point.arePointsClose( op.pt, op.prev.pt, distSqrd )) { - op = excludeOp( op ); - cnt--; - } - else if (Point.arePointsClose( op.prev.pt, op.next.pt, distSqrd )) { - excludeOp( op.next ); - op = excludeOp( op ); - cnt -= 2; - } - else if (Point.slopesNearCollinear( op.prev.pt, op.pt, op.next.pt, distSqrd )) { - op = excludeOp( op ); - cnt--; - } - else { - op.idx = 1; - op = op.next; - } - } - - if (cnt < 3) { - cnt = 0; - } - final Path result = new Path( cnt ); - for (int i = 0; i < cnt; ++i) { - result.add( op.pt ); - op = op.next; - } - outPts = null; - return result; - } - - public int isPointInPolygon( LongPoint pt ) { - //returns 0 if false, +1 if true, -1 if pt ON polygon boundary - //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos - //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf - int result = 0; - final int cnt = size(); - if (cnt < 3) { - return 0; - } - LongPoint ip = get( 0 ); - for (int i = 1; i <= cnt; ++i) { - final LongPoint ipNext = i == cnt ? get( 0 ) : get( i ); - if (ipNext.getY() == pt.getY()) { - if (ipNext.getX() == pt.getX() || ip.getY() == pt.getY() && ipNext.getX() > pt.getX() == ip.getX() < pt.getX()) { - return -1; - } - } - if (ip.getY() < pt.getY() != ipNext.getY() < pt.getY()) { - if (ip.getX() >= pt.getX()) { - if (ipNext.getX() > pt.getX()) { - result = 1 - result; - } - else { - final double d = (double) (ip.getX() - pt.getX()) * (ipNext.getY() - pt.getY()) - (double) (ipNext.getX() - pt.getX()) - * (ip.getY() - pt.getY()); - if (d == 0) { - return -1; - } - else if (d > 0 == ipNext.getY() > ip.getY()) { - result = 1 - result; - } - } - } - else { - if (ipNext.getX() > pt.getX()) { - final double d = (double) (ip.getX() - pt.getX()) * (ipNext.getY() - pt.getY()) - (double) (ipNext.getX() - pt.getX()) - * (ip.getY() - pt.getY()); - if (d == 0) { - return -1; - } - else if (d > 0 == ipNext.getY() > ip.getY()) { - result = 1 - result; - } - } - } - } - ip = ipNext; - } - return result; - } - - public boolean orientation() { - return area() >= 0; - } - - public void reverse() { - Collections.reverse( this ); - } - - public Path TranslatePath(LongPoint delta ) { - final Path outPath = new Path( size() ); - for (int i = 0; i < size(); i++) { - outPath.add( new LongPoint( get( i ).getX() + delta.getX(), get( i ).getY() + delta.getY() ) ); - } - return outPath; - } -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Paths.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Paths.java deleted file mode 100644 index 306567d..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Paths.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import java.util.ArrayList; - -/** - * A pure convenience class to avoid writing List everywhere. - * - * @author Tobias Mahlmann - * - */ -public class Paths extends ArrayList { - - public static Paths closedPathsFromPolyTree(PolyTree polytree ) { - final Paths result = new Paths(); - // result.Capacity = polytree.Total; - result.addPolyNode( polytree, PolyNode.NodeType.CLOSED ); - return result; - } - - public static Paths makePolyTreeToPaths(PolyTree polytree ) { - - final Paths result = new Paths(); - // result.Capacity = polytree.Total; - result.addPolyNode( polytree, PolyNode.NodeType.ANY ); - return result; - } - - public static Paths openPathsFromPolyTree(PolyTree polytree ) { - final Paths result = new Paths(); - // result.Capacity = polytree.ChildCount; - for (final PolyNode c : polytree.getChilds()) { - if (c.isOpen()) { - result.add( c.getPolygon() ); - } - } - return result; - } - - /** - * - */ - private static final long serialVersionUID = 1910552127810480852L; - - public Paths() { - super(); - } - - public Paths( int initialCapacity ) { - super( initialCapacity ); - } - - public void addPolyNode(PolyNode polynode, PolyNode.NodeType nt ) { - boolean match = true; - switch (nt) { - case OPEN: - return; - case CLOSED: - match = !polynode.isOpen(); - break; - default: - break; - } - - if (polynode.getPolygon().size() > 0 && match) { - add( polynode.getPolygon() ); - } - for (final PolyNode pn : polynode.getChilds()) { - addPolyNode( pn, nt ); - } - } - - public Paths cleanPolygons() { - return cleanPolygons( 1.415 ); - } - - public Paths cleanPolygons(double distance ) { - final Paths result = new Paths( size() ); - for (int i = 0; i < size(); i++) { - result.add( get( i ).cleanPolygon( distance ) ); - } - return result; - } - - public LongRect getBounds() { - - int i = 0; - final int cnt = size(); - final LongRect result = new LongRect(); - while (i < cnt && get( i ).isEmpty()) { - i++; - } - if (i == cnt) { - return result; - } - - result.left = get( i ).get( 0 ).getX(); - result.right = result.left; - result.top = get( i ).get( 0 ).getY(); - result.bottom = result.top; - for (; i < cnt; i++) { - for (int j = 0; j < get( i ).size(); j++) { - if (get( i ).get( j ).getX() < result.left) { - result.left = get( i ).get( j ).getX(); - } - else if (get( i ).get( j ).getX() > result.right) { - result.right = get( i ).get( j ).getX(); - } - if (get( i ).get( j ).getY() < result.top) { - result.top = get( i ).get( j ).getY(); - } - else if (get( i ).get( j ).getY() > result.bottom) { - result.bottom = get( i ).get( j ).getY(); - } - } - } - return result; - } - - public void reversePaths() { - for (final Path poly : this) { - poly.reverse(); - } - } - -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Point.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Point.java deleted file mode 100644 index 96591e3..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/Point.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import java.util.Comparator; - -public abstract class Point> { - public static class DoublePoint extends Point { - public DoublePoint() { - this( 0, 0 ); - } - - public DoublePoint( double x, double y ) { - this( x, y, 0 ); - } - - public DoublePoint( double x, double y, double z ) { - super( x, y, z ); - } - - public DoublePoint( DoublePoint other ) { - super( other ); - } - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public double getZ() { - return z; - } - } - - public static class LongPoint extends Point { - public static double getDeltaX(LongPoint pt1, LongPoint pt2 ) { - if (pt1.getY() == pt2.getY()) { - return Edge.HORIZONTAL; - } - else { - return (double) (pt2.getX() - pt1.getX()) / (pt2.getY() - pt1.getY()); - } - } - - public LongPoint() { - this( 0, 0 ); - } - - public LongPoint( long x, long y ) { - this( x, y, 0 ); - } - - public LongPoint( long x, long y, long z ) { - super( x, y, z ); - } - - public LongPoint( LongPoint other ) { - super( other ); - } - - public long getX() { - return x; - } - - public long getY() { - return y; - } - - public long getZ() { - return z; - } - } - - private static class NumberComparator> implements Comparator { - - @Override - public int compare( T a, T b ) throws ClassCastException { - return a.compareTo( b ); - } - } - - static boolean arePointsClose(Point pt1, Point pt2, double distSqrd ) { - final double dx = pt1.x.doubleValue() - pt2.x.doubleValue(); - final double dy = pt1.y.doubleValue() - pt2.y.doubleValue(); - return dx * dx + dy * dy <= distSqrd; - } - - static double distanceFromLineSqrd(Point pt, Point ln1, Point ln2 ) { - //The equation of a line in general form (Ax + By + C = 0) - //given 2 points (x¹,y¹) & (x²,y²) is ... - //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 - //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ - //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - final double A = ln1.y.doubleValue() - ln2.y.doubleValue(); - final double B = ln2.x.doubleValue() - ln1.x.doubleValue(); - double C = A * ln1.x.doubleValue() + B * ln1.y.doubleValue(); - C = A * pt.x.doubleValue() + B * pt.y.doubleValue() - C; - return C * C / (A * A + B * B); - } - - static DoublePoint getUnitNormal( LongPoint pt1, LongPoint pt2 ) { - double dx = pt2.x - pt1.x; - double dy = pt2.y - pt1.y; - if (dx == 0 && dy == 0) { - return new DoublePoint(); - } - - final double f = 1 * 1.0 / Math.sqrt( dx * dx + dy * dy ); - dx *= f; - dy *= f; - - return new DoublePoint( dy, -dx ); - } - - protected static boolean isPt2BetweenPt1AndPt3( LongPoint pt1, LongPoint pt2, LongPoint pt3 ) { - if (pt1.equals( pt3 ) || pt1.equals( pt2 ) || pt3.equals( pt2 )) { - return false; - } - else if (pt1.x != pt3.x) { - return pt2.x > pt1.x == pt2.x < pt3.x; - } - else { - return pt2.y > pt1.y == pt2.y < pt3.y; - } - } - - protected static boolean slopesEqual( LongPoint pt1, LongPoint pt2, LongPoint pt3 ) { - return (pt1.y - pt2.y) * (pt2.x - pt3.x) - (pt1.x - pt2.x) * (pt2.y - pt3.y) == 0; - } - - protected static boolean slopesEqual( LongPoint pt1, LongPoint pt2, LongPoint pt3, LongPoint pt4 ) { - return (pt1.y - pt2.y) * (pt3.x - pt4.x) - (pt1.x - pt2.x) * (pt3.y - pt4.y) == 0; - } - - static boolean slopesNearCollinear( LongPoint pt1, LongPoint pt2, LongPoint pt3, double distSqrd ) { - //this function is more accurate when the point that's GEOMETRICALLY - //between the other 2 points is the one that's tested for distance. - //nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts - if (Math.abs( pt1.x - pt2.x ) > Math.abs( pt1.y - pt2.y )) { - if (pt1.x > pt2.x == pt1.x < pt3.x) { - return distanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd; - } - else if (pt2.x > pt1.x == pt2.x < pt3.x) { - return distanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd; - } - else { - return distanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd; - } - } - else { - if (pt1.y > pt2.y == pt1.y < pt3.y) { - return distanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd; - } - else if (pt2.y > pt1.y == pt2.y < pt3.y) { - return distanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd; - } - else { - return distanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd; - } - } - } - - private final static NumberComparator NUMBER_COMPARATOR = new NumberComparator(); - - protected T x; - - protected T y; - - protected T z; - - protected Point( Point pt ) { - this( pt.x, pt.y, pt.z ); - } - - protected Point( T x, T y, T z ) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals( Object obj ) { - if (obj == null) { - return false; - } - if (obj instanceof Point) { - final Point a = (Point) obj; - return NUMBER_COMPARATOR.compare( x, a.x ) == 0 && NUMBER_COMPARATOR.compare( y, a.y ) == 0; - } - else { - return false; - } - } - - public void set( Point other ) { - x = other.x; - y = other.y; - z = other.z; - } - - public void setX( T x ) { - this.x = x; - } - - public void setY( T y ) { - this.y = y; - } - - public void setZ( T z ) { - this.z = z; - } - - @Override - public String toString() { - return "Point [x=" + x + ", y=" + y + ", z=" + z + "]"; - } - -}// end struct IntPoint \ No newline at end of file diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyNode.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyNode.java deleted file mode 100644 index b9d3529..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyNode.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import com.visual.open.anpr.core.clipper.Clipper.EndType; -import com.visual.open.anpr.core.clipper.Clipper.JoinType; -import com.visual.open.anpr.core.clipper.Point.LongPoint; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class PolyNode { - - enum NodeType { - ANY, OPEN, CLOSED - } - - private PolyNode parent; - private final Path polygon = new Path(); - private int index; - private JoinType joinType; - private EndType endType; - final List childs = new ArrayList<>(); - private boolean isOpen; - - public void addChild( PolyNode child ) { - final int cnt = childs.size(); - childs.add( child ); - child.parent = this; - child.index = cnt; - } - - public int getChildCount() { - return childs.size(); - } - - public List getChilds() { - return Collections.unmodifiableList( childs ); - } - - public List getContour() { - return polygon; - } - - public EndType getEndType() { - return endType; - } - - public JoinType getJoinType() { - return joinType; - } - - public PolyNode getNext() { - if (!childs.isEmpty()) { - return childs.get( 0 ); - } - else { - return getNextSiblingUp(); - } - } - - private PolyNode getNextSiblingUp() { - if (parent == null) { - return null; - } - else if (index == parent.childs.size() - 1) { - return parent.getNextSiblingUp(); - } - else { - return parent.childs.get( index + 1 ); - } - } - - public PolyNode getParent() { - return parent; - } - - public Path getPolygon() { - return polygon; - } - - public boolean isHole() { - return isHoleNode(); - } - - private boolean isHoleNode() { - boolean result = true; - PolyNode node = parent; - while (node != null) { - result = !result; - node = node.parent; - } - return result; - } - - public boolean isOpen() { - return isOpen; - } - - public void setEndType( EndType value ) { - endType = value; - } - - public void setJoinType( JoinType value ) { - joinType = value; - } - - public void setOpen( boolean isOpen ) { - this.isOpen = isOpen; - } - - public void setParent( PolyNode n ) { - parent = n; - - } - -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyTree.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyTree.java deleted file mode 100644 index f0d3ed4..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/clipper/PolyTree.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.visual.open.anpr.core.clipper; - -import java.util.ArrayList; -import java.util.List; - -public class PolyTree extends PolyNode { - private final List allPolys = new ArrayList(); - - public void Clear() { - allPolys.clear(); - childs.clear(); - } - - public List getAllPolys() { - return allPolys; - } - - public PolyNode getFirst() { - if (!childs.isEmpty()) { - return childs.get( 0 ); - } - else { - return null; - } - } - - public int getTotalSize() { - int result = allPolys.size(); - //with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && childs.get( 0 ) != allPolys.get( 0 )) { - result--; - } - return result; - - } - -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/domain/Polygon.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/domain/Polygon.java deleted file mode 100644 index cbc2ef6..0000000 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/domain/Polygon.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.visual.open.anpr.core.domain; - -import org.opencv.core.Point; - -import java.util.List; - -public class Polygon { - private List points; - - public Polygon(List points){ - this.points = points; - } - - public double getArea(){ - int i, j; - double area = 0; - for (i = 0; i < this.points.size(); i++) - { - j = (i + 1) % this.points.size(); - area += this.points.get(i).x * this.points.get(j).y; - area -= this.points.get(i).y * this.points.get(j).x; - } - area /= 2; - return Math.abs(area); - } - - public double getLength(){ - double result = 0; - for (int i = 0; i < this.points.size(); i++){ - Point u = points.get(i); - Point v = (i+1==this.points.size()) ? points.get(0): points.get(i+1); - double m = u.x - v.x; - double n = u.y - v.y; - result += Math.sqrt(m * m + n * n); - } - return result; - } - -} diff --git a/open-anpr-core/src/main/java/com/visual/open/anpr/core/models/TorchPlateRecognition.java b/open-anpr-core/src/main/java/com/visual/open/anpr/core/models/TorchPlateRecognition.java index 346bc8f..f91b3dd 100644 --- a/open-anpr-core/src/main/java/com/visual/open/anpr/core/models/TorchPlateRecognition.java +++ b/open-anpr-core/src/main/java/com/visual/open/anpr/core/models/TorchPlateRecognition.java @@ -31,13 +31,13 @@ public class TorchPlateRecognition extends BaseOnnxInfer implements PlateRecogn } @Override - public ParseInfo inference(ImageMat image, boolean single, Map params) { + public ParseInfo inference(ImageMat image, Boolean single, Map params) { OnnxTensor tensor = null; OrtSession.Result output = null; ImageMat imageMat = null; try { //图片处理 - imageMat = single ? image.clone() : splitAndMergePlate(image); + imageMat = null == single || single ? image.clone() : splitAndMergePlate(image); //转换数据为张量 tensor = imageMat.resizeAndNoReleaseMat(168, 48) .blobFromImageAndDoReleaseMat(1.0/255, new Scalar(MEAN_VALUE, MEAN_VALUE, MEAN_VALUE), true)