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