1/*
2 * Copyright 2012 Google Inc.
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#ifndef SkPathOpsPoint_DEFINED
8#define SkPathOpsPoint_DEFINED
9
10#include "SkPathOpsTypes.h"
11#include "SkPoint.h"
12
13inline bool AlmostEqualUlps(const SkPoint& pt1, const SkPoint& pt2) {
14    return AlmostEqualUlps(pt1.fX, pt2.fX) && AlmostEqualUlps(pt1.fY, pt2.fY);
15}
16
17struct SkDVector {
18    double fX;
19    double fY;
20
21    void set(const SkVector& pt) {
22        fX = pt.fX;
23        fY = pt.fY;
24    }
25
26    // only used by testing
27    void operator+=(const SkDVector& v) {
28        fX += v.fX;
29        fY += v.fY;
30    }
31
32    // only called by nearestT, which is currently only used by testing
33    void operator-=(const SkDVector& v) {
34        fX -= v.fX;
35        fY -= v.fY;
36    }
37
38    // only used by testing
39    void operator/=(const double s) {
40        fX /= s;
41        fY /= s;
42    }
43
44    // only used by testing
45    void operator*=(const double s) {
46        fX *= s;
47        fY *= s;
48    }
49
50    SkVector asSkVector() const {
51        SkVector v = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
52        return v;
53    }
54
55    // only used by testing
56    double cross(const SkDVector& a) const {
57        return fX * a.fY - fY * a.fX;
58    }
59
60    // similar to cross, this bastardization considers nearly coincident to be zero
61    // uses ulps epsilon == 16
62    double crossCheck(const SkDVector& a) const {
63        double xy = fX * a.fY;
64        double yx = fY * a.fX;
65        return AlmostEqualUlps(xy, yx) ? 0 : xy - yx;
66    }
67
68    // allow tinier numbers
69    double crossNoNormalCheck(const SkDVector& a) const {
70        double xy = fX * a.fY;
71        double yx = fY * a.fX;
72        return AlmostEqualUlpsNoNormalCheck(xy, yx) ? 0 : xy - yx;
73    }
74
75    double dot(const SkDVector& a) const {
76        return fX * a.fX + fY * a.fY;
77    }
78
79    double length() const {
80        return sqrt(lengthSquared());
81    }
82
83    double lengthSquared() const {
84        return fX * fX + fY * fY;
85    }
86
87    void normalize() {
88        double inverseLength = 1 / this->length();
89        fX *= inverseLength;
90        fY *= inverseLength;
91    }
92};
93
94struct SkDPoint {
95    double fX;
96    double fY;
97
98    void set(const SkPoint& pt) {
99        fX = pt.fX;
100        fY = pt.fY;
101    }
102
103    friend SkDVector operator-(const SkDPoint& a, const SkDPoint& b);
104
105    friend bool operator==(const SkDPoint& a, const SkDPoint& b) {
106        return a.fX == b.fX && a.fY == b.fY;
107    }
108
109    friend bool operator!=(const SkDPoint& a, const SkDPoint& b) {
110        return a.fX != b.fX || a.fY != b.fY;
111    }
112
113    void operator=(const SkPoint& pt) {
114        fX = pt.fX;
115        fY = pt.fY;
116    }
117
118    // only used by testing
119    void operator+=(const SkDVector& v) {
120        fX += v.fX;
121        fY += v.fY;
122    }
123
124    // only used by testing
125    void operator-=(const SkDVector& v) {
126        fX -= v.fX;
127        fY -= v.fY;
128    }
129
130    // only used by testing
131    SkDPoint operator+(const SkDVector& v) {
132        SkDPoint result = *this;
133        result += v;
134        return result;
135    }
136
137    // only used by testing
138    SkDPoint operator-(const SkDVector& v) {
139        SkDPoint result = *this;
140        result -= v;
141        return result;
142    }
143
144    // note: this can not be implemented with
145    // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX);
146    // because that will not take the magnitude of the values into account
147    bool approximatelyDEqual(const SkDPoint& a) const {
148        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
149            return true;
150        }
151        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
152            return false;
153        }
154        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
155        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
156        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
157        largest = SkTMax(largest, -tiniest);
158        return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
159    }
160
161    bool approximatelyDEqual(const SkPoint& a) const {
162        SkDPoint dA;
163        dA.set(a);
164        return approximatelyDEqual(dA);
165    }
166
167    bool approximatelyEqual(const SkDPoint& a) const {
168        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
169            return true;
170        }
171        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
172            return false;
173        }
174        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
175        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
176        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
177        largest = SkTMax(largest, -tiniest);
178        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
179    }
180
181    bool approximatelyEqual(const SkPoint& a) const {
182        SkDPoint dA;
183        dA.set(a);
184        return approximatelyEqual(dA);
185    }
186
187    static bool ApproximatelyEqual(const SkPoint& a, const SkPoint& b) {
188        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
189            return true;
190        }
191        if (!RoughlyEqualUlps(a.fX, b.fX) || !RoughlyEqualUlps(a.fY, b.fY)) {
192            return false;
193        }
194        SkDPoint dA, dB;
195        dA.set(a);
196        dB.set(b);
197        double dist = dA.distance(dB);  // OPTIMIZATION: can we compare against distSq instead ?
198        float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
199        float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
200        largest = SkTMax(largest, -tiniest);
201        return AlmostDequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
202    }
203
204    // only used by testing
205    bool approximatelyZero() const {
206        return approximately_zero(fX) && approximately_zero(fY);
207    }
208
209    SkPoint asSkPoint() const {
210        SkPoint pt = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
211        return pt;
212    }
213
214    double distance(const SkDPoint& a) const {
215        SkDVector temp = *this - a;
216        return temp.length();
217    }
218
219    double distanceSquared(const SkDPoint& a) const {
220        SkDVector temp = *this - a;
221        return temp.lengthSquared();
222    }
223
224    static SkDPoint Mid(const SkDPoint& a, const SkDPoint& b) {
225        SkDPoint result;
226        result.fX = (a.fX + b.fX) / 2;
227        result.fY = (a.fY + b.fY) / 2;
228        return result;
229    }
230
231    bool roughlyEqual(const SkDPoint& a) const {
232        if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
233            return true;
234        }
235        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
236        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
237        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
238        largest = SkTMax(largest, -tiniest);
239        return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
240    }
241
242    static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
243        if (!RoughlyEqualUlps(a.fX, b.fX) && !RoughlyEqualUlps(a.fY, b.fY)) {
244            return false;
245        }
246        SkDPoint dA, dB;
247        dA.set(a);
248        dB.set(b);
249        double dist = dA.distance(dB);  // OPTIMIZATION: can we compare against distSq instead ?
250        float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
251        float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
252        largest = SkTMax(largest, -tiniest);
253        return RoughlyEqualUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
254    }
255
256    // very light weight check, should only be used for inequality check
257    static bool WayRoughlyEqual(const SkPoint& a, const SkPoint& b) {
258        float largestNumber = SkTMax(SkTAbs(a.fX), SkTMax(SkTAbs(a.fY),
259                SkTMax(SkTAbs(b.fX), SkTAbs(b.fY))));
260        SkVector diffs = a - b;
261        float largestDiff = SkTMax(diffs.fX, diffs.fY);
262        return roughly_zero_when_compared_to(largestDiff, largestNumber);
263    }
264
265    // utilities callable by the user from the debugger when the implementation code is linked in
266    void dump() const;
267    static void Dump(const SkPoint& pt);
268    static void DumpHex(const SkPoint& pt);
269};
270
271#endif
272