Search in sources :

Example 16 with Line

use of org.twak.utils.Line in project chordatlas by twak.

the class Concarnie method apply.

private List<Problem> apply(Problem problem, int count) {
    if (depth > 10 || problem.soup.isEmpty()) {
        problem.addPortal();
        return Collections.emptyList();
    }
    Set<Line> portals = new HashSet<>();
    Set<Line> in = new HashSet<>(problem.soup);
    for (Line sl : problem.hull) {
        RangeMerge<Line> rm = new RangeMerge<>(P.CON_TOL, P.CON_TOL);
        LinearForm lf = new LinearForm(sl);
        for (Line l : problem.soup) {
            if (onHull(sl, l)) {
                double pps = lf.findPParam(l.start), ppe = lf.findPParam(l.end);
                rm.add(pps, ppe, l);
                in.remove(l);
            }
        }
        List<Double> rmGet = rm.get();
        if (rmGet.isEmpty()) {
            if (!sl.start.equals(sl.end)) {
                if (Double.isNaN(sl.start.x))
                    System.out.println("help!");
                // whole thing is portal
                portals.add(sl);
            }
        } else {
            List<Point2d> occupied = new ArrayList();
            {
                double lf1 = lf.findPParam(sl.start), lf2 = lf.findPParam(sl.end);
                if (lf1 > lf2) {
                    double tmp = lf1;
                    lf1 = lf2;
                    lf2 = tmp;
                }
                for (double d : rmGet) {
                    d = Mathz.clamp(d, lf1, lf2);
                    occupied.add(lf.fromPParam(d));
                }
            }
            boolean onHull = false;
            {
                Point2d snapE = occupied.get(0), snapS = occupied.get(occupied.size() - 1);
                if (snapS.distance(sl.start) < P.CON_TOL)
                    snapS.set(sl.start);
                else {
                    occupied.add(new Point2d(sl.start));
                }
                if (snapE.distance(sl.end) < P.CON_TOL)
                    snapE.set(sl.end);
                else {
                    occupied.add(0, new Point2d(sl.end));
                    onHull = true;
                }
            }
            for (Pair<Point2d, Point2d> pair : new ConsecutiveItPairs<Point2d>(occupied)) {
                onHull = !onHull;
                if (pair.first().equals(pair.second()))
                    continue;
                Line line = new Line(pair.first(), pair.second());
                if (onHull) {
                    if (depth % 2 == 0)
                        line = line.reverse();
                    graph.add(line);
                } else {
                    portals.add(line.reverse());
                }
            }
        }
    }
    if (in.size() == problem.soup.size()) {
        // we didn't do anything! remove something, anything...
        List<Line> d = new ArrayList(in);
        Collections.sort(d, (a, b) -> Double.compare(a.length(), b.length()));
        for (// remove the shortest 1/3 lines
        int i = 0; // remove the shortest 1/3 lines
        i < Math.max(1, in.size() * 0.33); // remove the shortest 1/3 lines
        i++) in.remove(d.get(i));
    }
    List<Portal> mergedPortals = mergeConsecutive(portals);
    // assign each member of in to a portal
    MultiMapSet<Portal, Line> subproblems = new MultiMapSet();
    if (!mergedPortals.isEmpty()) {
        // O(n^3) closest-clique assignment
        MultiMapSet<Portal, Line> sub2 = new MultiMapSet();
        for (Portal p : mergedPortals) sub2.putAll(p, p.lines);
        while (!in.isEmpty()) {
            double bestDist = Double.MAX_VALUE;
            Portal bestP = null;
            Line bestL = null;
            for (Line l : in) for (Portal p : sub2.keySet()) for (Line sl : sub2.get(p)) {
                double dlsl = l.distance(sl);
                if (// ignore lines a long way away
                dlsl > Math.max(P.CON_TOL * 3, p.length * 0.5))
                    continue;
                double dist = dlsl + 0.1 * l.distance(p.summary);
                if (dist < bestDist) {
                    bestP = p;
                    bestDist = dist;
                    bestL = l;
                }
            }
            if (bestL == null)
                break;
            in.remove(bestL);
            double lenBestL = bestL.length();
            if (lenBestL > P.CON_TOL && lenBestL > 0.5 * bestP.summary.length()) {
                in.add(new Line(bestL.start, bestL.fromPPram(0.5)));
                in.add(new Line(bestL.fromPPram(0.5), bestL.end));
            } else {
                subproblems.put(bestP, bestL);
                sub2.put(bestP, bestL);
            }
        }
    } else {
        mergedPortals.add(null);
        subproblems.putAll(null, in);
    }
    return mergedPortals.stream().map(x -> new Problem(x == null ? Collections.emptyList() : x.lines, subproblems.get(x))).collect(Collectors.toList());
}
Also used : Arrays(java.util.Arrays) Location(org.apache.commons.math3.geometry.partitioning.Region.Location) ConsecutivePairs(org.twak.utils.collections.ConsecutivePairs) Segment(org.apache.commons.math3.geometry.euclidean.twod.Segment) ConvexHull2D(org.apache.commons.math3.geometry.euclidean.twod.hull.ConvexHull2D) MultiMapSet(org.twak.utils.collections.MultiMapSet) HashMap(java.util.HashMap) Graph2D(org.twak.utils.geom.Graph2D) Pair(org.twak.utils.Pair) ConsecutiveItPairs(org.twak.utils.collections.ConsecutiveItPairs) Vector2D(org.apache.commons.math3.geometry.euclidean.twod.Vector2D) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Loop(org.twak.utils.collections.Loop) Map(java.util.Map) Mathz(org.twak.utils.Mathz) PaintThing(org.twak.utils.PaintThing) Euclidean2D(org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D) MathIllegalArgumentException(org.apache.commons.math3.exception.MathIllegalArgumentException) LoopL(org.twak.utils.collections.LoopL) UnionWalker(org.twak.utils.geom.UnionWalker) Iterator(java.util.Iterator) Collection(java.util.Collection) Line(org.twak.utils.Line) Set(java.util.Set) MonotoneChain(org.apache.commons.math3.geometry.euclidean.twod.hull.MonotoneChain) Region(org.apache.commons.math3.geometry.partitioning.Region) Vector2d(javax.vecmath.Vector2d) LinearForm(org.twak.utils.geom.LinearForm) Collectors(java.util.stream.Collectors) Point2d(javax.vecmath.Point2d) List(java.util.List) Paint(java.awt.Paint) ItComb(org.twak.utils.collections.ItComb) Loopable(org.twak.utils.collections.Loopable) InsufficientDataException(org.apache.commons.math3.exception.InsufficientDataException) Anglez(org.twak.utils.geom.Anglez) Collections(java.util.Collections) ConsecutiveItPairs(org.twak.utils.collections.ConsecutiveItPairs) ArrayList(java.util.ArrayList) LinearForm(org.twak.utils.geom.LinearForm) Paint(java.awt.Paint) Line(org.twak.utils.Line) Point2d(javax.vecmath.Point2d) HashSet(java.util.HashSet) MultiMapSet(org.twak.utils.collections.MultiMapSet)

Example 17 with Line

use of org.twak.utils.Line in project chordatlas by twak.

the class Concarnie method tidy.

private void tidy(Graph2D graph) {
    UnionWalker uw = new UnionWalker();
    for (Point2d a : graph.map.keySet()) {
        for (Line l : graph.get(a)) {
            uw.addEdge(l.start, l.end);
        // PaintThing.debug.put("flibble", l);
        }
    }
    out = uw.findAll();
    // if (false)
    for (Loop<Point2d> loop : out) {
        for (Loopable<Point2d> pt : loop.loopableIterator()) {
            Line prev = findSupporting(pt, 1), next = findSupporting(pt, -1);
            if (prev != null && next != null) {
                Point2d bestFit = prev.intersects(next, false);
                if (bestFit != null && pt.get().distance(bestFit) < P.CON_TOL && // if nearer other ends of lines, don't use
                bestFit.distanceSquared(prev.start) < bestFit.distanceSquared(prev.end) && bestFit.distanceSquared(next.end) < bestFit.distanceSquared(next.start)) {
                    pt.get().set(bestFit);
                }
            }
        }
    }
    Iterator<Loop<Point2d>> lit = out.iterator();
    // if (false)
    while (lit.hasNext()) {
        Loop<Point2d> loop = lit.next();
        Loopable<Point2d> start = loop.start, current = start;
        int size = loop.count();
        boolean again;
        do {
            again = false;
            Point2d a = current.getPrev().get(), b = current.get(), c = current.getNext().get();
            Line ab = new Line(a, b), bc = new Line(b, c);
            double angle = Anglez.dist(ab.aTan2(), bc.aTan2());
            if (// corner filter moves corners to same point!
            a.distanceSquared(b) < 0.0001 || b.distanceSquared(c) < 0.0001 || // nearly parallel, but small area removed
            angle < 0.2 && Math.abs(Mathz.area(a, b, c)) < 50 * P.CON_TOL * P.CON_TOL) // ab.length() + bc.length() > TOL/2 &&
            // angle > Math.PI - 0.1 ) // pointy corner
            // a.distanceSquared(c) < 0.0001 )
            {
                current.getPrev().setNext(current.getNext());
                current.getNext().setPrev(current.getPrev());
                size--;
                if (start == current)
                    loop.start = start = current.getPrev();
                again = true;
                current = current.getPrev();
            } else
                current = current.getNext();
        } while ((again || current != start) && size > 2);
        if (size <= 2)
            lit.remove();
    }
}
Also used : Line(org.twak.utils.Line) Loop(org.twak.utils.collections.Loop) Point2d(javax.vecmath.Point2d) UnionWalker(org.twak.utils.geom.UnionWalker) Paint(java.awt.Paint)

Example 18 with Line

use of org.twak.utils.Line in project chordatlas by twak.

the class Concarnie method mergeConsecutive.

private List<Portal> mergeConsecutive(Set<Line> portals) {
    // merge consecutive portals
    List<Portal> portalsOut = new ArrayList();
    Map<Point2d, Line> starts = new HashMap<>(), ends = new HashMap<>();
    for (Line l : portals) {
        Line replace = starts.put(l.start, l);
        if (replace != null)
            portalsOut.add(new Portal(replace));
        replace = ends.put(l.end, l);
        if (replace != null)
            portalsOut.add(new Portal(replace));
    }
    while (!starts.isEmpty()) {
        Portal p = new Portal();
        portalsOut.add(p);
        Line start = starts.entrySet().iterator().next().getValue(), current = start;
        do {
            p.lines.add(current);
            p.length += current.length();
            starts.remove(current.start);
            ends.remove(current.end);
            current = starts.get(current.end);
        } while (current != null);
        current = start;
        current = ends.get(current.start);
        if (current != null)
            do {
                p.lines.add(0, current);
                p.length += current.length();
                starts.remove(current.start);
                ends.remove(current.end);
                current = ends.get(current.start);
            } while (current != null);
        p.summary = new Line(p.lines.get(0).start, p.lines.get(p.lines.size() - 1).end);
    }
    Collections.sort(portalsOut, (a, b) -> -Double.compare(a.length, b.length));
    return portalsOut;
}
Also used : Line(org.twak.utils.Line) Point2d(javax.vecmath.Point2d) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList)

Example 19 with Line

use of org.twak.utils.Line in project chordatlas by twak.

the class FindLines method regress.

private Line regress(Set<Line> things, LinearForm lfDir) {
    SimpleRegression fit = new SimpleRegression();
    // regression isn't happy on lines with infinite slope: so swap params!
    boolean flip = Math.abs(lfDir.x) > Math.abs(lfDir.y);
    for (Line l : things) if (flip) {
        fit.addData(l.start.y, l.start.x);
        fit.addData(l.end.y, l.end.x);
    } else {
        fit.addData(l.start.x, l.start.y);
        fit.addData(l.end.x, l.end.y);
    }
    double intercept = fit.getIntercept(), slope = fit.getSlope();
    if (Double.isNaN(intercept))
        return null;
    LinearForm lf;
    if (flip)
        lf = new LinearForm(1, -slope);
    else
        lf = new LinearForm(-slope, 1);
    if (lf.unitVector().angle(lfDir.unitVector()) > Math.PI / 2) {
        // if regression is pointing wrong way, flip
        lf.x = -lf.x;
        lf.y = -lf.y;
    }
    if (flip)
        lf.findC(intercept, 0);
    else
        lf.findC(0, intercept);
    double[] minMax = things.stream().map(x -> new double[] { lf.findPParam(x.start), lf.findPParam(x.end) }).collect(new InAxDoubleArray());
    // do regression
    return new Line(lf, minMax[0], minMax[1]);
}
Also used : Line(org.twak.utils.Line) Iterator(java.util.Iterator) Line(org.twak.utils.Line) Set(java.util.Set) MultiMapSet(org.twak.utils.collections.MultiMapSet) HashMap(java.util.HashMap) Vector2d(javax.vecmath.Vector2d) Pair(org.twak.utils.Pair) LinearForm(org.twak.utils.geom.LinearForm) ConsecutiveItPairs(org.twak.utils.collections.ConsecutiveItPairs) ArrayList(java.util.ArrayList) Point2d(javax.vecmath.Point2d) List(java.util.List) SimpleRegression(org.apache.commons.math3.stat.regression.SimpleRegression) Map(java.util.Map) Anglez(org.twak.utils.geom.Anglez) InAxDoubleArray(org.twak.utils.streams.InAxDoubleArray) Result(org.twak.viewTrace.RangeMerge.Result) InAxDoubleArray(org.twak.utils.streams.InAxDoubleArray) SimpleRegression(org.apache.commons.math3.stat.regression.SimpleRegression) LinearForm(org.twak.utils.geom.LinearForm)

Example 20 with Line

use of org.twak.utils.Line in project chordatlas by twak.

the class LineSoup method clique.

public Map<Line, Integer> clique(double highTol, double lowTol) {
    int currentC = 0;
    Graph2D lookup = new Graph2D(all);
    Set<Line> remaining = new LinkedHashSet<>(all), visited = new HashSet<>();
    Map<Line, Integer> out = new HashMap<>();
    while (!remaining.isEmpty()) {
        Set<Line> queue = new LinkedHashSet<>();
        queue.add(remaining.iterator().next());
        while (!queue.isEmpty()) {
            Line l = queue.iterator().next();
            queue.remove(l);
            remaining.remove(l);
            visited.add(l);
            out.put(l, currentC);
            for (Point2d pt : l.points()) {
                // find nearest with parity one
                double tol = lookup.get(pt).size() % 2 == 0 ? highTol : lowTol;
                for (Line l2 : getNear(pt, tol)) {
                    if (!visited.contains(l2) && l2.distance(pt, true) < tol)
                        queue.add(l2);
                }
            }
        }
        currentC++;
    }
    return out;
}
Also used : Line(org.twak.utils.Line) QLine(org.twak.viewTrace.QuadTree.QLine) LinkedHashSet(java.util.LinkedHashSet) HashMap(java.util.HashMap) Point2d(javax.vecmath.Point2d) Graph2D(org.twak.utils.geom.Graph2D) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Aggregations

Line (org.twak.utils.Line)59 Point2d (javax.vecmath.Point2d)38 ArrayList (java.util.ArrayList)25 SuperLine (org.twak.viewTrace.SuperLine)22 List (java.util.List)16 Point3d (javax.vecmath.Point3d)14 HashSet (java.util.HashSet)13 LinearForm (org.twak.utils.geom.LinearForm)13 HashMap (java.util.HashMap)12 Map (java.util.Map)11 Set (java.util.Set)11 HalfEdge (org.twak.utils.geom.HalfMesh2.HalfEdge)11 HalfFace (org.twak.utils.geom.HalfMesh2.HalfFace)11 Collectors (java.util.stream.Collectors)10 Vector2d (javax.vecmath.Vector2d)10 Iterator (java.util.Iterator)9 Vector3d (javax.vecmath.Vector3d)9 Tweed (org.twak.tweed.Tweed)8 TweedSettings (org.twak.tweed.TweedSettings)8 MFPoint (org.twak.tweed.gen.FeatureCache.MFPoint)8