• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2008 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 
9 #include "SkMathPriv.h"
10 #include "SkPointPriv.h"
11 
12 #if 0
13 void SkIPoint::rotateCW(SkIPoint* dst) const {
14     SkASSERT(dst);
15 
16     // use a tmp in case this == dst
17     int32_t tmp = fX;
18     dst->fX = -fY;
19     dst->fY = tmp;
20 }
21 
22 void SkIPoint::rotateCCW(SkIPoint* dst) const {
23     SkASSERT(dst);
24 
25     // use a tmp in case this == dst
26     int32_t tmp = fX;
27     dst->fX = fY;
28     dst->fY = -tmp;
29 }
30 #endif
31 
32 ///////////////////////////////////////////////////////////////////////////////
33 
scale(SkScalar scale,SkPoint * dst) const34 void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
35     SkASSERT(dst);
36     dst->set(fX * scale, fY * scale);
37 }
38 
normalize()39 bool SkPoint::normalize() {
40     return this->setLength(fX, fY, SK_Scalar1);
41 }
42 
setNormalize(SkScalar x,SkScalar y)43 bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
44     return this->setLength(x, y, SK_Scalar1);
45 }
46 
setLength(SkScalar length)47 bool SkPoint::setLength(SkScalar length) {
48     return this->setLength(fX, fY, length);
49 }
50 
51 // Returns the square of the Euclidian distance to (dx,dy).
getLengthSquared(float dx,float dy)52 static inline float getLengthSquared(float dx, float dy) {
53     return dx * dx + dy * dy;
54 }
55 
56 // Calculates the square of the Euclidian distance to (dx,dy) and stores it in
57 // *lengthSquared.  Returns true if the distance is judged to be "nearly zero".
58 //
59 // This logic is encapsulated in a helper method to make it explicit that we
60 // always perform this check in the same manner, to avoid inconsistencies
61 // (see http://code.google.com/p/skia/issues/detail?id=560 ).
is_length_nearly_zero(float dx,float dy,float * lengthSquared)62 static inline bool is_length_nearly_zero(float dx, float dy,
63                                          float *lengthSquared) {
64     *lengthSquared = getLengthSquared(dx, dy);
65     return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
66 }
67 
68 /*
69  *  We have to worry about 2 tricky conditions:
70  *  1. underflow of mag2 (compared against nearlyzero^2)
71  *  2. overflow of mag2 (compared w/ isfinite)
72  *
73  *  If we underflow, we return false. If we overflow, we compute again using
74  *  doubles, which is much slower (3x in a desktop test) but will not overflow.
75  */
set_point_length(SkPoint * pt,float x,float y,float length,float * orig_length=nullptr)76 template <bool use_rsqrt> bool set_point_length(SkPoint* pt, float x, float y, float length,
77                                                 float* orig_length = nullptr) {
78     SkASSERT(!use_rsqrt || (orig_length == nullptr));
79 
80     float mag = 0;
81     float mag2;
82     if (is_length_nearly_zero(x, y, &mag2)) {
83         pt->set(0, 0);
84         return false;
85     }
86 
87     if (sk_float_isfinite(mag2)) {
88         float scale;
89         if (use_rsqrt) {
90             scale = length * sk_float_rsqrt(mag2);
91         } else {
92             mag = sk_float_sqrt(mag2);
93             scale = length / mag;
94         }
95         x *= scale;
96         y *= scale;
97     } else {
98         // our mag2 step overflowed to infinity, so use doubles instead.
99         // much slower, but needed when x or y are very large, other wise we
100         // divide by inf. and return (0,0) vector.
101         double xx = x;
102         double yy = y;
103         double dmag = sqrt(xx * xx + yy * yy);
104         double dscale = length / dmag;
105         x *= dscale;
106         y *= dscale;
107         // check if we're not finite, or we're zero-length
108         if (!sk_float_isfinite(x) || !sk_float_isfinite(y) || (x == 0 && y == 0)) {
109             pt->set(0, 0);
110             return false;
111         }
112         if (orig_length) {
113             mag = sk_double_to_float(dmag);
114         }
115     }
116     pt->set(x, y);
117     if (orig_length) {
118         *orig_length = mag;
119     }
120     return true;
121 }
122 
Normalize(SkPoint * pt)123 SkScalar SkPoint::Normalize(SkPoint* pt) {
124     float mag;
125     if (set_point_length<false>(pt, pt->fX, pt->fY, 1.0f, &mag)) {
126         return mag;
127     }
128     return 0;
129 }
130 
Length(SkScalar dx,SkScalar dy)131 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
132     float mag2 = dx * dx + dy * dy;
133     if (SkScalarIsFinite(mag2)) {
134         return sk_float_sqrt(mag2);
135     } else {
136         double xx = dx;
137         double yy = dy;
138         return sk_double_to_float(sqrt(xx * xx + yy * yy));
139     }
140 }
141 
setLength(float x,float y,float length)142 bool SkPoint::setLength(float x, float y, float length) {
143     return set_point_length<false>(this, x, y, length);
144 }
145 
SetLengthFast(SkPoint * pt,float length)146 bool SkPointPriv::SetLengthFast(SkPoint* pt, float length) {
147     return set_point_length<true>(pt, pt->fX, pt->fY, length);
148 }
149 
150 
151 ///////////////////////////////////////////////////////////////////////////////
152 
DistanceToLineBetweenSqd(const SkPoint & pt,const SkPoint & a,const SkPoint & b,Side * side)153 SkScalar SkPointPriv::DistanceToLineBetweenSqd(const SkPoint& pt, const SkPoint& a,
154                                                const SkPoint& b,
155                                                Side* side) {
156 
157     SkVector u = b - a;
158     SkVector v = pt - a;
159 
160     SkScalar uLengthSqd = LengthSqd(u);
161     SkScalar det = u.cross(v);
162     if (side) {
163         SkASSERT(-1 == kLeft_Side &&
164                   0 == kOn_Side &&
165                   1 == kRight_Side);
166         *side = (Side) SkScalarSignAsInt(det);
167     }
168     SkScalar temp = sk_ieee_float_divide(det, uLengthSqd);
169     temp *= det;
170     // It's possible we have a degenerate line vector, or we're so far away it looks degenerate
171     // In this case, return squared distance to point A.
172     if (!SkScalarIsFinite(temp)) {
173         return LengthSqd(v);
174     }
175     return temp;
176 }
177 
DistanceToLineSegmentBetweenSqd(const SkPoint & pt,const SkPoint & a,const SkPoint & b)178 SkScalar SkPointPriv::DistanceToLineSegmentBetweenSqd(const SkPoint& pt, const SkPoint& a,
179                                                       const SkPoint& b) {
180     // See comments to distanceToLineBetweenSqd. If the projection of c onto
181     // u is between a and b then this returns the same result as that
182     // function. Otherwise, it returns the distance to the closer of a and
183     // b. Let the projection of v onto u be v'.  There are three cases:
184     //    1. v' points opposite to u. c is not between a and b and is closer
185     //       to a than b.
186     //    2. v' points along u and has magnitude less than y. c is between
187     //       a and b and the distance to the segment is the same as distance
188     //       to the line ab.
189     //    3. v' points along u and has greater magnitude than u. c is not
190     //       not between a and b and is closer to b than a.
191     // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're
192     // in case 1. If (u dot v)/|u| is > |u| we are in case 3. Otherwise
193     // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to
194     // avoid a sqrt to compute |u|.
195 
196     SkVector u = b - a;
197     SkVector v = pt - a;
198 
199     SkScalar uLengthSqd = LengthSqd(u);
200     SkScalar uDotV = SkPoint::DotProduct(u, v);
201 
202     // closest point is point A
203     if (uDotV <= 0) {
204         return LengthSqd(v);
205     // closest point is point B
206     } else if (uDotV > uLengthSqd) {
207         return DistanceToSqd(b, pt);
208     // closest point is inside segment
209     } else {
210         SkScalar det = u.cross(v);
211         SkScalar temp = sk_ieee_float_divide(det, uLengthSqd);
212         temp *= det;
213         // It's possible we have a degenerate segment, or we're so far away it looks degenerate
214         // In this case, return squared distance to point A.
215         if (!SkScalarIsFinite(temp)) {
216             return LengthSqd(v);
217         }
218         return temp;
219     }
220 }
221