use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SkelFootprint method mergeSameClassification.
public static void mergeSameClassification(HalfMesh2 mesh) {
// goal is to encode a polygon with holes as a single perimeter loop that backtracks along itself....
Set<HalfEdge> edges = new LinkedHashSet();
for (HalfFace hf : mesh.faces) for (HalfEdge e2 : hf.edges()) if (e2.over != null && ((SuperFace) e2.face).classification == ((SuperFace) e2.over.face).classification)
edges.add(e2);
edges: while (!edges.isEmpty()) {
HalfEdge e = edges.iterator().next();
if (e.over == null) {
// debug condition
edges.remove(e);
continue;
}
HalfFace a = e.face, b = e.over.face;
if (a == b) {
edges.remove(e);
continue;
}
{
HalfEdge start = null;
while (// set a's edge to be one that we won't fuck with
a.e.over != null && a.e.over.face == b) {
if (a.e == start) {
for (HalfEdge f : a.edges()) {
edges.remove(f);
edges.remove(f.over);
if (f.next.over.next.over != f) {
HalfEdge beforeBreak = f.over.findBefore();
beforeBreak.next = f.next.over.next;
b.e = beforeBreak;
}
}
((SuperFace) b).mergeFrom((SuperFace) a);
mesh.faces.remove(a);
continue edges;
}
if (start == null)
start = a.e;
a.e = a.e.next;
}
}
for (HalfEdge oeb : a.edges()) {
HalfEdge oe = oeb.next;
edges.remove(oe);
if ((oeb.over == null || oeb.over.face != b) && oe.over != null && oe.over.face == b) {
edges.remove(oe.over);
HalfEdge le = oe;
while (le.next.over != null && le.next.over.face == b && le.next.over.next == le.over) {
le = le.next;
edges.remove(le);
edges.remove(le.over);
}
le.over.findBefore().next = le.next;
oeb.next = oe.over.next;
((SuperFace) a).mergeFrom((SuperFace) b);
mesh.faces.remove(b);
for (HalfEdge e2 : a.edges()) {
if (e2.over != null && e2.over.face == b) {
edges.remove(e2);
edges.remove(e2.over);
}
e2.face = a;
}
// only remove the first boundary between a and b
break;
}
}
}
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SkelFootprint method mergeSmallFaces.
private static void mergeSmallFaces(SolverState SS) {
Set<HalfFace> togo = new LinkedHashSet(SS.mesh.faces);
while (!togo.isEmpty()) {
HalfFace f = togo.iterator().next();
togo.remove(f);
if (-f.area() < 5) {
double bestLen = 0;
HalfEdge longest = null;
for (HalfEdge e : f) {
double len = e.length();
if (e.over != null && e.over.face != f && len > bestLen) {
bestLen = len;
longest = e;
}
}
if (longest != null) {
togo.remove(f);
longest.dissolve(SS.mesh);
togo.add(longest.face);
}
}
}
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SkelFootprint method assignGreedyProfiles.
private static void assignGreedyProfiles(SolverState SS) {
Prof defaultProf = defaultProf(null);
for (HalfFace f : SS.mesh) for (List<HalfEdge> le : f.parallelFaces(0.1)) {
List<Prof> profs = new ArrayList();
for (HalfEdge he : le) {
SuperLine sl = ((SuperEdge) he).profLine;
if (sl != null) {
MegaFacade mf = sl.getMega();
if (mf != null)
profs.addAll(mf.getTween(he.start, he.end, 0));
}
}
Prof p = null;
if (!profs.isEmpty())
p = Prof.parameterize(profs);
else
p = defaultProf;
for (HalfEdge he : le) ((SuperEdge) he).prof = p;
}
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SkelFootprint method cleanFootprints.
private static void cleanFootprints(HalfMesh2 mesh) {
for (HalfFace hf : mesh.faces) for (HalfEdge e : hf.edges()) if (e.over != null && e.over.face != e.face)
e.over = null;
Map<HalfEdge, Double> mergePoint = new HashMap();
Predicate<HalfEdge> badEdges = new Predicate<HalfMesh2.HalfEdge>() {
@Override
public boolean test(HalfEdge t) {
if (// is edge within a single face
t.over != null)
// preserve as hole-marker
return false;
double len = t.length();
if (t.length() < 0.2) {
mergePoint.put(t, 0.5);
return true;
}
double angleNext = t.line().absAngle(t.next.line());
final double tol = 0.1;
if (t.next.over == null && len < t.next.length() && angleNext > Math.PI - tol) {
mergePoint.put(t, 0.);
return true;
}
if (t.next.over == null && angleNext < tol) {
mergePoint.put(t, 0.);
return true;
}
HalfEdge prev = t.findBefore();
double anglePrev = t.line().absAngle(prev.line());
if (prev.over == null && len <= prev.length() && anglePrev > Math.PI - tol) {
mergePoint.put(t, 1.);
return true;
}
if (prev.over == null && anglePrev < tol) {
mergePoint.put(t, 1.);
return true;
}
return false;
}
};
f: for (HalfFace f : new ArrayList<>(mesh.faces)) {
Set<HalfEdge> togo = Streamz.stream(f.edges()).filter(badEdges).collect(Collectors.toSet());
while (!togo.isEmpty()) {
HalfEdge g = togo.iterator().next(), p = g.findBefore(), n = g.next;
togo.remove(g);
togo.remove(p);
togo.remove(n);
if (g.replaceByPoint(mesh, g.line().fromPPram(mergePoint.get(g))))
continue f;
HalfEdge pp = p.findBefore();
Streamz.stream(pp, p, n, n.next).forEach(o -> togo.remove(o));
Streamz.stream(pp, p, n, n.next).filter(badEdges).forEach(e -> togo.add(e));
}
}
for (HalfFace f : mesh.faces) {
Set<Point2d> seen = new HashSet<>();
for (HalfEdge e : f) {
if (seen.contains(e.end) && e.over == null && e.next.over == null) {
HalfEdge n = e.next;
Point2d edited;
Vector2d b4 = e.line().dir(), af = n.line().dir();
b4.normalize();
af.normalize();
b4.set(b4.y, -b4.x);
af.set(af.y, -af.x);
b4.add(af);
b4.scale(1 / b4.length());
edited = new Point2d(b4);
edited.add(e.end);
n.start = edited;
e.end = new Point2d(edited);
}
seen.add(e.end);
}
}
}
use of org.twak.utils.geom.HalfMesh2.HalfEdge in project chordatlas by twak.
the class SkelFootprint method mergeOnProfiles.
private void mergeOnProfiles(HalfMesh2 mesh, List<Line> footprint) {
System.out.println("merging over profiles...");
TreeSet<HalfFace> togo = new TreeSet<>((HalfFace o1, HalfFace o2) -> Double.compare(o1.area(), o2.area()));
togo.addAll(mesh.faces);
int count = 0;
while (!togo.isEmpty()) {
HalfFace f = togo.pollFirst();
Cache<HalfEdge, MutableDouble> crossedBy = new Cach<>(e -> new MutableDouble(0));
for (HalfEdge e : f) {
SuperEdge se = (SuperEdge) e;
if (se.profLine != null) {
MegaFacade mf = ((SuperLine) se.profLine).mega;
if (mf != null)
for (Prof p : mf.getTween(se.start, se.end, 0)) {
Line proj = new Line(Pointz.to2(p.to3d(p.get(0))), Pointz.to2(p.to3d(p.get(p.size() - 1))));
for (HalfEdge e2 : f) {
SuperEdge se2 = (SuperEdge) e2;
if (se2.profLine == null && (se2.over == null || ((SuperEdge) se2.over).profLine == null) && e2.over != null && e2.line().intersects(proj) != null && Mathz.inRange(e2.line().absAngle(proj), 0.25 * Math.PI, 0.75 * Math.PI)) {
crossedBy.get(e2).d += TweedSettings.settings.profileHSampleDist;
}
}
}
}
}
count += crossedBy.cache.size();
Optional<Map.Entry<HalfEdge, MutableDouble>> longestO = crossedBy.cache.entrySet().stream().filter(//
e1 -> ((SuperEdge) e1.getKey()).profLine == null && e1.getValue().d > 0).max((e1, e2) -> Double.compare(e1.getValue().d, e2.getValue().d));
if (longestO.isPresent()) {
Map.Entry<HalfEdge, MutableDouble> longest = longestO.get();
if (longest.getValue().d > 0.6 * longest.getKey().length()) {
HalfFace tgf = longest.getKey().over.face;
togo.remove(tgf);
longest.getKey().face.merge(mesh, tgf);
((SuperFace) longest.getKey().face).mergeFrom((SuperFace) tgf);
togo.add(f);
}
}
}
System.out.println("found crossings " + count);
killDoubleEdges(mesh);
}
Aggregations