/** * Determine closest point on hyperbolic arm to a point pt * * @param pt : point * @return t value for closest point; not necessarily within clipped range */ public double closestPointTo(FPoint2 pt) { final boolean db = false; pt = toCurveSpace(pt, null); double U = B + 1; double V = -pt.y; double W = B * pt.x; Polyn p = new Polyn( // B * U * U, // 2 * B * U * V, // B * V * V + A * U * U - W * W, // 2 * A * U * V, // A * V * V // ); if (db) Streams.out.println("closestPointTo, pt=" + pt + "\n" + p); double ret = 0; try { DArray r = new DArray(); if (Math.abs(p.c(0)) < 1e-5) r.addDouble(0); else p.solve(r); if (r.isEmpty()) { throw new FPError("can't find closest point, poly=\n" + p); } double bestDist = 0; for (int i = 0; i < r.size(); i++) { double t = r.getDouble(i); FPoint2 apt = calcPoint(t); double dist = apt.distance(pt); if (i == 0 || dist < bestDist) { bestDist = dist; ret = t; } } } catch (FPError e) { Tools.warn("caught FPError"); // Streams.out.println("caught:\n" + e); ret = (this.minParameter() + this.maxParameter()) * .5; } return ret; }
/** * Calculate dx, dy values for a point * * @param t : parameter * @param delta : where to store dx, dy values */ private void calcTangentAt(double t, FPoint2 delta) { final boolean db = false; if (db) { System.out.println("calcTangentAt " + t); } t = toInt(t); if (db) { System.out.println(" flipped=" + flipped() + " ti=" + t); } double a = toW2.get(0, 0), b = toW2.get(0, 1), c = toW2.get(0, 2); double d = toW2.get(1, 0), e = toW2.get(1, 1), f = toW2.get(1, 2); if (db) { System.out.println(" a=" + a + " b=" + b + " c=" + c + "\n d=" + d + " e=" + e + " f=" + f); } double rt = Polyn.sqrt(A + B * t * t); double dx = a * B * t / rt + b; double dy = d * B * t / rt + e; if (flipped()) { dx = -dx; dy = -dy; } if (db) { System.out.println(" dx=" + dx + "\n dy=" + dy); System.out.println(" ratio=" + (dy / dx)); } delta.setLocation(dx, dy); }
/** * Find intersections of hyperbolic arm with a line. * * @param s0 : first point on line * @param s1 : second point on line * @param ipts : intersection points returned here */ public DArray findLineIntersect(FPoint2 s0, FPoint2 s1, DArray ipts) { if (ipts == null) ipts = new DArray(); ipts.clear(); // transform both line points to curve space FPoint2 c0 = toCurveSpace(s0, null), c1 = toCurveSpace(s1, null); double a = c0.x, b = c0.y, c = c1.x, d = c1.y; double e = d - b; double f = c - a; double D = f * f - B * e * e; double E = 2 * a * f - 2 * b * B * e; double F = a * a - A - B * b * b; Polyn q = new Polyn(D, E, F); DArray roots = new DArray(); q.solve(roots); for (int i = 0; i < roots.size(); i++) { double k = roots.getDouble(i); FPoint2 pt = // ipts.add( new FPoint2(s0.x + (s1.x - s0.x) * k, s0.y + (s1.y - s0.y) * k); // ); // Make sure this point is actually on the arm. FPoint2 cpt = toCurveSpace(pt, null); if (cpt.x < 0) { continue; } ipts.add(pt); } return ipts; }
/** * Calculate a point on the hyperbola * * @param t : parameter in internal space (after flipping has occurred) * @param dest : where to store the calculated point */ private FPoint2 calcPointInternal(double t, FPoint2 dest) { final boolean db = false; if (!valid) throw new FPError("calcPoint of invalid hyperbola"); // Tools.ASSERT(valid, "calcPoint of invalid hyperbola"); // final FPoint2 work = new FPoint2(); // // work.setLocation(Polyn.sqrt(A + t * t * B), t); dest = toW2.apply(Polyn.sqrt(A + t * t * B), t, dest); // // if (dest == null) // dest = new FPoint2(); // Matrix.apply(toW2, work, dest); if (db) { System.out.println( "calcPoint t=" + Tools.f(toExt(t)) // + " armsp=" + work + " world=" + dest); } return dest; }
/** * Calculate a point on the hyperbola, leave in arm space * * @param t : parameter, after conversion to 'internal' value * @return point in arm space */ private FPoint2 calcPointInArmSpace0(double t) { return new FPoint2(Polyn.sqrt(A + t * t * B), t); }
/** * 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)); } }
/** * Constructor * * @param f1 FPoint2 * @param f2 FPoint2 * @param pt FPoint2, or null for bisector */ private void construct(FPoint2 f1, FPoint2 f2, FPoint2 pt) { // userData[LEFT] = new DArray(); // userData[RIGHT] =new DArray(); final boolean db = false; if (db) { System.out.println("Hyperbola constructor\n f1=" + f1 + "\n f2=" + f2 + "\n pt=" + pt); } boolean bisector = (pt == null); initializeVisibleSegments(); // if point on arm is closer to f2 than f1, swap f1 & f2. if (!bisector && FPoint2.distanceSquared(f1, pt) > FPoint2.distanceSquared(f2, pt)) { flipped = true; } this.foci[RIGHT] = new FPoint2(f1); this.foci[LEFT] = new FPoint2(f2); if (!bisector) { this.pt = new FPoint2(pt); } double fociDist = FPoint2.distance(f1, f2); if (fociDist == 0) { throw new FPError("Hyperbola foci are same point"); } c = fociDist * .5; // calculate the translation of the hyperbola away from // standard position. FPoint2 rFocus = getFocus(0), lFocus = getFocus(1); origin = new FPoint2(.5 * (rFocus.x + lFocus.x), .5 * (rFocus.y + lFocus.y)); // calculate the angle of rotation of the hyperbola away // from the standard position. double theta = Math.atan2(rFocus.y - lFocus.y, rFocus.x - lFocus.x); Matrix fromCenterInW = Matrix.getTranslate(origin, true); Matrix rotToE = Matrix.getRotate(-theta); toE2 = rotToE; Matrix.mult(toE2, fromCenterInW, toE2); // calculate inverse toW2 = toE2.invert(null); // Matrix toCenterInW = Matrix.translationMatrix(origin, false); // Matrix rotToW = Matrix.getRotate2D(theta); // // toW2 = toCenterInW; // Matrix.mult(toW2, rotToW, toW2); // Tools.warn("just invert matrix here"); // if (bisector) { valid = true; } else { // get the arm point in hyperbola space. FPoint2 workPt = toE2.apply(pt, null); double xs = workPt.x * workPt.x; double cs = c * c; Polyn q = new Polyn(1, -(cs + xs + workPt.y * workPt.y), cs * xs); if (db) { System.out.println("a2 quadratic:\n" + q); } final DArray qsoln = new DArray(); q.solve(qsoln); if (db) { Streams.out.println(qsoln); } double val = q.c(1) * -.5; int ql = qsoln.size(); if (ql >= 1) { val = qsoln.getDouble(0); } // choose the root that is less than c*c. if (ql == 2) { if (val > qsoln.getDouble(1)) { val = qsoln.getDouble(1); if (db) { System.out.println(" two roots, choosing smaller."); } } } if (db) { System.out.println(" root chosen=" + val); } a = Polyn.sqrt(val); A = a * a; B = A / (c * c - A); } valid = true; if (db) { System.out.println(" ==> " + this); } }