| 1 | /* |
| 2 | * Copyright 2014 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 SkPathOpsTSect_DEFINED |
| 8 | #define SkPathOpsTSect_DEFINED |
| 9 | |
| 10 | #include "include/private/SkMacros.h" |
| 11 | #include "src/core/SkArenaAlloc.h" |
| 12 | #include "src/pathops/SkIntersections.h" |
| 13 | #include "src/pathops/SkPathOpsBounds.h" |
| 14 | #include "src/pathops/SkPathOpsRect.h" |
| 15 | #include "src/pathops/SkPathOpsTCurve.h" |
| 16 | |
| 17 | #include <utility> |
| 18 | |
| 19 | #ifdef SK_DEBUG |
| 20 | typedef uint8_t SkOpDebugBool; |
| 21 | #else |
| 22 | typedef bool SkOpDebugBool; |
| 23 | #endif |
| 24 | |
| 25 | static inline bool SkDoubleIsNaN(double x) { |
| 26 | return x != x; |
| 27 | } |
| 28 | |
| 29 | class SkTCoincident { |
| 30 | public: |
| 31 | SkTCoincident() { |
| 32 | this->init(); |
| 33 | } |
| 34 | |
| 35 | void debugInit() { |
| 36 | #ifdef SK_DEBUG |
| 37 | this->fPerpPt.fX = this->fPerpPt.fY = SK_ScalarNaN; |
| 38 | this->fPerpT = SK_ScalarNaN; |
| 39 | this->fMatch = 0xFF; |
| 40 | #endif |
| 41 | } |
| 42 | |
| 43 | char dumpIsCoincidentStr() const; |
| 44 | void dump() const; |
| 45 | |
| 46 | bool isMatch() const { |
| 47 | SkASSERT(!!fMatch == fMatch); |
| 48 | return SkToBool(fMatch); |
| 49 | } |
| 50 | |
| 51 | void init() { |
| 52 | fPerpT = -1; |
| 53 | fMatch = false; |
| 54 | fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN; |
| 55 | } |
| 56 | |
| 57 | void markCoincident() { |
| 58 | if (!fMatch) { |
| 59 | fPerpT = -1; |
| 60 | } |
| 61 | fMatch = true; |
| 62 | } |
| 63 | |
| 64 | const SkDPoint& perpPt() const { |
| 65 | return fPerpPt; |
| 66 | } |
| 67 | |
| 68 | double perpT() const { |
| 69 | return fPerpT; |
| 70 | } |
| 71 | |
| 72 | void setPerp(const SkTCurve& c1, double t, const SkDPoint& cPt, const SkTCurve& ); |
| 73 | |
| 74 | private: |
| 75 | SkDPoint fPerpPt; |
| 76 | double fPerpT; // perpendicular intersection on opposite curve |
| 77 | SkOpDebugBool fMatch; |
| 78 | }; |
| 79 | |
| 80 | class SkTSect; |
| 81 | class SkTSpan; |
| 82 | |
| 83 | struct SkTSpanBounded { |
| 84 | SkTSpan* fBounded; |
| 85 | SkTSpanBounded* fNext; |
| 86 | }; |
| 87 | |
| 88 | class SkTSpan { |
| 89 | public: |
| 90 | SkTSpan(const SkTCurve& curve, SkArenaAlloc& heap) { |
| 91 | fPart = curve.make(heap); |
| 92 | } |
| 93 | |
| 94 | void addBounded(SkTSpan* , SkArenaAlloc* ); |
| 95 | double closestBoundedT(const SkDPoint& pt) const; |
| 96 | bool contains(double t) const; |
| 97 | |
| 98 | void debugInit(const SkTCurve& curve, SkArenaAlloc& heap) { |
| 99 | #ifdef SK_DEBUG |
| 100 | SkTCurve* fake = curve.make(heap); |
| 101 | fake->debugInit(); |
| 102 | init(*fake); |
| 103 | initBounds(*fake); |
| 104 | fCoinStart.init(); |
| 105 | fCoinEnd.init(); |
| 106 | #endif |
| 107 | } |
| 108 | |
| 109 | const SkTSect* debugOpp() const; |
| 110 | |
| 111 | #ifdef SK_DEBUG |
| 112 | void debugSetGlobalState(SkOpGlobalState* state) { |
| 113 | fDebugGlobalState = state; |
| 114 | } |
| 115 | |
| 116 | const SkTSpan* debugSpan(int ) const; |
| 117 | const SkTSpan* debugT(double t) const; |
| 118 | bool debugIsBefore(const SkTSpan* span) const; |
| 119 | #endif |
| 120 | void dump() const; |
| 121 | void dumpAll() const; |
| 122 | void dumpBounded(int id) const; |
| 123 | void dumpBounds() const; |
| 124 | void dumpCoin() const; |
| 125 | |
| 126 | double endT() const { |
| 127 | return fEndT; |
| 128 | } |
| 129 | |
| 130 | SkTSpan* findOppSpan(const SkTSpan* opp) const; |
| 131 | |
| 132 | SkTSpan* findOppT(double t) const { |
| 133 | SkTSpan* result = oppT(t); |
| 134 | SkOPASSERT(result); |
| 135 | return result; |
| 136 | } |
| 137 | |
| 138 | SkDEBUGCODE(SkOpGlobalState* globalState() const { return fDebugGlobalState; }) |
| 139 | |
| 140 | bool hasOppT(double t) const { |
| 141 | return SkToBool(oppT(t)); |
| 142 | } |
| 143 | |
| 144 | int hullsIntersect(SkTSpan* span, bool* start, bool* oppStart); |
| 145 | void init(const SkTCurve& ); |
| 146 | bool initBounds(const SkTCurve& ); |
| 147 | |
| 148 | bool isBounded() const { |
| 149 | return fBounded != nullptr; |
| 150 | } |
| 151 | |
| 152 | bool linearsIntersect(SkTSpan* span); |
| 153 | double linearT(const SkDPoint& ) const; |
| 154 | |
| 155 | void markCoincident() { |
| 156 | fCoinStart.markCoincident(); |
| 157 | fCoinEnd.markCoincident(); |
| 158 | } |
| 159 | |
| 160 | const SkTSpan* next() const { |
| 161 | return fNext; |
| 162 | } |
| 163 | |
| 164 | bool onlyEndPointsInCommon(const SkTSpan* opp, bool* start, |
| 165 | bool* oppStart, bool* ptsInCommon); |
| 166 | |
| 167 | const SkTCurve& part() const { |
| 168 | return *fPart; |
| 169 | } |
| 170 | |
| 171 | int pointCount() const { |
| 172 | return fPart->pointCount(); |
| 173 | } |
| 174 | |
| 175 | const SkDPoint& pointFirst() const { |
| 176 | return (*fPart)[0]; |
| 177 | } |
| 178 | |
| 179 | const SkDPoint& pointLast() const { |
| 180 | return (*fPart)[fPart->pointLast()]; |
| 181 | } |
| 182 | |
| 183 | bool removeAllBounded(); |
| 184 | bool removeBounded(const SkTSpan* opp); |
| 185 | |
| 186 | void reset() { |
| 187 | fBounded = nullptr; |
| 188 | } |
| 189 | |
| 190 | void resetBounds(const SkTCurve& curve) { |
| 191 | fIsLinear = fIsLine = false; |
| 192 | initBounds(curve); |
| 193 | } |
| 194 | |
| 195 | bool split(SkTSpan* work, SkArenaAlloc* heap) { |
| 196 | return splitAt(work, (work->fStartT + work->fEndT) * 0.5, heap); |
| 197 | } |
| 198 | |
| 199 | bool splitAt(SkTSpan* work, double t, SkArenaAlloc* heap); |
| 200 | |
| 201 | double startT() const { |
| 202 | return fStartT; |
| 203 | } |
| 204 | |
| 205 | private: |
| 206 | |
| 207 | // implementation is for testing only |
| 208 | int debugID() const { |
| 209 | return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1); |
| 210 | } |
| 211 | |
| 212 | void dumpID() const; |
| 213 | |
| 214 | int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart); |
| 215 | int linearIntersects(const SkTCurve& ) const; |
| 216 | SkTSpan* oppT(double t) const; |
| 217 | |
| 218 | void validate() const; |
| 219 | void validateBounded() const; |
| 220 | void validatePerpT(double oppT) const; |
| 221 | void validatePerpPt(double t, const SkDPoint& ) const; |
| 222 | |
| 223 | SkTCurve* fPart; |
| 224 | SkTCoincident fCoinStart; |
| 225 | SkTCoincident fCoinEnd; |
| 226 | SkTSpanBounded* fBounded; |
| 227 | SkTSpan* fPrev; |
| 228 | SkTSpan* fNext; |
| 229 | SkDRect fBounds; |
| 230 | double fStartT; |
| 231 | double fEndT; |
| 232 | double fBoundsMax; |
| 233 | SkOpDebugBool fCollapsed; |
| 234 | SkOpDebugBool fHasPerp; |
| 235 | SkOpDebugBool fIsLinear; |
| 236 | SkOpDebugBool fIsLine; |
| 237 | SkOpDebugBool fDeleted; |
| 238 | SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState); |
| 239 | SkDEBUGCODE(SkTSect* fDebugSect); |
| 240 | PATH_OPS_DEBUG_T_SECT_CODE(int fID); |
| 241 | friend class SkTSect; |
| 242 | }; |
| 243 | |
| 244 | class SkTSect { |
| 245 | public: |
| 246 | SkTSect(const SkTCurve& c |
| 247 | SkDEBUGPARAMS(SkOpGlobalState* ) PATH_OPS_DEBUG_T_SECT_PARAMS(int id)); |
| 248 | static void BinarySearch(SkTSect* sect1, SkTSect* sect2, |
| 249 | SkIntersections* intersections); |
| 250 | |
| 251 | SkDEBUGCODE(SkOpGlobalState* globalState() { return fDebugGlobalState; }) |
| 252 | bool hasBounded(const SkTSpan* ) const; |
| 253 | |
| 254 | const SkTSect* debugOpp() const { |
| 255 | return SkDEBUGRELEASE(fOppSect, nullptr); |
| 256 | } |
| 257 | |
| 258 | #ifdef SK_DEBUG |
| 259 | const SkTSpan* debugSpan(int id) const; |
| 260 | const SkTSpan* debugT(double t) const; |
| 261 | #endif |
| 262 | void dump() const; |
| 263 | void dumpBoth(SkTSect* ) const; |
| 264 | void dumpBounded(int id) const; |
| 265 | void dumpBounds() const; |
| 266 | void dumpCoin() const; |
| 267 | void dumpCoinCurves() const; |
| 268 | void dumpCurves() const; |
| 269 | |
| 270 | private: |
| 271 | enum { |
| 272 | kZeroS1Set = 1, |
| 273 | kOneS1Set = 2, |
| 274 | kZeroS2Set = 4, |
| 275 | kOneS2Set = 8 |
| 276 | }; |
| 277 | |
| 278 | SkTSpan* addFollowing(SkTSpan* prior); |
| 279 | void addForPerp(SkTSpan* span, double t); |
| 280 | SkTSpan* addOne(); |
| 281 | |
| 282 | SkTSpan* addSplitAt(SkTSpan* span, double t) { |
| 283 | SkTSpan* result = this->addOne(); |
| 284 | SkDEBUGCODE(result->debugSetGlobalState(this->globalState())); |
| 285 | result->splitAt(span, t, &fHeap); |
| 286 | result->initBounds(fCurve); |
| 287 | span->initBounds(fCurve); |
| 288 | return result; |
| 289 | } |
| 290 | |
| 291 | bool binarySearchCoin(SkTSect* , double tStart, double tStep, double* t, |
| 292 | double* oppT, SkTSpan** oppFirst); |
| 293 | SkTSpan* boundsMax(); |
| 294 | bool coincidentCheck(SkTSect* sect2); |
| 295 | void coincidentForce(SkTSect* sect2, double start1s, double start1e); |
| 296 | bool coincidentHasT(double t); |
| 297 | int collapsed() const; |
| 298 | void computePerpendiculars(SkTSect* sect2, SkTSpan* first, |
| 299 | SkTSpan* last); |
| 300 | int countConsecutiveSpans(SkTSpan* first, |
| 301 | SkTSpan** last) const; |
| 302 | |
| 303 | int debugID() const { |
| 304 | return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1); |
| 305 | } |
| 306 | |
| 307 | bool deleteEmptySpans(); |
| 308 | void dumpCommon(const SkTSpan* ) const; |
| 309 | void dumpCommonCurves(const SkTSpan* ) const; |
| 310 | static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, |
| 311 | SkIntersections* ); |
| 312 | bool (SkTSect* sect2, SkTSpan* first, |
| 313 | SkTSpan* last, SkTSpan** result); |
| 314 | SkTSpan* findCoincidentRun(SkTSpan* first, SkTSpan** lastPtr); |
| 315 | int intersects(SkTSpan* span, SkTSect* opp, |
| 316 | SkTSpan* oppSpan, int* oppResult); |
| 317 | bool isParallel(const SkDLine& thisLine, const SkTSect* opp) const; |
| 318 | int linesIntersect(SkTSpan* span, SkTSect* opp, |
| 319 | SkTSpan* oppSpan, SkIntersections* ); |
| 320 | bool markSpanGone(SkTSpan* span); |
| 321 | bool matchedDirection(double t, const SkTSect* sect2, double t2) const; |
| 322 | void matchedDirCheck(double t, const SkTSect* sect2, double t2, |
| 323 | bool* calcMatched, bool* oppMatched) const; |
| 324 | void mergeCoincidence(SkTSect* sect2); |
| 325 | |
| 326 | const SkDPoint& pointLast() const { |
| 327 | return fCurve[fCurve.pointLast()]; |
| 328 | } |
| 329 | |
| 330 | SkTSpan* prev(SkTSpan* ) const; |
| 331 | bool removeByPerpendicular(SkTSect* opp); |
| 332 | void recoverCollapsed(); |
| 333 | bool removeCoincident(SkTSpan* span, bool isBetween); |
| 334 | void removeAllBut(const SkTSpan* keep, SkTSpan* span, |
| 335 | SkTSect* opp); |
| 336 | bool removeSpan(SkTSpan* span); |
| 337 | void removeSpanRange(SkTSpan* first, SkTSpan* last); |
| 338 | bool removeSpans(SkTSpan* span, SkTSect* opp); |
| 339 | void removedEndCheck(SkTSpan* span); |
| 340 | |
| 341 | void resetRemovedEnds() { |
| 342 | fRemovedStartT = fRemovedEndT = false; |
| 343 | } |
| 344 | |
| 345 | SkTSpan* spanAtT(double t, SkTSpan** priorSpan); |
| 346 | SkTSpan* tail(); |
| 347 | bool trim(SkTSpan* span, SkTSect* opp); |
| 348 | bool unlinkSpan(SkTSpan* span); |
| 349 | bool updateBounded(SkTSpan* first, SkTSpan* last, |
| 350 | SkTSpan* oppFirst); |
| 351 | void validate() const; |
| 352 | void validateBounded() const; |
| 353 | |
| 354 | const SkTCurve& fCurve; |
| 355 | SkSTArenaAlloc<1024> fHeap; |
| 356 | SkTSpan* fHead; |
| 357 | SkTSpan* fCoincident; |
| 358 | SkTSpan* fDeleted; |
| 359 | int fActiveCount; |
| 360 | bool fRemovedStartT; |
| 361 | bool fRemovedEndT; |
| 362 | bool fHung; |
| 363 | SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState); |
| 364 | SkDEBUGCODE(SkTSect* fOppSect); |
| 365 | PATH_OPS_DEBUG_T_SECT_CODE(int fID); |
| 366 | PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount); |
| 367 | #if DEBUG_T_SECT |
| 368 | int fDebugAllocatedCount; |
| 369 | #endif |
| 370 | friend class SkTSpan; |
| 371 | }; |
| 372 | |
| 373 | #endif |
| 374 | |