blob: e80e12f4461a34032f42aa85ba78e119453cda87 [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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -0400374 &edgeData[curr].fInset.fP0, &edgeData[curr].fInset.fP1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400375 return false;
376 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400377 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000378 }
379
Jim Van Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -0400397 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000398 // no point in considering this one again
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400399 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 --insetVertexCount;
401 // go back one segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400402 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000403 // we've already considered this intersection, we're done
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400404 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500405 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400406 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000407 1.0e-6f)) {
408 break;
409 } else {
410 // add intersection
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400411 currEdge->fIntersection = intersection;
412 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000413
414 // go to next segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -0400427 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000428 --insetVertexCount;
429 // go back one segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400430 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000431 } else {
432 // move to next segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400433 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000434 --insetVertexCount;
Jim Van Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -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 Verth8bb0db32018-07-17 14:13:47 -0400529 bool above(const ActiveEdge& that, SkScalar tolerance = SK_ScalarNearlyZero) const {
Jim Van Verth061cc212018-07-11 14:09:09 -0400530 SkASSERT(this->fSegment.fP0.fX < that.fSegment.fP0.fX ||
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400531 SkScalarNearlyEqual(this->fSegment.fP0.fX, that.fSegment.fP0.fX, tolerance));
532 // The idea here is that if the vector between the origins of the two segments (dv)
533 // rotates counterclockwise up to the vector representing the "this" segment (u),
534 // then we know that "this" is above that. If the result is clockwise we say it's below.
535 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
536 SkVector u = this->fSegment.fP1 - this->fSegment.fP0;
537 SkScalar cross = dv.cross(u);
538 if (cross > tolerance) {
539 return true;
540 } else if (cross < -tolerance) {
541 return false;
542 }
543 // If the result is 0 then either the two origins are equal or the origin of "that"
544 // lies on dv. So then we try the same for the vector from the tail of "this"
545 // to the head of "that". Again, ccw means "this" is above "that".
546 dv = that.fSegment.fP1 - this->fSegment.fP0;
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400547 if (dv.cross(u) > tolerance) {
548 return true;
549 }
550 // If the previous check fails, the two segments are nearly collinear
551 // If this segment starts to the left of that one,
552 // just need to check y-coord of 1st endpoint
553 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
554 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
555 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
556 return true;
557 }
558 // Otherwise the first endpoints are effectively the same, so check the other endpoint
559 if (this->fSegment.fP1.fX < that.fSegment.fP1.fX) {
560 return (this->fSegment.fP1.fY >= that.fSegment.fP1.fY);
561 } else {
562 return (this->fSegment.fP1.fY > that.fSegment.fP1.fY);
563 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400564 }
565
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400566 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400567 SkPoint intersection;
568 SkScalar s, t;
569 // check first to see if these edges are neighbors in the polygon
570 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
571 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
572 return false;
573 }
574 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
575 }
576
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400577 bool operator==(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400578 return (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1);
579 }
580
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400581 bool operator!=(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400582 return !operator==(that);
583 }
584
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400585 bool operator<(const ActiveEdge& that) const {
586 // this may not be necessary
587 if (this->fIndex0 == that.fIndex0 && this->fIndex1 == that.fIndex1) {
588 return false;
589 }
590 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX) {
591 return !that.above(*this);
592 }
593 return this->above(that);
594 }
595
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400596 OffsetSegment fSegment;
597 int32_t fIndex0; // indices for previous and next vertex
598 int32_t fIndex1;
599};
600
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400601class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400602public:
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400603 void reserve(int count) { }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400604
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400605 bool insert(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1) {
606 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
607 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400608 return false;
609 }
610
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400611 Iterator& curr = result.first;
612 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
613 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400614 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400615 Iterator next = std::next(curr);
616 if (next != fEdgeTree.end() && curr->intersect(*next)) {
617 return false;
618 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400619
620 return true;
621 }
622
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400623 bool remove(const ActiveEdge& edge) {
624 auto element = fEdgeTree.find(edge);
625 // this better not happen
626 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400627 return false;
628 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400629 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
630 return false;
631 }
632 Iterator next = std::next(element);
633 if (next != fEdgeTree.end() && element->intersect(*next)) {
634 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400635 }
636
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400637 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400638 return true;
639 }
640
641private:
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400642 std::set<ActiveEdge> fEdgeTree;
643 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400644};
645
646// Here we implement a sweep line algorithm to determine whether the provided points
647// represent a simple polygon, i.e., the polygon is non-self-intersecting.
648// We first insert the vertices into a priority queue sorting horizontally from left to right.
649// Then as we pop the vertices from the queue we generate events which indicate that an edge
650// should be added or removed from an edge list. If any intersections are detected in the edge
651// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400652bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400653 if (polygonSize < 3) {
654 return false;
655 }
656
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400657 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400658 for (int i = 0; i < polygonSize; ++i) {
659 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400660 if (!polygon[i].isFinite()) {
661 return false;
662 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400663 newVertex.fPosition = polygon[i];
664 newVertex.fIndex = i;
665 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
666 newVertex.fNextIndex = (i + 1) % polygonSize;
667 newVertex.fFlags = 0;
668 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
669 newVertex.fFlags |= kPrevLeft_VertexFlag;
670 }
671 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
672 newVertex.fFlags |= kNextLeft_VertexFlag;
673 }
674 vertexQueue.insert(newVertex);
675 }
676
677 // pop each vertex from the queue and generate events depending on
678 // where it lies relative to its neighboring edges
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400679 ActiveEdgeList sweepLine;
680 sweepLine.reserve(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400681 while (vertexQueue.count() > 0) {
682 const Vertex& v = vertexQueue.peek();
683
684 // check edge to previous vertex
685 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400686 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400687 if (!sweepLine.remove(edge)) {
688 break;
689 }
690 } else {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400691 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400692 break;
693 }
694 }
695
696 // check edge to next vertex
697 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400698 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400699 if (!sweepLine.remove(edge)) {
700 break;
701 }
702 } else {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400703 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400704 break;
705 }
706 }
707
708 vertexQueue.pop();
709 }
710
711 return (vertexQueue.count() == 0);
712}
713
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400714///////////////////////////////////////////////////////////////////////////////////////////
715
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400716// helper function for SkOffsetSimplePolygon
717static void setup_offset_edge(OffsetEdge* currEdge,
718 const SkPoint& endpoint0, const SkPoint& endpoint1,
719 int startIndex, int endIndex) {
720 currEdge->fInset.fP0 = endpoint0;
721 currEdge->fInset.fP1 = endpoint1;
722 currEdge->init(startIndex, endIndex);
723}
724
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400725bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400726 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
727 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400728 if (inputPolygonSize < 3) {
729 return false;
730 }
731
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400732 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400733 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400734 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400735 return false;
736 }
737
Jim Van Verthbdde4282018-06-14 09:09:18 -0400738 // build normals
739 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
740 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
741 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400742 if (!SkScalarIsFinite(currOffset)) {
743 return false;
744 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400745 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400746 if (!inputPolygonVerts[curr].isFinite()) {
747 return false;
748 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400749 int next = (curr + 1) % inputPolygonSize;
750 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400751 if (!SkScalarIsFinite(nextOffset)) {
752 return false;
753 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400754 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
755 currOffset, nextOffset, winding,
756 &normal0[curr], &normal1[next])) {
757 return false;
758 }
759 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400760 }
761
762 // build initial offset edge list
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400763 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400764 int prevIndex = inputPolygonSize - 1;
765 int currIndex = 0;
766 int nextIndex = 1;
767 while (currIndex < inputPolygonSize) {
768 int side = compute_side(inputPolygonVerts[prevIndex],
769 inputPolygonVerts[currIndex],
770 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400771 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400772 // if reflex point, fill in curve
773 if (side*winding*offset < 0) {
774 SkScalar rotSin, rotCos;
775 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400776 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400777 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
778 &rotSin, &rotCos, &numSteps)) {
779 return false;
780 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400781 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400782 for (int i = 0; i < numSteps - 1; ++i) {
783 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
784 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400785 setup_offset_edge(currEdge,
786 inputPolygonVerts[currIndex] + prevNormal,
787 inputPolygonVerts[currIndex] + currNormal,
788 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400789 prevNormal = currNormal;
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400790 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400791 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400792 setup_offset_edge(currEdge,
793 inputPolygonVerts[currIndex] + prevNormal,
794 inputPolygonVerts[currIndex] + normal0[currIndex],
795 currIndex, currIndex);
796 ++currEdge;
797
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400798 }
799
800 // Add the edge
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400801 auto edge = edgeData.push_back_n(1);
802 setup_offset_edge(edge,
803 inputPolygonVerts[currIndex] + normal0[currIndex],
804 inputPolygonVerts[nextIndex] + normal1[nextIndex],
805 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400806
807 prevIndex = currIndex;
808 currIndex++;
809 nextIndex = (nextIndex + 1) % inputPolygonSize;
810 }
811
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400812 // build linked list
813 // we have to do this as a post-process step because we might have reallocated
814 // the array when adding fans for reflex verts
815 prevIndex = edgeData.count()-1;
816 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
817 int nextIndex = (currIndex + 1) % edgeData.count();
818 edgeData[currIndex].fPrev = &edgeData[prevIndex];
819 edgeData[currIndex].fNext = &edgeData[nextIndex];
820 }
821
822 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400823 int edgeDataSize = edgeData.count();
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400824 auto head = &edgeData[0];
825 auto currEdge = head;
826 auto prevEdge = currEdge->fPrev;
827 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400828 int iterations = 0;
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400829 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400830 ++iterations;
831 // we should check each edge against each other edge at most once
832 if (iterations > edgeDataSize*edgeDataSize) {
833 return false;
834 }
835
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400836 SkScalar s, t;
837 SkPoint intersection;
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400838 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400839 &intersection, &s, &t)) {
840 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400841 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400842 // no point in considering this one again
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400843 remove_node(prevEdge, &head);
844 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400845 // go back one segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400846 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400847 // we've already considered this intersection, we're done
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400848 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400849 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400850 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400851 1.0e-6f)) {
852 break;
853 } else {
854 // add intersection
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400855 currEdge->fIntersection = intersection;
856 currEdge->fTValue = t;
857 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400858
859 // go to next segment
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400860 prevEdge = currEdge;
861 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400862 }
863 } else {
864 // If there is no intersection, we want to minimize the distance between
865 // the point where the segment lines cross and the segments themselves.
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400866 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
867 OffsetEdge* currNextEdge = currEdge->fNext;
868 SkScalar dist0 = compute_crossing_distance(currEdge->fInset,
869 prevPrevEdge->fInset);
870 SkScalar dist1 = compute_crossing_distance(prevEdge->fInset,
871 currNextEdge->fInset);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400872 if (dist0 < dist1) {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400873 remove_node(prevEdge, &head);
874 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400875 } else {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400876 remove_node(currEdge, &head);
877 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400878 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400879 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400880 }
881 }
882
883 // store all the valid intersections that aren't nearly coincident
884 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400885 offsetPolygon->reset();
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400886 if (head) {
887 static constexpr SkScalar kCleanupTolerance = 0.01f;
888 if (offsetVertexCount >= 0) {
889 offsetPolygon->setReserve(offsetVertexCount);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400890 }
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400891 int currIndex = 0;
892 OffsetEdge* currEdge = head;
893 *offsetPolygon->push() = currEdge->fIntersection;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400894 if (polygonIndices) {
Jim Van Verth8bb0db32018-07-17 14:13:47 -0400895 *polygonIndices->push() = currEdge->fIndex;
896 }
897 currEdge = currEdge->fNext;
898 while (currEdge != head) {
899 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
900 (*offsetPolygon)[currIndex],
901 kCleanupTolerance)) {
902 *offsetPolygon->push() = currEdge->fIntersection;
903 if (polygonIndices) {
904 *polygonIndices->push() = currEdge->fIndex;
905 }
906 currIndex++;
907 }
908 currEdge = currEdge->fNext;
909 }
910 // make sure the first and last points aren't coincident
911 if (currIndex >= 1 &&
912 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
913 kCleanupTolerance)) {
914 offsetPolygon->pop();
915 if (polygonIndices) {
916 polygonIndices->pop();
917 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400918 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400919 }
920
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400921 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400922 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400923
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400924 return (winding*offsetWinding > 0 &&
925 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400926}
927
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400928//////////////////////////////////////////////////////////////////////////////////////////
929
930struct TriangulationVertex {
931 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
932
933 enum class VertexType { kConvex, kReflex };
934
935 SkPoint fPosition;
936 VertexType fVertexType;
937 uint16_t fIndex;
938 uint16_t fPrevIndex;
939 uint16_t fNextIndex;
940};
941
942// test to see if point p is in triangle p0p1p2.
943// for now assuming strictly inside -- if on the edge it's outside
944static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
945 const SkPoint& p) {
946 SkVector v0 = p1 - p0;
947 SkVector v1 = p2 - p1;
948 SkScalar n = v0.cross(v1);
949
950 SkVector w0 = p - p0;
951 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
952 return false;
953 }
954
955 SkVector w1 = p - p1;
956 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
957 return false;
958 }
959
960 SkVector v2 = p0 - p2;
961 SkVector w2 = p - p2;
962 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
963 return false;
964 }
965
966 return true;
967}
968
969// Data structure to track reflex vertices and check whether any are inside a given triangle
970class ReflexHash {
971public:
972 void add(TriangulationVertex* v) {
973 fReflexList.addToTail(v);
974 }
975
976 void remove(TriangulationVertex* v) {
977 fReflexList.remove(v);
978 }
979
Jim Van Verth061cc212018-07-11 14:09:09 -0400980 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
981 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400982 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
983 reflexIter != fReflexList.end(); ++reflexIter) {
984 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -0400985 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
986 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400987 return true;
988 }
989 }
990
991 return false;
992 }
993
994private:
995 // TODO: switch to an actual spatial hash
996 SkTInternalLList<TriangulationVertex> fReflexList;
997};
998
999// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1000static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1001 int winding, ReflexHash* reflexHash,
1002 SkTInternalLList<TriangulationVertex>* convexList) {
1003 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1004 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1005 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001006 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001007 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1008 reflexHash->remove(p);
1009 p->fPrev = p->fNext = nullptr;
1010 convexList->addToTail(p);
1011 }
1012 }
1013}
1014
1015bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1016 SkTDArray<uint16_t>* triangleIndices) {
1017 if (polygonSize < 3) {
1018 return false;
1019 }
1020 // need to be able to represent all the vertices in the 16-bit indices
1021 if (polygonSize >= (1 << 16)) {
1022 return false;
1023 }
1024
1025 // get winding direction
1026 // TODO: we do this for all the polygon routines -- might be better to have the client
1027 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001028 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001029 if (0 == winding) {
1030 return false;
1031 }
1032
1033 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1034 // TODO: possibly sort the convexList in some way to get better triangles
1035 SkTInternalLList<TriangulationVertex> convexList;
1036 ReflexHash reflexHash;
1037 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1038 int prevIndex = polygonSize - 1;
1039 int currIndex = 0;
1040 int nextIndex = 1;
1041 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1042 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1043 for (int i = 0; i < polygonSize; ++i) {
1044 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1045 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1046 triangulationVertices[currIndex].fIndex = currIndex;
1047 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1048 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001049 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001050 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1051 convexList.addToTail(&triangulationVertices[currIndex]);
1052 } else {
1053 // We treat near collinear vertices as reflex
1054 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1055 reflexHash.add(&triangulationVertices[currIndex]);
1056 }
1057
1058 prevIndex = currIndex;
1059 currIndex = nextIndex;
1060 nextIndex = (currIndex + 1) % polygonSize;
1061 v0 = v1;
1062 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1063 }
1064
1065 // The general concept: We are trying to find three neighboring vertices where
1066 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1067 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1068 // we have triangulated the entire polygon.
1069 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1070 // noting that only convex vertices can be potential ears, and we only need to check whether
1071 // any reflex vertices lie inside the ear.
1072 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1073 int vertexCount = polygonSize;
1074 while (vertexCount > 3) {
1075 bool success = false;
1076 TriangulationVertex* earVertex = nullptr;
1077 TriangulationVertex* p0 = nullptr;
1078 TriangulationVertex* p2 = nullptr;
1079 // find a convex vertex to clip
1080 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1081 convexIter != convexList.end(); ++convexIter) {
1082 earVertex = *convexIter;
1083 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1084
1085 p0 = &triangulationVertices[earVertex->fPrevIndex];
1086 p2 = &triangulationVertices[earVertex->fNextIndex];
1087
1088 // see if any reflex vertices are inside the ear
1089 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001090 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001091 if (failed) {
1092 continue;
1093 }
1094
1095 // found one we can clip
1096 success = true;
1097 break;
1098 }
1099 // If we can't find any ears to clip, this probably isn't a simple polygon
1100 if (!success) {
1101 return false;
1102 }
1103
1104 // add indices
1105 auto indices = triangleIndices->append(3);
1106 indices[0] = indexMap[p0->fIndex];
1107 indices[1] = indexMap[earVertex->fIndex];
1108 indices[2] = indexMap[p2->fIndex];
1109
1110 // clip the ear
1111 convexList.remove(earVertex);
1112 --vertexCount;
1113
1114 // reclassify reflex verts
1115 p0->fNextIndex = earVertex->fNextIndex;
1116 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1117
1118 p2->fPrevIndex = earVertex->fPrevIndex;
1119 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1120 }
1121
1122 // output indices
1123 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1124 vertexIter != convexList.end(); ++vertexIter) {
1125 TriangulationVertex* vertex = *vertexIter;
1126 *triangleIndices->push() = indexMap[vertex->fIndex];
1127 }
1128
1129 return true;
1130}