blob: 8d378f5dc442ed03e90f65fe6d775298c36549e1 [file] [log] [blame]
Brian Salomonab664fa2017-03-24 16:07:20 +00001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Jim Van Verth8664a1d2018-06-28 16:26:50 -04008#include "SkPolyUtils.h"
Brian Salomonab664fa2017-03-24 16:07:20 +00009
Cary Clarkdf429f32017-11-08 11:44:31 -050010#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040011#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000012#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040013#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040014#include "SkTInternalLList.h"
15
16//////////////////////////////////////////////////////////////////////////////////
17// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000018
Jim Van Verth4db18ed2018-04-03 10:00:37 -040019struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000020 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040021 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000022};
23
Jim Van Verthba4847c2018-08-07 16:02:33 -040024constexpr SkScalar kCrossTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
25
26// Computes perpDot for point p compared to segment defined by origin p0 and vector v.
Brian Salomonab664fa2017-03-24 16:07:20 +000027// A positive value means the point is to the left of the segment,
28// negative is to the right, 0 is collinear.
Jim Van Verthba4847c2018-08-07 16:02:33 -040029static int compute_side(const SkPoint& p0, const SkVector& v, const SkPoint& p) {
30 SkVector w = p - p0;
31 SkScalar perpDot = v.cross(w);
32 if (!SkScalarNearlyZero(perpDot, kCrossTolerance)) {
Brian Salomonab664fa2017-03-24 16:07:20 +000033 return ((perpDot > 0) ? 1 : -1);
34 }
35
36 return 0;
37}
38
Jim Van Verth6784ffa2018-07-03 16:12:39 -040039// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
40int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
41 if (polygonSize < 3) {
42 return 0;
43 }
44
Jim Van Verth8664a1d2018-06-28 16:26:50 -040045 // compute area and use sign to determine winding
46 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040047 SkVector v0 = polygonVerts[1] - polygonVerts[0];
48 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040049 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040050 SkVector v1 = polygonVerts[next] - polygonVerts[0];
51 quadArea += v0.cross(v1);
52 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000053 }
Jim Van Verthba4847c2018-08-07 16:02:33 -040054 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040055 return 0;
56 }
57 // 1 == ccw, -1 == cw
58 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000059}
60
Jim Van Verthbdde4282018-06-14 09:09:18 -040061// Helper function to compute the individual vector for non-equal offsets
62inline void compute_offset(SkScalar d, const SkPoint& polyPoint, int side,
63 const SkPoint& outerTangentIntersect, SkVector* v) {
64 SkScalar dsq = d * d;
65 SkVector dP = outerTangentIntersect - polyPoint;
66 SkScalar dPlenSq = SkPointPriv::LengthSqd(dP);
Jim Van Verthba4847c2018-08-07 16:02:33 -040067 if (SkScalarNearlyZero(dPlenSq, SK_ScalarNearlyZero*SK_ScalarNearlyZero)) {
Jim Van Verthbdde4282018-06-14 09:09:18 -040068 v->set(0, 0);
69 } else {
70 SkScalar discrim = SkScalarSqrt(dPlenSq - dsq);
71 v->fX = (dsq*dP.fX - side * d*dP.fY*discrim) / dPlenSq;
72 v->fY = (dsq*dP.fY + side * d*dP.fX*discrim) / dPlenSq;
73 }
74}
75
76// Compute difference vector to offset p0-p1 'd0' and 'd1' units in direction specified by 'side'
77bool compute_offset_vectors(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
78 int side, SkPoint* vector0, SkPoint* vector1) {
Jim Van Verthda965502017-04-11 15:29:14 -040079 SkASSERT(side == -1 || side == 1);
Jim Van Verthda965502017-04-11 15:29:14 -040080 if (SkScalarNearlyEqual(d0, d1)) {
81 // if distances are equal, can just outset by the perpendicular
Jim Van Verthbdde4282018-06-14 09:09:18 -040082 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
Jim Van Verthda965502017-04-11 15:29:14 -040083 perp.setLength(d0*side);
Jim Van Verthbdde4282018-06-14 09:09:18 -040084 *vector0 = perp;
85 *vector1 = perp;
Jim Van Verthda965502017-04-11 15:29:14 -040086 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -040087 SkScalar d0abs = SkTAbs(d0);
88 SkScalar d1abs = SkTAbs(d1);
Jim Van Verthda965502017-04-11 15:29:14 -040089 // Otherwise we need to compute the outer tangent.
90 // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm
Jim Van Verthbdde4282018-06-14 09:09:18 -040091 if (d0abs < d1abs) {
Jim Van Verthda965502017-04-11 15:29:14 -040092 side = -side;
93 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040094 SkScalar dD = d0abs - d1abs;
Jim Van Verthda965502017-04-11 15:29:14 -040095 // if one circle is inside another, we can't compute an offset
Cary Clarkdf429f32017-11-08 11:44:31 -050096 if (dD*dD >= SkPointPriv::DistanceToSqd(p0, p1)) {
Jim Van Verthda965502017-04-11 15:29:14 -040097 return false;
98 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040099 SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0abs - p0.fX*d1abs) / dD,
100 (p1.fY*d0abs - p0.fY*d1abs) / dD);
Jim Van Verthda965502017-04-11 15:29:14 -0400101
Jim Van Verthbdde4282018-06-14 09:09:18 -0400102 compute_offset(d0, p0, side, outerTangentIntersect, vector0);
103 compute_offset(d1, p1, side, outerTangentIntersect, vector1);
Jim Van Verthda965502017-04-11 15:29:14 -0400104 }
105
106 return true;
Brian Salomonab664fa2017-03-24 16:07:20 +0000107}
108
Jim Van Verthbdde4282018-06-14 09:09:18 -0400109// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side'
110bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
111 int side, SkPoint* offset0, SkPoint* offset1) {
112 SkVector v0, v1;
113 if (!compute_offset_vectors(p0, p1, d0, d1, side, &v0, &v1)) {
114 return false;
115 }
116 *offset0 = p0 + v0;
117 *offset1 = p1 + v1;
118
119 return true;
120}
121
Jim Van Verthba4847c2018-08-07 16:02:33 -0400122// check interval to see if intersection is in segment
123static inline bool outside_interval(SkScalar numer, SkScalar denom, bool denomPositive) {
124 return (denomPositive && (numer < 0 || numer > denom)) ||
125 (!denomPositive && (numer > 0 || numer < denom));
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400126}
127
Brian Salomonab664fa2017-03-24 16:07:20 +0000128// Compute the intersection 'p' between segments s0 and s1, if any.
129// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
130// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400131static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +0000132 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400133 const SkVector& v0 = s0.fV;
134 const SkVector& v1 = s1.fV;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400135 SkVector w = s1.fP0 - s0.fP0;
136 SkScalar denom = v0.cross(v1);
137 bool denomPositive = (denom > 0);
138 SkScalar sNumer, tNumer;
139 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400140 // segments are parallel, but not collinear
Jim Van Verthba4847c2018-08-07 16:02:33 -0400141 if (!SkScalarNearlyZero(w.cross(v0), kCrossTolerance) ||
142 !SkScalarNearlyZero(w.cross(v1), kCrossTolerance)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400143 return false;
144 }
145
Jim Van Verthba4847c2018-08-07 16:02:33 -0400146 // Check for zero-length segments
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400147 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400148 // Both are zero-length
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400149 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
150 // Check if they're the same point
Jim Van Verthba4847c2018-08-07 16:02:33 -0400151 if (!SkPointPriv::CanNormalize(w.fX, w.fY)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400152 *p = s0.fP0;
153 *s = 0;
154 *t = 0;
155 return true;
156 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400157 return false;
158 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400159 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400160 // Otherwise project segment0's origin onto segment1
161 tNumer = v1.dot(-w);
162 denom = v1.dot(v1);
163 if (outside_interval(tNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400164 return false;
165 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400166 sNumer = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400167 } else {
168 // Project segment1's endpoints onto segment0
Jim Van Verthba4847c2018-08-07 16:02:33 -0400169 sNumer = v0.dot(w);
170 denom = v0.dot(v0);
171 tNumer = 0;
172 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400173 // The first endpoint doesn't lie on segment0
174 // If segment1 is degenerate, then there's no collision
175 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
176 return false;
177 }
178
179 // Otherwise try the other one
Jim Van Verthba4847c2018-08-07 16:02:33 -0400180 SkScalar oldSNumer = sNumer;
181 sNumer = v0.dot(w + v1);
182 tNumer = denom;
183 if (outside_interval(sNumer, denom, true)) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400184 // it's possible that segment1's interval surrounds segment0
185 // this is false if params have the same signs, and in that case no collision
Jim Van Verthba4847c2018-08-07 16:02:33 -0400186 if (sNumer*oldSNumer > 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400187 return false;
188 }
189 // otherwise project segment0's endpoint onto segment1 instead
Jim Van Verthba4847c2018-08-07 16:02:33 -0400190 sNumer = 0;
191 tNumer = v1.dot(-w);
192 denom = v1.dot(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400193 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400194 }
195 }
196 } else {
Jim Van Verthba4847c2018-08-07 16:02:33 -0400197 sNumer = w.cross(v1);
198 if (outside_interval(sNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400199 return false;
200 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400201 tNumer = w.cross(v0);
202 if (outside_interval(tNumer, denom, denomPositive)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400203 return false;
204 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000205 }
206
Jim Van Verthba4847c2018-08-07 16:02:33 -0400207 SkScalar localS = sNumer/denom;
208 SkScalar localT = tNumer/denom;
209
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400210 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000211 *s = localS;
212 *t = localT;
213
214 return true;
215}
216
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400217bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
218 if (polygonSize < 3) {
219 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400220 }
221
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400222 SkScalar lastArea = 0;
223 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400224
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400225 int prevIndex = polygonSize - 1;
226 int currIndex = 0;
227 int nextIndex = 1;
228 SkPoint origin = polygonVerts[0];
229 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
230 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
231 SkVector w0 = polygonVerts[currIndex] - origin;
232 SkVector w1 = polygonVerts[nextIndex] - origin;
233 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400234 if (!polygonVerts[i].isFinite()) {
235 return false;
236 }
237
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400238 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400239 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400240 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400241 return false;
242 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400243 if (0 != perpDot) {
244 lastPerpDot = perpDot;
245 }
246
247 // If the signed area ever flips it's concave
248 // TODO: see if we can verify convexity only with signed area
249 SkScalar quadArea = w0.cross(w1);
250 if (quadArea*lastArea < 0) {
251 return false;
252 }
253 if (0 != quadArea) {
254 lastArea = quadArea;
255 }
256
257 prevIndex = currIndex;
258 currIndex = nextIndex;
259 nextIndex = (currIndex + 1) % polygonSize;
260 v0 = v1;
261 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
262 w0 = w1;
263 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400264 }
265
266 return true;
267}
Jim Van Verth0513f142017-03-24 14:28:57 -0400268
Jim Van Verth00673692018-07-23 11:23:39 -0400269struct OffsetEdge {
270 OffsetEdge* fPrev;
271 OffsetEdge* fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400272 OffsetSegment fInset;
273 SkPoint fIntersection;
274 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400275 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400276 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400277
Jim Van Verth00673692018-07-23 11:23:39 -0400278 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400279 fIntersection = fInset.fP0;
280 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400281 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400282 fEnd = end;
283 }
284
285 // special intersection check that looks for endpoint intersection
286 bool checkIntersection(const OffsetEdge* that,
287 SkPoint* p, SkScalar* s, SkScalar* t) {
288 if (this->fEnd == that->fIndex) {
289 SkPoint p1 = this->fInset.fP0 + this->fInset.fV;
290 if (SkPointPriv::EqualsWithinTolerance(p1, that->fInset.fP0)) {
291 *p = p1;
292 *s = SK_Scalar1;
293 *t = 0;
294 return true;
295 }
296 }
297
298 return compute_intersection(this->fInset, that->fInset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400299 }
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400300
Jim Van Verthba4847c2018-08-07 16:02:33 -0400301 // computes the line intersection and then the "distance" from that to this
302 // this is really a signed squared distance, where negative means that
303 // the intersection lies inside this->fInset
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400304 SkScalar computeCrossingDistance(const OffsetEdge* that) {
305 const OffsetSegment& s0 = this->fInset;
306 const OffsetSegment& s1 = that->fInset;
307 const SkVector& v0 = s0.fV;
308 const SkVector& v1 = s1.fV;
309
Jim Van Verthba4847c2018-08-07 16:02:33 -0400310 SkScalar denom = v0.cross(v1);
311 if (SkScalarNearlyZero(denom, kCrossTolerance)) {
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400312 // segments are parallel
313 return SK_ScalarMax;
314 }
315
Jim Van Verthba4847c2018-08-07 16:02:33 -0400316 SkVector w = s1.fP0 - s0.fP0;
317 SkScalar localS = w.cross(v1) / denom;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400318 if (localS < 0) {
319 localS = -localS;
320 } else {
321 localS -= SK_Scalar1;
322 }
323
Jim Van Verthba4847c2018-08-07 16:02:33 -0400324 localS *= SkScalarAbs(localS);
325 localS *= v0.dot(v0);
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400326
327 return localS;
328 }
329
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400330};
331
Jim Van Verth00673692018-07-23 11:23:39 -0400332static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
333 // remove from linked list
334 node->fPrev->fNext = node->fNext;
335 node->fNext->fPrev = node->fPrev;
336 if (node == *head) {
337 *head = (node->fNext == node) ? nullptr : node->fNext;
338 }
339}
340
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400341//////////////////////////////////////////////////////////////////////////////////
342
Brian Salomonab664fa2017-03-24 16:07:20 +0000343// The objective here is to inset all of the edges by the given distance, and then
344// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
345// we should only be making left-hand turns (for cw polygons, we use the winding
346// parameter to reverse this). We detect this by checking whether the second intersection
347// on an edge is closer to its tail than the first one.
348//
349// We might also have the case that there is no intersection between two neighboring inset edges.
350// In this case, one edge will lie to the right of the other and should be discarded along with
351// its previous intersection (if any).
352//
353// Note: the assumption is that inputPolygon is convex and has no coincident points.
354//
355bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400356 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400357 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000358 if (inputPolygonSize < 3) {
359 return false;
360 }
361
Jim Van Vertha6316832018-07-24 09:30:37 -0400362 // restrict this to match other routines
363 // practically we don't want anything bigger than this anyway
364 if (inputPolygonSize >= (1 << 16)) {
365 return false;
366 }
367
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400368 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400369 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000370 if (0 == winding) {
371 return false;
372 }
373
374 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400375 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
376 int prev = inputPolygonSize - 1;
377 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
378 int next = (curr + 1) % inputPolygonSize;
379 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400380 return false;
381 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400382 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400383 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400384 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400385 return false;
386 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400387 SkPoint p0, p1;
Jim Van Verth00673692018-07-23 11:23:39 -0400388 if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
389 insetDistanceFunc(inputPolygonVerts[curr]),
390 insetDistanceFunc(inputPolygonVerts[next]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400391 winding,
Jim Van Vertha6316832018-07-24 09:30:37 -0400392 &p0, &p1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400393 return false;
394 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400395 edgeData[curr].fPrev = &edgeData[prev];
396 edgeData[curr].fNext = &edgeData[next];
397 edgeData[curr].fInset.fP0 = p0;
398 edgeData[curr].fInset.fV = p1 - p0;
Jim Van Verth00673692018-07-23 11:23:39 -0400399 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 }
401
Jim Van Verth00673692018-07-23 11:23:39 -0400402 OffsetEdge* head = &edgeData[0];
403 OffsetEdge* currEdge = head;
404 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000405 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400406 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400407 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400408 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400409 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400410 if (iterations > inputPolygonSize*inputPolygonSize) {
411 return false;
412 }
413
Brian Salomonab664fa2017-03-24 16:07:20 +0000414 SkScalar s, t;
415 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400416 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000417 &intersection, &s, &t)) {
418 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400419 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000420 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400421 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000422 --insetVertexCount;
423 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400424 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000425 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400426 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500427 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400428 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000429 1.0e-6f)) {
430 break;
431 } else {
432 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400433 currEdge->fIntersection = intersection;
434 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000435
436 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400437 prevEdge = currEdge;
438 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000439 }
440 } else {
441 // if prev to right side of curr
Jim Van Verth00673692018-07-23 11:23:39 -0400442 int side = winding*compute_side(currEdge->fInset.fP0,
Jim Van Vertha6316832018-07-24 09:30:37 -0400443 currEdge->fInset.fV,
444 prevEdge->fInset.fP0);
445 if (side < 0 &&
446 side == winding*compute_side(currEdge->fInset.fP0,
447 currEdge->fInset.fV,
448 prevEdge->fInset.fP0 + prevEdge->fInset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000449 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400450 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000451 --insetVertexCount;
452 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400453 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000454 } else {
455 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400456 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000457 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400458 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000459 }
460 }
461 }
462
Jim Van Verthda965502017-04-11 15:29:14 -0400463 // store all the valid intersections that aren't nearly coincident
464 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000465 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400466 if (head) {
467 static constexpr SkScalar kCleanupTolerance = 0.01f;
468 if (insetVertexCount >= 0) {
469 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000470 }
Jim Van Verth00673692018-07-23 11:23:39 -0400471 int currIndex = 0;
472 OffsetEdge* currEdge = head;
473 *insetPolygon->push() = currEdge->fIntersection;
474 currEdge = currEdge->fNext;
475 while (currEdge != head) {
476 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
477 (*insetPolygon)[currIndex],
478 kCleanupTolerance)) {
479 *insetPolygon->push() = currEdge->fIntersection;
480 currIndex++;
481 }
482 currEdge = currEdge->fNext;
483 }
484 // make sure the first and last points aren't coincident
485 if (currIndex >= 1 &&
486 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
487 kCleanupTolerance)) {
488 insetPolygon->pop();
489 }
Jim Van Verthda965502017-04-11 15:29:14 -0400490 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000491
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400492 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000493}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400494
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400495///////////////////////////////////////////////////////////////////////////////////////////
496
497// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400498bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar offset,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400499 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400500 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
501
502 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400503 if (!SkScalarIsFinite(rCos)) {
504 return false;
505 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400506 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400507 if (!SkScalarIsFinite(rSin)) {
508 return false;
509 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400510 SkScalar theta = SkScalarATan2(rSin, rCos);
511
Jim Van Verth66c5dc52018-08-06 14:38:31 -0400512 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
Jim Van Verth206dbe82018-07-23 11:48:31 -0400513 // limit the number of steps to at most max uint16_t (that's all we can index)
514 // knock one value off the top to account for rounding
515 if (floatSteps >= (1 << 16)-1) {
516 return false;
517 }
518 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400519
Jim Van Verth061cc212018-07-11 14:09:09 -0400520 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400521 *rotSin = SkScalarSinCos(dTheta, rotCos);
522 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400523 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400524}
525
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400526///////////////////////////////////////////////////////////////////////////////////////////
527
Jim Van Verth04d16322018-08-15 15:01:35 -0400528// 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 -0400529static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400530 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY > p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400531}
532
Jim Van Verth04d16322018-08-15 15:01:35 -0400533// a point is "right" to another if its x-coord is greater, or if equal, its y-coord is less
534static bool right(const SkPoint& p0, const SkPoint& p1) {
535 return p0.fX > p1.fX || (!(p0.fX < p1.fX) && p0.fY < p1.fY);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400536}
537
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400538struct Vertex {
539 static bool Left(const Vertex& qv0, const Vertex& qv1) {
540 return left(qv0.fPosition, qv1.fPosition);
541 }
Jim Van Verth00673692018-07-23 11:23:39 -0400542
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400543 // packed to fit into 16 bytes (one cache line)
544 SkPoint fPosition;
545 uint16_t fIndex; // index in unsorted polygon
546 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
547 uint16_t fNextIndex;
548 uint16_t fFlags;
549};
550
551enum VertexFlags {
552 kPrevLeft_VertexFlag = 0x1,
553 kNextLeft_VertexFlag = 0x2,
554};
555
Jim Van Verth00673692018-07-23 11:23:39 -0400556struct ActiveEdge {
Jim Van Verth04d16322018-08-15 15:01:35 -0400557 ActiveEdge() : fChild{ nullptr, nullptr }, fAbove(nullptr), fBelow(nullptr), fRed(false) {}
558 ActiveEdge(const SkPoint& p0, const SkVector& v, uint16_t index0, uint16_t index1)
559 : fSegment({ p0, v })
Jim Van Verth00673692018-07-23 11:23:39 -0400560 , fIndex0(index0)
Jim Van Verth04d16322018-08-15 15:01:35 -0400561 , fIndex1(index1)
562 , fAbove(nullptr)
563 , fBelow(nullptr)
564 , fRed(true) {
565 fChild[0] = nullptr;
566 fChild[1] = nullptr;
567 }
Jim Van Verth00673692018-07-23 11:23:39 -0400568
Jim Van Verth04d16322018-08-15 15:01:35 -0400569 // Returns true if "this" is above "that", assuming this->p0 is to the left of that->p0
570 // This is only used to verify the edgelist -- the actual test for insertion/deletion is much
571 // simpler because we can make certain assumptions then.
572 bool aboveIfLeft(const ActiveEdge* that) const {
573 const SkPoint& p0 = this->fSegment.fP0;
574 const SkPoint& q0 = that->fSegment.fP0;
575 SkASSERT(p0.fX <= q0.fX);
576 SkVector d = q0 - p0;
577 const SkVector& v = this->fSegment.fV;
578 const SkVector& w = that->fSegment.fV;
579 // The idea here is that if the vector between the origins of the two segments (d)
580 // rotates counterclockwise up to the vector representing the "this" segment (v),
581 // then we know that "this" is above "that". If the result is clockwise we say it's below.
582 if (this->fIndex0 != that->fIndex0) {
583 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400584 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400585 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400586 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400587 return false;
588 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400589 } else if (this->fIndex1 == that->fIndex1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400590 return false;
591 }
Jim Van Verth00673692018-07-23 11:23:39 -0400592 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400593 // lies on dv. So then we try the same for the vector from the tail of "this"
594 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Verth04d16322018-08-15 15:01:35 -0400595 // d = that.P1 - this.P0
596 // = that.fP0 + that.fV - this.fP0
597 // = that.fP0 - this.fP0 + that.fV
598 // = old_d + that.fV
599 d += w;
600 SkScalar cross = d.cross(v);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400601 if (cross > kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400602 return true;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400603 } else if (cross < -kCrossTolerance) {
Jim Van Verth00673692018-07-23 11:23:39 -0400604 return false;
605 }
606 // If the previous check fails, the two segments are nearly collinear
607 // First check y-coord of first endpoints
Jim Van Verth04d16322018-08-15 15:01:35 -0400608 if (p0.fX < q0.fX) {
609 return (p0.fY >= q0.fY);
610 } else if (p0.fY > q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400611 return true;
Jim Van Verth04d16322018-08-15 15:01:35 -0400612 } else if (p0.fY < q0.fY) {
Jim Van Verth00673692018-07-23 11:23:39 -0400613 return false;
614 }
615 // The first endpoints are the same, so check the other endpoint
Jim Van Verth04d16322018-08-15 15:01:35 -0400616 SkPoint p1 = p0 + v;
617 SkPoint q1 = q0 + w;
618 if (p1.fX < q1.fX) {
619 return (p1.fY >= q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400620 } else {
Jim Van Verth04d16322018-08-15 15:01:35 -0400621 return (p1.fY > q1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400622 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400623 }
624
Jim Van Verth04d16322018-08-15 15:01:35 -0400625 // same as leftAndAbove(), but generalized
626 bool above(const ActiveEdge* that) const {
627 const SkPoint& p0 = this->fSegment.fP0;
628 const SkPoint& q0 = that->fSegment.fP0;
629 if (right(p0, q0)) {
630 return !that->aboveIfLeft(this);
631 } else {
632 return this->aboveIfLeft(that);
633 }
634 }
635
636 bool intersect(const SkPoint& q0, const SkVector& w, uint16_t index0, uint16_t index1) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400637 // check first to see if these edges are neighbors in the polygon
Jim Van Verth04d16322018-08-15 15:01:35 -0400638 if (this->fIndex0 == index0 || this->fIndex1 == index0 ||
639 this->fIndex0 == index1 || this->fIndex1 == index1) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400640 return false;
641 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400642
643 // We don't need the exact intersection point so we can do a simpler test here.
644 const SkPoint& p0 = this->fSegment.fP0;
645 const SkVector& v = this->fSegment.fV;
646 SkPoint p1 = p0 + v;
Jim Van Verthba4847c2018-08-07 16:02:33 -0400647 SkPoint q1 = q0 + w;
648
Jim Van Verth04d16322018-08-15 15:01:35 -0400649 // We assume some x-overlap due to how the edgelist works
650 // This allows us to simplify our test
651 // We need some slop here because storing the vector and recomputing the second endpoint
652 // doesn't necessary give us the original result in floating point.
653 // TODO: Store vector as double? Store endpoint as well?
654 SkASSERT(q0.fX <= p1.fX + SK_ScalarNearlyZero);
Jim Van Verthba4847c2018-08-07 16:02:33 -0400655
656 // if each segment straddles the other (i.e., the endpoints have different sides)
657 // then they intersect
Jim Van Verth04d16322018-08-15 15:01:35 -0400658 bool result;
659 if (p0.fX < q0.fX) {
660 if (q1.fX < p1.fX) {
661 result = (compute_side(p0, v, q0)*compute_side(p0, v, q1) < 0);
662 } else {
663 result = (compute_side(p0, v, q0)*compute_side(q0, w, p1) > 0);
664 }
665 } else {
666 if (p1.fX < q1.fX) {
667 result = (compute_side(q0, w, p0)*compute_side(q0, w, p1) < 0);
668 } else {
669 result = (compute_side(q0, w, p0)*compute_side(p0, v, q1) > 0);
670 }
Jim Van Verthba4847c2018-08-07 16:02:33 -0400671 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400672 return result;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400673 }
674
Jim Van Verth04d16322018-08-15 15:01:35 -0400675 bool intersect(const ActiveEdge* edge) {
676 return this->intersect(edge->fSegment.fP0, edge->fSegment.fV, edge->fIndex0, edge->fIndex1);
677 }
678
679 bool lessThan(const ActiveEdge* that) const {
680 SkASSERT(!this->above(this));
681 SkASSERT(!that->above(that));
682 SkASSERT(!(this->above(that) && that->above(this)));
Jim Van Verth00673692018-07-23 11:23:39 -0400683 return this->above(that);
684 }
685
Jim Van Verth04d16322018-08-15 15:01:35 -0400686 bool equals(uint16_t index0, uint16_t index1) const {
687 return (this->fIndex0 == index0 && this->fIndex1 == index1);
Jim Van Verth00673692018-07-23 11:23:39 -0400688 }
689
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400690 OffsetSegment fSegment;
Jim Van Verth04d16322018-08-15 15:01:35 -0400691 uint16_t fIndex0; // indices for previous and next vertex in polygon
Jim Van Vertha6316832018-07-24 09:30:37 -0400692 uint16_t fIndex1;
Jim Van Verth04d16322018-08-15 15:01:35 -0400693 ActiveEdge* fChild[2];
694 ActiveEdge* fAbove;
695 ActiveEdge* fBelow;
696 int32_t fRed;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400697};
698
Jim Van Verth00673692018-07-23 11:23:39 -0400699class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400700public:
Jim Van Verth04d16322018-08-15 15:01:35 -0400701 ActiveEdgeList(int maxEdges) {
702 fAllocation = (char*) sk_malloc_throw(sizeof(ActiveEdge)*maxEdges);
703 fCurrFree = 0;
704 fMaxFree = maxEdges;
705 }
706 ~ActiveEdgeList() {
707 fTreeHead.fChild[1] = nullptr;
708 sk_free(fAllocation);
709 }
710
Jim Van Vertha6316832018-07-24 09:30:37 -0400711 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth04d16322018-08-15 15:01:35 -0400712 SkVector v = p1 - p0;
713 // empty tree case -- easy
714 if (!fTreeHead.fChild[1]) {
715 ActiveEdge* root = fTreeHead.fChild[1] = this->allocate(p0, v, index0, index1);
716 SkASSERT(root);
717 if (!root) {
718 return false;
719 }
720 root->fRed = false;
721 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400722 }
723
Jim Van Verth04d16322018-08-15 15:01:35 -0400724 // set up helpers
725 ActiveEdge* top = &fTreeHead;
726 ActiveEdge *grandparent = nullptr;
727 ActiveEdge *parent = nullptr;
728 ActiveEdge *curr = top->fChild[1];
729 int dir = 0;
730 int last = 0; // ?
731 // predecessor and successor, for intersection check
732 ActiveEdge* pred = nullptr;
733 ActiveEdge* succ = nullptr;
734
735 // search down the tree
736 while (true) {
737 if (!curr) {
738 // check for intersection with predecessor and successor
739 if ((pred && pred->intersect(p0, v, index0, index1)) ||
740 (succ && succ->intersect(p0, v, index0, index1))) {
741 return false;
742 }
743 // insert new node at bottom
744 parent->fChild[dir] = curr = this->allocate(p0, v, index0, index1);
745 SkASSERT(curr);
746 if (!curr) {
747 return false;
748 }
749 curr->fAbove = pred;
750 curr->fBelow = succ;
751 if (pred) {
752 pred->fBelow = curr;
753 }
754 if (succ) {
755 succ->fAbove = curr;
756 }
757 if (IsRed(parent)) {
758 int dir2 = (top->fChild[1] == grandparent);
759 if (curr == parent->fChild[last]) {
760 top->fChild[dir2] = SingleRotation(grandparent, !last);
761 } else {
762 top->fChild[dir2] = DoubleRotation(grandparent, !last);
763 }
764 }
765 break;
766 } else if (IsRed(curr->fChild[0]) && IsRed(curr->fChild[1])) {
767 // color flip
768 curr->fRed = true;
769 curr->fChild[0]->fRed = false;
770 curr->fChild[1]->fRed = false;
771 if (IsRed(parent)) {
772 int dir2 = (top->fChild[1] == grandparent);
773 if (curr == parent->fChild[last]) {
774 top->fChild[dir2] = SingleRotation(grandparent, !last);
775 } else {
776 top->fChild[dir2] = DoubleRotation(grandparent, !last);
777 }
778 }
779 }
780
781 last = dir;
782 int side;
783 // check to see if segment is above or below
784 if (curr->fIndex0 == index0) {
785 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
786 } else {
787 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
788 }
789 if (0 == side) {
790 return false;
791 }
792 dir = (side < 0);
793
794 if (0 == dir) {
795 succ = curr;
796 } else {
797 pred = curr;
798 }
799
800 // update helpers
801 if (grandparent) {
802 top = grandparent;
803 }
804 grandparent = parent;
805 parent = curr;
806 curr = curr->fChild[dir];
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400807 }
Jim Van Verth04d16322018-08-15 15:01:35 -0400808
809 // update root and make it black
810 fTreeHead.fChild[1]->fRed = false;
811
812 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400813
814 return true;
815 }
816
Jim Van Verth04d16322018-08-15 15:01:35 -0400817 // replaces edge p0p1 with p1p2
818 bool replace(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
819 uint16_t index0, uint16_t index1, uint16_t index2) {
820 if (!fTreeHead.fChild[1]) {
Jim Van Verth00673692018-07-23 11:23:39 -0400821 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400822 }
823
Jim Van Verth04d16322018-08-15 15:01:35 -0400824 SkVector v = p2 - p1;
825 ActiveEdge* curr = &fTreeHead;
826 ActiveEdge* found = nullptr;
827 int dir = 1;
828
829 // search
830 while (curr->fChild[dir] != nullptr) {
831 // update helpers
832 curr = curr->fChild[dir];
833 // save found node
834 if (curr->equals(index0, index1)) {
835 found = curr;
836 break;
837 } else {
838 // check to see if segment is above or below
839 int side;
840 if (curr->fIndex1 == index1) {
841 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
842 } else {
843 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
844 }
845 if (0 == side) {
846 return false;
847 }
848 dir = (side < 0);
849 }
850 }
851
852 if (!found) {
853 return false;
854 }
855
856 // replace if found
857 ActiveEdge* pred = found->fAbove;
858 ActiveEdge* succ = found->fBelow;
859 // check deletion and insert intersection cases
860 if (pred && (pred->intersect(found) || pred->intersect(p1, v, index1, index2))) {
861 return false;
862 }
863 if (succ && (succ->intersect(found) || succ->intersect(p1, v, index1, index2))) {
864 return false;
865 }
866 found->fSegment.fP0 = p1;
867 found->fSegment.fV = v;
868 found->fIndex0 = index1;
869 found->fIndex1 = index2;
870 // above and below should stay the same
871
872 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
873
874 return true;
875 }
876
877 bool remove(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
878 if (!fTreeHead.fChild[1]) {
879 return false;
880 }
881
882 ActiveEdge* curr = &fTreeHead;
883 ActiveEdge* parent = nullptr;
884 ActiveEdge* grandparent = nullptr;
885 ActiveEdge* found = nullptr;
886 int dir = 1;
887
888 // search and push a red node down
889 while (curr->fChild[dir] != nullptr) {
890 int last = dir;
891
892 // update helpers
893 grandparent = parent;
894 parent = curr;
895 curr = curr->fChild[dir];
896 // save found node
897 if (curr->equals(index0, index1)) {
898 found = curr;
899 dir = 0;
900 } else {
901 // check to see if segment is above or below
902 int side;
903 if (curr->fIndex1 == index1) {
904 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p0);
905 } else {
906 side = compute_side(curr->fSegment.fP0, curr->fSegment.fV, p1);
907 }
908 if (0 == side) {
909 return false;
910 }
911 dir = (side < 0);
912 }
913
914 // push the red node down
915 if (!IsRed(curr) && !IsRed(curr->fChild[dir])) {
916 if (IsRed(curr->fChild[!dir])) {
917 parent = parent->fChild[last] = SingleRotation(curr, dir);
918 } else {
919 ActiveEdge *s = parent->fChild[!last];
920
921 if (s != NULL) {
922 if (!IsRed(s->fChild[!last]) && !IsRed(s->fChild[last])) {
923 // color flip
924 parent->fRed = false;
925 s->fRed = true;
926 curr->fRed = true;
927 } else {
928 int dir2 = (grandparent->fChild[1] == parent);
929
930 if (IsRed(s->fChild[last])) {
931 grandparent->fChild[dir2] = DoubleRotation(parent, last);
932 } else if (IsRed(s->fChild[!last])) {
933 grandparent->fChild[dir2] = SingleRotation(parent, last);
934 }
935
936 // ensure correct coloring
937 curr->fRed = grandparent->fChild[dir2]->fRed = true;
938 grandparent->fChild[dir2]->fChild[0]->fRed = false;
939 grandparent->fChild[dir2]->fChild[1]->fRed = false;
940 }
941 }
942 }
943 }
944 }
945
946 // replace and remove if found
947 if (found) {
948 ActiveEdge* pred = found->fAbove;
949 ActiveEdge* succ = found->fBelow;
950 if ((pred && pred->intersect(found)) || (succ && succ->intersect(found))) {
951 return false;
952 }
953 if (found != curr) {
954 found->fSegment = curr->fSegment;
955 found->fIndex0 = curr->fIndex0;
956 found->fIndex1 = curr->fIndex1;
957 found->fAbove = curr->fAbove;
958 pred = found->fAbove;
959 // we don't need to set found->fBelow here
960 } else {
961 if (succ) {
962 succ->fAbove = pred;
963 }
964 }
965 if (pred) {
966 pred->fBelow = curr->fBelow;
967 }
968 parent->fChild[parent->fChild[1] == curr] = curr->fChild[!curr->fChild[0]];
969
970 // no need to delete
971 curr->fAbove = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
972 curr->fBelow = reinterpret_cast<ActiveEdge*>(0xdeadbeefll);
973 if (fTreeHead.fChild[1]) {
974 fTreeHead.fChild[1]->fRed = false;
975 }
976 }
977
978 // update root and make it black
979 if (fTreeHead.fChild[1]) {
980 fTreeHead.fChild[1]->fRed = false;
981 }
982
983 SkDEBUGCODE(VerifyTree(fTreeHead.fChild[1]));
984
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400985 return true;
986 }
987
988private:
Jim Van Verth04d16322018-08-15 15:01:35 -0400989 // allocator
990 ActiveEdge * allocate(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
991 if (fCurrFree >= fMaxFree) {
992 return nullptr;
993 }
994 char* bytes = fAllocation + sizeof(ActiveEdge)*fCurrFree;
995 ++fCurrFree;
996 return new(bytes) ActiveEdge(p0, p1, index0, index1);
997 }
998
999 ///////////////////////////////////////////////////////////////////////////////////
1000 // Red-black tree methods
1001 ///////////////////////////////////////////////////////////////////////////////////
1002 static bool IsRed(const ActiveEdge* node) {
1003 return node && node->fRed;
1004 }
1005
1006 static ActiveEdge* SingleRotation(ActiveEdge* node, int dir) {
1007 ActiveEdge* tmp = node->fChild[!dir];
1008
1009 node->fChild[!dir] = tmp->fChild[dir];
1010 tmp->fChild[dir] = node;
1011
1012 node->fRed = true;
1013 tmp->fRed = false;
1014
1015 return tmp;
1016 }
1017
1018 static ActiveEdge* DoubleRotation(ActiveEdge* node, int dir) {
1019 node->fChild[!dir] = SingleRotation(node->fChild[!dir], !dir);
1020
1021 return SingleRotation(node, dir);
1022 }
1023
1024 // returns black link count
1025 static int VerifyTree(const ActiveEdge* tree) {
1026 if (!tree) {
1027 return 1;
1028 }
1029
1030 const ActiveEdge* left = tree->fChild[0];
1031 const ActiveEdge* right = tree->fChild[1];
1032
1033 // no consecutive red links
1034 if (IsRed(tree) && (IsRed(left) || IsRed(right))) {
1035 SkASSERT(false);
1036 return 0;
1037 }
1038
1039 // check secondary links
1040 if (tree->fAbove) {
1041 SkASSERT(tree->fAbove->fBelow == tree);
1042 SkASSERT(tree->fAbove->lessThan(tree));
1043 }
1044 if (tree->fBelow) {
1045 SkASSERT(tree->fBelow->fAbove == tree);
1046 SkASSERT(tree->lessThan(tree->fBelow));
1047 }
1048
1049 // violates binary tree order
1050 if ((left && tree->lessThan(left)) || (right && right->lessThan(tree))) {
1051 SkASSERT(false);
1052 return 0;
1053 }
1054
1055 int leftCount = VerifyTree(left);
1056 int rightCount = VerifyTree(right);
1057
1058 // return black link count
1059 if (leftCount != 0 && rightCount != 0) {
1060 // black height mismatch
1061 if (leftCount != rightCount) {
1062 SkASSERT(false);
1063 return 0;
1064 }
1065 return IsRed(tree) ? leftCount : leftCount + 1;
1066 } else {
1067 return 0;
1068 }
1069 }
1070
1071 ActiveEdge fTreeHead;
1072 char* fAllocation;
1073 int fCurrFree;
1074 int fMaxFree;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001075};
1076
1077// Here we implement a sweep line algorithm to determine whether the provided points
1078// represent a simple polygon, i.e., the polygon is non-self-intersecting.
1079// We first insert the vertices into a priority queue sorting horizontally from left to right.
1080// Then as we pop the vertices from the queue we generate events which indicate that an edge
1081// should be added or removed from an edge list. If any intersections are detected in the edge
1082// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001083bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001084 if (polygonSize < 3) {
1085 return false;
1086 }
1087
Jim Van Vertha6316832018-07-24 09:30:37 -04001088 // need to be able to represent all the vertices in the 16-bit indices
1089 if (polygonSize >= (1 << 16)) {
1090 return false;
1091 }
1092
Jim Van Verth04d16322018-08-15 15:01:35 -04001093 // If it's convex, it's simple
1094 if (SkIsConvexPolygon(polygon, polygonSize)) {
1095 return true;
1096 }
1097
Jim Van Verth00673692018-07-23 11:23:39 -04001098 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001099 for (int i = 0; i < polygonSize; ++i) {
1100 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001101 if (!polygon[i].isFinite()) {
1102 return false;
1103 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001104 newVertex.fPosition = polygon[i];
1105 newVertex.fIndex = i;
1106 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1107 newVertex.fNextIndex = (i + 1) % polygonSize;
1108 newVertex.fFlags = 0;
1109 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1110 newVertex.fFlags |= kPrevLeft_VertexFlag;
1111 }
1112 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1113 newVertex.fFlags |= kNextLeft_VertexFlag;
1114 }
1115 vertexQueue.insert(newVertex);
1116 }
1117
1118 // pop each vertex from the queue and generate events depending on
1119 // where it lies relative to its neighboring edges
Jim Van Verth04d16322018-08-15 15:01:35 -04001120 ActiveEdgeList sweepLine(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001121 while (vertexQueue.count() > 0) {
1122 const Vertex& v = vertexQueue.peek();
1123
Jim Van Verth04d16322018-08-15 15:01:35 -04001124 // both to the right -- insert both
1125 if (v.fFlags == 0) {
Jim Van Verth00673692018-07-23 11:23:39 -04001126 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001127 break;
1128 }
Jim Van Verth00673692018-07-23 11:23:39 -04001129 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001130 break;
1131 }
Jim Van Verth04d16322018-08-15 15:01:35 -04001132 // both to the left -- remove both
1133 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1134 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1135 break;
1136 }
1137 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1138 break;
1139 }
1140 // one to left and right -- replace one with another
1141 } else {
1142 if (v.fFlags & kPrevLeft_VertexFlag) {
1143 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1144 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1145 break;
1146 }
1147 } else {
1148 SkASSERT(v.fFlags & kNextLeft_VertexFlag);
1149 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1150 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1151 break;
1152 }
1153 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001154 }
1155
1156 vertexQueue.pop();
1157 }
1158
1159 return (vertexQueue.count() == 0);
1160}
1161
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001162///////////////////////////////////////////////////////////////////////////////////////////
1163
Jim Van Verth00673692018-07-23 11:23:39 -04001164// helper function for SkOffsetSimplePolygon
1165static void setup_offset_edge(OffsetEdge* currEdge,
1166 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001167 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verth00673692018-07-23 11:23:39 -04001168 currEdge->fInset.fP0 = endpoint0;
Jim Van Vertha6316832018-07-24 09:30:37 -04001169 currEdge->fInset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -04001170 currEdge->init(startIndex, endIndex);
1171}
1172
Jim Van Verth98d33752018-08-03 15:59:46 -04001173static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
1174 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
1175 int side = compute_side(inputPolygonVerts[prevIndex],
1176 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
1177 inputPolygonVerts[nextIndex]);
1178 // if reflex point, we need to add extra edges
1179 return (side*winding*offset < 0);
1180}
1181
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001182bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -04001183 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
1184 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001185 if (inputPolygonSize < 3) {
1186 return false;
1187 }
1188
Jim Van Vertha6316832018-07-24 09:30:37 -04001189 // need to be able to represent all the vertices in the 16-bit indices
1190 if (inputPolygonSize >= (1 << 16)) {
1191 return false;
1192 }
1193
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001194 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001195 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001196 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001197 return false;
1198 }
1199
Jim Van Verthbdde4282018-06-14 09:09:18 -04001200 // build normals
1201 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
1202 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001203 SkAutoSTMalloc<64, SkScalar> offset(inputPolygonSize);
1204 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -04001205 if (!SkScalarIsFinite(currOffset)) {
1206 return false;
1207 }
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001208 offset[0] = currOffset;
Jim Van Verth98d33752018-08-03 15:59:46 -04001209 int numEdges = 0;
1210 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1211 currIndex < inputPolygonSize;
1212 prevIndex = currIndex, ++currIndex) {
1213 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -04001214 return false;
1215 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001216 int nextIndex = (currIndex + 1) % inputPolygonSize;
1217 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[nextIndex]);
Jim Van Verth061cc212018-07-11 14:09:09 -04001218 if (!SkScalarIsFinite(nextOffset)) {
1219 return false;
1220 }
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001221 offset[nextIndex] = nextOffset;
Jim Van Verth98d33752018-08-03 15:59:46 -04001222 if (!compute_offset_vectors(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
Jim Van Verthbdde4282018-06-14 09:09:18 -04001223 currOffset, nextOffset, winding,
Jim Van Verth98d33752018-08-03 15:59:46 -04001224 &normal0[currIndex], &normal1[nextIndex])) {
Jim Van Verthbdde4282018-06-14 09:09:18 -04001225 return false;
1226 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001227 if (currIndex > 0) {
1228 // if reflex point, we need to add extra edges
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001229 if (is_reflex_vertex(inputPolygonVerts, winding, currOffset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001230 prevIndex, currIndex, nextIndex)) {
1231 SkScalar rotSin, rotCos;
1232 int numSteps;
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001233 if (!SkComputeRadialSteps(normal1[currIndex], normal0[currIndex], currOffset,
Jim Van Verth98d33752018-08-03 15:59:46 -04001234 &rotSin, &rotCos, &numSteps)) {
1235 return false;
1236 }
1237 numEdges += SkTMax(numSteps, 1);
1238 }
1239 }
1240 numEdges++;
Jim Van Verthbdde4282018-06-14 09:09:18 -04001241 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001242 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001243 // finish up the edge counting
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001244 if (is_reflex_vertex(inputPolygonVerts, winding, currOffset, inputPolygonSize-1, 0, 1)) {
Jim Van Verth98d33752018-08-03 15:59:46 -04001245 SkScalar rotSin, rotCos;
1246 int numSteps;
1247 if (!SkComputeRadialSteps(normal1[0], normal0[0], currOffset,
1248 &rotSin, &rotCos, &numSteps)) {
1249 return false;
1250 }
1251 numEdges += SkTMax(numSteps, 1);
1252 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001253
1254 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -04001255 SkSTArray<64, OffsetEdge> edgeData(numEdges);
1256 OffsetEdge* prevEdge = nullptr;
1257 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1258 currIndex < inputPolygonSize;
1259 prevIndex = currIndex, ++currIndex) {
1260 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001261 // if reflex point, fill in curve
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001262 if (is_reflex_vertex(inputPolygonVerts, winding, offset[currIndex],
Jim Van Verth98d33752018-08-03 15:59:46 -04001263 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001264 SkScalar rotSin, rotCos;
1265 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -04001266 SkVector prevNormal = normal1[currIndex];
Jim Van Verth66c5dc52018-08-06 14:38:31 -04001267 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], offset[currIndex],
Jim Van Verth061cc212018-07-11 14:09:09 -04001268 &rotSin, &rotCos, &numSteps)) {
1269 return false;
1270 }
Jim Van Verth00673692018-07-23 11:23:39 -04001271 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001272 for (int i = 0; i < numSteps - 1; ++i) {
1273 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1274 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -04001275 setup_offset_edge(currEdge,
1276 inputPolygonVerts[currIndex] + prevNormal,
1277 inputPolygonVerts[currIndex] + currNormal,
1278 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001279 prevNormal = currNormal;
Jim Van Verth98d33752018-08-03 15:59:46 -04001280 currEdge->fPrev = prevEdge;
1281 if (prevEdge) {
1282 prevEdge->fNext = currEdge;
1283 }
1284 prevEdge = currEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001285 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001286 }
Jim Van Verth00673692018-07-23 11:23:39 -04001287 setup_offset_edge(currEdge,
1288 inputPolygonVerts[currIndex] + prevNormal,
1289 inputPolygonVerts[currIndex] + normal0[currIndex],
1290 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001291 currEdge->fPrev = prevEdge;
1292 if (prevEdge) {
1293 prevEdge->fNext = currEdge;
1294 }
1295 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001296 }
1297
1298 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -04001299 auto currEdge = edgeData.push_back_n(1);
1300 setup_offset_edge(currEdge,
Jim Van Verth00673692018-07-23 11:23:39 -04001301 inputPolygonVerts[currIndex] + normal0[currIndex],
1302 inputPolygonVerts[nextIndex] + normal1[nextIndex],
1303 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -04001304 currEdge->fPrev = prevEdge;
1305 if (prevEdge) {
1306 prevEdge->fNext = currEdge;
1307 }
1308 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001309 }
Jim Van Verth98d33752018-08-03 15:59:46 -04001310 // close up the linked list
1311 SkASSERT(prevEdge);
1312 prevEdge->fNext = &edgeData[0];
1313 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -04001314
1315 // now clip edges
Jim Van Verth98d33752018-08-03 15:59:46 -04001316 SkASSERT(edgeData.count() == numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -04001317 auto head = &edgeData[0];
1318 auto currEdge = head;
Jim Van Verth98d33752018-08-03 15:59:46 -04001319 int offsetVertexCount = numEdges;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001320 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -04001321 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001322 ++iterations;
1323 // we should check each edge against each other edge at most once
Jim Van Verth98d33752018-08-03 15:59:46 -04001324 if (iterations > numEdges*numEdges) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001325 return false;
1326 }
1327
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001328 SkScalar s, t;
1329 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -04001330 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001331 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001332 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001333 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -04001334 remove_node(prevEdge, &head);
1335 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001336 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -04001337 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001338 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -04001339 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001340 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -04001341 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001342 1.0e-6f)) {
1343 break;
1344 } else {
1345 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -04001346 currEdge->fIntersection = intersection;
1347 currEdge->fTValue = t;
1348 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001349
1350 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -04001351 prevEdge = currEdge;
1352 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001353 }
1354 } else {
1355 // If there is no intersection, we want to minimize the distance between
1356 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -04001357 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1358 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -04001359 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1360 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1361 // if both lead to direct collision
1362 if (dist0 < 0 && dist1 < 0) {
1363 // check first to see if either represent parts of one contour
1364 SkPoint p1 = prevPrevEdge->fInset.fP0 + prevPrevEdge->fInset.fV;
1365 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1366 prevEdge->fInset.fP0);
1367 p1 = currEdge->fInset.fP0 + currEdge->fInset.fV;
1368 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1369 currNextEdge->fInset.fP0);
1370
1371 // want to step along contour to find intersections rather than jump to new one
1372 if (currSameContour && !prevSameContour) {
1373 remove_node(currEdge, &head);
1374 currEdge = currNextEdge;
1375 --offsetVertexCount;
1376 continue;
1377 } else if (prevSameContour && !currSameContour) {
1378 remove_node(prevEdge, &head);
1379 prevEdge = prevPrevEdge;
1380 --offsetVertexCount;
1381 continue;
1382 }
1383 }
1384
1385 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001386 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -04001387 remove_node(prevEdge, &head);
1388 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001389 } else {
Jim Van Verth00673692018-07-23 11:23:39 -04001390 remove_node(currEdge, &head);
1391 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001392 }
Jim Van Verth00673692018-07-23 11:23:39 -04001393 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001394 }
1395 }
1396
1397 // store all the valid intersections that aren't nearly coincident
1398 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001399 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -04001400 if (head) {
1401 static constexpr SkScalar kCleanupTolerance = 0.01f;
1402 if (offsetVertexCount >= 0) {
1403 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +00001404 }
Jim Van Verth00673692018-07-23 11:23:39 -04001405 int currIndex = 0;
1406 OffsetEdge* currEdge = head;
1407 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +00001408 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -04001409 *polygonIndices->push() = currEdge->fIndex;
1410 }
1411 currEdge = currEdge->fNext;
1412 while (currEdge != head) {
1413 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1414 (*offsetPolygon)[currIndex],
1415 kCleanupTolerance)) {
1416 *offsetPolygon->push() = currEdge->fIntersection;
1417 if (polygonIndices) {
1418 *polygonIndices->push() = currEdge->fIndex;
1419 }
1420 currIndex++;
1421 }
1422 currEdge = currEdge->fNext;
1423 }
1424 // make sure the first and last points aren't coincident
1425 if (currIndex >= 1 &&
1426 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1427 kCleanupTolerance)) {
1428 offsetPolygon->pop();
1429 if (polygonIndices) {
1430 polygonIndices->pop();
1431 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001432 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001433 }
1434
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001435 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001436 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001437
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001438 return (winding*offsetWinding > 0 &&
1439 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001440}
1441
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001442//////////////////////////////////////////////////////////////////////////////////////////
1443
1444struct TriangulationVertex {
1445 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1446
1447 enum class VertexType { kConvex, kReflex };
1448
1449 SkPoint fPosition;
1450 VertexType fVertexType;
1451 uint16_t fIndex;
1452 uint16_t fPrevIndex;
1453 uint16_t fNextIndex;
1454};
1455
1456// test to see if point p is in triangle p0p1p2.
1457// for now assuming strictly inside -- if on the edge it's outside
1458static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1459 const SkPoint& p) {
1460 SkVector v0 = p1 - p0;
1461 SkVector v1 = p2 - p1;
1462 SkScalar n = v0.cross(v1);
1463
1464 SkVector w0 = p - p0;
1465 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1466 return false;
1467 }
1468
1469 SkVector w1 = p - p1;
1470 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1471 return false;
1472 }
1473
1474 SkVector v2 = p0 - p2;
1475 SkVector w2 = p - p2;
1476 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1477 return false;
1478 }
1479
1480 return true;
1481}
1482
1483// Data structure to track reflex vertices and check whether any are inside a given triangle
1484class ReflexHash {
1485public:
1486 void add(TriangulationVertex* v) {
1487 fReflexList.addToTail(v);
1488 }
1489
1490 void remove(TriangulationVertex* v) {
1491 fReflexList.remove(v);
1492 }
1493
Jim Van Verth061cc212018-07-11 14:09:09 -04001494 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1495 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001496 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1497 reflexIter != fReflexList.end(); ++reflexIter) {
1498 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001499 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1500 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001501 return true;
1502 }
1503 }
1504
1505 return false;
1506 }
1507
1508private:
1509 // TODO: switch to an actual spatial hash
1510 SkTInternalLList<TriangulationVertex> fReflexList;
1511};
1512
1513// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1514static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1515 int winding, ReflexHash* reflexHash,
1516 SkTInternalLList<TriangulationVertex>* convexList) {
1517 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1518 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1519 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001520 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001521 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1522 reflexHash->remove(p);
1523 p->fPrev = p->fNext = nullptr;
1524 convexList->addToTail(p);
1525 }
1526 }
1527}
1528
1529bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1530 SkTDArray<uint16_t>* triangleIndices) {
1531 if (polygonSize < 3) {
1532 return false;
1533 }
1534 // need to be able to represent all the vertices in the 16-bit indices
1535 if (polygonSize >= (1 << 16)) {
1536 return false;
1537 }
1538
1539 // get winding direction
1540 // TODO: we do this for all the polygon routines -- might be better to have the client
1541 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001542 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001543 if (0 == winding) {
1544 return false;
1545 }
1546
1547 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1548 // TODO: possibly sort the convexList in some way to get better triangles
1549 SkTInternalLList<TriangulationVertex> convexList;
1550 ReflexHash reflexHash;
1551 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1552 int prevIndex = polygonSize - 1;
1553 int currIndex = 0;
1554 int nextIndex = 1;
1555 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1556 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1557 for (int i = 0; i < polygonSize; ++i) {
1558 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1559 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1560 triangulationVertices[currIndex].fIndex = currIndex;
1561 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1562 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001563 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001564 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1565 convexList.addToTail(&triangulationVertices[currIndex]);
1566 } else {
1567 // We treat near collinear vertices as reflex
1568 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1569 reflexHash.add(&triangulationVertices[currIndex]);
1570 }
1571
1572 prevIndex = currIndex;
1573 currIndex = nextIndex;
1574 nextIndex = (currIndex + 1) % polygonSize;
1575 v0 = v1;
1576 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1577 }
1578
1579 // The general concept: We are trying to find three neighboring vertices where
1580 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1581 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1582 // we have triangulated the entire polygon.
1583 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1584 // noting that only convex vertices can be potential ears, and we only need to check whether
1585 // any reflex vertices lie inside the ear.
1586 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1587 int vertexCount = polygonSize;
1588 while (vertexCount > 3) {
1589 bool success = false;
1590 TriangulationVertex* earVertex = nullptr;
1591 TriangulationVertex* p0 = nullptr;
1592 TriangulationVertex* p2 = nullptr;
1593 // find a convex vertex to clip
1594 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1595 convexIter != convexList.end(); ++convexIter) {
1596 earVertex = *convexIter;
1597 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1598
1599 p0 = &triangulationVertices[earVertex->fPrevIndex];
1600 p2 = &triangulationVertices[earVertex->fNextIndex];
1601
1602 // see if any reflex vertices are inside the ear
1603 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001604 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001605 if (failed) {
1606 continue;
1607 }
1608
1609 // found one we can clip
1610 success = true;
1611 break;
1612 }
1613 // If we can't find any ears to clip, this probably isn't a simple polygon
1614 if (!success) {
1615 return false;
1616 }
1617
1618 // add indices
1619 auto indices = triangleIndices->append(3);
1620 indices[0] = indexMap[p0->fIndex];
1621 indices[1] = indexMap[earVertex->fIndex];
1622 indices[2] = indexMap[p2->fIndex];
1623
1624 // clip the ear
1625 convexList.remove(earVertex);
1626 --vertexCount;
1627
1628 // reclassify reflex verts
1629 p0->fNextIndex = earVertex->fNextIndex;
1630 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1631
1632 p2->fPrevIndex = earVertex->fPrevIndex;
1633 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1634 }
1635
1636 // output indices
1637 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1638 vertexIter != convexList.end(); ++vertexIter) {
1639 TriangulationVertex* vertex = *vertexIter;
1640 *triangleIndices->push() = indexMap[vertex->fIndex];
1641 }
1642
1643 return true;
1644}