blob: ee532fa52ae9b6f0d24da410ea95b71d3ac85b14 [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
Jim Van Verthb7c95512018-09-11 12:57:42 -040010#include <limits>
11
Jim Van Verth9b218252018-09-21 14:27:35 -040012#include "SkNx.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050013#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040014#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000015#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040016#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040017#include "SkTInternalLList.h"
18
19//////////////////////////////////////////////////////////////////////////////////
20// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000021
Jim Van Verth4db18ed2018-04-03 10:00:37 -040022struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000023 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040024 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000025};
26
Jim Van Verthba4847c2018-08-07 16:02:33 -040027constexpr SkScalar kCrossTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
28
29// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
Brian Salomonab664fa2017-03-24 16:07:20 +000030// A positive value means the point is to the left of the segment,
31// negative is to the right, 0 is collinear.
Jim Van Verthba4847c2018-08-07 16:02:33 -040032static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
33 SkVector w = p - p0;
34 SkScalar perpDot = v.cross(w);
35 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
Brian Salomonab664fa2017-03-24 16:07:20 +000036 return ((perpDot > 0) ? 1 : -1);
37 }
38
39 return 0;
40}
41
Jim Van Verth6784ffa2018-07-03 16:12:39 -040042// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
43int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
44 if (polygonSize < 3) {
45 return 0;
46 }
47
Jim Van Verth8664a1d2018-06-28 16:26:50 -040048 // compute area and use sign to determine winding
49 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040050 SkVector v0 = polygonVerts[1] - polygonVerts[0];
Jim Van Verth9b218252018-09-21 14:27:35 -040051 for (int curr = 2; curr < polygonSize; ++curr) {
52 SkVector v1 = polygonVerts[curr] - polygonVerts[0];
Jim Van Verth6784ffa2018-07-03 16:12:39 -040053 quadArea += v0.cross(v1);
54 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000055 }
Jim Van Verthba4847c2018-08-07 16:02:33 -040056 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040057 return 0;
58 }
59 // 1 == ccw, -1 == cw
60 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000061}
62
Jim Van Verthda58cac2018-09-05 12:41:56 -040063// Compute difference vector to offset p0-p1 'offset' units in direction specified by 'side'
64void compute_offset_vector(const SkPoint& p0, const SkPoint& p1, SkScalar offset, int side,
65 SkPoint* vector) {
Jim Van Verthda965502017-04-11 15:29:14 -040066 SkASSERT(side == -1 || side == 1);
Jim Van Verthda58cac2018-09-05 12:41:56 -040067 // if distances are equal, can just outset by the perpendicular
68 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
69 perp.setLength(offset*side);
70 *vector = perp;
Jim Van Verthbdde4282018-06-14 09:09:18 -040071}
72
Jim Van Verthba4847c2018-08-07 16:02:33 -040073// check interval to see if intersection is in segment
74static inline bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive) {
75 return (denomPositive && (numer < 0 || numer > denom)) ||
76 (!denomPositive && (numer > 0 || numer < denom));
Jim Van Verth6784ffa2018-07-03 16:12:39 -040077}
78
Brian Salomonab664fa2017-03-24 16:07:20 +000079// Compute the intersection 'p' between segments s0 and s1, if any.
80// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
81// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -040082static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +000083 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Vertha6316832018-07-24 09:30:37 -040084 const SkVector& v0 = s0.fV;
85 const SkVector& v1 = s1.fV;
Jim Van Verthba4847c2018-08-07 16:02:33 -040086 SkVector w = s1.fP0 - s0.fP0;
87 SkScalar denom = v0.cross(v1);
88 bool denomPositive = (denom > 0);
89 SkScalar sNumer, tNumer;
90 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -040091 // segments are parallel, but not collinear
Jim Van Verthba4847c2018-08-07 16:02:33 -040092 if (!SkScalarNearlyZero(w.cross(v0), kCrossTolerance) ||
93 !SkScalarNearlyZero(w.cross(v1), kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -040094 return false;
95 }
96
Jim Van Verthba4847c2018-08-07 16:02:33 -040097 // Check for zero-length segments
Jim Van Verth6784ffa2018-07-03 16:12:39 -040098 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
Jim Van Verthba4847c2018-08-07 16:02:33 -040099 // Both are zero-length
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400100 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
101 // Check if they're the same point
Jim Van Verthba4847c2018-08-07 16:02:33 -0400102 if (!SkPointPriv::CanNormalize(w.fX, w.fY)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400103 *p = s0.fP0;
104 *s = 0;
105 *t = 0;
106 return true;
107 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400108 return false;
109 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400110 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400111 // Otherwise project segment0's origin onto segment1
112 tNumer = v1.dot(-w);
113 denom = v1.dot(v1);
114 if (outside_interval(tNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400115 return false;
116 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400117 sNumer = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400118 } else {
119 // Project segment1's endpoints onto segment0
Jim Van Verthba4847c2018-08-07 16:02:33 -0400120 sNumer = v0.dot(w);
121 denom = v0.dot(v0);
122 tNumer = 0;
123 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400124 // The first endpoint doesn't lie on segment0
125 // If segment1 is degenerate, then there's no collision
126 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
127 return false;
128 }
129
130 // Otherwise try the other one
Jim Van Verthba4847c2018-08-07 16:02:33 -0400131 SkScalar oldSNumer = sNumer;
132 sNumer = v0.dot(w + v1);
133 tNumer = denom;
134 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400135 // it's possible that segment1's interval surrounds segment0
136 // this is false if params have the same signs, and in that case no collision
Jim Van Verthba4847c2018-08-07 16:02:33 -0400137 if (sNumer*oldSNumer > 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400138 return false;
139 }
140 // otherwise project segment0's endpoint onto segment1 instead
Jim Van Verthba4847c2018-08-07 16:02:33 -0400141 sNumer = 0;
142 tNumer = v1.dot(-w);
143 denom = v1.dot(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400144 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400145 }
146 }
147 } else {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400148 sNumer = w.cross(v1);
149 if (outside_interval(sNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400150 return false;
151 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400152 tNumer = w.cross(v0);
153 if (outside_interval(tNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400154 return false;
155 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000156 }
157
Jim Van Verthba4847c2018-08-07 16:02:33 -0400158 SkScalar localS = sNumer/denom;
159 SkScalar localT = tNumer/denom;
160
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400161 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000162 *s = localS;
163 *t = localT;
164
165 return true;
166}
167
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400168bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
169 if (polygonSize < 3) {
170 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400171 }
172
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400173 SkScalar lastArea = 0;
174 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400175
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400176 int prevIndex = polygonSize - 1;
177 int currIndex = 0;
178 int nextIndex = 1;
179 SkPoint origin = polygonVerts[0];
180 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
181 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
182 SkVector w0 = polygonVerts[currIndex] - origin;
183 SkVector w1 = polygonVerts[nextIndex] - origin;
184 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400185 if (!polygonVerts[i].isFinite()) {
186 return false;
187 }
188
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400189 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400190 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400191 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400192 return false;
193 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400194 if (0 != perpDot) {
195 lastPerpDot = perpDot;
196 }
197
198 // If the signed area ever flips it's concave
199 // TODO: see if we can verify convexity only with signed area
200 SkScalar quadArea = w0.cross(w1);
201 if (quadArea*lastArea < 0) {
202 return false;
203 }
204 if (0 != quadArea) {
205 lastArea = quadArea;
206 }
207
208 prevIndex = currIndex;
209 currIndex = nextIndex;
210 nextIndex = (currIndex + 1) % polygonSize;
211 v0 = v1;
212 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
213 w0 = w1;
214 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400215 }
216
217 return true;
218}
Jim Van Verth0513f142017-03-24 14:28:57 -0400219
Jim Van Verth00673692018-07-23 11:23:39 -0400220struct OffsetEdge {
221 OffsetEdge* fPrev;
222 OffsetEdge* fNext;
Jim Van Verthda58cac2018-09-05 12:41:56 -0400223 OffsetSegment fOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400224 SkPoint fIntersection;
225 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400226 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400227 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400228
Jim Van Verth00673692018-07-23 11:23:39 -0400229 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400230 fIntersection = fOffset.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400231 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400232 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400233 fEnd = end;
234 }
235
236 // special intersection check that looks for endpoint intersection
237 bool checkIntersection(const OffsetEdge* that,
238 SkPoint* p, SkScalar* s, SkScalar* t) {
239 if (this->fEnd == that->fIndex) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400240 SkPoint p1 = this->fOffset.fP0 + this->fOffset.fV;
241 if (SkPointPriv::EqualsWithinTolerance(p1, that->fOffset.fP0)) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400242 *p = p1;
243 *s = SK_Scalar1;
244 *t = 0;
245 return true;
246 }
247 }
248
Jim Van Verthda58cac2018-09-05 12:41:56 -0400249 return compute_intersection(this->fOffset, that->fOffset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400250 }
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400251
Jim Van Verthba4847c2018-08-07 16:02:33 -0400252 // computes the line intersection and then the "distance" from that to this
253 // this is really a signed squared distance, where negative means that
Jim Van Verthda58cac2018-09-05 12:41:56 -0400254 // the intersection lies inside this->fOffset
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400255 SkScalar computeCrossingDistance(const OffsetEdge* that) {
Jim Van Verthda58cac2018-09-05 12:41:56 -0400256 const OffsetSegment& s0 = this->fOffset;
257 const OffsetSegment& s1 = that->fOffset;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400258 const SkVector& v0 = s0.fV;
259 const SkVector& v1 = s1.fV;
260
Jim Van Verthba4847c2018-08-07 16:02:33 -0400261 SkScalar denom = v0.cross(v1);
262 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400263 // segments are parallel
264 return SK_ScalarMax;
265 }
266
Jim Van Verthba4847c2018-08-07 16:02:33 -0400267 SkVector w = s1.fP0 - s0.fP0;
268 SkScalar localS = w.cross(v1) / denom;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400269 if (localS < 0) {
270 localS = -localS;
271 } else {
272 localS -= SK_Scalar1;
273 }
274
Jim Van Verthba4847c2018-08-07 16:02:33 -0400275 localS *= SkScalarAbs(localS);
276 localS *= v0.dot(v0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400277
278 return localS;
279 }
280
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400281};
282
Jim Van Verth00673692018-07-23 11:23:39 -0400283static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
284 // remove from linked list
285 node->fPrev->fNext = node->fNext;
286 node->fNext->fPrev = node->fPrev;
287 if (node == *head) {
288 *head = (node->fNext == node) ? nullptr : node->fNext;
289 }
290}
291
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400292//////////////////////////////////////////////////////////////////////////////////
293
Brian Salomonab664fa2017-03-24 16:07:20 +0000294// The objective here is to inset all of the edges by the given distance, and then
295// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
296// we should only be making left-hand turns (for cw polygons, we use the winding
297// parameter to reverse this). We detect this by checking whether the second intersection
298// on an edge is closer to its tail than the first one.
299//
300// We might also have the case that there is no intersection between two neighboring inset edges.
301// In this case, one edge will lie to the right of the other and should be discarded along with
302// its previous intersection (if any).
303//
304// Note: the assumption is that inputPolygon is convex and has no coincident points.
305//
306bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthda58cac2018-09-05 12:41:56 -0400307 SkScalar inset, SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000308 if (inputPolygonSize < 3) {
309 return false;
310 }
311
Jim Van Vertha6316832018-07-24 09:30:37 -0400312 // restrict this to match other routines
313 // practically we don't want anything bigger than this anyway
Jim Van Verthb7c95512018-09-11 12:57:42 -0400314 if (inputPolygonSize > std::numeric_limits<uint16_t>::max()) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400315 return false;
316 }
317
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400318 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400319 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000320 if (0 == winding) {
321 return false;
322 }
323
324 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400325 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
326 int prev = inputPolygonSize - 1;
327 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
328 int next = (curr + 1) % inputPolygonSize;
329 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400330 return false;
331 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400332 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400333 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400334 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400335 return false;
336 }
Jim Van Verthda58cac2018-09-05 12:41:56 -0400337 SkVector v = inputPolygonVerts[next] - inputPolygonVerts[curr];
338 SkVector perp = SkVector::Make(-v.fY, v.fX);
339 perp.setLength(inset*winding);
Jim Van Vertha6316832018-07-24 09:30:37 -0400340 edgeData[curr].fPrev = &edgeData[prev];
341 edgeData[curr].fNext = &edgeData[next];
Jim Van Verthda58cac2018-09-05 12:41:56 -0400342 edgeData[curr].fOffset.fP0 = inputPolygonVerts[curr] + perp;
343 edgeData[curr].fOffset.fV = v;
Jim Van Verth00673692018-07-23 11:23:39 -0400344 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000345 }
346
Jim Van Verth00673692018-07-23 11:23:39 -0400347 OffsetEdge* head = &edgeData[0];
348 OffsetEdge* currEdge = head;
349 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000350 int insetVertexCount = inputPolygonSize;
Jim Van Verthb7c95512018-09-11 12:57:42 -0400351 unsigned int iterations = 0;
352 unsigned int maxIterations = inputPolygonSize * inputPolygonSize;
Jim Van Verth00673692018-07-23 11:23:39 -0400353 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400354 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400355 // we should check each edge against each other edge at most once
Jim Van Verthb7c95512018-09-11 12:57:42 -0400356 if (iterations > maxIterations) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400357 return false;
358 }
359
Brian Salomonab664fa2017-03-24 16:07:20 +0000360 SkScalar s, t;
361 SkPoint intersection;
Jim Van Verthda58cac2018-09-05 12:41:56 -0400362 if (compute_intersection(prevEdge->fOffset, currEdge->fOffset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000363 &intersection, &s, &t)) {
364 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400365 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000366 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400367 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000368 --insetVertexCount;
369 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400370 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000371 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400372 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500373 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400374 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000375 1.0e-6f)) {
376 break;
377 } else {
378 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400379 currEdge->fIntersection = intersection;
380 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000381
382 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400383 prevEdge = currEdge;
384 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000385 }
386 } else {
387 // if prev to right side of curr
Jim Van Verthda58cac2018-09-05 12:41:56 -0400388 int side = winding*compute_side(currEdge->fOffset.fP0,
389 currEdge->fOffset.fV,
390 prevEdge->fOffset.fP0);
Jim Van Vertha6316832018-07-24 09:30:37 -0400391 if (side < 0 &&
Jim Van Verthda58cac2018-09-05 12:41:56 -0400392 side == winding*compute_side(currEdge->fOffset.fP0,
393 currEdge->fOffset.fV,
394 prevEdge->fOffset.fP0 + prevEdge->fOffset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000395 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400396 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000397 --insetVertexCount;
398 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400399 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 } else {
401 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400402 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000403 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400404 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000405 }
406 }
407 }
408
Jim Van Verthda965502017-04-11 15:29:14 -0400409 // store all the valid intersections that aren't nearly coincident
410 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000411 insetPolygon->reset();
Jim Van Verthb7c95512018-09-11 12:57:42 -0400412 if (!head) {
413 return false;
414 }
415
416 static constexpr SkScalar kCleanupTolerance = 0.01f;
417 if (insetVertexCount >= 0) {
418 insetPolygon->setReserve(insetVertexCount);
419 }
420 int currIndex = 0;
421 *insetPolygon->push() = head->fIntersection;
422 currEdge = head->fNext;
423 while (currEdge != head) {
424 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
425 (*insetPolygon)[currIndex],
426 kCleanupTolerance)) {
427 *insetPolygon->push() = currEdge->fIntersection;
428 currIndex++;
Brian Salomonab664fa2017-03-24 16:07:20 +0000429 }
Jim Van Verth00673692018-07-23 11:23:39 -0400430 currEdge = currEdge->fNext;
Jim Van Verthb7c95512018-09-11 12:57:42 -0400431 }
432 // make sure the first and last points aren't coincident
433 if (currIndex >= 1 &&
434 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
435 kCleanupTolerance)) {
436 insetPolygon->pop();
Jim Van Verthda965502017-04-11 15:29:14 -0400437 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000438
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400439 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000440}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400441
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400442///////////////////////////////////////////////////////////////////////////////////////////
443
444// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400445bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar offset,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400446 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400447 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
448
449 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400450 if (!SkScalarIsFinite(rCos)) {
451 return false;
452 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400453 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400454 if (!SkScalarIsFinite(rSin)) {
455 return false;
456 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400457 SkScalar theta = SkScalarATan2(rSin, rCos);
458
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400459 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
Jim Van Verth206dbe82018-07-23 11:48:31 -0400460 // limit the number of steps to at most max uint16_t (that's all we can index)
461 // knock one value off the top to account for rounding
Jim Van Verthb7c95512018-09-11 12:57:42 -0400462 if (floatSteps >= std::numeric_limits<uint16_t>::max()) {
Jim Van Verth206dbe82018-07-23 11:48:31 -0400463 return false;
464 }
465 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400466
Jim Van Verth061cc212018-07-11 14:09:09 -0400467 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400468 *rotSin = SkScalarSinCos(dTheta, rotCos);
469 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400470 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400471}
472
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400473///////////////////////////////////////////////////////////////////////////////////////////
474
Jim Van Verth04d16322018-08-15 15:01:35 -0400475// 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 -0400476static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400477 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY > p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400478}
479
Jim Van Verth04d16322018-08-15 15:01:35 -0400480// a point is "right" to another if its x-coord is greater, or if equal, its y-coord is less
481static bool right(const SkPoint& p0, const SkPoint& p1) {
482 return p0.fX > p1.fX || (!(p0.fX < p1.fX) && p0.fY < p1.fY);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400483}
484
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400485struct Vertex {
486 static bool Left(const Vertex& qv0, const Vertex& qv1) {
487 return left(qv0.fPosition, qv1.fPosition);
488 }
Jim Van Verth00673692018-07-23 11:23:39 -0400489
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400490 // packed to fit into 16 bytes (one cache line)
491 SkPoint fPosition;
492 uint16_t fIndex; // index in unsorted polygon
493 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
494 uint16_t fNextIndex;
495 uint16_t fFlags;
496};
497
498enum VertexFlags {
499 kPrevLeft_VertexFlag = 0x1,
500 kNextLeft_VertexFlag = 0x2,
501};
502
Jim Van Verth00673692018-07-23 11:23:39 -0400503struct ActiveEdge {
Jim Van Verth04d16322018-08-15 15:01:35 -0400504 ActiveEdge() : fChild{ nullptr, nullptr }, fAbove(nullptr), fBelow(nullptr), fRed(false) {}
505 ActiveEdge(const SkPoint& p0, const SkVector& v, uint16_t index0, uint16_t index1)
506 : fSegment({ p0, v })
Jim Van Verth00673692018-07-23 11:23:39 -0400507 , fIndex0(index0)
Jim Van Verth04d16322018-08-15 15:01:35 -0400508 , fIndex1(index1)
509 , fAbove(nullptr)
510 , fBelow(nullptr)
511 , fRed(true) {
512 fChild[0] = nullptr;
513 fChild[1] = nullptr;
514 }
Jim Van Verth00673692018-07-23 11:23:39 -0400515
Jim Van Verth04d16322018-08-15 15:01:35 -0400516 // Returns true if "this" is above "that", assuming this->p0 is to the left of that->p0
517 // This is only used to verify the edgelist -- the actual test for insertion/deletion is much
518 // simpler because we can make certain assumptions then.
519 bool aboveIfLeft(const ActiveEdge* that) const {
520 const SkPoint& p0 = this->fSegment.fP0;
521 const SkPoint& q0 = that->fSegment.fP0;
522 SkASSERT(p0.fX <= q0.fX);
523 SkVector d = q0 - p0;
524 const SkVector& v = this->fSegment.fV;
525 const SkVector& w = that->fSegment.fV;
526 // The idea here is that if the vector between the origins of the two segments (d)
527 // rotates counterclockwise up to the vector representing the "this" segment (v),
528 // then we know that "this" is above "that". If the result is clockwise we say it's below.
529 if (this->fIndex0 != that->fIndex0) {
530 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400531 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400532 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400533 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400534 return false;
535 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400536 } else if (this->fIndex1 == that->fIndex1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400537 return false;
538 }
Jim Van Verth00673692018-07-23 11:23:39 -0400539 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400540 // lies on dv. So then we try the same for the vector from the tail of "this"
541 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Verth04d16322018-08-15 15:01:35 -0400542 // d = that.P1 - this.P0
543 // = that.fP0 + that.fV - this.fP0
544 // = that.fP0 - this.fP0 + that.fV
545 // = old_d + that.fV
546 d += w;
547 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400548 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400549 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400550 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400551 return false;
552 }
553 // If the previous check fails, the two segments are nearly collinear
554 // First check y-coord of first endpoints
Jim Van Verth04d16322018-08-15 15:01:35 -0400555 if (p0.fX < q0.fX) {
556 return (p0.fY >= q0.fY);
557 } else if (p0.fY > q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400558 return true;
Jim Van Verth04d16322018-08-15 15:01:35 -0400559 } else if (p0.fY < q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400560 return false;
561 }
562 // The first endpoints are the same, so check the other endpoint
Jim Van Verth04d16322018-08-15 15:01:35 -0400563 SkPoint p1 = p0 + v;
564 SkPoint q1 = q0 + w;
565 if (p1.fX < q1.fX) {
566 return (p1.fY >= q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400567 } else {
Jim Van Verth04d16322018-08-15 15:01:35 -0400568 return (p1.fY > q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400569 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400570 }
571
Jim Van Verth04d16322018-08-15 15:01:35 -0400572 // same as leftAndAbove(), but generalized
573 bool above(const ActiveEdge* that) const {
574 const SkPoint& p0 = this->fSegment.fP0;
575 const SkPoint& q0 = that->fSegment.fP0;
576 if (right(p0, q0)) {
577 return !that->aboveIfLeft(this);
578 } else {
579 return this->aboveIfLeft(that);
580 }
581 }
582
583 bool intersect(const SkPoint& q0, const SkVector& w, uint16_t index0, uint16_t index1) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400584 // check first to see if these edges are neighbors in the polygon
Jim Van Verth04d16322018-08-15 15:01:35 -0400585 if (this->fIndex0 == index0 || this->fIndex1 == index0 ||
586 this->fIndex0 == index1 || this->fIndex1 == index1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400587 return false;
588 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400589
590 // We don't need the exact intersection point so we can do a simpler test here.
591 const SkPoint& p0 = this->fSegment.fP0;
592 const SkVector& v = this->fSegment.fV;
593 SkPoint p1 = p0 + v;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400594 SkPoint q1 = q0 + w;
595
Jim Van Verth04d16322018-08-15 15:01:35 -0400596 // We assume some x-overlap due to how the edgelist works
597 // This allows us to simplify our test
598 // We need some slop here because storing the vector and recomputing the second endpoint
599 // doesn't necessary give us the original result in floating point.
600 // TODO: Store vector as double? Store endpoint as well?
601 SkASSERT(q0.fX <= p1.fX + SK_ScalarNearlyZero);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400602
603 // if each segment straddles the other (i.e., the endpoints have different sides)
604 // then they intersect
Jim Van Verth04d16322018-08-15 15:01:35 -0400605 bool result;
606 if (p0.fX < q0.fX) {
607 if (q1.fX < p1.fX) {
608 result = (compute_side(p0, v, q0)*compute_side(p0, v, q1) < 0);
609 } else {
610 result = (compute_side(p0, v, q0)*compute_side(q0, w, p1) > 0);
611 }
612 } else {
613 if (p1.fX < q1.fX) {
614 result = (compute_side(q0, w, p0)*compute_side(q0, w, p1) < 0);
615 } else {
616 result = (compute_side(q0, w, p0)*compute_side(p0, v, q1) > 0);
617 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400618 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400619 return result;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400620 }
621
Jim Van Verth04d16322018-08-15 15:01:35 -0400622 bool intersect(const ActiveEdge* edge) {
623 return this->intersect(edge->fSegment.fP0, edge->fSegment.fV, edge->fIndex0, edge->fIndex1);
624 }
625
626 bool lessThan(const ActiveEdge* that) const {
627 SkASSERT(!this->above(this));
628 SkASSERT(!that->above(that));
629 SkASSERT(!(this->above(that) && that->above(this)));
Jim Van Verth00673692018-07-23 11:23:39 -0400630 return this->above(that);
631 }
632
Jim Van Verth04d16322018-08-15 15:01:35 -0400633 bool equals(uint16_t index0, uint16_t index1) const {
634 return (this->fIndex0 == index0 && this->fIndex1 == index1);
Jim Van Verth00673692018-07-23 11:23:39 -0400635 }
636
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400637 OffsetSegment fSegment;
Jim Van Verth04d16322018-08-15 15:01:35 -0400638 uint16_t fIndex0; // indices for previous and next vertex in polygon
Jim Van Vertha6316832018-07-24 09:30:37 -0400639 uint16_t fIndex1;
Jim Van Verth04d16322018-08-15 15:01:35 -0400640 ActiveEdge* fChild[2];
641 ActiveEdge* fAbove;
642 ActiveEdge* fBelow;
643 int32_t fRed;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400644};
645
Jim Van Verth00673692018-07-23 11:23:39 -0400646class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400647public:
Jim Van Verth04d16322018-08-15 15:01:35 -0400648 ActiveEdgeList(int maxEdges) {
649 fAllocation = (char*) sk_malloc_throw(sizeof(ActiveEdge)*maxEdges);
650 fCurrFree = 0;
651 fMaxFree = maxEdges;
652 }
653 ~ActiveEdgeList() {
654 fTreeHead.fChild[1] = nullptr;
655 sk_free(fAllocation);
656 }
657
Jim Van Vertha6316832018-07-24 09:30:37 -0400658 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400659 SkVector v = p1 - p0;
Jim Van Verthc77cd1a2018-10-23 11:54:26 -0400660 if (!v.isFinite()) {
661 return false;
662 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400663 // empty tree case -- easy
664 if (!fTreeHead.fChild[1]) {
665 ActiveEdge* root = fTreeHead.fChild[1] = this->allocate(p0, v, index0, index1);
666 SkASSERT(root);
667 if (!root) {
668 return false;
669 }
670 root->fRed = false;
671 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400672 }
673
Jim Van Verth04d16322018-08-15 15:01:35 -0400674 // set up helpers
675 ActiveEdge* top = &fTreeHead;
676 ActiveEdge *grandparent = nullptr;
677 ActiveEdge *parent = nullptr;
678 ActiveEdge *curr = top->fChild[1];
679 int dir = 0;
680 int last = 0; // ?
681 // predecessor and successor, for intersection check
682 ActiveEdge* pred = nullptr;
683 ActiveEdge* succ = nullptr;
684
685 // search down the tree
686 while (true) {
687 if (!curr) {
688 // check for intersection with predecessor and successor
689 if ((pred && pred->intersect(p0, v, index0, index1)) ||
690 (succ && succ->intersect(p0, v, index0, index1))) {
691 return false;
692 }
693 // insert new node at bottom
694 parent->fChild[dir] = curr = this->allocate(p0, v, index0, index1);
695 SkASSERT(curr);
696 if (!curr) {
697 return false;
698 }
699 curr->fAbove = pred;
700 curr->fBelow = succ;
701 if (pred) {
702 pred->fBelow = curr;
703 }
704 if (succ) {
705 succ->fAbove = curr;
706 }
707 if (IsRed(parent)) {
708 int dir2 = (top->fChild[1] == grandparent);
709 if (curr == parent->fChild[last]) {
710 top->fChild[dir2] = SingleRotation(grandparent, !last);
711 } else {
712 top->fChild[dir2] = DoubleRotation(grandparent, !last);
713 }
714 }
715 break;
716 } else if (IsRed(curr->fChild[0]) && IsRed(curr->fChild[1])) {
717 // color flip
718 curr->fRed = true;
719 curr->fChild[0]->fRed = false;
720 curr->fChild[1]->fRed = false;
721 if (IsRed(parent)) {
722 int dir2 = (top->fChild[1] == grandparent);
723 if (curr == parent->fChild[last]) {
724 top->fChild[dir2] = SingleRotation(grandparent, !last);
725 } else {
726 top->fChild[dir2] = DoubleRotation(grandparent, !last);
727 }
728 }
729 }
730
731 last = dir;
732 int side;
733 // check to see if segment is above or below
734 if (curr->fIndex0 == index0) {
735 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
736 } else {
737 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
738 }
739 if (0 == side) {
740 return false;
741 }
742 dir = (side < 0);
743
744 if (0 == dir) {
745 succ = curr;
746 } else {
747 pred = curr;
748 }
749
750 // update helpers
751 if (grandparent) {
752 top = grandparent;
753 }
754 grandparent = parent;
755 parent = curr;
756 curr = curr->fChild[dir];
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400757 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400758
759 // update root and make it black
760 fTreeHead.fChild[1]->fRed = false;
761
762 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400763
764 return true;
765 }
766
Jim Van Verth04d16322018-08-15 15:01:35 -0400767 // replaces edge p0p1 with p1p2
768 bool replace(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
769 uint16_t index0, uint16_t index1, uint16_t index2) {
770 if (!fTreeHead.fChild[1]) {
Jim Van Verth00673692018-07-23 11:23:39 -0400771 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400772 }
773
Jim Van Verth04d16322018-08-15 15:01:35 -0400774 SkVector v = p2 - p1;
775 ActiveEdge* curr = &fTreeHead;
776 ActiveEdge* found = nullptr;
777 int dir = 1;
778
779 // search
780 while (curr->fChild[dir] != nullptr) {
781 // update helpers
782 curr = curr->fChild[dir];
783 // save found node
784 if (curr->equals(index0, index1)) {
785 found = curr;
786 break;
787 } else {
788 // check to see if segment is above or below
789 int side;
790 if (curr->fIndex1 == index1) {
791 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
792 } else {
793 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
794 }
795 if (0 == side) {
796 return false;
797 }
798 dir = (side < 0);
799 }
800 }
801
802 if (!found) {
803 return false;
804 }
805
806 // replace if found
807 ActiveEdge* pred = found->fAbove;
808 ActiveEdge* succ = found->fBelow;
809 // check deletion and insert intersection cases
810 if (pred && (pred->intersect(found) || pred->intersect(p1, v, index1, index2))) {
811 return false;
812 }
813 if (succ && (succ->intersect(found) || succ->intersect(p1, v, index1, index2))) {
814 return false;
815 }
816 found->fSegment.fP0 = p1;
817 found->fSegment.fV = v;
818 found->fIndex0 = index1;
819 found->fIndex1 = index2;
820 // above and below should stay the same
821
822 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
823
824 return true;
825 }
826
827 bool remove(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
828 if (!fTreeHead.fChild[1]) {
829 return false;
830 }
831
832 ActiveEdge* curr = &fTreeHead;
833 ActiveEdge* parent = nullptr;
834 ActiveEdge* grandparent = nullptr;
835 ActiveEdge* found = nullptr;
836 int dir = 1;
837
838 // search and push a red node down
839 while (curr->fChild[dir] != nullptr) {
840 int last = dir;
841
842 // update helpers
843 grandparent = parent;
844 parent = curr;
845 curr = curr->fChild[dir];
846 // save found node
847 if (curr->equals(index0, index1)) {
848 found = curr;
849 dir = 0;
850 } else {
851 // check to see if segment is above or below
852 int side;
853 if (curr->fIndex1 == index1) {
854 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
855 } else {
856 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
857 }
858 if (0 == side) {
859 return false;
860 }
861 dir = (side < 0);
862 }
863
864 // push the red node down
865 if (!IsRed(curr) && !IsRed(curr->fChild[dir])) {
866 if (IsRed(curr->fChild[!dir])) {
867 parent = parent->fChild[last] = SingleRotation(curr, dir);
868 } else {
869 ActiveEdge *s = parent->fChild[!last];
870
871 if (s != NULL) {
872 if (!IsRed(s->fChild[!last]) && !IsRed(s->fChild[last])) {
873 // color flip
874 parent->fRed = false;
875 s->fRed = true;
876 curr->fRed = true;
877 } else {
878 int dir2 = (grandparent->fChild[1] == parent);
879
880 if (IsRed(s->fChild[last])) {
881 grandparent->fChild[dir2] = DoubleRotation(parent, last);
882 } else if (IsRed(s->fChild[!last])) {
883 grandparent->fChild[dir2] = SingleRotation(parent, last);
884 }
885
886 // ensure correct coloring
887 curr->fRed = grandparent->fChild[dir2]->fRed = true;
888 grandparent->fChild[dir2]->fChild[0]->fRed = false;
889 grandparent->fChild[dir2]->fChild[1]->fRed = false;
890 }
891 }
892 }
893 }
894 }
895
896 // replace and remove if found
897 if (found) {
898 ActiveEdge* pred = found->fAbove;
899 ActiveEdge* succ = found->fBelow;
900 if ((pred && pred->intersect(found)) || (succ && succ->intersect(found))) {
901 return false;
902 }
903 if (found != curr) {
904 found->fSegment = curr->fSegment;
905 found->fIndex0 = curr->fIndex0;
906 found->fIndex1 = curr->fIndex1;
907 found->fAbove = curr->fAbove;
908 pred = found->fAbove;
909 // we don't need to set found->fBelow here
910 } else {
911 if (succ) {
912 succ->fAbove = pred;
913 }
914 }
915 if (pred) {
916 pred->fBelow = curr->fBelow;
917 }
918 parent->fChild[parent->fChild[1] == curr] = curr->fChild[!curr->fChild[0]];
919
920 // no need to delete
921 curr->fAbove = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
922 curr->fBelow = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
923 if (fTreeHead.fChild[1]) {
924 fTreeHead.fChild[1]->fRed = false;
925 }
926 }
927
928 // update root and make it black
929 if (fTreeHead.fChild[1]) {
930 fTreeHead.fChild[1]->fRed = false;
931 }
932
933 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
934
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400935 return true;
936 }
937
938private:
Jim Van Verth04d16322018-08-15 15:01:35 -0400939 // allocator
940 ActiveEdge * allocate(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
941 if (fCurrFree >= fMaxFree) {
942 return nullptr;
943 }
944 char* bytes = fAllocation + sizeof(ActiveEdge)*fCurrFree;
945 ++fCurrFree;
946 return new(bytes) ActiveEdge(p0, p1, index0, index1);
947 }
948
949 ///////////////////////////////////////////////////////////////////////////////////
950 // Red-black tree methods
951 ///////////////////////////////////////////////////////////////////////////////////
952 static bool IsRed(const ActiveEdge* node) {
953 return node && node->fRed;
954 }
955
956 static ActiveEdge* SingleRotation(ActiveEdge* node, int dir) {
957 ActiveEdge* tmp = node->fChild[!dir];
958
959 node->fChild[!dir] = tmp->fChild[dir];
960 tmp->fChild[dir] = node;
961
962 node->fRed = true;
963 tmp->fRed = false;
964
965 return tmp;
966 }
967
968 static ActiveEdge* DoubleRotation(ActiveEdge* node, int dir) {
969 node->fChild[!dir] = SingleRotation(node->fChild[!dir], !dir);
970
971 return SingleRotation(node, dir);
972 }
973
974 // returns black link count
975 static int VerifyTree(const ActiveEdge* tree) {
976 if (!tree) {
977 return 1;
978 }
979
980 const ActiveEdge* left = tree->fChild[0];
981 const ActiveEdge* right = tree->fChild[1];
982
983 // no consecutive red links
984 if (IsRed(tree) && (IsRed(left) || IsRed(right))) {
985 SkASSERT(false);
986 return 0;
987 }
988
989 // check secondary links
990 if (tree->fAbove) {
991 SkASSERT(tree->fAbove->fBelow == tree);
992 SkASSERT(tree->fAbove->lessThan(tree));
993 }
994 if (tree->fBelow) {
995 SkASSERT(tree->fBelow->fAbove == tree);
996 SkASSERT(tree->lessThan(tree->fBelow));
997 }
998
999 // violates binary tree order
1000 if ((left && tree->lessThan(left)) || (right && right->lessThan(tree))) {
1001 SkASSERT(false);
1002 return 0;
1003 }
1004
1005 int leftCount = VerifyTree(left);
1006 int rightCount = VerifyTree(right);
1007
1008 // return black link count
1009 if (leftCount != 0 && rightCount != 0) {
1010 // black height mismatch
1011 if (leftCount != rightCount) {
1012 SkASSERT(false);
1013 return 0;
1014 }
1015 return IsRed(tree) ? leftCount : leftCount + 1;
1016 } else {
1017 return 0;
1018 }
1019 }
1020
1021 ActiveEdge fTreeHead;
1022 char* fAllocation;
1023 int fCurrFree;
1024 int fMaxFree;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001025};
1026
1027// Here we implement a sweep line algorithm to determine whether the provided points
1028// represent a simple polygon, i.e., the polygon is non-self-intersecting.
1029// We first insert the vertices into a priority queue sorting horizontally from left to right.
1030// Then as we pop the vertices from the queue we generate events which indicate that an edge
1031// should be added or removed from an edge list. If any intersections are detected in the edge
1032// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001033bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001034 if (polygonSize < 3) {
1035 return false;
1036 }
1037
Jim Van Vertha6316832018-07-24 09:30:37 -04001038 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001039 if (polygonSize > std::numeric_limits<uint16_t>::max()) {
Jim Van Vertha6316832018-07-24 09:30:37 -04001040 return false;
1041 }
1042
Jim Van Verth04d16322018-08-15 15:01:35 -04001043 // If it's convex, it's simple
1044 if (SkIsConvexPolygon(polygon, polygonSize)) {
1045 return true;
1046 }
1047
Jim Van Verth00673692018-07-23 11:23:39 -04001048 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001049 for (int i = 0; i < polygonSize; ++i) {
1050 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001051 if (!polygon[i].isFinite()) {
1052 return false;
1053 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001054 newVertex.fPosition = polygon[i];
1055 newVertex.fIndex = i;
1056 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1057 newVertex.fNextIndex = (i + 1) % polygonSize;
1058 newVertex.fFlags = 0;
1059 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1060 newVertex.fFlags |= kPrevLeft_VertexFlag;
1061 }
1062 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1063 newVertex.fFlags |= kNextLeft_VertexFlag;
1064 }
1065 vertexQueue.insert(newVertex);
1066 }
1067
1068 // pop each vertex from the queue and generate events depending on
1069 // where it lies relative to its neighboring edges
Jim Van Verth04d16322018-08-15 15:01:35 -04001070 ActiveEdgeList sweepLine(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001071 while (vertexQueue.count() > 0) {
1072 const Vertex& v = vertexQueue.peek();
1073
Jim Van Verth04d16322018-08-15 15:01:35 -04001074 // both to the right -- insert both
1075 if (v.fFlags == 0) {
Jim Van Verth00673692018-07-23 11:23:39 -04001076 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001077 break;
1078 }
Jim Van Verth00673692018-07-23 11:23:39 -04001079 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001080 break;
1081 }
Jim Van Verth04d16322018-08-15 15:01:35 -04001082 // both to the left -- remove both
1083 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1084 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1085 break;
1086 }
1087 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1088 break;
1089 }
1090 // one to left and right -- replace one with another
1091 } else {
1092 if (v.fFlags & kPrevLeft_VertexFlag) {
1093 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1094 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1095 break;
1096 }
1097 } else {
1098 SkASSERT(v.fFlags & kNextLeft_VertexFlag);
1099 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1100 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1101 break;
1102 }
1103 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001104 }
1105
1106 vertexQueue.pop();
1107 }
1108
1109 return (vertexQueue.count() == 0);
1110}
1111
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001112///////////////////////////////////////////////////////////////////////////////////////////
1113
Jim Van Verth00673692018-07-23 11:23:39 -04001114// helper function for SkOffsetSimplePolygon
1115static void setup_offset_edge(OffsetEdge* currEdge,
1116 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001117 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verthda58cac2018-09-05 12:41:56 -04001118 currEdge->fOffset.fP0 = endpoint0;
1119 currEdge->fOffset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -04001120 currEdge->init(startIndex, endIndex);
1121}
1122
Jim Van Verth98d33752018-08-03 15:59:46 -04001123static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
1124 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
1125 int side = compute_side(inputPolygonVerts[prevIndex],
1126 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
1127 inputPolygonVerts[nextIndex]);
1128 // if reflex point, we need to add extra edges
1129 return (side*winding*offset < 0);
1130}
1131
Jim Van Verthda58cac2018-09-05 12:41:56 -04001132bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize, SkScalar offset,
Jim Van Verthbdde4282018-06-14 09:09:18 -04001133 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001134 if (inputPolygonSize < 3) {
1135 return false;
1136 }
1137
Jim Van Vertha6316832018-07-24 09:30:37 -04001138 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001139 if (inputPolygonSize >= std::numeric_limits<uint16_t>::max()) {
Jim Van Vertha6316832018-07-24 09:30:37 -04001140 return false;
1141 }
1142
Jim Van Verthda58cac2018-09-05 12:41:56 -04001143 if (!SkScalarIsFinite(offset)) {
1144 return false;
1145 }
1146
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001147 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001148 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001149 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001150 return false;
1151 }
1152
Jim Van Verthbdde4282018-06-14 09:09:18 -04001153 // build normals
Jim Van Verthda58cac2018-09-05 12:41:56 -04001154 SkAutoSTMalloc<64, SkVector> normals(inputPolygonSize);
Jim Van Verthb7c95512018-09-11 12:57:42 -04001155 unsigned int numEdges = 0;
Jim Van Verth98d33752018-08-03 15:59:46 -04001156 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1157 currIndex < inputPolygonSize;
1158 prevIndex = currIndex, ++currIndex) {
1159 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001160 return false;
1161 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001162 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001163 compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
1164 offset, winding, &normals[currIndex]);
Jim Van Verth98d33752018-08-03 15:59:46 -04001165 if (currIndex > 0) {
1166 // if reflex point, we need to add extra edges
Jim Van Verthda58cac2018-09-05 12:41:56 -04001167 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001168 prevIndex, currIndex, nextIndex)) {
1169 SkScalar rotSin, rotCos;
1170 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001171 if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001172 &rotSin, &rotCos, &numSteps)) {
1173 return false;
1174 }
1175 numEdges += SkTMax(numSteps, 1);
1176 }
1177 }
1178 numEdges++;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001179 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001180 // finish up the edge counting
Jim Van Verthda58cac2018-09-05 12:41:56 -04001181 if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
Jim Van Verth98d33752018-08-03 15:59:46 -04001182 SkScalar rotSin, rotCos;
1183 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001184 if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001185 &rotSin, &rotCos, &numSteps)) {
1186 return false;
1187 }
1188 numEdges += SkTMax(numSteps, 1);
1189 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001190
Jim Van Verthb7c95512018-09-11 12:57:42 -04001191 // Make sure we don't overflow the max array count.
1192 // We shouldn't overflow numEdges, as SkComputeRadialSteps returns a max of 2^16-1,
1193 // and we have a max of 2^16-1 original vertices.
1194 if (numEdges > (unsigned int)std::numeric_limits<int32_t>::max()) {
1195 return false;
1196 }
1197
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001198 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -04001199 SkSTArray<64, OffsetEdge> edgeData(numEdges);
1200 OffsetEdge* prevEdge = nullptr;
1201 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1202 currIndex < inputPolygonSize;
1203 prevIndex = currIndex, ++currIndex) {
1204 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001205 // if reflex point, fill in curve
Jim Van Verthda58cac2018-09-05 12:41:56 -04001206 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001207 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001208 SkScalar rotSin, rotCos;
1209 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001210 SkVector prevNormal = normals[prevIndex];
1211 if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
Jim Van Verth061cc212018-07-11 14:09:09 -04001212 &rotSin, &rotCos, &numSteps)) {
1213 return false;
1214 }
Jim Van Verth00673692018-07-23 11:23:39 -04001215 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001216 for (int i = 0; i < numSteps - 1; ++i) {
1217 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1218 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -04001219 setup_offset_edge(currEdge,
1220 inputPolygonVerts[currIndex] + prevNormal,
1221 inputPolygonVerts[currIndex] + currNormal,
1222 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001223 prevNormal = currNormal;
Jim Van Verth98d33752018-08-03 15:59:46 -04001224 currEdge->fPrev = prevEdge;
1225 if (prevEdge) {
1226 prevEdge->fNext = currEdge;
1227 }
1228 prevEdge = currEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001229 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001230 }
Jim Van Verth00673692018-07-23 11:23:39 -04001231 setup_offset_edge(currEdge,
1232 inputPolygonVerts[currIndex] + prevNormal,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001233 inputPolygonVerts[currIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001234 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001235 currEdge->fPrev = prevEdge;
1236 if (prevEdge) {
1237 prevEdge->fNext = currEdge;
1238 }
1239 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001240 }
1241
1242 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -04001243 auto currEdge = edgeData.push_back_n(1);
1244 setup_offset_edge(currEdge,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001245 inputPolygonVerts[currIndex] + normals[currIndex],
1246 inputPolygonVerts[nextIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001247 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001248 currEdge->fPrev = prevEdge;
1249 if (prevEdge) {
1250 prevEdge->fNext = currEdge;
1251 }
1252 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001253 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001254 // close up the linked list
1255 SkASSERT(prevEdge);
1256 prevEdge->fNext = &edgeData[0];
1257 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001258
1259 // now clip edges
Jim Van Verthb7c95512018-09-11 12:57:42 -04001260 SkASSERT(edgeData.count() == (int)numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -04001261 auto head = &edgeData[0];
1262 auto currEdge = head;
Jim Van Verthb7c95512018-09-11 12:57:42 -04001263 unsigned int offsetVertexCount = numEdges;
1264 unsigned long long iterations = 0;
1265 unsigned long long maxIterations = (unsigned long long)(numEdges*numEdges);
1266 while (head && prevEdge != currEdge && offsetVertexCount > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001267 ++iterations;
1268 // we should check each edge against each other edge at most once
Jim Van Verthb7c95512018-09-11 12:57:42 -04001269 if (iterations > maxIterations) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001270 return false;
1271 }
1272
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001273 SkScalar s, t;
1274 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -04001275 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001276 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001277 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001278 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -04001279 remove_node(prevEdge, &head);
1280 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001281 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -04001282 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001283 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -04001284 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001285 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -04001286 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001287 1.0e-6f)) {
1288 break;
1289 } else {
1290 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001291 currEdge->fIntersection = intersection;
1292 currEdge->fTValue = t;
1293 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001294
1295 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -04001296 prevEdge = currEdge;
1297 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001298 }
1299 } else {
1300 // If there is no intersection, we want to minimize the distance between
1301 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -04001302 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1303 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001304 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1305 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1306 // if both lead to direct collision
1307 if (dist0 < 0 && dist1 < 0) {
1308 // check first to see if either represent parts of one contour
Jim Van Verthda58cac2018-09-05 12:41:56 -04001309 SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001310 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001311 prevEdge->fOffset.fP0);
1312 p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001313 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001314 currNextEdge->fOffset.fP0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001315
1316 // want to step along contour to find intersections rather than jump to new one
1317 if (currSameContour && !prevSameContour) {
1318 remove_node(currEdge, &head);
1319 currEdge = currNextEdge;
1320 --offsetVertexCount;
1321 continue;
1322 } else if (prevSameContour && !currSameContour) {
1323 remove_node(prevEdge, &head);
1324 prevEdge = prevPrevEdge;
1325 --offsetVertexCount;
1326 continue;
1327 }
1328 }
1329
1330 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001331 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -04001332 remove_node(prevEdge, &head);
1333 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001334 } else {
Jim Van Verth00673692018-07-23 11:23:39 -04001335 remove_node(currEdge, &head);
1336 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001337 }
Jim Van Verth00673692018-07-23 11:23:39 -04001338 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001339 }
1340 }
1341
1342 // store all the valid intersections that aren't nearly coincident
1343 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001344 offsetPolygon->reset();
Jim Van Verthb7c95512018-09-11 12:57:42 -04001345 if (!head || offsetVertexCount == 0 ||
1346 offsetVertexCount >= std::numeric_limits<uint16_t>::max()) {
1347 return false;
1348 }
1349
1350 static constexpr SkScalar kCleanupTolerance = 0.01f;
1351 offsetPolygon->setReserve(offsetVertexCount);
1352 int currIndex = 0;
1353 *offsetPolygon->push() = head->fIntersection;
1354 if (polygonIndices) {
1355 *polygonIndices->push() = head->fIndex;
1356 }
1357 currEdge = head->fNext;
1358 while (currEdge != head) {
1359 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1360 (*offsetPolygon)[currIndex],
1361 kCleanupTolerance)) {
1362 *offsetPolygon->push() = currEdge->fIntersection;
1363 if (polygonIndices) {
1364 *polygonIndices->push() = currEdge->fIndex;
1365 }
1366 currIndex++;
Jim Van Verth00673692018-07-23 11:23:39 -04001367 }
1368 currEdge = currEdge->fNext;
Jim Van Verthb7c95512018-09-11 12:57:42 -04001369 }
1370 // make sure the first and last points aren't coincident
1371 if (currIndex >= 1 &&
1372 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1373 kCleanupTolerance)) {
1374 offsetPolygon->pop();
1375 if (polygonIndices) {
1376 polygonIndices->pop();
Jim Van Verth872da6b2018-04-10 11:24:11 -04001377 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001378 }
1379
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001380 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001381 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001382
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001383 return (winding*offsetWinding > 0 &&
1384 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001385}
1386
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001387//////////////////////////////////////////////////////////////////////////////////////////
1388
1389struct TriangulationVertex {
1390 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1391
1392 enum class VertexType { kConvex, kReflex };
1393
1394 SkPoint fPosition;
1395 VertexType fVertexType;
1396 uint16_t fIndex;
1397 uint16_t fPrevIndex;
1398 uint16_t fNextIndex;
1399};
1400
Jim Van Verth9b218252018-09-21 14:27:35 -04001401static void compute_triangle_bounds(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1402 SkRect* bounds) {
1403 Sk4s min, max;
1404 min = max = Sk4s(p0.fX, p0.fY, p0.fX, p0.fY);
1405 Sk4s xy(p1.fX, p1.fY, p2.fX, p2.fY);
1406 min = Sk4s::Min(min, xy);
1407 max = Sk4s::Max(max, xy);
1408 bounds->set(SkTMin(min[0], min[2]), SkTMin(min[1], min[3]),
1409 SkTMax(max[0], max[2]), SkTMax(max[1], max[3]));
1410}
1411
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001412// test to see if point p is in triangle p0p1p2.
1413// for now assuming strictly inside -- if on the edge it's outside
1414static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1415 const SkPoint& p) {
1416 SkVector v0 = p1 - p0;
1417 SkVector v1 = p2 - p1;
1418 SkScalar n = v0.cross(v1);
1419
1420 SkVector w0 = p - p0;
1421 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1422 return false;
1423 }
1424
1425 SkVector w1 = p - p1;
1426 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1427 return false;
1428 }
1429
1430 SkVector v2 = p0 - p2;
1431 SkVector w2 = p - p2;
1432 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1433 return false;
1434 }
1435
1436 return true;
1437}
1438
1439// Data structure to track reflex vertices and check whether any are inside a given triangle
1440class ReflexHash {
1441public:
Jim Van Verth9b218252018-09-21 14:27:35 -04001442 ReflexHash(const SkRect& bounds, int vertexCount)
1443 : fBounds(bounds)
1444 , fNumVerts(0) {
1445 // We want vertexCount grid cells, roughly distributed to match the bounds ratio
Jim Van Verthac9f0902018-09-24 12:24:15 -04001446 SkScalar hCount = SkScalarSqrt(vertexCount*bounds.width()/bounds.height());
1447 fHCount = SkTMax(SkTMin(SkScalarRoundToInt(hCount), vertexCount), 1);
Jim Van Verth9b218252018-09-21 14:27:35 -04001448 fVCount = vertexCount/fHCount;
1449 fGridConversion.set((fHCount - 0.001f)/bounds.width(), (fVCount - 0.001f)/bounds.height());
1450 fGrid.setCount(fHCount*fVCount);
1451 for (int i = 0; i < fGrid.count(); ++i) {
1452 fGrid[i].reset();
1453 }
1454 }
1455
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001456 void add(TriangulationVertex* v) {
Jim Van Verth9b218252018-09-21 14:27:35 -04001457 int index = hash(v);
1458 fGrid[index].addToTail(v);
1459 ++fNumVerts;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001460 }
1461
1462 void remove(TriangulationVertex* v) {
Jim Van Verth9b218252018-09-21 14:27:35 -04001463 int index = hash(v);
1464 fGrid[index].remove(v);
1465 --fNumVerts;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001466 }
1467
Jim Van Verth061cc212018-07-11 14:09:09 -04001468 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
Jim Van Verth9b218252018-09-21 14:27:35 -04001469 uint16_t ignoreIndex0, uint16_t ignoreIndex1) const {
1470 if (!fNumVerts) {
1471 return false;
1472 }
1473
1474 SkRect triBounds;
1475 compute_triangle_bounds(p0, p1, p2, &triBounds);
1476 int h0 = (triBounds.fLeft - fBounds.fLeft)*fGridConversion.fX;
1477 int h1 = (triBounds.fRight - fBounds.fLeft)*fGridConversion.fX;
1478 int v0 = (triBounds.fTop - fBounds.fTop)*fGridConversion.fY;
1479 int v1 = (triBounds.fBottom - fBounds.fTop)*fGridConversion.fY;
1480
1481 for (int v = v0; v <= v1; ++v) {
1482 for (int h = h0; h <= h1; ++h) {
1483 int i = v * fHCount + h;
1484 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fGrid[i].begin();
1485 reflexIter != fGrid[i].end(); ++reflexIter) {
1486 TriangulationVertex* reflexVertex = *reflexIter;
1487 if (reflexVertex->fIndex != ignoreIndex0 &&
1488 reflexVertex->fIndex != ignoreIndex1 &&
1489 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
1490 return true;
1491 }
1492 }
1493
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001494 }
1495 }
1496
1497 return false;
1498 }
1499
1500private:
Jim Van Verth9b218252018-09-21 14:27:35 -04001501 int hash(TriangulationVertex* vert) const {
1502 int h = (vert->fPosition.fX - fBounds.fLeft)*fGridConversion.fX;
1503 int v = (vert->fPosition.fY - fBounds.fTop)*fGridConversion.fY;
1504 return v*fHCount + h;
1505 }
1506
1507 SkRect fBounds;
1508 int fHCount;
1509 int fVCount;
1510 int fNumVerts;
1511 // converts distance from the origin to a grid location (when cast to int)
1512 SkVector fGridConversion;
1513 SkTDArray<SkTInternalLList<TriangulationVertex>> fGrid;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001514};
1515
1516// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1517static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1518 int winding, ReflexHash* reflexHash,
1519 SkTInternalLList<TriangulationVertex>* convexList) {
1520 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1521 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1522 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001523 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001524 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1525 reflexHash->remove(p);
1526 p->fPrev = p->fNext = nullptr;
1527 convexList->addToTail(p);
1528 }
1529 }
1530}
1531
1532bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1533 SkTDArray<uint16_t>* triangleIndices) {
1534 if (polygonSize < 3) {
1535 return false;
1536 }
1537 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001538 if (polygonSize >= std::numeric_limits<uint16_t>::max()) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001539 return false;
1540 }
1541
Jim Van Verth9b218252018-09-21 14:27:35 -04001542 // get bounds
1543 SkRect bounds;
Jim Van Verth11dd1ab2018-10-15 10:16:42 -04001544 if (!bounds.setBoundsCheck(polygonVerts, polygonSize)) {
1545 return false;
1546 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001547 // get winding direction
1548 // TODO: we do this for all the polygon routines -- might be better to have the client
1549 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001550 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001551 if (0 == winding) {
1552 return false;
1553 }
1554
Jim Van Verth9b218252018-09-21 14:27:35 -04001555 // Set up vertices
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001556 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1557 int prevIndex = polygonSize - 1;
Jim Van Verth9b218252018-09-21 14:27:35 -04001558 SkVector v0 = polygonVerts[0] - polygonVerts[prevIndex];
1559 for (int currIndex = 0; currIndex < polygonSize; ++currIndex) {
1560 int nextIndex = (currIndex + 1) % polygonSize;
1561
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001562 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1563 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1564 triangulationVertices[currIndex].fIndex = currIndex;
1565 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1566 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth9b218252018-09-21 14:27:35 -04001567 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -04001568 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001569 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001570 } else {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001571 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001572 }
1573
1574 prevIndex = currIndex;
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001575 v0 = v1;
Jim Van Verth9b218252018-09-21 14:27:35 -04001576 }
1577
1578 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1579 // TODO: possibly sort the convexList in some way to get better triangles
1580 SkTInternalLList<TriangulationVertex> convexList;
1581 ReflexHash reflexHash(bounds, polygonSize);
1582 prevIndex = polygonSize - 1;
1583 for (int currIndex = 0; currIndex < polygonSize; prevIndex = currIndex, ++currIndex) {
1584 TriangulationVertex::VertexType currType = triangulationVertices[currIndex].fVertexType;
1585 if (TriangulationVertex::VertexType::kConvex == currType) {
1586 int nextIndex = (currIndex + 1) % polygonSize;
1587 TriangulationVertex::VertexType prevType = triangulationVertices[prevIndex].fVertexType;
1588 TriangulationVertex::VertexType nextType = triangulationVertices[nextIndex].fVertexType;
1589 // We prioritize clipping vertices with neighboring reflex vertices.
1590 // The intent here is that it will cull reflex vertices more quickly.
1591 if (TriangulationVertex::VertexType::kReflex == prevType ||
1592 TriangulationVertex::VertexType::kReflex == nextType) {
1593 convexList.addToHead(&triangulationVertices[currIndex]);
1594 } else {
1595 convexList.addToTail(&triangulationVertices[currIndex]);
1596 }
1597 } else {
1598 // We treat near collinear vertices as reflex
1599 reflexHash.add(&triangulationVertices[currIndex]);
1600 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001601 }
1602
1603 // The general concept: We are trying to find three neighboring vertices where
1604 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1605 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1606 // we have triangulated the entire polygon.
1607 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1608 // noting that only convex vertices can be potential ears, and we only need to check whether
1609 // any reflex vertices lie inside the ear.
1610 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1611 int vertexCount = polygonSize;
1612 while (vertexCount > 3) {
1613 bool success = false;
1614 TriangulationVertex* earVertex = nullptr;
1615 TriangulationVertex* p0 = nullptr;
1616 TriangulationVertex* p2 = nullptr;
1617 // find a convex vertex to clip
1618 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1619 convexIter != convexList.end(); ++convexIter) {
1620 earVertex = *convexIter;
1621 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1622
1623 p0 = &triangulationVertices[earVertex->fPrevIndex];
1624 p2 = &triangulationVertices[earVertex->fNextIndex];
1625
1626 // see if any reflex vertices are inside the ear
1627 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001628 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001629 if (failed) {
1630 continue;
1631 }
1632
1633 // found one we can clip
1634 success = true;
1635 break;
1636 }
1637 // If we can't find any ears to clip, this probably isn't a simple polygon
1638 if (!success) {
1639 return false;
1640 }
1641
1642 // add indices
1643 auto indices = triangleIndices->append(3);
1644 indices[0] = indexMap[p0->fIndex];
1645 indices[1] = indexMap[earVertex->fIndex];
1646 indices[2] = indexMap[p2->fIndex];
1647
1648 // clip the ear
1649 convexList.remove(earVertex);
1650 --vertexCount;
1651
1652 // reclassify reflex verts
1653 p0->fNextIndex = earVertex->fNextIndex;
1654 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1655
1656 p2->fPrevIndex = earVertex->fPrevIndex;
1657 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1658 }
1659
1660 // output indices
1661 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1662 vertexIter != convexList.end(); ++vertexIter) {
1663 TriangulationVertex* vertex = *vertexIter;
1664 *triangleIndices->push() = indexMap[vertex->fIndex];
1665 }
1666
1667 return true;
1668}