/** * 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; }
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); }