blob: b2847b941a77a4935c49239b21c99f1ac6510d97 [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 Verth6784ffa2018-07-03 16:12:39 -0400211bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
212 if (polygonSize < 3) {
213 return false;
Jim Van Verth0513f142017-03-24 14:28:57 -0400214 }
215
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400216 SkScalar lastArea = 0;
217 SkScalar lastPerpDot = 0;
Jim Van Verth0513f142017-03-24 14:28:57 -0400218
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400219 int prevIndex = polygonSize - 1;
220 int currIndex = 0;
221 int nextIndex = 1;
222 SkPoint origin = polygonVerts[0];
223 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
224 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
225 SkVector w0 = polygonVerts[currIndex] - origin;
226 SkVector w1 = polygonVerts[nextIndex] - origin;
227 for (int i = 0; i < polygonSize; ++i) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400228 if (!polygonVerts[i].isFinite()) {
229 return false;
230 }
231
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400232 // Check that winding direction is always the same (otherwise we have a reflex vertex)
Jim Van Verth0513f142017-03-24 14:28:57 -0400233 SkScalar perpDot = v0.cross(v1);
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400234 if (lastPerpDot*perpDot < 0) {
Jim Van Verth0513f142017-03-24 14:28:57 -0400235 return false;
236 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400237 if (0 != perpDot) {
238 lastPerpDot = perpDot;
239 }
240
241 // If the signed area ever flips it's concave
242 // TODO: see if we can verify convexity only with signed area
243 SkScalar quadArea = w0.cross(w1);
244 if (quadArea*lastArea < 0) {
245 return false;
246 }
247 if (0 != quadArea) {
248 lastArea = quadArea;
249 }
250
251 prevIndex = currIndex;
252 currIndex = nextIndex;
253 nextIndex = (currIndex + 1) % polygonSize;
254 v0 = v1;
255 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
256 w0 = w1;
257 w1 = polygonVerts[nextIndex] - origin;
Jim Van Verth0513f142017-03-24 14:28:57 -0400258 }
259
260 return true;
261}
Jim Van Verth0513f142017-03-24 14:28:57 -0400262
Jim Van Verth00673692018-07-23 11:23:39 -0400263struct OffsetEdge {
264 OffsetEdge* fPrev;
265 OffsetEdge* fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400266 OffsetSegment fInset;
267 SkPoint fIntersection;
268 SkScalar fTValue;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400269 uint16_t fIndex;
Jim Van Vertha6316832018-07-24 09:30:37 -0400270 uint16_t fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400271
Jim Van Verth00673692018-07-23 11:23:39 -0400272 void init(uint16_t start = 0, uint16_t end = 0) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400273 fIntersection = fInset.fP0;
274 fTValue = SK_ScalarMin;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400275 fIndex = start;
Jim Van Vertha6316832018-07-24 09:30:37 -0400276 fEnd = end;
277 }
278
279 // special intersection check that looks for endpoint intersection
280 bool checkIntersection(const OffsetEdge* that,
281 SkPoint* p, SkScalar* s, SkScalar* t) {
282 if (this->fEnd == that->fIndex) {
283 SkPoint p1 = this->fInset.fP0 + this->fInset.fV;
284 if (SkPointPriv::EqualsWithinTolerance(p1, that->fInset.fP0)) {
285 *p = p1;
286 *s = SK_Scalar1;
287 *t = 0;
288 return true;
289 }
290 }
291
292 return compute_intersection(this->fInset, that->fInset, p, s, t);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400293 }
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400294
295 // computes the line intersection and then the distance from that to this
296 SkScalar computeCrossingDistance(const OffsetEdge* that) {
297 const OffsetSegment& s0 = this->fInset;
298 const OffsetSegment& s1 = that->fInset;
299 const SkVector& v0 = s0.fV;
300 const SkVector& v1 = s1.fV;
301
302 SkScalar perpDot = v0.cross(v1);
303 if (SkScalarNearlyZero(perpDot)) {
304 // segments are parallel
305 return SK_ScalarMax;
306 }
307
308 SkVector d = s1.fP0 - s0.fP0;
309 SkScalar localS = d.cross(v1) / perpDot;
310 if (localS < 0) {
311 localS = -localS;
312 } else {
313 localS -= SK_Scalar1;
314 }
315
316 localS *= v0.length();
317
318 return localS;
319 }
320
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400321};
322
Jim Van Verth00673692018-07-23 11:23:39 -0400323static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
324 // remove from linked list
325 node->fPrev->fNext = node->fNext;
326 node->fNext->fPrev = node->fPrev;
327 if (node == *head) {
328 *head = (node->fNext == node) ? nullptr : node->fNext;
329 }
330}
331
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400332//////////////////////////////////////////////////////////////////////////////////
333
Brian Salomonab664fa2017-03-24 16:07:20 +0000334// The objective here is to inset all of the edges by the given distance, and then
335// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
336// we should only be making left-hand turns (for cw polygons, we use the winding
337// parameter to reverse this). We detect this by checking whether the second intersection
338// on an edge is closer to its tail than the first one.
339//
340// We might also have the case that there is no intersection between two neighboring inset edges.
341// In this case, one edge will lie to the right of the other and should be discarded along with
342// its previous intersection (if any).
343//
344// Note: the assumption is that inputPolygon is convex and has no coincident points.
345//
346bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400347 std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
Jim Van Verthda965502017-04-11 15:29:14 -0400348 SkTDArray<SkPoint>* insetPolygon) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000349 if (inputPolygonSize < 3) {
350 return false;
351 }
352
Jim Van Vertha6316832018-07-24 09:30:37 -0400353 // restrict this to match other routines
354 // practically we don't want anything bigger than this anyway
355 if (inputPolygonSize >= (1 << 16)) {
356 return false;
357 }
358
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400359 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400360 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Brian Salomonab664fa2017-03-24 16:07:20 +0000361 if (0 == winding) {
362 return false;
363 }
364
365 // set up
Jim Van Verth00673692018-07-23 11:23:39 -0400366 SkAutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
367 int prev = inputPolygonSize - 1;
368 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
369 int next = (curr + 1) % inputPolygonSize;
370 if (!inputPolygonVerts[curr].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400371 return false;
372 }
Jim Van Verthb55eb282017-07-18 14:13:45 -0400373 // check for convexity just to be sure
Jim Van Vertha6316832018-07-24 09:30:37 -0400374 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
Jim Van Verth00673692018-07-23 11:23:39 -0400375 inputPolygonVerts[next])*winding < 0) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400376 return false;
377 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400378 SkPoint p0, p1;
Jim Van Verth00673692018-07-23 11:23:39 -0400379 if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
380 insetDistanceFunc(inputPolygonVerts[curr]),
381 insetDistanceFunc(inputPolygonVerts[next]),
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400382 winding,
Jim Van Vertha6316832018-07-24 09:30:37 -0400383 &p0, &p1)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400384 return false;
385 }
Jim Van Vertha6316832018-07-24 09:30:37 -0400386 edgeData[curr].fPrev = &edgeData[prev];
387 edgeData[curr].fNext = &edgeData[next];
388 edgeData[curr].fInset.fP0 = p0;
389 edgeData[curr].fInset.fV = p1 - p0;
Jim Van Verth00673692018-07-23 11:23:39 -0400390 edgeData[curr].init();
Brian Salomonab664fa2017-03-24 16:07:20 +0000391 }
392
Jim Van Verth00673692018-07-23 11:23:39 -0400393 OffsetEdge* head = &edgeData[0];
394 OffsetEdge* currEdge = head;
395 OffsetEdge* prevEdge = currEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000396 int insetVertexCount = inputPolygonSize;
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400397 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400398 while (head && prevEdge != currEdge) {
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400399 ++iterations;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400400 // we should check each edge against each other edge at most once
Jim Van Verth796bc1d2018-06-19 15:00:28 -0400401 if (iterations > inputPolygonSize*inputPolygonSize) {
402 return false;
403 }
404
Brian Salomonab664fa2017-03-24 16:07:20 +0000405 SkScalar s, t;
406 SkPoint intersection;
Jim Van Verth00673692018-07-23 11:23:39 -0400407 if (compute_intersection(prevEdge->fInset, currEdge->fInset,
Brian Salomonab664fa2017-03-24 16:07:20 +0000408 &intersection, &s, &t)) {
409 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400410 if (s < prevEdge->fTValue) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000411 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400412 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000413 --insetVertexCount;
414 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400415 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000416 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400417 } else if (currEdge->fTValue > SK_ScalarMin &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500418 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400419 currEdge->fIntersection,
Brian Salomonab664fa2017-03-24 16:07:20 +0000420 1.0e-6f)) {
421 break;
422 } else {
423 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400424 currEdge->fIntersection = intersection;
425 currEdge->fTValue = t;
Brian Salomonab664fa2017-03-24 16:07:20 +0000426
427 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400428 prevEdge = currEdge;
429 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000430 }
431 } else {
432 // if prev to right side of curr
Jim Van Verth00673692018-07-23 11:23:39 -0400433 int side = winding*compute_side(currEdge->fInset.fP0,
Jim Van Vertha6316832018-07-24 09:30:37 -0400434 currEdge->fInset.fV,
435 prevEdge->fInset.fP0);
436 if (side < 0 &&
437 side == winding*compute_side(currEdge->fInset.fP0,
438 currEdge->fInset.fV,
439 prevEdge->fInset.fP0 + prevEdge->fInset.fV)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000440 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400441 remove_node(prevEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000442 --insetVertexCount;
443 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400444 prevEdge = prevEdge->fPrev;
Brian Salomonab664fa2017-03-24 16:07:20 +0000445 } else {
446 // move to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400447 remove_node(currEdge, &head);
Brian Salomonab664fa2017-03-24 16:07:20 +0000448 --insetVertexCount;
Jim Van Verth00673692018-07-23 11:23:39 -0400449 currEdge = currEdge->fNext;
Brian Salomonab664fa2017-03-24 16:07:20 +0000450 }
451 }
452 }
453
Jim Van Verthda965502017-04-11 15:29:14 -0400454 // store all the valid intersections that aren't nearly coincident
455 // TODO: look at the main algorithm and see if we can detect these better
Brian Salomonab664fa2017-03-24 16:07:20 +0000456 insetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400457 if (head) {
458 static constexpr SkScalar kCleanupTolerance = 0.01f;
459 if (insetVertexCount >= 0) {
460 insetPolygon->setReserve(insetVertexCount);
Brian Salomonab664fa2017-03-24 16:07:20 +0000461 }
Jim Van Verth00673692018-07-23 11:23:39 -0400462 int currIndex = 0;
463 OffsetEdge* currEdge = head;
464 *insetPolygon->push() = currEdge->fIntersection;
465 currEdge = currEdge->fNext;
466 while (currEdge != head) {
467 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
468 (*insetPolygon)[currIndex],
469 kCleanupTolerance)) {
470 *insetPolygon->push() = currEdge->fIntersection;
471 currIndex++;
472 }
473 currEdge = currEdge->fNext;
474 }
475 // make sure the first and last points aren't coincident
476 if (currIndex >= 1 &&
477 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
478 kCleanupTolerance)) {
479 insetPolygon->pop();
480 }
Jim Van Verthda965502017-04-11 15:29:14 -0400481 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000482
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400483 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000484}
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400485
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400486///////////////////////////////////////////////////////////////////////////////////////////
487
488// compute the number of points needed for a circular join when offsetting a reflex vertex
Jim Van Verth061cc212018-07-11 14:09:09 -0400489bool SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400490 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400491 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
492
493 SkScalar rCos = v1.dot(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400494 if (!SkScalarIsFinite(rCos)) {
495 return false;
496 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400497 SkScalar rSin = v1.cross(v2);
Jim Van Verth061cc212018-07-11 14:09:09 -0400498 if (!SkScalarIsFinite(rSin)) {
499 return false;
500 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400501 SkScalar theta = SkScalarATan2(rSin, rCos);
502
Jim Van Verth206dbe82018-07-23 11:48:31 -0400503 SkScalar floatSteps = SkScalarAbs(r*theta*kRecipPixelsPerArcSegment);
504 // limit the number of steps to at most max uint16_t (that's all we can index)
505 // knock one value off the top to account for rounding
506 if (floatSteps >= (1 << 16)-1) {
507 return false;
508 }
509 int steps = SkScalarRoundToInt(floatSteps);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400510
Jim Van Verth061cc212018-07-11 14:09:09 -0400511 SkScalar dTheta = steps > 0 ? theta / steps : 0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400512 *rotSin = SkScalarSinCos(dTheta, rotCos);
513 *n = steps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400514 return true;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400515}
516
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400517///////////////////////////////////////////////////////////////////////////////////////////
518
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400519// a point is "left" to another if its x coordinate is less, or if equal, its y coordinate
520static bool left(const SkPoint& p0, const SkPoint& p1) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400521 return p0.fX < p1.fX || (!(p0.fX > p1.fX) && p0.fY < p1.fY);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400522}
523
524struct Vertex {
525 static bool Left(const Vertex& qv0, const Vertex& qv1) {
526 return left(qv0.fPosition, qv1.fPosition);
527 }
Jim Van Verth00673692018-07-23 11:23:39 -0400528
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400529 // packed to fit into 16 bytes (one cache line)
530 SkPoint fPosition;
531 uint16_t fIndex; // index in unsorted polygon
532 uint16_t fPrevIndex; // indices for previous and next vertex in unsorted polygon
533 uint16_t fNextIndex;
534 uint16_t fFlags;
535};
536
537enum VertexFlags {
538 kPrevLeft_VertexFlag = 0x1,
539 kNextLeft_VertexFlag = 0x2,
540};
541
Jim Van Verth00673692018-07-23 11:23:39 -0400542struct ActiveEdge {
Jim Van Vertha6316832018-07-24 09:30:37 -0400543 ActiveEdge(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1)
544 : fSegment({p0, p1-p0})
Jim Van Verth00673692018-07-23 11:23:39 -0400545 , fIndex0(index0)
546 , fIndex1(index1) {}
547
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400548 // returns true if "this" is above "that"
Jim Van Verth00673692018-07-23 11:23:39 -0400549 bool above(const ActiveEdge& that) const {
550 SkASSERT(this->fSegment.fP0.fX <= that.fSegment.fP0.fX);
551 const SkScalar kTolerance = SK_ScalarNearlyZero * SK_ScalarNearlyZero;
Jim Van Vertha6316832018-07-24 09:30:37 -0400552 const SkVector& u = this->fSegment.fV;
553 SkVector dv = that.fSegment.fP0 - this->fSegment.fP0;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400554 // The idea here is that if the vector between the origins of the two segments (dv)
555 // rotates counterclockwise up to the vector representing the "this" segment (u),
556 // 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 -0400557 if (this->fIndex0 != that.fIndex0) {
Jim Van Verth00673692018-07-23 11:23:39 -0400558 SkScalar cross = dv.cross(u);
559 if (cross > kTolerance) {
560 return true;
561 } else if (cross < -kTolerance) {
562 return false;
563 }
564 } else if (this->fIndex1 == that.fIndex1) {
565 // they're the same edge
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400566 return false;
567 }
Jim Van Verth00673692018-07-23 11:23:39 -0400568 // At this point either the two origins are nearly equal or the origin of "that"
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400569 // lies on dv. So then we try the same for the vector from the tail of "this"
570 // to the head of "that". Again, ccw means "this" is above "that".
Jim Van Vertha6316832018-07-24 09:30:37 -0400571 // dv = that.P1 - this.P0
572 // = that.fP0 + that.fV - this.fP0
573 // = that.fP0 - this.fP0 + that.fV
574 // = old_dv + that.fV
575 dv += that.fSegment.fV;
Jim Van Verth00673692018-07-23 11:23:39 -0400576 SkScalar cross = dv.cross(u);
577 if (cross > kTolerance) {
578 return true;
579 } else if (cross < -kTolerance) {
580 return false;
581 }
582 // If the previous check fails, the two segments are nearly collinear
583 // First check y-coord of first endpoints
584 if (this->fSegment.fP0.fX < that.fSegment.fP0.fX) {
585 return (this->fSegment.fP0.fY >= that.fSegment.fP0.fY);
586 } else if (this->fSegment.fP0.fY > that.fSegment.fP0.fY) {
587 return true;
588 } else if (this->fSegment.fP0.fY < that.fSegment.fP0.fY) {
589 return false;
590 }
591 // The first endpoints are the same, so check the other endpoint
Jim Van Vertha6316832018-07-24 09:30:37 -0400592 SkPoint thisP1 = this->fSegment.fP0 + this->fSegment.fV;
593 SkPoint thatP1 = that.fSegment.fP0 + that.fSegment.fV;
594 if (thisP1.fX < thatP1.fX) {
595 return (thisP1.fY >= thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400596 } else {
Jim Van Vertha6316832018-07-24 09:30:37 -0400597 return (thisP1.fY > thatP1.fY);
Jim Van Verth00673692018-07-23 11:23:39 -0400598 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400599 }
600
Jim Van Verth00673692018-07-23 11:23:39 -0400601 bool intersect(const ActiveEdge& that) const {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400602 SkPoint intersection;
603 SkScalar s, t;
604 // check first to see if these edges are neighbors in the polygon
605 if (this->fIndex0 == that.fIndex0 || this->fIndex1 == that.fIndex0 ||
606 this->fIndex0 == that.fIndex1 || this->fIndex1 == that.fIndex1) {
607 return false;
608 }
609 return compute_intersection(this->fSegment, that.fSegment, &intersection, &s, &t);
610 }
611
Jim Van Verth00673692018-07-23 11:23:39 -0400612 bool lessThan(const ActiveEdge& that) const {
613 if (this->fSegment.fP0.fX > that.fSegment.fP0.fX ||
614 (this->fSegment.fP0.fX == that.fSegment.fP0.fX &&
615 this->fSegment.fP0.fY < that.fSegment.fP0.fY)) {
616 return !that.above(*this);
617 }
618 return this->above(that);
619 }
620
621 bool operator<(const ActiveEdge& that) const {
622 SkASSERT(!this->lessThan(*this));
623 SkASSERT(!that.lessThan(that));
624 SkASSERT(!(this->lessThan(that) && that.lessThan(*this)));
625 return this->lessThan(that);
626 }
627
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400628 OffsetSegment fSegment;
Jim Van Vertha6316832018-07-24 09:30:37 -0400629 uint16_t fIndex0; // indices for previous and next vertex
630 uint16_t fIndex1;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400631};
632
Jim Van Verth00673692018-07-23 11:23:39 -0400633class ActiveEdgeList {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400634public:
Jim Van Vertha6316832018-07-24 09:30:37 -0400635 bool insert(const SkPoint& p0, const SkPoint& p1, uint16_t index0, uint16_t index1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400636 std::pair<Iterator, bool> result = fEdgeTree.emplace(p0, p1, index0, index1);
637 if (!result.second) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400638 return false;
639 }
640
Jim Van Verth00673692018-07-23 11:23:39 -0400641 Iterator& curr = result.first;
642 if (curr != fEdgeTree.begin() && curr->intersect(*std::prev(curr))) {
643 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400644 }
Jim Van Verth00673692018-07-23 11:23:39 -0400645 Iterator next = std::next(curr);
646 if (next != fEdgeTree.end() && curr->intersect(*next)) {
647 return false;
648 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400649
650 return true;
651 }
652
Jim Van Verth00673692018-07-23 11:23:39 -0400653 bool remove(const ActiveEdge& edge) {
654 auto element = fEdgeTree.find(edge);
655 // this better not happen
656 if (element == fEdgeTree.end()) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400657 return false;
658 }
Jim Van Verth00673692018-07-23 11:23:39 -0400659 if (element != fEdgeTree.begin() && element->intersect(*std::prev(element))) {
660 return false;
661 }
662 Iterator next = std::next(element);
663 if (next != fEdgeTree.end() && element->intersect(*next)) {
664 return false;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400665 }
666
Jim Van Verth00673692018-07-23 11:23:39 -0400667 fEdgeTree.erase(element);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400668 return true;
669 }
670
671private:
Jim Van Verth00673692018-07-23 11:23:39 -0400672 std::set<ActiveEdge> fEdgeTree;
673 typedef std::set<ActiveEdge>::iterator Iterator;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400674};
675
676// Here we implement a sweep line algorithm to determine whether the provided points
677// represent a simple polygon, i.e., the polygon is non-self-intersecting.
678// We first insert the vertices into a priority queue sorting horizontally from left to right.
679// Then as we pop the vertices from the queue we generate events which indicate that an edge
680// should be added or removed from an edge list. If any intersections are detected in the edge
681// list, then we know the polygon is self-intersecting and hence not simple.
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400682bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400683 if (polygonSize < 3) {
684 return false;
685 }
686
Jim Van Vertha6316832018-07-24 09:30:37 -0400687 // need to be able to represent all the vertices in the 16-bit indices
688 if (polygonSize >= (1 << 16)) {
689 return false;
690 }
691
Jim Van Verth00673692018-07-23 11:23:39 -0400692 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400693 for (int i = 0; i < polygonSize; ++i) {
694 Vertex newVertex;
Jim Van Verth061cc212018-07-11 14:09:09 -0400695 if (!polygon[i].isFinite()) {
696 return false;
697 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400698 newVertex.fPosition = polygon[i];
699 newVertex.fIndex = i;
700 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
701 newVertex.fNextIndex = (i + 1) % polygonSize;
702 newVertex.fFlags = 0;
703 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
704 newVertex.fFlags |= kPrevLeft_VertexFlag;
705 }
706 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
707 newVertex.fFlags |= kNextLeft_VertexFlag;
708 }
709 vertexQueue.insert(newVertex);
710 }
711
712 // pop each vertex from the queue and generate events depending on
713 // where it lies relative to its neighboring edges
Jim Van Verth00673692018-07-23 11:23:39 -0400714 ActiveEdgeList sweepLine;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400715 while (vertexQueue.count() > 0) {
716 const Vertex& v = vertexQueue.peek();
717
718 // check edge to previous vertex
719 if (v.fFlags & kPrevLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400720 ActiveEdge edge(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400721 if (!sweepLine.remove(edge)) {
722 break;
723 }
724 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400725 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400726 break;
727 }
728 }
729
730 // check edge to next vertex
731 if (v.fFlags & kNextLeft_VertexFlag) {
Jim Van Verth00673692018-07-23 11:23:39 -0400732 ActiveEdge edge(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400733 if (!sweepLine.remove(edge)) {
734 break;
735 }
736 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400737 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400738 break;
739 }
740 }
741
742 vertexQueue.pop();
743 }
744
745 return (vertexQueue.count() == 0);
746}
747
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400748///////////////////////////////////////////////////////////////////////////////////////////
749
Jim Van Verth00673692018-07-23 11:23:39 -0400750// helper function for SkOffsetSimplePolygon
751static void setup_offset_edge(OffsetEdge* currEdge,
752 const SkPoint& endpoint0, const SkPoint& endpoint1,
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400753 uint16_t startIndex, uint16_t endIndex) {
Jim Van Verth00673692018-07-23 11:23:39 -0400754 currEdge->fInset.fP0 = endpoint0;
Jim Van Vertha6316832018-07-24 09:30:37 -0400755 currEdge->fInset.fV = endpoint1 - endpoint0;
Jim Van Verth00673692018-07-23 11:23:39 -0400756 currEdge->init(startIndex, endIndex);
757}
758
Jim Van Verth98d33752018-08-03 15:59:46 -0400759static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkScalar offset,
760 uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex) {
761 int side = compute_side(inputPolygonVerts[prevIndex],
762 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
763 inputPolygonVerts[nextIndex]);
764 // if reflex point, we need to add extra edges
765 return (side*winding*offset < 0);
766}
767
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400768bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400769 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
770 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400771 if (inputPolygonSize < 3) {
772 return false;
773 }
774
Jim Van Vertha6316832018-07-24 09:30:37 -0400775 // need to be able to represent all the vertices in the 16-bit indices
776 if (inputPolygonSize >= (1 << 16)) {
777 return false;
778 }
779
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400780 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400781 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400782 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400783 return false;
784 }
785
Jim Van Verthbdde4282018-06-14 09:09:18 -0400786 // build normals
787 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
788 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
Jim Van Verth98d33752018-08-03 15:59:46 -0400789 SkScalar baseOffset = offsetDistanceFunc(inputPolygonVerts[0]);
790 SkScalar currOffset = baseOffset;
Jim Van Verth061cc212018-07-11 14:09:09 -0400791 if (!SkScalarIsFinite(currOffset)) {
792 return false;
793 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400794 int numEdges = 0;
795 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
796 currIndex < inputPolygonSize;
797 prevIndex = currIndex, ++currIndex) {
798 if (!inputPolygonVerts[currIndex].isFinite()) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400799 return false;
800 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400801 int nextIndex = (currIndex + 1) % inputPolygonSize;
802 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[nextIndex]);
803 // all offsets should either inset or outset
804 if (currOffset*nextOffset < 0) {
805 return false;
806 }
Jim Van Verth061cc212018-07-11 14:09:09 -0400807 if (!SkScalarIsFinite(nextOffset)) {
808 return false;
809 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400810 if (!compute_offset_vectors(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
Jim Van Verthbdde4282018-06-14 09:09:18 -0400811 currOffset, nextOffset, winding,
Jim Van Verth98d33752018-08-03 15:59:46 -0400812 &normal0[currIndex], &normal1[nextIndex])) {
Jim Van Verthbdde4282018-06-14 09:09:18 -0400813 return false;
814 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400815 if (currIndex > 0) {
816 // if reflex point, we need to add extra edges
817 if (is_reflex_vertex(inputPolygonVerts, winding, baseOffset,
818 prevIndex, currIndex, nextIndex)) {
819 SkScalar rotSin, rotCos;
820 int numSteps;
821 if (!SkComputeRadialSteps(normal1[currIndex], normal0[currIndex],
822 normal0[currIndex].length(),
823 &rotSin, &rotCos, &numSteps)) {
824 return false;
825 }
826 numEdges += SkTMax(numSteps, 1);
827 }
828 }
829 numEdges++;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400830 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400831 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400832 // finish up the edge counting
833 if (is_reflex_vertex(inputPolygonVerts, winding, baseOffset, inputPolygonSize-1, 0, 1)) {
834 SkScalar rotSin, rotCos;
835 int numSteps;
836 if (!SkComputeRadialSteps(normal1[0], normal0[0], currOffset,
837 &rotSin, &rotCos, &numSteps)) {
838 return false;
839 }
840 numEdges += SkTMax(numSteps, 1);
841 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400842
843 // build initial offset edge list
Jim Van Verth98d33752018-08-03 15:59:46 -0400844 SkSTArray<64, OffsetEdge> edgeData(numEdges);
845 OffsetEdge* prevEdge = nullptr;
846 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
847 currIndex < inputPolygonSize;
848 prevIndex = currIndex, ++currIndex) {
849 int nextIndex = (currIndex + 1) % inputPolygonSize;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400850 // if reflex point, fill in curve
Jim Van Verth98d33752018-08-03 15:59:46 -0400851 if (is_reflex_vertex(inputPolygonVerts, winding, baseOffset,
852 prevIndex, currIndex, nextIndex)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400853 SkScalar rotSin, rotCos;
854 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400855 SkVector prevNormal = normal1[currIndex];
Jim Van Verth98d33752018-08-03 15:59:46 -0400856 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], normal0[currIndex].length(),
Jim Van Verth061cc212018-07-11 14:09:09 -0400857 &rotSin, &rotCos, &numSteps)) {
858 return false;
859 }
Jim Van Verth00673692018-07-23 11:23:39 -0400860 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400861 for (int i = 0; i < numSteps - 1; ++i) {
862 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
863 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400864 setup_offset_edge(currEdge,
865 inputPolygonVerts[currIndex] + prevNormal,
866 inputPolygonVerts[currIndex] + currNormal,
867 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400868 prevNormal = currNormal;
Jim Van Verth98d33752018-08-03 15:59:46 -0400869 currEdge->fPrev = prevEdge;
870 if (prevEdge) {
871 prevEdge->fNext = currEdge;
872 }
873 prevEdge = currEdge;
Jim Van Verth00673692018-07-23 11:23:39 -0400874 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400875 }
Jim Van Verth00673692018-07-23 11:23:39 -0400876 setup_offset_edge(currEdge,
877 inputPolygonVerts[currIndex] + prevNormal,
878 inputPolygonVerts[currIndex] + normal0[currIndex],
879 currIndex, currIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -0400880 currEdge->fPrev = prevEdge;
881 if (prevEdge) {
882 prevEdge->fNext = currEdge;
883 }
884 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400885 }
886
887 // Add the edge
Jim Van Verth98d33752018-08-03 15:59:46 -0400888 auto currEdge = edgeData.push_back_n(1);
889 setup_offset_edge(currEdge,
Jim Van Verth00673692018-07-23 11:23:39 -0400890 inputPolygonVerts[currIndex] + normal0[currIndex],
891 inputPolygonVerts[nextIndex] + normal1[nextIndex],
892 currIndex, nextIndex);
Jim Van Verth98d33752018-08-03 15:59:46 -0400893 currEdge->fPrev = prevEdge;
894 if (prevEdge) {
895 prevEdge->fNext = currEdge;
896 }
897 prevEdge = currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400898 }
Jim Van Verth98d33752018-08-03 15:59:46 -0400899 // close up the linked list
900 SkASSERT(prevEdge);
901 prevEdge->fNext = &edgeData[0];
902 edgeData[0].fPrev = prevEdge;
Jim Van Verth00673692018-07-23 11:23:39 -0400903
904 // now clip edges
Jim Van Verth98d33752018-08-03 15:59:46 -0400905 SkASSERT(edgeData.count() == numEdges);
Jim Van Verth00673692018-07-23 11:23:39 -0400906 auto head = &edgeData[0];
907 auto currEdge = head;
Jim Van Verth98d33752018-08-03 15:59:46 -0400908 int offsetVertexCount = numEdges;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400909 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400910 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400911 ++iterations;
912 // we should check each edge against each other edge at most once
Jim Van Verth98d33752018-08-03 15:59:46 -0400913 if (iterations > numEdges*numEdges) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400914 return false;
915 }
916
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400917 SkScalar s, t;
918 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -0400919 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400920 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400921 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400922 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400923 remove_node(prevEdge, &head);
924 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400925 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400926 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400927 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400928 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400929 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400930 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400931 1.0e-6f)) {
932 break;
933 } else {
934 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400935 currEdge->fIntersection = intersection;
936 currEdge->fTValue = t;
937 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400938
939 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400940 prevEdge = currEdge;
941 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400942 }
943 } else {
944 // If there is no intersection, we want to minimize the distance between
945 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400946 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
947 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400948 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
949 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
950 // if both lead to direct collision
951 if (dist0 < 0 && dist1 < 0) {
952 // check first to see if either represent parts of one contour
953 SkPoint p1 = prevPrevEdge->fInset.fP0 + prevPrevEdge->fInset.fV;
954 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
955 prevEdge->fInset.fP0);
956 p1 = currEdge->fInset.fP0 + currEdge->fInset.fV;
957 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
958 currNextEdge->fInset.fP0);
959
960 // want to step along contour to find intersections rather than jump to new one
961 if (currSameContour && !prevSameContour) {
962 remove_node(currEdge, &head);
963 currEdge = currNextEdge;
964 --offsetVertexCount;
965 continue;
966 } else if (prevSameContour && !currSameContour) {
967 remove_node(prevEdge, &head);
968 prevEdge = prevPrevEdge;
969 --offsetVertexCount;
970 continue;
971 }
972 }
973
974 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400975 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400976 remove_node(prevEdge, &head);
977 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400978 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400979 remove_node(currEdge, &head);
980 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400981 }
Jim Van Verth00673692018-07-23 11:23:39 -0400982 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400983 }
984 }
985
986 // store all the valid intersections that aren't nearly coincident
987 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400988 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400989 if (head) {
990 static constexpr SkScalar kCleanupTolerance = 0.01f;
991 if (offsetVertexCount >= 0) {
992 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +0000993 }
Jim Van Verth00673692018-07-23 11:23:39 -0400994 int currIndex = 0;
995 OffsetEdge* currEdge = head;
996 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +0000997 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -0400998 *polygonIndices->push() = currEdge->fIndex;
999 }
1000 currEdge = currEdge->fNext;
1001 while (currEdge != head) {
1002 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1003 (*offsetPolygon)[currIndex],
1004 kCleanupTolerance)) {
1005 *offsetPolygon->push() = currEdge->fIntersection;
1006 if (polygonIndices) {
1007 *polygonIndices->push() = currEdge->fIndex;
1008 }
1009 currIndex++;
1010 }
1011 currEdge = currEdge->fNext;
1012 }
1013 // make sure the first and last points aren't coincident
1014 if (currIndex >= 1 &&
1015 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1016 kCleanupTolerance)) {
1017 offsetPolygon->pop();
1018 if (polygonIndices) {
1019 polygonIndices->pop();
1020 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001021 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001022 }
1023
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001024 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001025 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001026
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001027 return (winding*offsetWinding > 0 &&
1028 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -04001029}
1030
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001031//////////////////////////////////////////////////////////////////////////////////////////
1032
1033struct TriangulationVertex {
1034 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
1035
1036 enum class VertexType { kConvex, kReflex };
1037
1038 SkPoint fPosition;
1039 VertexType fVertexType;
1040 uint16_t fIndex;
1041 uint16_t fPrevIndex;
1042 uint16_t fNextIndex;
1043};
1044
1045// test to see if point p is in triangle p0p1p2.
1046// for now assuming strictly inside -- if on the edge it's outside
1047static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1048 const SkPoint& p) {
1049 SkVector v0 = p1 - p0;
1050 SkVector v1 = p2 - p1;
1051 SkScalar n = v0.cross(v1);
1052
1053 SkVector w0 = p - p0;
1054 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1055 return false;
1056 }
1057
1058 SkVector w1 = p - p1;
1059 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1060 return false;
1061 }
1062
1063 SkVector v2 = p0 - p2;
1064 SkVector w2 = p - p2;
1065 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1066 return false;
1067 }
1068
1069 return true;
1070}
1071
1072// Data structure to track reflex vertices and check whether any are inside a given triangle
1073class ReflexHash {
1074public:
1075 void add(TriangulationVertex* v) {
1076 fReflexList.addToTail(v);
1077 }
1078
1079 void remove(TriangulationVertex* v) {
1080 fReflexList.remove(v);
1081 }
1082
Jim Van Verth061cc212018-07-11 14:09:09 -04001083 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1084 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001085 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1086 reflexIter != fReflexList.end(); ++reflexIter) {
1087 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001088 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1089 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001090 return true;
1091 }
1092 }
1093
1094 return false;
1095 }
1096
1097private:
1098 // TODO: switch to an actual spatial hash
1099 SkTInternalLList<TriangulationVertex> fReflexList;
1100};
1101
1102// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1103static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1104 int winding, ReflexHash* reflexHash,
1105 SkTInternalLList<TriangulationVertex>* convexList) {
1106 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1107 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1108 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001109 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001110 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1111 reflexHash->remove(p);
1112 p->fPrev = p->fNext = nullptr;
1113 convexList->addToTail(p);
1114 }
1115 }
1116}
1117
1118bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1119 SkTDArray<uint16_t>* triangleIndices) {
1120 if (polygonSize < 3) {
1121 return false;
1122 }
1123 // need to be able to represent all the vertices in the 16-bit indices
1124 if (polygonSize >= (1 << 16)) {
1125 return false;
1126 }
1127
1128 // get winding direction
1129 // TODO: we do this for all the polygon routines -- might be better to have the client
1130 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001131 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001132 if (0 == winding) {
1133 return false;
1134 }
1135
1136 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1137 // TODO: possibly sort the convexList in some way to get better triangles
1138 SkTInternalLList<TriangulationVertex> convexList;
1139 ReflexHash reflexHash;
1140 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1141 int prevIndex = polygonSize - 1;
1142 int currIndex = 0;
1143 int nextIndex = 1;
1144 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1145 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1146 for (int i = 0; i < polygonSize; ++i) {
1147 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1148 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1149 triangulationVertices[currIndex].fIndex = currIndex;
1150 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1151 triangulationVertices[currIndex].fNextIndex = nextIndex;
Jim Van Verth061cc212018-07-11 14:09:09 -04001152 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001153 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1154 convexList.addToTail(&triangulationVertices[currIndex]);
1155 } else {
1156 // We treat near collinear vertices as reflex
1157 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1158 reflexHash.add(&triangulationVertices[currIndex]);
1159 }
1160
1161 prevIndex = currIndex;
1162 currIndex = nextIndex;
1163 nextIndex = (currIndex + 1) % polygonSize;
1164 v0 = v1;
1165 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1166 }
1167
1168 // The general concept: We are trying to find three neighboring vertices where
1169 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1170 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1171 // we have triangulated the entire polygon.
1172 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1173 // noting that only convex vertices can be potential ears, and we only need to check whether
1174 // any reflex vertices lie inside the ear.
1175 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1176 int vertexCount = polygonSize;
1177 while (vertexCount > 3) {
1178 bool success = false;
1179 TriangulationVertex* earVertex = nullptr;
1180 TriangulationVertex* p0 = nullptr;
1181 TriangulationVertex* p2 = nullptr;
1182 // find a convex vertex to clip
1183 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1184 convexIter != convexList.end(); ++convexIter) {
1185 earVertex = *convexIter;
1186 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1187
1188 p0 = &triangulationVertices[earVertex->fPrevIndex];
1189 p2 = &triangulationVertices[earVertex->fNextIndex];
1190
1191 // see if any reflex vertices are inside the ear
1192 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001193 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001194 if (failed) {
1195 continue;
1196 }
1197
1198 // found one we can clip
1199 success = true;
1200 break;
1201 }
1202 // If we can't find any ears to clip, this probably isn't a simple polygon
1203 if (!success) {
1204 return false;
1205 }
1206
1207 // add indices
1208 auto indices = triangleIndices->append(3);
1209 indices[0] = indexMap[p0->fIndex];
1210 indices[1] = indexMap[earVertex->fIndex];
1211 indices[2] = indexMap[p2->fIndex];
1212
1213 // clip the ear
1214 convexList.remove(earVertex);
1215 --vertexCount;
1216
1217 // reclassify reflex verts
1218 p0->fNextIndex = earVertex->fNextIndex;
1219 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1220
1221 p2->fPrevIndex = earVertex->fPrevIndex;
1222 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1223 }
1224
1225 // output indices
1226 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1227 vertexIter != convexList.end(); ++vertexIter) {
1228 TriangulationVertex* vertex = *vertexIter;
1229 *triangleIndices->push() = indexMap[vertex->fIndex];
1230 }
1231
1232 return true;
1233}