Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkPolyUtils.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 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
10#include "include/core/SkRect.h"
18#include "src/base/SkTDPQueue.h"
20#include "src/base/SkVx.h"
22#include "src/core/SkRectPriv.h"
23
24#include <algorithm>
25#include <cstdint>
26#include <limits>
27#include <new>
28
29using namespace skia_private;
30
31#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
32
33//////////////////////////////////////////////////////////////////////////////////
34// Helper data structures and functions
35
40
42
43// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
44// A positive value means the point is to the left of the segment,
45// negative is to the right, 0 is collinear.
46static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
47 SkVector w = p - p0;
48 SkScalar perpDot = v.cross(w);
49 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
50 return ((perpDot > 0) ? 1 : -1);
51 }
52
53 return 0;
54}
55
56// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
57int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
58 if (polygonSize < 3) {
59 return 0;
60 }
61
62 // compute area and use sign to determine winding
63 SkScalar quadArea = 0;
64 SkVector v0 = polygonVerts[1] - polygonVerts[0];
65 for (int curr = 2; curr < polygonSize; ++curr) {
66 SkVector v1 = polygonVerts[curr] - polygonVerts[0];
67 quadArea += v0.cross(v1);
68 v0 = v1;
69 }
70 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
71 return 0;
72 }
73 // 1 == ccw, -1 == cw
74 return (quadArea > 0) ? 1 : -1;
75}
76
77// Compute difference vector to offset p0-p1 'offset' units in direction specified by 'side'
78bool compute_offset_vector(const SkPoint& p0, const SkPoint& p1, SkScalar offset, int side,
79 SkPoint* vector) {
80 SkASSERT(side == -1 || side == 1);
81 // if distances are equal, can just outset by the perpendicular
82 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
83 if (!perp.setLength(offset*side)) {
84 return false;
85 }
86 *vector = perp;
87 return true;
88}
89
90// check interval to see if intersection is in segment
91static inline bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive) {
92 return (denomPositive && (numer < 0 || numer > denom)) ||
93 (!denomPositive && (numer > 0 || numer < denom));
94}
95
96// special zero-length test when we're using vdotv as a denominator
97static inline bool zero_length(const SkPoint& v, SkScalar vdotv) {
98 return !(SkIsFinite(v.fX, v.fY) && vdotv);
99}
100
101// Compute the intersection 'p' between segments s0 and s1, if any.
102// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
103// Returns false if there is no intersection.
104// If the length squared of a segment is 0, then we treat the segment as degenerate
105// and use only the first endpoint for tests.
106static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
107 SkPoint* p, SkScalar* s, SkScalar* t) {
108 const SkVector& v0 = s0.fV;
109 const SkVector& v1 = s1.fV;
110 SkVector w = s1.fP0 - s0.fP0;
111 SkScalar denom = v0.cross(v1);
112 bool denomPositive = (denom > 0);
113 SkScalar sNumer, tNumer;
115 // segments are parallel, but not collinear
116 if (!SkScalarNearlyZero(w.cross(v0), kCrossTolerance) ||
117 !SkScalarNearlyZero(w.cross(v1), kCrossTolerance)) {
118 return false;
119 }
120
121 // Check for zero-length segments
122 SkScalar v0dotv0 = v0.dot(v0);
123 if (zero_length(v0, v0dotv0)) {
124 // Both are zero-length
125 SkScalar v1dotv1 = v1.dot(v1);
126 if (zero_length(v1, v1dotv1)) {
127 // Check if they're the same point
128 if (!SkPointPriv::CanNormalize(w.fX, w.fY)) {
129 *p = s0.fP0;
130 *s = 0;
131 *t = 0;
132 return true;
133 } else {
134 // Intersection is indeterminate
135 return false;
136 }
137 }
138 // Otherwise project segment0's origin onto segment1
139 tNumer = v1.dot(-w);
140 denom = v1dotv1;
141 if (outside_interval(tNumer, denom, true)) {
142 return false;
143 }
144 sNumer = 0;
145 } else {
146 // Project segment1's endpoints onto segment0
147 sNumer = v0.dot(w);
148 denom = v0dotv0;
149 tNumer = 0;
150 if (outside_interval(sNumer, denom, true)) {
151 // The first endpoint doesn't lie on segment0
152 // If segment1 is degenerate, then there's no collision
153 SkScalar v1dotv1 = v1.dot(v1);
154 if (zero_length(v1, v1dotv1)) {
155 return false;
156 }
157
158 // Otherwise try the other one
159 SkScalar oldSNumer = sNumer;
160 sNumer = v0.dot(w + v1);
161 tNumer = denom;
162 if (outside_interval(sNumer, denom, true)) {
163 // it's possible that segment1's interval surrounds segment0
164 // this is false if params have the same signs, and in that case no collision
165 if (sNumer*oldSNumer > 0) {
166 return false;
167 }
168 // otherwise project segment0's endpoint onto segment1 instead
169 sNumer = 0;
170 tNumer = v1.dot(-w);
171 denom = v1dotv1;
172 }
173 }
174 }
175 } else {
176 sNumer = w.cross(v1);
177 if (outside_interval(sNumer, denom, denomPositive)) {
178 return false;
179 }
180 tNumer = w.cross(v0);
181 if (outside_interval(tNumer, denom, denomPositive)) {
182 return false;
183 }
184 }
185
186 SkScalar localS = sNumer/denom;
187 SkScalar localT = tNumer/denom;
188
189 *p = s0.fP0 + v0*localS;
190 *s = localS;
191 *t = localT;
192
193 return true;
194}
195
196bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
197 if (polygonSize < 3) {
198 return false;
199 }
200
201 SkScalar lastPerpDot = 0;
202 int xSignChangeCount = 0;
203 int ySignChangeCount = 0;
204
205 int prevIndex = polygonSize - 1;
206 int currIndex = 0;
207 int nextIndex = 1;
208 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
209 SkScalar lastVx = v0.fX;
210 SkScalar lastVy = v0.fY;
211 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
212 for (int i = 0; i < polygonSize; ++i) {
213 if (!polygonVerts[i].isFinite()) {
214 return false;
215 }
216
217 // Check that winding direction is always the same (otherwise we have a reflex vertex)
218 SkScalar perpDot = v0.cross(v1);
219 if (lastPerpDot*perpDot < 0) {
220 return false;
221 }
222 if (0 != perpDot) {
223 lastPerpDot = perpDot;
224 }
225
226 // Check that the signs of the edge vectors don't change more than twice per coordinate
227 if (lastVx*v1.fX < 0) {
228 xSignChangeCount++;
229 }
230 if (lastVy*v1.fY < 0) {
231 ySignChangeCount++;
232 }
233 if (xSignChangeCount > 2 || ySignChangeCount > 2) {
234 return false;
235 }
236 prevIndex = currIndex;
237 currIndex = nextIndex;
238 nextIndex = (currIndex + 1) % polygonSize;
239 if (v1.fX != 0) {
240 lastVx = v1.fX;
241 }
242 if (v1.fY != 0) {
243 lastVy = v1.fY;
244 }
245 v0 = v1;
246 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
247 }
248
249 return true;
250}
251
258 uint16_t fIndex;
259 uint16_t fEnd;
260
261 void init(uint16_t start = 0, uint16_t end = 0) {
264 fIndex = start;
265 fEnd = end;
266 }
267
268 // special intersection check that looks for endpoint intersection
270 SkPoint* p, SkScalar* s, SkScalar* t) {
271 if (this->fEnd == that->fIndex) {
272 SkPoint p1 = this->fOffset.fP0 + this->fOffset.fV;
274 *p = p1;
275 *s = SK_Scalar1;
276 *t = 0;
277 return true;
278 }
279 }
280
281 return compute_intersection(this->fOffset, that->fOffset, p, s, t);
282 }
283
284 // computes the line intersection and then the "distance" from that to this
285 // this is really a signed squared distance, where negative means that
286 // the intersection lies inside this->fOffset
288 const OffsetSegment& s0 = this->fOffset;
289 const OffsetSegment& s1 = that->fOffset;
290 const SkVector& v0 = s0.fV;
291 const SkVector& v1 = s1.fV;
292
293 SkScalar denom = v0.cross(v1);
295 // segments are parallel
296 return SK_ScalarMax;
297 }
298
299 SkVector w = s1.fP0 - s0.fP0;
300 SkScalar localS = w.cross(v1) / denom;
301 if (localS < 0) {
302 localS = -localS;
303 } else {
304 localS -= SK_Scalar1;
305 }
306
307 localS *= SkScalarAbs(localS);
308 localS *= v0.dot(v0);
309
310 return localS;
311 }
312
313};
314
315static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
316 // remove from linked list
317 node->fPrev->fNext = node->fNext;
318 node->fNext->fPrev = node->fPrev;
319 if (node == *head) {
320 *head = (node->fNext == node) ? nullptr : node->fNext;
321 }
322}
323
324//////////////////////////////////////////////////////////////////////////////////
325
326// The objective here is to inset all of the edges by the given distance, and then
327// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
328// we should only be making left-hand turns (for cw polygons, we use the winding
329// parameter to reverse this). We detect this by checking whether the second intersection
330// on an edge is closer to its tail than the first one.
331//
332// We might also have the case that there is no intersection between two neighboring inset edges.
333// In this case, one edge will lie to the right of the other and should be discarded along with
334// its previous intersection (if any).
335//
336// Note: the assumption is that inputPolygon is convex and has no coincident points.
337//
338bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
339 SkScalar inset, SkTDArray<SkPoint>* insetPolygon) {
340 if (inputPolygonSize < 3) {
341 return false;
342 }
343
344 // restrict this to match other routines
345 // practically we don't want anything bigger than this anyway
346 if (inputPolygonSize > std::numeric_limits<uint16_t>::max()) {
347 return false;
348 }
349
350 // can't inset by a negative or non-finite amount
352 return false;
353 }
354
355 // insetting close to zero just returns the original poly
356 if (inset <= SK_ScalarNearlyZero) {
357 for (int i = 0; i < inputPolygonSize; ++i) {
358 *insetPolygon->append() = inputPolygonVerts[i];
359 }
360 return true;
361 }
362
363 // get winding direction
364 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
365 if (0 == winding) {
366 return false;
367 }
368
369 // set up
370 AutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
371 int prev = inputPolygonSize - 1;
372 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
373 int next = (curr + 1) % inputPolygonSize;
374 if (!inputPolygonVerts[curr].isFinite()) {
375 return false;
376 }
377 // check for convexity just to be sure
378 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
379 inputPolygonVerts[next])*winding < 0) {
380 return false;
381 }
382 SkVector v = inputPolygonVerts[next] - inputPolygonVerts[curr];
383 SkVector perp = SkVector::Make(-v.fY, v.fX);
384 perp.setLength(inset*winding);
385 edgeData[curr].fPrev = &edgeData[prev];
386 edgeData[curr].fNext = &edgeData[next];
387 edgeData[curr].fOffset.fP0 = inputPolygonVerts[curr] + perp;
388 edgeData[curr].fOffset.fV = v;
389 edgeData[curr].init();
390 }
391
392 OffsetEdge* head = &edgeData[0];
393 OffsetEdge* currEdge = head;
394 OffsetEdge* prevEdge = currEdge->fPrev;
395 int insetVertexCount = inputPolygonSize;
396 unsigned int iterations = 0;
397 unsigned int maxIterations = inputPolygonSize * inputPolygonSize;
398 while (head && prevEdge != currEdge) {
399 ++iterations;
400 // we should check each edge against each other edge at most once
401 if (iterations > maxIterations) {
402 return false;
403 }
404
405 SkScalar s, t;
406 SkPoint intersection;
407 if (compute_intersection(prevEdge->fOffset, currEdge->fOffset,
408 &intersection, &s, &t)) {
409 // if new intersection is further back on previous inset from the prior intersection
410 if (s < prevEdge->fTValue) {
411 // no point in considering this one again
412 remove_node(prevEdge, &head);
413 --insetVertexCount;
414 // go back one segment
415 prevEdge = prevEdge->fPrev;
416 // we've already considered this intersection, we're done
417 } else if (currEdge->fTValue > SK_ScalarMin &&
419 currEdge->fIntersection,
420 1.0e-6f)) {
421 break;
422 } else {
423 // add intersection
424 currEdge->fIntersection = intersection;
425 currEdge->fTValue = t;
426
427 // go to next segment
428 prevEdge = currEdge;
429 currEdge = currEdge->fNext;
430 }
431 } else {
432 // if prev to right side of curr
433 int side = winding*compute_side(currEdge->fOffset.fP0,
434 currEdge->fOffset.fV,
435 prevEdge->fOffset.fP0);
436 if (side < 0 &&
437 side == winding*compute_side(currEdge->fOffset.fP0,
438 currEdge->fOffset.fV,
439 prevEdge->fOffset.fP0 + prevEdge->fOffset.fV)) {
440 // no point in considering this one again
441 remove_node(prevEdge, &head);
442 --insetVertexCount;
443 // go back one segment
444 prevEdge = prevEdge->fPrev;
445 } else {
446 // move to next segment
447 remove_node(currEdge, &head);
448 --insetVertexCount;
449 currEdge = currEdge->fNext;
450 }
451 }
452 }
453
454 // store all the valid intersections that aren't nearly coincident
455 // TODO: look at the main algorithm and see if we can detect these better
456 insetPolygon->reset();
457 if (!head) {
458 return false;
459 }
460
461 static constexpr SkScalar kCleanupTolerance = 0.01f;
462 if (insetVertexCount >= 0) {
463 insetPolygon->reserve(insetVertexCount);
464 }
465 int currIndex = 0;
466 *insetPolygon->append() = head->fIntersection;
467 currEdge = head->fNext;
468 while (currEdge != head) {
470 (*insetPolygon)[currIndex],
471 kCleanupTolerance)) {
472 *insetPolygon->append() = currEdge->fIntersection;
473 currIndex++;
474 }
475 currEdge = currEdge->fNext;
476 }
477 // make sure the first and last points aren't coincident
478 if (currIndex >= 1 &&
479 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
480 kCleanupTolerance)) {
481 insetPolygon->pop_back();
482 }
483
484 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->size());
485}
486
487///////////////////////////////////////////////////////////////////////////////////////////
488
489// compute the number of points needed for a circular join when offsetting a reflex vertex
491 SkScalar* rotSin, SkScalar* rotCos, int* n) {
492 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
493
494 SkScalar rCos = v1.dot(v2);
495 if (!SkIsFinite(rCos)) {
496 return false;
497 }
498 SkScalar rSin = v1.cross(v2);
499 if (!SkIsFinite(rSin)) {
500 return false;
501 }
502 SkScalar theta = SkScalarATan2(rSin, rCos);
503
504 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
505 // limit the number of steps to at most max uint16_t (that's all we can index)
506 // knock one value off the top to account for rounding
507 if (floatSteps >= std::numeric_limits<uint16_t>::max()) {
508 return false;
509 }
510 int steps = SkScalarRoundToInt(floatSteps);
511
512 SkScalar dTheta = steps > 0 ? theta / steps : 0;
513 *rotSin = SkScalarSin(dTheta);
514 *rotCos = SkScalarCos(dTheta);
515 // Our offset may be so large that we end up with a tiny dTheta, in which case we
516 // lose precision when computing rotSin and rotCos.
517 if (steps > 0 && (*rotSin == 0 || *rotCos == 1)) {
518 return false;
519 }
520 *n = steps;
521 return true;
522}
523
524///////////////////////////////////////////////////////////////////////////////////////////
525
526// a point is "left" to another if its x-coord is less, or if equal, its y-coord is greater
527static bool left(const SkPoint& p0, const SkPoint& p1) {
528 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY > p1.fY);
529}
530
531// a point is "right" to another if its x-coord is greater, or if equal, its y-coord is less
532static bool right(const SkPoint& p0, const SkPoint& p1) {
533 return p0.fX > p1.fX || (!(p0.fX < p1.fX) && p0.fY < p1.fY);
534}
535
536struct Vertex {
537 static bool Left(const Vertex& qv0, const Vertex& qv1) {
538 return left(qv0.fPosition, qv1.fPosition);
539 }
540
541 // packed to fit into 16 bytes (one cache line)
543 uint16_t fIndex; // index in unsorted polygon
544 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
545 uint16_t fNextIndex;
546 uint16_t fFlags;
547};
548
553
555 ActiveEdge() : fChild{ nullptr, nullptr }, fAbove(nullptr), fBelow(nullptr), fRed(false) {}
556 ActiveEdge(const SkPoint& p0, const SkVector& v, uint16_t index0, uint16_t index1)
557 : fSegment({ p0, v })
558 , fIndex0(index0)
559 , fIndex1(index1)
560 , fAbove(nullptr)
561 , fBelow(nullptr)
562 , fRed(true) {
563 fChild[0] = nullptr;
564 fChild[1] = nullptr;
565 }
566
567 // Returns true if "this" is above "that", assuming this->p0 is to the left of that->p0
568 // This is only used to verify the edgelist -- the actual test for insertion/deletion is much
569 // simpler because we can make certain assumptions then.
570 bool aboveIfLeft(const ActiveEdge* that) const {
571 const SkPoint& p0 = this->fSegment.fP0;
572 const SkPoint& q0 = that->fSegment.fP0;
573 SkASSERT(p0.fX <= q0.fX);
574 SkVector d = q0 - p0;
575 const SkVector& v = this->fSegment.fV;
576 const SkVector& w = that->fSegment.fV;
577 // The idea here is that if the vector between the origins of the two segments (d)
578 // rotates counterclockwise up to the vector representing the "this" segment (v),
579 // then we know that "this" is above "that". If the result is clockwise we say it's below.
580 if (this->fIndex0 != that->fIndex0) {
581 SkScalar cross = d.cross(v);
582 if (cross > kCrossTolerance) {
583 return true;
584 } else if (cross < -kCrossTolerance) {
585 return false;
586 }
587 } else if (this->fIndex1 == that->fIndex1) {
588 return false;
589 }
590 // At this point either the two origins are nearly equal or the origin of "that"
591 // lies on dv. So then we try the same for the vector from the tail of "this"
592 // to the head of "that". Again, ccw means "this" is above "that".
593 // d = that.P1 - this.P0
594 // = that.fP0 + that.fV - this.fP0
595 // = that.fP0 - this.fP0 + that.fV
596 // = old_d + that.fV
597 d += w;
598 SkScalar cross = d.cross(v);
599 if (cross > kCrossTolerance) {
600 return true;
601 } else if (cross < -kCrossTolerance) {
602 return false;
603 }
604 // If the previous check fails, the two segments are nearly collinear
605 // First check y-coord of first endpoints
606 if (p0.fX < q0.fX) {
607 return (p0.fY >= q0.fY);
608 } else if (p0.fY > q0.fY) {
609 return true;
610 } else if (p0.fY < q0.fY) {
611 return false;
612 }
613 // The first endpoints are the same, so check the other endpoint
614 SkPoint p1 = p0 + v;
615 SkPoint q1 = q0 + w;
616 if (p1.fX < q1.fX) {
617 return (p1.fY >= q1.fY);
618 } else {
619 return (p1.fY > q1.fY);
620 }
621 }
622
623 // same as leftAndAbove(), but generalized
624 bool above(const ActiveEdge* that) const {
625 const SkPoint& p0 = this->fSegment.fP0;
626 const SkPoint& q0 = that->fSegment.fP0;
627 if (right(p0, q0)) {
628 return !that->aboveIfLeft(this);
629 } else {
630 return this->aboveIfLeft(that);
631 }
632 }
633
634 bool intersect(const SkPoint& q0, const SkVector& w, uint16_t index0, uint16_t index1) const {
635 // check first to see if these edges are neighbors in the polygon
636 if (this->fIndex0 == index0 || this->fIndex1 == index0 ||
637 this->fIndex0 == index1 || this->fIndex1 == index1) {
638 return false;
639 }
640
641 // We don't need the exact intersection point so we can do a simpler test here.
642 const SkPoint& p0 = this->fSegment.fP0;
643 const SkVector& v = this->fSegment.fV;
644 SkPoint p1 = p0 + v;
645 SkPoint q1 = q0 + w;
646
647 // We assume some x-overlap due to how the edgelist works
648 // This allows us to simplify our test
649 // We need some slop here because storing the vector and recomputing the second endpoint
650 // doesn't necessary give us the original result in floating point.
651 // TODO: Store vector as double? Store endpoint as well?
652 SkASSERT(q0.fX <= p1.fX + SK_ScalarNearlyZero);
653
654 // if each segment straddles the other (i.e., the endpoints have different sides)
655 // then they intersect
656 bool result;
657 if (p0.fX < q0.fX) {
658 if (q1.fX < p1.fX) {
659 result = (compute_side(p0, v, q0)*compute_side(p0, v, q1) < 0);
660 } else {
661 result = (compute_side(p0, v, q0)*compute_side(q0, w, p1) > 0);
662 }
663 } else {
664 if (p1.fX < q1.fX) {
665 result = (compute_side(q0, w, p0)*compute_side(q0, w, p1) < 0);
666 } else {
667 result = (compute_side(q0, w, p0)*compute_side(p0, v, q1) > 0);
668 }
669 }
670 return result;
671 }
672
673 bool intersect(const ActiveEdge* edge) {
674 return this->intersect(edge->fSegment.fP0, edge->fSegment.fV, edge->fIndex0, edge->fIndex1);
675 }
676
677 bool lessThan(const ActiveEdge* that) const {
678 SkASSERT(!this->above(this));
679 SkASSERT(!that->above(that));
680 SkASSERT(!(this->above(that) && that->above(this)));
681 return this->above(that);
682 }
683
684 bool equals(uint16_t index0, uint16_t index1) const {
685 return (this->fIndex0 == index0 && this->fIndex1 == index1);
686 }
687
689 uint16_t fIndex0; // indices for previous and next vertex in polygon
690 uint16_t fIndex1;
694 int32_t fRed;
695};
696
698public:
699 ActiveEdgeList(int maxEdges) {
700 fAllocation = (char*) sk_malloc_throw(sizeof(ActiveEdge)*maxEdges);
701 fCurrFree = 0;
702 fMaxFree = maxEdges;
703 }
705 fTreeHead.fChild[1] = nullptr;
706 sk_free(fAllocation);
707 }
708
709 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
710 SkVector v = p1 - p0;
711 if (!v.isFinite()) {
712 return false;
713 }
714 // empty tree case -- easy
715 if (!fTreeHead.fChild[1]) {
716 ActiveEdge* root = fTreeHead.fChild[1] = this->allocate(p0, v, index0, index1);
717 SkASSERT(root);
718 if (!root) {
719 return false;
720 }
721 root->fRed = false;
722 return true;
723 }
724
725 // set up helpers
726 ActiveEdge* top = &fTreeHead;
727 ActiveEdge *grandparent = nullptr;
728 ActiveEdge *parent = nullptr;
729 ActiveEdge *curr = top->fChild[1];
730 int dir = 0;
731 int last = 0; // ?
732 // predecessor and successor, for intersection check
733 ActiveEdge* pred = nullptr;
734 ActiveEdge* succ = nullptr;
735
736 // search down the tree
737 while (true) {
738 if (!curr) {
739 // check for intersection with predecessor and successor
740 if ((pred && pred->intersect(p0, v, index0, index1)) ||
741 (succ && succ->intersect(p0, v, index0, index1))) {
742 return false;
743 }
744 // insert new node at bottom
745 parent->fChild[dir] = curr = this->allocate(p0, v, index0, index1);
746 SkASSERT(curr);
747 if (!curr) {
748 return false;
749 }
750 curr->fAbove = pred;
751 curr->fBelow = succ;
752 if (pred) {
753 if (pred->fSegment.fP0 == curr->fSegment.fP0 &&
754 pred->fSegment.fV == curr->fSegment.fV) {
755 return false;
756 }
757 pred->fBelow = curr;
758 }
759 if (succ) {
760 if (succ->fSegment.fP0 == curr->fSegment.fP0 &&
761 succ->fSegment.fV == curr->fSegment.fV) {
762 return false;
763 }
764 succ->fAbove = curr;
765 }
766 if (IsRed(parent)) {
767 int dir2 = (top->fChild[1] == grandparent);
768 if (curr == parent->fChild[last]) {
769 top->fChild[dir2] = SingleRotation(grandparent, !last);
770 } else {
771 top->fChild[dir2] = DoubleRotation(grandparent, !last);
772 }
773 }
774 break;
775 } else if (IsRed(curr->fChild[0]) && IsRed(curr->fChild[1])) {
776 // color flip
777 curr->fRed = true;
778 curr->fChild[0]->fRed = false;
779 curr->fChild[1]->fRed = false;
780 if (IsRed(parent)) {
781 int dir2 = (top->fChild[1] == grandparent);
782 if (curr == parent->fChild[last]) {
783 top->fChild[dir2] = SingleRotation(grandparent, !last);
784 } else {
785 top->fChild[dir2] = DoubleRotation(grandparent, !last);
786 }
787 }
788 }
789
790 last = dir;
791 int side;
792 // check to see if segment is above or below
793 if (curr->fIndex0 == index0) {
794 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
795 } else {
796 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
797 }
798 if (0 == side) {
799 return false;
800 }
801 dir = (side < 0);
802
803 if (0 == dir) {
804 succ = curr;
805 } else {
806 pred = curr;
807 }
808
809 // update helpers
810 if (grandparent) {
811 top = grandparent;
812 }
813 grandparent = parent;
814 parent = curr;
815 curr = curr->fChild[dir];
816 }
817
818 // update root and make it black
819 fTreeHead.fChild[1]->fRed = false;
820
821 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
822
823 return true;
824 }
825
826 // replaces edge p0p1 with p1p2
827 bool replace(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
828 uint16_t index0, uint16_t index1, uint16_t index2) {
829 if (!fTreeHead.fChild[1]) {
830 return false;
831 }
832
833 SkVector v = p2 - p1;
834 ActiveEdge* curr = &fTreeHead;
835 ActiveEdge* found = nullptr;
836 int dir = 1;
837
838 // search
839 while (curr->fChild[dir] != nullptr) {
840 // update helpers
841 curr = curr->fChild[dir];
842 // save found node
843 if (curr->equals(index0, index1)) {
844 found = curr;
845 break;
846 } else {
847 // check to see if segment is above or below
848 int side;
849 if (curr->fIndex1 == index1) {
850 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
851 } else {
852 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
853 }
854 if (0 == side) {
855 return false;
856 }
857 dir = (side < 0);
858 }
859 }
860
861 if (!found) {
862 return false;
863 }
864
865 // replace if found
866 ActiveEdge* pred = found->fAbove;
867 ActiveEdge* succ = found->fBelow;
868 // check deletion and insert intersection cases
869 if (pred && (pred->intersect(found) || pred->intersect(p1, v, index1, index2))) {
870 return false;
871 }
872 if (succ && (succ->intersect(found) || succ->intersect(p1, v, index1, index2))) {
873 return false;
874 }
875 found->fSegment.fP0 = p1;
876 found->fSegment.fV = v;
877 found->fIndex0 = index1;
878 found->fIndex1 = index2;
879 // above and below should stay the same
880
881 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
882
883 return true;
884 }
885
886 bool remove(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
887 if (!fTreeHead.fChild[1]) {
888 return false;
889 }
890
891 ActiveEdge* curr = &fTreeHead;
892 ActiveEdge* parent = nullptr;
893 ActiveEdge* grandparent = nullptr;
894 ActiveEdge* found = nullptr;
895 int dir = 1;
896
897 // search and push a red node down
898 while (curr->fChild[dir] != nullptr) {
899 int last = dir;
900
901 // update helpers
902 grandparent = parent;
903 parent = curr;
904 curr = curr->fChild[dir];
905 // save found node
906 if (curr->equals(index0, index1)) {
907 found = curr;
908 dir = 0;
909 } else {
910 // check to see if segment is above or below
911 int side;
912 if (curr->fIndex1 == index1) {
913 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
914 } else {
915 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
916 }
917 if (0 == side) {
918 return false;
919 }
920 dir = (side < 0);
921 }
922
923 // push the red node down
924 if (!IsRed(curr) && !IsRed(curr->fChild[dir])) {
925 if (IsRed(curr->fChild[!dir])) {
926 parent = parent->fChild[last] = SingleRotation(curr, dir);
927 } else {
928 ActiveEdge *s = parent->fChild[!last];
929
930 if (s != nullptr) {
931 if (!IsRed(s->fChild[!last]) && !IsRed(s->fChild[last])) {
932 // color flip
933 parent->fRed = false;
934 s->fRed = true;
935 curr->fRed = true;
936 } else {
937 int dir2 = (grandparent->fChild[1] == parent);
938
939 if (IsRed(s->fChild[last])) {
940 grandparent->fChild[dir2] = DoubleRotation(parent, last);
941 } else if (IsRed(s->fChild[!last])) {
942 grandparent->fChild[dir2] = SingleRotation(parent, last);
943 }
944
945 // ensure correct coloring
946 curr->fRed = grandparent->fChild[dir2]->fRed = true;
947 grandparent->fChild[dir2]->fChild[0]->fRed = false;
948 grandparent->fChild[dir2]->fChild[1]->fRed = false;
949 }
950 }
951 }
952 }
953 }
954
955 // replace and remove if found
956 if (found) {
957 ActiveEdge* pred = found->fAbove;
958 ActiveEdge* succ = found->fBelow;
959 if ((pred && pred->intersect(found)) || (succ && succ->intersect(found))) {
960 return false;
961 }
962 if (found != curr) {
963 found->fSegment = curr->fSegment;
964 found->fIndex0 = curr->fIndex0;
965 found->fIndex1 = curr->fIndex1;
966 found->fAbove = curr->fAbove;
967 pred = found->fAbove;
968 // we don't need to set found->fBelow here
969 } else {
970 if (succ) {
971 succ->fAbove = pred;
972 }
973 }
974 if (pred) {
975 pred->fBelow = curr->fBelow;
976 }
977 parent->fChild[parent->fChild[1] == curr] = curr->fChild[!curr->fChild[0]];
978
979 // no need to delete
980 curr->fAbove = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
981 curr->fBelow = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
982 if (fTreeHead.fChild[1]) {
983 fTreeHead.fChild[1]->fRed = false;
984 }
985 }
986
987 // update root and make it black
988 if (fTreeHead.fChild[1]) {
989 fTreeHead.fChild[1]->fRed = false;
990 }
991
992 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
993
994 return true;
995 }
996
997private:
998 // allocator
999 ActiveEdge * allocate(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
1000 if (fCurrFree >= fMaxFree) {
1001 return nullptr;
1002 }
1003 char* bytes = fAllocation + sizeof(ActiveEdge)*fCurrFree;
1004 ++fCurrFree;
1005 return new(bytes) ActiveEdge(p0, p1, index0, index1);
1006 }
1007
1008 ///////////////////////////////////////////////////////////////////////////////////
1009 // Red-black tree methods
1010 ///////////////////////////////////////////////////////////////////////////////////
1011 static bool IsRed(const ActiveEdge* node) {
1012 return node && node->fRed;
1013 }
1014
1015 static ActiveEdge* SingleRotation(ActiveEdge* node, int dir) {
1016 ActiveEdge* tmp = node->fChild[!dir];
1017
1018 node->fChild[!dir] = tmp->fChild[dir];
1019 tmp->fChild[dir] = node;
1020
1021 node->fRed = true;
1022 tmp->fRed = false;
1023
1024 return tmp;
1025 }
1026
1027 static ActiveEdge* DoubleRotation(ActiveEdge* node, int dir) {
1028 node->fChild[!dir] = SingleRotation(node->fChild[!dir], !dir);
1029
1030 return SingleRotation(node, dir);
1031 }
1032
1033 // returns black link count
1034 static int VerifyTree(const ActiveEdge* tree) {
1035 if (!tree) {
1036 return 1;
1037 }
1038
1039 const ActiveEdge* left = tree->fChild[0];
1040 const ActiveEdge* right = tree->fChild[1];
1041
1042 // no consecutive red links
1043 if (IsRed(tree) && (IsRed(left) || IsRed(right))) {
1044 SkASSERT(false);
1045 return 0;
1046 }
1047
1048 // check secondary links
1049 if (tree->fAbove) {
1050 SkASSERT(tree->fAbove->fBelow == tree);
1051 SkASSERT(tree->fAbove->lessThan(tree));
1052 }
1053 if (tree->fBelow) {
1054 SkASSERT(tree->fBelow->fAbove == tree);
1055 SkASSERT(tree->lessThan(tree->fBelow));
1056 }
1057
1058 // violates binary tree order
1059 if ((left && tree->lessThan(left)) || (right && right->lessThan(tree))) {
1060 SkASSERT(false);
1061 return 0;
1062 }
1063
1064 int leftCount = VerifyTree(left);
1065 int rightCount = VerifyTree(right);
1066
1067 // return black link count
1068 if (leftCount != 0 && rightCount != 0) {
1069 // black height mismatch
1070 if (leftCount != rightCount) {
1071 SkASSERT(false);
1072 return 0;
1073 }
1074 return IsRed(tree) ? leftCount : leftCount + 1;
1075 } else {
1076 return 0;
1077 }
1078 }
1079
1080 ActiveEdge fTreeHead;
1081 char* fAllocation;
1082 int fCurrFree;
1083 int fMaxFree;
1084};
1085
1086// Here we implement a sweep line algorithm to determine whether the provided points
1087// represent a simple polygon, i.e., the polygon is non-self-intersecting.
1088// We first insert the vertices into a priority queue sorting horizontally from left to right.
1089// Then as we pop the vertices from the queue we generate events which indicate that an edge
1090// should be added or removed from an edge list. If any intersections are detected in the edge
1091// list, then we know the polygon is self-intersecting and hence not simple.
1092bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
1093 if (polygonSize < 3) {
1094 return false;
1095 }
1096
1097 // If it's convex, it's simple
1098 if (SkIsConvexPolygon(polygon, polygonSize)) {
1099 return true;
1100 }
1101
1102 // practically speaking, it takes too long to process large polygons
1103 if (polygonSize > 2048) {
1104 return false;
1105 }
1106
1107 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
1108 for (int i = 0; i < polygonSize; ++i) {
1109 Vertex newVertex;
1110 if (!polygon[i].isFinite()) {
1111 return false;
1112 }
1113 newVertex.fPosition = polygon[i];
1114 newVertex.fIndex = i;
1115 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1116 newVertex.fNextIndex = (i + 1) % polygonSize;
1117 newVertex.fFlags = 0;
1118 // The two edges adjacent to this vertex are the same, so polygon is not simple
1119 if (polygon[newVertex.fPrevIndex] == polygon[newVertex.fNextIndex]) {
1120 return false;
1121 }
1122 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1123 newVertex.fFlags |= kPrevLeft_VertexFlag;
1124 }
1125 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1126 newVertex.fFlags |= kNextLeft_VertexFlag;
1127 }
1128 vertexQueue.insert(newVertex);
1129 }
1130
1131 // pop each vertex from the queue and generate events depending on
1132 // where it lies relative to its neighboring edges
1133 ActiveEdgeList sweepLine(polygonSize);
1134 while (vertexQueue.count() > 0) {
1135 const Vertex& v = vertexQueue.peek();
1136
1137 // both to the right -- insert both
1138 if (v.fFlags == 0) {
1139 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
1140 break;
1141 }
1142 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
1143 break;
1144 }
1145 // both to the left -- remove both
1146 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1147 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1148 break;
1149 }
1150 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1151 break;
1152 }
1153 // one to left and right -- replace one with another
1154 } else {
1155 if (v.fFlags & kPrevLeft_VertexFlag) {
1156 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1157 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1158 break;
1159 }
1160 } else {
1162 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1163 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1164 break;
1165 }
1166 }
1167 }
1168
1169 vertexQueue.pop();
1170 }
1171
1172 return (vertexQueue.count() == 0);
1173}
1174
1175///////////////////////////////////////////////////////////////////////////////////////////
1176
1177// helper function for SkOffsetSimplePolygon
1178static void setup_offset_edge(OffsetEdge* currEdge,
1179 const SkPoint& endpoint0, const SkPoint& endpoint1,
1180 uint16_t startIndex, uint16_t endIndex) {
1181 currEdge->fOffset.fP0 = endpoint0;
1182 currEdge->fOffset.fV = endpoint1 - endpoint0;
1183 currEdge->init(startIndex, endIndex);
1184}
1185
1186static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
1187 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
1188 int side = compute_side(inputPolygonVerts[prevIndex],
1189 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
1190 inputPolygonVerts[nextIndex]);
1191 // if reflex point, we need to add extra edges
1192 return (side*winding*offset < 0);
1193}
1194
1195bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
1196 const SkRect& bounds, SkScalar offset,
1197 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
1198 if (inputPolygonSize < 3) {
1199 return false;
1200 }
1201
1202 // need to be able to represent all the vertices in the 16-bit indices
1203 if (inputPolygonSize >= std::numeric_limits<uint16_t>::max()) {
1204 return false;
1205 }
1206
1207 if (!SkIsFinite(offset)) {
1208 return false;
1209 }
1210
1211 // can't inset more than the half bounds of the polygon
1212 if (offset > std::min(SkTAbs(SkRectPriv::HalfWidth(bounds)),
1213 SkTAbs(SkRectPriv::HalfHeight(bounds)))) {
1214 return false;
1215 }
1216
1217 // offsetting close to zero just returns the original poly
1219 for (int i = 0; i < inputPolygonSize; ++i) {
1220 *offsetPolygon->append() = inputPolygonVerts[i];
1221 if (polygonIndices) {
1222 *polygonIndices->append() = i;
1223 }
1224 }
1225 return true;
1226 }
1227
1228 // get winding direction
1229 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
1230 if (0 == winding) {
1231 return false;
1232 }
1233
1234 // build normals
1235 AutoSTMalloc<64, SkVector> normals(inputPolygonSize);
1236 unsigned int numEdges = 0;
1237 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1238 currIndex < inputPolygonSize;
1239 prevIndex = currIndex, ++currIndex) {
1240 if (!inputPolygonVerts[currIndex].isFinite()) {
1241 return false;
1242 }
1243 int nextIndex = (currIndex + 1) % inputPolygonSize;
1244 if (!compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
1245 offset, winding, &normals[currIndex])) {
1246 return false;
1247 }
1248 if (currIndex > 0) {
1249 // if reflex point, we need to add extra edges
1250 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
1251 prevIndex, currIndex, nextIndex)) {
1252 SkScalar rotSin, rotCos;
1253 int numSteps;
1254 if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
1255 &rotSin, &rotCos, &numSteps)) {
1256 return false;
1257 }
1258 numEdges += std::max(numSteps, 1);
1259 }
1260 }
1261 numEdges++;
1262 }
1263 // finish up the edge counting
1264 if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
1265 SkScalar rotSin, rotCos;
1266 int numSteps;
1267 if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
1268 &rotSin, &rotCos, &numSteps)) {
1269 return false;
1270 }
1271 numEdges += std::max(numSteps, 1);
1272 }
1273
1274 // Make sure we don't overflow the max array count.
1275 // We shouldn't overflow numEdges, as SkComputeRadialSteps returns a max of 2^16-1,
1276 // and we have a max of 2^16-1 original vertices.
1277 if (numEdges > (unsigned int)std::numeric_limits<int32_t>::max()) {
1278 return false;
1279 }
1280
1281 // build initial offset edge list
1282 STArray<64, OffsetEdge> edgeData(numEdges);
1283 OffsetEdge* prevEdge = nullptr;
1284 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1285 currIndex < inputPolygonSize;
1286 prevIndex = currIndex, ++currIndex) {
1287 int nextIndex = (currIndex + 1) % inputPolygonSize;
1288 // if reflex point, fill in curve
1289 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
1290 prevIndex, currIndex, nextIndex)) {
1291 SkScalar rotSin, rotCos;
1292 int numSteps;
1293 SkVector prevNormal = normals[prevIndex];
1294 if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
1295 &rotSin, &rotCos, &numSteps)) {
1296 return false;
1297 }
1298 auto currEdge = edgeData.push_back_n(std::max(numSteps, 1));
1299 for (int i = 0; i < numSteps - 1; ++i) {
1300 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1301 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
1302 setup_offset_edge(currEdge,
1303 inputPolygonVerts[currIndex] + prevNormal,
1304 inputPolygonVerts[currIndex] + currNormal,
1305 currIndex, currIndex);
1306 prevNormal = currNormal;
1307 currEdge->fPrev = prevEdge;
1308 if (prevEdge) {
1309 prevEdge->fNext = currEdge;
1310 }
1311 prevEdge = currEdge;
1312 ++currEdge;
1313 }
1314 setup_offset_edge(currEdge,
1315 inputPolygonVerts[currIndex] + prevNormal,
1316 inputPolygonVerts[currIndex] + normals[currIndex],
1317 currIndex, currIndex);
1318 currEdge->fPrev = prevEdge;
1319 if (prevEdge) {
1320 prevEdge->fNext = currEdge;
1321 }
1322 prevEdge = currEdge;
1323 }
1324
1325 // Add the edge
1326 auto currEdge = edgeData.push_back_n(1);
1327 setup_offset_edge(currEdge,
1328 inputPolygonVerts[currIndex] + normals[currIndex],
1329 inputPolygonVerts[nextIndex] + normals[currIndex],
1330 currIndex, nextIndex);
1331 currEdge->fPrev = prevEdge;
1332 if (prevEdge) {
1333 prevEdge->fNext = currEdge;
1334 }
1335 prevEdge = currEdge;
1336 }
1337 // close up the linked list
1338 SkASSERT(prevEdge);
1339 prevEdge->fNext = &edgeData[0];
1340 edgeData[0].fPrev = prevEdge;
1341
1342 // now clip edges
1343 SkASSERT(edgeData.size() == (int)numEdges);
1344 auto head = &edgeData[0];
1345 auto currEdge = head;
1346 unsigned int offsetVertexCount = numEdges;
1347 unsigned long long iterations = 0;
1348 unsigned long long maxIterations = (unsigned long long)(numEdges) * numEdges;
1349 while (head && prevEdge != currEdge && offsetVertexCount > 0) {
1350 ++iterations;
1351 // we should check each edge against each other edge at most once
1352 if (iterations > maxIterations) {
1353 return false;
1354 }
1355
1356 SkScalar s, t;
1357 SkPoint intersection;
1358 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
1359 // if new intersection is further back on previous inset from the prior intersection
1360 if (s < prevEdge->fTValue) {
1361 // no point in considering this one again
1362 remove_node(prevEdge, &head);
1363 --offsetVertexCount;
1364 // go back one segment
1365 prevEdge = prevEdge->fPrev;
1366 // we've already considered this intersection, we're done
1367 } else if (currEdge->fTValue > SK_ScalarMin &&
1369 currEdge->fIntersection,
1370 1.0e-6f)) {
1371 break;
1372 } else {
1373 // add intersection
1374 currEdge->fIntersection = intersection;
1375 currEdge->fTValue = t;
1376 currEdge->fIndex = prevEdge->fEnd;
1377
1378 // go to next segment
1379 prevEdge = currEdge;
1380 currEdge = currEdge->fNext;
1381 }
1382 } else {
1383 // If there is no intersection, we want to minimize the distance between
1384 // the point where the segment lines cross and the segments themselves.
1385 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1386 OffsetEdge* currNextEdge = currEdge->fNext;
1387 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1388 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1389 // if both lead to direct collision
1390 if (dist0 < 0 && dist1 < 0) {
1391 // check first to see if either represent parts of one contour
1392 SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
1393 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1394 prevEdge->fOffset.fP0);
1395 p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
1396 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1397 currNextEdge->fOffset.fP0);
1398
1399 // want to step along contour to find intersections rather than jump to new one
1400 if (currSameContour && !prevSameContour) {
1401 remove_node(currEdge, &head);
1402 currEdge = currNextEdge;
1403 --offsetVertexCount;
1404 continue;
1405 } else if (prevSameContour && !currSameContour) {
1406 remove_node(prevEdge, &head);
1407 prevEdge = prevPrevEdge;
1408 --offsetVertexCount;
1409 continue;
1410 }
1411 }
1412
1413 // otherwise minimize collision distance along segment
1414 if (dist0 < dist1) {
1415 remove_node(prevEdge, &head);
1416 prevEdge = prevPrevEdge;
1417 } else {
1418 remove_node(currEdge, &head);
1419 currEdge = currNextEdge;
1420 }
1421 --offsetVertexCount;
1422 }
1423 }
1424
1425 // store all the valid intersections that aren't nearly coincident
1426 // TODO: look at the main algorithm and see if we can detect these better
1427 offsetPolygon->reset();
1428 if (!head || offsetVertexCount == 0 ||
1429 offsetVertexCount >= std::numeric_limits<uint16_t>::max()) {
1430 return false;
1431 }
1432
1433 static constexpr SkScalar kCleanupTolerance = 0.01f;
1434 offsetPolygon->reserve(offsetVertexCount);
1435 int currIndex = 0;
1436 *offsetPolygon->append() = head->fIntersection;
1437 if (polygonIndices) {
1438 *polygonIndices->append() = head->fIndex;
1439 }
1440 currEdge = head->fNext;
1441 while (currEdge != head) {
1442 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1443 (*offsetPolygon)[currIndex],
1444 kCleanupTolerance)) {
1445 *offsetPolygon->append() = currEdge->fIntersection;
1446 if (polygonIndices) {
1447 *polygonIndices->append() = currEdge->fIndex;
1448 }
1449 currIndex++;
1450 }
1451 currEdge = currEdge->fNext;
1452 }
1453 // make sure the first and last points aren't coincident
1454 if (currIndex >= 1 &&
1455 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1456 kCleanupTolerance)) {
1457 offsetPolygon->pop_back();
1458 if (polygonIndices) {
1459 polygonIndices->pop_back();
1460 }
1461 }
1462
1463 // check winding of offset polygon (it should be same as the original polygon)
1464 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->size());
1465
1466 return (winding*offsetWinding > 0 &&
1467 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->size()));
1468}
1469
1470//////////////////////////////////////////////////////////////////////////////////////////
1471
1483
1484static void compute_triangle_bounds(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1485 SkRect* bounds) {
1487 min = max = skvx::float4(p0.fX, p0.fY, p0.fX, p0.fY);
1488 skvx::float4 xy(p1.fX, p1.fY, p2.fX, p2.fY);
1489 min = skvx::min(min, xy);
1490 max = skvx::max(max, xy);
1491 bounds->setLTRB(std::min(min[0], min[2]), std::min(min[1], min[3]),
1492 std::max(max[0], max[2]), std::max(max[1], max[3]));
1493}
1494
1495// test to see if point p is in triangle p0p1p2.
1496// for now assuming strictly inside -- if on the edge it's outside
1497static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1498 const SkPoint& p) {
1499 SkVector v0 = p1 - p0;
1500 SkVector v1 = p2 - p1;
1501 SkScalar n = v0.cross(v1);
1502
1503 SkVector w0 = p - p0;
1504 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1505 return false;
1506 }
1507
1508 SkVector w1 = p - p1;
1509 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1510 return false;
1511 }
1512
1513 SkVector v2 = p0 - p2;
1514 SkVector w2 = p - p2;
1515 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1516 return false;
1517 }
1518
1519 return true;
1520}
1521
1522// Data structure to track reflex vertices and check whether any are inside a given triangle
1524public:
1525 bool init(const SkRect& bounds, int vertexCount) {
1526 fBounds = bounds;
1527 fNumVerts = 0;
1528 SkScalar width = bounds.width();
1529 SkScalar height = bounds.height();
1530 if (!SkIsFinite(width, height)) {
1531 return false;
1532 }
1533
1534 // We want vertexCount grid cells, roughly distributed to match the bounds ratio
1535 SkScalar hCount = SkScalarSqrt(sk_ieee_float_divide(vertexCount*width, height));
1536 if (!SkIsFinite(hCount)) {
1537 return false;
1538 }
1539 fHCount = std::max(std::min(SkScalarRoundToInt(hCount), vertexCount), 1);
1540 fVCount = vertexCount/fHCount;
1541 fGridConversion.set(sk_ieee_float_divide(fHCount - 0.001f, width),
1542 sk_ieee_float_divide(fVCount - 0.001f, height));
1543 if (!fGridConversion.isFinite()) {
1544 return false;
1545 }
1546
1547 fGrid.resize(fHCount*fVCount);
1548 for (int i = 0; i < fGrid.size(); ++i) {
1549 fGrid[i].reset();
1550 }
1551
1552 return true;
1553 }
1554
1556 int index = hash(v);
1557 fGrid[index].addToTail(v);
1558 ++fNumVerts;
1559 }
1560
1562 int index = hash(v);
1563 fGrid[index].remove(v);
1564 --fNumVerts;
1565 }
1566
1567 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1568 uint16_t ignoreIndex0, uint16_t ignoreIndex1) const {
1569 if (!fNumVerts) {
1570 return false;
1571 }
1572
1573 SkRect triBounds;
1574 compute_triangle_bounds(p0, p1, p2, &triBounds);
1575 int h0 = (triBounds.fLeft - fBounds.fLeft)*fGridConversion.fX;
1576 int h1 = (triBounds.fRight - fBounds.fLeft)*fGridConversion.fX;
1577 int v0 = (triBounds.fTop - fBounds.fTop)*fGridConversion.fY;
1578 int v1 = (triBounds.fBottom - fBounds.fTop)*fGridConversion.fY;
1579
1580 for (int v = v0; v <= v1; ++v) {
1581 for (int h = h0; h <= h1; ++h) {
1582 int i = v * fHCount + h;
1583 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fGrid[i].begin();
1584 reflexIter != fGrid[i].end(); ++reflexIter) {
1585 TriangulationVertex* reflexVertex = *reflexIter;
1586 if (reflexVertex->fIndex != ignoreIndex0 &&
1587 reflexVertex->fIndex != ignoreIndex1 &&
1588 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
1589 return true;
1590 }
1591 }
1592
1593 }
1594 }
1595
1596 return false;
1597 }
1598
1599private:
1600 int hash(TriangulationVertex* vert) const {
1601 int h = (vert->fPosition.fX - fBounds.fLeft)*fGridConversion.fX;
1602 int v = (vert->fPosition.fY - fBounds.fTop)*fGridConversion.fY;
1603 SkASSERT(v*fHCount + h >= 0);
1604 return v*fHCount + h;
1605 }
1606
1607 SkRect fBounds;
1608 int fHCount;
1609 int fVCount;
1610 int fNumVerts;
1611 // converts distance from the origin to a grid location (when cast to int)
1612 SkVector fGridConversion;
1614};
1615
1616// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1617static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1618 int winding, ReflexHash* reflexHash,
1620 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1621 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1622 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
1623 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
1625 reflexHash->remove(p);
1626 p->fPrev = p->fNext = nullptr;
1627 convexList->addToTail(p);
1628 }
1629 }
1630}
1631
1632bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1633 SkTDArray<uint16_t>* triangleIndices) {
1634 if (polygonSize < 3) {
1635 return false;
1636 }
1637 // need to be able to represent all the vertices in the 16-bit indices
1638 if (polygonSize >= std::numeric_limits<uint16_t>::max()) {
1639 return false;
1640 }
1641
1642 // get bounds
1643 SkRect bounds;
1644 if (!bounds.setBoundsCheck(polygonVerts, polygonSize)) {
1645 return false;
1646 }
1647 // get winding direction
1648 // TODO: we do this for all the polygon routines -- might be better to have the client
1649 // compute it and pass it in
1650 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
1651 if (0 == winding) {
1652 return false;
1653 }
1654
1655 // Set up vertices
1656 AutoSTArray<64, TriangulationVertex> triangulationVertices(polygonSize);
1657 int prevIndex = polygonSize - 1;
1658 SkVector v0 = polygonVerts[0] - polygonVerts[prevIndex];
1659 for (int currIndex = 0; currIndex < polygonSize; ++currIndex) {
1660 int nextIndex = (currIndex + 1) % polygonSize;
1661
1662 triangulationVertices[currIndex] = TriangulationVertex{};
1663 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1664 triangulationVertices[currIndex].fIndex = currIndex;
1665 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1666 triangulationVertices[currIndex].fNextIndex = nextIndex;
1667 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1668 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
1669 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1670 } else {
1671 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1672 }
1673
1674 prevIndex = currIndex;
1675 v0 = v1;
1676 }
1677
1678 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1679 // TODO: possibly sort the convexList in some way to get better triangles
1681 ReflexHash reflexHash;
1682 if (!reflexHash.init(bounds, polygonSize)) {
1683 return false;
1684 }
1685 prevIndex = polygonSize - 1;
1686 for (int currIndex = 0; currIndex < polygonSize; prevIndex = currIndex, ++currIndex) {
1687 TriangulationVertex::VertexType currType = triangulationVertices[currIndex].fVertexType;
1689 int nextIndex = (currIndex + 1) % polygonSize;
1690 TriangulationVertex::VertexType prevType = triangulationVertices[prevIndex].fVertexType;
1691 TriangulationVertex::VertexType nextType = triangulationVertices[nextIndex].fVertexType;
1692 // We prioritize clipping vertices with neighboring reflex vertices.
1693 // The intent here is that it will cull reflex vertices more quickly.
1696 convexList.addToHead(&triangulationVertices[currIndex]);
1697 } else {
1698 convexList.addToTail(&triangulationVertices[currIndex]);
1699 }
1700 } else {
1701 // We treat near collinear vertices as reflex
1702 reflexHash.add(&triangulationVertices[currIndex]);
1703 }
1704 }
1705
1706 // The general concept: We are trying to find three neighboring vertices where
1707 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1708 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1709 // we have triangulated the entire polygon.
1710 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1711 // noting that only convex vertices can be potential ears, and we only need to check whether
1712 // any reflex vertices lie inside the ear.
1713 triangleIndices->reserve(triangleIndices->size() + 3 * (polygonSize - 2));
1714 int vertexCount = polygonSize;
1715 while (vertexCount > 3) {
1716 bool success = false;
1717 TriangulationVertex* earVertex = nullptr;
1718 TriangulationVertex* p0 = nullptr;
1719 TriangulationVertex* p2 = nullptr;
1720 // find a convex vertex to clip
1721 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1722 convexIter != convexList.end(); ++convexIter) {
1723 earVertex = *convexIter;
1725
1726 p0 = &triangulationVertices[earVertex->fPrevIndex];
1727 p2 = &triangulationVertices[earVertex->fNextIndex];
1728
1729 // see if any reflex vertices are inside the ear
1730 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
1731 p2->fPosition, p0->fIndex, p2->fIndex);
1732 if (failed) {
1733 continue;
1734 }
1735
1736 // found one we can clip
1737 success = true;
1738 break;
1739 }
1740 // If we can't find any ears to clip, this probably isn't a simple polygon
1741 if (!success) {
1742 return false;
1743 }
1744
1745 // add indices
1746 auto indices = triangleIndices->append(3);
1747 indices[0] = indexMap[p0->fIndex];
1748 indices[1] = indexMap[earVertex->fIndex];
1749 indices[2] = indexMap[p2->fIndex];
1750
1751 // clip the ear
1752 convexList.remove(earVertex);
1753 --vertexCount;
1754
1755 // reclassify reflex verts
1756 p0->fNextIndex = earVertex->fNextIndex;
1757 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1758
1759 p2->fPrevIndex = earVertex->fPrevIndex;
1760 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1761 }
1762
1763 // output indices
1764 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1765 vertexIter != convexList.end(); ++vertexIter) {
1766 TriangulationVertex* vertex = *vertexIter;
1767 *triangleIndices->append() = indexMap[vertex->fIndex];
1768 }
1769
1770 return true;
1771}
1772
1773#endif // !defined(SK_ENABLE_OPTIMIZE_SIZE)
1774
static SkM44 normals(SkM44 m)
Definition 3DSlide.cpp:78
static bool isFinite(const SkRect &r)
static float next(float f)
static float prev(float f)
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static bool SkIsFinite(T x, Pack... values)
static constexpr float sk_ieee_float_divide(float numer, float denom)
SK_API void sk_free(void *)
static void * sk_malloc_throw(size_t size)
Definition SkMalloc.h:67
static int side(double x)
static bool left(const SkPoint &p0, const SkPoint &p1)
bool SkComputeRadialSteps(const SkVector &v1, const SkVector &v2, SkScalar offset, SkScalar *rotSin, SkScalar *rotCos, int *n)
bool SkIsConvexPolygon(const SkPoint *polygonVerts, int polygonSize)
static void reclassify_vertex(TriangulationVertex *p, const SkPoint *polygonVerts, int winding, ReflexHash *reflexHash, SkTInternalLList< TriangulationVertex > *convexList)
VertexFlags
@ kNextLeft_VertexFlag
@ kPrevLeft_VertexFlag
bool SkTriangulateSimplePolygon(const SkPoint *polygonVerts, uint16_t *indexMap, int polygonSize, SkTDArray< uint16_t > *triangleIndices)
bool compute_offset_vector(const SkPoint &p0, const SkPoint &p1, SkScalar offset, int side, SkPoint *vector)
static bool right(const SkPoint &p0, const SkPoint &p1)
bool SkOffsetSimplePolygon(const SkPoint *inputPolygonVerts, int inputPolygonSize, const SkRect &bounds, SkScalar offset, SkTDArray< SkPoint > *offsetPolygon, SkTDArray< int > *polygonIndices)
static void setup_offset_edge(OffsetEdge *currEdge, const SkPoint &endpoint0, const SkPoint &endpoint1, uint16_t startIndex, uint16_t endIndex)
static bool zero_length(const SkPoint &v, SkScalar vdotv)
static bool compute_intersection(const OffsetSegment &s0, const OffsetSegment &s1, SkPoint *p, SkScalar *s, SkScalar *t)
bool SkIsSimplePolygon(const SkPoint *polygon, int polygonSize)
static void remove_node(const OffsetEdge *node, OffsetEdge **head)
static int compute_side(const SkPoint &p0, const SkVector &v, const SkPoint &p)
static void compute_triangle_bounds(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, SkRect *bounds)
constexpr SkScalar kCrossTolerance
static bool is_reflex_vertex(const SkPoint *inputPolygonVerts, int winding, SkScalar offset, uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex)
static bool point_in_triangle(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, const SkPoint &p)
static bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive)
int SkGetPolygonWinding(const SkPoint *polygonVerts, int polygonSize)
bool SkInsetConvexPolygon(const SkPoint *inputPolygonVerts, int inputPolygonSize, SkScalar inset, SkTDArray< SkPoint > *insetPolygon)
#define SkScalarATan2(y, x)
Definition SkScalar.h:50
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SK_ScalarMin
Definition SkScalar.h:25
#define SK_ScalarMax
Definition SkScalar.h:24
#define SkScalarSin(radians)
Definition SkScalar.h:45
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SkScalarSqrt(x)
Definition SkScalar.h:42
#define SkScalarAbs(x)
Definition SkScalar.h:39
static T SkTAbs(T value)
Definition SkTemplates.h:43
Vec2Value v2
bool insert(const SkPoint &p0, const SkPoint &p1, uint16_t index0, uint16_t index1)
bool remove(const SkPoint &p0, const SkPoint &p1, uint16_t index0, uint16_t index1)
ActiveEdgeList(int maxEdges)
bool replace(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, uint16_t index0, uint16_t index1, uint16_t index2)
void remove(TriangulationVertex *v)
bool init(const SkRect &bounds, int vertexCount)
bool checkTriangle(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, uint16_t ignoreIndex0, uint16_t ignoreIndex1) const
void add(TriangulationVertex *v)
static bool CanNormalize(SkScalar dx, SkScalar dy)
Definition SkPointPriv.h:28
static bool EqualsWithinTolerance(const SkPoint &p1, const SkPoint &p2)
Definition SkPointPriv.h:54
static constexpr float HalfWidth(const SkRect &r)
Definition SkRectPriv.h:62
static constexpr float HalfHeight(const SkRect &r)
Definition SkRectPriv.h:66
T * end()
Definition SkTDArray.h:152
int size() const
Definition SkTDArray.h:138
void reset()
Definition SkTDArray.h:171
void reserve(int n)
Definition SkTDArray.h:187
T * begin()
Definition SkTDArray.h:150
T * append()
Definition SkTDArray.h:191
void resize(int count)
Definition SkTDArray.h:183
void remove(int index, int count=1)
Definition SkTDArray.h:210
void pop_back()
Definition SkTDArray.h:223
void pop()
Definition SkTDPQueue.h:52
const T & peek() const
Definition SkTDPQueue.h:48
int count() const
Definition SkTDPQueue.h:45
void insert(T entry)
Definition SkTDPQueue.h:69
void remove(T *entry)
void addToHead(T *entry)
void addToTail(T *entry)
T * push_back_n(int n)
Definition SkTArray.h:262
int size() const
Definition SkTArray.h:416
static const char * begin(const StringSlice &s)
Definition editor.cpp:252
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
float SkScalar
Definition extension.cpp:12
struct MyStruct s
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
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition switches.h:145
Vec< 4, float > float4
Definition SkVx.h:1146
SIT T max(const Vec< 1, T > &x)
Definition SkVx.h:641
SIT T min(const Vec< 1, T > &x)
Definition SkVx.h:640
static SkRect inset(const SkRect &r)
SkScalar w
SkScalar h
int32_t height
int32_t width
Point offset
bool aboveIfLeft(const ActiveEdge *that) const
uint16_t fIndex1
ActiveEdge * fAbove
ActiveEdge * fBelow
bool intersect(const SkPoint &q0, const SkVector &w, uint16_t index0, uint16_t index1) const
ActiveEdge(const SkPoint &p0, const SkVector &v, uint16_t index0, uint16_t index1)
bool lessThan(const ActiveEdge *that) const
uint16_t fIndex0
bool above(const ActiveEdge *that) const
bool intersect(const ActiveEdge *edge)
ActiveEdge * fChild[2]
OffsetSegment fSegment
bool equals(uint16_t index0, uint16_t index1) const
bool checkIntersection(const OffsetEdge *that, SkPoint *p, SkScalar *s, SkScalar *t)
OffsetEdge * fNext
uint16_t fEnd
uint16_t fIndex
SkPoint fIntersection
OffsetSegment fOffset
SkScalar fTValue
OffsetEdge * fPrev
void init(uint16_t start=0, uint16_t end=0)
SkScalar computeCrossingDistance(const OffsetEdge *that)
bool setLength(float length)
Definition SkPoint.cpp:30
float fX
x-axis value
bool isFinite() const
float dot(const SkVector &vec) const
static constexpr SkPoint Make(float x, float y)
void set(float x, float y)
float cross(const SkVector &vec) const
float fY
y-axis value
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex)
uint16_t fFlags
uint16_t fIndex
static bool Left(const Vertex &qv0, const Vertex &qv1)
SkPoint fPosition
uint16_t fNextIndex
uint16_t fPrevIndex