blob: 290fa68f2d8642cda0ceff52d77628d1c778fb5a [file] [log] [blame]
Brian Salomonab664fa2017-03-24 16:07:20 +00001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Jim Van Verth8664a1d2018-06-28 16:26:50 -04008#include "SkPolyUtils.h"
Brian Salomonab664fa2017-03-24 16:07:20 +00009
Jim Van Verth00673692018-07-23 11:23:39 -040010#include <set>
Cary Clarkdf429f32017-11-08 11:44:31 -050011#include "SkPointPriv.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040012#include "SkTArray.h"
Brian Salomonab664fa2017-03-24 16:07:20 +000013#include "SkTemplates.h"
Jim Van Verth4db18ed2018-04-03 10:00:37 -040014#include "SkTDPQueue.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040015#include "SkTInternalLList.h"
16
17//////////////////////////////////////////////////////////////////////////////////
18// Helper data structures and functions
Brian Salomonab664fa2017-03-24 16:07:20 +000019
Jim Van Verth4db18ed2018-04-03 10:00:37 -040020struct OffsetSegment {
Brian Salomonab664fa2017-03-24 16:07:20 +000021 SkPoint fP0;
Jim Van Vertha6316832018-07-24 09:30:37 -040022 SkVector fV;
Brian Salomonab664fa2017-03-24 16:07:20 +000023};
24
Jim Van Vertha6316832018-07-24 09:30:37 -040025// Computes perpDot for point p compared to segment defined by origin s0 and vector v0.
Brian Salomonab664fa2017-03-24 16:07:20 +000026// A positive value means the point is to the left of the segment,
27// negative is to the right, 0 is collinear.
Jim Van Vertha6316832018-07-24 09:30:37 -040028static int compute_side(const SkPoint& s0, const SkVector& v0, const SkPoint& p) {
Brian Salomonab664fa2017-03-24 16:07:20 +000029 SkVector v1 = p - s0;
30 SkScalar perpDot = v0.cross(v1);
31 if (!SkScalarNearlyZero(perpDot)) {
32 return ((perpDot > 0) ? 1 : -1);
33 }
34
35 return 0;
36}
37
Jim Van Verth6784ffa2018-07-03 16:12:39 -040038// Returns 1 for cw, -1 for ccw and 0 if zero signed area (either degenerate or self-intersecting)
39int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
40 if (polygonSize < 3) {
41 return 0;
42 }
43
Jim Van Verth8664a1d2018-06-28 16:26:50 -040044 // compute area and use sign to determine winding
45 SkScalar quadArea = 0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040046 SkVector v0 = polygonVerts[1] - polygonVerts[0];
47 for (int curr = 1; curr < polygonSize - 1; ++curr) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -040048 int next = (curr + 1) % polygonSize;
Jim Van Verth6784ffa2018-07-03 16:12:39 -040049 SkVector v1 = polygonVerts[next] - polygonVerts[0];
50 quadArea += v0.cross(v1);
51 v0 = v1;
Brian Salomonab664fa2017-03-24 16:07:20 +000052 }
Jim Van Verth8664a1d2018-06-28 16:26:50 -040053 if (SkScalarNearlyZero(quadArea)) {
54 return 0;
55 }
56 // 1 == ccw, -1 == cw
57 return (quadArea > 0) ? 1 : -1;
Brian Salomonab664fa2017-03-24 16:07:20 +000058}
59
Jim Van Verthbdde4282018-06-14 09:09:18 -040060// Helper function to compute the individual vector for non-equal offsets
61inline void compute_offset(SkScalar d, const SkPoint& polyPoint, int side,
62 const SkPoint& outerTangentIntersect, SkVector* v) {
63 SkScalar dsq = d * d;
64 SkVector dP = outerTangentIntersect - polyPoint;
65 SkScalar dPlenSq = SkPointPriv::LengthSqd(dP);
66 if (SkScalarNearlyZero(dPlenSq)) {
67 v->set(0, 0);
68 } else {
69 SkScalar discrim = SkScalarSqrt(dPlenSq - dsq);
70 v->fX = (dsq*dP.fX - side * d*dP.fY*discrim) / dPlenSq;
71 v->fY = (dsq*dP.fY + side * d*dP.fX*discrim) / dPlenSq;
72 }
73}
74
75// Compute difference vector to offset p0-p1 'd0' and 'd1' units in direction specified by 'side'
76bool compute_offset_vectors(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
77 int side, SkPoint* vector0, SkPoint* vector1) {
Jim Van Verthda965502017-04-11 15:29:14 -040078 SkASSERT(side == -1 || side == 1);
Jim Van Verthda965502017-04-11 15:29:14 -040079 if (SkScalarNearlyEqual(d0, d1)) {
80 // if distances are equal, can just outset by the perpendicular
Jim Van Verthbdde4282018-06-14 09:09:18 -040081 SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
Jim Van Verthda965502017-04-11 15:29:14 -040082 perp.setLength(d0*side);
Jim Van Verthbdde4282018-06-14 09:09:18 -040083 *vector0 = perp;
84 *vector1 = perp;
Jim Van Verthda965502017-04-11 15:29:14 -040085 } else {
Jim Van Verthbdde4282018-06-14 09:09:18 -040086 SkScalar d0abs = SkTAbs(d0);
87 SkScalar d1abs = SkTAbs(d1);
Jim Van Verthda965502017-04-11 15:29:14 -040088 // Otherwise we need to compute the outer tangent.
89 // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm
Jim Van Verthbdde4282018-06-14 09:09:18 -040090 if (d0abs < d1abs) {
Jim Van Verthda965502017-04-11 15:29:14 -040091 side = -side;
92 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040093 SkScalar dD = d0abs - d1abs;
Jim Van Verthda965502017-04-11 15:29:14 -040094 // if one circle is inside another, we can't compute an offset
Cary Clarkdf429f32017-11-08 11:44:31 -050095 if (dD*dD >= SkPointPriv::DistanceToSqd(p0, p1)) {
Jim Van Verthda965502017-04-11 15:29:14 -040096 return false;
97 }
Jim Van Verthbdde4282018-06-14 09:09:18 -040098 SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0abs - p0.fX*d1abs) / dD,
99 (p1.fY*d0abs - p0.fY*d1abs) / dD);
Jim Van Verthda965502017-04-11 15:29:14 -0400100
Jim Van Verthbdde4282018-06-14 09:09:18 -0400101 compute_offset(d0, p0, side, outerTangentIntersect, vector0);
102 compute_offset(d1, p1, side, outerTangentIntersect, vector1);
Jim Van Verthda965502017-04-11 15:29:14 -0400103 }
104
105 return true;
Brian Salomonab664fa2017-03-24 16:07:20 +0000106}
107
Jim Van Verthbdde4282018-06-14 09:09:18 -0400108// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side'
109bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
110 int side, SkPoint* offset0, SkPoint* offset1) {
111 SkVector v0, v1;
112 if (!compute_offset_vectors(p0, p1, d0, d1, side, &v0, &v1)) {
113 return false;
114 }
115 *offset0 = p0 + v0;
116 *offset1 = p1 + v1;
117
118 return true;
119}
120
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400121// compute fraction of d along v
122static inline SkScalar compute_param(const SkVector& v, const SkVector& d) {
123 if (SkScalarNearlyZero(v.fX)) {
124 return d.fY / v.fY;
125 } else {
126 return d.fX / v.fX;
127 }
128}
129
Brian Salomonab664fa2017-03-24 16:07:20 +0000130// Compute the intersection 'p' between segments s0 and s1, if any.
131// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
132// Returns false if there is no intersection.
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400133static bool compute_intersection(const OffsetSegment& s0, const OffsetSegment& s1,
Brian Salomonab664fa2017-03-24 16:07:20 +0000134 SkPoint* p, SkScalar* s, SkScalar* t) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400135 const SkVector& v0 = s0.fV;
136 const SkVector& v1 = s1.fV;
Brian Salomonab664fa2017-03-24 16:07:20 +0000137 SkVector d = s1.fP0 - s0.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400138 SkScalar perpDot = v0.cross(v1);
139 SkScalar localS, localT;
140 if (SkScalarNearlyZero(perpDot)) {
141 // segments are parallel, but not collinear
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400142 if (!SkScalarNearlyZero(d.cross(v0)) || !SkScalarNearlyZero(d.cross(v1))) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400143 return false;
144 }
145
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400146 // Check for degenerate segments
147 if (!SkPointPriv::CanNormalize(v0.fX, v0.fY)) {
148 // Both are degenerate
149 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
150 // Check if they're the same point
151 if (!SkPointPriv::CanNormalize(d.fX, d.fY)) {
152 *p = s0.fP0;
153 *s = 0;
154 *t = 0;
155 return true;
156 } else {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400157 return false;
158 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400159 }
160 // Otherwise project onto segment1
161 localT = compute_param(v1, -d);
162 if (localT < 0 || localT > SK_Scalar1) {
163 return false;
164 }
165 localS = 0;
166 } else {
167 // Project segment1's endpoints onto segment0
168 localS = compute_param(v0, d);
169 localT = 0;
170 if (localS < 0 || localS > SK_Scalar1) {
171 // The first endpoint doesn't lie on segment0
172 // If segment1 is degenerate, then there's no collision
173 if (!SkPointPriv::CanNormalize(v1.fX, v1.fY)) {
174 return false;
175 }
176
177 // Otherwise try the other one
178 SkScalar oldLocalS = localS;
Jim Van Vertha6316832018-07-24 09:30:37 -0400179 localS = compute_param(v0, d + v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400180 localT = SK_Scalar1;
181 if (localS < 0 || localS > SK_Scalar1) {
182 // it's possible that segment1's interval surrounds segment0
183 // this is false if params have the same signs, and in that case no collision
184 if (localS*oldLocalS > 0) {
185 return false;
186 }
187 // otherwise project segment0's endpoint onto segment1 instead
188 localS = 0;
189 localT = compute_param(v1, -d);
190 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400191 }
192 }
193 } else {
194 localS = d.cross(v1) / perpDot;
195 if (localS < 0 || localS > SK_Scalar1) {
196 return false;
197 }
198 localT = d.cross(v0) / perpDot;
199 if (localT < 0 || localT > SK_Scalar1) {
200 return false;
201 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000202 }
203
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400204 *p = s0.fP0 + v0*localS;
Brian Salomonab664fa2017-03-24 16:07:20 +0000205 *s = localS;
206 *t = localT;
207
208 return true;
209}
210
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400211// computes the line intersection and then the distance to s0's endpoint
212static SkScalar compute_crossing_distance(const OffsetSegment& s0, const OffsetSegment& s1) {
Jim Van Vertha6316832018-07-24 09:30:37 -0400213 const SkVector& v0 = s0.fV;
214 const SkVector& v1 = s1.fV;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400215
216 SkScalar perpDot = v0.cross(v1);
217 if (SkScalarNearlyZero(perpDot)) {
218 // segments are parallel
219 return SK_ScalarMax;
220 }
221
222 SkVector d = s1.fP0 - s0.fP0;
223 SkScalar localS = d.cross(v1) / perpDot;
224 if (localS < 0) {
225 localS = -localS;
226 } else {
227 localS -= SK_Scalar1;
228 }
229
230 localS *= v0.length();
231
232 return localS;
233}
234
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400235bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
236 if (polygonSize < 3) {
237 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400238 }
239
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400240 SkScalar lastArea = 0;
241 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400242
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400243 int prevIndex = polygonSize - 1;
244 int currIndex = 0;
245 int nextIndex = 1;
246 SkPoint origin = polygonVerts[0];
247 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
248 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
249 SkVector w0 = polygonVerts[currIndex] - origin;
250 SkVector w1 = polygonVerts[nextIndex] - origin;
251 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400252 if (!polygonVerts[i].isFinite()) {
253 return false;
254 }
255
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400256 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400257 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400258 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400259 return false;
260 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400261 if (0 != perpDot) {
262 lastPerpDot = perpDot;
263 }
264
265 // If the signed area ever flips it's concave
266 // TODO: see if we can verify convexity only with signed area
267 SkScalar quadArea = w0.cross(w1);
268 if (quadArea*lastArea < 0) {
269 return false;
270 }
271 if (0 != quadArea) {
272 lastArea = quadArea;
273 }
274
275 prevIndex = currIndex;
276 currIndex = nextIndex;
277 nextIndex = (currIndex + 1) % polygonSize;
278 v0 = v1;
279 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
280 w0 = w1;
281 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400282 }
283
284 return true;
285}
Jim Van Verth0513f142017-03-24 14:28:57 -0400286
Jim Van Verth00673692018-07-23 11:23:39 -0400287struct OffsetEdge {
288 OffsetEdge* fPrev;
289 OffsetEdge* fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400290 OffsetSegment fInset;
291 SkPoint fIntersection;
292 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400293 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400294 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400295
Jim Van Verth00673692018-07-23 11:23:39 -0400296 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400297 fIntersection = fInset.fP0;
298 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400299 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400300 fEnd = end;
301 }
302
303 // special intersection check that looks for endpoint intersection
304 bool checkIntersection(const OffsetEdge* that,
305 SkPoint* p, SkScalar* s, SkScalar* t) {
306 if (this->fEnd == that->fIndex) {
307 SkPoint p1 = this->fInset.fP0 + this->fInset.fV;
308 if (SkPointPriv::EqualsWithinTolerance(p1, that->fInset.fP0)) {
309 *p = p1;
310 *s = SK_Scalar1;
311 *t = 0;
312 return true;
313 }
314 }
315
316 return compute_intersection(this->fInset, that->fInset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400317 }
318};
319
Jim Van Verth00673692018-07-23 11:23:39 -0400320static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
321 // remove from linked list
322 node->fPrev->fNext = node->fNext;
323 node->fNext->fPrev = node->fPrev;
324 if (node == *head) {
325 *head = (node->fNext == node) ? nullptr : node->fNext;
326 }
327}
328
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400329//////////////////////////////////////////////////////////////////////////////////
330
Brian Salomonab664fa2017-03-24 16:07:20 +0000331// The objective here is to inset all of the edges by the given distance, and then
332// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
333// we should only be making left-hand turns (for cw polygons, we use the winding
334// parameter to reverse this). We detect this by checking whether the second intersection
335// on an edge is closer to its tail than the first one.
336//
337// We might also have the case that there is no intersection between two neighboring inset edges.
338// In this case, one edge will lie to the right of the other and should be discarded along with
339// its previous intersection (if any).
340//
341// Note: the assumption is that inputPolygon is convex and has no coincident points.
342//
343bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400344 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400345 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000346 if (inputPolygonSize < 3) {
347 return false;
348 }
349
Jim Van Vertha6316832018-07-24 09:30:37 -0400350 // restrict this to match other routines
351 // practically we don't want anything bigger than this anyway
352 if (inputPolygonSize >= (1 << 16)) {
353 return false;
354 }
355
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400356 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400357 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000358 if (0 == winding) {
359 return false;
360 }
361
362 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400363 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
364 int prev = inputPolygonSize - 1;
365 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
366 int next = (curr + 1) % inputPolygonSize;
367 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400368 return false;
369 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400370 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400371 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400372 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400373 return false;
374 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400375 SkPoint p0, p1;
Jim Van Verth00673692018-07-23 11:23:39 -0400376 if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
377 insetDistanceFunc(inputPolygonVerts[curr]),
378 insetDistanceFunc(inputPolygonVerts[next]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400379 winding,
Jim Van Vertha6316832018-07-24 09:30:37 -0400380 &p0, &p1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400381 return false;
382 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400383 edgeData[curr].fPrev = &edgeData[prev];
384 edgeData[curr].fNext = &edgeData[next];
385 edgeData[curr].fInset.fP0 = p0;
386 edgeData[curr].fInset.fV = p1 - p0;
Jim Van Verth00673692018-07-23 11:23:39 -0400387 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000388 }
389
Jim Van Verth00673692018-07-23 11:23:39 -0400390 OffsetEdge* head = &edgeData[0];
391 OffsetEdge* currEdge = head;
392 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000393 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400394 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400395 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400396 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400397 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400398 if (iterations > inputPolygonSize*inputPolygonSize) {
399 return false;
400 }
401
Brian Salomonab664fa2017-03-24 16:07:20 +0000402 SkScalar s, t;
403 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400404 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000405 &intersection, &s, &t)) {
406 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400407 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000408 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400409 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000410 --insetVertexCount;
411 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400412 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000413 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400414 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500415 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400416 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000417 1.0e-6f)) {
418 break;
419 } else {
420 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400421 currEdge->fIntersection = intersection;
422 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000423
424 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400425 prevEdge = currEdge;
426 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000427 }
428 } else {
429 // if prev to right side of curr
Jim Van Verth00673692018-07-23 11:23:39 -0400430 int side = winding*compute_side(currEdge->fInset.fP0,
Jim Van Vertha6316832018-07-24 09:30:37 -0400431 currEdge->fInset.fV,
432 prevEdge->fInset.fP0);
433 if (side < 0 &&
434 side == winding*compute_side(currEdge->fInset.fP0,
435 currEdge->fInset.fV,
436 prevEdge->fInset.fP0 + prevEdge->fInset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000437 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400438 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000439 --insetVertexCount;
440 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400441 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000442 } else {
443 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400444 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000445 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400446 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000447 }
448 }
449 }
450
Jim Van Verthda965502017-04-11 15:29:14 -0400451 // store all the valid intersections that aren't nearly coincident
452 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000453 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400454 if (head) {
455 static constexpr SkScalar kCleanupTolerance = 0.01f;
456 if (insetVertexCount >= 0) {
457 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000458 }
Jim Van Verth00673692018-07-23 11:23:39 -0400459 int currIndex = 0;
460 OffsetEdge* currEdge = head;
461 *insetPolygon->push() = currEdge->fIntersection;
462 currEdge = currEdge->fNext;
463 while (currEdge != head) {
464 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
465 (*insetPolygon)[currIndex],
466 kCleanupTolerance)) {
467 *insetPolygon->push() = currEdge->fIntersection;
468 currIndex++;
469 }
470 currEdge = currEdge->fNext;
471 }
472 // make sure the first and last points aren't coincident
473 if (currIndex >= 1 &&
474 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
475 kCleanupTolerance)) {
476 insetPolygon->pop();
477 }
Jim Van Verthda965502017-04-11 15:29:14 -0400478 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000479
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400480 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000481}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400482
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400483///////////////////////////////////////////////////////////////////////////////////////////
484
485// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth061cc212018-07-11 14:09:09 -0400486bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400487 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400488 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
489
490 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400491 if (!SkScalarIsFinite(rCos)) {
492 return false;
493 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400494 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400495 if (!SkScalarIsFinite(rSin)) {
496 return false;
497 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400498 SkScalar theta = SkScalarATan2(rSin, rCos);
499
Jim Van Verth206dbe82018-07-23 11:48:31 -0400500 SkScalar floatSteps = SkScalarAbs(r*theta*kRecipPixelsPerArcSegment);
501 // limit the number of steps to at most max uint16_t (that's all we can index)
502 // knock one value off the top to account for rounding
503 if (floatSteps >= (1 << 16)-1) {
504 return false;
505 }
506 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400507
Jim Van Verth061cc212018-07-11 14:09:09 -0400508 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400509 *rotSin = SkScalarSinCos(dTheta, rotCos);
510 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400511 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400512}
513
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400514///////////////////////////////////////////////////////////////////////////////////////////
515
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400516// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
517static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400518 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400519}
520
521struct Vertex {
522 static bool Left(const Vertex& qv0, const Vertex& qv1) {
523 return left(qv0.fPosition, qv1.fPosition);
524 }
Jim Van Verth00673692018-07-23 11:23:39 -0400525
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400526 // packed to fit into 16 bytes (one cache line)
527 SkPoint fPosition;
528 uint16_t fIndex; // index in unsorted polygon
529 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
530 uint16_t fNextIndex;
531 uint16_t fFlags;
532};
533
534enum VertexFlags {
535 kPrevLeft_VertexFlag = 0x1,
536 kNextLeft_VertexFlag = 0x2,
537};
538
Jim Van Verth00673692018-07-23 11:23:39 -0400539struct ActiveEdge {
Jim Van Vertha6316832018-07-24 09:30:37 -0400540 ActiveEdge(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1)
541 : fSegment({p0, p1-p0})
Jim Van Verth00673692018-07-23 11:23:39 -0400542 , fIndex0(index0)
543 , fIndex1(index1) {}
544
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400545 // returns true if "this" is above "that"
Jim Van Verth00673692018-07-23 11:23:39 -0400546 bool above(const ActiveEdge& that) const {
547 SkASSERT(this->fSegment.fP0.fX <= that.fSegment.fP0.fX);
548 const SkScalar kTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
Jim Van Vertha6316832018-07-24 09:30:37 -0400549 const SkVector& u = this->fSegment.fV;
550 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400551 // The idea here is that if the vector between the origins of the two segments (dv)
552 // rotates counterclockwise up to the vector representing the "this" segment (u),
553 // then we know that "this" is above that. If the result is clockwise we say it's below.
Jim Van Verth00673692018-07-23 11:23:39 -0400554 if (this->fIndex0 != that.fIndex0) {
Jim Van Verth00673692018-07-23 11:23:39 -0400555 SkScalar cross = dv.cross(u);
556 if (cross > kTolerance) {
557 return true;
558 } else if (cross < -kTolerance) {
559 return false;
560 }
561 } else if (this->fIndex1 == that.fIndex1) {
562 // they're the same edge
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400563 return false;
564 }
Jim Van Verth00673692018-07-23 11:23:39 -0400565 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400566 // lies on dv. So then we try the same for the vector from the tail of "this"
567 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Vertha6316832018-07-24 09:30:37 -0400568 // dv = that.P1 - this.P0
569 // = that.fP0 + that.fV - this.fP0
570 // = that.fP0 - this.fP0 + that.fV
571 // = old_dv + that.fV
572 dv += that.fSegment.fV;
Jim Van Verth00673692018-07-23 11:23:39 -0400573 SkScalar cross = dv.cross(u);
574 if (cross > kTolerance) {
575 return true;
576 } else if (cross < -kTolerance) {
577 return false;
578 }
579 // If the previous check fails, the two segments are nearly collinear
580 // First check y-coord of first endpoints
581 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
582 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
583 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
584 return true;
585 } else if (this->fSegment.fP0.fY < that.fSegment.fP0.fY) {
586 return false;
587 }
588 // The first endpoints are the same, so check the other endpoint
Jim Van Vertha6316832018-07-24 09:30:37 -0400589 SkPoint thisP1 = this->fSegment.fP0 + this->fSegment.fV;
590 SkPoint thatP1 = that.fSegment.fP0 + that.fSegment.fV;
591 if (thisP1.fX < thatP1.fX) {
592 return (thisP1.fY >= thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400593 } else {
Jim Van Vertha6316832018-07-24 09:30:37 -0400594 return (thisP1.fY > thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400595 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400596 }
597
Jim Van Verth00673692018-07-23 11:23:39 -0400598 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400599 SkPoint intersection;
600 SkScalar s, t;
601 // check first to see if these edges are neighbors in the polygon
602 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
603 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
604 return false;
605 }
606 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
607 }
608
Jim Van Verth00673692018-07-23 11:23:39 -0400609 bool lessThan(const ActiveEdge& that) const {
610 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX ||
611 (this->fSegment.fP0.fX == that.fSegment.fP0.fX &&
612 this->fSegment.fP0.fY < that.fSegment.fP0.fY)) {
613 return !that.above(*this);
614 }
615 return this->above(that);
616 }
617
618 bool operator<(const ActiveEdge& that) const {
619 SkASSERT(!this->lessThan(*this));
620 SkASSERT(!that.lessThan(that));
621 SkASSERT(!(this->lessThan(that) && that.lessThan(*this)));
622 return this->lessThan(that);
623 }
624
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400625 OffsetSegment fSegment;
Jim Van Vertha6316832018-07-24 09:30:37 -0400626 uint16_t fIndex0; // indices for previous and next vertex
627 uint16_t fIndex1;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400628};
629
Jim Van Verth00673692018-07-23 11:23:39 -0400630class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400631public:
Jim Van Vertha6316832018-07-24 09:30:37 -0400632 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400633 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
634 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400635 return false;
636 }
637
Jim Van Verth00673692018-07-23 11:23:39 -0400638 Iterator& curr = result.first;
639 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
640 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400641 }
Jim Van Verth00673692018-07-23 11:23:39 -0400642 Iterator next = std::next(curr);
643 if (next != fEdgeTree.end() && curr->intersect(*next)) {
644 return false;
645 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400646
647 return true;
648 }
649
Jim Van Verth00673692018-07-23 11:23:39 -0400650 bool remove(const ActiveEdge& edge) {
651 auto element = fEdgeTree.find(edge);
652 // this better not happen
653 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400654 return false;
655 }
Jim Van Verth00673692018-07-23 11:23:39 -0400656 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
657 return false;
658 }
659 Iterator next = std::next(element);
660 if (next != fEdgeTree.end() && element->intersect(*next)) {
661 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400662 }
663
Jim Van Verth00673692018-07-23 11:23:39 -0400664 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400665 return true;
666 }
667
668private:
Jim Van Verth00673692018-07-23 11:23:39 -0400669 std::set<ActiveEdge> fEdgeTree;
670 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400671};
672
673// Here we implement a sweep line algorithm to determine whether the provided points
674// represent a simple polygon, i.e., the polygon is non-self-intersecting.
675// We first insert the vertices into a priority queue sorting horizontally from left to right.
676// Then as we pop the vertices from the queue we generate events which indicate that an edge
677// should be added or removed from an edge list. If any intersections are detected in the edge
678// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400679bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400680 if (polygonSize < 3) {
681 return false;
682 }
683
Jim Van Vertha6316832018-07-24 09:30:37 -0400684 // need to be able to represent all the vertices in the 16-bit indices
685 if (polygonSize >= (1 << 16)) {
686 return false;
687 }
688
Jim Van Verth00673692018-07-23 11:23:39 -0400689 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400690 for (int i = 0; i < polygonSize; ++i) {
691 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400692 if (!polygon[i].isFinite()) {
693 return false;
694 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400695 newVertex.fPosition = polygon[i];
696 newVertex.fIndex = i;
697 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
698 newVertex.fNextIndex = (i + 1) % polygonSize;
699 newVertex.fFlags = 0;
700 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
701 newVertex.fFlags |= kPrevLeft_VertexFlag;
702 }
703 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
704 newVertex.fFlags |= kNextLeft_VertexFlag;
705 }
706 vertexQueue.insert(newVertex);
707 }
708
709 // pop each vertex from the queue and generate events depending on
710 // where it lies relative to its neighboring edges
Jim Van Verth00673692018-07-23 11:23:39 -0400711 ActiveEdgeList sweepLine;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400712 while (vertexQueue.count() > 0) {
713 const Vertex& v = vertexQueue.peek();
714
715 // check edge to previous vertex
716 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400717 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400718 if (!sweepLine.remove(edge)) {
719 break;
720 }
721 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400722 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400723 break;
724 }
725 }
726
727 // check edge to next vertex
728 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400729 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400730 if (!sweepLine.remove(edge)) {
731 break;
732 }
733 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400734 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400735 break;
736 }
737 }
738
739 vertexQueue.pop();
740 }
741
742 return (vertexQueue.count() == 0);
743}
744
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400745///////////////////////////////////////////////////////////////////////////////////////////
746
Jim Van Verth00673692018-07-23 11:23:39 -0400747// helper function for SkOffsetSimplePolygon
748static void setup_offset_edge(OffsetEdge* currEdge,
749 const SkPoint& endpoint0, const SkPoint& endpoint1,
750 int startIndex, int endIndex) {
751 currEdge->fInset.fP0 = endpoint0;
Jim Van Vertha6316832018-07-24 09:30:37 -0400752 currEdge->fInset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -0400753 currEdge->init(startIndex, endIndex);
754}
755
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400756bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400757 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
758 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400759 if (inputPolygonSize < 3) {
760 return false;
761 }
762
Jim Van Vertha6316832018-07-24 09:30:37 -0400763 // need to be able to represent all the vertices in the 16-bit indices
764 if (inputPolygonSize >= (1 << 16)) {
765 return false;
766 }
767
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400768 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400769 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400770 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400771 return false;
772 }
773
Jim Van Verthbdde4282018-06-14 09:09:18 -0400774 // build normals
775 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
776 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
777 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400778 if (!SkScalarIsFinite(currOffset)) {
779 return false;
780 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400781 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400782 if (!inputPolygonVerts[curr].isFinite()) {
783 return false;
784 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400785 int next = (curr + 1) % inputPolygonSize;
786 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400787 if (!SkScalarIsFinite(nextOffset)) {
788 return false;
789 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400790 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
791 currOffset, nextOffset, winding,
792 &normal0[curr], &normal1[next])) {
793 return false;
794 }
795 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400796 }
797
798 // build initial offset edge list
Jim Van Verth00673692018-07-23 11:23:39 -0400799 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400800 int prevIndex = inputPolygonSize - 1;
801 int currIndex = 0;
802 int nextIndex = 1;
803 while (currIndex < inputPolygonSize) {
804 int side = compute_side(inputPolygonVerts[prevIndex],
Jim Van Vertha6316832018-07-24 09:30:37 -0400805 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400806 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400807 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400808 // if reflex point, fill in curve
809 if (side*winding*offset < 0) {
810 SkScalar rotSin, rotCos;
811 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400812 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400813 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
814 &rotSin, &rotCos, &numSteps)) {
815 return false;
816 }
Jim Van Verth00673692018-07-23 11:23:39 -0400817 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400818 for (int i = 0; i < numSteps - 1; ++i) {
819 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
820 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400821 setup_offset_edge(currEdge,
822 inputPolygonVerts[currIndex] + prevNormal,
823 inputPolygonVerts[currIndex] + currNormal,
824 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400825 prevNormal = currNormal;
Jim Van Verth00673692018-07-23 11:23:39 -0400826 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400827 }
Jim Van Verth00673692018-07-23 11:23:39 -0400828 setup_offset_edge(currEdge,
829 inputPolygonVerts[currIndex] + prevNormal,
830 inputPolygonVerts[currIndex] + normal0[currIndex],
831 currIndex, currIndex);
832 ++currEdge;
833
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400834 }
835
836 // Add the edge
Jim Van Verth00673692018-07-23 11:23:39 -0400837 auto edge = edgeData.push_back_n(1);
838 setup_offset_edge(edge,
839 inputPolygonVerts[currIndex] + normal0[currIndex],
840 inputPolygonVerts[nextIndex] + normal1[nextIndex],
841 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400842
843 prevIndex = currIndex;
844 currIndex++;
845 nextIndex = (nextIndex + 1) % inputPolygonSize;
846 }
847
Jim Van Verth00673692018-07-23 11:23:39 -0400848 // build linked list
849 // we have to do this as a post-process step because we might have reallocated
850 // the array when adding fans for reflex verts
851 prevIndex = edgeData.count()-1;
852 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
853 int nextIndex = (currIndex + 1) % edgeData.count();
854 edgeData[currIndex].fPrev = &edgeData[prevIndex];
855 edgeData[currIndex].fNext = &edgeData[nextIndex];
856 }
857
858 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400859 int edgeDataSize = edgeData.count();
Jim Van Verth00673692018-07-23 11:23:39 -0400860 auto head = &edgeData[0];
861 auto currEdge = head;
862 auto prevEdge = currEdge->fPrev;
863 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400864 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400865 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400866 ++iterations;
867 // we should check each edge against each other edge at most once
868 if (iterations > edgeDataSize*edgeDataSize) {
869 return false;
870 }
871
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400872 SkScalar s, t;
873 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -0400874 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400875 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400876 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400877 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400878 remove_node(prevEdge, &head);
879 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400880 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400881 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400882 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400883 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400884 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400885 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400886 1.0e-6f)) {
887 break;
888 } else {
889 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400890 currEdge->fIntersection = intersection;
891 currEdge->fTValue = t;
892 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400893
894 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400895 prevEdge = currEdge;
896 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400897 }
898 } else {
899 // If there is no intersection, we want to minimize the distance between
900 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400901 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
902 OffsetEdge* currNextEdge = currEdge->fNext;
903 SkScalar dist0 = compute_crossing_distance(currEdge->fInset,
904 prevPrevEdge->fInset);
905 SkScalar dist1 = compute_crossing_distance(prevEdge->fInset,
906 currNextEdge->fInset);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400907 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400908 remove_node(prevEdge, &head);
909 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400910 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400911 remove_node(currEdge, &head);
912 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400913 }
Jim Van Verth00673692018-07-23 11:23:39 -0400914 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400915 }
916 }
917
918 // store all the valid intersections that aren't nearly coincident
919 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400920 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400921 if (head) {
922 static constexpr SkScalar kCleanupTolerance = 0.01f;
923 if (offsetVertexCount >= 0) {
924 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +0000925 }
Jim Van Verth00673692018-07-23 11:23:39 -0400926 int currIndex = 0;
927 OffsetEdge* currEdge = head;
928 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +0000929 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -0400930 *polygonIndices->push() = currEdge->fIndex;
931 }
932 currEdge = currEdge->fNext;
933 while (currEdge != head) {
934 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
935 (*offsetPolygon)[currIndex],
936 kCleanupTolerance)) {
937 *offsetPolygon->push() = currEdge->fIntersection;
938 if (polygonIndices) {
939 *polygonIndices->push() = currEdge->fIndex;
940 }
941 currIndex++;
942 }
943 currEdge = currEdge->fNext;
944 }
945 // make sure the first and last points aren't coincident
946 if (currIndex >= 1 &&
947 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
948 kCleanupTolerance)) {
949 offsetPolygon->pop();
950 if (polygonIndices) {
951 polygonIndices->pop();
952 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400953 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400954 }
955
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400956 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400957 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400958
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400959 return (winding*offsetWinding > 0 &&
960 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400961}
962
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400963//////////////////////////////////////////////////////////////////////////////////////////
964
965struct TriangulationVertex {
966 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
967
968 enum class VertexType { kConvex, kReflex };
969
970 SkPoint fPosition;
971 VertexType fVertexType;
972 uint16_t fIndex;
973 uint16_t fPrevIndex;
974 uint16_t fNextIndex;
975};
976
977// test to see if point p is in triangle p0p1p2.
978// for now assuming strictly inside -- if on the edge it's outside
979static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
980 const SkPoint& p) {
981 SkVector v0 = p1 - p0;
982 SkVector v1 = p2 - p1;
983 SkScalar n = v0.cross(v1);
984
985 SkVector w0 = p - p0;
986 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
987 return false;
988 }
989
990 SkVector w1 = p - p1;
991 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
992 return false;
993 }
994
995 SkVector v2 = p0 - p2;
996 SkVector w2 = p - p2;
997 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
998 return false;
999 }
1000
1001 return true;
1002}
1003
1004// Data structure to track reflex vertices and check whether any are inside a given triangle
1005class ReflexHash {
1006public:
1007 void add(TriangulationVertex* v) {
1008 fReflexList.addToTail(v);
1009 }
1010
1011 void remove(TriangulationVertex* v) {
1012 fReflexList.remove(v);
1013 }
1014
Jim Van Verth061cc212018-07-11 14:09:09 -04001015 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1016 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001017 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1018 reflexIter != fReflexList.end(); ++reflexIter) {
1019 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001020 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1021 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001022 return true;
1023 }
1024 }
1025
1026 return false;
1027 }
1028
1029private:
1030 // TODO: switch to an actual spatial hash
1031 SkTInternalLList<TriangulationVertex> fReflexList;
1032};
1033
1034// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1035static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1036 int winding, ReflexHash* reflexHash,
1037 SkTInternalLList<TriangulationVertex>* convexList) {
1038 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1039 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1040 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001041 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001042 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1043 reflexHash->remove(p);
1044 p->fPrev = p->fNext = nullptr;
1045 convexList->addToTail(p);
1046 }
1047 }
1048}
1049
1050bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1051 SkTDArray<uint16_t>* triangleIndices) {
1052 if (polygonSize < 3) {
1053 return false;
1054 }
1055 // need to be able to represent all the vertices in the 16-bit indices
1056 if (polygonSize >= (1 << 16)) {
1057 return false;
1058 }
1059
1060 // get winding direction
1061 // TODO: we do this for all the polygon routines -- might be better to have the client
1062 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001063 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001064 if (0 == winding) {
1065 return false;
1066 }
1067
1068 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1069 // TODO: possibly sort the convexList in some way to get better triangles
1070 SkTInternalLList<TriangulationVertex> convexList;
1071 ReflexHash reflexHash;
1072 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1073 int prevIndex = polygonSize - 1;
1074 int currIndex = 0;
1075 int nextIndex = 1;
1076 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1077 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1078 for (int i = 0; i < polygonSize; ++i) {
1079 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1080 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1081 triangulationVertices[currIndex].fIndex = currIndex;
1082 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1083 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001084 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001085 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1086 convexList.addToTail(&triangulationVertices[currIndex]);
1087 } else {
1088 // We treat near collinear vertices as reflex
1089 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1090 reflexHash.add(&triangulationVertices[currIndex]);
1091 }
1092
1093 prevIndex = currIndex;
1094 currIndex = nextIndex;
1095 nextIndex = (currIndex + 1) % polygonSize;
1096 v0 = v1;
1097 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1098 }
1099
1100 // The general concept: We are trying to find three neighboring vertices where
1101 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1102 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1103 // we have triangulated the entire polygon.
1104 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1105 // noting that only convex vertices can be potential ears, and we only need to check whether
1106 // any reflex vertices lie inside the ear.
1107 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1108 int vertexCount = polygonSize;
1109 while (vertexCount > 3) {
1110 bool success = false;
1111 TriangulationVertex* earVertex = nullptr;
1112 TriangulationVertex* p0 = nullptr;
1113 TriangulationVertex* p2 = nullptr;
1114 // find a convex vertex to clip
1115 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1116 convexIter != convexList.end(); ++convexIter) {
1117 earVertex = *convexIter;
1118 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1119
1120 p0 = &triangulationVertices[earVertex->fPrevIndex];
1121 p2 = &triangulationVertices[earVertex->fNextIndex];
1122
1123 // see if any reflex vertices are inside the ear
1124 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001125 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001126 if (failed) {
1127 continue;
1128 }
1129
1130 // found one we can clip
1131 success = true;
1132 break;
1133 }
1134 // If we can't find any ears to clip, this probably isn't a simple polygon
1135 if (!success) {
1136 return false;
1137 }
1138
1139 // add indices
1140 auto indices = triangleIndices->append(3);
1141 indices[0] = indexMap[p0->fIndex];
1142 indices[1] = indexMap[earVertex->fIndex];
1143 indices[2] = indexMap[p2->fIndex];
1144
1145 // clip the ear
1146 convexList.remove(earVertex);
1147 --vertexCount;
1148
1149 // reclassify reflex verts
1150 p0->fNextIndex = earVertex->fNextIndex;
1151 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1152
1153 p2->fPrevIndex = earVertex->fPrevIndex;
1154 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1155 }
1156
1157 // output indices
1158 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1159 vertexIter != convexList.end(); ++vertexIter) {
1160 TriangulationVertex* vertex = *vertexIter;
1161 *triangleIndices->push() = indexMap[vertex->fIndex];
1162 }
1163
1164 return true;
1165}