Finding if Path2D self-intersects

前端 未结 2 1886
无人共我
无人共我 2021-02-14 08:18

I need to find if Path2D intersects itself. For now, I do it by simply extracting an array of lines from path, and finding if any of these intersect. But it has O(n^2) complexit

2条回答
  •  栀梦
    栀梦 (楼主)
    2021-02-14 08:55

    Here is my Java implementation of this algorithm:

    import java.awt.Point;
    import java.awt.geom.Line2D;
    import java.awt.geom.PathIterator;
    import java.util.*;
    
    /**
     * Path2D helper functions.
     * 

    * @author Gili Tzabari */ public class Path2Ds { /** * Indicates if a Path2D intersects itself. *

    * @return true if a Path2D intersects itself */ public static boolean isSelfIntersecting(PathIterator path) { SortedSet lines = getLines(path); if (lines.size() <= 1) return false; Set candidates = new HashSet(); for (Line2D line: lines) { if (Double.compare(line.getP1().distance(line.getP2()), 0) <= 0) { // Lines of length 0 do not cause self-intersection continue; } for (Iterator i = candidates.iterator(); i.hasNext();) { Line2D candidate = i.next(); // Logic borrowed from Line2D.intersectsLine() int lineRelativeToCandidate1 = Line2D.relativeCCW(line.getX1(), line.getY1(), line. getX2(), line.getY2(), candidate.getX1(), candidate.getY1()); int lineRelativeToCandidate2 = Line2D.relativeCCW(line.getX1(), line.getY1(), line. getX2(), line.getY2(), candidate.getX2(), candidate.getY2()); int candidateRelativeToLine1 = Line2D.relativeCCW(candidate.getX1(), candidate.getY1(), candidate.getX2(), candidate.getY2(), line.getX1(), line.getY1()); int candidateRelativeToLine2 = Line2D.relativeCCW(candidate.getX1(), candidate.getY1(), candidate.getX2(), candidate.getY2(), line.getX2(), line.getY2()); boolean intersection = (lineRelativeToCandidate1 * lineRelativeToCandidate2 <= 0) && (candidateRelativeToLine1 * candidateRelativeToLine2 <= 0); if (intersection) { // Lines may share a point, so long as they extend in different directions if (lineRelativeToCandidate1 == 0 && lineRelativeToCandidate2 != 0) { // candidate.P1 shares a point with line if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0) { // line.P1 == candidate.P1 continue; } if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0) { // line.P2 == candidate.P1 continue; } // else candidate.P1 intersects line } else if (lineRelativeToCandidate1 != 0 && lineRelativeToCandidate2 == 0) { // candidate.P2 shares a point with line if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0) { // line.P1 == candidate.P2 continue; } if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0) { // line.P2 == candidate.P2 continue; } // else candidate.P2 intersects line } else { // line and candidate overlap } return true; } if (candidate.getX2() < line.getX1()) i.remove(); } candidates.add(line); } return false; } /** * Returns all lines in a path. The lines are constructed such that the starting point is found * on the left (or same x-coordinate) of the ending point. *

    * @param path the path * @return the lines, sorted in ascending order of the x-coordinate of the starting point and * ending point, respectively */ private static SortedSet getLines(PathIterator path) { double[] coords = new double[6]; SortedSet result = new TreeSet(new Comparator() { @Override public int compare(Line2D o1, Line2D o2) { int result = Double.compare(o1.getX1(), o2.getX1()); if (result == 0) { // Ensure we are consistent with equals() return Double.compare(o1.getX2(), o2.getX2()); } return result; } }); if (path.isDone()) return result; int type = path.currentSegment(coords); assert (type == PathIterator.SEG_MOVETO): type; Point.Double startPoint = new Point.Double(coords[0], coords[1]); Point.Double openPoint = startPoint; path.next(); while (!path.isDone()) { type = path.currentSegment(coords); assert (type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO): type; switch (type) { case PathIterator.SEG_MOVETO: { openPoint = startPoint; break; } case PathIterator.SEG_CLOSE: { coords[0] = openPoint.x; coords[1] = openPoint.y; break; } } Point.Double endPoint = new Point.Double(coords[0], coords[1]); if (Double.compare(startPoint.getX(), endPoint.getX()) < 0) result.add(new Line2D.Double(startPoint, endPoint)); else result.add(new Line2D.Double(endPoint, startPoint)); path.next(); startPoint = endPoint; } return result; } }

提交回复
热议问题