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); }
/** * Determine convex hull of two polygons, using rotating calipers method * * @param pa first polygon * @param pb second polygon * @return convex hull structure */ private static PtEntry hullOfPolygons(PtEntry pa, PtEntry pb, PtEntry aHull, PtEntry bHull) { boolean db = C.vb(DB_INITIALHULL); if (db && T.update()) T.msg( "construct convex hull of polygons" + T.show(pa, MyColor.cBLUE, STRK_THICK, -1) + T.show(pb, MyColor.cDARKGREEN, STRK_THICK, -1)); PtEntry hullVertex = null; // A hull vertex and index PtEntry av = rightMostVertex(aHull); // B hull vertex and index PtEntry bv = rightMostVertex(bHull); double theta = Math.PI / 2; LineEqn aLine = new LineEqn(av, theta); int bSide = aLine.sideOfLine(bv); boolean bActive = (bSide == 0) ? (bv.y > av.y) : bSide < 0; if (db && T.update()) T.msg("rightmost vertices" + T.show(av) + T.show(bv)); // construct initial vertex of hull hullVertex = new PtEntry(!bActive ? av : bv); // if (db && T.update()) // T.msg("constructed initial hull vertex: " + hullVertex); PtEntry.join(hullVertex, hullVertex); PtEntry firstEnt = hullVertex; while (true) { Inf.update(inf); PtEntry av2 = av.next(true); PtEntry bv2 = bv.next(true); // next vertex is either A advance, B advance, or bridge double anga = MyMath.polarAngle(av, av2); double angb = MyMath.polarAngle(bv, bv2); double angBridge = bActive ? MyMath.polarAngle(bv, av) : MyMath.polarAngle(av, bv); double ta = MyMath.normalizeAnglePositive(anga - theta); double tb = MyMath.normalizeAnglePositive(angb - theta); double tc = MyMath.normalizeAnglePositive(angBridge - theta); // precision problem: if A and B tangent lines are parallel, both can // reach near zero simultaneously final double MAX = Math.PI * 2 - 1e-3; if (ta >= MAX) ta = 0; if (tb >= MAX) tb = 0; if (tc >= MAX) tc = 0; theta += Math.min(ta, Math.min(tb, tc)); if (db && T.update()) T.msg("caliper" + T.show(hullVertex) + tr(hullVertex, theta) + tp(av) + tp(bv)); PtEntry newPoint = null; if (ta <= tb && ta <= tc) { if (db && T.update()) T.msg("A vertex is nearest" + tl(av, av2)); // ai++; av = av2; if (!bActive) newPoint = av; } else if (tb <= ta && tb <= tc) { if (db && T.update()) T.msg("B vertex is nearest" + tl(bv, bv2)); // bi++; bv = bv2; if (bActive) newPoint = bv; } else { if (db && T.update()) T.msg("Bridge vertex is nearest" + tl(bActive ? bv : av, bActive ? av : bv)); bActive ^= true; newPoint = bActive ? bv : av; } if (newPoint != null) { if (PtEntry.samePoint(newPoint, firstEnt)) { break; } // construct new vertex for hull of the two; // remember, use original vertex, not the convex hull hullVertex = hullVertex.insert(new PtEntry(newPoint), true); if (db && T.update()) T.msg("adding new caliper vertex " + T.show(hullVertex)); } } return hullVertex; }