blob: 8a4bf4900063882ccaa52c6f92a9affb61c1712c [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 Verth00673692018-07-23 11:23:39 -040010#include <set>
Cary Clarkdf429f32017-11-08 11:44:31 -050011#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040012#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000013#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040014#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040015#include "SkTInternalLList.h"
16
17//////////////////////////////////////////////////////////////////////////////////
18// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000019
Jim Van Verth4db18ed2018-04-03 10:00:37 -040020struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000021 SkPoint fP0;
22 SkPoint fP1;
23};
24
25// Computes perpDot for point compared to segment.
26// A positive value means the point is to the left of the segment,
27// negative is to the right, 0 is collinear.
28static int compute_side(const SkPoint& s0, const SkPoint& s1, const SkPoint& p) {
29 SkVector v0 = s1 - s0;
30 SkVector v1 = p - s0;
31 SkScalar perpDot = v0.cross(v1);
32 if (!SkScalarNearlyZero(perpDot)) {
33 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 Verth8664a1d2018-06-28 16:26:50 -040054 if (SkScalarNearlyZero(quadArea)) {
55 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);
67 if (SkScalarNearlyZero(dPlenSq)) {
68 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 Verth6784ffa2018-07-03 16:12:39 -0400122// compute fraction of d along v
123static inline SkScalar compute_param(const SkVector& v, const SkVector& d) {
124 if (SkScalarNearlyZero(v.fX)) {
125 return d.fY / v.fY;
126 } else {
127 return d.fX / v.fX;
128 }
129}
130
Brian Salomonab664fa2017-03-24 16:07:20 +0000131// Compute the intersection 'p' between segments s0 and s1, if any.
132// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
133// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400134static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +0000135 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400136 // Common cases for polygon chains -- check if endpoints are touching
137 if (SkPointPriv::EqualsWithinTolerance(s0.fP1, s1.fP0)) {
138 *p = s0.fP1;
139 *s = SK_Scalar1;
140 *t = 0;
141 return true;
142 }
143 if (SkPointPriv::EqualsWithinTolerance(s1.fP1, s0.fP0)) {
144 *p = s1.fP1;
145 *s = 0;
146 *t = SK_Scalar1;
147 return true;
148 }
149
Brian Salomonab664fa2017-03-24 16:07:20 +0000150 SkVector v0 = s0.fP1 - s0.fP0;
151 SkVector v1 = s1.fP1 - s1.fP0;
Brian Salomonab664fa2017-03-24 16:07:20 +0000152 SkVector d = s1.fP0 - s0.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400153 SkScalar perpDot = v0.cross(v1);
154 SkScalar localS, localT;
155 if (SkScalarNearlyZero(perpDot)) {
156 // segments are parallel, but not collinear
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400157 if (!SkScalarNearlyZero(d.cross(v0)) || !SkScalarNearlyZero(d.cross(v1))) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400158 return false;
159 }
160
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400161 // Check for degenerate segments
162 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
163 // Both are degenerate
164 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
165 // Check if they're the same point
166 if (!SkPointPriv::CanNormalize(d.fX, d.fY)) {
167 *p = s0.fP0;
168 *s = 0;
169 *t = 0;
170 return true;
171 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400172 return false;
173 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400174 }
175 // Otherwise project onto segment1
176 localT = compute_param(v1, -d);
177 if (localT < 0 || localT > SK_Scalar1) {
178 return false;
179 }
180 localS = 0;
181 } else {
182 // Project segment1's endpoints onto segment0
183 localS = compute_param(v0, d);
184 localT = 0;
185 if (localS < 0 || localS > SK_Scalar1) {
186 // The first endpoint doesn't lie on segment0
187 // If segment1 is degenerate, then there's no collision
188 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
189 return false;
190 }
191
192 // Otherwise try the other one
193 SkScalar oldLocalS = localS;
194 localS = compute_param(v0, s1.fP1 - s0.fP0);
195 localT = SK_Scalar1;
196 if (localS < 0 || localS > SK_Scalar1) {
197 // it's possible that segment1's interval surrounds segment0
198 // this is false if params have the same signs, and in that case no collision
199 if (localS*oldLocalS > 0) {
200 return false;
201 }
202 // otherwise project segment0's endpoint onto segment1 instead
203 localS = 0;
204 localT = compute_param(v1, -d);
205 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400206 }
207 }
208 } else {
209 localS = d.cross(v1) / perpDot;
210 if (localS < 0 || localS > SK_Scalar1) {
211 return false;
212 }
213 localT = d.cross(v0) / perpDot;
214 if (localT < 0 || localT > SK_Scalar1) {
215 return false;
216 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000217 }
218
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400219 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000220 *s = localS;
221 *t = localT;
222
223 return true;
224}
225
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400226// computes the line intersection and then the distance to s0's endpoint
227static SkScalar compute_crossing_distance(const OffsetSegment& s0, const OffsetSegment& s1) {
228 SkVector v0 = s0.fP1 - s0.fP0;
229 SkVector v1 = s1.fP1 - s1.fP0;
230
231 SkScalar perpDot = v0.cross(v1);
232 if (SkScalarNearlyZero(perpDot)) {
233 // segments are parallel
234 return SK_ScalarMax;
235 }
236
237 SkVector d = s1.fP0 - s0.fP0;
238 SkScalar localS = d.cross(v1) / perpDot;
239 if (localS < 0) {
240 localS = -localS;
241 } else {
242 localS -= SK_Scalar1;
243 }
244
245 localS *= v0.length();
246
247 return localS;
248}
249
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400250bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
251 if (polygonSize < 3) {
252 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400253 }
254
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400255 SkScalar lastArea = 0;
256 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400257
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400258 int prevIndex = polygonSize - 1;
259 int currIndex = 0;
260 int nextIndex = 1;
261 SkPoint origin = polygonVerts[0];
262 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
263 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
264 SkVector w0 = polygonVerts[currIndex] - origin;
265 SkVector w1 = polygonVerts[nextIndex] - origin;
266 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400267 if (!polygonVerts[i].isFinite()) {
268 return false;
269 }
270
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400271 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400272 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400273 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400274 return false;
275 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400276 if (0 != perpDot) {
277 lastPerpDot = perpDot;
278 }
279
280 // If the signed area ever flips it's concave
281 // TODO: see if we can verify convexity only with signed area
282 SkScalar quadArea = w0.cross(w1);
283 if (quadArea*lastArea < 0) {
284 return false;
285 }
286 if (0 != quadArea) {
287 lastArea = quadArea;
288 }
289
290 prevIndex = currIndex;
291 currIndex = nextIndex;
292 nextIndex = (currIndex + 1) % polygonSize;
293 v0 = v1;
294 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
295 w0 = w1;
296 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400297 }
298
299 return true;
300}
Jim Van Verth0513f142017-03-24 14:28:57 -0400301
Jim Van Verth00673692018-07-23 11:23:39 -0400302struct OffsetEdge {
303 OffsetEdge* fPrev;
304 OffsetEdge* fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400305 OffsetSegment fInset;
306 SkPoint fIntersection;
307 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400308 uint16_t fEnd;
309 uint16_t fIndex;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400310
Jim Van Verth00673692018-07-23 11:23:39 -0400311 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400312 fIntersection = fInset.fP0;
313 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400314 fEnd = end;
315 fIndex = start;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400316 }
317};
318
Jim Van Verth00673692018-07-23 11:23:39 -0400319static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
320 // remove from linked list
321 node->fPrev->fNext = node->fNext;
322 node->fNext->fPrev = node->fPrev;
323 if (node == *head) {
324 *head = (node->fNext == node) ? nullptr : node->fNext;
325 }
326}
327
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400328//////////////////////////////////////////////////////////////////////////////////
329
Brian Salomonab664fa2017-03-24 16:07:20 +0000330// The objective here is to inset all of the edges by the given distance, and then
331// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
332// we should only be making left-hand turns (for cw polygons, we use the winding
333// parameter to reverse this). We detect this by checking whether the second intersection
334// on an edge is closer to its tail than the first one.
335//
336// We might also have the case that there is no intersection between two neighboring inset edges.
337// In this case, one edge will lie to the right of the other and should be discarded along with
338// its previous intersection (if any).
339//
340// Note: the assumption is that inputPolygon is convex and has no coincident points.
341//
342bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400343 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400344 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000345 if (inputPolygonSize < 3) {
346 return false;
347 }
348
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400349 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400350 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000351 if (0 == winding) {
352 return false;
353 }
354
355 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400356 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
357 int prev = inputPolygonSize - 1;
358 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
359 int next = (curr + 1) % inputPolygonSize;
360 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400361 return false;
362 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400363 // check for convexity just to be sure
Jim Van Verth00673692018-07-23 11:23:39 -0400364 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr],
365 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400366 return false;
367 }
Jim Van Verth00673692018-07-23 11:23:39 -0400368 edgeData[curr].fPrev = &edgeData[prev];
369 edgeData[curr].fNext = &edgeData[next];
370 if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
371 insetDistanceFunc(inputPolygonVerts[curr]),
372 insetDistanceFunc(inputPolygonVerts[next]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400373 winding,
Jim Van Verth00673692018-07-23 11:23:39 -0400374 &edgeData[curr].fInset.fP0, &edgeData[curr].fInset.fP1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400375 return false;
376 }
Jim Van Verth00673692018-07-23 11:23:39 -0400377 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000378 }
379
Jim Van Verth00673692018-07-23 11:23:39 -0400380 OffsetEdge* head = &edgeData[0];
381 OffsetEdge* currEdge = head;
382 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000383 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400384 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400385 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400386 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400387 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400388 if (iterations > inputPolygonSize*inputPolygonSize) {
389 return false;
390 }
391
Brian Salomonab664fa2017-03-24 16:07:20 +0000392 SkScalar s, t;
393 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400394 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000395 &intersection, &s, &t)) {
396 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400397 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000398 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400399 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 --insetVertexCount;
401 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400402 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000403 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400404 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500405 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400406 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000407 1.0e-6f)) {
408 break;
409 } else {
410 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400411 currEdge->fIntersection = intersection;
412 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000413
414 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400415 prevEdge = currEdge;
416 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000417 }
418 } else {
419 // if prev to right side of curr
Jim Van Verth00673692018-07-23 11:23:39 -0400420 int side = winding*compute_side(currEdge->fInset.fP0,
421 currEdge->fInset.fP1,
422 prevEdge->fInset.fP1);
423 if (side < 0 && side == winding*compute_side(currEdge->fInset.fP0,
424 currEdge->fInset.fP1,
425 prevEdge->fInset.fP0)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000426 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400427 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000428 --insetVertexCount;
429 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400430 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000431 } else {
432 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400433 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000434 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400435 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000436 }
437 }
438 }
439
Jim Van Verthda965502017-04-11 15:29:14 -0400440 // store all the valid intersections that aren't nearly coincident
441 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000442 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400443 if (head) {
444 static constexpr SkScalar kCleanupTolerance = 0.01f;
445 if (insetVertexCount >= 0) {
446 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000447 }
Jim Van Verth00673692018-07-23 11:23:39 -0400448 int currIndex = 0;
449 OffsetEdge* currEdge = head;
450 *insetPolygon->push() = currEdge->fIntersection;
451 currEdge = currEdge->fNext;
452 while (currEdge != head) {
453 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
454 (*insetPolygon)[currIndex],
455 kCleanupTolerance)) {
456 *insetPolygon->push() = currEdge->fIntersection;
457 currIndex++;
458 }
459 currEdge = currEdge->fNext;
460 }
461 // make sure the first and last points aren't coincident
462 if (currIndex >= 1 &&
463 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
464 kCleanupTolerance)) {
465 insetPolygon->pop();
466 }
Jim Van Verthda965502017-04-11 15:29:14 -0400467 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000468
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400469 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000470}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400471
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400472///////////////////////////////////////////////////////////////////////////////////////////
473
474// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth061cc212018-07-11 14:09:09 -0400475bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400476 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400477 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
478
479 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400480 if (!SkScalarIsFinite(rCos)) {
481 return false;
482 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400483 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400484 if (!SkScalarIsFinite(rSin)) {
485 return false;
486 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400487 SkScalar theta = SkScalarATan2(rSin, rCos);
488
Jim Van Verth206dbe82018-07-23 11:48:31 -0400489 SkScalar floatSteps = SkScalarAbs(r*theta*kRecipPixelsPerArcSegment);
490 // limit the number of steps to at most max uint16_t (that's all we can index)
491 // knock one value off the top to account for rounding
492 if (floatSteps >= (1 << 16)-1) {
493 return false;
494 }
495 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400496
Jim Van Verth061cc212018-07-11 14:09:09 -0400497 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400498 *rotSin = SkScalarSinCos(dTheta, rotCos);
499 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400500 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400501}
502
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400503///////////////////////////////////////////////////////////////////////////////////////////
504
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400505// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
506static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400507 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400508}
509
510struct Vertex {
511 static bool Left(const Vertex& qv0, const Vertex& qv1) {
512 return left(qv0.fPosition, qv1.fPosition);
513 }
Jim Van Verth00673692018-07-23 11:23:39 -0400514
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400515 // packed to fit into 16 bytes (one cache line)
516 SkPoint fPosition;
517 uint16_t fIndex; // index in unsorted polygon
518 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
519 uint16_t fNextIndex;
520 uint16_t fFlags;
521};
522
523enum VertexFlags {
524 kPrevLeft_VertexFlag = 0x1,
525 kNextLeft_VertexFlag = 0x2,
526};
527
Jim Van Verth00673692018-07-23 11:23:39 -0400528struct ActiveEdge {
529 ActiveEdge(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1)
530 : fSegment({p0, p1})
531 , fIndex0(index0)
532 , fIndex1(index1) {}
533
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400534 // returns true if "this" is above "that"
Jim Van Verth00673692018-07-23 11:23:39 -0400535 bool above(const ActiveEdge& that) const {
536 SkASSERT(this->fSegment.fP0.fX <= that.fSegment.fP0.fX);
537 const SkScalar kTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
538 SkVector u = this->fSegment.fP1 - this->fSegment.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400539 // The idea here is that if the vector between the origins of the two segments (dv)
540 // rotates counterclockwise up to the vector representing the "this" segment (u),
541 // then we know that "this" is above that. If the result is clockwise we say it's below.
Jim Van Verth00673692018-07-23 11:23:39 -0400542 if (this->fIndex0 != that.fIndex0) {
543 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
544 SkScalar cross = dv.cross(u);
545 if (cross > kTolerance) {
546 return true;
547 } else if (cross < -kTolerance) {
548 return false;
549 }
550 } else if (this->fIndex1 == that.fIndex1) {
551 // they're the same edge
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400552 return false;
553 }
Jim Van Verth00673692018-07-23 11:23:39 -0400554 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400555 // lies on dv. So then we try the same for the vector from the tail of "this"
556 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Verth00673692018-07-23 11:23:39 -0400557 SkVector dv = that.fSegment.fP1 - this->fSegment.fP0;
558 SkScalar cross = dv.cross(u);
559 if (cross > kTolerance) {
560 return true;
561 } else if (cross < -kTolerance) {
562 return false;
563 }
564 // If the previous check fails, the two segments are nearly collinear
565 // First check y-coord of first endpoints
566 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
567 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
568 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
569 return true;
570 } else if (this->fSegment.fP0.fY < that.fSegment.fP0.fY) {
571 return false;
572 }
573 // The first endpoints are the same, so check the other endpoint
574 if (this->fSegment.fP1.fX < that.fSegment.fP1.fX) {
575 return (this->fSegment.fP1.fY >= that.fSegment.fP1.fY);
576 } else {
577 return (this->fSegment.fP1.fY > that.fSegment.fP1.fY);
578 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400579 }
580
Jim Van Verth00673692018-07-23 11:23:39 -0400581 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400582 SkPoint intersection;
583 SkScalar s, t;
584 // check first to see if these edges are neighbors in the polygon
585 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
586 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
587 return false;
588 }
589 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
590 }
591
Jim Van Verth00673692018-07-23 11:23:39 -0400592 bool operator==(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400593 return (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1);
594 }
595
Jim Van Verth00673692018-07-23 11:23:39 -0400596 bool operator!=(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400597 return !operator==(that);
598 }
599
Jim Van Verth00673692018-07-23 11:23:39 -0400600 bool lessThan(const ActiveEdge& that) const {
601 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX ||
602 (this->fSegment.fP0.fX == that.fSegment.fP0.fX &&
603 this->fSegment.fP0.fY < that.fSegment.fP0.fY)) {
604 return !that.above(*this);
605 }
606 return this->above(that);
607 }
608
609 bool operator<(const ActiveEdge& that) const {
610 SkASSERT(!this->lessThan(*this));
611 SkASSERT(!that.lessThan(that));
612 SkASSERT(!(this->lessThan(that) && that.lessThan(*this)));
613 return this->lessThan(that);
614 }
615
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400616 OffsetSegment fSegment;
617 int32_t fIndex0; // indices for previous and next vertex
618 int32_t fIndex1;
619};
620
Jim Van Verth00673692018-07-23 11:23:39 -0400621class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400622public:
Jim Van Verth00673692018-07-23 11:23:39 -0400623 void reserve(int count) { }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400624
Jim Van Verth00673692018-07-23 11:23:39 -0400625 bool insert(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1) {
626 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
627 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400628 return false;
629 }
630
Jim Van Verth00673692018-07-23 11:23:39 -0400631 Iterator& curr = result.first;
632 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
633 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400634 }
Jim Van Verth00673692018-07-23 11:23:39 -0400635 Iterator next = std::next(curr);
636 if (next != fEdgeTree.end() && curr->intersect(*next)) {
637 return false;
638 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400639
640 return true;
641 }
642
Jim Van Verth00673692018-07-23 11:23:39 -0400643 bool remove(const ActiveEdge& edge) {
644 auto element = fEdgeTree.find(edge);
645 // this better not happen
646 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400647 return false;
648 }
Jim Van Verth00673692018-07-23 11:23:39 -0400649 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
650 return false;
651 }
652 Iterator next = std::next(element);
653 if (next != fEdgeTree.end() && element->intersect(*next)) {
654 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400655 }
656
Jim Van Verth00673692018-07-23 11:23:39 -0400657 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400658 return true;
659 }
660
661private:
Jim Van Verth00673692018-07-23 11:23:39 -0400662 std::set<ActiveEdge> fEdgeTree;
663 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400664};
665
666// Here we implement a sweep line algorithm to determine whether the provided points
667// represent a simple polygon, i.e., the polygon is non-self-intersecting.
668// We first insert the vertices into a priority queue sorting horizontally from left to right.
669// Then as we pop the vertices from the queue we generate events which indicate that an edge
670// should be added or removed from an edge list. If any intersections are detected in the edge
671// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400672bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400673 if (polygonSize < 3) {
674 return false;
675 }
676
Jim Van Verth00673692018-07-23 11:23:39 -0400677 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400678 for (int i = 0; i < polygonSize; ++i) {
679 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400680 if (!polygon[i].isFinite()) {
681 return false;
682 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400683 newVertex.fPosition = polygon[i];
684 newVertex.fIndex = i;
685 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
686 newVertex.fNextIndex = (i + 1) % polygonSize;
687 newVertex.fFlags = 0;
688 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
689 newVertex.fFlags |= kPrevLeft_VertexFlag;
690 }
691 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
692 newVertex.fFlags |= kNextLeft_VertexFlag;
693 }
694 vertexQueue.insert(newVertex);
695 }
696
697 // pop each vertex from the queue and generate events depending on
698 // where it lies relative to its neighboring edges
Jim Van Verth00673692018-07-23 11:23:39 -0400699 ActiveEdgeList sweepLine;
700 sweepLine.reserve(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400701 while (vertexQueue.count() > 0) {
702 const Vertex& v = vertexQueue.peek();
703
704 // check edge to previous vertex
705 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400706 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400707 if (!sweepLine.remove(edge)) {
708 break;
709 }
710 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400711 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400712 break;
713 }
714 }
715
716 // check edge to next vertex
717 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400718 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400719 if (!sweepLine.remove(edge)) {
720 break;
721 }
722 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400723 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400724 break;
725 }
726 }
727
728 vertexQueue.pop();
729 }
730
731 return (vertexQueue.count() == 0);
732}
733
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400734///////////////////////////////////////////////////////////////////////////////////////////
735
Jim Van Verth00673692018-07-23 11:23:39 -0400736// helper function for SkOffsetSimplePolygon
737static void setup_offset_edge(OffsetEdge* currEdge,
738 const SkPoint& endpoint0, const SkPoint& endpoint1,
739 int startIndex, int endIndex) {
740 currEdge->fInset.fP0 = endpoint0;
741 currEdge->fInset.fP1 = endpoint1;
742 currEdge->init(startIndex, endIndex);
743}
744
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400745bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400746 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
747 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400748 if (inputPolygonSize < 3) {
749 return false;
750 }
751
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400752 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400753 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400754 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400755 return false;
756 }
757
Jim Van Verthbdde4282018-06-14 09:09:18 -0400758 // build normals
759 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
760 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
761 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400762 if (!SkScalarIsFinite(currOffset)) {
763 return false;
764 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400765 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400766 if (!inputPolygonVerts[curr].isFinite()) {
767 return false;
768 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400769 int next = (curr + 1) % inputPolygonSize;
770 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400771 if (!SkScalarIsFinite(nextOffset)) {
772 return false;
773 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400774 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
775 currOffset, nextOffset, winding,
776 &normal0[curr], &normal1[next])) {
777 return false;
778 }
779 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400780 }
781
782 // build initial offset edge list
Jim Van Verth00673692018-07-23 11:23:39 -0400783 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400784 int prevIndex = inputPolygonSize - 1;
785 int currIndex = 0;
786 int nextIndex = 1;
787 while (currIndex < inputPolygonSize) {
788 int side = compute_side(inputPolygonVerts[prevIndex],
789 inputPolygonVerts[currIndex],
790 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400791 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400792 // if reflex point, fill in curve
793 if (side*winding*offset < 0) {
794 SkScalar rotSin, rotCos;
795 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400796 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400797 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
798 &rotSin, &rotCos, &numSteps)) {
799 return false;
800 }
Jim Van Verth00673692018-07-23 11:23:39 -0400801 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400802 for (int i = 0; i < numSteps - 1; ++i) {
803 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
804 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400805 setup_offset_edge(currEdge,
806 inputPolygonVerts[currIndex] + prevNormal,
807 inputPolygonVerts[currIndex] + currNormal,
808 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400809 prevNormal = currNormal;
Jim Van Verth00673692018-07-23 11:23:39 -0400810 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400811 }
Jim Van Verth00673692018-07-23 11:23:39 -0400812 setup_offset_edge(currEdge,
813 inputPolygonVerts[currIndex] + prevNormal,
814 inputPolygonVerts[currIndex] + normal0[currIndex],
815 currIndex, currIndex);
816 ++currEdge;
817
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400818 }
819
820 // Add the edge
Jim Van Verth00673692018-07-23 11:23:39 -0400821 auto edge = edgeData.push_back_n(1);
822 setup_offset_edge(edge,
823 inputPolygonVerts[currIndex] + normal0[currIndex],
824 inputPolygonVerts[nextIndex] + normal1[nextIndex],
825 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400826
827 prevIndex = currIndex;
828 currIndex++;
829 nextIndex = (nextIndex + 1) % inputPolygonSize;
830 }
831
Jim Van Verth00673692018-07-23 11:23:39 -0400832 // build linked list
833 // we have to do this as a post-process step because we might have reallocated
834 // the array when adding fans for reflex verts
835 prevIndex = edgeData.count()-1;
836 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
837 int nextIndex = (currIndex + 1) % edgeData.count();
838 edgeData[currIndex].fPrev = &edgeData[prevIndex];
839 edgeData[currIndex].fNext = &edgeData[nextIndex];
840 }
841
842 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400843 int edgeDataSize = edgeData.count();
Jim Van Verth00673692018-07-23 11:23:39 -0400844 auto head = &edgeData[0];
845 auto currEdge = head;
846 auto prevEdge = currEdge->fPrev;
847 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400848 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400849 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400850 ++iterations;
851 // we should check each edge against each other edge at most once
852 if (iterations > edgeDataSize*edgeDataSize) {
853 return false;
854 }
855
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400856 SkScalar s, t;
857 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400858 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400859 &intersection, &s, &t)) {
860 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400861 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400862 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400863 remove_node(prevEdge, &head);
864 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400865 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400866 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400867 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400868 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400869 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400870 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400871 1.0e-6f)) {
872 break;
873 } else {
874 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400875 currEdge->fIntersection = intersection;
876 currEdge->fTValue = t;
877 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400878
879 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400880 prevEdge = currEdge;
881 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400882 }
883 } else {
884 // If there is no intersection, we want to minimize the distance between
885 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400886 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
887 OffsetEdge* currNextEdge = currEdge->fNext;
888 SkScalar dist0 = compute_crossing_distance(currEdge->fInset,
889 prevPrevEdge->fInset);
890 SkScalar dist1 = compute_crossing_distance(prevEdge->fInset,
891 currNextEdge->fInset);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400892 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400893 remove_node(prevEdge, &head);
894 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400895 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400896 remove_node(currEdge, &head);
897 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400898 }
Jim Van Verth00673692018-07-23 11:23:39 -0400899 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400900 }
901 }
902
903 // store all the valid intersections that aren't nearly coincident
904 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400905 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400906 if (head) {
907 static constexpr SkScalar kCleanupTolerance = 0.01f;
908 if (offsetVertexCount >= 0) {
909 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +0000910 }
Jim Van Verth00673692018-07-23 11:23:39 -0400911 int currIndex = 0;
912 OffsetEdge* currEdge = head;
913 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +0000914 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -0400915 *polygonIndices->push() = currEdge->fIndex;
916 }
917 currEdge = currEdge->fNext;
918 while (currEdge != head) {
919 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
920 (*offsetPolygon)[currIndex],
921 kCleanupTolerance)) {
922 *offsetPolygon->push() = currEdge->fIntersection;
923 if (polygonIndices) {
924 *polygonIndices->push() = currEdge->fIndex;
925 }
926 currIndex++;
927 }
928 currEdge = currEdge->fNext;
929 }
930 // make sure the first and last points aren't coincident
931 if (currIndex >= 1 &&
932 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
933 kCleanupTolerance)) {
934 offsetPolygon->pop();
935 if (polygonIndices) {
936 polygonIndices->pop();
937 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400938 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400939 }
940
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400941 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400942 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400943
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400944 return (winding*offsetWinding > 0 &&
945 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400946}
947
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400948//////////////////////////////////////////////////////////////////////////////////////////
949
950struct TriangulationVertex {
951 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
952
953 enum class VertexType { kConvex, kReflex };
954
955 SkPoint fPosition;
956 VertexType fVertexType;
957 uint16_t fIndex;
958 uint16_t fPrevIndex;
959 uint16_t fNextIndex;
960};
961
962// test to see if point p is in triangle p0p1p2.
963// for now assuming strictly inside -- if on the edge it's outside
964static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
965 const SkPoint& p) {
966 SkVector v0 = p1 - p0;
967 SkVector v1 = p2 - p1;
968 SkScalar n = v0.cross(v1);
969
970 SkVector w0 = p - p0;
971 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
972 return false;
973 }
974
975 SkVector w1 = p - p1;
976 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
977 return false;
978 }
979
980 SkVector v2 = p0 - p2;
981 SkVector w2 = p - p2;
982 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
983 return false;
984 }
985
986 return true;
987}
988
989// Data structure to track reflex vertices and check whether any are inside a given triangle
990class ReflexHash {
991public:
992 void add(TriangulationVertex* v) {
993 fReflexList.addToTail(v);
994 }
995
996 void remove(TriangulationVertex* v) {
997 fReflexList.remove(v);
998 }
999
Jim Van Verth061cc212018-07-11 14:09:09 -04001000 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1001 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001002 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1003 reflexIter != fReflexList.end(); ++reflexIter) {
1004 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001005 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1006 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001007 return true;
1008 }
1009 }
1010
1011 return false;
1012 }
1013
1014private:
1015 // TODO: switch to an actual spatial hash
1016 SkTInternalLList<TriangulationVertex> fReflexList;
1017};
1018
1019// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1020static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1021 int winding, ReflexHash* reflexHash,
1022 SkTInternalLList<TriangulationVertex>* convexList) {
1023 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1024 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1025 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001026 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001027 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1028 reflexHash->remove(p);
1029 p->fPrev = p->fNext = nullptr;
1030 convexList->addToTail(p);
1031 }
1032 }
1033}
1034
1035bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1036 SkTDArray<uint16_t>* triangleIndices) {
1037 if (polygonSize < 3) {
1038 return false;
1039 }
1040 // need to be able to represent all the vertices in the 16-bit indices
1041 if (polygonSize >= (1 << 16)) {
1042 return false;
1043 }
1044
1045 // get winding direction
1046 // TODO: we do this for all the polygon routines -- might be better to have the client
1047 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001048 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001049 if (0 == winding) {
1050 return false;
1051 }
1052
1053 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1054 // TODO: possibly sort the convexList in some way to get better triangles
1055 SkTInternalLList<TriangulationVertex> convexList;
1056 ReflexHash reflexHash;
1057 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1058 int prevIndex = polygonSize - 1;
1059 int currIndex = 0;
1060 int nextIndex = 1;
1061 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1062 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1063 for (int i = 0; i < polygonSize; ++i) {
1064 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1065 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1066 triangulationVertices[currIndex].fIndex = currIndex;
1067 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1068 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001069 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001070 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1071 convexList.addToTail(&triangulationVertices[currIndex]);
1072 } else {
1073 // We treat near collinear vertices as reflex
1074 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1075 reflexHash.add(&triangulationVertices[currIndex]);
1076 }
1077
1078 prevIndex = currIndex;
1079 currIndex = nextIndex;
1080 nextIndex = (currIndex + 1) % polygonSize;
1081 v0 = v1;
1082 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1083 }
1084
1085 // The general concept: We are trying to find three neighboring vertices where
1086 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1087 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1088 // we have triangulated the entire polygon.
1089 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1090 // noting that only convex vertices can be potential ears, and we only need to check whether
1091 // any reflex vertices lie inside the ear.
1092 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1093 int vertexCount = polygonSize;
1094 while (vertexCount > 3) {
1095 bool success = false;
1096 TriangulationVertex* earVertex = nullptr;
1097 TriangulationVertex* p0 = nullptr;
1098 TriangulationVertex* p2 = nullptr;
1099 // find a convex vertex to clip
1100 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1101 convexIter != convexList.end(); ++convexIter) {
1102 earVertex = *convexIter;
1103 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1104
1105 p0 = &triangulationVertices[earVertex->fPrevIndex];
1106 p2 = &triangulationVertices[earVertex->fNextIndex];
1107
1108 // see if any reflex vertices are inside the ear
1109 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001110 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001111 if (failed) {
1112 continue;
1113 }
1114
1115 // found one we can clip
1116 success = true;
1117 break;
1118 }
1119 // If we can't find any ears to clip, this probably isn't a simple polygon
1120 if (!success) {
1121 return false;
1122 }
1123
1124 // add indices
1125 auto indices = triangleIndices->append(3);
1126 indices[0] = indexMap[p0->fIndex];
1127 indices[1] = indexMap[earVertex->fIndex];
1128 indices[2] = indexMap[p2->fIndex];
1129
1130 // clip the ear
1131 convexList.remove(earVertex);
1132 --vertexCount;
1133
1134 // reclassify reflex verts
1135 p0->fNextIndex = earVertex->fNextIndex;
1136 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1137
1138 p2->fPrevIndex = earVertex->fPrevIndex;
1139 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1140 }
1141
1142 // output indices
1143 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1144 vertexIter != convexList.end(); ++vertexIter) {
1145 TriangulationVertex* vertex = *vertexIter;
1146 *triangleIndices->push() = indexMap[vertex->fIndex];
1147 }
1148
1149 return true;
1150}