14 changed files with 3 additions and 5063 deletions
			
			
		@ -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); | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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<OutRec> 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<Edge> 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" ); | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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<DoublePoint> 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; | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
    //------------------------------------------------------------------------------
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
								
									
										File diff suppressed because it is too large
									
								
							
						
					@ -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(); | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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; | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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<IntPoint> everywhere. | 
					 | 
				
			||||
 * | 
					 | 
				
			||||
 * @author Tobias Mahlmann | 
					 | 
				
			||||
 * | 
					 | 
				
			||||
 */ | 
					 | 
				
			||||
public class Path extends ArrayList<LongPoint> { | 
					 | 
				
			||||
    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; | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -1,125 +0,0 @@ | 
				
			|||||
package com.visual.open.anpr.core.clipper; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
import java.util.ArrayList; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
/** | 
					 | 
				
			||||
 * A pure convenience class to avoid writing List<Path> everywhere. | 
					 | 
				
			||||
 * | 
					 | 
				
			||||
 * @author Tobias Mahlmann | 
					 | 
				
			||||
 * | 
					 | 
				
			||||
 */ | 
					 | 
				
			||||
public class Paths extends ArrayList<Path> { | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    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(); | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -1,220 +0,0 @@ | 
				
			|||||
package com.visual.open.anpr.core.clipper; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
import java.util.Comparator; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
public abstract class Point<T extends Number & Comparable<T>> { | 
					 | 
				
			||||
    public static class DoublePoint extends Point<Double> { | 
					 | 
				
			||||
        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<Long> { | 
					 | 
				
			||||
        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<T extends Number & Comparable<T>> implements Comparator<T> { | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        @Override | 
					 | 
				
			||||
        public int compare( T a, T b ) throws ClassCastException { | 
					 | 
				
			||||
            return a.compareTo( b ); | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    static boolean arePointsClose(Point<? extends Number> pt1, Point<? extends Number> 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<? extends Number> pt, Point<? extends Number> ln1, Point<? extends Number> 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<T> 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<T> 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
 | 
					 | 
				
			||||
@ -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<PolyNode> 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<PolyNode> getChilds() { | 
					 | 
				
			||||
        return Collections.unmodifiableList( childs ); | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    public List<LongPoint> 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; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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<PolyNode> allPolys = new ArrayList<PolyNode>(); | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    public void Clear() { | 
					 | 
				
			||||
        allPolys.clear(); | 
					 | 
				
			||||
        childs.clear(); | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    public List<PolyNode> 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; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -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<Point> points; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    public Polygon(List<Point> 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; | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue