blob: 0d1b71c76bcf5bef2995a5e851df4fc688349346 [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
489 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
490
Jim Van Verth061cc212018-07-11 14:09:09 -0400491 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400492 *rotSin = SkScalarSinCos(dTheta, rotCos);
493 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400494 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400495}
496
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400497///////////////////////////////////////////////////////////////////////////////////////////
498
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400499// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
500static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400501 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400502}
503
504struct Vertex {
505 static bool Left(const Vertex& qv0, const Vertex& qv1) {
506 return left(qv0.fPosition, qv1.fPosition);
507 }
Jim Van Verth00673692018-07-23 11:23:39 -0400508
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400509 // packed to fit into 16 bytes (one cache line)
510 SkPoint fPosition;
511 uint16_t fIndex; // index in unsorted polygon
512 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
513 uint16_t fNextIndex;
514 uint16_t fFlags;
515};
516
517enum VertexFlags {
518 kPrevLeft_VertexFlag = 0x1,
519 kNextLeft_VertexFlag = 0x2,
520};
521
Jim Van Verth00673692018-07-23 11:23:39 -0400522struct ActiveEdge {
523 ActiveEdge(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1)
524 : fSegment({p0, p1})
525 , fIndex0(index0)
526 , fIndex1(index1) {}
527
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400528 // returns true if "this" is above "that"
Jim Van Verth00673692018-07-23 11:23:39 -0400529 bool above(const ActiveEdge& that) const {
530 SkASSERT(this->fSegment.fP0.fX <= that.fSegment.fP0.fX);
531 const SkScalar kTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
532 SkVector u = this->fSegment.fP1 - this->fSegment.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400533 // The idea here is that if the vector between the origins of the two segments (dv)
534 // rotates counterclockwise up to the vector representing the "this" segment (u),
535 // 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 -0400536 if (this->fIndex0 != that.fIndex0) {
537 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
538 SkScalar cross = dv.cross(u);
539 if (cross > kTolerance) {
540 return true;
541 } else if (cross < -kTolerance) {
542 return false;
543 }
544 } else if (this->fIndex1 == that.fIndex1) {
545 // they're the same edge
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400546 return false;
547 }
Jim Van Verth00673692018-07-23 11:23:39 -0400548 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400549 // lies on dv. So then we try the same for the vector from the tail of "this"
550 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Verth00673692018-07-23 11:23:39 -0400551 SkVector dv = that.fSegment.fP1 - this->fSegment.fP0;
552 SkScalar cross = dv.cross(u);
553 if (cross > kTolerance) {
554 return true;
555 } else if (cross < -kTolerance) {
556 return false;
557 }
558 // If the previous check fails, the two segments are nearly collinear
559 // First check y-coord of first endpoints
560 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
561 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
562 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
563 return true;
564 } else if (this->fSegment.fP0.fY < that.fSegment.fP0.fY) {
565 return false;
566 }
567 // The first endpoints are the same, so check the other endpoint
568 if (this->fSegment.fP1.fX < that.fSegment.fP1.fX) {
569 return (this->fSegment.fP1.fY >= that.fSegment.fP1.fY);
570 } else {
571 return (this->fSegment.fP1.fY > that.fSegment.fP1.fY);
572 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400573 }
574
Jim Van Verth00673692018-07-23 11:23:39 -0400575 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400576 SkPoint intersection;
577 SkScalar s, t;
578 // check first to see if these edges are neighbors in the polygon
579 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
580 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
581 return false;
582 }
583 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
584 }
585
Jim Van Verth00673692018-07-23 11:23:39 -0400586 bool operator==(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400587 return (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1);
588 }
589
Jim Van Verth00673692018-07-23 11:23:39 -0400590 bool operator!=(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400591 return !operator==(that);
592 }
593
Jim Van Verth00673692018-07-23 11:23:39 -0400594 bool lessThan(const ActiveEdge& that) const {
595 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX ||
596 (this->fSegment.fP0.fX == that.fSegment.fP0.fX &&
597 this->fSegment.fP0.fY < that.fSegment.fP0.fY)) {
598 return !that.above(*this);
599 }
600 return this->above(that);
601 }
602
603 bool operator<(const ActiveEdge& that) const {
604 SkASSERT(!this->lessThan(*this));
605 SkASSERT(!that.lessThan(that));
606 SkASSERT(!(this->lessThan(that) && that.lessThan(*this)));
607 return this->lessThan(that);
608 }
609
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400610 OffsetSegment fSegment;
611 int32_t fIndex0; // indices for previous and next vertex
612 int32_t fIndex1;
613};
614
Jim Van Verth00673692018-07-23 11:23:39 -0400615class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400616public:
Jim Van Verth00673692018-07-23 11:23:39 -0400617 void reserve(int count) { }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400618
Jim Van Verth00673692018-07-23 11:23:39 -0400619 bool insert(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1) {
620 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
621 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400622 return false;
623 }
624
Jim Van Verth00673692018-07-23 11:23:39 -0400625 Iterator& curr = result.first;
626 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
627 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400628 }
Jim Van Verth00673692018-07-23 11:23:39 -0400629 Iterator next = std::next(curr);
630 if (next != fEdgeTree.end() && curr->intersect(*next)) {
631 return false;
632 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400633
634 return true;
635 }
636
Jim Van Verth00673692018-07-23 11:23:39 -0400637 bool remove(const ActiveEdge& edge) {
638 auto element = fEdgeTree.find(edge);
639 // this better not happen
640 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400641 return false;
642 }
Jim Van Verth00673692018-07-23 11:23:39 -0400643 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
644 return false;
645 }
646 Iterator next = std::next(element);
647 if (next != fEdgeTree.end() && element->intersect(*next)) {
648 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400649 }
650
Jim Van Verth00673692018-07-23 11:23:39 -0400651 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400652 return true;
653 }
654
655private:
Jim Van Verth00673692018-07-23 11:23:39 -0400656 std::set<ActiveEdge> fEdgeTree;
657 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400658};
659
660// Here we implement a sweep line algorithm to determine whether the provided points
661// represent a simple polygon, i.e., the polygon is non-self-intersecting.
662// We first insert the vertices into a priority queue sorting horizontally from left to right.
663// Then as we pop the vertices from the queue we generate events which indicate that an edge
664// should be added or removed from an edge list. If any intersections are detected in the edge
665// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400666bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400667 if (polygonSize < 3) {
668 return false;
669 }
670
Jim Van Verth00673692018-07-23 11:23:39 -0400671 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400672 for (int i = 0; i < polygonSize; ++i) {
673 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400674 if (!polygon[i].isFinite()) {
675 return false;
676 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400677 newVertex.fPosition = polygon[i];
678 newVertex.fIndex = i;
679 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
680 newVertex.fNextIndex = (i + 1) % polygonSize;
681 newVertex.fFlags = 0;
682 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
683 newVertex.fFlags |= kPrevLeft_VertexFlag;
684 }
685 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
686 newVertex.fFlags |= kNextLeft_VertexFlag;
687 }
688 vertexQueue.insert(newVertex);
689 }
690
691 // pop each vertex from the queue and generate events depending on
692 // where it lies relative to its neighboring edges
Jim Van Verth00673692018-07-23 11:23:39 -0400693 ActiveEdgeList sweepLine;
694 sweepLine.reserve(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400695 while (vertexQueue.count() > 0) {
696 const Vertex& v = vertexQueue.peek();
697
698 // check edge to previous vertex
699 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400700 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400701 if (!sweepLine.remove(edge)) {
702 break;
703 }
704 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400705 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400706 break;
707 }
708 }
709
710 // check edge to next vertex
711 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400712 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400713 if (!sweepLine.remove(edge)) {
714 break;
715 }
716 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400717 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400718 break;
719 }
720 }
721
722 vertexQueue.pop();
723 }
724
725 return (vertexQueue.count() == 0);
726}
727
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400728///////////////////////////////////////////////////////////////////////////////////////////
729
Jim Van Verth00673692018-07-23 11:23:39 -0400730// helper function for SkOffsetSimplePolygon
731static void setup_offset_edge(OffsetEdge* currEdge,
732 const SkPoint& endpoint0, const SkPoint& endpoint1,
733 int startIndex, int endIndex) {
734 currEdge->fInset.fP0 = endpoint0;
735 currEdge->fInset.fP1 = endpoint1;
736 currEdge->init(startIndex, endIndex);
737}
738
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400739bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400740 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
741 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400742 if (inputPolygonSize < 3) {
743 return false;
744 }
745
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400746 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400747 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400748 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400749 return false;
750 }
751
Jim Van Verthbdde4282018-06-14 09:09:18 -0400752 // build normals
753 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
754 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
755 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400756 if (!SkScalarIsFinite(currOffset)) {
757 return false;
758 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400759 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400760 if (!inputPolygonVerts[curr].isFinite()) {
761 return false;
762 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400763 int next = (curr + 1) % inputPolygonSize;
764 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400765 if (!SkScalarIsFinite(nextOffset)) {
766 return false;
767 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400768 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
769 currOffset, nextOffset, winding,
770 &normal0[curr], &normal1[next])) {
771 return false;
772 }
773 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400774 }
775
776 // build initial offset edge list
Jim Van Verth00673692018-07-23 11:23:39 -0400777 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400778 int prevIndex = inputPolygonSize - 1;
779 int currIndex = 0;
780 int nextIndex = 1;
781 while (currIndex < inputPolygonSize) {
782 int side = compute_side(inputPolygonVerts[prevIndex],
783 inputPolygonVerts[currIndex],
784 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400785 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400786 // if reflex point, fill in curve
787 if (side*winding*offset < 0) {
788 SkScalar rotSin, rotCos;
789 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400790 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400791 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
792 &rotSin, &rotCos, &numSteps)) {
793 return false;
794 }
Jim Van Verth00673692018-07-23 11:23:39 -0400795 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400796 for (int i = 0; i < numSteps - 1; ++i) {
797 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
798 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400799 setup_offset_edge(currEdge,
800 inputPolygonVerts[currIndex] + prevNormal,
801 inputPolygonVerts[currIndex] + currNormal,
802 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400803 prevNormal = currNormal;
Jim Van Verth00673692018-07-23 11:23:39 -0400804 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400805 }
Jim Van Verth00673692018-07-23 11:23:39 -0400806 setup_offset_edge(currEdge,
807 inputPolygonVerts[currIndex] + prevNormal,
808 inputPolygonVerts[currIndex] + normal0[currIndex],
809 currIndex, currIndex);
810 ++currEdge;
811
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400812 }
813
814 // Add the edge
Jim Van Verth00673692018-07-23 11:23:39 -0400815 auto edge = edgeData.push_back_n(1);
816 setup_offset_edge(edge,
817 inputPolygonVerts[currIndex] + normal0[currIndex],
818 inputPolygonVerts[nextIndex] + normal1[nextIndex],
819 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400820
821 prevIndex = currIndex;
822 currIndex++;
823 nextIndex = (nextIndex + 1) % inputPolygonSize;
824 }
825
Jim Van Verth00673692018-07-23 11:23:39 -0400826 // build linked list
827 // we have to do this as a post-process step because we might have reallocated
828 // the array when adding fans for reflex verts
829 prevIndex = edgeData.count()-1;
830 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
831 int nextIndex = (currIndex + 1) % edgeData.count();
832 edgeData[currIndex].fPrev = &edgeData[prevIndex];
833 edgeData[currIndex].fNext = &edgeData[nextIndex];
834 }
835
836 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400837 int edgeDataSize = edgeData.count();
Jim Van Verth00673692018-07-23 11:23:39 -0400838 auto head = &edgeData[0];
839 auto currEdge = head;
840 auto prevEdge = currEdge->fPrev;
841 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400842 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400843 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400844 ++iterations;
845 // we should check each edge against each other edge at most once
846 if (iterations > edgeDataSize*edgeDataSize) {
847 return false;
848 }
849
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400850 SkScalar s, t;
851 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400852 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400853 &intersection, &s, &t)) {
854 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400855 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400856 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400857 remove_node(prevEdge, &head);
858 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400859 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400860 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400861 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400862 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400863 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400864 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400865 1.0e-6f)) {
866 break;
867 } else {
868 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400869 currEdge->fIntersection = intersection;
870 currEdge->fTValue = t;
871 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400872
873 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400874 prevEdge = currEdge;
875 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400876 }
877 } else {
878 // If there is no intersection, we want to minimize the distance between
879 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400880 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
881 OffsetEdge* currNextEdge = currEdge->fNext;
882 SkScalar dist0 = compute_crossing_distance(currEdge->fInset,
883 prevPrevEdge->fInset);
884 SkScalar dist1 = compute_crossing_distance(prevEdge->fInset,
885 currNextEdge->fInset);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400886 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400887 remove_node(prevEdge, &head);
888 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400889 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400890 remove_node(currEdge, &head);
891 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400892 }
Jim Van Verth00673692018-07-23 11:23:39 -0400893 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400894 }
895 }
896
897 // store all the valid intersections that aren't nearly coincident
898 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400899 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400900 if (head) {
901 static constexpr SkScalar kCleanupTolerance = 0.01f;
902 if (offsetVertexCount >= 0) {
903 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +0000904 }
Jim Van Verth00673692018-07-23 11:23:39 -0400905 int currIndex = 0;
906 OffsetEdge* currEdge = head;
907 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +0000908 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -0400909 *polygonIndices->push() = currEdge->fIndex;
910 }
911 currEdge = currEdge->fNext;
912 while (currEdge != head) {
913 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
914 (*offsetPolygon)[currIndex],
915 kCleanupTolerance)) {
916 *offsetPolygon->push() = currEdge->fIntersection;
917 if (polygonIndices) {
918 *polygonIndices->push() = currEdge->fIndex;
919 }
920 currIndex++;
921 }
922 currEdge = currEdge->fNext;
923 }
924 // make sure the first and last points aren't coincident
925 if (currIndex >= 1 &&
926 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
927 kCleanupTolerance)) {
928 offsetPolygon->pop();
929 if (polygonIndices) {
930 polygonIndices->pop();
931 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400932 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400933 }
934
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400935 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400936 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400937
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400938 return (winding*offsetWinding > 0 &&
939 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400940}
941
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400942//////////////////////////////////////////////////////////////////////////////////////////
943
944struct TriangulationVertex {
945 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
946
947 enum class VertexType { kConvex, kReflex };
948
949 SkPoint fPosition;
950 VertexType fVertexType;
951 uint16_t fIndex;
952 uint16_t fPrevIndex;
953 uint16_t fNextIndex;
954};
955
956// test to see if point p is in triangle p0p1p2.
957// for now assuming strictly inside -- if on the edge it's outside
958static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
959 const SkPoint& p) {
960 SkVector v0 = p1 - p0;
961 SkVector v1 = p2 - p1;
962 SkScalar n = v0.cross(v1);
963
964 SkVector w0 = p - p0;
965 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
966 return false;
967 }
968
969 SkVector w1 = p - p1;
970 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
971 return false;
972 }
973
974 SkVector v2 = p0 - p2;
975 SkVector w2 = p - p2;
976 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
977 return false;
978 }
979
980 return true;
981}
982
983// Data structure to track reflex vertices and check whether any are inside a given triangle
984class ReflexHash {
985public:
986 void add(TriangulationVertex* v) {
987 fReflexList.addToTail(v);
988 }
989
990 void remove(TriangulationVertex* v) {
991 fReflexList.remove(v);
992 }
993
Jim Van Verth061cc212018-07-11 14:09:09 -0400994 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
995 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400996 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
997 reflexIter != fReflexList.end(); ++reflexIter) {
998 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -0400999 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1000 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001001 return true;
1002 }
1003 }
1004
1005 return false;
1006 }
1007
1008private:
1009 // TODO: switch to an actual spatial hash
1010 SkTInternalLList<TriangulationVertex> fReflexList;
1011};
1012
1013// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1014static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1015 int winding, ReflexHash* reflexHash,
1016 SkTInternalLList<TriangulationVertex>* convexList) {
1017 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1018 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1019 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001020 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001021 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1022 reflexHash->remove(p);
1023 p->fPrev = p->fNext = nullptr;
1024 convexList->addToTail(p);
1025 }
1026 }
1027}
1028
1029bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1030 SkTDArray<uint16_t>* triangleIndices) {
1031 if (polygonSize < 3) {
1032 return false;
1033 }
1034 // need to be able to represent all the vertices in the 16-bit indices
1035 if (polygonSize >= (1 << 16)) {
1036 return false;
1037 }
1038
1039 // get winding direction
1040 // TODO: we do this for all the polygon routines -- might be better to have the client
1041 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001042 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001043 if (0 == winding) {
1044 return false;
1045 }
1046
1047 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1048 // TODO: possibly sort the convexList in some way to get better triangles
1049 SkTInternalLList<TriangulationVertex> convexList;
1050 ReflexHash reflexHash;
1051 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1052 int prevIndex = polygonSize - 1;
1053 int currIndex = 0;
1054 int nextIndex = 1;
1055 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1056 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1057 for (int i = 0; i < polygonSize; ++i) {
1058 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1059 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1060 triangulationVertices[currIndex].fIndex = currIndex;
1061 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1062 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001063 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001064 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1065 convexList.addToTail(&triangulationVertices[currIndex]);
1066 } else {
1067 // We treat near collinear vertices as reflex
1068 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1069 reflexHash.add(&triangulationVertices[currIndex]);
1070 }
1071
1072 prevIndex = currIndex;
1073 currIndex = nextIndex;
1074 nextIndex = (currIndex + 1) % polygonSize;
1075 v0 = v1;
1076 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1077 }
1078
1079 // The general concept: We are trying to find three neighboring vertices where
1080 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1081 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1082 // we have triangulated the entire polygon.
1083 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1084 // noting that only convex vertices can be potential ears, and we only need to check whether
1085 // any reflex vertices lie inside the ear.
1086 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1087 int vertexCount = polygonSize;
1088 while (vertexCount > 3) {
1089 bool success = false;
1090 TriangulationVertex* earVertex = nullptr;
1091 TriangulationVertex* p0 = nullptr;
1092 TriangulationVertex* p2 = nullptr;
1093 // find a convex vertex to clip
1094 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1095 convexIter != convexList.end(); ++convexIter) {
1096 earVertex = *convexIter;
1097 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1098
1099 p0 = &triangulationVertices[earVertex->fPrevIndex];
1100 p2 = &triangulationVertices[earVertex->fNextIndex];
1101
1102 // see if any reflex vertices are inside the ear
1103 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001104 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001105 if (failed) {
1106 continue;
1107 }
1108
1109 // found one we can clip
1110 success = true;
1111 break;
1112 }
1113 // If we can't find any ears to clip, this probably isn't a simple polygon
1114 if (!success) {
1115 return false;
1116 }
1117
1118 // add indices
1119 auto indices = triangleIndices->append(3);
1120 indices[0] = indexMap[p0->fIndex];
1121 indices[1] = indexMap[earVertex->fIndex];
1122 indices[2] = indexMap[p2->fIndex];
1123
1124 // clip the ear
1125 convexList.remove(earVertex);
1126 --vertexCount;
1127
1128 // reclassify reflex verts
1129 p0->fNextIndex = earVertex->fNextIndex;
1130 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1131
1132 p2->fPrevIndex = earVertex->fPrevIndex;
1133 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1134 }
1135
1136 // output indices
1137 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1138 vertexIter != convexList.end(); ++vertexIter) {
1139 TriangulationVertex* vertex = *vertexIter;
1140 *triangleIndices->push() = indexMap[vertex->fIndex];
1141 }
1142
1143 return true;
1144}