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    friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);
27
28    void operator+=(const SkDVector& v) {
29        fX += v.fX;
30        fY += v.fY;
31    }
32
33    void operator-=(const SkDVector& v) {
34        fX -= v.fX;
35        fY -= v.fY;
36    }
37
38    void operator/=(const double s) {
39        fX /= s;
40        fY /= s;
41    }
42
43    void operator*=(const double s) {
44        fX *= s;
45        fY *= s;
46    }
47
48    SkVector asSkVector() const {
49        SkVector v = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
50        return v;
51    }
52
53    double cross(const SkDVector& a) const {
54        return fX * a.fY - fY * a.fX;
55    }
56
57    // similar to cross, this bastardization considers nearly coincident to be zero
58    double crossCheck(const SkDVector& a) const {
59        double xy = fX * a.fY;
60        double yx = fY * a.fX;
61        return AlmostEqualUlps(xy, yx) ? 0 : xy - yx;
62    }
63
64    double dot(const SkDVector& a) const {
65        return fX * a.fX + fY * a.fY;
66    }
67
68    double length() const {
69        return sqrt(lengthSquared());
70    }
71
72    double lengthSquared() const {
73        return fX * fX + fY * fY;
74    }
75};
76
77struct SkDPoint {
78    double fX;
79    double fY;
80
81    void set(const SkPoint& pt) {
82        fX = pt.fX;
83        fY = pt.fY;
84    }
85
86    friend SkDVector operator-(const SkDPoint& a, const SkDPoint& b);
87
88    friend bool operator==(const SkDPoint& a, const SkDPoint& b) {
89        return a.fX == b.fX && a.fY == b.fY;
90    }
91
92    friend bool operator!=(const SkDPoint& a, const SkDPoint& b) {
93        return a.fX != b.fX || a.fY != b.fY;
94    }
95
96    void operator=(const SkPoint& pt) {
97        fX = pt.fX;
98        fY = pt.fY;
99    }
100
101    void operator+=(const SkDVector& v) {
102        fX += v.fX;
103        fY += v.fY;
104    }
105
106    void operator-=(const SkDVector& v) {
107        fX -= v.fX;
108        fY -= v.fY;
109    }
110
111    // note: this can not be implemented with
112    // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX);
113    // because that will not take the magnitude of the values into account
114    bool approximatelyEqual(const SkDPoint& a) const {
115        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
116            return true;
117        }
118        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
119            return false;
120        }
121        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
122        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
123        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
124        largest = SkTMax(largest, -tiniest);
125        return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
126    }
127
128    bool approximatelyEqual(const SkPoint& a) const {
129        SkDPoint dA;
130        dA.set(a);
131        return approximatelyEqual(dA);
132    }
133
134    static bool ApproximatelyEqual(const SkPoint& a, const SkPoint& b) {
135        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
136            return true;
137        }
138        if (!RoughlyEqualUlps(a.fX, b.fX) || !RoughlyEqualUlps(a.fY, b.fY)) {
139            return false;
140        }
141        SkDPoint dA, dB;
142        dA.set(a);
143        dB.set(b);
144        double dist = dA.distance(dB);  // OPTIMIZATION: can we compare against distSq instead ?
145        float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
146        float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
147        largest = SkTMax(largest, -tiniest);
148        return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
149    }
150
151    static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
152        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
153            return true;
154        }
155        return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
156    }
157
158    bool approximatelyPEqual(const SkDPoint& a) const {
159        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
160            return true;
161        }
162        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
163            return false;
164        }
165        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
166        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
167        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
168        largest = SkTMax(largest, -tiniest);
169        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
170    }
171
172    bool approximatelyDEqual(const SkDPoint& a) const {
173        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
174            return true;
175        }
176        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
177            return false;
178        }
179        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
180        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
181        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
182        largest = SkTMax(largest, -tiniest);
183        return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
184    }
185
186    bool approximatelyZero() const {
187        return approximately_zero(fX) && approximately_zero(fY);
188    }
189
190    SkPoint asSkPoint() const {
191        SkPoint pt = {SkDoubleToScalar(fX), SkDoubleToScalar(fY)};
192        return pt;
193    }
194
195    double distance(const SkDPoint& a) const {
196        SkDVector temp = *this - a;
197        return temp.length();
198    }
199
200    double distanceSquared(const SkDPoint& a) const {
201        SkDVector temp = *this - a;
202        return temp.lengthSquared();
203    }
204
205    static SkDPoint Mid(const SkDPoint& a, const SkDPoint& b) {
206        SkDPoint result;
207        result.fX = (a.fX + b.fX) / 2;
208        result.fY = (a.fY + b.fY) / 2;
209        return result;
210    }
211
212    bool moreRoughlyEqual(const SkDPoint& a) const {
213        if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
214            return true;
215        }
216        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
217        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
218        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
219        largest = SkTMax(largest, -tiniest);
220        return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
221    }
222
223    bool roughlyEqual(const SkDPoint& a) const {
224        return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
225    }
226
227    // utilities callable by the user from the debugger when the implementation code is linked in
228    void dump() const;
229    static void Dump(const SkPoint& pt);
230};
231
232#endif
233