use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SuperMeshPainter method paint.
@Override
public void paint(Object oa, Graphics2D g2, PanMouseAdaptor ma) {
HalfMesh2 o = (HalfMesh2) oa;
double scatterRadius = 0.0;
if (o.faces.isEmpty())
return;
double maxHeight = o.faces.stream().mapToDouble(x -> ((SuperFace) x).height).max().getAsDouble();
// (int) ( Math.random() * 100 );
int fc = 0;
for (HalfFace f : o.faces) {
fc++;
Polygon pwt = new Polygon();
try {
for (HalfEdge e : f.edges()) {
pwt.addPoint(ma.toX(e.start.x + Math.random() * scatterRadius), ma.toY(e.start.y + Math.random() * scatterRadius));
// PaintThing.paint(e.start, g2, ma);
}
} catch (Throwable t) {
t.printStackTrace();
}
SuperFace sf = (SuperFace) f;
Color c;
if (false) {
int h = (int) Mathz.clamp(((SuperFace) f).height * 5, 0, 255);
if (((SuperFace) f).height == -Double.MAX_VALUE)
c = Color.green;
else if (((SuperFace) f).height < 0)
c = Color.red;
else
c = new Color(h, h, h);
} else if (sf.height == -Double.MAX_VALUE) {
c = Color.yellow;
} else if (sf.classification == -1 || sf.height < 0) {
c = Color.red;
} else {
// c = Color.white;
c = Rainbow.getColour(sf.classification + 1);
// c = Rainbow.getColour( fc++ + 1 );
}
// c = Color.white;
// new Color( c.getRed(), c.getGreen(), c.getBlue(), 50 ) );
g2.setColor(c);
g2.fill(pwt);
// Loop<Point2d> pts = new Loop<>();
// for ( HalfEdge e : f.edges() )
// pts.append(e.end);
//
// if ( ( Loopz.area( pts ) ) < 0.1 ) {
// g2.setColor(Color.red);
// g2.setStroke( new BasicStroke( 4f ) );
// g2.draw( pwt );
// }
}
for (HalfFace f : o.faces) {
g2.setColor(Color.black);
try {
for (HalfEdge e : f) {
SuperEdge se = (SuperEdge) e;
g2.setColor(Color.black);
if (se.proceduralFacade != null) {
g2.setStroke(new BasicStroke(3f));
} else {
g2.setStroke(new BasicStroke(1f));
}
// if ( se.profLine != null || ( se.over != null && ((SuperEdge)se.over).profLine != null ) ) {
// g2.setStroke( new BasicStroke( 2f ) );
// g2.setColor( new Color (255, 0, 0 ) );
// }
// else
// {
// g2.setStroke( new BasicStroke( 1f ) );
// g2.setColor( Color.black );
// }
// g2.setColor( ((SuperEdge)e).profLine == null? Color.green : Color.magenta);
// PaintThing.paint ( e.line(), g2, ma );
// }
}
} catch (Throwable th) {
th.printStackTrace();
}
g2.setStroke(new BasicStroke(3));
if (false)
for (HalfEdge e : f.edges()) {
SuperEdge se = (SuperEdge) e;
if (se.profLine != null) {
// g2.setColor( Color.BLACK );
// new Line (new Point2d (e.start), new Point2d (e.end));
Line l = se.line();
// PaintThing.drawArrow( g2, ma, l, 5 );
g2.drawLine(ma.toX(l.start.x), ma.toY(l.start.y), ma.toX(l.end.x), ma.toY(l.end.y));
}
// if (e.over != null)
// {
// l.moveLeft( ma.fromZoom( 2 ) );
//
// double delta = Math.abs ( ((SuperEdge)e).localHeight - ((SuperEdge)e.over).localHeight );
//
// int h = (int) Math.min(255, delta * 10 ) ;
// g2.setColor ( new Color (0,h,h) );
// g2.setStroke (new BasicStroke( 3f ));
//
// }
// if ( ( (SuperEdge) e ).debug ) {
// if ( e.over == null || ((SuperFace)e.over.face).classification != ((SuperFace)f).classification ) {
// g2.setColor( Color.black );
// g2.setStroke( new BasicStroke( 2 ) );
// PaintThing.paint( e.line(), g2, ma );
// }
}
}
fc = 0;
if (false)
for (HalfFace f : o.faces) {
for (HalfEdge e : f.edges()) {
// Point2d pt = new Point2d(e.start);
// pt.add( off );
// Point2d pt2 = new Point2d(e.end);
// pt2.add( off );
//
// PaintThing.paint( new Line (pt, pt2) , g2, ma );
g2.setColor(new Color(255, 0, 0, 20));
if (e.line().absAngle(e.next.line()) > Math.PI - 0.001) {
g2.setColor(new Color(0, 255, 0, 255));
}
PaintThing.paint(new Line(e.start, e.end), g2, ma);
PaintThing.paint(new Line(e.next.start, e.next.end), g2, ma);
}
// for ( HalfEdge e : f.edges() ) {
// PaintThing.drawArrow( g2, ma, e.line(), 5 );
// }
}
if (false)
for (HalfFace f : o.faces) for (HalfEdge e : f.edges()) {
if (e.face != f || (e.over != null && (e.over.over != e || !o.faces.contains(e.over.face))) || e.face == null || e.start == null || e.end == null || e.next == null) {
g2.setColor(Color.red);
g2.setStroke(new BasicStroke(4));
PaintThing.paint(e.line(), g2, ma);
}
}
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge 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.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class GurobiSkelSolver method findNear.
public static List<HalfEdge> findNear(Line l, Point2d start, HalfMesh2 mesh2) {
double tol = 4;
l = new Line(l);
l.moveLeft(tol);
List<HalfEdge> out = new ArrayList<>();
for (HalfFace f : mesh2) try {
for (HalfEdge e : f) {
if (e.end.distanceSquared(start) < tol * tol && l.isOnLeft(e.start) && l.isOnLeft(e.end) && Mathz.inRangeTol(e.line().absAngle(e.next.line()), Mathz.PI2, 1)) {
double angle = l.absAngle(e.line());
if (// regular T-jn on edge or left corner
(e.next.over == null && Mathz.inRangeTol(angle, Mathz.PI2, 1)) || // right corner
(e.over == null && e.next.over == null && angle < 1))
out.add(e);
}
}
} catch (Throwable th) {
th.printStackTrace();
}
return out;
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge 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.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class GurobiSkelSolver method buildProblem.
public void buildProblem() throws GRBException {
model = new GRBModel(new GRBEnv(Tweed.SCRATCH + System.currentTimeMillis() + ".log"));
target = new GRBQuadExpr();
edgeInfo = new HashMap<>();
faceInfo = new HashMap<>();
buildColouringProblem();
buildIsEdge();
buildBadGeom(false);
if (globalProfs != null)
buildProfiles();
if (minis != null && !minis.isEmpty())
buildMini();
for (HalfEdge he : edges) {
target.addTerm(20 * he.length(), edgeInfo.get(he).edgeNoProfile);
target.addTerm(40 * he.length(), edgeInfo.get(he).profileNoEdge);
}
// target.addTerm( 50 * he.length(), edgeInfo.get(he).isEdge );
double isEdgeHeight = 0, isNotEdgeHeight = 0;
for (HalfEdge e : edges) {
double cost;
if (e.over != null) {
cost = e.length() * Math.abs(((SuperFace) e.face).height - ((SuperFace) e.over.face).height);
isEdgeHeight += cost;
isNotEdgeHeight += cost;
// target.addTerm ( cost, edgeInfo.get(e).isNotEdge );
target.addTerm(-4 * cost, edgeInfo.get(e).isEdge);
}
}
print("is not edge height penalty " + isNotEdgeHeight + " (" + (isNotEdgeHeight / faceInfo.size()) + ")");
print("is edge height penalty " + isEdgeHeight);
model.setObjective(target, GRB.MINIMIZE);
}
Aggregations