blob: ed4b71d59f8a6e43f3fa785263bcb2bdff3a26eb [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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -0400374 &edgeData[curr].fInset.fP0, &edgeData[curr].fInset.fP1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400375 return false;
376 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400377 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000378 }
379
Jim Van Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -0400397 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000398 // no point in considering this one again
Jim Van Verth946c3702018-07-18 13:38:25 -0400399 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000400 --insetVertexCount;
401 // go back one segment
Jim Van Verth946c3702018-07-18 13:38:25 -0400402 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000403 // we've already considered this intersection, we're done
Jim Van Verth946c3702018-07-18 13:38:25 -0400404 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500405 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth946c3702018-07-18 13:38:25 -0400406 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000407 1.0e-6f)) {
408 break;
409 } else {
410 // add intersection
Jim Van Verth946c3702018-07-18 13:38:25 -0400411 currEdge->fIntersection = intersection;
412 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000413
414 // go to next segment
Jim Van Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -0400427 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000428 --insetVertexCount;
429 // go back one segment
Jim Van Verth946c3702018-07-18 13:38:25 -0400430 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000431 } else {
432 // move to next segment
Jim Van Verth946c3702018-07-18 13:38:25 -0400433 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000434 --insetVertexCount;
Jim Van Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -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 Verth946c3702018-07-18 13:38:25 -0400590 bool operator!=(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400591 return !operator==(that);
592 }
593
Jim Van Verth946c3702018-07-18 13:38:25 -0400594 bool operator<(const ActiveEdge& that) const {
595 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX) {
596 return !that.above(*this);
597 }
598 return this->above(that);
599 }
600
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400601 OffsetSegment fSegment;
602 int32_t fIndex0; // indices for previous and next vertex
603 int32_t fIndex1;
604};
605
Jim Van Verth946c3702018-07-18 13:38:25 -0400606class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400607public:
Jim Van Verth946c3702018-07-18 13:38:25 -0400608 void reserve(int count) { }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400609
Jim Van Verth946c3702018-07-18 13:38:25 -0400610 bool insert(const SkPoint& p0, const SkPoint& p1, int32_t index0, int32_t index1) {
611 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
612 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400613 return false;
614 }
615
Jim Van Verth946c3702018-07-18 13:38:25 -0400616 Iterator& curr = result.first;
617 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
618 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400619 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400620 Iterator next = std::next(curr);
621 if (next != fEdgeTree.end() && curr->intersect(*next)) {
622 return false;
623 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400624
625 return true;
626 }
627
Jim Van Verth946c3702018-07-18 13:38:25 -0400628 bool remove(const ActiveEdge& edge) {
629 auto element = fEdgeTree.find(edge);
630 // this better not happen
631 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400632 return false;
633 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400634 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
635 return false;
636 }
637 Iterator next = std::next(element);
638 if (next != fEdgeTree.end() && element->intersect(*next)) {
639 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400640 }
641
Jim Van Verth946c3702018-07-18 13:38:25 -0400642 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400643 return true;
644 }
645
646private:
Jim Van Verth946c3702018-07-18 13:38:25 -0400647 std::set<ActiveEdge> fEdgeTree;
648 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400649};
650
651// Here we implement a sweep line algorithm to determine whether the provided points
652// represent a simple polygon, i.e., the polygon is non-self-intersecting.
653// We first insert the vertices into a priority queue sorting horizontally from left to right.
654// Then as we pop the vertices from the queue we generate events which indicate that an edge
655// should be added or removed from an edge list. If any intersections are detected in the edge
656// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400657bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400658 if (polygonSize < 3) {
659 return false;
660 }
661
Jim Van Verth946c3702018-07-18 13:38:25 -0400662 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400663 for (int i = 0; i < polygonSize; ++i) {
664 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400665 if (!polygon[i].isFinite()) {
666 return false;
667 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400668 newVertex.fPosition = polygon[i];
669 newVertex.fIndex = i;
670 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
671 newVertex.fNextIndex = (i + 1) % polygonSize;
672 newVertex.fFlags = 0;
673 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
674 newVertex.fFlags |= kPrevLeft_VertexFlag;
675 }
676 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
677 newVertex.fFlags |= kNextLeft_VertexFlag;
678 }
679 vertexQueue.insert(newVertex);
680 }
681
682 // pop each vertex from the queue and generate events depending on
683 // where it lies relative to its neighboring edges
Jim Van Verth946c3702018-07-18 13:38:25 -0400684 ActiveEdgeList sweepLine;
685 sweepLine.reserve(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400686 while (vertexQueue.count() > 0) {
687 const Vertex& v = vertexQueue.peek();
688
689 // check edge to previous vertex
690 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth946c3702018-07-18 13:38:25 -0400691 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400692 if (!sweepLine.remove(edge)) {
693 break;
694 }
695 } else {
Jim Van Verth946c3702018-07-18 13:38:25 -0400696 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400697 break;
698 }
699 }
700
701 // check edge to next vertex
702 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth946c3702018-07-18 13:38:25 -0400703 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400704 if (!sweepLine.remove(edge)) {
705 break;
706 }
707 } else {
Jim Van Verth946c3702018-07-18 13:38:25 -0400708 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400709 break;
710 }
711 }
712
713 vertexQueue.pop();
714 }
715
716 return (vertexQueue.count() == 0);
717}
718
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400719///////////////////////////////////////////////////////////////////////////////////////////
720
Jim Van Verth946c3702018-07-18 13:38:25 -0400721// helper function for SkOffsetSimplePolygon
722static void setup_offset_edge(OffsetEdge* currEdge,
723 const SkPoint& endpoint0, const SkPoint& endpoint1,
724 int startIndex, int endIndex) {
725 currEdge->fInset.fP0 = endpoint0;
726 currEdge->fInset.fP1 = endpoint1;
727 currEdge->init(startIndex, endIndex);
728}
729
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400730bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400731 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
732 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400733 if (inputPolygonSize < 3) {
734 return false;
735 }
736
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400737 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400738 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400739 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400740 return false;
741 }
742
Jim Van Verthbdde4282018-06-14 09:09:18 -0400743 // build normals
744 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
745 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
746 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400747 if (!SkScalarIsFinite(currOffset)) {
748 return false;
749 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400750 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400751 if (!inputPolygonVerts[curr].isFinite()) {
752 return false;
753 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400754 int next = (curr + 1) % inputPolygonSize;
755 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400756 if (!SkScalarIsFinite(nextOffset)) {
757 return false;
758 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400759 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
760 currOffset, nextOffset, winding,
761 &normal0[curr], &normal1[next])) {
762 return false;
763 }
764 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400765 }
766
767 // build initial offset edge list
Jim Van Verth946c3702018-07-18 13:38:25 -0400768 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400769 int prevIndex = inputPolygonSize - 1;
770 int currIndex = 0;
771 int nextIndex = 1;
772 while (currIndex < inputPolygonSize) {
773 int side = compute_side(inputPolygonVerts[prevIndex],
774 inputPolygonVerts[currIndex],
775 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400776 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400777 // if reflex point, fill in curve
778 if (side*winding*offset < 0) {
779 SkScalar rotSin, rotCos;
780 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400781 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400782 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
783 &rotSin, &rotCos, &numSteps)) {
784 return false;
785 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400786 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400787 for (int i = 0; i < numSteps - 1; ++i) {
788 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
789 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth946c3702018-07-18 13:38:25 -0400790 setup_offset_edge(currEdge,
791 inputPolygonVerts[currIndex] + prevNormal,
792 inputPolygonVerts[currIndex] + currNormal,
793 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400794 prevNormal = currNormal;
Jim Van Verth946c3702018-07-18 13:38:25 -0400795 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400796 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400797 setup_offset_edge(currEdge,
798 inputPolygonVerts[currIndex] + prevNormal,
799 inputPolygonVerts[currIndex] + normal0[currIndex],
800 currIndex, currIndex);
801 ++currEdge;
802
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400803 }
804
805 // Add the edge
Jim Van Verth946c3702018-07-18 13:38:25 -0400806 auto edge = edgeData.push_back_n(1);
807 setup_offset_edge(edge,
808 inputPolygonVerts[currIndex] + normal0[currIndex],
809 inputPolygonVerts[nextIndex] + normal1[nextIndex],
810 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400811
812 prevIndex = currIndex;
813 currIndex++;
814 nextIndex = (nextIndex + 1) % inputPolygonSize;
815 }
816
Jim Van Verth946c3702018-07-18 13:38:25 -0400817 // build linked list
818 // we have to do this as a post-process step because we might have reallocated
819 // the array when adding fans for reflex verts
820 prevIndex = edgeData.count()-1;
821 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
822 int nextIndex = (currIndex + 1) % edgeData.count();
823 edgeData[currIndex].fPrev = &edgeData[prevIndex];
824 edgeData[currIndex].fNext = &edgeData[nextIndex];
825 }
826
827 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400828 int edgeDataSize = edgeData.count();
Jim Van Verth946c3702018-07-18 13:38:25 -0400829 auto head = &edgeData[0];
830 auto currEdge = head;
831 auto prevEdge = currEdge->fPrev;
832 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400833 int iterations = 0;
Jim Van Verth946c3702018-07-18 13:38:25 -0400834 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400835 ++iterations;
836 // we should check each edge against each other edge at most once
837 if (iterations > edgeDataSize*edgeDataSize) {
838 return false;
839 }
840
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400841 SkScalar s, t;
842 SkPoint intersection;
Jim Van Verth946c3702018-07-18 13:38:25 -0400843 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400844 &intersection, &s, &t)) {
845 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth946c3702018-07-18 13:38:25 -0400846 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400847 // no point in considering this one again
Jim Van Verth946c3702018-07-18 13:38:25 -0400848 remove_node(prevEdge, &head);
849 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400850 // go back one segment
Jim Van Verth946c3702018-07-18 13:38:25 -0400851 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400852 // we've already considered this intersection, we're done
Jim Van Verth946c3702018-07-18 13:38:25 -0400853 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400854 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth946c3702018-07-18 13:38:25 -0400855 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400856 1.0e-6f)) {
857 break;
858 } else {
859 // add intersection
Jim Van Verth946c3702018-07-18 13:38:25 -0400860 currEdge->fIntersection = intersection;
861 currEdge->fTValue = t;
862 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400863
864 // go to next segment
Jim Van Verth946c3702018-07-18 13:38:25 -0400865 prevEdge = currEdge;
866 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400867 }
868 } else {
869 // If there is no intersection, we want to minimize the distance between
870 // the point where the segment lines cross and the segments themselves.
Jim Van Verth946c3702018-07-18 13:38:25 -0400871 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
872 OffsetEdge* currNextEdge = currEdge->fNext;
873 SkScalar dist0 = compute_crossing_distance(currEdge->fInset,
874 prevPrevEdge->fInset);
875 SkScalar dist1 = compute_crossing_distance(prevEdge->fInset,
876 currNextEdge->fInset);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400877 if (dist0 < dist1) {
Jim Van Verth946c3702018-07-18 13:38:25 -0400878 remove_node(prevEdge, &head);
879 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400880 } else {
Jim Van Verth946c3702018-07-18 13:38:25 -0400881 remove_node(currEdge, &head);
882 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400883 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400884 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400885 }
886 }
887
888 // store all the valid intersections that aren't nearly coincident
889 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400890 offsetPolygon->reset();
Jim Van Verth946c3702018-07-18 13:38:25 -0400891 if (head) {
892 static constexpr SkScalar kCleanupTolerance = 0.01f;
893 if (offsetVertexCount >= 0) {
894 offsetPolygon->setReserve(offsetVertexCount);
Jim Van Verth3229e652018-07-17 20:22:39 +0000895 }
Jim Van Verth946c3702018-07-18 13:38:25 -0400896 int currIndex = 0;
897 OffsetEdge* currEdge = head;
898 *offsetPolygon->push() = currEdge->fIntersection;
Jim Van Verth3229e652018-07-17 20:22:39 +0000899 if (polygonIndices) {
Jim Van Verth946c3702018-07-18 13:38:25 -0400900 *polygonIndices->push() = currEdge->fIndex;
901 }
902 currEdge = currEdge->fNext;
903 while (currEdge != head) {
904 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
905 (*offsetPolygon)[currIndex],
906 kCleanupTolerance)) {
907 *offsetPolygon->push() = currEdge->fIntersection;
908 if (polygonIndices) {
909 *polygonIndices->push() = currEdge->fIndex;
910 }
911 currIndex++;
912 }
913 currEdge = currEdge->fNext;
914 }
915 // make sure the first and last points aren't coincident
916 if (currIndex >= 1 &&
917 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
918 kCleanupTolerance)) {
919 offsetPolygon->pop();
920 if (polygonIndices) {
921 polygonIndices->pop();
922 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400923 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400924 }
925
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400926 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400927 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400928
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400929 return (winding*offsetWinding > 0 &&
930 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400931}
932
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400933//////////////////////////////////////////////////////////////////////////////////////////
934
935struct TriangulationVertex {
936 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
937
938 enum class VertexType { kConvex, kReflex };
939
940 SkPoint fPosition;
941 VertexType fVertexType;
942 uint16_t fIndex;
943 uint16_t fPrevIndex;
944 uint16_t fNextIndex;
945};
946
947// test to see if point p is in triangle p0p1p2.
948// for now assuming strictly inside -- if on the edge it's outside
949static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
950 const SkPoint& p) {
951 SkVector v0 = p1 - p0;
952 SkVector v1 = p2 - p1;
953 SkScalar n = v0.cross(v1);
954
955 SkVector w0 = p - p0;
956 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
957 return false;
958 }
959
960 SkVector w1 = p - p1;
961 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
962 return false;
963 }
964
965 SkVector v2 = p0 - p2;
966 SkVector w2 = p - p2;
967 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
968 return false;
969 }
970
971 return true;
972}
973
974// Data structure to track reflex vertices and check whether any are inside a given triangle
975class ReflexHash {
976public:
977 void add(TriangulationVertex* v) {
978 fReflexList.addToTail(v);
979 }
980
981 void remove(TriangulationVertex* v) {
982 fReflexList.remove(v);
983 }
984
Jim Van Verth061cc212018-07-11 14:09:09 -0400985 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
986 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400987 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
988 reflexIter != fReflexList.end(); ++reflexIter) {
989 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -0400990 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
991 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400992 return true;
993 }
994 }
995
996 return false;
997 }
998
999private:
1000 // TODO: switch to an actual spatial hash
1001 SkTInternalLList<TriangulationVertex> fReflexList;
1002};
1003
1004// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1005static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1006 int winding, ReflexHash* reflexHash,
1007 SkTInternalLList<TriangulationVertex>* convexList) {
1008 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1009 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1010 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001011 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001012 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1013 reflexHash->remove(p);
1014 p->fPrev = p->fNext = nullptr;
1015 convexList->addToTail(p);
1016 }
1017 }
1018}
1019
1020bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1021 SkTDArray<uint16_t>* triangleIndices) {
1022 if (polygonSize < 3) {
1023 return false;
1024 }
1025 // need to be able to represent all the vertices in the 16-bit indices
1026 if (polygonSize >= (1 << 16)) {
1027 return false;
1028 }
1029
1030 // get winding direction
1031 // TODO: we do this for all the polygon routines -- might be better to have the client
1032 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001033 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001034 if (0 == winding) {
1035 return false;
1036 }
1037
1038 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1039 // TODO: possibly sort the convexList in some way to get better triangles
1040 SkTInternalLList<TriangulationVertex> convexList;
1041 ReflexHash reflexHash;
1042 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1043 int prevIndex = polygonSize - 1;
1044 int currIndex = 0;
1045 int nextIndex = 1;
1046 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1047 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1048 for (int i = 0; i < polygonSize; ++i) {
1049 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1050 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1051 triangulationVertices[currIndex].fIndex = currIndex;
1052 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1053 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001054 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001055 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1056 convexList.addToTail(&triangulationVertices[currIndex]);
1057 } else {
1058 // We treat near collinear vertices as reflex
1059 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1060 reflexHash.add(&triangulationVertices[currIndex]);
1061 }
1062
1063 prevIndex = currIndex;
1064 currIndex = nextIndex;
1065 nextIndex = (currIndex + 1) % polygonSize;
1066 v0 = v1;
1067 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1068 }
1069
1070 // The general concept: We are trying to find three neighboring vertices where
1071 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1072 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1073 // we have triangulated the entire polygon.
1074 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1075 // noting that only convex vertices can be potential ears, and we only need to check whether
1076 // any reflex vertices lie inside the ear.
1077 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1078 int vertexCount = polygonSize;
1079 while (vertexCount > 3) {
1080 bool success = false;
1081 TriangulationVertex* earVertex = nullptr;
1082 TriangulationVertex* p0 = nullptr;
1083 TriangulationVertex* p2 = nullptr;
1084 // find a convex vertex to clip
1085 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1086 convexIter != convexList.end(); ++convexIter) {
1087 earVertex = *convexIter;
1088 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1089
1090 p0 = &triangulationVertices[earVertex->fPrevIndex];
1091 p2 = &triangulationVertices[earVertex->fNextIndex];
1092
1093 // see if any reflex vertices are inside the ear
1094 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001095 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001096 if (failed) {
1097 continue;
1098 }
1099
1100 // found one we can clip
1101 success = true;
1102 break;
1103 }
1104 // If we can't find any ears to clip, this probably isn't a simple polygon
1105 if (!success) {
1106 return false;
1107 }
1108
1109 // add indices
1110 auto indices = triangleIndices->append(3);
1111 indices[0] = indexMap[p0->fIndex];
1112 indices[1] = indexMap[earVertex->fIndex];
1113 indices[2] = indexMap[p2->fIndex];
1114
1115 // clip the ear
1116 convexList.remove(earVertex);
1117 --vertexCount;
1118
1119 // reclassify reflex verts
1120 p0->fNextIndex = earVertex->fNextIndex;
1121 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1122
1123 p2->fPrevIndex = earVertex->fPrevIndex;
1124 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1125 }
1126
1127 // output indices
1128 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1129 vertexIter != convexList.end(); ++vertexIter) {
1130 TriangulationVertex* vertex = *vertexIter;
1131 *triangleIndices->push() = indexMap[vertex->fIndex];
1132 }
1133
1134 return true;
1135}