use of org.twak.utils.Cache in project chordatlas by twak.
the class GreebleCGA method cga.
protected double cga(Loop<Point2d> rect, Matrix4d to3d, WallTag wallTag, MeshBuilder normalWall, MeshBuilder ground, Cache<float[], MeshBuilder> mbs, List<DRectangle> occlusions) {
double[] bounds = Loopz.minMax2d(rect);
GreebleGrid gg = new GreebleGrid(tweed, new MMeshBuilderCache());
DRectangle all = new DRectangle(bounds[0], bounds[2], bounds[1] - bounds[0], bounds[3] - bounds[2]);
// for (DRectangle d : occlusions)
// mbs.get( new float[] {1,1,0,1} ).add( d, to3d );
float[] windowColor = new float[] { 0.8f, .8f, 0.8f, 1 }, glassColor = new float[] { 0.3f, 0.3f, 1, 1 }, mouldingColor = new float[] { 0.7f, .7f, 0.7f, 1 };
double groundFloorHeight = 0;
List<DRectangle> floors = all.splitY(r -> splitFloors(r, 3, 2.5, 2));
for (int f = 0; f < floors.size(); f++) {
boolean isGround = f == 0 && wallTag.isGroundFloor;
DRectangle floor = floors.get(f);
MeshBuilder wall = isGround ? ground : normalWall;
List<DRectangle> edges = floor.splitX(r -> split3(r, 1, 1));
if (isGround)
groundFloorHeight = floor.height;
if (edges.size() != 3) {
wall.add(floor, to3d);
} else {
wall.add(edges.get(0), to3d);
wall.add(edges.get(edges.size() - 1), to3d);
DRectangle cen = edges.get(1);
if (cen.height < 1.8)
wall.add(cen, to3d);
else {
if (f == 0 && wallTag.isGroundFloor) {
List<DRectangle> groundPanel = cen.splitX(r -> split1(r, 0.9));
if (groundPanel.get(0).width < 0.7)
wall.add(groundPanel.get(0), to3d);
else if (wallTag.makeDoor) {
List<DRectangle> doorHeight = groundPanel.get(0).splitY(r -> split1(r, 2.2));
if (visible(doorHeight.get(0), occlusions))
greebleGrid.createDoor(doorHeight.get(0), to3d, wall, mbs.get(windowColor), wallTag.doorDepth);
else
wall.add(doorHeight.get(0), to3d);
if (doorHeight.size() > 1)
wall.add(doorHeight.get(1), to3d);
if (groundPanel.size() > 1) {
List<DRectangle> gWindowPanelH = groundPanel.get(1).splitX(r -> split3(r, 0.5, 0.0));
if (gWindowPanelH.size() > 2) {
wall.add(gWindowPanelH.get(0), to3d);
wall.add(gWindowPanelH.get(2), to3d);
List<DRectangle> gWindowPanelV = gWindowPanelH.get(1).splitY(r -> split3(r, 0.5, 0.5));
if (gWindowPanelV.size() > 2) {
wall.add(gWindowPanelV.get(0), to3d);
wall.add(gWindowPanelV.get(2), to3d);
if (visible(gWindowPanelV.get(1), occlusions))
greebleGrid.createWindow(gWindowPanelV.get(1), to3d, wall, mbs.get(windowColor), mbs.get(glassColor), wallTag.windowDepth, -1, -1, -1, 0.6, 0.9);
else
wall.add(gWindowPanelV.get(1), to3d);
} else
for (DRectangle d : gWindowPanelV) wall.add(d, to3d);
} else
for (DRectangle d : gWindowPanelH) wall.add(d, to3d);
}
} else
windowStrip(to3d, wallTag, mbs.get(windowColor), mbs.get(glassColor), wall, cen, false, occlusions, greebleGrid);
} else {
windowStrip(to3d, wallTag, mbs.get(windowColor), mbs.get(glassColor), wall, cen, f > 0 && f < floors.size() - 1 && wallTag.makeBalcony, occlusions, greebleGrid);
if (f == 1 && wallTag.isGroundFloor)
greebleGrid.moulding(to3d, new DRectangle(floor.x, floor.y, floor.width, 0.5), mbs.get(mouldingColor));
}
}
}
}
return groundFloorHeight;
}
use of org.twak.utils.Cache in project chordatlas by twak.
the class GurobiSkelSolver method dumpModelStats.
private void dumpModelStats(List<HalfEdge> edges) throws GRBException {
print("profile count " + (globalProfs == null ? 0 : globalProfs.size()));
print("acute corner constraints " + countBadCorners);
print("parallel edge constraints " + countBadEdges);
print("nearby profile terms " + countNearbyProfiles);
print("total half edges " + edges.size());
print("total faces " + faceInfo.size());
if (profFit.get(edges.get(0)) != null)
print("total profiles " + profFit.get(edges.get(0)).length);
print("total edge length " + totalEdgeLength);
print("\nobjective fn breakdown follows...\n");
Cache<String, Double> penalties = new Cach<String, Double>(x -> 0.);
Cache<String, Integer> count = new Cach<String, Integer>(x -> 0);
for (GRBVar v : model.getVars()) {
penalties.cache.put(v.get(StringAttr.VarName), penalties.get(v.get(StringAttr.VarName)) + v.get(GRB.DoubleAttr.Obj) * v.get(GRB.DoubleAttr.X));
count.cache.put(v.get(StringAttr.VarName), count.get(v.get(StringAttr.VarName)) + 1);
}
List<String> vals = new ArrayList<>(penalties.cache.keySet());
Collections.sort(vals);
double obj = penalties.cache.values().stream().mapToDouble(x -> x).sum();
for (String k : vals) print(k + "(" + count.get(k) + ") :: " + penalties.get(k) + " ");
print("\ntotal obj " + obj + "\n\n");
}
use of org.twak.utils.Cache in project chordatlas by twak.
the class GurobiSkelSolver method buildMini.
private void buildMini() throws GRBException {
Cache<HalfEdge, GRBLinExpr> isMiniExpr = new Cach<HalfMesh2.HalfEdge, GRBLinExpr>(he -> new GRBLinExpr());
if (minis == null)
return;
for (MegaFeatures mf : minis.keySet()) {
List<MiniPtCluster> clusterVars = new ArrayList<>();
mega2clusters.put(mf, clusterVars);
double mLen = mf.megafacade.length();
DumbCluster1D<MFPoint> clusters = clusterMinis(mf, minis);
for (DumbCluster1D.Cluster<MFPoint> d : clusters) {
// for each cluster, we pick a single MFPoint as the boundary
MiniPtCluster miniPtVar = new MiniPtCluster();
Cache<HalfEdge, GRBLinExpr> isEdgeBind = new Cach<HalfMesh2.HalfEdge, GRBLinExpr>(he -> new GRBLinExpr());
GRBLinExpr selectHE = new GRBLinExpr();
boolean one = false;
for (MFPoint pt : d.things) {
MiniSelectEdge miniSelectEdge = new MiniSelectEdge();
miniPtVar.put(pt, miniSelectEdge);
List<HalfEdge> nearCorners = findNear(mf.megafacade, pt, mesh);
try {
for (HalfEdge he : nearCorners) {
GRBVar heVar = model.addVar(0.0, 1.0, 0, GRB.BINARY, MINI_TO_EDGE);
miniSelectEdge.edge.put(he, heVar);
selectHE.addTerm(1, heVar);
double cost = he.end.distance(pt);
if (he.over != null) {
if (pt.right != null)
cost += Math.abs(pt.right.height - ((SuperFace) he.face).height);
if (pt.left != null)
cost += Math.abs(pt.left.height - ((SuperFace) he.over.face).height);
isEdgeBind.get(he).addTerm(1, heVar);
} else
// bonus for being on a corner;
cost -= totalEdgeLength * 0.1;
target.addTerm(cost, heVar);
isMiniExpr.get(he).addTerm(1, heVar);
one = true;
}
} catch (Throwable th) {
th.printStackTrace();
}
}
if (one) {
clusterVars.add(miniPtVar);
model.addConstr(selectHE, GRB.EQUAL, 1, "pick one near " + d.things.iterator().next());
} else
print("warning skipping minifacade loction " + d.things.size());
for (HalfEdge he : isEdgeBind.cache.keySet()) model.addConstr(isEdgeBind.get(he), GRB.EQUAL, edgeInfo.get(he).isEdge, "minifacade boundary must terminate on edge " + he);
miniPtVar.mean = mf.megafacade.fromPPram(d.mean / mLen);
}
}
double penalty = totalEdgeLength * 0.1;
for (HalfEdge he : edges) {
if (he.over != null && he.next.over == null) {
// edge comes to boundary without minifacade --> penalty
OptionalDouble miniDist = minis.keySet().stream().map(mf -> mf.megafacade).mapToDouble(line -> line.distance(he.end, true)).min();
if (!miniDist.isPresent() || miniDist.getAsDouble() > 4)
continue;
EdgeVars ei = edgeInfo.get(he);
ei.edgeNoMini = model.addVar(0.0, 1.0, 0, GRB.BINARY, EDGE_NO_MINI);
if (isMiniExpr.cache.containsKey(he)) {
ei.isMini = model.addVar(0.0, 1.0, 0, GRB.BINARY, IS_MINI);
GRBLinExpr is = isMiniExpr.get(he);
/* ei.isMini might take 0 or positive integer... assume it's below 10 (0.1 = 1/10) */
model.addConstr(ei.isMini, GRB.LESS_EQUAL, is, "is minifacade on edge " + he);
model.addConstr(scale(0.1, is), GRB.LESS_EQUAL, ei.isMini, "is minifacade on edge " + he);
GRBLinExpr expr = new GRBLinExpr();
expr.addTerm(1, ei.isEdge);
expr.addTerm(1, ei.isMini);
model.addConstr(ei.edgeNoMini, GRB.LESS_EQUAL, expr, null);
expr = new GRBLinExpr();
expr.addTerm(1, ei.edgeNoMini);
expr.addTerm(1, ei.isMini);
model.addConstr(expr, GRB.LESS_EQUAL, 1, null);
expr = new GRBLinExpr();
expr.addTerm(1, ei.isEdge);
expr.addTerm(-1, ei.isMini);
model.addConstr(ei.edgeNoMini, GRB.GREATER_EQUAL, expr, null);
} else {
// no mini, but easier debug
model.addConstr(ei.edgeNoMini, GRB.EQUAL, ei.isEdge, null);
}
target.addTerm(penalty, ei.edgeNoMini);
}
}
}
use of org.twak.utils.Cache in project chordatlas by twak.
the class SkelGen method buildCamp.
private static PlanSkeleton buildCamp(SuperFace sf, Double cap) {
Plan plan = new Plan();
LoopL<Bar> loopl = new LoopL();
Loop<Bar> loop = new Loop();
loopl.add(loop);
Cache<Point2d, Point2d> cache = new Cache<Point2d, Point2d>() {
@Override
public Point2d create(Point2d i) {
return new Point2d(i.x, i.y);
}
};
LoopL<HalfEdge> edges = sf.findHoles();
LoopL<Point2d> lpd = new LoopL();
for (Loop<HalfEdge> loopHE : edges) {
Map<Point2d, SuperEdge> ses = new HashMap();
Loop<Point2d> lp = new Loop();
lpd.add(lp);
for (HalfEdge he : loopHE) {
SuperEdge se = (SuperEdge) he;
lp.append(se.start);
ses.put(se.start, se);
}
lp = Loopz.mergeAdjacentEdges2(lp, 0.001);
for (Loopable<Point2d> lpb : lp.loopableIterator()) {
Bar b = new Bar(cache.get(lpb.getNext().get()), cache.get(lpb.get()));
SuperEdge se = ses.get(lpb.get());
Profile profile = null;
if (se.prof == null || se.prof.size() < 2) {
List<Point2d> defpts = new ArrayList<>();
defpts.add(new Point2d(0, 0));
defpts.add(new Point2d(0, -sf.height * 1.2));
profile = new Profile(defpts);
} else {
profile = toProfile(se.prof);
}
tagWalls(profile, ((SuperFace) se.face).roofColor, se, lpb.get(), lpb.getNext().get());
plan.addLoop(profile.points.get(0), plan.root, profile);
b.tags.add(new SETag(se));
loop.prepend(b);
plan.profiles.put(b, profile);
}
}
plan.points = loopl;
if (cap != null) {
// skel.capAt( cap, a -> skel.capArea = a ); simple...but doesn't show in the siteplan ui
Ship s = new FlatRoofShip(cap, plan);
for (Profile prof : plan.profiles.values()) {
for (Loop<Bar> lb : prof.points) {
boolean addedMarker = false;
for (Bar b : lb) {
if (-b.start.y < cap && -b.end.y > cap || (!addedMarker && b == lb.start.getPrev().get())) {
Marker m = new Marker();
m.set(b.toLine().xAtY(-cap), -cap);
m.bar = b;
m.bar.mould.create(m, null);
Instance i = s.newInstance();
i.anchors[0].setProfileGen(m.generator);
addedMarker = true;
}
}
}
}
plan.ships.add(s);
}
PlanSkeleton skel = new PlanSkeleton(plan);
skel.skeleton();
return skel;
}
use of org.twak.utils.Cache 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;
}
Aggregations