blob: 3d48c8015b0cfec1ef5379352e6c5001ebc82ef1 [file] [log] [blame]
caryclark1049f122015-04-20 08:31:59 -07001/*
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
John Stilesdf078002020-07-14 09:44:57 -04008#include "src/core/SkTSort.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "src/pathops/SkPathOpsTSect.h"
caryclark1049f122015-04-20 08:31:59 -070010
Cary Clark8762fb62018-10-16 16:06:24 -040011#define COINCIDENT_SPAN_COUNT 9
12
13void SkTCoincident::setPerp(const SkTCurve& c1, double t,
14 const SkDPoint& cPt, const SkTCurve& c2) {
15 SkDVector dxdy = c1.dxdyAtT(t);
16 SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
17 SkIntersections i SkDEBUGCODE((c1.globalState()));
18 int used = i.intersectRay(c2, perp);
19 // only keep closest
20 if (used == 0 || used == 3) {
21 this->init();
22 return;
23 }
24 fPerpT = i[0][0];
25 fPerpPt = i.pt(0);
26 SkASSERT(used <= 2);
27 if (used == 2) {
28 double distSq = (fPerpPt - cPt).lengthSquared();
29 double dist2Sq = (i.pt(1) - cPt).lengthSquared();
30 if (dist2Sq < distSq) {
31 fPerpT = i[0][1];
32 fPerpPt = i.pt(1);
33 }
34 }
35#if DEBUG_T_SECT
36 SkDebugf("setPerp t=%1.9g cPt=(%1.9g,%1.9g) %s oppT=%1.9g fPerpPt=(%1.9g,%1.9g)\n",
37 t, cPt.fX, cPt.fY,
38 cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpT, fPerpPt.fX, fPerpPt.fY);
39#endif
40 fMatch = cPt.approximatelyEqual(fPerpPt);
41#if DEBUG_T_SECT
42 if (fMatch) {
43 SkDebugf(""); // allow setting breakpoint
44 }
45#endif
46}
47
48void SkTSpan::addBounded(SkTSpan* span, SkArenaAlloc* heap) {
49 SkTSpanBounded* bounded = heap->make<SkTSpanBounded>();
50 bounded->fBounded = span;
51 bounded->fNext = fBounded;
52 fBounded = bounded;
53}
54
55SkTSpan* SkTSect::addFollowing(
56 SkTSpan* prior) {
57 SkTSpan* result = this->addOne();
58 SkDEBUGCODE(result->debugSetGlobalState(this->globalState()));
59 result->fStartT = prior ? prior->fEndT : 0;
60 SkTSpan* next = prior ? prior->fNext : fHead;
61 result->fEndT = next ? next->fStartT : 1;
62 result->fPrev = prior;
63 result->fNext = next;
64 if (prior) {
65 prior->fNext = result;
66 } else {
67 fHead = result;
68 }
69 if (next) {
70 next->fPrev = result;
71 }
72 result->resetBounds(fCurve);
73 // world may not be consistent to call validate here
74 result->validate();
75 return result;
76}
77
78void SkTSect::addForPerp(SkTSpan* span, double t) {
79 if (!span->hasOppT(t)) {
80 SkTSpan* priorSpan;
81 SkTSpan* opp = this->spanAtT(t, &priorSpan);
82 if (!opp) {
83 opp = this->addFollowing(priorSpan);
84#if DEBUG_PERP
85 SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan ?
86 priorSpan->debugID() : -1, t, opp->debugID());
87#endif
88 }
89#if DEBUG_PERP
90 opp->dump(); SkDebugf("\n");
91 SkDebugf("%s addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan ?
92 priorSpan->debugID() : -1, opp->debugID());
93#endif
94 opp->addBounded(span, &fHeap);
95 span->addBounded(opp, &fHeap);
96 }
97 this->validate();
98#if DEBUG_T_SECT
99 span->validatePerpT(t);
100#endif
101}
102
103double SkTSpan::closestBoundedT(const SkDPoint& pt) const {
104 double result = -1;
105 double closest = DBL_MAX;
106 const SkTSpanBounded* testBounded = fBounded;
107 while (testBounded) {
108 const SkTSpan* test = testBounded->fBounded;
109 double startDist = test->pointFirst().distanceSquared(pt);
110 if (closest > startDist) {
111 closest = startDist;
112 result = test->fStartT;
113 }
114 double endDist = test->pointLast().distanceSquared(pt);
115 if (closest > endDist) {
116 closest = endDist;
117 result = test->fEndT;
118 }
119 testBounded = testBounded->fNext;
120 }
121 SkASSERT(between(0, result, 1));
122 return result;
123}
124
125#ifdef SK_DEBUG
126
127bool SkTSpan::debugIsBefore(const SkTSpan* span) const {
128 const SkTSpan* work = this;
129 do {
130 if (span == work) {
131 return true;
132 }
133 } while ((work = work->fNext));
134 return false;
135}
136#endif
137
138bool SkTSpan::contains(double t) const {
139 const SkTSpan* work = this;
140 do {
141 if (between(work->fStartT, t, work->fEndT)) {
142 return true;
143 }
144 } while ((work = work->fNext));
145 return false;
146}
147
148const SkTSect* SkTSpan::debugOpp() const {
149 return SkDEBUGRELEASE(fDebugSect->debugOpp(), nullptr);
150}
151
152SkTSpan* SkTSpan::findOppSpan(
153 const SkTSpan* opp) const {
154 SkTSpanBounded* bounded = fBounded;
155 while (bounded) {
156 SkTSpan* test = bounded->fBounded;
157 if (opp == test) {
158 return test;
159 }
160 bounded = bounded->fNext;
161 }
162 return nullptr;
163}
164
165// returns 0 if no hull intersection
166// 1 if hulls intersect
167// 2 if hulls only share a common endpoint
168// -1 if linear and further checking is required
169
170int SkTSpan::hullCheck(const SkTSpan* opp,
171 bool* start, bool* oppStart) {
172 if (fIsLinear) {
173 return -1;
174 }
175 bool ptsInCommon;
176 if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
177 SkASSERT(ptsInCommon);
178 return 2;
179 }
180 bool linear;
181 if (fPart->hullIntersects(*opp->fPart, &linear)) {
182 if (!linear) { // check set true if linear
183 return 1;
184 }
185 fIsLinear = true;
186 fIsLine = fPart->controlsInside();
187 return ptsInCommon ? 1 : -1;
188 } else { // hull is not linear; check set true if intersected at the end points
189 return ((int) ptsInCommon) << 1; // 0 or 2
190 }
191 return 0;
192}
193
194// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
195// use line intersection to guess a better split than 0.5
196// OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
197
198int SkTSpan::hullsIntersect(SkTSpan* opp,
199 bool* start, bool* oppStart) {
200 if (!fBounds.intersects(opp->fBounds)) {
201 return 0;
202 }
203 int hullSect = this->hullCheck(opp, start, oppStart);
204 if (hullSect >= 0) {
205 return hullSect;
206 }
207 hullSect = opp->hullCheck(this, oppStart, start);
208 if (hullSect >= 0) {
209 return hullSect;
210 }
211 return -1;
212}
213
214void SkTSpan::init(const SkTCurve& c) {
215 fPrev = fNext = nullptr;
216 fStartT = 0;
217 fEndT = 1;
218 fBounded = nullptr;
219 resetBounds(c);
220}
221
222bool SkTSpan::initBounds(const SkTCurve& c) {
223 if (SkDoubleIsNaN(fStartT) || SkDoubleIsNaN(fEndT)) {
224 return false;
225 }
226 c.subDivide(fStartT, fEndT, fPart);
227 fBounds.setBounds(*fPart);
228 fCoinStart.init();
229 fCoinEnd.init();
Brian Osman788b9162020-02-07 10:36:46 -0500230 fBoundsMax = std::max(fBounds.width(), fBounds.height());
Cary Clark8762fb62018-10-16 16:06:24 -0400231 fCollapsed = fPart->collapsed();
232 fHasPerp = false;
233 fDeleted = false;
234#if DEBUG_T_SECT
235 if (fCollapsed) {
236 SkDebugf(""); // for convenient breakpoints
237 }
238#endif
239 return fBounds.valid();
240}
241
242bool SkTSpan::linearsIntersect(SkTSpan* span) {
243 int result = this->linearIntersects(*span->fPart);
244 if (result <= 1) {
245 return SkToBool(result);
246 }
247 SkASSERT(span->fIsLinear);
248 result = span->linearIntersects(*fPart);
249// SkASSERT(result <= 1);
250 return SkToBool(result);
251}
252
253double SkTSpan::linearT(const SkDPoint& pt) const {
254 SkDVector len = this->pointLast() - this->pointFirst();
255 return fabs(len.fX) > fabs(len.fY)
256 ? (pt.fX - this->pointFirst().fX) / len.fX
257 : (pt.fY - this->pointFirst().fY) / len.fY;
258}
259
260int SkTSpan::linearIntersects(const SkTCurve& q2) const {
261 // looks like q1 is near-linear
262 int start = 0, end = fPart->pointLast(); // the outside points are usually the extremes
263 if (!fPart->controlsInside()) {
264 double dist = 0; // if there's any question, compute distance to find best outsiders
265 for (int outer = 0; outer < this->pointCount() - 1; ++outer) {
266 for (int inner = outer + 1; inner < this->pointCount(); ++inner) {
267 double test = ((*fPart)[outer] - (*fPart)[inner]).lengthSquared();
268 if (dist > test) {
269 continue;
270 }
271 dist = test;
272 start = outer;
273 end = inner;
274 }
275 }
276 }
277 // see if q2 is on one side of the line formed by the extreme points
278 double origX = (*fPart)[start].fX;
279 double origY = (*fPart)[start].fY;
280 double adj = (*fPart)[end].fX - origX;
281 double opp = (*fPart)[end].fY - origY;
Brian Osman788b9162020-02-07 10:36:46 -0500282 double maxPart = std::max(fabs(adj), fabs(opp));
Cary Clark8762fb62018-10-16 16:06:24 -0400283 double sign = 0; // initialization to shut up warning in release build
284 for (int n = 0; n < q2.pointCount(); ++n) {
285 double dx = q2[n].fY - origY;
286 double dy = q2[n].fX - origX;
Brian Osman788b9162020-02-07 10:36:46 -0500287 double maxVal = std::max(maxPart, std::max(fabs(dx), fabs(dy)));
Cary Clark8762fb62018-10-16 16:06:24 -0400288 double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
289 if (precisely_zero_when_compared_to(test, maxVal)) {
290 return 1;
291 }
292 if (approximately_zero_when_compared_to(test, maxVal)) {
293 return 3;
294 }
295 if (n == 0) {
296 sign = test;
297 continue;
298 }
299 if (test * sign < 0) {
300 return 1;
301 }
302 }
303 return 0;
304}
305
306bool SkTSpan::onlyEndPointsInCommon(const SkTSpan* opp,
307 bool* start, bool* oppStart, bool* ptsInCommon) {
308 if (opp->pointFirst() == this->pointFirst()) {
309 *start = *oppStart = true;
310 } else if (opp->pointFirst() == this->pointLast()) {
311 *start = false;
312 *oppStart = true;
313 } else if (opp->pointLast() == this->pointFirst()) {
314 *start = true;
315 *oppStart = false;
316 } else if (opp->pointLast() == this->pointLast()) {
317 *start = *oppStart = false;
318 } else {
319 *ptsInCommon = false;
320 return false;
321 }
322 *ptsInCommon = true;
323 const SkDPoint* otherPts[4], * oppOtherPts[4];
324// const SkDPoint* otherPts[this->pointCount() - 1], * oppOtherPts[opp->pointCount() - 1];
325 int baseIndex = *start ? 0 : fPart->pointLast();
326 fPart->otherPts(baseIndex, otherPts);
327 opp->fPart->otherPts(*oppStart ? 0 : opp->fPart->pointLast(), oppOtherPts);
328 const SkDPoint& base = (*fPart)[baseIndex];
329 for (int o1 = 0; o1 < this->pointCount() - 1; ++o1) {
330 SkDVector v1 = *otherPts[o1] - base;
331 for (int o2 = 0; o2 < opp->pointCount() - 1; ++o2) {
332 SkDVector v2 = *oppOtherPts[o2] - base;
333 if (v2.dot(v1) >= 0) {
334 return false;
335 }
336 }
337 }
338 return true;
339}
340
341SkTSpan* SkTSpan::oppT(double t) const {
342 SkTSpanBounded* bounded = fBounded;
343 while (bounded) {
344 SkTSpan* test = bounded->fBounded;
345 if (between(test->fStartT, t, test->fEndT)) {
346 return test;
347 }
348 bounded = bounded->fNext;
349 }
350 return nullptr;
351}
352
353bool SkTSpan::removeAllBounded() {
354 bool deleteSpan = false;
355 SkTSpanBounded* bounded = fBounded;
356 while (bounded) {
357 SkTSpan* opp = bounded->fBounded;
358 deleteSpan |= opp->removeBounded(this);
359 bounded = bounded->fNext;
360 }
361 return deleteSpan;
362}
363
364bool SkTSpan::removeBounded(const SkTSpan* opp) {
365 if (fHasPerp) {
366 bool foundStart = false;
367 bool foundEnd = false;
368 SkTSpanBounded* bounded = fBounded;
369 while (bounded) {
370 SkTSpan* test = bounded->fBounded;
371 if (opp != test) {
372 foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
373 foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
374 }
375 bounded = bounded->fNext;
376 }
377 if (!foundStart || !foundEnd) {
378 fHasPerp = false;
379 fCoinStart.init();
380 fCoinEnd.init();
381 }
382 }
383 SkTSpanBounded* bounded = fBounded;
384 SkTSpanBounded* prev = nullptr;
385 while (bounded) {
386 SkTSpanBounded* boundedNext = bounded->fNext;
387 if (opp == bounded->fBounded) {
388 if (prev) {
389 prev->fNext = boundedNext;
390 return false;
391 } else {
392 fBounded = boundedNext;
393 return fBounded == nullptr;
394 }
395 }
396 prev = bounded;
397 bounded = boundedNext;
398 }
399 SkOPASSERT(0);
400 return false;
401}
402
403bool SkTSpan::splitAt(SkTSpan* work, double t, SkArenaAlloc* heap) {
404 fStartT = t;
405 fEndT = work->fEndT;
406 if (fStartT == fEndT) {
407 fCollapsed = true;
408 return false;
409 }
410 work->fEndT = t;
411 if (work->fStartT == work->fEndT) {
412 work->fCollapsed = true;
413 return false;
414 }
415 fPrev = work;
416 fNext = work->fNext;
417 fIsLinear = work->fIsLinear;
418 fIsLine = work->fIsLine;
419
420 work->fNext = this;
421 if (fNext) {
422 fNext->fPrev = this;
423 }
424 this->validate();
425 SkTSpanBounded* bounded = work->fBounded;
426 fBounded = nullptr;
427 while (bounded) {
428 this->addBounded(bounded->fBounded, heap);
429 bounded = bounded->fNext;
430 }
431 bounded = fBounded;
432 while (bounded) {
433 bounded->fBounded->addBounded(this, heap);
434 bounded = bounded->fNext;
435 }
436 return true;
437}
438
439void SkTSpan::validate() const {
440#if DEBUG_VALIDATE
441 SkASSERT(this != fPrev);
442 SkASSERT(this != fNext);
443 SkASSERT(fNext == nullptr || fNext != fPrev);
444 SkASSERT(fNext == nullptr || this == fNext->fPrev);
445 SkASSERT(fPrev == nullptr || this == fPrev->fNext);
446 this->validateBounded();
447#endif
448#if DEBUG_T_SECT
449 SkASSERT(fBounds.width() || fBounds.height() || fCollapsed);
Brian Osman788b9162020-02-07 10:36:46 -0500450 SkASSERT(fBoundsMax == std::max(fBounds.width(), fBounds.height()) || fCollapsed == 0xFF);
Cary Clark8762fb62018-10-16 16:06:24 -0400451 SkASSERT(0 <= fStartT);
452 SkASSERT(fEndT <= 1);
453 SkASSERT(fStartT <= fEndT);
454 SkASSERT(fBounded || fCollapsed == 0xFF);
455 if (fHasPerp) {
456 if (fCoinStart.isMatch()) {
457 validatePerpT(fCoinStart.perpT());
458 validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
459 }
460 if (fCoinEnd.isMatch()) {
461 validatePerpT(fCoinEnd.perpT());
462 validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
463 }
464 }
465#endif
466}
467
468void SkTSpan::validateBounded() const {
469#if DEBUG_VALIDATE
470 const SkTSpanBounded* testBounded = fBounded;
471 while (testBounded) {
472 SkDEBUGCODE(const SkTSpan* overlap = testBounded->fBounded);
473 SkASSERT(!overlap->fDeleted);
474#if DEBUG_T_SECT
475 SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
476 SkASSERT(overlap->findOppSpan(this));
477#endif
478 testBounded = testBounded->fNext;
479 }
480#endif
481}
482
483void SkTSpan::validatePerpT(double oppT) const {
484 const SkTSpanBounded* testBounded = fBounded;
485 while (testBounded) {
486 const SkTSpan* overlap = testBounded->fBounded;
487 if (precisely_between(overlap->fStartT, oppT, overlap->fEndT)) {
488 return;
489 }
490 testBounded = testBounded->fNext;
491 }
492 SkASSERT(0);
493}
494
495void SkTSpan::validatePerpPt(double t, const SkDPoint& pt) const {
496 SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt);
497}
498
499SkTSect::SkTSect(const SkTCurve& c
500 SkDEBUGPARAMS(SkOpGlobalState* debugGlobalState)
501 PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
502 : fCurve(c)
503 , fHeap(sizeof(SkTSpan) * 4)
504 , fCoincident(nullptr)
505 , fDeleted(nullptr)
506 , fActiveCount(0)
507 , fHung(false)
508 SkDEBUGPARAMS(fDebugGlobalState(debugGlobalState))
509 PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
510 PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
511 PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0))
512{
513 this->resetRemovedEnds();
514 fHead = this->addOne();
515 SkDEBUGCODE(fHead->debugSetGlobalState(debugGlobalState));
516 fHead->init(c);
517}
518
519SkTSpan* SkTSect::addOne() {
520 SkTSpan* result;
521 if (fDeleted) {
522 result = fDeleted;
523 fDeleted = result->fNext;
524 } else {
525 result = fHeap.make<SkTSpan>(fCurve, fHeap);
526#if DEBUG_T_SECT
527 ++fDebugAllocatedCount;
528#endif
529 }
530 result->reset();
531 result->fHasPerp = false;
532 result->fDeleted = false;
533 ++fActiveCount;
534 PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
535 SkDEBUGCODE(result->fDebugSect = this);
536#ifdef SK_DEBUG
537 result->debugInit(fCurve, fHeap);
538 result->fCoinStart.debugInit();
539 result->fCoinEnd.debugInit();
540 result->fPrev = result->fNext = nullptr;
541 result->fBounds.debugInit();
542 result->fStartT = result->fEndT = result->fBoundsMax = SK_ScalarNaN;
543 result->fCollapsed = result->fIsLinear = result->fIsLine = 0xFF;
544#endif
545 return result;
546}
547
548bool SkTSect::binarySearchCoin(SkTSect* sect2, double tStart,
549 double tStep, double* resultT, double* oppT, SkTSpan** oppFirst) {
550 SkTSpan work(fCurve, fHeap);
551 double result = work.fStartT = work.fEndT = tStart;
552 SkDEBUGCODE(work.fDebugSect = this);
553 SkDPoint last = fCurve.ptAtT(tStart);
554 SkDPoint oppPt;
555 bool flip = false;
556 bool contained = false;
557 bool down = tStep < 0;
558 const SkTCurve& opp = sect2->fCurve;
559 do {
560 tStep *= 0.5;
561 work.fStartT += tStep;
562 if (flip) {
563 tStep = -tStep;
564 flip = false;
565 }
566 work.initBounds(fCurve);
567 if (work.fCollapsed) {
568 return false;
569 }
570 if (last.approximatelyEqual(work.pointFirst())) {
571 break;
572 }
573 last = work.pointFirst();
574 work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp);
575 if (work.fCoinStart.isMatch()) {
576#if DEBUG_T_SECT
577 work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt());
578#endif
579 double oppTTest = work.fCoinStart.perpT();
580 if (sect2->fHead->contains(oppTTest)) {
581 *oppT = oppTTest;
582 oppPt = work.fCoinStart.perpPt();
583 contained = true;
584 if (down ? result <= work.fStartT : result >= work.fStartT) {
585 *oppFirst = nullptr; // signal caller to fail
586 return false;
587 }
588 result = work.fStartT;
589 continue;
590 }
591 }
592 tStep = -tStep;
593 flip = true;
594 } while (true);
595 if (!contained) {
596 return false;
597 }
598 if (last.approximatelyEqual(fCurve[0])) {
599 result = 0;
600 } else if (last.approximatelyEqual(this->pointLast())) {
601 result = 1;
602 }
603 if (oppPt.approximatelyEqual(opp[0])) {
604 *oppT = 0;
605 } else if (oppPt.approximatelyEqual(sect2->pointLast())) {
606 *oppT = 1;
607 }
608 *resultT = result;
609 return true;
610}
611
612// OPTIMIZE ? keep a sorted list of sizes in the form of a doubly-linked list in quad span
613// so that each quad sect has a pointer to the largest, and can update it as spans
614// are split
615
616SkTSpan* SkTSect::boundsMax() {
617 SkTSpan* test = fHead;
618 SkTSpan* largest = fHead;
619 bool lCollapsed = largest->fCollapsed;
620 int safetyNet = 10000;
621 while ((test = test->fNext)) {
622 if (!--safetyNet) {
623 fHung = true;
624 return nullptr;
625 }
626 bool tCollapsed = test->fCollapsed;
627 if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
628 largest->fBoundsMax < test->fBoundsMax)) {
629 largest = test;
630 lCollapsed = test->fCollapsed;
631 }
632 }
633 return largest;
634}
635
636bool SkTSect::coincidentCheck(SkTSect* sect2) {
637 SkTSpan* first = fHead;
638 if (!first) {
639 return false;
640 }
641 SkTSpan* last, * next;
642 do {
643 int consecutive = this->countConsecutiveSpans(first, &last);
644 next = last->fNext;
645 if (consecutive < COINCIDENT_SPAN_COUNT) {
646 continue;
647 }
648 this->validate();
649 sect2->validate();
650 this->computePerpendiculars(sect2, first, last);
651 this->validate();
652 sect2->validate();
653 // check to see if a range of points are on the curve
654 SkTSpan* coinStart = first;
655 do {
656 bool success = this->extractCoincident(sect2, coinStart, last, &coinStart);
657 if (!success) {
658 return false;
659 }
660 } while (coinStart && !last->fDeleted);
661 if (!fHead || !sect2->fHead) {
662 break;
663 }
664 if (!next || next->fDeleted) {
665 break;
666 }
667 } while ((first = next));
668 return true;
669}
670
671void SkTSect::coincidentForce(SkTSect* sect2,
672 double start1s, double start1e) {
673 SkTSpan* first = fHead;
674 SkTSpan* last = this->tail();
675 SkTSpan* oppFirst = sect2->fHead;
676 SkTSpan* oppLast = sect2->tail();
Cary Clarkfc48b612018-10-18 13:07:14 -0400677 if (!last || !oppLast) {
678 return;
679 }
Cary Clark8762fb62018-10-16 16:06:24 -0400680 bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
681 deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
682 this->removeSpanRange(first, last);
683 sect2->removeSpanRange(oppFirst, oppLast);
684 first->fStartT = start1s;
685 first->fEndT = start1e;
686 first->resetBounds(fCurve);
687 first->fCoinStart.setPerp(fCurve, start1s, fCurve[0], sect2->fCurve);
688 first->fCoinEnd.setPerp(fCurve, start1e, this->pointLast(), sect2->fCurve);
689 bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
Brian Osman788b9162020-02-07 10:36:46 -0500690 double oppStartT = first->fCoinStart.perpT() == -1 ? 0 : std::max(0., first->fCoinStart.perpT());
691 double oppEndT = first->fCoinEnd.perpT() == -1 ? 1 : std::min(1., first->fCoinEnd.perpT());
Cary Clark8762fb62018-10-16 16:06:24 -0400692 if (!oppMatched) {
693 using std::swap;
694 swap(oppStartT, oppEndT);
695 }
696 oppFirst->fStartT = oppStartT;
697 oppFirst->fEndT = oppEndT;
698 oppFirst->resetBounds(sect2->fCurve);
699 this->removeCoincident(first, false);
700 sect2->removeCoincident(oppFirst, true);
701 if (deleteEmptySpans) {
702 this->deleteEmptySpans();
703 sect2->deleteEmptySpans();
704 }
705}
706
707bool SkTSect::coincidentHasT(double t) {
708 SkTSpan* test = fCoincident;
709 while (test) {
710 if (between(test->fStartT, t, test->fEndT)) {
711 return true;
712 }
713 test = test->fNext;
714 }
715 return false;
716}
717
718int SkTSect::collapsed() const {
719 int result = 0;
720 const SkTSpan* test = fHead;
721 while (test) {
722 if (test->fCollapsed) {
723 ++result;
724 }
725 test = test->next();
726 }
727 return result;
728}
729
730void SkTSect::computePerpendiculars(SkTSect* sect2,
731 SkTSpan* first, SkTSpan* last) {
Cary Clark4929a4a2018-10-17 14:12:41 -0400732 if (!last) {
733 return;
734 }
Cary Clark8762fb62018-10-16 16:06:24 -0400735 const SkTCurve& opp = sect2->fCurve;
736 SkTSpan* work = first;
737 SkTSpan* prior = nullptr;
738 do {
739 if (!work->fHasPerp && !work->fCollapsed) {
740 if (prior) {
741 work->fCoinStart = prior->fCoinEnd;
742 } else {
743 work->fCoinStart.setPerp(fCurve, work->fStartT, work->pointFirst(), opp);
744 }
745 if (work->fCoinStart.isMatch()) {
746 double perpT = work->fCoinStart.perpT();
747 if (sect2->coincidentHasT(perpT)) {
748 work->fCoinStart.init();
749 } else {
750 sect2->addForPerp(work, perpT);
751 }
752 }
753 work->fCoinEnd.setPerp(fCurve, work->fEndT, work->pointLast(), opp);
754 if (work->fCoinEnd.isMatch()) {
755 double perpT = work->fCoinEnd.perpT();
756 if (sect2->coincidentHasT(perpT)) {
757 work->fCoinEnd.init();
758 } else {
759 sect2->addForPerp(work, perpT);
760 }
761 }
762 work->fHasPerp = true;
763 }
764 if (work == last) {
765 break;
766 }
767 prior = work;
768 work = work->fNext;
769 SkASSERT(work);
770 } while (true);
771}
772
773int SkTSect::countConsecutiveSpans(SkTSpan* first,
774 SkTSpan** lastPtr) const {
775 int consecutive = 1;
776 SkTSpan* last = first;
777 do {
778 SkTSpan* next = last->fNext;
779 if (!next) {
780 break;
781 }
782 if (next->fStartT > last->fEndT) {
783 break;
784 }
785 ++consecutive;
786 last = next;
787 } while (true);
788 *lastPtr = last;
789 return consecutive;
790}
791
792bool SkTSect::hasBounded(const SkTSpan* span) const {
793 const SkTSpan* test = fHead;
794 if (!test) {
795 return false;
796 }
797 do {
798 if (test->findOppSpan(span)) {
799 return true;
800 }
801 } while ((test = test->next()));
802 return false;
803}
804
805bool SkTSect::deleteEmptySpans() {
806 SkTSpan* test;
807 SkTSpan* next = fHead;
808 int safetyHatch = 1000;
809 while ((test = next)) {
810 next = test->fNext;
811 if (!test->fBounded) {
812 if (!this->removeSpan(test)) {
813 return false;
814 }
815 }
816 if (--safetyHatch < 0) {
817 return false;
818 }
819 }
820 return true;
821}
822
823bool SkTSect::extractCoincident(
824 SkTSect* sect2,
825 SkTSpan* first, SkTSpan* last,
826 SkTSpan** result) {
827 first = findCoincidentRun(first, &last);
828 if (!first || !last) {
829 *result = nullptr;
830 return true;
831 }
832 // march outwards to find limit of coincidence from here to previous and next spans
833 double startT = first->fStartT;
834 double oppStartT SK_INIT_TO_AVOID_WARNING;
835 double oppEndT SK_INIT_TO_AVOID_WARNING;
836 SkTSpan* prev = first->fPrev;
837 SkASSERT(first->fCoinStart.isMatch());
838 SkTSpan* oppFirst = first->findOppT(first->fCoinStart.perpT());
839 SkOPASSERT(last->fCoinEnd.isMatch());
840 bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
841 double coinStart;
842 SkDEBUGCODE(double coinEnd);
843 SkTSpan* cutFirst;
844 if (prev && prev->fEndT == startT
845 && this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
846 &oppStartT, &oppFirst)
847 && prev->fStartT < coinStart && coinStart < startT
848 && (cutFirst = prev->oppT(oppStartT))) {
849 oppFirst = cutFirst;
850 first = this->addSplitAt(prev, coinStart);
851 first->markCoincident();
852 prev->fCoinEnd.markCoincident();
853 if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
854 SkTSpan* oppHalf = sect2->addSplitAt(oppFirst, oppStartT);
855 if (oppMatched) {
856 oppFirst->fCoinEnd.markCoincident();
857 oppHalf->markCoincident();
858 oppFirst = oppHalf;
859 } else {
860 oppFirst->markCoincident();
861 oppHalf->fCoinStart.markCoincident();
862 }
863 }
864 } else {
865 if (!oppFirst) {
866 return false;
867 }
868 SkDEBUGCODE(coinStart = first->fStartT);
869 SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
870 }
871 // FIXME: incomplete : if we're not at the end, find end of coin
872 SkTSpan* oppLast;
873 SkOPASSERT(last->fCoinEnd.isMatch());
874 oppLast = last->findOppT(last->fCoinEnd.perpT());
875 SkDEBUGCODE(coinEnd = last->fEndT);
876#ifdef SK_DEBUG
877 if (!this->globalState() || !this->globalState()->debugSkipAssert()) {
878 oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT;
879 }
880#endif
881 if (!oppMatched) {
882 using std::swap;
883 swap(oppFirst, oppLast);
884 swap(oppStartT, oppEndT);
885 }
886 SkOPASSERT(oppStartT < oppEndT);
887 SkASSERT(coinStart == first->fStartT);
888 SkASSERT(coinEnd == last->fEndT);
Cary Clark8762fb62018-10-16 16:06:24 -0400889 if (!oppFirst) {
890 *result = nullptr;
891 return true;
892 }
Greg Kaiserfe046de2019-02-08 17:04:00 -0800893 SkOPASSERT(oppStartT == oppFirst->fStartT);
Cary Clark8762fb62018-10-16 16:06:24 -0400894 if (!oppLast) {
895 *result = nullptr;
896 return true;
897 }
Greg Kaiserfe046de2019-02-08 17:04:00 -0800898 SkOPASSERT(oppEndT == oppLast->fEndT);
Cary Clark8762fb62018-10-16 16:06:24 -0400899 // reduce coincident runs to single entries
900 this->validate();
901 sect2->validate();
902 bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
903 deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
904 this->removeSpanRange(first, last);
905 sect2->removeSpanRange(oppFirst, oppLast);
906 first->fEndT = last->fEndT;
907 first->resetBounds(this->fCurve);
908 first->fCoinStart.setPerp(fCurve, first->fStartT, first->pointFirst(), sect2->fCurve);
909 first->fCoinEnd.setPerp(fCurve, first->fEndT, first->pointLast(), sect2->fCurve);
910 oppStartT = first->fCoinStart.perpT();
911 oppEndT = first->fCoinEnd.perpT();
912 if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) {
913 if (!oppMatched) {
914 using std::swap;
915 swap(oppStartT, oppEndT);
916 }
917 oppFirst->fStartT = oppStartT;
918 oppFirst->fEndT = oppEndT;
919 oppFirst->resetBounds(sect2->fCurve);
920 }
921 this->validateBounded();
922 sect2->validateBounded();
923 last = first->fNext;
924 if (!this->removeCoincident(first, false)) {
925 return false;
926 }
927 if (!sect2->removeCoincident(oppFirst, true)) {
928 return false;
929 }
930 if (deleteEmptySpans) {
931 if (!this->deleteEmptySpans() || !sect2->deleteEmptySpans()) {
932 *result = nullptr;
933 return false;
934 }
935 }
936 this->validate();
937 sect2->validate();
938 *result = last && !last->fDeleted && fHead && sect2->fHead ? last : nullptr;
939 return true;
940}
941
942SkTSpan* SkTSect::findCoincidentRun(
943 SkTSpan* first, SkTSpan** lastPtr) {
944 SkTSpan* work = first;
945 SkTSpan* lastCandidate = nullptr;
946 first = nullptr;
947 // find the first fully coincident span
948 do {
949 if (work->fCoinStart.isMatch()) {
950#if DEBUG_T_SECT
951 work->validatePerpT(work->fCoinStart.perpT());
952 work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
953#endif
954 SkOPASSERT(work->hasOppT(work->fCoinStart.perpT()));
955 if (!work->fCoinEnd.isMatch()) {
956 break;
957 }
958 lastCandidate = work;
959 if (!first) {
960 first = work;
961 }
962 } else if (first && work->fCollapsed) {
963 *lastPtr = lastCandidate;
964 return first;
965 } else {
966 lastCandidate = nullptr;
967 SkOPASSERT(!first);
968 }
969 if (work == *lastPtr) {
970 return first;
971 }
972 work = work->fNext;
973 if (!work) {
974 return nullptr;
975 }
976 } while (true);
977 if (lastCandidate) {
978 *lastPtr = lastCandidate;
979 }
980 return first;
981}
982
983int SkTSect::intersects(SkTSpan* span,
984 SkTSect* opp,
985 SkTSpan* oppSpan, int* oppResult) {
986 bool spanStart, oppStart;
987 int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
988 if (hullResult >= 0) {
989 if (hullResult == 2) { // hulls have one point in common
990 if (!span->fBounded || !span->fBounded->fNext) {
991 SkASSERT(!span->fBounded || span->fBounded->fBounded == oppSpan);
992 if (spanStart) {
993 span->fEndT = span->fStartT;
994 } else {
995 span->fStartT = span->fEndT;
996 }
997 } else {
998 hullResult = 1;
999 }
1000 if (!oppSpan->fBounded || !oppSpan->fBounded->fNext) {
Cary Clark13795082018-11-07 16:18:39 -05001001 if (oppSpan->fBounded && oppSpan->fBounded->fBounded != span) {
1002 return 0;
1003 }
Cary Clark8762fb62018-10-16 16:06:24 -04001004 if (oppStart) {
1005 oppSpan->fEndT = oppSpan->fStartT;
1006 } else {
1007 oppSpan->fStartT = oppSpan->fEndT;
1008 }
1009 *oppResult = 2;
1010 } else {
1011 *oppResult = 1;
1012 }
1013 } else {
1014 *oppResult = 1;
1015 }
1016 return hullResult;
1017 }
1018 if (span->fIsLine && oppSpan->fIsLine) {
1019 SkIntersections i;
1020 int sects = this->linesIntersect(span, opp, oppSpan, &i);
1021 if (sects == 2) {
1022 return *oppResult = 1;
1023 }
1024 if (!sects) {
1025 return -1;
1026 }
1027 this->removedEndCheck(span);
1028 span->fStartT = span->fEndT = i[0][0];
1029 opp->removedEndCheck(oppSpan);
1030 oppSpan->fStartT = oppSpan->fEndT = i[1][0];
1031 return *oppResult = 2;
1032 }
1033 if (span->fIsLinear || oppSpan->fIsLinear) {
1034 return *oppResult = (int) span->linearsIntersect(oppSpan);
1035 }
1036 return *oppResult = 1;
1037}
1038
1039template<typename SkTCurve>
1040static bool is_parallel(const SkDLine& thisLine, const SkTCurve& opp) {
1041 if (!opp.IsConic()) {
1042 return false; // FIXME : breaks a lot of stuff now
1043 }
1044 int finds = 0;
1045 SkDLine thisPerp;
1046 thisPerp.fPts[0].fX = thisLine.fPts[1].fX + (thisLine.fPts[1].fY - thisLine.fPts[0].fY);
1047 thisPerp.fPts[0].fY = thisLine.fPts[1].fY + (thisLine.fPts[0].fX - thisLine.fPts[1].fX);
1048 thisPerp.fPts[1] = thisLine.fPts[1];
1049 SkIntersections perpRayI;
1050 perpRayI.intersectRay(opp, thisPerp);
1051 for (int pIndex = 0; pIndex < perpRayI.used(); ++pIndex) {
1052 finds += perpRayI.pt(pIndex).approximatelyEqual(thisPerp.fPts[1]);
1053 }
1054 thisPerp.fPts[1].fX = thisLine.fPts[0].fX + (thisLine.fPts[1].fY - thisLine.fPts[0].fY);
1055 thisPerp.fPts[1].fY = thisLine.fPts[0].fY + (thisLine.fPts[0].fX - thisLine.fPts[1].fX);
1056 thisPerp.fPts[0] = thisLine.fPts[0];
1057 perpRayI.intersectRay(opp, thisPerp);
1058 for (int pIndex = 0; pIndex < perpRayI.used(); ++pIndex) {
1059 finds += perpRayI.pt(pIndex).approximatelyEqual(thisPerp.fPts[0]);
1060 }
1061 return finds >= 2;
1062}
1063
1064// while the intersection points are sufficiently far apart:
1065// construct the tangent lines from the intersections
1066// find the point where the tangent line intersects the opposite curve
1067
1068int SkTSect::linesIntersect(SkTSpan* span,
1069 SkTSect* opp,
1070 SkTSpan* oppSpan, SkIntersections* i) {
1071 SkIntersections thisRayI SkDEBUGCODE((span->fDebugGlobalState));
1072 SkIntersections oppRayI SkDEBUGCODE((span->fDebugGlobalState));
1073 SkDLine thisLine = {{ span->pointFirst(), span->pointLast() }};
1074 SkDLine oppLine = {{ oppSpan->pointFirst(), oppSpan->pointLast() }};
1075 int loopCount = 0;
1076 double bestDistSq = DBL_MAX;
1077 if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
1078 return 0;
1079 }
1080 if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
1081 return 0;
1082 }
1083 // if the ends of each line intersect the opposite curve, the lines are coincident
1084 if (thisRayI.used() > 1) {
1085 int ptMatches = 0;
1086 for (int tIndex = 0; tIndex < thisRayI.used(); ++tIndex) {
1087 for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(thisLine.fPts); ++lIndex) {
1088 ptMatches += thisRayI.pt(tIndex).approximatelyEqual(thisLine.fPts[lIndex]);
1089 }
1090 }
1091 if (ptMatches == 2 || is_parallel(thisLine, opp->fCurve)) {
1092 return 2;
1093 }
1094 }
1095 if (oppRayI.used() > 1) {
1096 int ptMatches = 0;
1097 for (int oIndex = 0; oIndex < oppRayI.used(); ++oIndex) {
1098 for (int lIndex = 0; lIndex < (int) SK_ARRAY_COUNT(oppLine.fPts); ++lIndex) {
1099 ptMatches += oppRayI.pt(oIndex).approximatelyEqual(oppLine.fPts[lIndex]);
1100 }
1101 }
1102 if (ptMatches == 2|| is_parallel(oppLine, this->fCurve)) {
1103 return 2;
1104 }
1105 }
1106 do {
1107 // pick the closest pair of points
1108 double closest = DBL_MAX;
1109 int closeIndex SK_INIT_TO_AVOID_WARNING;
1110 int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
1111 for (int index = 0; index < oppRayI.used(); ++index) {
1112 if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
1113 continue;
1114 }
1115 for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
1116 if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
1117 continue;
1118 }
1119 double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
1120 if (closest > distSq) {
1121 closest = distSq;
1122 closeIndex = index;
1123 oppCloseIndex = oIndex;
1124 }
1125 }
1126 }
1127 if (closest == DBL_MAX) {
1128 break;
1129 }
1130 const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex);
1131 const SkDPoint& iPt = oppRayI.pt(closeIndex);
1132 if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
1133 && between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
1134 && oppIPt.approximatelyEqual(iPt)) {
1135 i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex);
1136 return i->used();
1137 }
1138 double distSq = oppIPt.distanceSquared(iPt);
1139 if (bestDistSq < distSq || ++loopCount > 5) {
1140 return 0;
1141 }
1142 bestDistSq = distSq;
1143 double oppStart = oppRayI[0][closeIndex];
1144 thisLine[0] = fCurve.ptAtT(oppStart);
1145 thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppStart);
1146 if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
1147 break;
1148 }
1149 double start = thisRayI[0][oppCloseIndex];
1150 oppLine[0] = opp->fCurve.ptAtT(start);
1151 oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(start);
1152 if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
1153 break;
1154 }
1155 } while (true);
1156 // convergence may fail if the curves are nearly coincident
1157 SkTCoincident oCoinS, oCoinE;
1158 oCoinS.setPerp(opp->fCurve, oppSpan->fStartT, oppSpan->pointFirst(), fCurve);
1159 oCoinE.setPerp(opp->fCurve, oppSpan->fEndT, oppSpan->pointLast(), fCurve);
1160 double tStart = oCoinS.perpT();
1161 double tEnd = oCoinE.perpT();
1162 bool swap = tStart > tEnd;
1163 if (swap) {
1164 using std::swap;
1165 swap(tStart, tEnd);
1166 }
Brian Osman788b9162020-02-07 10:36:46 -05001167 tStart = std::max(tStart, span->fStartT);
1168 tEnd = std::min(tEnd, span->fEndT);
Cary Clark8762fb62018-10-16 16:06:24 -04001169 if (tStart > tEnd) {
1170 return 0;
1171 }
1172 SkDVector perpS, perpE;
1173 if (tStart == span->fStartT) {
1174 SkTCoincident coinS;
1175 coinS.setPerp(fCurve, span->fStartT, span->pointFirst(), opp->fCurve);
1176 perpS = span->pointFirst() - coinS.perpPt();
1177 } else if (swap) {
1178 perpS = oCoinE.perpPt() - oppSpan->pointLast();
1179 } else {
1180 perpS = oCoinS.perpPt() - oppSpan->pointFirst();
1181 }
1182 if (tEnd == span->fEndT) {
1183 SkTCoincident coinE;
1184 coinE.setPerp(fCurve, span->fEndT, span->pointLast(), opp->fCurve);
1185 perpE = span->pointLast() - coinE.perpPt();
1186 } else if (swap) {
1187 perpE = oCoinS.perpPt() - oppSpan->pointFirst();
1188 } else {
1189 perpE = oCoinE.perpPt() - oppSpan->pointLast();
1190 }
1191 if (perpS.dot(perpE) >= 0) {
1192 return 0;
1193 }
1194 SkTCoincident coinW;
1195 double workT = tStart;
1196 double tStep = tEnd - tStart;
1197 SkDPoint workPt;
1198 do {
1199 tStep *= 0.5;
1200 if (precisely_zero(tStep)) {
1201 return 0;
1202 }
1203 workT += tStep;
1204 workPt = fCurve.ptAtT(workT);
1205 coinW.setPerp(fCurve, workT, workPt, opp->fCurve);
1206 double perpT = coinW.perpT();
1207 if (coinW.isMatch() ? !between(oppSpan->fStartT, perpT, oppSpan->fEndT) : perpT < 0) {
1208 continue;
1209 }
1210 SkDVector perpW = workPt - coinW.perpPt();
1211 if ((perpS.dot(perpW) >= 0) == (tStep < 0)) {
1212 tStep = -tStep;
1213 }
1214 if (workPt.approximatelyEqual(coinW.perpPt())) {
1215 break;
1216 }
1217 } while (true);
1218 double oppTTest = coinW.perpT();
1219 if (!opp->fHead->contains(oppTTest)) {
1220 return 0;
1221 }
1222 i->setMax(1);
1223 i->insert(workT, oppTTest, workPt);
1224 return 1;
1225}
1226
1227bool SkTSect::markSpanGone(SkTSpan* span) {
1228 if (--fActiveCount < 0) {
1229 return false;
1230 }
1231 span->fNext = fDeleted;
1232 fDeleted = span;
1233 SkOPASSERT(!span->fDeleted);
1234 span->fDeleted = true;
1235 return true;
1236}
1237
1238bool SkTSect::matchedDirection(double t, const SkTSect* sect2,
1239 double t2) const {
1240 SkDVector dxdy = this->fCurve.dxdyAtT(t);
1241 SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2);
1242 return dxdy.dot(dxdy2) >= 0;
1243}
1244
1245void SkTSect::matchedDirCheck(double t, const SkTSect* sect2,
1246 double t2, bool* calcMatched, bool* oppMatched) const {
1247 if (*calcMatched) {
1248 SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
1249 } else {
1250 *oppMatched = this->matchedDirection(t, sect2, t2);
1251 *calcMatched = true;
1252 }
1253}
1254
1255void SkTSect::mergeCoincidence(SkTSect* sect2) {
1256 double smallLimit = 0;
1257 do {
1258 // find the smallest unprocessed span
1259 SkTSpan* smaller = nullptr;
1260 SkTSpan* test = fCoincident;
1261 do {
1262 if (!test) {
1263 return;
1264 }
1265 if (test->fStartT < smallLimit) {
1266 continue;
1267 }
1268 if (smaller && smaller->fEndT < test->fStartT) {
1269 continue;
1270 }
1271 smaller = test;
1272 } while ((test = test->fNext));
1273 if (!smaller) {
1274 return;
1275 }
1276 smallLimit = smaller->fEndT;
1277 // find next larger span
1278 SkTSpan* prior = nullptr;
1279 SkTSpan* larger = nullptr;
1280 SkTSpan* largerPrior = nullptr;
1281 test = fCoincident;
1282 do {
1283 if (test->fStartT < smaller->fEndT) {
1284 continue;
1285 }
1286 SkOPASSERT(test->fStartT != smaller->fEndT);
1287 if (larger && larger->fStartT < test->fStartT) {
1288 continue;
1289 }
1290 largerPrior = prior;
1291 larger = test;
1292 } while ((void) (prior = test), (test = test->fNext));
1293 if (!larger) {
1294 continue;
1295 }
1296 // check middle t value to see if it is coincident as well
1297 double midT = (smaller->fEndT + larger->fStartT) / 2;
1298 SkDPoint midPt = fCurve.ptAtT(midT);
1299 SkTCoincident coin;
1300 coin.setPerp(fCurve, midT, midPt, sect2->fCurve);
1301 if (coin.isMatch()) {
1302 smaller->fEndT = larger->fEndT;
1303 smaller->fCoinEnd = larger->fCoinEnd;
1304 if (largerPrior) {
1305 largerPrior->fNext = larger->fNext;
1306 largerPrior->validate();
1307 } else {
1308 fCoincident = larger->fNext;
1309 }
1310 }
1311 } while (true);
1312}
1313
1314SkTSpan* SkTSect::prev(
1315 SkTSpan* span) const {
1316 SkTSpan* result = nullptr;
1317 SkTSpan* test = fHead;
1318 while (span != test) {
1319 result = test;
1320 test = test->fNext;
1321 SkASSERT(test);
1322 }
1323 return result;
1324}
1325
1326void SkTSect::recoverCollapsed() {
1327 SkTSpan* deleted = fDeleted;
1328 while (deleted) {
1329 SkTSpan* delNext = deleted->fNext;
1330 if (deleted->fCollapsed) {
1331 SkTSpan** spanPtr = &fHead;
1332 while (*spanPtr && (*spanPtr)->fEndT <= deleted->fStartT) {
1333 spanPtr = &(*spanPtr)->fNext;
1334 }
1335 deleted->fNext = *spanPtr;
1336 *spanPtr = deleted;
1337 }
1338 deleted = delNext;
1339 }
1340}
1341
1342void SkTSect::removeAllBut(const SkTSpan* keep,
1343 SkTSpan* span, SkTSect* opp) {
1344 const SkTSpanBounded* testBounded = span->fBounded;
1345 while (testBounded) {
1346 SkTSpan* bounded = testBounded->fBounded;
1347 const SkTSpanBounded* next = testBounded->fNext;
1348 // may have been deleted when opp did 'remove all but'
1349 if (bounded != keep && !bounded->fDeleted) {
1350 SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
1351 if (bounded->removeBounded(span)) {
1352 opp->removeSpan(bounded);
1353 }
1354 }
1355 testBounded = next;
1356 }
1357 SkASSERT(!span->fDeleted);
1358 SkASSERT(span->findOppSpan(keep));
1359 SkASSERT(keep->findOppSpan(span));
1360}
1361
1362bool SkTSect::removeByPerpendicular(SkTSect* opp) {
1363 SkTSpan* test = fHead;
1364 SkTSpan* next;
1365 do {
1366 next = test->fNext;
1367 if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) {
1368 continue;
1369 }
1370 SkDVector startV = test->fCoinStart.perpPt() - test->pointFirst();
1371 SkDVector endV = test->fCoinEnd.perpPt() - test->pointLast();
1372#if DEBUG_T_SECT
1373 SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__,
1374 startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV));
1375#endif
1376 if (startV.dot(endV) <= 0) {
1377 continue;
1378 }
1379 if (!this->removeSpans(test, opp)) {
1380 return false;
1381 }
1382 } while ((test = next));
1383 return true;
1384}
1385
1386bool SkTSect::removeCoincident(SkTSpan* span, bool isBetween) {
1387 if (!this->unlinkSpan(span)) {
1388 return false;
1389 }
1390 if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
1391 --fActiveCount;
1392 span->fNext = fCoincident;
1393 fCoincident = span;
1394 } else {
1395 this->markSpanGone(span);
1396 }
1397 return true;
1398}
1399
1400void SkTSect::removedEndCheck(SkTSpan* span) {
1401 if (!span->fStartT) {
1402 fRemovedStartT = true;
1403 }
1404 if (1 == span->fEndT) {
1405 fRemovedEndT = true;
1406 }
1407}
1408
1409bool SkTSect::removeSpan(SkTSpan* span) {\
1410 this->removedEndCheck(span);
1411 if (!this->unlinkSpan(span)) {
1412 return false;
1413 }
1414 return this->markSpanGone(span);
1415}
1416
1417void SkTSect::removeSpanRange(SkTSpan* first,
1418 SkTSpan* last) {
1419 if (first == last) {
1420 return;
1421 }
1422 SkTSpan* span = first;
1423 SkASSERT(span);
1424 SkTSpan* final = last->fNext;
1425 SkTSpan* next = span->fNext;
1426 while ((span = next) && span != final) {
1427 next = span->fNext;
1428 this->markSpanGone(span);
1429 }
1430 if (final) {
1431 final->fPrev = first;
1432 }
1433 first->fNext = final;
1434 // world may not be ready for validation here
1435 first->validate();
1436}
1437
1438bool SkTSect::removeSpans(SkTSpan* span,
1439 SkTSect* opp) {
1440 SkTSpanBounded* bounded = span->fBounded;
1441 while (bounded) {
1442 SkTSpan* spanBounded = bounded->fBounded;
1443 SkTSpanBounded* next = bounded->fNext;
1444 if (span->removeBounded(spanBounded)) { // shuffles last into position 0
1445 this->removeSpan(span);
1446 }
1447 if (spanBounded->removeBounded(span)) {
1448 opp->removeSpan(spanBounded);
1449 }
1450 if (span->fDeleted && opp->hasBounded(span)) {
1451 return false;
1452 }
1453 bounded = next;
1454 }
1455 return true;
1456}
1457
1458SkTSpan* SkTSect::spanAtT(double t,
1459 SkTSpan** priorSpan) {
1460 SkTSpan* test = fHead;
1461 SkTSpan* prev = nullptr;
1462 while (test && test->fEndT < t) {
1463 prev = test;
1464 test = test->fNext;
1465 }
1466 *priorSpan = prev;
1467 return test && test->fStartT <= t ? test : nullptr;
1468}
1469
1470SkTSpan* SkTSect::tail() {
1471 SkTSpan* result = fHead;
1472 SkTSpan* next = fHead;
Cary Clark4929a4a2018-10-17 14:12:41 -04001473 int safetyNet = 100000;
Cary Clark8762fb62018-10-16 16:06:24 -04001474 while ((next = next->fNext)) {
Cary Clark4929a4a2018-10-17 14:12:41 -04001475 if (!--safetyNet) {
1476 return nullptr;
1477 }
Cary Clark8762fb62018-10-16 16:06:24 -04001478 if (next->fEndT > result->fEndT) {
1479 result = next;
1480 }
1481 }
1482 return result;
1483}
1484
1485/* Each span has a range of opposite spans it intersects. After the span is split in two,
1486 adjust the range to its new size */
1487
1488bool SkTSect::trim(SkTSpan* span,
1489 SkTSect* opp) {
1490 FAIL_IF(!span->initBounds(fCurve));
1491 const SkTSpanBounded* testBounded = span->fBounded;
1492 while (testBounded) {
1493 SkTSpan* test = testBounded->fBounded;
1494 const SkTSpanBounded* next = testBounded->fNext;
1495 int oppSects, sects = this->intersects(span, opp, test, &oppSects);
1496 if (sects >= 1) {
1497 if (oppSects == 2) {
1498 test->initBounds(opp->fCurve);
1499 opp->removeAllBut(span, test, this);
1500 }
1501 if (sects == 2) {
1502 span->initBounds(fCurve);
1503 this->removeAllBut(test, span, opp);
1504 return true;
1505 }
1506 } else {
1507 if (span->removeBounded(test)) {
1508 this->removeSpan(span);
1509 }
1510 if (test->removeBounded(span)) {
1511 opp->removeSpan(test);
1512 }
1513 }
1514 testBounded = next;
1515 }
1516 return true;
1517}
1518
1519bool SkTSect::unlinkSpan(SkTSpan* span) {
1520 SkTSpan* prev = span->fPrev;
1521 SkTSpan* next = span->fNext;
1522 if (prev) {
1523 prev->fNext = next;
1524 if (next) {
1525 next->fPrev = prev;
1526 if (next->fStartT > next->fEndT) {
1527 return false;
1528 }
1529 // world may not be ready for validate here
1530 next->validate();
1531 }
1532 } else {
1533 fHead = next;
1534 if (next) {
1535 next->fPrev = nullptr;
1536 }
1537 }
1538 return true;
1539}
1540
1541bool SkTSect::updateBounded(SkTSpan* first,
1542 SkTSpan* last, SkTSpan* oppFirst) {
1543 SkTSpan* test = first;
1544 const SkTSpan* final = last->next();
1545 bool deleteSpan = false;
1546 do {
1547 deleteSpan |= test->removeAllBounded();
1548 } while ((test = test->fNext) != final && test);
1549 first->fBounded = nullptr;
1550 first->addBounded(oppFirst, &fHeap);
1551 // cannot call validate until remove span range is called
1552 return deleteSpan;
1553}
1554
1555void SkTSect::validate() const {
1556#if DEBUG_VALIDATE
1557 int count = 0;
1558 double last = 0;
1559 if (fHead) {
1560 const SkTSpan* span = fHead;
1561 SkASSERT(!span->fPrev);
1562 const SkTSpan* next;
1563 do {
1564 span->validate();
1565 SkASSERT(span->fStartT >= last);
1566 last = span->fEndT;
1567 ++count;
1568 next = span->fNext;
1569 SkASSERT(next != span);
1570 } while ((span = next) != nullptr);
1571 }
1572 SkASSERT(count == fActiveCount);
1573#endif
1574#if DEBUG_T_SECT
1575 SkASSERT(fActiveCount <= fDebugAllocatedCount);
1576 int deletedCount = 0;
1577 const SkTSpan* deleted = fDeleted;
1578 while (deleted) {
1579 ++deletedCount;
1580 deleted = deleted->fNext;
1581 }
1582 const SkTSpan* coincident = fCoincident;
1583 while (coincident) {
1584 ++deletedCount;
1585 coincident = coincident->fNext;
1586 }
1587 SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
1588#endif
1589}
1590
1591void SkTSect::validateBounded() const {
1592#if DEBUG_VALIDATE
1593 if (!fHead) {
1594 return;
1595 }
1596 const SkTSpan* span = fHead;
1597 do {
1598 span->validateBounded();
1599 } while ((span = span->fNext) != nullptr);
1600#endif
1601}
1602
1603int SkTSect::EndsEqual(const SkTSect* sect1,
1604 const SkTSect* sect2, SkIntersections* intersections) {
1605 int zeroOneSet = 0;
1606 if (sect1->fCurve[0] == sect2->fCurve[0]) {
1607 zeroOneSet |= kZeroS1Set | kZeroS2Set;
1608 intersections->insert(0, 0, sect1->fCurve[0]);
1609 }
1610 if (sect1->fCurve[0] == sect2->pointLast()) {
1611 zeroOneSet |= kZeroS1Set | kOneS2Set;
1612 intersections->insert(0, 1, sect1->fCurve[0]);
1613 }
1614 if (sect1->pointLast() == sect2->fCurve[0]) {
1615 zeroOneSet |= kOneS1Set | kZeroS2Set;
1616 intersections->insert(1, 0, sect1->pointLast());
1617 }
1618 if (sect1->pointLast() == sect2->pointLast()) {
1619 zeroOneSet |= kOneS1Set | kOneS2Set;
1620 intersections->insert(1, 1, sect1->pointLast());
1621 }
1622 // check for zero
1623 if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
1624 && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
1625 zeroOneSet |= kZeroS1Set | kZeroS2Set;
1626 intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
1627 }
1628 if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
1629 && sect1->fCurve[0].approximatelyEqual(sect2->pointLast())) {
1630 zeroOneSet |= kZeroS1Set | kOneS2Set;
1631 intersections->insertNear(0, 1, sect1->fCurve[0], sect2->pointLast());
1632 }
1633 // check for one
1634 if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
1635 && sect1->pointLast().approximatelyEqual(sect2->fCurve[0])) {
1636 zeroOneSet |= kOneS1Set | kZeroS2Set;
1637 intersections->insertNear(1, 0, sect1->pointLast(), sect2->fCurve[0]);
1638 }
1639 if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
1640 && sect1->pointLast().approximatelyEqual(sect2->pointLast())) {
1641 zeroOneSet |= kOneS1Set | kOneS2Set;
1642 intersections->insertNear(1, 1, sect1->pointLast(), sect2->pointLast());
1643 }
1644 return zeroOneSet;
1645}
1646
1647struct SkClosestRecord {
1648 bool operator<(const SkClosestRecord& rh) const {
1649 return fClosest < rh.fClosest;
1650 }
1651
1652 void addIntersection(SkIntersections* intersections) const {
1653 double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT();
1654 double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT();
1655 intersections->insert(r1t, r2t, fC1Span->part()[fC1Index]);
1656 }
1657
1658 void findEnd(const SkTSpan* span1, const SkTSpan* span2,
1659 int c1Index, int c2Index) {
1660 const SkTCurve& c1 = span1->part();
1661 const SkTCurve& c2 = span2->part();
1662 if (!c1[c1Index].approximatelyEqual(c2[c2Index])) {
1663 return;
1664 }
1665 double dist = c1[c1Index].distanceSquared(c2[c2Index]);
1666 if (fClosest < dist) {
1667 return;
1668 }
1669 fC1Span = span1;
1670 fC2Span = span2;
1671 fC1StartT = span1->startT();
1672 fC1EndT = span1->endT();
1673 fC2StartT = span2->startT();
1674 fC2EndT = span2->endT();
1675 fC1Index = c1Index;
1676 fC2Index = c2Index;
1677 fClosest = dist;
1678 }
1679
1680 bool matesWith(const SkClosestRecord& mate SkDEBUGPARAMS(SkIntersections* i)) const {
1681 SkOPOBJASSERT(i, fC1Span == mate.fC1Span || fC1Span->endT() <= mate.fC1Span->startT()
1682 || mate.fC1Span->endT() <= fC1Span->startT());
1683 SkOPOBJASSERT(i, fC2Span == mate.fC2Span || fC2Span->endT() <= mate.fC2Span->startT()
1684 || mate.fC2Span->endT() <= fC2Span->startT());
1685 return fC1Span == mate.fC1Span || fC1Span->endT() == mate.fC1Span->startT()
1686 || fC1Span->startT() == mate.fC1Span->endT()
1687 || fC2Span == mate.fC2Span
1688 || fC2Span->endT() == mate.fC2Span->startT()
1689 || fC2Span->startT() == mate.fC2Span->endT();
1690 }
1691
1692 void merge(const SkClosestRecord& mate) {
1693 fC1Span = mate.fC1Span;
1694 fC2Span = mate.fC2Span;
1695 fClosest = mate.fClosest;
1696 fC1Index = mate.fC1Index;
1697 fC2Index = mate.fC2Index;
1698 }
1699
1700 void reset() {
1701 fClosest = FLT_MAX;
1702 SkDEBUGCODE(fC1Span = nullptr);
1703 SkDEBUGCODE(fC2Span = nullptr);
1704 SkDEBUGCODE(fC1Index = fC2Index = -1);
1705 }
1706
1707 void update(const SkClosestRecord& mate) {
Brian Osman788b9162020-02-07 10:36:46 -05001708 fC1StartT = std::min(fC1StartT, mate.fC1StartT);
1709 fC1EndT = std::max(fC1EndT, mate.fC1EndT);
1710 fC2StartT = std::min(fC2StartT, mate.fC2StartT);
1711 fC2EndT = std::max(fC2EndT, mate.fC2EndT);
Cary Clark8762fb62018-10-16 16:06:24 -04001712 }
1713
1714 const SkTSpan* fC1Span;
1715 const SkTSpan* fC2Span;
1716 double fC1StartT;
1717 double fC1EndT;
1718 double fC2StartT;
1719 double fC2EndT;
1720 double fClosest;
1721 int fC1Index;
1722 int fC2Index;
1723};
1724
1725struct SkClosestSect {
1726 SkClosestSect()
1727 : fUsed(0) {
1728 fClosest.push_back().reset();
1729 }
1730
1731 bool find(const SkTSpan* span1, const SkTSpan* span2
1732 SkDEBUGPARAMS(SkIntersections* i)) {
1733 SkClosestRecord* record = &fClosest[fUsed];
1734 record->findEnd(span1, span2, 0, 0);
1735 record->findEnd(span1, span2, 0, span2->part().pointLast());
1736 record->findEnd(span1, span2, span1->part().pointLast(), 0);
1737 record->findEnd(span1, span2, span1->part().pointLast(), span2->part().pointLast());
1738 if (record->fClosest == FLT_MAX) {
1739 return false;
1740 }
1741 for (int index = 0; index < fUsed; ++index) {
1742 SkClosestRecord* test = &fClosest[index];
1743 if (test->matesWith(*record SkDEBUGPARAMS(i))) {
1744 if (test->fClosest > record->fClosest) {
1745 test->merge(*record);
1746 }
1747 test->update(*record);
1748 record->reset();
1749 return false;
1750 }
1751 }
1752 ++fUsed;
1753 fClosest.push_back().reset();
1754 return true;
1755 }
1756
1757 void finish(SkIntersections* intersections) const {
John Stiles6e9ead92020-07-14 00:13:51 +00001758 SkSTArray<SkDCubic::kMaxIntersections * 3,
1759 const SkClosestRecord*, true> closestPtrs;
Cary Clark8762fb62018-10-16 16:06:24 -04001760 for (int index = 0; index < fUsed; ++index) {
1761 closestPtrs.push_back(&fClosest[index]);
1762 }
John Stiles6e9ead92020-07-14 00:13:51 +00001763 SkTQSort<const SkClosestRecord >(closestPtrs.begin(), closestPtrs.end()
1764 - 1);
Cary Clark8762fb62018-10-16 16:06:24 -04001765 for (int index = 0; index < fUsed; ++index) {
1766 const SkClosestRecord* test = closestPtrs[index];
1767 test->addIntersection(intersections);
1768 }
1769 }
1770
1771 // this is oversized so that an extra records can merge into final one
1772 SkSTArray<SkDCubic::kMaxIntersections * 2, SkClosestRecord, true> fClosest;
1773 int fUsed;
1774};
1775
1776// returns true if the rect is too small to consider
1777
1778void SkTSect::BinarySearch(SkTSect* sect1,
1779 SkTSect* sect2, SkIntersections* intersections) {
1780#if DEBUG_T_SECT_DUMP > 1
1781 gDumpTSectNum = 0;
1782#endif
1783 SkDEBUGCODE(sect1->fOppSect = sect2);
1784 SkDEBUGCODE(sect2->fOppSect = sect1);
1785 intersections->reset();
1786 intersections->setMax(sect1->fCurve.maxIntersections() + 4); // give extra for slop
1787 SkTSpan* span1 = sect1->fHead;
1788 SkTSpan* span2 = sect2->fHead;
1789 int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
1790// SkASSERT(between(0, sect, 2));
1791 if (!sect) {
1792 return;
1793 }
1794 if (sect == 2 && oppSect == 2) {
1795 (void) EndsEqual(sect1, sect2, intersections);
1796 return;
1797 }
1798 span1->addBounded(span2, &sect1->fHeap);
1799 span2->addBounded(span1, &sect2->fHeap);
1800 const int kMaxCoinLoopCount = 8;
1801 int coinLoopCount = kMaxCoinLoopCount;
1802 double start1s SK_INIT_TO_AVOID_WARNING;
1803 double start1e SK_INIT_TO_AVOID_WARNING;
1804 do {
1805 // find the largest bounds
1806 SkTSpan* largest1 = sect1->boundsMax();
1807 if (!largest1) {
1808 if (sect1->fHung) {
1809 return;
1810 }
1811 break;
1812 }
1813 SkTSpan* largest2 = sect2->boundsMax();
1814 // split it
1815 if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
1816 || (!largest1->fCollapsed && largest2->fCollapsed)))) {
1817 if (sect2->fHung) {
1818 return;
1819 }
1820 if (largest1->fCollapsed) {
1821 break;
1822 }
1823 sect1->resetRemovedEnds();
1824 sect2->resetRemovedEnds();
1825 // trim parts that don't intersect the opposite
1826 SkTSpan* half1 = sect1->addOne();
1827 SkDEBUGCODE(half1->debugSetGlobalState(sect1->globalState()));
1828 if (!half1->split(largest1, &sect1->fHeap)) {
1829 break;
1830 }
1831 if (!sect1->trim(largest1, sect2)) {
1832 SkOPOBJASSERT(intersections, 0);
1833 return;
1834 }
1835 if (!sect1->trim(half1, sect2)) {
1836 SkOPOBJASSERT(intersections, 0);
1837 return;
1838 }
1839 } else {
1840 if (largest2->fCollapsed) {
1841 break;
1842 }
1843 sect1->resetRemovedEnds();
1844 sect2->resetRemovedEnds();
1845 // trim parts that don't intersect the opposite
1846 SkTSpan* half2 = sect2->addOne();
1847 SkDEBUGCODE(half2->debugSetGlobalState(sect2->globalState()));
1848 if (!half2->split(largest2, &sect2->fHeap)) {
1849 break;
1850 }
1851 if (!sect2->trim(largest2, sect1)) {
1852 SkOPOBJASSERT(intersections, 0);
1853 return;
1854 }
1855 if (!sect2->trim(half2, sect1)) {
1856 SkOPOBJASSERT(intersections, 0);
1857 return;
1858 }
1859 }
1860 sect1->validate();
1861 sect2->validate();
1862#if DEBUG_T_SECT_LOOP_COUNT
1863 intersections->debugBumpLoopCount(SkIntersections::kIterations_DebugLoop);
1864#endif
1865 // if there are 9 or more continuous spans on both sects, suspect coincidence
1866 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
1867 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
1868 if (coinLoopCount == kMaxCoinLoopCount) {
1869 start1s = sect1->fHead->fStartT;
1870 start1e = sect1->tail()->fEndT;
1871 }
1872 if (!sect1->coincidentCheck(sect2)) {
1873 return;
1874 }
1875 sect1->validate();
1876 sect2->validate();
1877#if DEBUG_T_SECT_LOOP_COUNT
1878 intersections->debugBumpLoopCount(SkIntersections::kCoinCheck_DebugLoop);
1879#endif
1880 if (!--coinLoopCount && sect1->fHead && sect2->fHead) {
1881 /* All known working cases resolve in two tries. Sadly, cubicConicTests[0]
1882 gets stuck in a loop. It adds an extension to allow a coincident end
1883 perpendicular to track its intersection in the opposite curve. However,
1884 the bounding box of the extension does not intersect the original curve,
1885 so the extension is discarded, only to be added again the next time around. */
1886 sect1->coincidentForce(sect2, start1s, start1e);
1887 sect1->validate();
1888 sect2->validate();
1889 }
1890 }
1891 if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
1892 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
1893 if (!sect1->fHead) {
1894 return;
1895 }
1896 sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
1897 if (!sect2->fHead) {
1898 return;
1899 }
1900 sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
1901 if (!sect1->removeByPerpendicular(sect2)) {
1902 return;
1903 }
1904 sect1->validate();
1905 sect2->validate();
1906#if DEBUG_T_SECT_LOOP_COUNT
1907 intersections->debugBumpLoopCount(SkIntersections::kComputePerp_DebugLoop);
1908#endif
1909 if (sect1->collapsed() > sect1->fCurve.maxIntersections()) {
1910 break;
1911 }
1912 }
1913#if DEBUG_T_SECT_DUMP
1914 sect1->dumpBoth(sect2);
1915#endif
1916 if (!sect1->fHead || !sect2->fHead) {
1917 break;
1918 }
1919 } while (true);
1920 SkTSpan* coincident = sect1->fCoincident;
1921 if (coincident) {
1922 // if there is more than one coincident span, check loosely to see if they should be joined
1923 if (coincident->fNext) {
1924 sect1->mergeCoincidence(sect2);
1925 coincident = sect1->fCoincident;
1926 }
1927 SkASSERT(sect2->fCoincident); // courtesy check : coincidence only looks at sect 1
1928 do {
1929 if (!coincident) {
1930 return;
1931 }
1932 if (!coincident->fCoinStart.isMatch()) {
1933 continue;
1934 }
1935 if (!coincident->fCoinEnd.isMatch()) {
1936 continue;
1937 }
1938 double perpT = coincident->fCoinStart.perpT();
1939 if (perpT < 0) {
1940 return;
1941 }
1942 int index = intersections->insertCoincident(coincident->fStartT,
1943 perpT, coincident->pointFirst());
1944 if ((intersections->insertCoincident(coincident->fEndT,
1945 coincident->fCoinEnd.perpT(),
1946 coincident->pointLast()) < 0) && index >= 0) {
1947 intersections->clearCoincidence(index);
1948 }
1949 } while ((coincident = coincident->fNext));
1950 }
1951 int zeroOneSet = EndsEqual(sect1, sect2, intersections);
1952// if (!sect1->fHead || !sect2->fHead) {
1953 // if the final iteration contains an end (0 or 1),
1954 if (sect1->fRemovedStartT && !(zeroOneSet & kZeroS1Set)) {
1955 SkTCoincident perp; // intersect perpendicular with opposite curve
1956 perp.setPerp(sect1->fCurve, 0, sect1->fCurve[0], sect2->fCurve);
1957 if (perp.isMatch()) {
1958 intersections->insert(0, perp.perpT(), perp.perpPt());
1959 }
1960 }
1961 if (sect1->fRemovedEndT && !(zeroOneSet & kOneS1Set)) {
1962 SkTCoincident perp;
1963 perp.setPerp(sect1->fCurve, 1, sect1->pointLast(), sect2->fCurve);
1964 if (perp.isMatch()) {
1965 intersections->insert(1, perp.perpT(), perp.perpPt());
1966 }
1967 }
1968 if (sect2->fRemovedStartT && !(zeroOneSet & kZeroS2Set)) {
1969 SkTCoincident perp;
1970 perp.setPerp(sect2->fCurve, 0, sect2->fCurve[0], sect1->fCurve);
1971 if (perp.isMatch()) {
1972 intersections->insert(perp.perpT(), 0, perp.perpPt());
1973 }
1974 }
1975 if (sect2->fRemovedEndT && !(zeroOneSet & kOneS2Set)) {
1976 SkTCoincident perp;
1977 perp.setPerp(sect2->fCurve, 1, sect2->pointLast(), sect1->fCurve);
1978 if (perp.isMatch()) {
1979 intersections->insert(perp.perpT(), 1, perp.perpPt());
1980 }
1981 }
1982// }
1983 if (!sect1->fHead || !sect2->fHead) {
1984 return;
1985 }
1986 sect1->recoverCollapsed();
1987 sect2->recoverCollapsed();
1988 SkTSpan* result1 = sect1->fHead;
1989 // check heads and tails for zero and ones and insert them if we haven't already done so
1990 const SkTSpan* head1 = result1;
1991 if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
1992 const SkDPoint& start1 = sect1->fCurve[0];
1993 if (head1->isBounded()) {
1994 double t = head1->closestBoundedT(start1);
1995 if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
1996 intersections->insert(0, t, start1);
1997 }
1998 }
1999 }
2000 const SkTSpan* head2 = sect2->fHead;
2001 if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
2002 const SkDPoint& start2 = sect2->fCurve[0];
2003 if (head2->isBounded()) {
2004 double t = head2->closestBoundedT(start2);
2005 if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
2006 intersections->insert(t, 0, start2);
2007 }
2008 }
2009 }
Cary Clark7d988462018-10-25 09:14:21 -04002010 if (!(zeroOneSet & kOneS1Set)) {
2011 const SkTSpan* tail1 = sect1->tail();
2012 if (!tail1) {
2013 return;
2014 }
2015 if (approximately_greater_than_one(tail1->fEndT)) {
2016 const SkDPoint& end1 = sect1->pointLast();
2017 if (tail1->isBounded()) {
2018 double t = tail1->closestBoundedT(end1);
2019 if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
2020 intersections->insert(1, t, end1);
2021 }
Cary Clark8762fb62018-10-16 16:06:24 -04002022 }
2023 }
2024 }
Cary Clark7d988462018-10-25 09:14:21 -04002025 if (!(zeroOneSet & kOneS2Set)) {
2026 const SkTSpan* tail2 = sect2->tail();
2027 if (!tail2) {
2028 return;
2029 }
2030 if (approximately_greater_than_one(tail2->fEndT)) {
2031 const SkDPoint& end2 = sect2->pointLast();
2032 if (tail2->isBounded()) {
2033 double t = tail2->closestBoundedT(end2);
2034 if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
2035 intersections->insert(t, 1, end2);
2036 }
Cary Clark8762fb62018-10-16 16:06:24 -04002037 }
2038 }
2039 }
2040 SkClosestSect closest;
2041 do {
2042 while (result1 && result1->fCoinStart.isMatch() && result1->fCoinEnd.isMatch()) {
2043 result1 = result1->fNext;
2044 }
2045 if (!result1) {
2046 break;
2047 }
2048 SkTSpan* result2 = sect2->fHead;
2049 bool found = false;
2050 while (result2) {
2051 found |= closest.find(result1, result2 SkDEBUGPARAMS(intersections));
2052 result2 = result2->fNext;
2053 }
2054 } while ((result1 = result1->fNext));
2055 closest.finish(intersections);
2056 // if there is more than one intersection and it isn't already coincident, check
2057 int last = intersections->used() - 1;
2058 for (int index = 0; index < last; ) {
2059 if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
2060 ++index;
2061 continue;
2062 }
2063 double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
2064 SkDPoint midPt = sect1->fCurve.ptAtT(midT);
2065 // intersect perpendicular with opposite curve
2066 SkTCoincident perp;
2067 perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve);
2068 if (!perp.isMatch()) {
2069 ++index;
2070 continue;
2071 }
2072 if (intersections->isCoincident(index)) {
2073 intersections->removeOne(index);
2074 --last;
2075 } else if (intersections->isCoincident(index + 1)) {
2076 intersections->removeOne(index + 1);
2077 --last;
2078 } else {
2079 intersections->setCoincident(index++);
2080 }
2081 intersections->setCoincident(index);
2082 }
2083 SkOPOBJASSERT(intersections, intersections->used() <= sect1->fCurve.maxIntersections());
2084}
Cary Clark0a671982018-10-11 12:16:49 -04002085
2086int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
2087 SkTQuad quad1(q1);
2088 SkTQuad quad2(q2);
Cary Clark8762fb62018-10-16 16:06:24 -04002089 SkTSect sect1(quad1 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2090 SkTSect sect2(quad2 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2091 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002092 return used();
2093}
2094
2095int SkIntersections::intersect(const SkDConic& c, const SkDQuad& q) {
2096 SkTConic conic(c);
2097 SkTQuad quad(q);
Cary Clark8762fb62018-10-16 16:06:24 -04002098 SkTSect sect1(conic SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2099 SkTSect sect2(quad SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2100 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002101 return used();
2102}
2103
2104int SkIntersections::intersect(const SkDConic& c1, const SkDConic& c2) {
2105 SkTConic conic1(c1);
2106 SkTConic conic2(c2);
Cary Clark8762fb62018-10-16 16:06:24 -04002107 SkTSect sect1(conic1 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2108 SkTSect sect2(conic2 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2109 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002110 return used();
2111}
2112
2113int SkIntersections::intersect(const SkDCubic& c, const SkDQuad& q) {
2114 SkTCubic cubic(c);
2115 SkTQuad quad(q);
Cary Clark8762fb62018-10-16 16:06:24 -04002116 SkTSect sect1(cubic SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2117 SkTSect sect2(quad SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2118 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002119 return used();
2120}
2121
2122int SkIntersections::intersect(const SkDCubic& cu, const SkDConic& co) {
2123 SkTCubic cubic(cu);
2124 SkTConic conic(co);
Cary Clark8762fb62018-10-16 16:06:24 -04002125 SkTSect sect1(cubic SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2126 SkTSect sect2(conic SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2127 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002128 return used();
2129
2130}
2131
2132int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
2133 SkTCubic cubic1(c1);
2134 SkTCubic cubic2(c2);
Cary Clark8762fb62018-10-16 16:06:24 -04002135 SkTSect sect1(cubic1 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(1));
2136 SkTSect sect2(cubic2 SkDEBUGPARAMS(globalState()) PATH_OPS_DEBUG_T_SECT_PARAMS(2));
2137 SkTSect::BinarySearch(&sect1, &sect2, this);
Cary Clark0a671982018-10-11 12:16:49 -04002138 return used();
2139}