SkScalar SkPoint::distanceToLineSegmentBetweenSqd(const SkPoint& a, const SkPoint& b) const { // See comments to distanceToLineBetweenSqd. If the projection of c onto // u is between a and b then this returns the same result as that // function. Otherwise, it returns the distance to the closer of a and // b. Let the projection of v onto u be v'. There are three cases: // 1. v' points opposite to u. c is not between a and b and is closer // to a than b. // 2. v' points along u and has magnitude less than y. c is between // a and b and the distance to the segment is the same as distance // to the line ab. // 3. v' points along u and has greater magnitude than u. c is not // not between a and b and is closer to b than a. // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're // in case 1. If (u dot v)/|u| is > |u| we are in case 3. Otherwise // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to // avoid a sqrt to compute |u|. SkVector u = b - a; SkVector v = *this - a; SkScalar uLengthSqd = u.lengthSqd(); SkScalar uDotV = SkPoint::DotProduct(u, v); if (uDotV <= 0) { return v.lengthSqd(); } else if (uDotV > uLengthSqd) { return b.distanceToSqd(*this); } else { SkScalar det = u.cross(v); return SkScalarMulDiv(det, det, uLengthSqd); } }
// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side' bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1, int side, SkPoint* offset0, SkPoint* offset1) { SkASSERT(side == -1 || side == 1); SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX); if (SkScalarNearlyEqual(d0, d1)) { // if distances are equal, can just outset by the perpendicular perp.setLength(d0*side); *offset0 = p0 + perp; *offset1 = p1 + perp; } else { // Otherwise we need to compute the outer tangent. // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm if (d0 < d1) { side = -side; } SkScalar dD = d0 - d1; // if one circle is inside another, we can't compute an offset if (dD*dD >= p0.distanceToSqd(p1)) { return false; } SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0 - p0.fX*d1) / dD, (p1.fY*d0 - p0.fY*d1) / dD); SkScalar d0sq = d0*d0; SkVector dP = outerTangentIntersect - p0; SkScalar dPlenSq = dP.lengthSqd(); SkScalar discrim = SkScalarSqrt(dPlenSq - d0sq); offset0->fX = p0.fX + (d0sq*dP.fX - side*d0*dP.fY*discrim) / dPlenSq; offset0->fY = p0.fY + (d0sq*dP.fY + side*d0*dP.fX*discrim) / dPlenSq; SkScalar d1sq = d1*d1; dP = outerTangentIntersect - p1; dPlenSq = dP.lengthSqd(); discrim = SkScalarSqrt(dPlenSq - d1sq); offset1->fX = p1.fX + (d1sq*dP.fX - side*d1*dP.fY*discrim) / dPlenSq; offset1->fY = p1.fY + (d1sq*dP.fY + side*d1*dP.fX*discrim) / dPlenSq; } return true; }
SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a, const SkPoint& b, Side* side) const { SkVector u = b - a; SkVector v = *this - a; SkScalar uLengthSqd = u.lengthSqd(); SkScalar det = u.cross(v); if (side) { SkASSERT(-1 == SkPoint::kLeft_Side && 0 == SkPoint::kOn_Side && 1 == kRight_Side); *side = (Side) SkScalarSignAsInt(det); } return SkScalarMulDiv(det, det, uLengthSqd); }