use of org.twak.utils.geom.Line3d in project chordatlas by twak.
the class ProfileGen method processMegaFaces.
private void processMegaFaces() {
addOnJmeThread.clear();
if (faces == null || faces.isEmpty()) {
JOptionPane.showMessageDialog(tweed.frame(), "Failed to cluster facades");
return;
}
Node mfNode = new Node();
// Node cProfileNode = new Node();
Random randy = new Random(2);
dbgProfileLookup.clear();
final boolean DBG = true;
computeProfiles(faces);
int i = 0;
for (MegaFacade mf : faces) {
// if (i == 4)
{
System.out.println("building profiles over megafacade " + i + "/" + faces.size());
ColorRGBA dispCol = new ColorRGBA(color.getRed() * randy.nextFloat() / 255f, color.getGreen() * randy.nextFloat() / 255f, color.getBlue() * randy.nextFloat() / 255f, 1);
if (DBG) {
List<Line3d> dbg = new ArrayList();
for (int d : mf.keySet()) {
for (Line l2 : mf.get(d)) {
Line3d oLine = new Line3d(l2.start.x, getHeight(d), l2.start.y, l2.end.x, getHeight(d), l2.end.y);
dbg.add(oLine);
}
}
mfNode.attachChild(Jme3z.lines(tweed.getAssetManager(), dbg, dispCol, 0.1f, true));
Line l2 = mf.origin.line;
Line3d oLine = new Line3d(l2.start.x, getHeight(mf.origin.height), l2.start.y, l2.end.x, getHeight(mf.origin.height), l2.end.y);
// mfNode.attachChild( Jme3z.lines( tweed.getAssetManager(), Collections.singletonList( oLine ), dispCol, 0.5f, true ) );
}
{
Line l3 = mf.origin.line;
// if ( mf.profiles.values().stream().mapToDouble( p -> p.get( 0 ).y ).min().getAsDouble() > 8 )
// continue;
totalPlanLineLength += l3.length();
Line3d oLine = new Line3d(l3.start.x, getHeight(mf.origin.height), l3.start.y, l3.end.x, getHeight(mf.origin.height), l3.end.y);
List<SuperLine> pLines = Prof.findProfileLines(mf.profiles.values(), oLine);
for (int pi = 0; pi < pLines.size(); pi++) {
SuperLine profileLine = pLines.get(pi);
// if ( distance ( gis, profileLine.start ) > 2 || distance ( gis, profileLine.end ) > 2 )
// continue;
Node profileNode = new Node();
// dispCol = new ColorRGBA( randy.nextFloat(), randy.nextFloat(), randy.nextFloat(), 1 );
MegaFacade pMF = mf.moveTo(profileLine);
if (pi >= 1) {
Line newOrigin = null;
double bestDist = Double.MAX_VALUE;
Point3d plCen = Pointz.to3(profileLine.fromPPram(0.5), getHeight(mf.hExtentMin));
for (int li = mf.hExtentMin; li <= mf.hExtentMax; li++) for (Line l : mf.get(li)) {
double dist = Pointz.to3(l.fromPPram(0.5), getHeight(li)).distance(plCen);
if (dist < bestDist) {
newOrigin = l;
bestDist = dist;
}
}
if (newOrigin != null) {
pMF.setOrigin(new LineAtHeight(pMF.hExtentMin, newOrigin));
pMF.computeProfiles(ProfileGen.this);
}
}
profileLine.setMega(pMF);
footprint.add(profileLine);
dbgProfileLookup.put(i++, pMF);
if (DBG) {
profileNode.attachChild(Jme3z.lines(tweed.getAssetManager(), Collections.singletonList(new Line3d(profileLine.start.x, 0, profileLine.start.y, profileLine.end.x, 0, profileLine.end.y)), dispCol, 0.3f, true));
render(new ArrayList<>(pMF.profiles.values()), tweed, dispCol, profileNode);
// List<Prof> cleans = new ArrayList<>();
// for ( Prof p : pMF.profiles.values() ) {
// p.render( tweed, profileNode, dispCol, 1f );
// cleans.add( new Prof( p ).parameterize() );
// .render( tweed, cProfileNode, dispCol.add( ColorRGBA.Gray ), 1f );
//
// }
// render ( cleans, tweed, cProfileNode );
profileNode.attachChild(Jme3z.lines(tweed.getAssetManager(), Collections.singletonList(oLine), dispCol, 0.3f, true));
profileNode.setUserData(ProfileGen.class.getSimpleName(), i);
addOnJmeThread.add(profileNode);
}
}
}
}
}
if (DBG)
tweed.frame.addGen(new JmeGen("horizontal lines", tweed, mfNode), false);
// tweed.frame.addGen( new JmeGen( "clean profiles", tweed, cProfileNode ), false );
calculateOnJmeThread();
}
use of org.twak.utils.geom.Line3d in project chordatlas by twak.
the class CutHoles method find.
public static Point3d find(Point2d pt, Map<Point2d, Point3d> root, Map<Point2d, Line> created) {
if (root.containsKey(pt))
return root.get(pt);
else {
Line l = created.get(pt);
Point3d a = find(l.start, root, created), b = find(l.end, root, created);
return new Line3d(a, b).fromPPram(l.findPPram(pt));
}
}
use of org.twak.utils.geom.Line3d in project chordatlas by twak.
the class Prof method climb.
private static void climb(List<Line3d> lines, Line3d first, Prof monotonic, double max, boolean isUp) {
Point3d end = isUp ? highestEnd(first) : lowestEnd(first), extrema = end;
monotonic.add(isUp ? monotonic.size() : 0, end);
Map<Line3d, Object> ends = new IdentityHashMap();
BetterThan bt = new BetterThan() {
@Override
public boolean betterThan(double h1, double h2) {
return isUp ? h1 > h2 : h1 < h2;
}
@Override
public boolean betterThanOrEqualTo(double h1, double h2) {
return isUp ? h1 >= h2 : h1 <= h2;
}
};
Line3d next = null;
boolean worseThanExtrema = false;
double BIG_JUMP = TweedSettings.settings.meshHoleJumpSize;
do {
double bestDist = 1;
int startOrEnd = -1;
next = null;
for (Line3d l : lines) {
if ((bt.betterThan(l.end.y, extrema.y) || bt.betterThan(l.start.y, extrema.y)) && !ends.containsKey(l)) {
for (int i = 0; i < 2; i++) {
double dist = l.points()[i].distance(worseThanExtrema ? extrema : end);
if (dist < bestDist) {
bestDist = dist;
startOrEnd = i;
next = l;
}
}
}
}
if (next == null) {
// nothing strictly montonic, try near...limit search to near extrema (balcony width)
bestDist = BIG_JUMP;
for (Line3d l : lines) {
if (!ends.containsKey(l)) {
for (int i = 0; i < 2; i++) {
double dist = l.points()[i].distance(extrema);
if (dist < bestDist) {
//
bestDist = dist;
startOrEnd = i;
next = l;
}
}
}
}
}
if (next != null) {
ends.put(next, next);
end = next.points()[1 - startOrEnd];
if (bt.betterThanOrEqualTo(end.y, extrema.y)) {
if (worseThanExtrema) {
// add intermediate point
double angle = next.angle(UP);
if (angle < Math.PI / 2 - 0.2 || angle > Math.PI / 2 + 0.2) {
// intersecting with horizontal lines is messy.
Point3d pt = new LinearForm3D(UP, extrema).collide(end, next.dir(), next.length());
if (bt.betterThan(max, pt.y))
monotonic.add(isUp ? monotonic.size() : 0, pt);
}
}
extrema = end;
Point3d toAdd = end;
if (bt.betterThan(end.y, max)) {
// trim to max
toAdd = new LinearForm3D(UP, new Vector3d(0, max, 0)).collide(next.points()[startOrEnd], next.dir(), next.length());
if (// intersection failed
toAdd.getClass() != Point3d.class && bt.betterThan(max, toAdd.y))
toAdd = new Point3d(end.x, max, end.z);
else
toAdd = null;
next = null;
}
if (toAdd != null)
monotonic.add(isUp ? monotonic.size() : 0, toAdd);
worseThanExtrema = false;
} else
worseThanExtrema = true;
}
} while (next != null);
}
use of org.twak.utils.geom.Line3d in project chordatlas by twak.
the class Prof method findProfileLines.
/**
* We find an initial base offset. Then we cluster the start point of all
* (clean) profiles. If any are a good distance from the initial base, we
* add those as their own profile lines.
*
* The original line is offset by the remaiing data.
*/
public static List<SuperLine> findProfileLines(Collection<Prof> profiles, Line3d line) {
List<SuperLine> out = new ArrayList();
// PaintThing.debug.clear();
SuperLine superLine = new SuperLine(line.start.x, line.start.z, line.end.x, line.end.z);
double outLen = superLine.length();
double min = Double.MAX_VALUE, max = -Double.MAX_VALUE;
Cache<Prof, Double> vLength = new Cache<Prof, Double>() {
@Override
public Double create(Prof i) {
return i.verticalLength(0.5);
}
};
double vLen = profiles.stream().mapToDouble(p -> vLength.get(p)).sum();
boolean useVertical = vLen / profiles.size() > 1;
class Wrapper implements Clusterable {
double[] pt;
public Wrapper(Point2d pt) {
this.pt = new double[] { pt.x, pt.y };
}
@Override
public double[] getPoint() {
return pt;
}
}
List<Wrapper> toCluster = new ArrayList();
List<Double> baseLineOffset = new ArrayList();
for (Prof p : profiles) {
if (// vLen / (5*profiles.size()))
useVertical && vLength.get(p) < 1)
continue;
Prof clean = p.parameterize();
Point2d pt = clean.get(0);
Point3d pt3 = clean.to3d(pt);
double ppram = superLine.findPPram(new Point2d(pt3.x, pt3.z));
baseLineOffset.add(pt.x);
toCluster.add(new Wrapper(new Point2d(pt.x, ppram * outLen)));
min = Math.min(min, ppram);
max = Math.max(max, ppram);
}
if (min == max || toCluster.isEmpty())
return out;
if (true) {
baseLineOffset.sort(Double::compareTo);
double modeBaselineOffset = baseLineOffset.get(baseLineOffset.size() / 2);
DBSCANClusterer<Wrapper> cr = new DBSCANClusterer<>(1.5, 0);
List<Cluster<Wrapper>> results = cr.cluster(toCluster);
Iterator<Cluster<Wrapper>> cit = results.iterator();
while (cit.hasNext()) {
Cluster<Wrapper> cw = cit.next();
if (cw.getPoints().size() < 2 / TweedSettings.settings.profileHSampleDist) {
cit.remove();
double cMeanY = cw.getPoints().stream().mapToDouble(x -> x.pt[1]).average().getAsDouble();
double bestDist = Double.MAX_VALUE;
Cluster<Wrapper> bestWrapper = null;
for (Cluster<Wrapper> near : results) {
double meanY = near.getPoints().stream().mapToDouble(x -> x.pt[1]).average().getAsDouble();
double dist = Math.abs(meanY - cMeanY);
if (dist < bestDist) {
bestDist = dist;
bestWrapper = near;
}
}
if (bestWrapper != null)
bestWrapper.getPoints().addAll(cw.getPoints());
}
}
{
baseLineOffset.clear();
int c = 0;
for (Cluster<Wrapper> cw : results) {
double[] minMax = cw.getPoints().stream().map(p -> new double[] { p.pt[1] }).collect(new InAxDoubleArray());
double[] offsetA = cw.getPoints().stream().mapToDouble(p -> p.pt[0]).sorted().toArray();
double offset = offsetA[offsetA.length / 2];
if (offset - modeBaselineOffset < 1) {
for (Wrapper w : cw.getPoints()) baseLineOffset.add(w.pt[0]);
continue;
}
SuperLine sl = new SuperLine(superLine.fromPPram(minMax[0] / outLen), superLine.fromPPram(minMax[1] / outLen));
sl.moveLeft(offset);
out.add(sl);
List<Point2d> pts = cw.getPoints().stream().map(w -> new Point2d(w.pt[0], w.pt[1])).collect(Collectors.toList());
PaintThing.debug(Rainbow.getColour(c++), 1, pts);
}
}
}
Point2d nStart = superLine.fromPPram(min), nEnd = superLine.fromPPram(max);
superLine.start = nStart;
superLine.end = nEnd;
baseLineOffset.sort(Double::compare);
if (!baseLineOffset.isEmpty())
superLine.moveLeft(baseLineOffset.get(baseLineOffset.size() / 2));
out.add(0, superLine);
return out;
}
use of org.twak.utils.geom.Line3d in project chordatlas by twak.
the class Prof method retarget.
public static Prof retarget(Prof p, SuperLine profileLine) {
Line3d l = new Line3d(Pointz.to3(profileLine));
Prof out = buildProfile(new Line3d(Pointz.to3(profileLine)), l.closestPointOn(p.to3d(p.get(0)), false));
for (Point2d p2 : p) out.add(p.to3d(p2));
return out;
}
Aggregations