blob: 3416345a7f3036f2cdc45a2115fbf53cd1b0d176 [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 Verth4db18ed2018-04-03 10:00:37 -0400759bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
Jim Van Verthbdde4282018-06-14 09:09:18 -0400760 std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
761 SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400762 if (inputPolygonSize < 3) {
763 return false;
764 }
765
Jim Van Vertha6316832018-07-24 09:30:37 -0400766 // need to be able to represent all the vertices in the 16-bit indices
767 if (inputPolygonSize >= (1 << 16)) {
768 return false;
769 }
770
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400771 // get winding direction
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400772 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400773 if (0 == winding) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400774 return false;
775 }
776
Jim Van Verthbdde4282018-06-14 09:09:18 -0400777 // build normals
778 SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
779 SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
780 SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400781 if (!SkScalarIsFinite(currOffset)) {
782 return false;
783 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400784 for (int curr = 0; curr < inputPolygonSize; ++curr) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400785 if (!inputPolygonVerts[curr].isFinite()) {
786 return false;
787 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400788 int next = (curr + 1) % inputPolygonSize;
789 SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[next]);
Jim Van Verth061cc212018-07-11 14:09:09 -0400790 if (!SkScalarIsFinite(nextOffset)) {
791 return false;
792 }
Jim Van Verthbdde4282018-06-14 09:09:18 -0400793 if (!compute_offset_vectors(inputPolygonVerts[curr], inputPolygonVerts[next],
794 currOffset, nextOffset, winding,
795 &normal0[curr], &normal1[next])) {
796 return false;
797 }
798 currOffset = nextOffset;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400799 }
800
801 // build initial offset edge list
Jim Van Verth00673692018-07-23 11:23:39 -0400802 SkSTArray<64, OffsetEdge> edgeData(inputPolygonSize);
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400803 uint16_t prevIndex = inputPolygonSize - 1;
804 uint16_t currIndex = 0;
805 uint16_t nextIndex = 1;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400806 while (currIndex < inputPolygonSize) {
807 int side = compute_side(inputPolygonVerts[prevIndex],
Jim Van Vertha6316832018-07-24 09:30:37 -0400808 inputPolygonVerts[currIndex] - inputPolygonVerts[prevIndex],
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400809 inputPolygonVerts[nextIndex]);
Jim Van Verthbdde4282018-06-14 09:09:18 -0400810 SkScalar offset = offsetDistanceFunc(inputPolygonVerts[currIndex]);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400811 // if reflex point, fill in curve
812 if (side*winding*offset < 0) {
813 SkScalar rotSin, rotCos;
814 int numSteps;
Jim Van Verthbdde4282018-06-14 09:09:18 -0400815 SkVector prevNormal = normal1[currIndex];
Jim Van Verth061cc212018-07-11 14:09:09 -0400816 if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
817 &rotSin, &rotCos, &numSteps)) {
818 return false;
819 }
Jim Van Verth00673692018-07-23 11:23:39 -0400820 auto currEdge = edgeData.push_back_n(SkTMax(numSteps, 1));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400821 for (int i = 0; i < numSteps - 1; ++i) {
822 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
823 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
Jim Van Verth00673692018-07-23 11:23:39 -0400824 setup_offset_edge(currEdge,
825 inputPolygonVerts[currIndex] + prevNormal,
826 inputPolygonVerts[currIndex] + currNormal,
827 currIndex, currIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400828 prevNormal = currNormal;
Jim Van Verth00673692018-07-23 11:23:39 -0400829 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400830 }
Jim Van Verth00673692018-07-23 11:23:39 -0400831 setup_offset_edge(currEdge,
832 inputPolygonVerts[currIndex] + prevNormal,
833 inputPolygonVerts[currIndex] + normal0[currIndex],
834 currIndex, currIndex);
835 ++currEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400836 }
837
838 // Add the edge
Jim Van Verth00673692018-07-23 11:23:39 -0400839 auto edge = edgeData.push_back_n(1);
840 setup_offset_edge(edge,
841 inputPolygonVerts[currIndex] + normal0[currIndex],
842 inputPolygonVerts[nextIndex] + normal1[nextIndex],
843 currIndex, nextIndex);
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400844
845 prevIndex = currIndex;
846 currIndex++;
847 nextIndex = (nextIndex + 1) % inputPolygonSize;
848 }
849
Jim Van Verth00673692018-07-23 11:23:39 -0400850 // build linked list
851 // we have to do this as a post-process step because we might have reallocated
852 // the array when adding fans for reflex verts
853 prevIndex = edgeData.count()-1;
854 for (int currIndex = 0; currIndex < edgeData.count(); prevIndex = currIndex, ++currIndex) {
855 int nextIndex = (currIndex + 1) % edgeData.count();
856 edgeData[currIndex].fPrev = &edgeData[prevIndex];
857 edgeData[currIndex].fNext = &edgeData[nextIndex];
858 }
859
860 // now clip edges
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400861 int edgeDataSize = edgeData.count();
Jim Van Verth00673692018-07-23 11:23:39 -0400862 auto head = &edgeData[0];
863 auto currEdge = head;
864 auto prevEdge = currEdge->fPrev;
865 int offsetVertexCount = edgeDataSize;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400866 int iterations = 0;
Jim Van Verth00673692018-07-23 11:23:39 -0400867 while (head && prevEdge != currEdge) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400868 ++iterations;
869 // we should check each edge against each other edge at most once
870 if (iterations > edgeDataSize*edgeDataSize) {
871 return false;
872 }
873
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400874 SkScalar s, t;
875 SkPoint intersection;
Jim Van Vertha6316832018-07-24 09:30:37 -0400876 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400877 // if new intersection is further back on previous inset from the prior intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400878 if (s < prevEdge->fTValue) {
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400879 // no point in considering this one again
Jim Van Verth00673692018-07-23 11:23:39 -0400880 remove_node(prevEdge, &head);
881 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400882 // go back one segment
Jim Van Verth00673692018-07-23 11:23:39 -0400883 prevEdge = prevEdge->fPrev;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400884 // we've already considered this intersection, we're done
Jim Van Verth00673692018-07-23 11:23:39 -0400885 } else if (currEdge->fTValue > SK_ScalarMin &&
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400886 SkPointPriv::EqualsWithinTolerance(intersection,
Jim Van Verth00673692018-07-23 11:23:39 -0400887 currEdge->fIntersection,
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400888 1.0e-6f)) {
889 break;
890 } else {
891 // add intersection
Jim Van Verth00673692018-07-23 11:23:39 -0400892 currEdge->fIntersection = intersection;
893 currEdge->fTValue = t;
894 currEdge->fIndex = prevEdge->fEnd;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400895
896 // go to next segment
Jim Van Verth00673692018-07-23 11:23:39 -0400897 prevEdge = currEdge;
898 currEdge = currEdge->fNext;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400899 }
900 } else {
901 // If there is no intersection, we want to minimize the distance between
902 // the point where the segment lines cross and the segments themselves.
Jim Van Verth00673692018-07-23 11:23:39 -0400903 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
904 OffsetEdge* currNextEdge = currEdge->fNext;
Jim Van Vertheddb3d92018-08-02 10:56:26 -0400905 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
906 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
907 // if both lead to direct collision
908 if (dist0 < 0 && dist1 < 0) {
909 // check first to see if either represent parts of one contour
910 SkPoint p1 = prevPrevEdge->fInset.fP0 + prevPrevEdge->fInset.fV;
911 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
912 prevEdge->fInset.fP0);
913 p1 = currEdge->fInset.fP0 + currEdge->fInset.fV;
914 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
915 currNextEdge->fInset.fP0);
916
917 // want to step along contour to find intersections rather than jump to new one
918 if (currSameContour && !prevSameContour) {
919 remove_node(currEdge, &head);
920 currEdge = currNextEdge;
921 --offsetVertexCount;
922 continue;
923 } else if (prevSameContour && !currSameContour) {
924 remove_node(prevEdge, &head);
925 prevEdge = prevPrevEdge;
926 --offsetVertexCount;
927 continue;
928 }
929 }
930
931 // otherwise minimize collision distance along segment
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400932 if (dist0 < dist1) {
Jim Van Verth00673692018-07-23 11:23:39 -0400933 remove_node(prevEdge, &head);
934 prevEdge = prevPrevEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400935 } else {
Jim Van Verth00673692018-07-23 11:23:39 -0400936 remove_node(currEdge, &head);
937 currEdge = currNextEdge;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400938 }
Jim Van Verth00673692018-07-23 11:23:39 -0400939 --offsetVertexCount;
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400940 }
941 }
942
943 // store all the valid intersections that aren't nearly coincident
944 // TODO: look at the main algorithm and see if we can detect these better
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400945 offsetPolygon->reset();
Jim Van Verth00673692018-07-23 11:23:39 -0400946 if (head) {
947 static constexpr SkScalar kCleanupTolerance = 0.01f;
948 if (offsetVertexCount >= 0) {
949 offsetPolygon->setReserve(offsetVertexCount);
Ben Wagnere9dd3162018-07-18 21:27:57 +0000950 }
Jim Van Verth00673692018-07-23 11:23:39 -0400951 int currIndex = 0;
952 OffsetEdge* currEdge = head;
953 *offsetPolygon->push() = currEdge->fIntersection;
Ben Wagnere9dd3162018-07-18 21:27:57 +0000954 if (polygonIndices) {
Jim Van Verth00673692018-07-23 11:23:39 -0400955 *polygonIndices->push() = currEdge->fIndex;
956 }
957 currEdge = currEdge->fNext;
958 while (currEdge != head) {
959 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
960 (*offsetPolygon)[currIndex],
961 kCleanupTolerance)) {
962 *offsetPolygon->push() = currEdge->fIntersection;
963 if (polygonIndices) {
964 *polygonIndices->push() = currEdge->fIndex;
965 }
966 currIndex++;
967 }
968 currEdge = currEdge->fNext;
969 }
970 // make sure the first and last points aren't coincident
971 if (currIndex >= 1 &&
972 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
973 kCleanupTolerance)) {
974 offsetPolygon->pop();
975 if (polygonIndices) {
976 polygonIndices->pop();
977 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400978 }
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400979 }
980
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400981 // check winding of offset polygon (it should be same as the original polygon)
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400982 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->count());
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400983
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400984 return (winding*offsetWinding > 0 &&
985 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
Jim Van Verth4db18ed2018-04-03 10:00:37 -0400986}
987
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400988//////////////////////////////////////////////////////////////////////////////////////////
989
990struct TriangulationVertex {
991 SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
992
993 enum class VertexType { kConvex, kReflex };
994
995 SkPoint fPosition;
996 VertexType fVertexType;
997 uint16_t fIndex;
998 uint16_t fPrevIndex;
999 uint16_t fNextIndex;
1000};
1001
1002// test to see if point p is in triangle p0p1p2.
1003// for now assuming strictly inside -- if on the edge it's outside
1004static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1005 const SkPoint& p) {
1006 SkVector v0 = p1 - p0;
1007 SkVector v1 = p2 - p1;
1008 SkScalar n = v0.cross(v1);
1009
1010 SkVector w0 = p - p0;
1011 if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
1012 return false;
1013 }
1014
1015 SkVector w1 = p - p1;
1016 if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
1017 return false;
1018 }
1019
1020 SkVector v2 = p0 - p2;
1021 SkVector w2 = p - p2;
1022 if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
1023 return false;
1024 }
1025
1026 return true;
1027}
1028
1029// Data structure to track reflex vertices and check whether any are inside a given triangle
1030class ReflexHash {
1031public:
1032 void add(TriangulationVertex* v) {
1033 fReflexList.addToTail(v);
1034 }
1035
1036 void remove(TriangulationVertex* v) {
1037 fReflexList.remove(v);
1038 }
1039
Jim Van Verth061cc212018-07-11 14:09:09 -04001040 bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
1041 uint16_t ignoreIndex0, uint16_t ignoreIndex1) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001042 for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
1043 reflexIter != fReflexList.end(); ++reflexIter) {
1044 TriangulationVertex* reflexVertex = *reflexIter;
Jim Van Verth061cc212018-07-11 14:09:09 -04001045 if (reflexVertex->fIndex != ignoreIndex0 && reflexVertex->fIndex != ignoreIndex1 &&
1046 point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001047 return true;
1048 }
1049 }
1050
1051 return false;
1052 }
1053
1054private:
1055 // TODO: switch to an actual spatial hash
1056 SkTInternalLList<TriangulationVertex> fReflexList;
1057};
1058
1059// Check to see if a reflex vertex has become a convex vertex after clipping an ear
1060static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
1061 int winding, ReflexHash* reflexHash,
1062 SkTInternalLList<TriangulationVertex>* convexList) {
1063 if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
1064 SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
1065 SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
Jim Van Verth061cc212018-07-11 14:09:09 -04001066 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001067 p->fVertexType = TriangulationVertex::VertexType::kConvex;
1068 reflexHash->remove(p);
1069 p->fPrev = p->fNext = nullptr;
1070 convexList->addToTail(p);
1071 }
1072 }
1073}
1074
1075bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
1076 SkTDArray<uint16_t>* triangleIndices) {
1077 if (polygonSize < 3) {
1078 return false;
1079 }
1080 // need to be able to represent all the vertices in the 16-bit indices
1081 if (polygonSize >= (1 << 16)) {
1082 return false;
1083 }
1084
1085 // get winding direction
1086 // TODO: we do this for all the polygon routines -- might be better to have the client
1087 // compute it and pass it in
Jim Van Verth6784ffa2018-07-03 16:12:39 -04001088 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001089 if (0 == winding) {
1090 return false;
1091 }
1092
1093 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1094 // TODO: possibly sort the convexList in some way to get better triangles
1095 SkTInternalLList<TriangulationVertex> convexList;
1096 ReflexHash reflexHash;
1097 SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
1098 int prevIndex = polygonSize - 1;
1099 int currIndex = 0;
1100 int nextIndex = 1;
1101 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
1102 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1103 for (int i = 0; i < polygonSize; ++i) {
1104 SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
1105 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1106 triangulationVertices[currIndex].fIndex = currIndex;
1107 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1108 triangulationVertices[currIndex].fNextIndex = nextIndex;
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 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1111 convexList.addToTail(&triangulationVertices[currIndex]);
1112 } else {
1113 // We treat near collinear vertices as reflex
1114 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1115 reflexHash.add(&triangulationVertices[currIndex]);
1116 }
1117
1118 prevIndex = currIndex;
1119 currIndex = nextIndex;
1120 nextIndex = (currIndex + 1) % polygonSize;
1121 v0 = v1;
1122 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1123 }
1124
1125 // The general concept: We are trying to find three neighboring vertices where
1126 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1127 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1128 // we have triangulated the entire polygon.
1129 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1130 // noting that only convex vertices can be potential ears, and we only need to check whether
1131 // any reflex vertices lie inside the ear.
1132 triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
1133 int vertexCount = polygonSize;
1134 while (vertexCount > 3) {
1135 bool success = false;
1136 TriangulationVertex* earVertex = nullptr;
1137 TriangulationVertex* p0 = nullptr;
1138 TriangulationVertex* p2 = nullptr;
1139 // find a convex vertex to clip
1140 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1141 convexIter != convexList.end(); ++convexIter) {
1142 earVertex = *convexIter;
1143 SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
1144
1145 p0 = &triangulationVertices[earVertex->fPrevIndex];
1146 p2 = &triangulationVertices[earVertex->fNextIndex];
1147
1148 // see if any reflex vertices are inside the ear
1149 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
Jim Van Verth061cc212018-07-11 14:09:09 -04001150 p2->fPosition, p0->fIndex, p2->fIndex);
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001151 if (failed) {
1152 continue;
1153 }
1154
1155 // found one we can clip
1156 success = true;
1157 break;
1158 }
1159 // If we can't find any ears to clip, this probably isn't a simple polygon
1160 if (!success) {
1161 return false;
1162 }
1163
1164 // add indices
1165 auto indices = triangleIndices->append(3);
1166 indices[0] = indexMap[p0->fIndex];
1167 indices[1] = indexMap[earVertex->fIndex];
1168 indices[2] = indexMap[p2->fIndex];
1169
1170 // clip the ear
1171 convexList.remove(earVertex);
1172 --vertexCount;
1173
1174 // reclassify reflex verts
1175 p0->fNextIndex = earVertex->fNextIndex;
1176 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1177
1178 p2->fPrevIndex = earVertex->fPrevIndex;
1179 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1180 }
1181
1182 // output indices
1183 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1184 vertexIter != convexList.end(); ++vertexIter) {
1185 TriangulationVertex* vertex = *vertexIter;
1186 *triangleIndices->push() = indexMap[vertex->fIndex];
1187 }
1188
1189 return true;
1190}