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