use of org.twak.tweed.gen.FeatureCache.MFPoint 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.tweed.gen.FeatureCache.MFPoint in project chordatlas by twak.
the class SolverState method miniPainter.
public ICanPaint miniPainter() {
return new ICanPaint() {
@Override
public void paint(Graphics2D g, PanMouseAdaptor ma) {
// for ( MegaFeatures f : SS.minis.keySet() ) {
//
// for ( MFPoint mfp : SS.minis.get( f ) ) {
// Line l = mfp.image.mega.megafacade;
//
// spreadImages( g, ma, l, mfp, Listz.from( mfp.left, mfp.right ) );
//
// }
// }
// if (SS.minis == null)
int brake = 100;
for (HalfFace f : mesh) {
for (HalfEdge e : f) {
if (e.over != null)
continue;
if (brake-- < 0)
break;
SuperEdge se = (SuperEdge) e;
if (se.mini == null)
continue;
List<MiniFacade> mfs = new ArrayList(se.mini);
// while (mfs .size() < 2)
// mfs.add(null);
spreadImages(g, ma, se.line(), se.line().fromPPram(0.5), mfs);
}
}
int i = 0;
if (minis != null)
for (MegaFeatures f : minis.keySet()) {
// PaintThing.paint (f.megafacade, g, ma);
DumbCluster1D<MFPoint> res = GurobiSkelSolver.clusterMinis(f, minis);
Vector2d dir = f.megafacade.dir();
Vector2d out = new Vector2d(dir);
out.set(-out.y, out.x);
out.scale(ma.toZoom(2) / out.length());
for (Cluster<MFPoint> c : res) {
g.setColor(Rainbow.getColour(i++));
for (MFPoint mfp : c.things) {
Point2d pt = new Point2d(mfp);
pt.add(out);
g.setStroke(new BasicStroke(0.2f));
PaintThing.paint(pt, g, ma);
g.setStroke(new BasicStroke(0.2f));
for (HalfEdge e : GurobiSkelSolver.findNear(f.megafacade, mfp, mesh)) g.drawLine(ma.toX(pt.x), ma.toY(pt.y), ma.toX(e.end.x), ma.toY(e.end.y));
if (mfp.selectedEdge != null) {
g.setStroke(new BasicStroke(2f));
g.drawLine(ma.toX(pt.x), ma.toY(pt.y), ma.toX(mfp.selectedEdge.end.x), ma.toY(mfp.selectedEdge.end.y));
}
}
}
}
g.setStroke(new BasicStroke(1));
}
private void spreadImages(Graphics2D g, PanMouseAdaptor ma, Line sel, Point2d cen, List<MiniFacade> mfs) {
Vector2d perp = sel.dir();
perp.set(-perp.y, perp.x);
perp.normalize();
for (int i = 0; i < mfs.size(); i++) {
MiniFacade mf = mfs.get(i);
Vector2d p2 = new Vector2d(perp);
p2.scale((i + 1) * 10);
p2.add(cen);
if (mf == null) {
g.setColor(Color.black);
g.fillRect(ma.toX(p2.x - 1), ma.toY(p2.y - 3), ma.toZoom(2), ma.toZoom(6));
continue;
}
double w = mf.width * 0.1;
double h = mf.height * 0.1;
mf.paintImage(g, ma, p2.x - w, p2.y - h, p2.x + w, p2.y + h);
}
}
};
}
use of org.twak.tweed.gen.FeatureCache.MFPoint in project chordatlas by twak.
the class GurobiSkelSolver method solve.
public void solve() {
try {
for (HalfFace f : mesh.faces) for (HalfEdge e : f.edges()) {
edges.add(e);
totalEdgeLength += e.length();
}
print("total edge length " + totalEdgeLength);
progress.setProgress((int) (Math.random() * 90));
buildProblem();
model.getEnv().set(GRB.DoubleParam.TimeLimit, GRB.INFINITY);
long time = System.currentTimeMillis();
new Thread(() -> {
while (!gurobiDone) {
try {
Thread.sleep(300);
if ((System.currentTimeMillis() - time) / 1000 > maxTimeSecs) {
print("time's up");
model.terminate();
progress.close();
return;
}
} catch (InterruptedException e1) {
}
progress.setProgress((int) (Math.random() * 90));
if (progress.isCanceled()) {
print("user cancelled");
model.terminate();
}
}
progress.setProgress(100);
}).start();
try {
String file = Tweed.SCRATCH + "problem_" + System.currentTimeMillis() + ".mps";
new File(file).getParentFile().mkdirs();
model.write(file);
model.optimize();
} finally {
gurobiDone = true;
progress.setProgress(100);
}
print("time " + (System.currentTimeMillis() - time) / 1000. + " seconds");
if (model.get(GRB.IntAttr.Status) == GRB.Status.INFEASIBLE) {
print("Can't solve; won't solve");
return;
}
for (HalfFace f : mesh.faces) ((SuperFace) f).classification = index(faceInfo.get(f).color);
if (minis != null)
for (MegaFeatures mf : minis.keySet()) {
if (mega2clusters.containsKey(mf))
for (MiniPtCluster pt : mega2clusters.get(mf)) {
for (Map.Entry<MFPoint, MiniSelectEdge> selectPt : pt.entrySet()) {
for (Map.Entry<HalfEdge, GRBVar> selectEdge : selectPt.getValue().edge.entrySet()) {
if (selectEdge.getValue().get(GRB.DoubleAttr.X) > 0.5) {
HalfEdge e = selectEdge.getKey();
selectPt.getKey().selectedEdge = e;
selectPt.getKey().sameCluster = pt.keySet();
if (e.over != null && e.next.over == null) {
for (MFPoint p : pt.keySet()) ((SuperEdge) e.next).addMini(p.right);
for (MFPoint p : pt.keySet()) ((SuperEdge) e.over.findBefore()).addMini(p.left);
}
if (e.over == null && e.next.over == null) {
Line parallel = selectPt.getKey().mega.megafacade;
if (parallel.absAngle(e.line()) < parallel.absAngle(e.next.line()))
for (MFPoint p : pt.keySet()) ((SuperEdge) e).addMini(p.left);
else
for (MFPoint p : pt.keySet()) ((SuperEdge) e.next).addMini(p.right);
}
}
}
}
}
}
if (false)
for (HalfEdge he : edges) {
if (he.over != null && he.next.over == null) {
// face comes to boundary
boolean viz = false;
EdgeVars ei = edgeInfo.get(he);
if (ei.edgeNoMini != null) {
viz = true;
}
if (viz)
PaintThing.debug.put("dd", new Point2d(he.end));
}
}
if (globalProfs != null)
for (HalfEdge e : edges) {
EdgeVars ei = edgeInfo.get(e);
GRBVar plot = ei.debug;
if (plot != null) {
// && plot.get(GRB.DoubleAttr.X) > 0.5 ) {
Point2d o = new Point2d(e.end);
PaintThing.debug.put(1, o);
}
int p = index(ei.profile);
((SuperEdge) e).profI = p;
((SuperEdge) e).prof = p < 0 ? null : globalProfs.get(p);
}
dumpModelStats(edges);
model.getEnv().dispose();
model.dispose();
} catch (GRBException e) {
print("Error code: " + e.getErrorCode() + ". " + e.getMessage());
e.printStackTrace();
}
}
use of org.twak.tweed.gen.FeatureCache.MFPoint in project chordatlas by twak.
the class SkelFootprint method buildFootprint.
public SolverState buildFootprint(List<Line> footprint, ProgressMonitor m, FeatureCache features, BlockGen blockGen) {
MultiMap<MegaFeatures, MFPoint> minis = features == null ? null : features.createMinis(blockGen);
Map<SuperEdge, double[]> profFit = new HashMap();
HalfMesh2 mesh = boundMesh(footprint);
globalProfs = null;
Collections.sort(footprint, megaAreaComparator);
for (Line l : footprint) {
MegaFacade mf = ((SuperLine) l).getMega();
if (mf.area < megaFacadeAreaThreshold)
break;
insert(mesh, l, 2, true, true);
if (m.isCanceled())
return null;
}
if (features != null)
fractureOnFeatures(minis, footprint, mesh);
m.setProgress(2);
if (!TweedSettings.settings.useGreedyProfiles) {
globalProfs = new ArrayList();
findProfiles(footprint, globalProfs);
calcProfFit(mesh, globalProfs, profFit, m);
}
if (FALSE && profMergeTol > 0)
mergeOnProfiles(mesh, footprint);
if (exitA)
return new SolverState(mesh, minis, globalProfs, profFit, footprint);
System.out.println("sampling...");
for (HalfFace f : mesh) meanModeHeightColor(Loopz.from(f), (SuperFace) f, blockGen);
pushHeightsToSmallFaces(mesh);
for (HalfFace f : new ArrayList<>(mesh.faces)) {
SuperFace sf = (SuperFace) f;
if (sf.height < heightCutoff)
sf.remove(mesh);
}
removeExposedFaces(mesh);
return new SolverState(mesh, minis, globalProfs, profFit, footprint);
}
use of org.twak.tweed.gen.FeatureCache.MFPoint in project chordatlas by twak.
the class SkelFootprint method fractureOnFeatures.
private void fractureOnFeatures(MultiMap<MegaFeatures, MFPoint> minis, List<Line> footprint, HalfMesh2 mesh) {
for (MegaFeatures mf : minis.keySet()) pt: for (MFPoint pt : minis.get(mf)) {
if (!Mathz.inRange(mf.megafacade.findPPram(pt), 0, 1))
continue;
Vector2d dir = pt.mega.megafacade.dir();
dir.set(dir.y, -dir.x);
Point2d probe = new Point2d(dir);
probe.scale(2 / dir.length());
probe.add(pt);
for (// don't fracture near minifacade boundaries...we can't distinguish nice block bondaries
Point2d avoid : // don't fracture near minifacade boundaries...we can't distinguish nice block bondaries
pt.mega.megafacade.points()) if (avoid.distanceSquared(pt) < 4)
continue pt;
double bestDist = Double.MAX_VALUE;
for (HalfFace f : mesh.faces) for (HalfEdge e : f) if (e.line().dir().angle(dir) < 0.4) {
double dist = e.line().distance(probe);
if (dist < bestDist)
bestDist = dist;
}
if (bestDist > 0.3) {
Vector2d end = new Vector2d(dir);
end.scale(3 / end.length());
end.add(probe);
Vector2d start = new Vector2d(dir);
start.scale(0.5 / start.length());
start.add(pt);
Line extra = new Line(new Point2d(start), new Point2d(end));
SkelFootprint.insert(mesh, extra, 2, false, false);
}
}
}
Aggregations