blob: 06e854677b3700fe2d5d96f56880136aa1d3c99e [file] [log] [blame]
Brian Salomonab664fa2017-03-24 16:07:20 +00001/*
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
Jim Van Verth8664a1d2018-06-28 16:26:50 -04008#include "SkPolyUtils.h"
Brian Salomonab664fa2017-03-24 16:07:20 +00009
Cary Clarkdf429f32017-11-08 11:44:31 -050010#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040011#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000012#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040013#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040014#include "SkTInternalLList.h"
15
16//////////////////////////////////////////////////////////////////////////////////
17// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000018
Jim Van Verth4db18ed2018-04-03 10:00:37 -040019struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000020 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040021 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000022};
23
Jim Van Verthba4847c2018-08-07 16:02:33 -040024constexpr SkScalar kCrossTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
25
26// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
Brian Salomonab664fa2017-03-24 16:07:20 +000027// A positive value means the point is to the left of the segment,
28// negative is to the right, 0 is collinear.
Jim Van Verthba4847c2018-08-07 16:02:33 -040029static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
30 SkVector w = p - p0;
31 SkScalar perpDot = v.cross(w);
32 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
Brian Salomonab664fa2017-03-24 16:07:20 +000033 return ((perpDot > 0) ? 1 : -1);
34 }
35
36 return 0;
37}
38
Jim Van Verth6784ffa2018-07-03 16:12:39 -040039// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
40int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
41 if (polygonSize < 3) {
42 return 0;
43 }
44
Jim Van Verth8664a1d2018-06-28 16:26:50 -040045 // compute area and use sign to determine winding
46 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040047 SkVector v0 = polygonVerts[1] - polygonVerts[0];
48 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040049 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040050 SkVector v1 = polygonVerts[next] - polygonVerts[0];
51 quadArea += v0.cross(v1);
52 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000053 }
Jim Van Verthba4847c2018-08-07 16:02:33 -040054 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040055 return 0;
56 }
57 // 1 == ccw, -1 == cw
58 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000059}
60
Jim Van Verthda58cac2018-09-05 12:41:56 -040061// Compute difference vector to offset p0-p1 'offset' units in direction specified by 'side'
62void compute_offset_vector(const SkPoint& p0, const SkPoint& p1, SkScalar offset, int side,
63 SkPoint* vector) {
Jim Van Verthda965502017-04-11 15:29:14 -040064 SkASSERT(side == -1 || side == 1);
Jim Van Verthda58cac2018-09-05 12:41:56 -040065 // if distances are equal, can just outset by the perpendicular
66 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
67 perp.setLength(offset*side);
68 *vector = perp;
Jim Van Verthbdde4282018-06-14 09:09:18 -040069}
70
Jim Van Verthba4847c2018-08-07 16:02:33 -040071// check interval to see if intersection is in segment
72static inline bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive) {
73 return (denomPositive && (numer < 0 || numer > denom)) ||
74 (!denomPositive && (numer > 0 || numer < denom));
Jim Van Verth6784ffa2018-07-03 16:12:39 -040075}
76
Brian Salomonab664fa2017-03-24 16:07:20 +000077// Compute the intersection 'p' between segments s0 and s1, if any.
78// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
79// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -040080static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +000081 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Vertha6316832018-07-24 09:30:37 -040082 const SkVector& v0 = s0.fV;
83 const SkVector& v1 = s1.fV;
Jim Van Verthba4847c2018-08-07 16:02:33 -040084 SkVector w = s1.fP0 - s0.fP0;
85 SkScalar denom = v0.cross(v1);
86 bool denomPositive = (denom > 0);
87 SkScalar sNumer, tNumer;
88 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -040089 // segments are parallel, but not collinear
Jim Van Verthba4847c2018-08-07 16:02:33 -040090 if (!SkScalarNearlyZero(w.cross(v0), kCrossTolerance) ||
91 !SkScalarNearlyZero(w.cross(v1), kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -040092 return false;
93 }
94
Jim Van Verthba4847c2018-08-07 16:02:33 -040095 // Check for zero-length segments
Jim Van Verth6784ffa2018-07-03 16:12:39 -040096 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
Jim Van Verthba4847c2018-08-07 16:02:33 -040097 // Both are zero-length
Jim Van Verth6784ffa2018-07-03 16:12:39 -040098 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
99 // Check if they're the same point
Jim Van Verthba4847c2018-08-07 16:02:33 -0400100 if (!SkPointPriv::CanNormalize(w.fX, w.fY)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400101 *p = s0.fP0;
102 *s = 0;
103 *t = 0;
104 return true;
105 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400106 return false;
107 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400108 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400109 // Otherwise project segment0's origin onto segment1
110 tNumer = v1.dot(-w);
111 denom = v1.dot(v1);
112 if (outside_interval(tNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400113 return false;
114 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400115 sNumer = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400116 } else {
117 // Project segment1's endpoints onto segment0
Jim Van Verthba4847c2018-08-07 16:02:33 -0400118 sNumer = v0.dot(w);
119 denom = v0.dot(v0);
120 tNumer = 0;
121 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400122 // The first endpoint doesn't lie on segment0
123 // If segment1 is degenerate, then there's no collision
124 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
125 return false;
126 }
127
128 // Otherwise try the other one
Jim Van Verthba4847c2018-08-07 16:02:33 -0400129 SkScalar oldSNumer = sNumer;
130 sNumer = v0.dot(w + v1);
131 tNumer = denom;
132 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400133 // it's possible that segment1's interval surrounds segment0
134 // this is false if params have the same signs, and in that case no collision
Jim Van Verthba4847c2018-08-07 16:02:33 -0400135 if (sNumer*oldSNumer > 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400136 return false;
137 }
138 // otherwise project segment0's endpoint onto segment1 instead
Jim Van Verthba4847c2018-08-07 16:02:33 -0400139 sNumer = 0;
140 tNumer = v1.dot(-w);
141 denom = v1.dot(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400142 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400143 }
144 }
145 } else {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400146 sNumer = w.cross(v1);
147 if (outside_interval(sNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400148 return false;
149 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400150 tNumer = w.cross(v0);
151 if (outside_interval(tNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400152 return false;
153 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000154 }
155
Jim Van Verthba4847c2018-08-07 16:02:33 -0400156 SkScalar localS = sNumer/denom;
157 SkScalar localT = tNumer/denom;
158
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400159 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000160 *s = localS;
161 *t = localT;
162
163 return true;
164}
165
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400166bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
167 if (polygonSize < 3) {
168 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400169 }
170
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400171 SkScalar lastArea = 0;
172 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400173
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400174 int prevIndex = polygonSize - 1;
175 int currIndex = 0;
176 int nextIndex = 1;
177 SkPoint origin = polygonVerts[0];
178 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
179 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
180 SkVector w0 = polygonVerts[currIndex] - origin;
181 SkVector w1 = polygonVerts[nextIndex] - origin;
182 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400183 if (!polygonVerts[i].isFinite()) {
184 return false;
185 }
186
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400187 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400188 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400189 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400190 return false;
191 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400192 if (0 != perpDot) {
193 lastPerpDot = perpDot;
194 }
195
196 // If the signed area ever flips it's concave
197 // TODO: see if we can verify convexity only with signed area
198 SkScalar quadArea = w0.cross(w1);
199 if (quadArea*lastArea < 0) {
200 return false;
201 }
202 if (0 != quadArea) {
203 lastArea = quadArea;
204 }
205
206 prevIndex = currIndex;
207 currIndex = nextIndex;
208 nextIndex = (currIndex + 1) % polygonSize;
209 v0 = v1;
210 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
211 w0 = w1;
212 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400213 }
214
215 return true;
216}
Jim Van Verth0513f142017-03-24 14:28:57 -0400217
Jim Van Verth00673692018-07-23 11:23:39 -0400218struct OffsetEdge {
219 OffsetEdge* fPrev;
220 OffsetEdge* fNext;
Jim Van Verthda58cac2018-09-05 12:41:56 -0400221 OffsetSegment fOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400222 SkPoint fIntersection;
223 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400224 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400225 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400226
Jim Van Verth00673692018-07-23 11:23:39 -0400227 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400228 fIntersection = fOffset.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400229 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400230 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400231 fEnd = end;
232 }
233
234 // special intersection check that looks for endpoint intersection
235 bool checkIntersection(const OffsetEdge* that,
236 SkPoint* p, SkScalar* s, SkScalar* t) {
237 if (this->fEnd == that->fIndex) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400238 SkPoint p1 = this->fOffset.fP0 + this->fOffset.fV;
239 if (SkPointPriv::EqualsWithinTolerance(p1, that->fOffset.fP0)) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400240 *p = p1;
241 *s = SK_Scalar1;
242 *t = 0;
243 return true;
244 }
245 }
246
Jim Van Verthda58cac2018-09-05 12:41:56 -0400247 return compute_intersection(this->fOffset, that->fOffset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400248 }
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400249
Jim Van Verthba4847c2018-08-07 16:02:33 -0400250 // computes the line intersection and then the "distance" from that to this
251 // this is really a signed squared distance, where negative means that
Jim Van Verthda58cac2018-09-05 12:41:56 -0400252 // the intersection lies inside this->fOffset
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400253 SkScalar computeCrossingDistance(const OffsetEdge* that) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400254 const OffsetSegment& s0 = this->fOffset;
255 const OffsetSegment& s1 = that->fOffset;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400256 const SkVector& v0 = s0.fV;
257 const SkVector& v1 = s1.fV;
258
Jim Van Verthba4847c2018-08-07 16:02:33 -0400259 SkScalar denom = v0.cross(v1);
260 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400261 // segments are parallel
262 return SK_ScalarMax;
263 }
264
Jim Van Verthba4847c2018-08-07 16:02:33 -0400265 SkVector w = s1.fP0 - s0.fP0;
266 SkScalar localS = w.cross(v1) / denom;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400267 if (localS < 0) {
268 localS = -localS;
269 } else {
270 localS -= SK_Scalar1;
271 }
272
Jim Van Verthba4847c2018-08-07 16:02:33 -0400273 localS *= SkScalarAbs(localS);
274 localS *= v0.dot(v0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400275
276 return localS;
277 }
278
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400279};
280
Jim Van Verth00673692018-07-23 11:23:39 -0400281static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
282 // remove from linked list
283 node->fPrev->fNext = node->fNext;
284 node->fNext->fPrev = node->fPrev;
285 if (node == *head) {
286 *head = (node->fNext == node) ? nullptr : node->fNext;
287 }
288}
289
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400290//////////////////////////////////////////////////////////////////////////////////
291
Brian Salomonab664fa2017-03-24 16:07:20 +0000292// The objective here is to inset all of the edges by the given distance, and then
293// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
294// we should only be making left-hand turns (for cw polygons, we use the winding
295// parameter to reverse this). We detect this by checking whether the second intersection
296// on an edge is closer to its tail than the first one.
297//
298// We might also have the case that there is no intersection between two neighboring inset edges.
299// In this case, one edge will lie to the right of the other and should be discarded along with
300// its previous intersection (if any).
301//
302// Note: the assumption is that inputPolygon is convex and has no coincident points.
303//
304bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthda58cac2018-09-05 12:41:56 -0400305 SkScalar inset, SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000306 if (inputPolygonSize < 3) {
307 return false;
308 }
309
Jim Van Vertha6316832018-07-24 09:30:37 -0400310 // restrict this to match other routines
311 // practically we don't want anything bigger than this anyway
312 if (inputPolygonSize >= (1 << 16)) {
313 return false;
314 }
315
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400316 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400317 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000318 if (0 == winding) {
319 return false;
320 }
321
322 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400323 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
324 int prev = inputPolygonSize - 1;
325 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
326 int next = (curr + 1) % inputPolygonSize;
327 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400328 return false;
329 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400330 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400331 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400332 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400333 return false;
334 }
Jim Van Verthda58cac2018-09-05 12:41:56 -0400335 SkVector v = inputPolygonVerts[next] - inputPolygonVerts[curr];
336 SkVector perp = SkVector::Make(-v.fY, v.fX);
337 perp.setLength(inset*winding);
Jim Van Vertha6316832018-07-24 09:30:37 -0400338 edgeData[curr].fPrev = &edgeData[prev];
339 edgeData[curr].fNext = &edgeData[next];
Jim Van Verthda58cac2018-09-05 12:41:56 -0400340 edgeData[curr].fOffset.fP0 = inputPolygonVerts[curr] + perp;
341 edgeData[curr].fOffset.fV = v;
Jim Van Verth00673692018-07-23 11:23:39 -0400342 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000343 }
344
Jim Van Verth00673692018-07-23 11:23:39 -0400345 OffsetEdge* head = &edgeData[0];
346 OffsetEdge* currEdge = head;
347 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000348 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400349 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400350 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400351 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400352 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400353 if (iterations > inputPolygonSize*inputPolygonSize) {
354 return false;
355 }
356
Brian Salomonab664fa2017-03-24 16:07:20 +0000357 SkScalar s, t;
358 SkPoint intersection;
Jim Van Verthda58cac2018-09-05 12:41:56 -0400359 if (compute_intersection(prevEdge->fOffset, currEdge->fOffset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000360 &intersection, &s, &t)) {
361 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400362 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000363 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400364 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000365 --insetVertexCount;
366 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400367 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000368 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400369 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500370 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400371 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000372 1.0e-6f)) {
373 break;
374 } else {
375 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400376 currEdge->fIntersection = intersection;
377 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000378
379 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400380 prevEdge = currEdge;
381 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000382 }
383 } else {
384 // if prev to right side of curr
Jim Van Verthda58cac2018-09-05 12:41:56 -0400385 int side = winding*compute_side(currEdge->fOffset.fP0,
386 currEdge->fOffset.fV,
387 prevEdge->fOffset.fP0);
Jim Van Vertha6316832018-07-24 09:30:37 -0400388 if (side < 0 &&
Jim Van Verthda58cac2018-09-05 12:41:56 -0400389 side == winding*compute_side(currEdge->fOffset.fP0,
390 currEdge->fOffset.fV,
391 prevEdge->fOffset.fP0 + prevEdge->fOffset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000392 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400393 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000394 --insetVertexCount;
395 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400396 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000397 } else {
398 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400399 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400401 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000402 }
403 }
404 }
405
Jim Van Verthda965502017-04-11 15:29:14 -0400406 // store all the valid intersections that aren't nearly coincident
407 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000408 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400409 if (head) {
410 static constexpr SkScalar kCleanupTolerance = 0.01f;
411 if (insetVertexCount >= 0) {
412 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000413 }
Jim Van Verth00673692018-07-23 11:23:39 -0400414 int currIndex = 0;
415 OffsetEdge* currEdge = head;
416 *insetPolygon->push() = currEdge->fIntersection;
417 currEdge = currEdge->fNext;
418 while (currEdge != head) {
419 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
420 (*insetPolygon)[currIndex],
421 kCleanupTolerance)) {
422 *insetPolygon->push() = currEdge->fIntersection;
423 currIndex++;
424 }
425 currEdge = currEdge->fNext;
426 }
427 // make sure the first and last points aren't coincident
428 if (currIndex >= 1 &&
429 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
430 kCleanupTolerance)) {
431 insetPolygon->pop();
432 }
Jim Van Verthda965502017-04-11 15:29:14 -0400433 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000434
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400435 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000436}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400437
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400438///////////////////////////////////////////////////////////////////////////////////////////
439
440// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400441bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar offset,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400442 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400443 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
444
445 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400446 if (!SkScalarIsFinite(rCos)) {
447 return false;
448 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400449 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400450 if (!SkScalarIsFinite(rSin)) {
451 return false;
452 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400453 SkScalar theta = SkScalarATan2(rSin, rCos);
454
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400455 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
Jim Van Verth206dbe82018-07-23 11:48:31 -0400456 // limit the number of steps to at most max uint16_t (that's all we can index)
457 // knock one value off the top to account for rounding
458 if (floatSteps >= (1 << 16)-1) {
459 return false;
460 }
461 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400462
Jim Van Verth061cc212018-07-11 14:09:09 -0400463 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400464 *rotSin = SkScalarSinCos(dTheta, rotCos);
465 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400466 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400467}
468
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400469///////////////////////////////////////////////////////////////////////////////////////////
470
Jim Van Verth04d16322018-08-15 15:01:35 -0400471// a point is "left" to another if its x-coord is less, or if equal, its y-coord is greater
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400472static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400473 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY > p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400474}
475
Jim Van Verth04d16322018-08-15 15:01:35 -0400476// a point is "right" to another if its x-coord is greater, or if equal, its y-coord is less
477static bool right(const SkPoint& p0, const SkPoint& p1) {
478 return p0.fX > p1.fX || (!(p0.fX < p1.fX) && p0.fY < p1.fY);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400479}
480
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400481struct Vertex {
482 static bool Left(const Vertex& qv0, const Vertex& qv1) {
483 return left(qv0.fPosition, qv1.fPosition);
484 }
Jim Van Verth00673692018-07-23 11:23:39 -0400485
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400486 // packed to fit into 16 bytes (one cache line)
487 SkPoint fPosition;
488 uint16_t fIndex; // index in unsorted polygon
489 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
490 uint16_t fNextIndex;
491 uint16_t fFlags;
492};
493
494enum VertexFlags {
495 kPrevLeft_VertexFlag = 0x1,
496 kNextLeft_VertexFlag = 0x2,
497};
498
Jim Van Verth00673692018-07-23 11:23:39 -0400499struct ActiveEdge {
Jim Van Verth04d16322018-08-15 15:01:35 -0400500 ActiveEdge() : fChild{ nullptr, nullptr }, fAbove(nullptr), fBelow(nullptr), fRed(false) {}
501 ActiveEdge(const SkPoint& p0, const SkVector& v, uint16_t index0, uint16_t index1)
502 : fSegment({ p0, v })
Jim Van Verth00673692018-07-23 11:23:39 -0400503 , fIndex0(index0)
Jim Van Verth04d16322018-08-15 15:01:35 -0400504 , fIndex1(index1)
505 , fAbove(nullptr)
506 , fBelow(nullptr)
507 , fRed(true) {
508 fChild[0] = nullptr;
509 fChild[1] = nullptr;
510 }
Jim Van Verth00673692018-07-23 11:23:39 -0400511
Jim Van Verth04d16322018-08-15 15:01:35 -0400512 // Returns true if "this" is above "that", assuming this->p0 is to the left of that->p0
513 // This is only used to verify the edgelist -- the actual test for insertion/deletion is much
514 // simpler because we can make certain assumptions then.
515 bool aboveIfLeft(const ActiveEdge* that) const {
516 const SkPoint& p0 = this->fSegment.fP0;
517 const SkPoint& q0 = that->fSegment.fP0;
518 SkASSERT(p0.fX <= q0.fX);
519 SkVector d = q0 - p0;
520 const SkVector& v = this->fSegment.fV;
521 const SkVector& w = that->fSegment.fV;
522 // The idea here is that if the vector between the origins of the two segments (d)
523 // rotates counterclockwise up to the vector representing the "this" segment (v),
524 // then we know that "this" is above "that". If the result is clockwise we say it's below.
525 if (this->fIndex0 != that->fIndex0) {
526 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400527 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400528 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400529 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400530 return false;
531 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400532 } else if (this->fIndex1 == that->fIndex1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400533 return false;
534 }
Jim Van Verth00673692018-07-23 11:23:39 -0400535 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400536 // lies on dv. So then we try the same for the vector from the tail of "this"
537 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Verth04d16322018-08-15 15:01:35 -0400538 // d = that.P1 - this.P0
539 // = that.fP0 + that.fV - this.fP0
540 // = that.fP0 - this.fP0 + that.fV
541 // = old_d + that.fV
542 d += w;
543 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400544 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400545 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400546 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400547 return false;
548 }
549 // If the previous check fails, the two segments are nearly collinear
550 // First check y-coord of first endpoints
Jim Van Verth04d16322018-08-15 15:01:35 -0400551 if (p0.fX < q0.fX) {
552 return (p0.fY >= q0.fY);
553 } else if (p0.fY > q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400554 return true;
Jim Van Verth04d16322018-08-15 15:01:35 -0400555 } else if (p0.fY < q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400556 return false;
557 }
558 // The first endpoints are the same, so check the other endpoint
Jim Van Verth04d16322018-08-15 15:01:35 -0400559 SkPoint p1 = p0 + v;
560 SkPoint q1 = q0 + w;
561 if (p1.fX < q1.fX) {
562 return (p1.fY >= q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400563 } else {
Jim Van Verth04d16322018-08-15 15:01:35 -0400564 return (p1.fY > q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400565 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400566 }
567
Jim Van Verth04d16322018-08-15 15:01:35 -0400568 // same as leftAndAbove(), but generalized
569 bool above(const ActiveEdge* that) const {
570 const SkPoint& p0 = this->fSegment.fP0;
571 const SkPoint& q0 = that->fSegment.fP0;
572 if (right(p0, q0)) {
573 return !that->aboveIfLeft(this);
574 } else {
575 return this->aboveIfLeft(that);
576 }
577 }
578
579 bool intersect(const SkPoint& q0, const SkVector& w, uint16_t index0, uint16_t index1) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400580 // check first to see if these edges are neighbors in the polygon
Jim Van Verth04d16322018-08-15 15:01:35 -0400581 if (this->fIndex0 == index0 || this->fIndex1 == index0 ||
582 this->fIndex0 == index1 || this->fIndex1 == index1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400583 return false;
584 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400585
586 // We don't need the exact intersection point so we can do a simpler test here.
587 const SkPoint& p0 = this->fSegment.fP0;
588 const SkVector& v = this->fSegment.fV;
589 SkPoint p1 = p0 + v;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400590 SkPoint q1 = q0 + w;
591
Jim Van Verth04d16322018-08-15 15:01:35 -0400592 // We assume some x-overlap due to how the edgelist works
593 // This allows us to simplify our test
594 // We need some slop here because storing the vector and recomputing the second endpoint
595 // doesn't necessary give us the original result in floating point.
596 // TODO: Store vector as double? Store endpoint as well?
597 SkASSERT(q0.fX <= p1.fX + SK_ScalarNearlyZero);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400598
599 // if each segment straddles the other (i.e., the endpoints have different sides)
600 // then they intersect
Jim Van Verth04d16322018-08-15 15:01:35 -0400601 bool result;
602 if (p0.fX < q0.fX) {
603 if (q1.fX < p1.fX) {
604 result = (compute_side(p0, v, q0)*compute_side(p0, v, q1) < 0);
605 } else {
606 result = (compute_side(p0, v, q0)*compute_side(q0, w, p1) > 0);
607 }
608 } else {
609 if (p1.fX < q1.fX) {
610 result = (compute_side(q0, w, p0)*compute_side(q0, w, p1) < 0);
611 } else {
612 result = (compute_side(q0, w, p0)*compute_side(p0, v, q1) > 0);
613 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400614 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400615 return result;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400616 }
617
Jim Van Verth04d16322018-08-15 15:01:35 -0400618 bool intersect(const ActiveEdge* edge) {
619 return this->intersect(edge->fSegment.fP0, edge->fSegment.fV, edge->fIndex0, edge->fIndex1);
620 }
621
622 bool lessThan(const ActiveEdge* that) const {
623 SkASSERT(!this->above(this));
624 SkASSERT(!that->above(that));
625 SkASSERT(!(this->above(that) && that->above(this)));
Jim Van Verth00673692018-07-23 11:23:39 -0400626 return this->above(that);
627 }
628
Jim Van Verth04d16322018-08-15 15:01:35 -0400629 bool equals(uint16_t index0, uint16_t index1) const {
630 return (this->fIndex0 == index0 && this->fIndex1 == index1);
Jim Van Verth00673692018-07-23 11:23:39 -0400631 }
632
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400633 OffsetSegment fSegment;
Jim Van Verth04d16322018-08-15 15:01:35 -0400634 uint16_t fIndex0; // indices for previous and next vertex in polygon
Jim Van Vertha6316832018-07-24 09:30:37 -0400635 uint16_t fIndex1;
Jim Van Verth04d16322018-08-15 15:01:35 -0400636 ActiveEdge* fChild[2];
637 ActiveEdge* fAbove;
638 ActiveEdge* fBelow;
639 int32_t fRed;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400640};
641
Jim Van Verth00673692018-07-23 11:23:39 -0400642class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400643public:
Jim Van Verth04d16322018-08-15 15:01:35 -0400644 ActiveEdgeList(int maxEdges) {
645 fAllocation = (char*) sk_malloc_throw(sizeof(ActiveEdge)*maxEdges);
646 fCurrFree = 0;
647 fMaxFree = maxEdges;
648 }
649 ~ActiveEdgeList() {
650 fTreeHead.fChild[1] = nullptr;
651 sk_free(fAllocation);
652 }
653
Jim Van Vertha6316832018-07-24 09:30:37 -0400654 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400655 SkVector v = p1 - p0;
656 // empty tree case -- easy
657 if (!fTreeHead.fChild[1]) {
658 ActiveEdge* root = fTreeHead.fChild[1] = this->allocate(p0, v, index0, index1);
659 SkASSERT(root);
660 if (!root) {
661 return false;
662 }
663 root->fRed = false;
664 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400665 }
666
Jim Van Verth04d16322018-08-15 15:01:35 -0400667 // set up helpers
668 ActiveEdge* top = &fTreeHead;
669 ActiveEdge *grandparent = nullptr;
670 ActiveEdge *parent = nullptr;
671 ActiveEdge *curr = top->fChild[1];
672 int dir = 0;
673 int last = 0; // ?
674 // predecessor and successor, for intersection check
675 ActiveEdge* pred = nullptr;
676 ActiveEdge* succ = nullptr;
677
678 // search down the tree
679 while (true) {
680 if (!curr) {
681 // check for intersection with predecessor and successor
682 if ((pred && pred->intersect(p0, v, index0, index1)) ||
683 (succ && succ->intersect(p0, v, index0, index1))) {
684 return false;
685 }
686 // insert new node at bottom
687 parent->fChild[dir] = curr = this->allocate(p0, v, index0, index1);
688 SkASSERT(curr);
689 if (!curr) {
690 return false;
691 }
692 curr->fAbove = pred;
693 curr->fBelow = succ;
694 if (pred) {
695 pred->fBelow = curr;
696 }
697 if (succ) {
698 succ->fAbove = curr;
699 }
700 if (IsRed(parent)) {
701 int dir2 = (top->fChild[1] == grandparent);
702 if (curr == parent->fChild[last]) {
703 top->fChild[dir2] = SingleRotation(grandparent, !last);
704 } else {
705 top->fChild[dir2] = DoubleRotation(grandparent, !last);
706 }
707 }
708 break;
709 } else if (IsRed(curr->fChild[0]) && IsRed(curr->fChild[1])) {
710 // color flip
711 curr->fRed = true;
712 curr->fChild[0]->fRed = false;
713 curr->fChild[1]->fRed = false;
714 if (IsRed(parent)) {
715 int dir2 = (top->fChild[1] == grandparent);
716 if (curr == parent->fChild[last]) {
717 top->fChild[dir2] = SingleRotation(grandparent, !last);
718 } else {
719 top->fChild[dir2] = DoubleRotation(grandparent, !last);
720 }
721 }
722 }
723
724 last = dir;
725 int side;
726 // check to see if segment is above or below
727 if (curr->fIndex0 == index0) {
728 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
729 } else {
730 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
731 }
732 if (0 == side) {
733 return false;
734 }
735 dir = (side < 0);
736
737 if (0 == dir) {
738 succ = curr;
739 } else {
740 pred = curr;
741 }
742
743 // update helpers
744 if (grandparent) {
745 top = grandparent;
746 }
747 grandparent = parent;
748 parent = curr;
749 curr = curr->fChild[dir];
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400750 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400751
752 // update root and make it black
753 fTreeHead.fChild[1]->fRed = false;
754
755 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400756
757 return true;
758 }
759
Jim Van Verth04d16322018-08-15 15:01:35 -0400760 // replaces edge p0p1 with p1p2
761 bool replace(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
762 uint16_t index0, uint16_t index1, uint16_t index2) {
763 if (!fTreeHead.fChild[1]) {
Jim Van Verth00673692018-07-23 11:23:39 -0400764 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400765 }
766
Jim Van Verth04d16322018-08-15 15:01:35 -0400767 SkVector v = p2 - p1;
768 ActiveEdge* curr = &fTreeHead;
769 ActiveEdge* found = nullptr;
770 int dir = 1;
771
772 // search
773 while (curr->fChild[dir] != nullptr) {
774 // update helpers
775 curr = curr->fChild[dir];
776 // save found node
777 if (curr->equals(index0, index1)) {
778 found = curr;
779 break;
780 } else {
781 // check to see if segment is above or below
782 int side;
783 if (curr->fIndex1 == index1) {
784 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
785 } else {
786 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
787 }
788 if (0 == side) {
789 return false;
790 }
791 dir = (side < 0);
792 }
793 }
794
795 if (!found) {
796 return false;
797 }
798
799 // replace if found
800 ActiveEdge* pred = found->fAbove;
801 ActiveEdge* succ = found->fBelow;
802 // check deletion and insert intersection cases
803 if (pred && (pred->intersect(found) || pred->intersect(p1, v, index1, index2))) {
804 return false;
805 }
806 if (succ && (succ->intersect(found) || succ->intersect(p1, v, index1, index2))) {
807 return false;
808 }
809 found->fSegment.fP0 = p1;
810 found->fSegment.fV = v;
811 found->fIndex0 = index1;
812 found->fIndex1 = index2;
813 // above and below should stay the same
814
815 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
816
817 return true;
818 }
819
820 bool remove(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
821 if (!fTreeHead.fChild[1]) {
822 return false;
823 }
824
825 ActiveEdge* curr = &fTreeHead;
826 ActiveEdge* parent = nullptr;
827 ActiveEdge* grandparent = nullptr;
828 ActiveEdge* found = nullptr;
829 int dir = 1;
830
831 // search and push a red node down
832 while (curr->fChild[dir] != nullptr) {
833 int last = dir;
834
835 // update helpers
836 grandparent = parent;
837 parent = curr;
838 curr = curr->fChild[dir];
839 // save found node
840 if (curr->equals(index0, index1)) {
841 found = curr;
842 dir = 0;
843 } else {
844 // check to see if segment is above or below
845 int side;
846 if (curr->fIndex1 == index1) {
847 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
848 } else {
849 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
850 }
851 if (0 == side) {
852 return false;
853 }
854 dir = (side < 0);
855 }
856
857 // push the red node down
858 if (!IsRed(curr) && !IsRed(curr->fChild[dir])) {
859 if (IsRed(curr->fChild[!dir])) {
860 parent = parent->fChild[last] = SingleRotation(curr, dir);
861 } else {
862 ActiveEdge *s = parent->fChild[!last];
863
864 if (s != NULL) {
865 if (!IsRed(s->fChild[!last]) && !IsRed(s->fChild[last])) {
866 // color flip
867 parent->fRed = false;
868 s->fRed = true;
869 curr->fRed = true;
870 } else {
871 int dir2 = (grandparent->fChild[1] == parent);
872
873 if (IsRed(s->fChild[last])) {
874 grandparent->fChild[dir2] = DoubleRotation(parent, last);
875 } else if (IsRed(s->fChild[!last])) {
876 grandparent->fChild[dir2] = SingleRotation(parent, last);
877 }
878
879 // ensure correct coloring
880 curr->fRed = grandparent->fChild[dir2]->fRed = true;
881 grandparent->fChild[dir2]->fChild[0]->fRed = false;
882 grandparent->fChild[dir2]->fChild[1]->fRed = false;
883 }
884 }
885 }
886 }
887 }
888
889 // replace and remove if found
890 if (found) {
891 ActiveEdge* pred = found->fAbove;
892 ActiveEdge* succ = found->fBelow;
893 if ((pred && pred->intersect(found)) || (succ && succ->intersect(found))) {
894 return false;
895 }
896 if (found != curr) {
897 found->fSegment = curr->fSegment;
898 found->fIndex0 = curr->fIndex0;
899 found->fIndex1 = curr->fIndex1;
900 found->fAbove = curr->fAbove;
901 pred = found->fAbove;
902 // we don't need to set found->fBelow here
903 } else {
904 if (succ) {
905 succ->fAbove = pred;
906 }
907 }
908 if (pred) {
909 pred->fBelow = curr->fBelow;
910 }
911 parent->fChild[parent->fChild[1] == curr] = curr->fChild[!curr->fChild[0]];
912
913 // no need to delete
914 curr->fAbove = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
915 curr->fBelow = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
916 if (fTreeHead.fChild[1]) {
917 fTreeHead.fChild[1]->fRed = false;
918 }
919 }
920
921 // update root and make it black
922 if (fTreeHead.fChild[1]) {
923 fTreeHead.fChild[1]->fRed = false;
924 }
925
926 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
927
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400928 return true;
929 }
930
931private:
Jim Van Verth04d16322018-08-15 15:01:35 -0400932 // allocator
933 ActiveEdge * allocate(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
934 if (fCurrFree >= fMaxFree) {
935 return nullptr;
936 }
937 char* bytes = fAllocation + sizeof(ActiveEdge)*fCurrFree;
938 ++fCurrFree;
939 return new(bytes) ActiveEdge(p0, p1, index0, index1);
940 }
941
942 ///////////////////////////////////////////////////////////////////////////////////
943 // Red-black tree methods
944 ///////////////////////////////////////////////////////////////////////////////////
945 static bool IsRed(const ActiveEdge* node) {
946 return node && node->fRed;
947 }
948
949 static ActiveEdge* SingleRotation(ActiveEdge* node, int dir) {
950 ActiveEdge* tmp = node->fChild[!dir];
951
952 node->fChild[!dir] = tmp->fChild[dir];
953 tmp->fChild[dir] = node;
954
955 node->fRed = true;
956 tmp->fRed = false;
957
958 return tmp;
959 }
960
961 static ActiveEdge* DoubleRotation(ActiveEdge* node, int dir) {
962 node->fChild[!dir] = SingleRotation(node->fChild[!dir], !dir);
963
964 return SingleRotation(node, dir);
965 }
966
967 // returns black link count
968 static int VerifyTree(const ActiveEdge* tree) {
969 if (!tree) {
970 return 1;
971 }
972
973 const ActiveEdge* left = tree->fChild[0];
974 const ActiveEdge* right = tree->fChild[1];
975
976 // no consecutive red links
977 if (IsRed(tree) && (IsRed(left) || IsRed(right))) {
978 SkASSERT(false);
979 return 0;
980 }
981
982 // check secondary links
983 if (tree->fAbove) {
984 SkASSERT(tree->fAbove->fBelow == tree);
985 SkASSERT(tree->fAbove->lessThan(tree));
986 }
987 if (tree->fBelow) {
988 SkASSERT(tree->fBelow->fAbove == tree);
989 SkASSERT(tree->lessThan(tree->fBelow));
990 }
991
992 // violates binary tree order
993 if ((left && tree->lessThan(left)) || (right && right->lessThan(tree))) {
994 SkASSERT(false);
995 return 0;
996 }
997
998 int leftCount = VerifyTree(left);
999 int rightCount = VerifyTree(right);
1000
1001 // return black link count
1002 if (leftCount != 0 && rightCount != 0) {
1003 // black height mismatch
1004 if (leftCount != rightCount) {
1005 SkASSERT(false);
1006 return 0;
1007 }
1008 return IsRed(tree) ? leftCount : leftCount + 1;
1009 } else {
1010 return 0;
1011 }
1012 }
1013
1014 ActiveEdge fTreeHead;
1015 char* fAllocation;
1016 int fCurrFree;
1017 int fMaxFree;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001018};
1019
1020// Here we implement a sweep line algorithm to determine whether the provided points
1021// represent a simple polygon, i.e., the polygon is non-self-intersecting.
1022// We first insert the vertices into a priority queue sorting horizontally from left to right.
1023// Then as we pop the vertices from the queue we generate events which indicate that an edge
1024// should be added or removed from an edge list. If any intersections are detected in the edge
1025// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001026bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001027 if (polygonSize < 3) {
1028 return false;
1029 }
1030
Jim Van Vertha6316832018-07-24 09:30:37 -04001031 // need to be able to represent all the vertices in the 16-bit indices
1032 if (polygonSize >= (1 << 16)) {
1033 return false;
1034 }
1035
Jim Van Verth04d16322018-08-15 15:01:35 -04001036 // If it's convex, it's simple
1037 if (SkIsConvexPolygon(polygon, polygonSize)) {
1038 return true;
1039 }
1040
Jim Van Verth00673692018-07-23 11:23:39 -04001041 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001042 for (int i = 0; i < polygonSize; ++i) {
1043 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001044 if (!polygon[i].isFinite()) {
1045 return false;
1046 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001047 newVertex.fPosition = polygon[i];
1048 newVertex.fIndex = i;
1049 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1050 newVertex.fNextIndex = (i + 1) % polygonSize;
1051 newVertex.fFlags = 0;
1052 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1053 newVertex.fFlags |= kPrevLeft_VertexFlag;
1054 }
1055 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1056 newVertex.fFlags |= kNextLeft_VertexFlag;
1057 }
1058 vertexQueue.insert(newVertex);
1059 }
1060
1061 // pop each vertex from the queue and generate events depending on
1062 // where it lies relative to its neighboring edges
Jim Van Verth04d16322018-08-15 15:01:35 -04001063 ActiveEdgeList sweepLine(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001064 while (vertexQueue.count() > 0) {
1065 const Vertex& v = vertexQueue.peek();
1066
Jim Van Verth04d16322018-08-15 15:01:35 -04001067 // both to the right -- insert both
1068 if (v.fFlags == 0) {
Jim Van Verth00673692018-07-23 11:23:39 -04001069 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001070 break;
1071 }
Jim Van Verth00673692018-07-23 11:23:39 -04001072 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001073 break;
1074 }
Jim Van Verth04d16322018-08-15 15:01:35 -04001075 // both to the left -- remove both
1076 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1077 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1078 break;
1079 }
1080 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1081 break;
1082 }
1083 // one to left and right -- replace one with another
1084 } else {
1085 if (v.fFlags & kPrevLeft_VertexFlag) {
1086 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1087 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1088 break;
1089 }
1090 } else {
1091 SkASSERT(v.fFlags & kNextLeft_VertexFlag);
1092 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1093 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1094 break;
1095 }
1096 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001097 }
1098
1099 vertexQueue.pop();
1100 }
1101
1102 return (vertexQueue.count() == 0);
1103}
1104
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001105///////////////////////////////////////////////////////////////////////////////////////////
1106
Jim Van Verth00673692018-07-23 11:23:39 -04001107// helper function for SkOffsetSimplePolygon
1108static void setup_offset_edge(OffsetEdge* currEdge,
1109 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001110 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verthda58cac2018-09-05 12:41:56 -04001111 currEdge->fOffset.fP0 = endpoint0;
1112 currEdge->fOffset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -04001113 currEdge->init(startIndex, endIndex);
1114}
1115
Jim Van Verth98d33752018-08-03 15:59:46 -04001116static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
1117 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
1118 int side = compute_side(inputPolygonVerts[prevIndex],
1119 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
1120 inputPolygonVerts[nextIndex]);
1121 // if reflex point, we need to add extra edges
1122 return (side*winding*offset < 0);
1123}
1124
Jim Van Verthda58cac2018-09-05 12:41:56 -04001125bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize, SkScalar offset,
Jim Van Verthbdde4282018-06-14 09:09:18 -04001126 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001127 if (inputPolygonSize < 3) {
1128 return false;
1129 }
1130
Jim Van Vertha6316832018-07-24 09:30:37 -04001131 // need to be able to represent all the vertices in the 16-bit indices
1132 if (inputPolygonSize >= (1 << 16)) {
1133 return false;
1134 }
1135
Jim Van Verthda58cac2018-09-05 12:41:56 -04001136 if (!SkScalarIsFinite(offset)) {
1137 return false;
1138 }
1139
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001140 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001141 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001142 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001143 return false;
1144 }
1145
Jim Van Verthbdde4282018-06-14 09:09:18 -04001146 // build normals
Jim Van Verthda58cac2018-09-05 12:41:56 -04001147 SkAutoSTMalloc<64, SkVector> normals(inputPolygonSize);
Jim Van Verth98d33752018-08-03 15:59:46 -04001148 int numEdges = 0;
1149 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1150 currIndex < inputPolygonSize;
1151 prevIndex = currIndex, ++currIndex) {
1152 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001153 return false;
1154 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001155 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001156 compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
1157 offset, winding, &normals[currIndex]);
Jim Van Verth98d33752018-08-03 15:59:46 -04001158 if (currIndex > 0) {
1159 // if reflex point, we need to add extra edges
Jim Van Verthda58cac2018-09-05 12:41:56 -04001160 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001161 prevIndex, currIndex, nextIndex)) {
1162 SkScalar rotSin, rotCos;
1163 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001164 if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001165 &rotSin, &rotCos, &numSteps)) {
1166 return false;
1167 }
1168 numEdges += SkTMax(numSteps, 1);
1169 }
1170 }
1171 numEdges++;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001172 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001173 // finish up the edge counting
Jim Van Verthda58cac2018-09-05 12:41:56 -04001174 if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
Jim Van Verth98d33752018-08-03 15:59:46 -04001175 SkScalar rotSin, rotCos;
1176 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001177 if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001178 &rotSin, &rotCos, &numSteps)) {
1179 return false;
1180 }
1181 numEdges += SkTMax(numSteps, 1);
1182 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001183
1184 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -04001185 SkSTArray<64, OffsetEdge> edgeData(numEdges);
1186 OffsetEdge* prevEdge = nullptr;
1187 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1188 currIndex < inputPolygonSize;
1189 prevIndex = currIndex, ++currIndex) {
1190 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001191 // if reflex point, fill in curve
Jim Van Verthda58cac2018-09-05 12:41:56 -04001192 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001193 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001194 SkScalar rotSin, rotCos;
1195 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001196 SkVector prevNormal = normals[prevIndex];
1197 if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
Jim Van Verth061cc212018-07-11 14:09:09 -04001198 &rotSin, &rotCos, &numSteps)) {
1199 return false;
1200 }
Jim Van Verth00673692018-07-23 11:23:39 -04001201 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001202 for (int i = 0; i < numSteps - 1; ++i) {
1203 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1204 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -04001205 setup_offset_edge(currEdge,
1206 inputPolygonVerts[currIndex] + prevNormal,
1207 inputPolygonVerts[currIndex] + currNormal,
1208 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001209 prevNormal = currNormal;
Jim Van Verth98d33752018-08-03 15:59:46 -04001210 currEdge->fPrev = prevEdge;
1211 if (prevEdge) {
1212 prevEdge->fNext = currEdge;
1213 }
1214 prevEdge = currEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001215 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001216 }
Jim Van Verth00673692018-07-23 11:23:39 -04001217 setup_offset_edge(currEdge,
1218 inputPolygonVerts[currIndex] + prevNormal,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001219 inputPolygonVerts[currIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001220 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001221 currEdge->fPrev = prevEdge;
1222 if (prevEdge) {
1223 prevEdge->fNext = currEdge;
1224 }
1225 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001226 }
1227
1228 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -04001229 auto currEdge = edgeData.push_back_n(1);
1230 setup_offset_edge(currEdge,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001231 inputPolygonVerts[currIndex] + normals[currIndex],
1232 inputPolygonVerts[nextIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001233 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001234 currEdge->fPrev = prevEdge;
1235 if (prevEdge) {
1236 prevEdge->fNext = currEdge;
1237 }
1238 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001239 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001240 // close up the linked list
1241 SkASSERT(prevEdge);
1242 prevEdge->fNext = &edgeData[0];
1243 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001244
1245 // now clip edges
Jim Van Verth98d33752018-08-03 15:59:46 -04001246 SkASSERT(edgeData.count() == numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -04001247 auto head = &edgeData[0];
1248 auto currEdge = head;
Jim Van Verth98d33752018-08-03 15:59:46 -04001249 int offsetVertexCount = numEdges;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001250 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -04001251 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001252 ++iterations;
1253 // we should check each edge against each other edge at most once
Jim Van Verth98d33752018-08-03 15:59:46 -04001254 if (iterations > numEdges*numEdges) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001255 return false;
1256 }
1257
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001258 SkScalar s, t;
1259 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -04001260 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001261 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001262 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001263 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -04001264 remove_node(prevEdge, &head);
1265 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001266 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -04001267 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001268 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -04001269 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001270 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -04001271 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001272 1.0e-6f)) {
1273 break;
1274 } else {
1275 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001276 currEdge->fIntersection = intersection;
1277 currEdge->fTValue = t;
1278 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001279
1280 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -04001281 prevEdge = currEdge;
1282 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001283 }
1284 } else {
1285 // If there is no intersection, we want to minimize the distance between
1286 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -04001287 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1288 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001289 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1290 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1291 // if both lead to direct collision
1292 if (dist0 < 0 && dist1 < 0) {
1293 // check first to see if either represent parts of one contour
Jim Van Verthda58cac2018-09-05 12:41:56 -04001294 SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001295 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001296 prevEdge->fOffset.fP0);
1297 p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001298 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001299 currNextEdge->fOffset.fP0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001300
1301 // want to step along contour to find intersections rather than jump to new one
1302 if (currSameContour && !prevSameContour) {
1303 remove_node(currEdge, &head);
1304 currEdge = currNextEdge;
1305 --offsetVertexCount;
1306 continue;
1307 } else if (prevSameContour && !currSameContour) {
1308 remove_node(prevEdge, &head);
1309 prevEdge = prevPrevEdge;
1310 --offsetVertexCount;
1311 continue;
1312 }
1313 }
1314
1315 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001316 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -04001317 remove_node(prevEdge, &head);
1318 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001319 } else {
Jim Van Verth00673692018-07-23 11:23:39 -04001320 remove_node(currEdge, &head);
1321 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001322 }
Jim Van Verth00673692018-07-23 11:23:39 -04001323 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001324 }
1325 }
1326
1327 // store all the valid intersections that aren't nearly coincident
1328 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001329 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -04001330 if (head) {
1331 static constexpr SkScalar kCleanupTolerance = 0.01f;
1332 if (offsetVertexCount >= 0) {
1333 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +00001334 }
Jim Van Verth00673692018-07-23 11:23:39 -04001335 int currIndex = 0;
1336 OffsetEdge* currEdge = head;
1337 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +00001338 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -04001339 *polygonIndices->push() = currEdge->fIndex;
1340 }
1341 currEdge = currEdge->fNext;
1342 while (currEdge != head) {
1343 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1344 (*offsetPolygon)[currIndex],
1345 kCleanupTolerance)) {
1346 *offsetPolygon->push() = currEdge->fIntersection;
1347 if (polygonIndices) {
1348 *polygonIndices->push() = currEdge->fIndex;
1349 }
1350 currIndex++;
1351 }
1352 currEdge = currEdge->fNext;
1353 }
1354 // make sure the first and last points aren't coincident
1355 if (currIndex >= 1 &&
1356 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1357 kCleanupTolerance)) {
1358 offsetPolygon->pop();
1359 if (polygonIndices) {
1360 polygonIndices->pop();
1361 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001362 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001363 }
1364
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001365 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001366 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001367
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001368 return (winding*offsetWinding > 0 &&
1369 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001370}
1371
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001372//////////////////////////////////////////////////////////////////////////////////////////
1373
1374struct TriangulationVertex {
1375 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1376
1377 enum class VertexType { kConvex, kReflex };
1378
1379 SkPoint fPosition;
1380 VertexType fVertexType;
1381 uint16_t fIndex;
1382 uint16_t fPrevIndex;
1383 uint16_t fNextIndex;
1384};
1385
1386// test to see if point p is in triangle p0p1p2.
1387// for now assuming strictly inside -- if on the edge it's outside
1388static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1389 const SkPoint& p) {
1390 SkVector v0 = p1 - p0;
1391 SkVector v1 = p2 - p1;
1392 SkScalar n = v0.cross(v1);
1393
1394 SkVector w0 = p - p0;
1395 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1396 return false;
1397 }
1398
1399 SkVector w1 = p - p1;
1400 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1401 return false;
1402 }
1403
1404 SkVector v2 = p0 - p2;
1405 SkVector w2 = p - p2;
1406 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1407 return false;
1408 }
1409
1410 return true;
1411}
1412
1413// Data structure to track reflex vertices and check whether any are inside a given triangle
1414class ReflexHash {
1415public:
1416 void add(TriangulationVertex* v) {
1417 fReflexList.addToTail(v);
1418 }
1419
1420 void remove(TriangulationVertex* v) {
1421 fReflexList.remove(v);
1422 }
1423
Jim Van Verth061cc212018-07-11 14:09:09 -04001424 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1425 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001426 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1427 reflexIter != fReflexList.end(); ++reflexIter) {
1428 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001429 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1430 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001431 return true;
1432 }
1433 }
1434
1435 return false;
1436 }
1437
1438private:
1439 // TODO: switch to an actual spatial hash
1440 SkTInternalLList<TriangulationVertex> fReflexList;
1441};
1442
1443// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1444static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1445 int winding, ReflexHash* reflexHash,
1446 SkTInternalLList<TriangulationVertex>* convexList) {
1447 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1448 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1449 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001450 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001451 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1452 reflexHash->remove(p);
1453 p->fPrev = p->fNext = nullptr;
1454 convexList->addToTail(p);
1455 }
1456 }
1457}
1458
1459bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1460 SkTDArray<uint16_t>* triangleIndices) {
1461 if (polygonSize < 3) {
1462 return false;
1463 }
1464 // need to be able to represent all the vertices in the 16-bit indices
1465 if (polygonSize >= (1 << 16)) {
1466 return false;
1467 }
1468
1469 // get winding direction
1470 // TODO: we do this for all the polygon routines -- might be better to have the client
1471 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001472 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001473 if (0 == winding) {
1474 return false;
1475 }
1476
1477 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1478 // TODO: possibly sort the convexList in some way to get better triangles
1479 SkTInternalLList<TriangulationVertex> convexList;
1480 ReflexHash reflexHash;
1481 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1482 int prevIndex = polygonSize - 1;
1483 int currIndex = 0;
1484 int nextIndex = 1;
1485 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1486 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1487 for (int i = 0; i < polygonSize; ++i) {
1488 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1489 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1490 triangulationVertices[currIndex].fIndex = currIndex;
1491 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1492 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001493 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001494 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1495 convexList.addToTail(&triangulationVertices[currIndex]);
1496 } else {
1497 // We treat near collinear vertices as reflex
1498 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1499 reflexHash.add(&triangulationVertices[currIndex]);
1500 }
1501
1502 prevIndex = currIndex;
1503 currIndex = nextIndex;
1504 nextIndex = (currIndex + 1) % polygonSize;
1505 v0 = v1;
1506 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1507 }
1508
1509 // The general concept: We are trying to find three neighboring vertices where
1510 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1511 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1512 // we have triangulated the entire polygon.
1513 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1514 // noting that only convex vertices can be potential ears, and we only need to check whether
1515 // any reflex vertices lie inside the ear.
1516 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1517 int vertexCount = polygonSize;
1518 while (vertexCount > 3) {
1519 bool success = false;
1520 TriangulationVertex* earVertex = nullptr;
1521 TriangulationVertex* p0 = nullptr;
1522 TriangulationVertex* p2 = nullptr;
1523 // find a convex vertex to clip
1524 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1525 convexIter != convexList.end(); ++convexIter) {
1526 earVertex = *convexIter;
1527 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1528
1529 p0 = &triangulationVertices[earVertex->fPrevIndex];
1530 p2 = &triangulationVertices[earVertex->fNextIndex];
1531
1532 // see if any reflex vertices are inside the ear
1533 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001534 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001535 if (failed) {
1536 continue;
1537 }
1538
1539 // found one we can clip
1540 success = true;
1541 break;
1542 }
1543 // If we can't find any ears to clip, this probably isn't a simple polygon
1544 if (!success) {
1545 return false;
1546 }
1547
1548 // add indices
1549 auto indices = triangleIndices->append(3);
1550 indices[0] = indexMap[p0->fIndex];
1551 indices[1] = indexMap[earVertex->fIndex];
1552 indices[2] = indexMap[p2->fIndex];
1553
1554 // clip the ear
1555 convexList.remove(earVertex);
1556 --vertexCount;
1557
1558 // reclassify reflex verts
1559 p0->fNextIndex = earVertex->fNextIndex;
1560 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1561
1562 p2->fPrevIndex = earVertex->fPrevIndex;
1563 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1564 }
1565
1566 // output indices
1567 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1568 vertexIter != convexList.end(); ++vertexIter) {
1569 TriangulationVertex* vertex = *vertexIter;
1570 *triangleIndices->push() = indexMap[vertex->fIndex];
1571 }
1572
1573 return true;
1574}