use of org.twak.utils.geom.LinearForm 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++;
}
}
use of org.twak.utils.geom.LinearForm in project chordatlas by twak.
the class GreebleSkel method mapTo2d.
protected void mapTo2d(Face f, Loop<LPoint3d> ll, MiniFacade mf, WallTag wallTag, Set<QuadF> features, MatMeshBuilder faceMaterial) {
Matrix4d to2dXY = new Matrix4d();
Vector3d up = f.edge.uphill, along = f.edge.direction(), out = f.edge.getPlaneNormal();
along.normalize();
to2dXY.setRow(2, up.x, up.y, up.z, 0);
to2dXY.setRow(1, out.x, out.y, out.z, 0);
to2dXY.setRow(0, -along.x, -along.y, -along.z, 0);
Point3d bottomS = f.definingSE.iterator().next().getStart(f), bottomE = f.definingSE.iterator().next().getEnd(f);
Point3d start = new Point3d(bottomS);
Point3d end = new Point3d(bottomE);
to2dXY.m33 = 1;
to2dXY.transform(start);
to2dXY.m03 = -start.x;
to2dXY.m13 = -start.y;
to2dXY.m23 = -start.z;
start = new Point3d(bottomS);
to2dXY.transform(start);
to2dXY.transform(end);
Loop<LPoint2d> flat = GreebleHelper.to2dLoop(GreebleHelper.transform(ll, to2dXY), 1);
Matrix4d to3d = new Matrix4d(to2dXY);
to3d.invert();
{
// face in z-up, we're in y-up
double[] one = new double[4], two = new double[4];
to3d.getRow(1, one);
to3d.getRow(2, two);
to3d.setRow(1, two);
to3d.setRow(2, one);
}
// now in jme space
Matrix4d to2d = new Matrix4d(to3d);
to2d.invert();
MiniFacade forFace = null;
if (mf != null) {
forFace = new MiniFacade(mf);
forFace.rects.clear();
}
LinearForm3D facePlane = new LinearForm3D(new Vector3d(out.x, out.z, out.y), new Point3d(bottomS.x, bottomS.z, bottomS.y));
LoopL<Point2d> sides = null;
DRectangle facadeRect = null;
if (wallTag != null) {
sides = findRectagle(flat, Pointz.to2(start), Pointz.to2(end));
if (sides != null)
facadeRect = findRect(sides.remove(0));
}
List<DRectangle> floors = new ArrayList();
List<MeshBuilder> materials = new ArrayList();
if (wallTag != null && facadeRect != null && mf != null && wallTag.isGroundFloor && mf.groundFloorHeight > 0 && wallTag.groundFloorColor != null && facadeRect.x < mf.groundFloorHeight && facadeRect.getMaxX() > mf.groundFloorHeight) {
floors.addAll(facadeRect.splitY(mf.groundFloorHeight));
MatMeshBuilder gfm = greebleGrid.mbs.get("brick", wallTag.groundFloorColor);
for (Loop<Point2d> loop : sides) {
Loop<Point2d>[] cut = Loopz.cutConvex(loop, new LinearForm(0, 1, mf.groundFloorHeight));
faceMaterial.add(cut[1].singleton(), to3d);
gfm.add(cut[0].singleton(), to3d);
}
materials.add(gfm);
materials.add(faceMaterial);
} else {
floors.add(facadeRect);
materials.add(faceMaterial);
if (sides != null)
faceMaterial.add(sides, to3d);
}
for (int j = 0; j < floors.size(); j++) {
DRectangle floorRect = floors.get(j);
MeshBuilder m = materials.get(j);
Iterator<QuadF> quit = features.iterator();
while (quit.hasNext()) {
QuadF n = quit.next();
if (n.project(to2d, to3d, flat, facePlane, new Vector3d(along.y, 0, -along.x)) && wallTag != null && floorRect != null && forFace != null) {
// set the vertical bounds, so we can just render in 2d
FRect bounds = new FRect(n.original);
n.setBounds(to2d, bounds);
if (floorRect.contains(bounds)) {
forFace.rects.put(n.original.f, bounds);
quit.remove();
}
}
}
if (wallTag == null || forFace == null || floorRect == null) {
m.add(flat.singleton(), to3d);
return;
}
List<DRectangle> occlusions = new ArrayList<>();
for (LineHeight lh : wallTag.occlusions) {
Point3d s = new Point3d(lh.start.x, lh.start.y, lh.min), e = new Point3d(lh.end.x, lh.end.y, lh.max);
to2dXY.transform(s);
to2dXY.transform(e);
occlusions.add(new DRectangle(Math.min(s.x, e.x), s.z, Math.abs(e.x - s.x), Math.abs(e.z - s.z)));
}
if (mf.texture == null)
greebleGrid.buildGrid(floorRect, to3d, forFace, m, wallTag);
else
greebleGrid.textureGrid(floorRect, to3d, mf);
}
}
use of org.twak.utils.geom.LinearForm in project chordatlas by twak.
the class GreebleSkel method setToHeight.
private static PtInChain setToHeight(List<Point2d> chain, boolean left, double x2, double y2) {
double bestX = chain.get(0).x;
for (int i = 1; i < chain.size(); i++) {
Point2d p = chain.get(i - 1), n = chain.get(i);
bestX = left ? Math.max(p.x, bestX) : Math.min(p.x, bestX);
PtInChain first = new PtInChain(new Point2d(n), i, 0);
first.x = bestX;
if (Math.abs(n.y - y2) < 0.001) {
first.x = left ? Math.max(n.x, first.x) : Math.min(n.x, first.x);
return first;
}
Line pn = new Line(p, n);
if (n.y > y2) {
Point2d sec = new LinearForm(pn).intersect(new LinearForm(0, 1, y2));
if (sec == null)
return first;
sec.x = left ? Math.max(bestX, sec.x) : Math.min(bestX, sec.x);
return new PtInChain(sec, i - 1, pn.findPPram(sec));
}
}
return null;
}
use of org.twak.utils.geom.LinearForm in project chordatlas by twak.
the class GurobiSkelSolver method buildBadGeom.
private void buildBadGeom(boolean isContraint) throws GRBException {
countBadCorners = countBadEdges = 0;
for (HalfEdge e1 : edges) {
if (e1.over != null && e1.next.over != null)
if (e1.line().absAngle(e1.next.line()) > Math.PI - TweedSettings.settings.badGeomAngle) {
notBoth(e1, e1.next, isContraint, totalEdgeLength * 0.5);
countBadCorners++;
}
}
// if over both h
for (HalfFace f : mesh) {
Set<HalfEdge> togo = new HashSet<>();
for (HalfEdge e : f) togo.add(e);
while (!togo.isEmpty()) {
HalfEdge start = togo.iterator().next();
togo.remove(start);
// if (start.over == null)
// continue;
boolean parallelHasOverProfile = start.over == null ? false : ((SuperEdge) start.over).profLine != null, oppositeHasOverProfile = false;
Line sl = start.line();
LinearForm slf = new LinearForm(sl);
Map<HalfFace, HalfEdge> parallel = new HashMap<>();
parallel.put(start.over == null ? null : start.over.face, start);
Iterator<HalfEdge> tig = togo.iterator();
while (tig.hasNext()) {
HalfEdge e2 = tig.next();
HalfFace f2 = e2.over == null ? null : e2.over.face;
if (sl.absAngle(e2.line()) < 0.05) {
// one from each non-f face
parallel.put(f2, e2);
tig.remove();
parallelHasOverProfile |= (e2.over != null && ((SuperEdge) e2.over).profLine != null);
}
}
Map<HalfFace, HalfEdge> opposite = new HashMap();
tig = togo.iterator();
while (tig.hasNext()) {
HalfEdge e2 = tig.next();
HalfFace f2 = e2.over == null ? null : e2.over.face;
if (// e2l.lengthSquared() > 1 &&
sl.absAngle(e2.line()) > Math.PI - 0.3 && (slf.distance(e2.start) < 0.3 || slf.distance(e2.end) < 0.3)) {
opposite.put(f2, e2);
tig.remove();
oppositeHasOverProfile |= (e2.over != null && ((SuperEdge) e2.over).profLine != null);
}
}
if (parallelHasOverProfile && oppositeHasOverProfile)
continue;
for (HalfEdge e1 : parallel.values()) for (HalfEdge e2 : opposite.values()) {
notBoth(e1, e2, isContraint, totalEdgeLength * 0.5);
countBadEdges++;
}
}
}
print(countBadCorners + " " + countBadEdges);
}
use of org.twak.utils.geom.LinearForm in project chordatlas by twak.
the class Prof method parameterize.
public Prof parameterize() {
// find and subtract vertical lines
Set<Line> lines = new HashSet<>();
for (int i = 1; i < size(); i++) lines.add(new Line(get(i - 1), get(i)));
double avgMinY = get(0).y;
SliceParameters P = new SliceParameters(5);
P.FL_REGRESS = false;
P.FL_BINS = 20;
// simple = 0.4
double A = 0.4;
// simple = 1;
double B = 1;
lines = new FindLines(lines, P) {
protected double nextAngle(Set<Line> remaining, int iteration) {
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);
}
// return MUtils.PI2;
if (iteration < 1 && aBin.getWeight(Mathz.PI2) >= 5)
return Mathz.PI2;
int aBinI = aBin.maxI();
return aBin.val(aBinI);
}
protected double getTolNearLine(Point2d p) {
return P.FL_NEAR_LINE * (p.y < avgMinY + 5 ? 5 : B);
}
protected double getTolNearLine2(Point2d p) {
return P.FL_NEAR_LINE_2 * (p.y < avgMinY + 5 ? 10 : B);
}
}.result.all;
clean = new Prof(this);
clean.clear();
if (lines.isEmpty()) {
clean.add(new Point2d(0, 0));
clean.add(new Point2d(0, 1));
return clean;
}
List<Line> llines = new ArrayList(lines);
llines.stream().filter(l -> l.start.y > l.end.y).forEach(l -> l.reverseLocal());
Collections.sort(llines, new Comparator<Line>() {
public int compare(Line o1, Line o2) {
return Double.compare(o1.start.y + o1.end.y, o2.start.y + o2.end.y);
}
});
for (int i = 0; i < llines.size(); i++) {
Line l = llines.get(i);
double angle = l.aTan2();
if (angle < Mathz.PI2 + 0.1 && angle > Mathz.PI2 - 0.4)
llines.set(i, FindLines.rotateToAngle(l, l.fromPPram(0.5), Mathz.PI2));
}
Line bottomLine = llines.get(0);
llines.add(0, new Line(new Point2d(bottomLine.start.x, get(0).y), new Point2d(bottomLine.start.x, get(0).y)));
double lastY = -Double.MAX_VALUE, lastX = Double.MAX_VALUE;
for (Line l : llines) {
boolean startAbove = l.start.y >= lastY && l.start.x <= lastX, endAbove = l.end.y >= lastY && l.end.x <= lastX;
if (startAbove && endAbove) {
clean.add(l.start);
clean.add(l.end);
} else if (!startAbove && endAbove) {
if (l.start.y < lastY) {
Point2d sec = new LinearForm(new Vector2d(1, 0)).findC(new Point2d(0, lastY)).intersect(new LinearForm(l));
if (sec != null)
l.start = sec;
}
if (l.start.x > lastX) {
Point2d sec = new LinearForm(new Vector2d(0, 1)).findC(new Point2d(lastX, 0)).intersect(new LinearForm(l));
if (sec != null)
l.start = sec;
}
if (l.end.distanceSquared(l.start) < 100)
clean.add(l.start);
clean.add(l.end);
} else {
Vector2d dir = l.dir();
if (Math.abs(dir.x) > Math.abs(dir.y)) {
LinearForm x = new LinearForm(new Vector2d(1, 0)).findC(new Point2d(0, lastY));
l.start = x.project(l.start);
l.end = x.project(l.end);
l.start.x = Math.min(l.start.x, lastX);
l.end.x = Math.min(l.end.x, lastX);
} else {
LinearForm y = new LinearForm(new Vector2d(0, 1)).findC(new Point2d(lastX, 0));
l.start = y.project(l.start);
l.end = y.project(l.end);
l.start.y = Math.max(l.start.y, lastY);
l.end.y = Math.max(l.end.y, lastY);
}
clean.add(l.start);
clean.add(l.end);
}
lastY = l.end.y;
lastX = l.end.x;
}
clean.clean(0.2);
return clean;
}
Aggregations