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