blob: 5bf8e3ecb55a08ea33a77a0e4d999e2c3a81cfcc [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
Cary Clarkdf429f32017-11-08 11:44:31 -050012#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040013#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000014#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040015#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040016#include "SkTInternalLList.h"
17
18//////////////////////////////////////////////////////////////////////////////////
19// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000020
Jim Van Verth4db18ed2018-04-03 10:00:37 -040021struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000022 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040023 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000024};
25
Jim Van Verthba4847c2018-08-07 16:02:33 -040026constexpr SkScalar kCrossTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
27
28// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
Brian Salomonab664fa2017-03-24 16:07:20 +000029// A positive value means the point is to the left of the segment,
30// negative is to the right, 0 is collinear.
Jim Van Verthba4847c2018-08-07 16:02:33 -040031static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
32 SkVector w = p - p0;
33 SkScalar perpDot = v.cross(w);
34 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
Brian Salomonab664fa2017-03-24 16:07:20 +000035 return ((perpDot > 0) ? 1 : -1);
36 }
37
38 return 0;
39}
40
Jim Van Verth6784ffa2018-07-03 16:12:39 -040041// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
42int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
43 if (polygonSize < 3) {
44 return 0;
45 }
46
Jim Van Verth8664a1d2018-06-28 16:26:50 -040047 // compute area and use sign to determine winding
48 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040049 SkVector v0 = polygonVerts[1] - polygonVerts[0];
50 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040051 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040052 SkVector v1 = polygonVerts[next] - polygonVerts[0];
53 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;
660 // empty tree case -- easy
661 if (!fTreeHead.fChild[1]) {
662 ActiveEdge* root = fTreeHead.fChild[1] = this->allocate(p0, v, index0, index1);
663 SkASSERT(root);
664 if (!root) {
665 return false;
666 }
667 root->fRed = false;
668 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400669 }
670
Jim Van Verth04d16322018-08-15 15:01:35 -0400671 // set up helpers
672 ActiveEdge* top = &fTreeHead;
673 ActiveEdge *grandparent = nullptr;
674 ActiveEdge *parent = nullptr;
675 ActiveEdge *curr = top->fChild[1];
676 int dir = 0;
677 int last = 0; // ?
678 // predecessor and successor, for intersection check
679 ActiveEdge* pred = nullptr;
680 ActiveEdge* succ = nullptr;
681
682 // search down the tree
683 while (true) {
684 if (!curr) {
685 // check for intersection with predecessor and successor
686 if ((pred && pred->intersect(p0, v, index0, index1)) ||
687 (succ && succ->intersect(p0, v, index0, index1))) {
688 return false;
689 }
690 // insert new node at bottom
691 parent->fChild[dir] = curr = this->allocate(p0, v, index0, index1);
692 SkASSERT(curr);
693 if (!curr) {
694 return false;
695 }
696 curr->fAbove = pred;
697 curr->fBelow = succ;
698 if (pred) {
699 pred->fBelow = curr;
700 }
701 if (succ) {
702 succ->fAbove = curr;
703 }
704 if (IsRed(parent)) {
705 int dir2 = (top->fChild[1] == grandparent);
706 if (curr == parent->fChild[last]) {
707 top->fChild[dir2] = SingleRotation(grandparent, !last);
708 } else {
709 top->fChild[dir2] = DoubleRotation(grandparent, !last);
710 }
711 }
712 break;
713 } else if (IsRed(curr->fChild[0]) && IsRed(curr->fChild[1])) {
714 // color flip
715 curr->fRed = true;
716 curr->fChild[0]->fRed = false;
717 curr->fChild[1]->fRed = false;
718 if (IsRed(parent)) {
719 int dir2 = (top->fChild[1] == grandparent);
720 if (curr == parent->fChild[last]) {
721 top->fChild[dir2] = SingleRotation(grandparent, !last);
722 } else {
723 top->fChild[dir2] = DoubleRotation(grandparent, !last);
724 }
725 }
726 }
727
728 last = dir;
729 int side;
730 // check to see if segment is above or below
731 if (curr->fIndex0 == index0) {
732 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
733 } else {
734 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
735 }
736 if (0 == side) {
737 return false;
738 }
739 dir = (side < 0);
740
741 if (0 == dir) {
742 succ = curr;
743 } else {
744 pred = curr;
745 }
746
747 // update helpers
748 if (grandparent) {
749 top = grandparent;
750 }
751 grandparent = parent;
752 parent = curr;
753 curr = curr->fChild[dir];
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400754 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400755
756 // update root and make it black
757 fTreeHead.fChild[1]->fRed = false;
758
759 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400760
761 return true;
762 }
763
Jim Van Verth04d16322018-08-15 15:01:35 -0400764 // replaces edge p0p1 with p1p2
765 bool replace(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
766 uint16_t index0, uint16_t index1, uint16_t index2) {
767 if (!fTreeHead.fChild[1]) {
Jim Van Verth00673692018-07-23 11:23:39 -0400768 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400769 }
770
Jim Van Verth04d16322018-08-15 15:01:35 -0400771 SkVector v = p2 - p1;
772 ActiveEdge* curr = &fTreeHead;
773 ActiveEdge* found = nullptr;
774 int dir = 1;
775
776 // search
777 while (curr->fChild[dir] != nullptr) {
778 // update helpers
779 curr = curr->fChild[dir];
780 // save found node
781 if (curr->equals(index0, index1)) {
782 found = curr;
783 break;
784 } else {
785 // check to see if segment is above or below
786 int side;
787 if (curr->fIndex1 == index1) {
788 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
789 } else {
790 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
791 }
792 if (0 == side) {
793 return false;
794 }
795 dir = (side < 0);
796 }
797 }
798
799 if (!found) {
800 return false;
801 }
802
803 // replace if found
804 ActiveEdge* pred = found->fAbove;
805 ActiveEdge* succ = found->fBelow;
806 // check deletion and insert intersection cases
807 if (pred && (pred->intersect(found) || pred->intersect(p1, v, index1, index2))) {
808 return false;
809 }
810 if (succ && (succ->intersect(found) || succ->intersect(p1, v, index1, index2))) {
811 return false;
812 }
813 found->fSegment.fP0 = p1;
814 found->fSegment.fV = v;
815 found->fIndex0 = index1;
816 found->fIndex1 = index2;
817 // above and below should stay the same
818
819 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
820
821 return true;
822 }
823
824 bool remove(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
825 if (!fTreeHead.fChild[1]) {
826 return false;
827 }
828
829 ActiveEdge* curr = &fTreeHead;
830 ActiveEdge* parent = nullptr;
831 ActiveEdge* grandparent = nullptr;
832 ActiveEdge* found = nullptr;
833 int dir = 1;
834
835 // search and push a red node down
836 while (curr->fChild[dir] != nullptr) {
837 int last = dir;
838
839 // update helpers
840 grandparent = parent;
841 parent = curr;
842 curr = curr->fChild[dir];
843 // save found node
844 if (curr->equals(index0, index1)) {
845 found = curr;
846 dir = 0;
847 } else {
848 // check to see if segment is above or below
849 int side;
850 if (curr->fIndex1 == index1) {
851 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
852 } else {
853 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
854 }
855 if (0 == side) {
856 return false;
857 }
858 dir = (side < 0);
859 }
860
861 // push the red node down
862 if (!IsRed(curr) && !IsRed(curr->fChild[dir])) {
863 if (IsRed(curr->fChild[!dir])) {
864 parent = parent->fChild[last] = SingleRotation(curr, dir);
865 } else {
866 ActiveEdge *s = parent->fChild[!last];
867
868 if (s != NULL) {
869 if (!IsRed(s->fChild[!last]) && !IsRed(s->fChild[last])) {
870 // color flip
871 parent->fRed = false;
872 s->fRed = true;
873 curr->fRed = true;
874 } else {
875 int dir2 = (grandparent->fChild[1] == parent);
876
877 if (IsRed(s->fChild[last])) {
878 grandparent->fChild[dir2] = DoubleRotation(parent, last);
879 } else if (IsRed(s->fChild[!last])) {
880 grandparent->fChild[dir2] = SingleRotation(parent, last);
881 }
882
883 // ensure correct coloring
884 curr->fRed = grandparent->fChild[dir2]->fRed = true;
885 grandparent->fChild[dir2]->fChild[0]->fRed = false;
886 grandparent->fChild[dir2]->fChild[1]->fRed = false;
887 }
888 }
889 }
890 }
891 }
892
893 // replace and remove if found
894 if (found) {
895 ActiveEdge* pred = found->fAbove;
896 ActiveEdge* succ = found->fBelow;
897 if ((pred && pred->intersect(found)) || (succ && succ->intersect(found))) {
898 return false;
899 }
900 if (found != curr) {
901 found->fSegment = curr->fSegment;
902 found->fIndex0 = curr->fIndex0;
903 found->fIndex1 = curr->fIndex1;
904 found->fAbove = curr->fAbove;
905 pred = found->fAbove;
906 // we don't need to set found->fBelow here
907 } else {
908 if (succ) {
909 succ->fAbove = pred;
910 }
911 }
912 if (pred) {
913 pred->fBelow = curr->fBelow;
914 }
915 parent->fChild[parent->fChild[1] == curr] = curr->fChild[!curr->fChild[0]];
916
917 // no need to delete
918 curr->fAbove = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
919 curr->fBelow = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
920 if (fTreeHead.fChild[1]) {
921 fTreeHead.fChild[1]->fRed = false;
922 }
923 }
924
925 // update root and make it black
926 if (fTreeHead.fChild[1]) {
927 fTreeHead.fChild[1]->fRed = false;
928 }
929
930 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
931
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400932 return true;
933 }
934
935private:
Jim Van Verth04d16322018-08-15 15:01:35 -0400936 // allocator
937 ActiveEdge * allocate(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
938 if (fCurrFree >= fMaxFree) {
939 return nullptr;
940 }
941 char* bytes = fAllocation + sizeof(ActiveEdge)*fCurrFree;
942 ++fCurrFree;
943 return new(bytes) ActiveEdge(p0, p1, index0, index1);
944 }
945
946 ///////////////////////////////////////////////////////////////////////////////////
947 // Red-black tree methods
948 ///////////////////////////////////////////////////////////////////////////////////
949 static bool IsRed(const ActiveEdge* node) {
950 return node && node->fRed;
951 }
952
953 static ActiveEdge* SingleRotation(ActiveEdge* node, int dir) {
954 ActiveEdge* tmp = node->fChild[!dir];
955
956 node->fChild[!dir] = tmp->fChild[dir];
957 tmp->fChild[dir] = node;
958
959 node->fRed = true;
960 tmp->fRed = false;
961
962 return tmp;
963 }
964
965 static ActiveEdge* DoubleRotation(ActiveEdge* node, int dir) {
966 node->fChild[!dir] = SingleRotation(node->fChild[!dir], !dir);
967
968 return SingleRotation(node, dir);
969 }
970
971 // returns black link count
972 static int VerifyTree(const ActiveEdge* tree) {
973 if (!tree) {
974 return 1;
975 }
976
977 const ActiveEdge* left = tree->fChild[0];
978 const ActiveEdge* right = tree->fChild[1];
979
980 // no consecutive red links
981 if (IsRed(tree) && (IsRed(left) || IsRed(right))) {
982 SkASSERT(false);
983 return 0;
984 }
985
986 // check secondary links
987 if (tree->fAbove) {
988 SkASSERT(tree->fAbove->fBelow == tree);
989 SkASSERT(tree->fAbove->lessThan(tree));
990 }
991 if (tree->fBelow) {
992 SkASSERT(tree->fBelow->fAbove == tree);
993 SkASSERT(tree->lessThan(tree->fBelow));
994 }
995
996 // violates binary tree order
997 if ((left && tree->lessThan(left)) || (right && right->lessThan(tree))) {
998 SkASSERT(false);
999 return 0;
1000 }
1001
1002 int leftCount = VerifyTree(left);
1003 int rightCount = VerifyTree(right);
1004
1005 // return black link count
1006 if (leftCount != 0 && rightCount != 0) {
1007 // black height mismatch
1008 if (leftCount != rightCount) {
1009 SkASSERT(false);
1010 return 0;
1011 }
1012 return IsRed(tree) ? leftCount : leftCount + 1;
1013 } else {
1014 return 0;
1015 }
1016 }
1017
1018 ActiveEdge fTreeHead;
1019 char* fAllocation;
1020 int fCurrFree;
1021 int fMaxFree;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001022};
1023
1024// Here we implement a sweep line algorithm to determine whether the provided points
1025// represent a simple polygon, i.e., the polygon is non-self-intersecting.
1026// We first insert the vertices into a priority queue sorting horizontally from left to right.
1027// Then as we pop the vertices from the queue we generate events which indicate that an edge
1028// should be added or removed from an edge list. If any intersections are detected in the edge
1029// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001030bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001031 if (polygonSize < 3) {
1032 return false;
1033 }
1034
Jim Van Vertha6316832018-07-24 09:30:37 -04001035 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001036 if (polygonSize > std::numeric_limits<uint16_t>::max()) {
Jim Van Vertha6316832018-07-24 09:30:37 -04001037 return false;
1038 }
1039
Jim Van Verth04d16322018-08-15 15:01:35 -04001040 // If it's convex, it's simple
1041 if (SkIsConvexPolygon(polygon, polygonSize)) {
1042 return true;
1043 }
1044
Jim Van Verth00673692018-07-23 11:23:39 -04001045 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001046 for (int i = 0; i < polygonSize; ++i) {
1047 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001048 if (!polygon[i].isFinite()) {
1049 return false;
1050 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001051 newVertex.fPosition = polygon[i];
1052 newVertex.fIndex = i;
1053 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1054 newVertex.fNextIndex = (i + 1) % polygonSize;
1055 newVertex.fFlags = 0;
1056 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1057 newVertex.fFlags |= kPrevLeft_VertexFlag;
1058 }
1059 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1060 newVertex.fFlags |= kNextLeft_VertexFlag;
1061 }
1062 vertexQueue.insert(newVertex);
1063 }
1064
1065 // pop each vertex from the queue and generate events depending on
1066 // where it lies relative to its neighboring edges
Jim Van Verth04d16322018-08-15 15:01:35 -04001067 ActiveEdgeList sweepLine(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001068 while (vertexQueue.count() > 0) {
1069 const Vertex& v = vertexQueue.peek();
1070
Jim Van Verth04d16322018-08-15 15:01:35 -04001071 // both to the right -- insert both
1072 if (v.fFlags == 0) {
Jim Van Verth00673692018-07-23 11:23:39 -04001073 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001074 break;
1075 }
Jim Van Verth00673692018-07-23 11:23:39 -04001076 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001077 break;
1078 }
Jim Van Verth04d16322018-08-15 15:01:35 -04001079 // both to the left -- remove both
1080 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1081 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1082 break;
1083 }
1084 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1085 break;
1086 }
1087 // one to left and right -- replace one with another
1088 } else {
1089 if (v.fFlags & kPrevLeft_VertexFlag) {
1090 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1091 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1092 break;
1093 }
1094 } else {
1095 SkASSERT(v.fFlags & kNextLeft_VertexFlag);
1096 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1097 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1098 break;
1099 }
1100 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001101 }
1102
1103 vertexQueue.pop();
1104 }
1105
1106 return (vertexQueue.count() == 0);
1107}
1108
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001109///////////////////////////////////////////////////////////////////////////////////////////
1110
Jim Van Verth00673692018-07-23 11:23:39 -04001111// helper function for SkOffsetSimplePolygon
1112static void setup_offset_edge(OffsetEdge* currEdge,
1113 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001114 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verthda58cac2018-09-05 12:41:56 -04001115 currEdge->fOffset.fP0 = endpoint0;
1116 currEdge->fOffset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -04001117 currEdge->init(startIndex, endIndex);
1118}
1119
Jim Van Verth98d33752018-08-03 15:59:46 -04001120static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
1121 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
1122 int side = compute_side(inputPolygonVerts[prevIndex],
1123 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
1124 inputPolygonVerts[nextIndex]);
1125 // if reflex point, we need to add extra edges
1126 return (side*winding*offset < 0);
1127}
1128
Jim Van Verthda58cac2018-09-05 12:41:56 -04001129bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize, SkScalar offset,
Jim Van Verthbdde4282018-06-14 09:09:18 -04001130 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001131 if (inputPolygonSize < 3) {
1132 return false;
1133 }
1134
Jim Van Vertha6316832018-07-24 09:30:37 -04001135 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001136 if (inputPolygonSize >= std::numeric_limits<uint16_t>::max()) {
Jim Van Vertha6316832018-07-24 09:30:37 -04001137 return false;
1138 }
1139
Jim Van Verthda58cac2018-09-05 12:41:56 -04001140 if (!SkScalarIsFinite(offset)) {
1141 return false;
1142 }
1143
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001144 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001145 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001146 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001147 return false;
1148 }
1149
Jim Van Verthbdde4282018-06-14 09:09:18 -04001150 // build normals
Jim Van Verthda58cac2018-09-05 12:41:56 -04001151 SkAutoSTMalloc<64, SkVector> normals(inputPolygonSize);
Jim Van Verthb7c95512018-09-11 12:57:42 -04001152 unsigned int numEdges = 0;
Jim Van Verth98d33752018-08-03 15:59:46 -04001153 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1154 currIndex < inputPolygonSize;
1155 prevIndex = currIndex, ++currIndex) {
1156 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001157 return false;
1158 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001159 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001160 compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
1161 offset, winding, &normals[currIndex]);
Jim Van Verth98d33752018-08-03 15:59:46 -04001162 if (currIndex > 0) {
1163 // if reflex point, we need to add extra edges
Jim Van Verthda58cac2018-09-05 12:41:56 -04001164 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001165 prevIndex, currIndex, nextIndex)) {
1166 SkScalar rotSin, rotCos;
1167 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001168 if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001169 &rotSin, &rotCos, &numSteps)) {
1170 return false;
1171 }
1172 numEdges += SkTMax(numSteps, 1);
1173 }
1174 }
1175 numEdges++;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001176 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001177 // finish up the edge counting
Jim Van Verthda58cac2018-09-05 12:41:56 -04001178 if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
Jim Van Verth98d33752018-08-03 15:59:46 -04001179 SkScalar rotSin, rotCos;
1180 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001181 if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001182 &rotSin, &rotCos, &numSteps)) {
1183 return false;
1184 }
1185 numEdges += SkTMax(numSteps, 1);
1186 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001187
Jim Van Verthb7c95512018-09-11 12:57:42 -04001188 // Make sure we don't overflow the max array count.
1189 // We shouldn't overflow numEdges, as SkComputeRadialSteps returns a max of 2^16-1,
1190 // and we have a max of 2^16-1 original vertices.
1191 if (numEdges > (unsigned int)std::numeric_limits<int32_t>::max()) {
1192 return false;
1193 }
1194
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001195 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -04001196 SkSTArray<64, OffsetEdge> edgeData(numEdges);
1197 OffsetEdge* prevEdge = nullptr;
1198 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1199 currIndex < inputPolygonSize;
1200 prevIndex = currIndex, ++currIndex) {
1201 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001202 // if reflex point, fill in curve
Jim Van Verthda58cac2018-09-05 12:41:56 -04001203 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001204 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001205 SkScalar rotSin, rotCos;
1206 int numSteps;
Jim Van Verthda58cac2018-09-05 12:41:56 -04001207 SkVector prevNormal = normals[prevIndex];
1208 if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
Jim Van Verth061cc212018-07-11 14:09:09 -04001209 &rotSin, &rotCos, &numSteps)) {
1210 return false;
1211 }
Jim Van Verth00673692018-07-23 11:23:39 -04001212 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001213 for (int i = 0; i < numSteps - 1; ++i) {
1214 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1215 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -04001216 setup_offset_edge(currEdge,
1217 inputPolygonVerts[currIndex] + prevNormal,
1218 inputPolygonVerts[currIndex] + currNormal,
1219 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001220 prevNormal = currNormal;
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 Verth00673692018-07-23 11:23:39 -04001226 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001227 }
Jim Van Verth00673692018-07-23 11:23:39 -04001228 setup_offset_edge(currEdge,
1229 inputPolygonVerts[currIndex] + prevNormal,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001230 inputPolygonVerts[currIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001231 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001232 currEdge->fPrev = prevEdge;
1233 if (prevEdge) {
1234 prevEdge->fNext = currEdge;
1235 }
1236 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001237 }
1238
1239 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -04001240 auto currEdge = edgeData.push_back_n(1);
1241 setup_offset_edge(currEdge,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001242 inputPolygonVerts[currIndex] + normals[currIndex],
1243 inputPolygonVerts[nextIndex] + normals[currIndex],
Jim Van Verth00673692018-07-23 11:23:39 -04001244 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001245 currEdge->fPrev = prevEdge;
1246 if (prevEdge) {
1247 prevEdge->fNext = currEdge;
1248 }
1249 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001250 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001251 // close up the linked list
1252 SkASSERT(prevEdge);
1253 prevEdge->fNext = &edgeData[0];
1254 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001255
1256 // now clip edges
Jim Van Verthb7c95512018-09-11 12:57:42 -04001257 SkASSERT(edgeData.count() == (int)numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -04001258 auto head = &edgeData[0];
1259 auto currEdge = head;
Jim Van Verthb7c95512018-09-11 12:57:42 -04001260 unsigned int offsetVertexCount = numEdges;
1261 unsigned long long iterations = 0;
1262 unsigned long long maxIterations = (unsigned long long)(numEdges*numEdges);
1263 while (head && prevEdge != currEdge && offsetVertexCount > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001264 ++iterations;
1265 // we should check each edge against each other edge at most once
Jim Van Verthb7c95512018-09-11 12:57:42 -04001266 if (iterations > maxIterations) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001267 return false;
1268 }
1269
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001270 SkScalar s, t;
1271 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -04001272 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001273 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001274 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001275 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -04001276 remove_node(prevEdge, &head);
1277 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001278 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -04001279 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001280 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -04001281 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001282 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -04001283 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001284 1.0e-6f)) {
1285 break;
1286 } else {
1287 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001288 currEdge->fIntersection = intersection;
1289 currEdge->fTValue = t;
1290 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001291
1292 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -04001293 prevEdge = currEdge;
1294 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001295 }
1296 } else {
1297 // If there is no intersection, we want to minimize the distance between
1298 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -04001299 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1300 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001301 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1302 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1303 // if both lead to direct collision
1304 if (dist0 < 0 && dist1 < 0) {
1305 // check first to see if either represent parts of one contour
Jim Van Verthda58cac2018-09-05 12:41:56 -04001306 SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001307 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001308 prevEdge->fOffset.fP0);
1309 p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001310 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
Jim Van Verthda58cac2018-09-05 12:41:56 -04001311 currNextEdge->fOffset.fP0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001312
1313 // want to step along contour to find intersections rather than jump to new one
1314 if (currSameContour && !prevSameContour) {
1315 remove_node(currEdge, &head);
1316 currEdge = currNextEdge;
1317 --offsetVertexCount;
1318 continue;
1319 } else if (prevSameContour && !currSameContour) {
1320 remove_node(prevEdge, &head);
1321 prevEdge = prevPrevEdge;
1322 --offsetVertexCount;
1323 continue;
1324 }
1325 }
1326
1327 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001328 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -04001329 remove_node(prevEdge, &head);
1330 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001331 } else {
Jim Van Verth00673692018-07-23 11:23:39 -04001332 remove_node(currEdge, &head);
1333 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001334 }
Jim Van Verth00673692018-07-23 11:23:39 -04001335 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001336 }
1337 }
1338
1339 // store all the valid intersections that aren't nearly coincident
1340 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001341 offsetPolygon->reset();
Jim Van Verthb7c95512018-09-11 12:57:42 -04001342 if (!head || offsetVertexCount == 0 ||
1343 offsetVertexCount >= std::numeric_limits<uint16_t>::max()) {
1344 return false;
1345 }
1346
1347 static constexpr SkScalar kCleanupTolerance = 0.01f;
1348 offsetPolygon->setReserve(offsetVertexCount);
1349 int currIndex = 0;
1350 *offsetPolygon->push() = head->fIntersection;
1351 if (polygonIndices) {
1352 *polygonIndices->push() = head->fIndex;
1353 }
1354 currEdge = head->fNext;
1355 while (currEdge != head) {
1356 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1357 (*offsetPolygon)[currIndex],
1358 kCleanupTolerance)) {
1359 *offsetPolygon->push() = currEdge->fIntersection;
1360 if (polygonIndices) {
1361 *polygonIndices->push() = currEdge->fIndex;
1362 }
1363 currIndex++;
Jim Van Verth00673692018-07-23 11:23:39 -04001364 }
1365 currEdge = currEdge->fNext;
Jim Van Verthb7c95512018-09-11 12:57:42 -04001366 }
1367 // make sure the first and last points aren't coincident
1368 if (currIndex >= 1 &&
1369 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1370 kCleanupTolerance)) {
1371 offsetPolygon->pop();
1372 if (polygonIndices) {
1373 polygonIndices->pop();
Jim Van Verth872da6b2018-04-10 11:24:11 -04001374 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001375 }
1376
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001377 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001378 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001379
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001380 return (winding*offsetWinding > 0 &&
1381 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001382}
1383
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001384//////////////////////////////////////////////////////////////////////////////////////////
1385
1386struct TriangulationVertex {
1387 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1388
1389 enum class VertexType { kConvex, kReflex };
1390
1391 SkPoint fPosition;
1392 VertexType fVertexType;
1393 uint16_t fIndex;
1394 uint16_t fPrevIndex;
1395 uint16_t fNextIndex;
1396};
1397
1398// test to see if point p is in triangle p0p1p2.
1399// for now assuming strictly inside -- if on the edge it's outside
1400static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1401 const SkPoint& p) {
1402 SkVector v0 = p1 - p0;
1403 SkVector v1 = p2 - p1;
1404 SkScalar n = v0.cross(v1);
1405
1406 SkVector w0 = p - p0;
1407 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1408 return false;
1409 }
1410
1411 SkVector w1 = p - p1;
1412 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1413 return false;
1414 }
1415
1416 SkVector v2 = p0 - p2;
1417 SkVector w2 = p - p2;
1418 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1419 return false;
1420 }
1421
1422 return true;
1423}
1424
1425// Data structure to track reflex vertices and check whether any are inside a given triangle
1426class ReflexHash {
1427public:
1428 void add(TriangulationVertex* v) {
1429 fReflexList.addToTail(v);
1430 }
1431
1432 void remove(TriangulationVertex* v) {
1433 fReflexList.remove(v);
1434 }
1435
Jim Van Verth061cc212018-07-11 14:09:09 -04001436 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1437 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001438 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1439 reflexIter != fReflexList.end(); ++reflexIter) {
1440 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001441 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1442 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001443 return true;
1444 }
1445 }
1446
1447 return false;
1448 }
1449
1450private:
1451 // TODO: switch to an actual spatial hash
1452 SkTInternalLList<TriangulationVertex> fReflexList;
1453};
1454
1455// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1456static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1457 int winding, ReflexHash* reflexHash,
1458 SkTInternalLList<TriangulationVertex>* convexList) {
1459 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1460 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1461 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001462 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001463 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1464 reflexHash->remove(p);
1465 p->fPrev = p->fNext = nullptr;
1466 convexList->addToTail(p);
1467 }
1468 }
1469}
1470
1471bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1472 SkTDArray<uint16_t>* triangleIndices) {
1473 if (polygonSize < 3) {
1474 return false;
1475 }
1476 // need to be able to represent all the vertices in the 16-bit indices
Jim Van Verthb7c95512018-09-11 12:57:42 -04001477 if (polygonSize >= std::numeric_limits<uint16_t>::max()) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001478 return false;
1479 }
1480
1481 // get winding direction
1482 // TODO: we do this for all the polygon routines -- might be better to have the client
1483 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001484 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001485 if (0 == winding) {
1486 return false;
1487 }
1488
1489 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1490 // TODO: possibly sort the convexList in some way to get better triangles
1491 SkTInternalLList<TriangulationVertex> convexList;
1492 ReflexHash reflexHash;
1493 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1494 int prevIndex = polygonSize - 1;
1495 int currIndex = 0;
1496 int nextIndex = 1;
1497 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1498 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1499 for (int i = 0; i < polygonSize; ++i) {
1500 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1501 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1502 triangulationVertices[currIndex].fIndex = currIndex;
1503 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1504 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001505 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001506 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1507 convexList.addToTail(&triangulationVertices[currIndex]);
1508 } else {
1509 // We treat near collinear vertices as reflex
1510 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1511 reflexHash.add(&triangulationVertices[currIndex]);
1512 }
1513
1514 prevIndex = currIndex;
1515 currIndex = nextIndex;
1516 nextIndex = (currIndex + 1) % polygonSize;
1517 v0 = v1;
1518 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1519 }
1520
1521 // The general concept: We are trying to find three neighboring vertices where
1522 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1523 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1524 // we have triangulated the entire polygon.
1525 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1526 // noting that only convex vertices can be potential ears, and we only need to check whether
1527 // any reflex vertices lie inside the ear.
1528 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1529 int vertexCount = polygonSize;
1530 while (vertexCount > 3) {
1531 bool success = false;
1532 TriangulationVertex* earVertex = nullptr;
1533 TriangulationVertex* p0 = nullptr;
1534 TriangulationVertex* p2 = nullptr;
1535 // find a convex vertex to clip
1536 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1537 convexIter != convexList.end(); ++convexIter) {
1538 earVertex = *convexIter;
1539 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1540
1541 p0 = &triangulationVertices[earVertex->fPrevIndex];
1542 p2 = &triangulationVertices[earVertex->fNextIndex];
1543
1544 // see if any reflex vertices are inside the ear
1545 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001546 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001547 if (failed) {
1548 continue;
1549 }
1550
1551 // found one we can clip
1552 success = true;
1553 break;
1554 }
1555 // If we can't find any ears to clip, this probably isn't a simple polygon
1556 if (!success) {
1557 return false;
1558 }
1559
1560 // add indices
1561 auto indices = triangleIndices->append(3);
1562 indices[0] = indexMap[p0->fIndex];
1563 indices[1] = indexMap[earVertex->fIndex];
1564 indices[2] = indexMap[p2->fIndex];
1565
1566 // clip the ear
1567 convexList.remove(earVertex);
1568 --vertexCount;
1569
1570 // reclassify reflex verts
1571 p0->fNextIndex = earVertex->fNextIndex;
1572 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1573
1574 p2->fPrevIndex = earVertex->fPrevIndex;
1575 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1576 }
1577
1578 // output indices
1579 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1580 vertexIter != convexList.end(); ++vertexIter) {
1581 TriangulationVertex* vertex = *vertexIter;
1582 *triangleIndices->push() = indexMap[vertex->fIndex];
1583 }
1584
1585 return true;
1586}