use of org.twak.utils.Line in project chordatlas by twak.
the class CutHoles method cutHoles.
public static void cutHoles(LoopL<Point2d> out, double tol, Map<Point2d, Line> created) {
MultiMap<Boolean, Loop<Point2d>> holeToLoop = new MultiMap<>();
Iterator<Loop<Point2d>> lit = out.iterator();
while (lit.hasNext()) {
// a hole can be a backwards loop...
Loop<Point2d> loop = lit.next();
double area = Loopz.area(loop);
if (Math.abs(area) < tol * tol)
lit.remove();
boolean isHole = area > 0;
holeToLoop.put(isHole, loop);
for (Loop<Point2d> h : loop.holes) {
if (Loopz.area(h) > 0)
h.reverse();
holeToLoop.put(false, h);
}
}
for (Loop<Point2d> hole : holeToLoop.get(false)) {
Point2d origin = new Point2d(Double.MAX_VALUE, 0);
Loopable<Point2d> originL = null;
for (Loopable<Point2d> p : hole.loopableIterator()) {
if (p.get().x < origin.x) {
originL = p;
origin = originL.get();
}
}
LinearForm ray = new LinearForm(0, 1);
ray.findC(origin);
double nearestD = Double.MAX_VALUE;
Loopable<Point2d> nearestL = null;
Point2d nearestH = null;
for (Loop<Point2d> loop : out) {
for (Loopable<Point2d> line : loop.loopableIterator()) {
Point2d a = line.get(), b = line.getNext().get();
if (a.y > origin.y && b.y < origin.y || a.y < origin.y && b.y > origin.y) {
Point2d hit = new Line(a, b).intersects(ray);
if (hit != null && hit.x < origin.x && (origin.x - hit.x < nearestD)) {
nearestD = origin.x - hit.x;
nearestL = line;
nearestH = hit;
}
}
}
}
if (nearestH == null)
System.err.println("failed to find outer ring for hole");
else {
if (created != null)
created.put(nearestH, new Line(nearestL.get(), nearestL.getNext().get()));
Loopable<Point2d> hitPtF = new Loopable<Point2d>(nearestH), hitPtS = new Loopable<Point2d>(nearestH), originL2 = new Loopable(origin);
hitPtS.setNext(nearestL.getNext());
hitPtF.setPrev(nearestL);
hitPtS.getNext().setPrev(hitPtS);
hitPtF.getPrev().setNext(hitPtF);
originL.getNext().setPrev(originL2);
originL2.setNext(originL.getNext());
originL.setNext(hitPtS);
hitPtS.setPrev(originL);
hitPtF.setNext(originL2);
originL2.setPrev(hitPtF);
}
out.remove(hole);
}
}
use of org.twak.utils.Line in project chordatlas by twak.
the class Concarnie method findSupporting.
private Line findSupporting(Loopable<Point2d> pt, int dir) {
double totalDist = 0;
Point2d ptg = pt.get();
Loopable<Point2d> current = pt;
int count = 0;
do {
Point2d a = current.get(), b = current.move(dir).get();
if (dir < 0) {
Point2d tmp = a;
a = b;
b = tmp;
}
Line hull = new Line(a, b);
double bestDist = Double.MAX_VALUE;
Line bestLine = null;
for (Line l : new ItComb<>(in.getNear(hull.start, 0.5), in.getNear(hull.end, 0.5))) if (Anglez.dist(l.aTan2(), hull.aTan2()) < 0.2 && maxPerpDistance(l, hull) < 0.2) {
double dist = l.distance(ptg, true);
if (dist < bestDist) {
bestDist = dist;
bestLine = l;
}
}
if (bestLine != null)
return bestLine;
totalDist += hull.length();
current = current.move(dir);
} while (totalDist < 1 && count++ < 30);
return null;
}
use of org.twak.utils.Line in project chordatlas by twak.
the class FindLines method findNextAngle.
// private double findNextAngle2(GBias bias, Set<Line> remaining, Bin directionBias, double remainingLength) {
//
// Bin aBin = buildNormalized(remaining, remainingLength);
//
// aBin.multiply(directionBias, 0.5);
//
// int aBinI = aBin.maxI();
// return aBin.val(aBinI);
//
// }
// private Bin buildNormalized(Set<Line> remaining, double length) {
// double delta = Math.PI / P.FL_BINS;
// Bin<Line> aBin = new Bin(-Math.PI - delta, Math.PI + delta, P.FL_BINS, true); // angle bin
//
// for (Line l : remaining) {
// double len = l.length();
// aBin.add( l.aTan2(), len, l );
// }
//
// aBin.multiply(1/length);
// return aBin;
// }
private double findNextAngle(Set<Line> remaining) {
double delta = Math.PI / P.FL_BINS;
// angle bin
Bin<Line> aBin = new Bin(-Math.PI - delta, Math.PI + delta, P.FL_BINS * 2, true);
for (Line l : remaining) {
double len = l.length();
double angle = l.aTan2();
aBin.add(angle, len, l);
}
int aBinI = aBin.maxI();
return aBin.val(aBinI);
}
use of org.twak.utils.Line in project chordatlas by twak.
the class FindLines method rotateToAngle.
public static Line rotateToAngle(Line nLine, Point2d cen, double angle) {
double len = nLine.length() / 2;
Vector2d dir = new Vector2d(-Math.cos(angle) * len, -Math.sin(angle) * len);
Point2d start = new Point2d(cen), end = new Point2d(cen);
start.add(dir);
end.sub(dir);
return new Line(start, end);
}
use of org.twak.utils.Line in project chordatlas by twak.
the class FindLines method forClique.
private void forClique(Set<Line> remaining, List<Line> out, int debugLine, int clique) {
Iterator<Line> filter = remaining.iterator();
while (filter.hasNext()) {
Line l = filter.next();
// double l = filter.next().length();
if (l.lengthSquared() == 0 || l.hasNaN())
filter.remove();
}
if (remaining.isEmpty())
return;
int count = 0;
while (!remaining.isEmpty()) {
double angle = nextAngle(remaining, count);
// offset bin
Bin.Builder<Line> oBBin = new Bin.Builder<>();
LinearForm lfPerp = new LinearForm(Math.cos(angle), Math.sin(angle));
for (Line l : remaining) {
if (Anglez.dist(l.aTan2(), angle) < Math.PI / 2) {
// and line angle near angle+-180
oBBin.add(lfPerp.findPParam(l.start), l.length(), l);
oBBin.add(lfPerp.findPParam(l.end), l.length(), l);
}
}
// we're super sensitive to the number of orientation bins :(
Bin<Line> oBin = oBBin.done(P.FL_BINS * 2, false);
int oBinI = oBin.maxI();
int[] oBinM = oBin.maxMode(1, 10);
LinearForm lfDir = new LinearForm(lfPerp.y, -lfPerp.x);
lfDir.findC(lfPerp.fromPParam(oBin.val(oBinI)));
RangeMerge<Line> rm = new RangeMerge(P.FL_GAP_TOL, P.FL_GAP_TOL / 2);
for (Line w : oBin.getThings(oBinI, oBinM)) {
if (lfDir.distance(w.start) < getTolNearLine(w.start) && lfDir.distance(w.end) < getTolNearLine(w.end) && // and line angle near angle+-180
Anglez.dist(w.aTan2(), angle) < Math.PI / 2.5) {
rm.add(lfDir.findPParam(w.start), lfDir.findPParam(w.end), w);
}
}
List<Result<Line>> merged = rm.getResults();
if (merged.isEmpty()) {
for (Line w : oBin.getThings(oBinI, oBinM)) remaining.remove(w);
} else {
double maxP = merged.stream().map(x -> x.max - x.min).max(Double::compare).get();
for (Result<Line> r : merged) {
if (r.max - r.min < 0.2 * maxP)
continue;
Line line = null;
if (P.FL_REGRESS)
line = regress(r.things, lfDir);
if (line == null)
line = new Line(lfDir, r.min, r.max);
Iterator<Line> rit = remaining.iterator();
boolean removed = false;
Bin<Line> counts = null;
if (P.MIN_LINES > 1)
counts = new Bin<>(0, 1, 50, false);
while (rit.hasNext()) {
Line rine = rit.next();
if (line.distance(rine.start, true) < getTolNearLine2(rine.start) && line.distance(rine.end, true) < getTolNearLine2(rine.end) && Anglez.dist(line.aTan2(), rine.aTan2()) < getTolRemoveAngle(rine)) {
if (counts != null)
counts.addRange(line.findPPram(rine.start), line.findPPram(rine.end), 1, rine);
removed = true;
rit.remove();
}
}
if (counts != null) {
// for (Pair<Double, Double > d : new ConsecutiveItPairs<>( counts.getRegionAbove( P.MIN_LINES ) )) {
//
// Point2d start = line.fromPPram( counts.getFirst( P.MIN_LINES ) ),
// end = line.fromPPram( counts.getLast( P.MIN_LINES ) );
//
// if ( start != null && end != null ) {
// out.add( new Line(start, end) );
// }
// }
Point2d start = line.fromPPram(counts.getFirst(P.MIN_LINES)), end = line.fromPPram(counts.getLast(P.MIN_LINES));
if (start != null && end != null) {
line.start = start;
line.end = end;
} else
line = null;
}
if (!removed)
for (Line l : r.things) {
if (remaining.contains(l)) {
remaining.remove(l);
}
}
if (line != null) {
if (bias != null) {
Point2d cen = line.fromPPram(0.5);
Double toGIS = bias.getAngle(line, cen);
if (toGIS != null)
line = rotateToAngle(line, cen, toGIS);
}
if (debugFilter(count, debugLine) && line.start.distanceSquared(line.end) > 0.001) {
out.add(line);
cliques.put(line, clique);
}
}
}
}
count++;
}
}
Aggregations