Algorithm to split self-intersected Path2D into several not self-intersected paths?

前端 未结 3 1851
眼角桃花
眼角桃花 2021-02-06 13:24

I need to get rid of self-intersections in a shape. Shape is constructed from an array of points, so all segments of that shape are lines. (only lines, no curves and arcs

相关标签:
3条回答
  • 2021-02-06 13:30

    If Area is not working for you, you could try using a GLUtessellator to decompose your Shape into a set of triangles, or (using the GL_LINE_LOOP option) just the boundary edges.

    0 讨论(0)
  • 2021-02-06 13:46

    So, since I was unable to find anything like this on the web, I written my own algorithm.

    It may be insanely ineffective, but it works fast enough for me.

    Here it goes:

    /**
     * Takes a polygon, defined by a list of lines, and splits it into several
     * paths on points of intersection. If non-self-intersected path is passed in,
     * the same path is returned.
     * @param path
     * @return
     */
    public static List<List<Line2D>> splitPath(List<Line2D> lines) {
        List<List<Line2D>> splitted = new ArrayList<List<Line2D>>();
        // find intersections.
        loop1:
        for (Line2D l1 : lines) {
            for (Line2D l2 : lines) {
                if (l1 == l2) continue;
                Point2D intr;
                if ((intr = linesIntersect(l1, l2)) != null) {
                    // creating two splitted subpaths
                    int i1 = lines.indexOf(l1);
                    int i2 = lines.indexOf(l2);
    
                    List<Line2D> subpath1 = new ArrayList<Line2D>();
                    subpath1.addAll(lines.subList(0, i1));
                    subpath1.add(new Line2D.Double(l1.getP1(), intr));
                    subpath1.add(new Line2D.Double(intr, l2.getP2()));
                    subpath1.addAll(lines.subList(i2 + 1, lines.size()));
                    splitted.addAll(splitPath(subpath1));
    
                    List<Line2D> subpath2 = new ArrayList<Line2D>();
                    subpath2.add(new Line2D.Double(intr, l1.getP2()));
                    subpath2.addAll(lines.subList(i1 + 1, i2));
                    subpath2.add(new Line2D.Double(l2.getP1(), intr));
                    splitted.addAll(splitPath(subpath2));
                    break loop1;
                }
            }
        }
        if (splitted.size() > 0) {
            return splitted;
        } else {
            return Collections.singletonList(lines);
        }
    }
    
    /**
     * Returns point of intersection of this line segments.
     * @param l1
     * @param l2
     * @return
     */
    public static Point2D linesIntersect(Line2D l1, Line2D l2) {
        if (l1.getP1().equals(l2.getP2()) || l1.getP2().equals(l2.getP1())) return null;
        Point2D inter = lineIntersection(l1, l2);
        if (inter == null) return null;
        double infS = HEADER.infS;
        double x = inter.getX();
        if (((l1.getX1() > l1.getX2()) ? (x + infS > l1.getX2() && x - infS < l1.getX1()) : (x - infS < l1.getX2() && x + infS > l1.getX1())) &&
               ((l2.getX1() > l2.getX2()) ? (x + infS > l2.getX2() && x - infS < l2.getX1()) : (x - infS < l2.getX2() && x + infS > l2.getX1()))) {
            return inter;
        } else {
            return null;
        }
    }
    
    /**
     * Returns point of lines intersection, or null if they are parallel.
     * @param l1
     * @param l2
     * @return
     */
    public static Point2D lineIntersection(Line2D l1, Line2D l2) {
        double a1 = l1.getY2() - l1.getY1();
        double b1 = l1.getX1() - l1.getX2();
        double c1 = a1*l1.getX1() + b1*l1.getY1();
        double a2 = l2.getY2() - l2.getY1();
        double b2 = l2.getX1() - l2.getX2();
        double c2 = a2*l2.getX1() + b2*l2.getY1();
        double det = a1*b2 - a2*b1;
        if (det != 0) {
            double x = (b2*c1 - b1*c2)/det;
            double y = (a1*c2 - a2*c1)/det;
            return new Point2D.Double(x, y);
        } else {
            // lines are parallel
            return null;
        }
    }
    
    0 讨论(0)
  • 2021-02-06 13:54

    I bookmarked your question/answer in case I had to implement something similar myself, but then I found the GEOS project which has a simple way of achieving this. I'm calling GEOS from Python/Django, but the whole thing is based on JTS (Java Topology Suite) so I'd start there and treat the following Python as psuedo-code.

    Basically, the UNION operation will split a line into simply connected parts if it is not simply connected (explained here), so UNIONing the line with it's first point does what we need:

    line  = LineString(list_of_lines_x_y_coordinates)
    # union with first point splits into MultiLineString containing segments
    segments = line.union(line[0]) 
    
    0 讨论(0)
提交回复
热议问题