private static void renderHull(PtEntry pt) { Inf inf = Inf.create(); int count = 30; PtEntry ptStart = pt; do { inf.update(); if (pt.prev(true) != null) { boolean valley = false; valley = (pt.prev(true).source() == pt.source() && pt.prev(true).orig().next(true) != pt.orig()); V.pushColor(MyColor.cBLUE); V.pushStroke(valley ? STRK_RUBBERBAND : STRK_THICK); V.drawLine(pt.prev(true), pt); V.pop(2); } V.pushColor(MyColor.cDARKGREEN); V.mark(pt, MARK_DISC, .6); V.pop(); if (Editor.withLabels(true)) { StringBuilder sb = new StringBuilder(); sb.append(" #" + pt.id()); if (pt.source() != null) sb.append(" <" + pt.source() + ">"); V.pushScale(.6); V.draw(sb.toString(), MyMath.ptOnCircle(pt, MyMath.radians(30), 3), TX_FRAME | TX_BGND); V.pop(); } pt = pt.next(true); if (count-- == 0) V.draw("too many points rendering!", 50, 50, TX_CLAMP); } while (pt != ptStart && count > 0); }
/** * Calculate possible hull of a pair of polygons * * @param a * @param b * @return possible hull */ private static EdPolygon calcPHull(EdPolygon aPoly, EdPolygon bPoly) { final boolean db = true; if (db && T.update()) T.msg( "calc possible hull of two polygons" + T.show(aPoly, MyColor.cBLUE, STRK_THICK, -1) + T.show(bPoly, MyColor.cDARKGREEN, STRK_THICK, -1)); PtEntry a = PtEntry.buildFrom(aPoly); PtEntry b = PtEntry.buildFrom(bPoly); PtEntry aHull = PtEntry.convexHull(a); PtEntry bHull = PtEntry.convexHull(b); inf = Inf.create(); if (db && T.update()) { T.msg( "convex hulls" + T.show(aHull.toPolygon(), MyColor.cBLUE, STRK_THICK, -1) + T.show(bHull.toPolygon(), MyColor.cDARKGREEN, STRK_THICK, -1)); } PtEntry ph = hullOfPolygons(a, b, aHull, bHull); traceHullPt = ph; insertValleys(ph, a.source(), b.source()); expandHull(ph, aHull, bHull, true); expandHull(ph, aHull, bHull, false); return ph.toPolygon(); }
private static void OLDexpandHull( PtEntry convHullEntry, PtEntry aHull, PtEntry bHull, boolean ccw) { boolean db__OLD = C.vb(DB_HULLEXPAND); if (db__OLD && T.update()) T.msg("expandHull" + T.show(convHullEntry) + " ccw=" + ccw); PtEntry[] opp = new PtEntry[2]; opp[0] = aHull; opp[1] = bHull; PtEntry old____hEnt = convHullEntry; boolean advanced = false; do { if (old____hEnt != convHullEntry) advanced = true; inf.update(); if (old____hEnt.source() == null) { if (db__OLD && T.update()) T.msg("expandHull, source unknown, guaranteed not convex" + T.show(old____hEnt)); old____hEnt = old____hEnt.next(ccw); continue; } int w = (old____hEnt.source() == opp[0].source()) ? 1 : 0; PtEntry oppEnt = opp[w]; boolean isTangent = !COper3.right(old____hEnt, oppEnt, oppEnt.next(true), ccw) && !COper3.right(old____hEnt, oppEnt, oppEnt.prev(true), ccw); if (!isTangent) { if (db__OLD && T.update()) T.msg( "expandHull, advance tangent line" + T.show(oppEnt.toPolygon(), MyColor.cDARKGRAY, -1, MARK_X) + T.show(old____hEnt) + tl(old____hEnt, oppEnt)); opp[w] = oppEnt.next(ccw); continue; } if (COper3.left(old____hEnt, oppEnt, old____hEnt.next(ccw), ccw) && COper3.left(old____hEnt, oppEnt, old____hEnt.prev(ccw), ccw)) { DArray dispPts = new DArray(); // delete points until cross tangent line PtEntry next = old____hEnt.next(ccw); while (true) { PtEntry prev = next; dispPts.add(prev); next = prev.delete(ccw); inf.update(); if (COper3.right(old____hEnt, oppEnt, next, ccw)) { FPoint2 cross = MyMath.linesIntersection(old____hEnt, oppEnt, prev, next, null); old____hEnt = old____hEnt.insert(new PtEntry(cross), ccw); if (db__OLD && T.update()) T.msg( "expandHull, clipped to shadow region" + tl(old____hEnt, oppEnt) + T.show(old____hEnt) + T.show(dispPts)); break; } } } else { if (db__OLD && T.update()) T.msg( "expandHull, not dipping into shadow region" + T.show(old____hEnt.next(ccw)) + tl(old____hEnt, oppEnt)); } old____hEnt = old____hEnt.next(ccw); } while (!advanced || old____hEnt != convHullEntry); }
private static void insertValleys( PtEntry hullPt, Object aSrc, Object bSrc) { // pa, MyPolygon pb) { boolean db = C.vb(DB_INSERTVALLEY); PtEntry ent = hullPt; if (db && T.update()) T.msg("insertValleys"); do { PtEntry next = ent.next(true); if (ent.source() == next.source()) { PtEntry orig = ent.orig(); if (orig.next(true) != next.orig()) { PtEntry vPeak0 = ent; PtEntry vPeak1 = next; EdPolygon opp = (EdPolygon) (vPeak0.source() == aSrc ? bSrc : aSrc); FPoint2 kernelPt = opp.getPointMod(C.vi(KERNELVERT)); // construct a chain from the vertices of the valley PtEntry handle = new PtEntry(vPeak0); PtEntry hNext = handle; PtEntry e = vPeak0.orig(); while (e != vPeak1.orig()) { inf.update(); e = e.next(true); hNext = hNext.insert(new PtEntry(e), true); } if (C.vb(SKIPCONTOUR)) { PtEntry h0 = handle.next(true); PtEntry h1 = hNext.prev(true); PtEntry.join(vPeak0, h0); PtEntry.join(h1, vPeak1); if (db && T.update()) T.msg("inserted unmodified valley" + T.show(vPeak0) + T.show(vPeak1)); } else { if (!C.vb(DB_CONTOUR)) T.disable(); PtEntry hull = COper3.buildHullForChain(handle, kernelPt); if (!C.vb(DB_CONTOUR)) T.enable(); // find entries corresponding to start, end of hull PtEntry peak0 = null, peak1 = null; { PtEntry hEnt = hull; while (peak0 == null || peak1 == null) { inf.update(); if (hEnt.orig() == vPeak0.orig()) peak0 = hEnt; if (hEnt.orig() == vPeak1.orig()) peak1 = hEnt; hEnt = hEnt.next(true); } } PtEntry.join(vPeak0, peak0.next(true)); PtEntry.join(peak1.prev(true), vPeak1); if (db && T.update()) T.msg("inserted monotonic valley" + T.show(vPeak0) + T.show(vPeak1)); } } } ent = next; } while (ent != hullPt); }
/** * Apply hull expansion procedure * * @param convHullEntry an entry of the hull (should be on the convex hull, so it is not deleted * or replaced and is still valid for subsequent calls) * @param aHull entry on convex hull of polygon A * @param bHull entry on convex hull of polygon B * @param ccw true to move in ccw direction, else cw */ private static void expandHull(PtEntry convHullEntry, PtEntry aHull, PtEntry bHull, boolean ccw) { if (C.vb(OLDMETHOD)) { OLDexpandHull(convHullEntry, aHull, bHull, ccw); return; } boolean db = C.vb(DB_HULLEXPAND); if (db && T.update()) T.msg("expandHull" + T.show(convHullEntry) + " ccw=" + ccw); // tangent points for A, B PtEntry[] tangentPoints = new PtEntry[2]; tangentPoints[0] = aHull; tangentPoints[1] = bHull; PtEntry hEnt = convHullEntry; do { inf.update(); // calculate tangent ray R PtEntry tangentPt = null; while (true) { int tanIndex = (hEnt.source() == tangentPoints[0].source()) ? 1 : 0; tangentPt = tangentPoints[tanIndex]; if (!COper3.right(hEnt, tangentPt, tangentPt.next(true), ccw) && !COper3.right(hEnt, tangentPt, tangentPt.prev(true), ccw)) break; tangentPt = tangentPt.next(ccw); if (db && T.update()) T.msg( "expandHull, advance tangent line" + T.show(tangentPt.toPolygon(), MyColor.cDARKGRAY, -1, MARK_DISC) + T.show(hEnt) + tl(hEnt, tangentPt)); tangentPoints[tanIndex] = tangentPt; } if (COper3.left(hEnt, tangentPt, hEnt.next(ccw), ccw)) { DArray dispPts = new DArray(); // delete points until cross tangent line PtEntry next = hEnt.next(ccw); while (true) { PtEntry prev = next; dispPts.add(prev); next = prev.delete(ccw); if (COper3.right(hEnt, tangentPt, next, ccw)) { FPoint2 cross = MyMath.linesIntersection(hEnt, tangentPt, prev, next, null); hEnt = hEnt.insert(new PtEntry(cross), ccw); if (db && T.update()) T.msg( "expandHull, clipped to shadow region" + tl(hEnt, tangentPt) + T.show(hEnt) + T.show(dispPts)); break; } } } else { if (db && T.update()) T.msg( "expandHull, not dipping into shadow region" + T.show(hEnt.next(ccw)) + tl(hEnt, tangentPt)); } while (true) { hEnt = hEnt.next(ccw); if (COper3.left(hEnt.prev(ccw), hEnt, hEnt.next(ccw), ccw)) break; if (db && T.update()) T.msg("skipping reflex vertex" + T.show(hEnt)); } } while (hEnt != convHullEntry); }