SkPoint.cpp revision 74f623d1617e0ccf3eddf37aeecabd0ac72369fd
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 "SkPoint.h"
11
12void SkIPoint::rotateCW(SkIPoint* dst) const {
13    SkASSERT(dst);
14
15    // use a tmp in case this == dst
16    int32_t tmp = fX;
17    dst->fX = -fY;
18    dst->fY = tmp;
19}
20
21void SkIPoint::rotateCCW(SkIPoint* dst) const {
22    SkASSERT(dst);
23
24    // use a tmp in case this == dst
25    int32_t tmp = fX;
26    dst->fX = fY;
27    dst->fY = -tmp;
28}
29
30///////////////////////////////////////////////////////////////////////////////
31
32void SkPoint::rotateCW(SkPoint* dst) const {
33    SkASSERT(dst);
34
35    // use a tmp in case this == dst
36    SkScalar tmp = fX;
37    dst->fX = -fY;
38    dst->fY = tmp;
39}
40
41void SkPoint::rotateCCW(SkPoint* dst) const {
42    SkASSERT(dst);
43
44    // use a tmp in case this == dst
45    SkScalar tmp = fX;
46    dst->fX = fY;
47    dst->fY = -tmp;
48}
49
50void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
51    SkASSERT(dst);
52    dst->set(fX * scale, fY * scale);
53}
54
55bool SkPoint::normalize() {
56    return this->setLength(fX, fY, SK_Scalar1);
57}
58
59bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
60    return this->setLength(x, y, SK_Scalar1);
61}
62
63bool SkPoint::setLength(SkScalar length) {
64    return this->setLength(fX, fY, length);
65}
66
67// Returns the square of the Euclidian distance to (dx,dy).
68static inline float getLengthSquared(float dx, float dy) {
69    return dx * dx + dy * dy;
70}
71
72// Calculates the square of the Euclidian distance to (dx,dy) and stores it in
73// *lengthSquared.  Returns true if the distance is judged to be "nearly zero".
74//
75// This logic is encapsulated in a helper method to make it explicit that we
76// always perform this check in the same manner, to avoid inconsistencies
77// (see http://code.google.com/p/skia/issues/detail?id=560 ).
78static inline bool is_length_nearly_zero(float dx, float dy,
79                                         float *lengthSquared) {
80    *lengthSquared = getLengthSquared(dx, dy);
81    return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
82}
83
84SkScalar SkPoint::Normalize(SkPoint* pt) {
85    float x = pt->fX;
86    float y = pt->fY;
87    float mag2;
88    if (is_length_nearly_zero(x, y, &mag2)) {
89        pt->set(0, 0);
90        return 0;
91    }
92
93    float mag, scale;
94    if (SkScalarIsFinite(mag2)) {
95        mag = sk_float_sqrt(mag2);
96        scale = 1 / mag;
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 magmag = sqrt(xx * xx + yy * yy);
104        mag = (float)magmag;
105        // we perform the divide with the double magmag, to stay exactly the
106        // same as setLength. It would be faster to perform the divide with
107        // mag, but it is possible that mag has overflowed to inf. but still
108        // have a non-zero value for scale (thanks to denormalized numbers).
109        scale = (float)(1 / magmag);
110    }
111    pt->set(x * scale, y * scale);
112    return mag;
113}
114
115SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
116    float mag2 = dx * dx + dy * dy;
117    if (SkScalarIsFinite(mag2)) {
118        return sk_float_sqrt(mag2);
119    } else {
120        double xx = dx;
121        double yy = dy;
122        return sk_double_to_float(sqrt(xx * xx + yy * yy));
123    }
124}
125
126/*
127 *  We have to worry about 2 tricky conditions:
128 *  1. underflow of mag2 (compared against nearlyzero^2)
129 *  2. overflow of mag2 (compared w/ isfinite)
130 *
131 *  If we underflow, we return false. If we overflow, we compute again using
132 *  doubles, which is much slower (3x in a desktop test) but will not overflow.
133 */
134bool SkPoint::setLength(float x, float y, float length) {
135    float mag2;
136    if (is_length_nearly_zero(x, y, &mag2)) {
137        this->set(0, 0);
138        return false;
139    }
140
141    float scale;
142    if (SkScalarIsFinite(mag2)) {
143        scale = length / sk_float_sqrt(mag2);
144    } else {
145        // our mag2 step overflowed to infinity, so use doubles instead.
146        // much slower, but needed when x or y are very large, other wise we
147        // divide by inf. and return (0,0) vector.
148        double xx = x;
149        double yy = y;
150    #ifdef SK_CPU_FLUSH_TO_ZERO
151        // The iOS ARM processor discards small denormalized numbers to go faster.
152        // Casting this to a float would cause the scale to go to zero. Keeping it
153        // as a double for the multiply keeps the scale non-zero.
154        double dscale = length / sqrt(xx * xx + yy * yy);
155        fX = x * dscale;
156        fY = y * dscale;
157        return true;
158    #else
159        scale = (float)(length / sqrt(xx * xx + yy * yy));
160    #endif
161    }
162    fX = x * scale;
163    fY = y * scale;
164    return true;
165}
166
167bool SkPoint::setLengthFast(float length) {
168    return this->setLengthFast(fX, fY, length);
169}
170
171bool SkPoint::setLengthFast(float x, float y, float length) {
172    float mag2;
173    if (is_length_nearly_zero(x, y, &mag2)) {
174        this->set(0, 0);
175        return false;
176    }
177
178    float scale;
179    if (SkScalarIsFinite(mag2)) {
180        scale = length * sk_float_rsqrt(mag2);  // <--- this is the difference
181    } else {
182        // our mag2 step overflowed to infinity, so use doubles instead.
183        // much slower, but needed when x or y are very large, other wise we
184        // divide by inf. and return (0,0) vector.
185        double xx = x;
186        double yy = y;
187        scale = (float)(length / sqrt(xx * xx + yy * yy));
188    }
189    fX = x * scale;
190    fY = y * scale;
191    return true;
192}
193
194
195///////////////////////////////////////////////////////////////////////////////
196
197SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a,
198                                           const SkPoint& b,
199                                           Side* side) const {
200
201    SkVector u = b - a;
202    SkVector v = *this - a;
203
204    SkScalar uLengthSqd = u.lengthSqd();
205    SkScalar det = u.cross(v);
206    if (side) {
207        SkASSERT(-1 == SkPoint::kLeft_Side &&
208                  0 == SkPoint::kOn_Side &&
209                  1 == kRight_Side);
210        *side = (Side) SkScalarSignAsInt(det);
211    }
212    SkScalar temp = det / uLengthSqd;
213    temp *= det;
214    return temp;
215}
216
217SkScalar SkPoint::distanceToLineSegmentBetweenSqd(const SkPoint& a,
218                                                  const SkPoint& b) const {
219    // See comments to distanceToLineBetweenSqd. If the projection of c onto
220    // u is between a and b then this returns the same result as that
221    // function. Otherwise, it returns the distance to the closer of a and
222    // b. Let the projection of v onto u be v'.  There are three cases:
223    //    1. v' points opposite to u. c is not between a and b and is closer
224    //       to a than b.
225    //    2. v' points along u and has magnitude less than y. c is between
226    //       a and b and the distance to the segment is the same as distance
227    //       to the line ab.
228    //    3. v' points along u and has greater magnitude than u. c is not
229    //       not between a and b and is closer to b than a.
230    // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're
231    // in case 1. If (u dot v)/|u| is > |u| we are in case 3. Otherwise
232    // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to
233    // avoid a sqrt to compute |u|.
234
235    SkVector u = b - a;
236    SkVector v = *this - a;
237
238    SkScalar uLengthSqd = u.lengthSqd();
239    SkScalar uDotV = SkPoint::DotProduct(u, v);
240
241    if (uDotV <= 0) {
242        return v.lengthSqd();
243    } else if (uDotV > uLengthSqd) {
244        return b.distanceToSqd(*this);
245    } else {
246        SkScalar det = u.cross(v);
247        SkScalar temp = det / uLengthSqd;
248        temp *= det;
249        return temp;
250    }
251}
252