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);
  }
Beispiel #2
0
  public static void plotDirectedHalfPlane(FPoint2 p0, FPoint2 p1, int markType) {
    double SEP = .4 * V.getScale();
    double ang = MyMath.polarAngle(p0, p1);
    FPoint2 d0 = MyMath.ptOnCircle(p0, ang + Math.PI / 2, SEP);
    FPoint2 d1 = MyMath.ptOnCircle(p1, ang + Math.PI / 2, SEP);

    EdSegment.plotDirectedLine(p0, p1);
    V.pushStroke(STRK_RUBBERBAND);
    V.drawLine(d0, d1);
    V.popStroke();

    if (markType >= 0) {
      V.mark(d0, markType);
      V.mark(d1, markType);
    }
  }
Beispiel #3
0
 public double thetaP() {
   return MyMath.normalizeAnglePositive(theta());
 }
Beispiel #4
0
  private void construct(EdDisc a, EdDisc b) {
    this.discA = a;
    this.discB = b;

    if (EdDisc.partiallyDisjoint(a, b)) {

      // if (!UHullMain.oldBitanMethod())
      {
        final boolean db = false;

        if (a.getRadius() == b.getRadius()) {
          FPoint2 oa = a.getOrigin(), ob = b.getOrigin();

          FPoint2 n = new FPoint2(-(ob.y - oa.y), ob.x - oa.x);
          n.normalize();
          n.x *= a.getRadius();
          n.y *= a.getRadius();
          seg = new DirSeg(FPoint2.add(oa, n, null), FPoint2.add(ob, n, null));
          return;
        }

        boolean swap = a.getRadius() > b.getRadius();

        if (swap) {
          b = (EdDisc) discA;
          a = (EdDisc) discB;
        }

        if (db && T.update())
          T.msg(
              "BiTangent construct, arad="
                  + Tools.f(a.getRadius())
                  + " brad="
                  + Tools.f(b.getRadius())
                  + " swap="
                  + swap
                  + " origin.a="
                  + T.show(a.getOrigin()));

        FPoint2 oa = a.getOrigin();
        FPoint2 ob = b.getOrigin();

        double U = ob.x, V = ob.y;
        double A = oa.x - U, B = oa.y - V;
        double R1 = a.getRadius();
        double R2 = b.getRadius();
        double S = R2 - R1;

        double x1, y1, x2, y2;
        x1 = A;
        y1 = B;

        boolean secondRoot;
        boolean altSlope = Math.abs(B) < Math.abs(A);
        if (!altSlope) {

          double C1 = S * S / B, C2 = -A / B;
          double qA = 1 + C2 * C2, qB = 2 * C1 * C2, qC = C1 * C1 - S * S;
          double root = Math.sqrt(qB * qB - 4 * qA * qC);
          x2 = (-qB - root) / (2 * qA);
          y2 = C1 + C2 * x2;

          secondRoot = MyMath.sideOfLine(x2, y2, A, B, 0, 0) < 0;

          if (swap ^ secondRoot) {
            x2 = (-qB + root) / (2 * qA);
            y2 = C1 + C2 * x2;
          }
        } else {

          double C1 = S * S / A, C2 = -B / A;
          double qA = 1 + C2 * C2, qB = 2 * C1 * C2, qC = C1 * C1 - S * S;
          double root = Math.sqrt(qB * qB - 4 * qA * qC);
          y2 = (-qB - root) / (2 * qA);
          x2 = C1 + C2 * y2;

          secondRoot = MyMath.sideOfLine(x2, y2, A, B, 0, 0) < 0;

          if (swap ^ secondRoot) {
            y2 = (-qB + root) / (2 * qA);
            x2 = C1 + C2 * y2;
          }
        }
        // now grow both discs back to r1, r2

        double tx = U;
        double ty = V;

        //        if (S == 0) {
        //          FPoint2 unit = new FPoint2(-A, -B);
        //          if (swap) {
        //            unit.x = -unit.x;
        //            unit.y = -unit.y;
        //          }
        //          unit.normalize();
        //          tx += -unit.y * R1;
        //          ty += unit.x * R1;
        //        } else
        {
          double F = R1 / S;
          tx += x2 * F;
          ty += y2 * F;
        }

        if (db && T.update())
          T.msg("adding offset to both points: " + tx + ", " + ty + T.show(new FPoint2(tx, ty)));
        x1 += tx;
        y1 += ty;
        x2 += tx;
        y2 += ty;
        FPoint2 p1 = new FPoint2(x1, y1);
        FPoint2 p2 = new FPoint2(x2, y2);
        if (swap) {
          FPoint2 tmp = p1;
          p1 = p2;
          p2 = tmp;
        }

        seg = new DirSeg(p1, p2);

        if (db && T.update())
          T.msg(
              "swap="
                  + swap
                  + " altSlope="
                  + altSlope
                  + " secondRoot="
                  + secondRoot
                  + " dirseg="
                  + EdSegment.showDirected(p1, p2));
      }
      //      else {
      //
      //        double th = calcTheta(a, b);
      //        LineEqn eqn = new LineEqn(a.polarPoint(th + Math.PI / 2), th);
      //        double ta = eqn.parameterFor(a.getOrigin());
      //        double tb = eqn.parameterFor(b.getOrigin());
      //        seg = new DirSeg(eqn.pt(ta), eqn.pt(tb));
      //
      //      }
    }
  }
  /**
   * Find intersections of hyperbolic arm with an axes-aligned line segment. Arm must intersect
   * segment, not its underlying (infinite) line. The intersection points are sorted into increasing
   * parameter values w.r.t. the arm.
   *
   * @param s0 : start of line segment
   * @param s1 : end of line segment
   * @param ipts : intersection points returned here
   * @param reverseOrder : if true, points are sorted by decreasing parameter values
   * @param dbFlag : true to print debug information
   */
  public void findOrthogonalIntersect(
      FPoint2 s0, FPoint2 s1, DArray ipts, boolean reverseOrder, boolean dbFlag) {

    final boolean db = true && dbFlag;

    if (db) {
      System.out.println(
          "findOrthogonalIntersect " + s0 + " -> " + s1 + " rev:" + Tools.f(reverseOrder));
    }

    // Determine whether this is a vertical or horizontal line segment.

    boolean vert = (Math.abs(s1.y - s0.y) > Math.abs(s1.x - s0.x));
    if (db) {
      System.out.println(" vert=" + Tools.f(vert));
    }

    // Find the quadratic to solve.
    Polyn p;
    PlaneCurve cv = getCurve();
    if (vert) {
      p = cv.solveForX(s0.x);
    } else {
      p = cv.solveForY(s0.y);
    }

    DArray lst = new DArray();
    p.solve(lst);

    if (db) {
      System.out.println(" curve=" + cv.toString(true));
      System.out.println(" polyn=" + p.toString(true));
      System.out.println(" roots=" + Tools.d(lst));
    }

    // Sort points, discarding those not on line segment,
    // and those not on the correct arm.
    ipts.clear();

    for (int i = 0; i < lst.size(); i++) {
      double ta = lst.getDouble(i);
      FPoint2 pt;
      if (vert) {
        pt = new FPoint2(s0.x, ta);
      } else {
        pt = new FPoint2(ta, s0.y);
      }

      if (db) {
        System.out.println(" position on arm for ta=" + ta + " is " + pt);
      }
      double t = MyMath.positionOnSegment(pt, s0, s1);
      if (db) {
        System.out.println("  pt=" + pt + " t=" + t);
      }
      if (t < 0 || t > 1) {
        if (db) {
          System.out.println("   not on segment, skipping");
        }
        continue;
      }

      FPoint2 cpt = toCurveSpace(pt, null);
      if (db) {
        System.out.println("  curveSpace=" + cpt);
      }
      if (cpt.x < 0) {
        if (db) {
          System.out.println("   skipping...");
        }
        continue;
      }

      double t1 = calcParameter(pt);
      int j = 0;
      while (true) {
        if (j == ipts.size()) {
          break;
        }
        double t2 = calcParameter(ipts.getFPoint2(j));
        if (!reverseOrder) {
          if (t1 < t2) {
            break;
          }
        } else if (t1 > t2) {
          break;
        }
        j++;
      }
      ipts.add(j, pt);
    }
    if (db) {
      System.out.println(" ipts=" + Tools.d(ipts));
    }
  }
  public void render(Color c, int stroke, int markType) {
    final boolean db = false;
    // Get array of visible segments.  If no such array exists,
    // use default.
    DArray vseg = visSeg;
    boolean dashed = false;

    //    if (step == 0) {
    double step = renderStep();
    //    }
    //    vp V = TestBed.view();

    if (db) Streams.out.println(" step=" + step);

    // plot each visible segment

    for (int seg = 0; seg < vseg.size(); seg += 2) {
      double t0 = vseg.getDouble(seg + 0), t1 = vseg.getDouble(seg + 1);
      t0 = MyMath.clamp(t0, -500.0, 500.0);
      t1 = MyMath.clamp(t1, -500.0, 500.0);

      // render() expects external parameters.

      double s0 = toExt(t0), s1 = toExt(t1);
      if (s0 > s1) {
        double tmp = s0;
        s0 = s1;
        s1 = tmp;
      }
      FPoint2 p0 = calcPoint(s0), p1 = calcPoint(s1);
      if (db) Streams.out.println(" p0=" + p0 + ", p1=" + p1);
      if (isLine() && !dashed) {
        V.drawLine(p0, p1);
      } else {

        /*
                  if (Math.abs(s0) >= 500
           ||Math.abs(s1) >= 500)
         System.out.println("Rendering "+t0+" to "+t1+" step "+step);
        */
        if (dashed) V.pushStroke(Globals.STRK_RUBBERBAND);
        {
          //          int count = 0;
          boolean first = true;
          for (double t = t0; ; t += step) { // , count++) {
            boolean last = (t >= t1);
            if (last) {
              t = t1;
            }
            calcPointInternal(t, p1);

            if (!p1.isValid()) {
              if (last) {
                break;
              }
              continue;
            }

            if (db) {
              System.out.println(" calcPt " + Tools.f(toExt(t)) + " = " + p1.x + "," + p1.y);
            }
            if (!first) {
              V.drawLine(p0, p1);
              if (false) {
                Tools.warn("highlighting int");
                V.mark(p0);
              }
            }
            if (last) {
              break;
            }
            p0.setLocation(p1);
            first = false;
          }
        }
        if (dashed) V.popStroke();
      }
    }
  }
  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);
  }
  /**
   * 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 String tr(FPoint2 rayStart, double theta) {
   return tl(rayStart, MyMath.ptOnCircle(rayStart, theta, 20));
 }
  /**
   * 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);
  }