Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Functions
SkPolyUtils.h File Reference
#include "include/core/SkPoint.h"
#include "include/core/SkScalar.h"
#include <cstdint>

Go to the source code of this file.

Functions

bool SkInsetConvexPolygon (const SkPoint *inputPolygonVerts, int inputPolygonSize, SkScalar inset, SkTDArray< SkPoint > *insetPolygon)
 
bool SkOffsetSimplePolygon (const SkPoint *inputPolygonVerts, int inputPolygonSize, const SkRect &bounds, SkScalar offset, SkTDArray< SkPoint > *offsetPolygon, SkTDArray< int > *polygonIndices=nullptr)
 
bool SkComputeRadialSteps (const SkVector &offset0, const SkVector &offset1, SkScalar offset, SkScalar *rotSin, SkScalar *rotCos, int *n)
 
int SkGetPolygonWinding (const SkPoint *polygonVerts, int polygonSize)
 
bool SkIsConvexPolygon (const SkPoint *polygonVerts, int polygonSize)
 
bool SkIsSimplePolygon (const SkPoint *polygonVerts, int polygonSize)
 
bool SkTriangulateSimplePolygon (const SkPoint *polygonVerts, uint16_t *indexMap, int polygonSize, SkTDArray< uint16_t > *triangleIndices)
 

Function Documentation

◆ SkComputeRadialSteps()

bool SkComputeRadialSteps ( const SkVector offset0,
const SkVector offset1,
SkScalar  offset,
SkScalar rotSin,
SkScalar rotCos,
int n 
)

Compute the number of points needed for a circular join when offsetting a vertex. The lengths of offset0 and offset1 don't have to equal |offset| – only the direction matters. The segment lengths will be approximately four pixels.

Parameters
offset0Starting offset vector direction.
offset1Ending offset vector direction.
offsetOffset value (can be negative).
rotSinSine of rotation delta per step.
rotCosCosine of rotation delta per step.
nNumber of steps to fill out the arc.
Returns
true for success, false otherwise

Definition at line 490 of file SkPolyUtils.cpp.

491 {
492 const SkScalar kRecipPixelsPerArcSegment = 0.25f;
493
494 SkScalar rCos = v1.dot(v2);
495 if (!SkIsFinite(rCos)) {
496 return false;
497 }
498 SkScalar rSin = v1.cross(v2);
499 if (!SkIsFinite(rSin)) {
500 return false;
501 }
502 SkScalar theta = SkScalarATan2(rSin, rCos);
503
504 SkScalar floatSteps = SkScalarAbs(offset*theta*kRecipPixelsPerArcSegment);
505 // limit the number of steps to at most max uint16_t (that's all we can index)
506 // knock one value off the top to account for rounding
507 if (floatSteps >= std::numeric_limits<uint16_t>::max()) {
508 return false;
509 }
510 int steps = SkScalarRoundToInt(floatSteps);
511
512 SkScalar dTheta = steps > 0 ? theta / steps : 0;
513 *rotSin = SkScalarSin(dTheta);
514 *rotCos = SkScalarCos(dTheta);
515 // Our offset may be so large that we end up with a tiny dTheta, in which case we
516 // lose precision when computing rotSin and rotCos.
517 if (steps > 0 && (*rotSin == 0 || *rotCos == 1)) {
518 return false;
519 }
520 *n = steps;
521 return true;
522}
static bool SkIsFinite(T x, Pack... values)
#define SkScalarATan2(y, x)
Definition SkScalar.h:50
#define SkScalarSin(radians)
Definition SkScalar.h:45
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SkScalarAbs(x)
Definition SkScalar.h:39
Vec2Value v2
float SkScalar
Definition extension.cpp:12
Point offset

◆ SkGetPolygonWinding()

int SkGetPolygonWinding ( const SkPoint polygonVerts,
int  polygonSize 
)

Determine winding direction for a polygon. The input polygon must be simple or the result will be meaningless.

Parameters
polygonVertsArray of points representing the vertices of the polygon.
polygonSizeNumber of vertices in the polygon.
Returns
1 for cw, -1 for ccw, and 0 if zero signed area (either degenerate or self-intersecting). The y-axis is assumed to be pointing down.

Definition at line 57 of file SkPolyUtils.cpp.

57 {
58 if (polygonSize < 3) {
59 return 0;
60 }
61
62 // compute area and use sign to determine winding
63 SkScalar quadArea = 0;
64 SkVector v0 = polygonVerts[1] - polygonVerts[0];
65 for (int curr = 2; curr < polygonSize; ++curr) {
66 SkVector v1 = polygonVerts[curr] - polygonVerts[0];
67 quadArea += v0.cross(v1);
68 v0 = v1;
69 }
70 if (SkScalarNearlyZero(quadArea, kCrossTolerance)) {
71 return 0;
72 }
73 // 1 == ccw, -1 == cw
74 return (quadArea > 0) ? 1 : -1;
75}
constexpr SkScalar kCrossTolerance
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
float cross(const SkVector &vec) const

◆ SkInsetConvexPolygon()

bool SkInsetConvexPolygon ( const SkPoint inputPolygonVerts,
int  inputPolygonSize,
SkScalar  inset,
SkTDArray< SkPoint > *  insetPolygon 
)

Generates a polygon that is inset a constant from the boundary of a given convex polygon. The input polygon is expected to have values clamped to the nearest 1/16th.

Parameters
inputPolygonVertsArray of points representing the vertices of the original polygon. It should be convex and have no coincident points.
inputPolygonSizeNumber of vertices in the original polygon.
insetHow far we wish to inset the polygon. This should be a positive value.
insetPolygonThe resulting inset polygon, if any.
Returns
true if an inset polygon exists, false otherwise.

Definition at line 338 of file SkPolyUtils.cpp.

339 {
340 if (inputPolygonSize < 3) {
341 return false;
342 }
343
344 // restrict this to match other routines
345 // practically we don't want anything bigger than this anyway
346 if (inputPolygonSize > std::numeric_limits<uint16_t>::max()) {
347 return false;
348 }
349
350 // can't inset by a negative or non-finite amount
352 return false;
353 }
354
355 // insetting close to zero just returns the original poly
356 if (inset <= SK_ScalarNearlyZero) {
357 for (int i = 0; i < inputPolygonSize; ++i) {
358 *insetPolygon->append() = inputPolygonVerts[i];
359 }
360 return true;
361 }
362
363 // get winding direction
364 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
365 if (0 == winding) {
366 return false;
367 }
368
369 // set up
370 AutoSTMalloc<64, OffsetEdge> edgeData(inputPolygonSize);
371 int prev = inputPolygonSize - 1;
372 for (int curr = 0; curr < inputPolygonSize; prev = curr, ++curr) {
373 int next = (curr + 1) % inputPolygonSize;
374 if (!inputPolygonVerts[curr].isFinite()) {
375 return false;
376 }
377 // check for convexity just to be sure
378 if (compute_side(inputPolygonVerts[prev], inputPolygonVerts[curr] - inputPolygonVerts[prev],
379 inputPolygonVerts[next])*winding < 0) {
380 return false;
381 }
382 SkVector v = inputPolygonVerts[next] - inputPolygonVerts[curr];
383 SkVector perp = SkVector::Make(-v.fY, v.fX);
384 perp.setLength(inset*winding);
385 edgeData[curr].fPrev = &edgeData[prev];
386 edgeData[curr].fNext = &edgeData[next];
387 edgeData[curr].fOffset.fP0 = inputPolygonVerts[curr] + perp;
388 edgeData[curr].fOffset.fV = v;
389 edgeData[curr].init();
390 }
391
392 OffsetEdge* head = &edgeData[0];
393 OffsetEdge* currEdge = head;
394 OffsetEdge* prevEdge = currEdge->fPrev;
395 int insetVertexCount = inputPolygonSize;
396 unsigned int iterations = 0;
397 unsigned int maxIterations = inputPolygonSize * inputPolygonSize;
398 while (head && prevEdge != currEdge) {
399 ++iterations;
400 // we should check each edge against each other edge at most once
401 if (iterations > maxIterations) {
402 return false;
403 }
404
405 SkScalar s, t;
406 SkPoint intersection;
407 if (compute_intersection(prevEdge->fOffset, currEdge->fOffset,
408 &intersection, &s, &t)) {
409 // if new intersection is further back on previous inset from the prior intersection
410 if (s < prevEdge->fTValue) {
411 // no point in considering this one again
412 remove_node(prevEdge, &head);
413 --insetVertexCount;
414 // go back one segment
415 prevEdge = prevEdge->fPrev;
416 // we've already considered this intersection, we're done
417 } else if (currEdge->fTValue > SK_ScalarMin &&
419 currEdge->fIntersection,
420 1.0e-6f)) {
421 break;
422 } else {
423 // add intersection
424 currEdge->fIntersection = intersection;
425 currEdge->fTValue = t;
426
427 // go to next segment
428 prevEdge = currEdge;
429 currEdge = currEdge->fNext;
430 }
431 } else {
432 // if prev to right side of curr
433 int side = winding*compute_side(currEdge->fOffset.fP0,
434 currEdge->fOffset.fV,
435 prevEdge->fOffset.fP0);
436 if (side < 0 &&
437 side == winding*compute_side(currEdge->fOffset.fP0,
438 currEdge->fOffset.fV,
439 prevEdge->fOffset.fP0 + prevEdge->fOffset.fV)) {
440 // no point in considering this one again
441 remove_node(prevEdge, &head);
442 --insetVertexCount;
443 // go back one segment
444 prevEdge = prevEdge->fPrev;
445 } else {
446 // move to next segment
447 remove_node(currEdge, &head);
448 --insetVertexCount;
449 currEdge = currEdge->fNext;
450 }
451 }
452 }
453
454 // 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
456 insetPolygon->reset();
457 if (!head) {
458 return false;
459 }
460
461 static constexpr SkScalar kCleanupTolerance = 0.01f;
462 if (insetVertexCount >= 0) {
463 insetPolygon->reserve(insetVertexCount);
464 }
465 int currIndex = 0;
466 *insetPolygon->append() = head->fIntersection;
467 currEdge = head->fNext;
468 while (currEdge != head) {
470 (*insetPolygon)[currIndex],
471 kCleanupTolerance)) {
472 *insetPolygon->append() = currEdge->fIntersection;
473 currIndex++;
474 }
475 currEdge = currEdge->fNext;
476 }
477 // make sure the first and last points aren't coincident
478 if (currIndex >= 1 &&
479 SkPointPriv::EqualsWithinTolerance((*insetPolygon)[0], (*insetPolygon)[currIndex],
480 kCleanupTolerance)) {
481 insetPolygon->pop_back();
482 }
483
484 return SkIsConvexPolygon(insetPolygon->begin(), insetPolygon->size());
485}
static bool isFinite(const SkRect &r)
static float next(float f)
static float prev(float f)
static int side(double x)
bool SkIsConvexPolygon(const SkPoint *polygonVerts, int polygonSize)
static bool compute_intersection(const OffsetSegment &s0, const OffsetSegment &s1, SkPoint *p, SkScalar *s, SkScalar *t)
static void remove_node(const OffsetEdge *node, OffsetEdge **head)
static int compute_side(const SkPoint &p0, const SkVector &v, const SkPoint &p)
int SkGetPolygonWinding(const SkPoint *polygonVerts, int polygonSize)
#define SK_ScalarMin
Definition SkScalar.h:25
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
static bool EqualsWithinTolerance(const SkPoint &p1, const SkPoint &p2)
Definition SkPointPriv.h:54
int size() const
Definition SkTDArray.h:138
void reset()
Definition SkTDArray.h:171
void reserve(int n)
Definition SkTDArray.h:187
T * begin()
Definition SkTDArray.h:150
T * append()
Definition SkTDArray.h:191
void pop_back()
Definition SkTDArray.h:223
struct MyStruct s
static SkRect inset(const SkRect &r)
OffsetEdge * fNext
SkPoint fIntersection
OffsetSegment fOffset
SkScalar fTValue
OffsetEdge * fPrev
bool setLength(float length)
Definition SkPoint.cpp:30
float fX
x-axis value
static constexpr SkPoint Make(float x, float y)
float fY
y-axis value

◆ SkIsConvexPolygon()

bool SkIsConvexPolygon ( const SkPoint polygonVerts,
int  polygonSize 
)

Determine whether a polygon is convex or not.

Parameters
polygonVertsArray of points representing the vertices of the polygon.
polygonSizeNumber of vertices in the polygon.
Returns
true if the polygon is convex, false otherwise.

Definition at line 196 of file SkPolyUtils.cpp.

196 {
197 if (polygonSize < 3) {
198 return false;
199 }
200
201 SkScalar lastPerpDot = 0;
202 int xSignChangeCount = 0;
203 int ySignChangeCount = 0;
204
205 int prevIndex = polygonSize - 1;
206 int currIndex = 0;
207 int nextIndex = 1;
208 SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
209 SkScalar lastVx = v0.fX;
210 SkScalar lastVy = v0.fY;
211 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
212 for (int i = 0; i < polygonSize; ++i) {
213 if (!polygonVerts[i].isFinite()) {
214 return false;
215 }
216
217 // Check that winding direction is always the same (otherwise we have a reflex vertex)
218 SkScalar perpDot = v0.cross(v1);
219 if (lastPerpDot*perpDot < 0) {
220 return false;
221 }
222 if (0 != perpDot) {
223 lastPerpDot = perpDot;
224 }
225
226 // Check that the signs of the edge vectors don't change more than twice per coordinate
227 if (lastVx*v1.fX < 0) {
228 xSignChangeCount++;
229 }
230 if (lastVy*v1.fY < 0) {
231 ySignChangeCount++;
232 }
233 if (xSignChangeCount > 2 || ySignChangeCount > 2) {
234 return false;
235 }
236 prevIndex = currIndex;
237 currIndex = nextIndex;
238 nextIndex = (currIndex + 1) % polygonSize;
239 if (v1.fX != 0) {
240 lastVx = v1.fX;
241 }
242 if (v1.fY != 0) {
243 lastVy = v1.fY;
244 }
245 v0 = v1;
246 v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
247 }
248
249 return true;
250}

◆ SkIsSimplePolygon()

bool SkIsSimplePolygon ( const SkPoint polygonVerts,
int  polygonSize 
)

Determine whether a polygon is simple (i.e., not self-intersecting) or not. The input polygon must have no coincident vertices or the test will fail. The polygon is also expected to have values clamped to the nearest 1/16th.

Parameters
polygonVertsArray of points representing the vertices of the polygon.
polygonSizeNumber of vertices in the polygon.
Returns
true if the polygon is simple, false otherwise.

Definition at line 1092 of file SkPolyUtils.cpp.

1092 {
1093 if (polygonSize < 3) {
1094 return false;
1095 }
1096
1097 // If it's convex, it's simple
1098 if (SkIsConvexPolygon(polygon, polygonSize)) {
1099 return true;
1100 }
1101
1102 // practically speaking, it takes too long to process large polygons
1103 if (polygonSize > 2048) {
1104 return false;
1105 }
1106
1107 SkTDPQueue <Vertex, Vertex::Left> vertexQueue(polygonSize);
1108 for (int i = 0; i < polygonSize; ++i) {
1109 Vertex newVertex;
1110 if (!polygon[i].isFinite()) {
1111 return false;
1112 }
1113 newVertex.fPosition = polygon[i];
1114 newVertex.fIndex = i;
1115 newVertex.fPrevIndex = (i - 1 + polygonSize) % polygonSize;
1116 newVertex.fNextIndex = (i + 1) % polygonSize;
1117 newVertex.fFlags = 0;
1118 // The two edges adjacent to this vertex are the same, so polygon is not simple
1119 if (polygon[newVertex.fPrevIndex] == polygon[newVertex.fNextIndex]) {
1120 return false;
1121 }
1122 if (left(polygon[newVertex.fPrevIndex], polygon[i])) {
1123 newVertex.fFlags |= kPrevLeft_VertexFlag;
1124 }
1125 if (left(polygon[newVertex.fNextIndex], polygon[i])) {
1126 newVertex.fFlags |= kNextLeft_VertexFlag;
1127 }
1128 vertexQueue.insert(newVertex);
1129 }
1130
1131 // pop each vertex from the queue and generate events depending on
1132 // where it lies relative to its neighboring edges
1133 ActiveEdgeList sweepLine(polygonSize);
1134 while (vertexQueue.count() > 0) {
1135 const Vertex& v = vertexQueue.peek();
1136
1137 // both to the right -- insert both
1138 if (v.fFlags == 0) {
1139 if (!sweepLine.insert(v.fPosition, polygon[v.fPrevIndex], v.fIndex, v.fPrevIndex)) {
1140 break;
1141 }
1142 if (!sweepLine.insert(v.fPosition, polygon[v.fNextIndex], v.fIndex, v.fNextIndex)) {
1143 break;
1144 }
1145 // both to the left -- remove both
1146 } else if (v.fFlags == (kPrevLeft_VertexFlag | kNextLeft_VertexFlag)) {
1147 if (!sweepLine.remove(polygon[v.fPrevIndex], v.fPosition, v.fPrevIndex, v.fIndex)) {
1148 break;
1149 }
1150 if (!sweepLine.remove(polygon[v.fNextIndex], v.fPosition, v.fNextIndex, v.fIndex)) {
1151 break;
1152 }
1153 // one to left and right -- replace one with another
1154 } else {
1155 if (v.fFlags & kPrevLeft_VertexFlag) {
1156 if (!sweepLine.replace(polygon[v.fPrevIndex], v.fPosition, polygon[v.fNextIndex],
1157 v.fPrevIndex, v.fIndex, v.fNextIndex)) {
1158 break;
1159 }
1160 } else {
1162 if (!sweepLine.replace(polygon[v.fNextIndex], v.fPosition, polygon[v.fPrevIndex],
1163 v.fNextIndex, v.fIndex, v.fPrevIndex)) {
1164 break;
1165 }
1166 }
1167 }
1168
1169 vertexQueue.pop();
1170 }
1171
1172 return (vertexQueue.count() == 0);
1173}
#define SkASSERT(cond)
Definition SkAssert.h:116
static bool left(const SkPoint &p0, const SkPoint &p1)
@ kNextLeft_VertexFlag
@ kPrevLeft_VertexFlag
uint16_t fFlags
uint16_t fIndex
SkPoint fPosition
uint16_t fNextIndex
uint16_t fPrevIndex

◆ SkOffsetSimplePolygon()

bool SkOffsetSimplePolygon ( const SkPoint inputPolygonVerts,
int  inputPolygonSize,
const SkRect bounds,
SkScalar  offset,
SkTDArray< SkPoint > *  offsetPolygon,
SkTDArray< int > *  polygonIndices = nullptr 
)

Generates a simple polygon (if possible) that is offset a constant distance from the boundary of a given simple polygon. The input polygon must be simple, have no coincident vertices or collinear edges, and have values clamped to the nearest 1/16th.

Parameters
inputPolygonVertsArray of points representing the vertices of the original polygon.
inputPolygonSizeNumber of vertices in the original polygon.
boundsBounding rectangle for the original polygon.
offsetHow far we wish to offset the polygon. Positive values indicate insetting, negative values outsetting.
offsetPolgonThe resulting offset polygon, if any.
polygonIndicesThe indices of the original polygon that map to the new one.
Returns
true if an offset simple polygon exists, false otherwise.

Definition at line 1195 of file SkPolyUtils.cpp.

1197 {
1198 if (inputPolygonSize < 3) {
1199 return false;
1200 }
1201
1202 // need to be able to represent all the vertices in the 16-bit indices
1203 if (inputPolygonSize >= std::numeric_limits<uint16_t>::max()) {
1204 return false;
1205 }
1206
1207 if (!SkIsFinite(offset)) {
1208 return false;
1209 }
1210
1211 // can't inset more than the half bounds of the polygon
1212 if (offset > std::min(SkTAbs(SkRectPriv::HalfWidth(bounds)),
1213 SkTAbs(SkRectPriv::HalfHeight(bounds)))) {
1214 return false;
1215 }
1216
1217 // offsetting close to zero just returns the original poly
1219 for (int i = 0; i < inputPolygonSize; ++i) {
1220 *offsetPolygon->append() = inputPolygonVerts[i];
1221 if (polygonIndices) {
1222 *polygonIndices->append() = i;
1223 }
1224 }
1225 return true;
1226 }
1227
1228 // get winding direction
1229 int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
1230 if (0 == winding) {
1231 return false;
1232 }
1233
1234 // build normals
1235 AutoSTMalloc<64, SkVector> normals(inputPolygonSize);
1236 unsigned int numEdges = 0;
1237 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1238 currIndex < inputPolygonSize;
1239 prevIndex = currIndex, ++currIndex) {
1240 if (!inputPolygonVerts[currIndex].isFinite()) {
1241 return false;
1242 }
1243 int nextIndex = (currIndex + 1) % inputPolygonSize;
1244 if (!compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
1245 offset, winding, &normals[currIndex])) {
1246 return false;
1247 }
1248 if (currIndex > 0) {
1249 // if reflex point, we need to add extra edges
1250 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
1251 prevIndex, currIndex, nextIndex)) {
1252 SkScalar rotSin, rotCos;
1253 int numSteps;
1254 if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
1255 &rotSin, &rotCos, &numSteps)) {
1256 return false;
1257 }
1258 numEdges += std::max(numSteps, 1);
1259 }
1260 }
1261 numEdges++;
1262 }
1263 // finish up the edge counting
1264 if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
1265 SkScalar rotSin, rotCos;
1266 int numSteps;
1267 if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
1268 &rotSin, &rotCos, &numSteps)) {
1269 return false;
1270 }
1271 numEdges += std::max(numSteps, 1);
1272 }
1273
1274 // Make sure we don't overflow the max array count.
1275 // We shouldn't overflow numEdges, as SkComputeRadialSteps returns a max of 2^16-1,
1276 // and we have a max of 2^16-1 original vertices.
1277 if (numEdges > (unsigned int)std::numeric_limits<int32_t>::max()) {
1278 return false;
1279 }
1280
1281 // build initial offset edge list
1282 STArray<64, OffsetEdge> edgeData(numEdges);
1283 OffsetEdge* prevEdge = nullptr;
1284 for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
1285 currIndex < inputPolygonSize;
1286 prevIndex = currIndex, ++currIndex) {
1287 int nextIndex = (currIndex + 1) % inputPolygonSize;
1288 // if reflex point, fill in curve
1289 if (is_reflex_vertex(inputPolygonVerts, winding, offset,
1290 prevIndex, currIndex, nextIndex)) {
1291 SkScalar rotSin, rotCos;
1292 int numSteps;
1293 SkVector prevNormal = normals[prevIndex];
1294 if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
1295 &rotSin, &rotCos, &numSteps)) {
1296 return false;
1297 }
1298 auto currEdge = edgeData.push_back_n(std::max(numSteps, 1));
1299 for (int i = 0; i < numSteps - 1; ++i) {
1300 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
1301 prevNormal.fY*rotCos + prevNormal.fX*rotSin);
1302 setup_offset_edge(currEdge,
1303 inputPolygonVerts[currIndex] + prevNormal,
1304 inputPolygonVerts[currIndex] + currNormal,
1305 currIndex, currIndex);
1306 prevNormal = currNormal;
1307 currEdge->fPrev = prevEdge;
1308 if (prevEdge) {
1309 prevEdge->fNext = currEdge;
1310 }
1311 prevEdge = currEdge;
1312 ++currEdge;
1313 }
1314 setup_offset_edge(currEdge,
1315 inputPolygonVerts[currIndex] + prevNormal,
1316 inputPolygonVerts[currIndex] + normals[currIndex],
1317 currIndex, currIndex);
1318 currEdge->fPrev = prevEdge;
1319 if (prevEdge) {
1320 prevEdge->fNext = currEdge;
1321 }
1322 prevEdge = currEdge;
1323 }
1324
1325 // Add the edge
1326 auto currEdge = edgeData.push_back_n(1);
1327 setup_offset_edge(currEdge,
1328 inputPolygonVerts[currIndex] + normals[currIndex],
1329 inputPolygonVerts[nextIndex] + normals[currIndex],
1330 currIndex, nextIndex);
1331 currEdge->fPrev = prevEdge;
1332 if (prevEdge) {
1333 prevEdge->fNext = currEdge;
1334 }
1335 prevEdge = currEdge;
1336 }
1337 // close up the linked list
1338 SkASSERT(prevEdge);
1339 prevEdge->fNext = &edgeData[0];
1340 edgeData[0].fPrev = prevEdge;
1341
1342 // now clip edges
1343 SkASSERT(edgeData.size() == (int)numEdges);
1344 auto head = &edgeData[0];
1345 auto currEdge = head;
1346 unsigned int offsetVertexCount = numEdges;
1347 unsigned long long iterations = 0;
1348 unsigned long long maxIterations = (unsigned long long)(numEdges) * numEdges;
1349 while (head && prevEdge != currEdge && offsetVertexCount > 0) {
1350 ++iterations;
1351 // we should check each edge against each other edge at most once
1352 if (iterations > maxIterations) {
1353 return false;
1354 }
1355
1356 SkScalar s, t;
1357 SkPoint intersection;
1358 if (prevEdge->checkIntersection(currEdge, &intersection, &s, &t)) {
1359 // if new intersection is further back on previous inset from the prior intersection
1360 if (s < prevEdge->fTValue) {
1361 // no point in considering this one again
1362 remove_node(prevEdge, &head);
1363 --offsetVertexCount;
1364 // go back one segment
1365 prevEdge = prevEdge->fPrev;
1366 // we've already considered this intersection, we're done
1367 } else if (currEdge->fTValue > SK_ScalarMin &&
1369 currEdge->fIntersection,
1370 1.0e-6f)) {
1371 break;
1372 } else {
1373 // add intersection
1374 currEdge->fIntersection = intersection;
1375 currEdge->fTValue = t;
1376 currEdge->fIndex = prevEdge->fEnd;
1377
1378 // go to next segment
1379 prevEdge = currEdge;
1380 currEdge = currEdge->fNext;
1381 }
1382 } else {
1383 // If there is no intersection, we want to minimize the distance between
1384 // the point where the segment lines cross and the segments themselves.
1385 OffsetEdge* prevPrevEdge = prevEdge->fPrev;
1386 OffsetEdge* currNextEdge = currEdge->fNext;
1387 SkScalar dist0 = currEdge->computeCrossingDistance(prevPrevEdge);
1388 SkScalar dist1 = prevEdge->computeCrossingDistance(currNextEdge);
1389 // if both lead to direct collision
1390 if (dist0 < 0 && dist1 < 0) {
1391 // check first to see if either represent parts of one contour
1392 SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
1393 bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1394 prevEdge->fOffset.fP0);
1395 p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
1396 bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
1397 currNextEdge->fOffset.fP0);
1398
1399 // want to step along contour to find intersections rather than jump to new one
1400 if (currSameContour && !prevSameContour) {
1401 remove_node(currEdge, &head);
1402 currEdge = currNextEdge;
1403 --offsetVertexCount;
1404 continue;
1405 } else if (prevSameContour && !currSameContour) {
1406 remove_node(prevEdge, &head);
1407 prevEdge = prevPrevEdge;
1408 --offsetVertexCount;
1409 continue;
1410 }
1411 }
1412
1413 // otherwise minimize collision distance along segment
1414 if (dist0 < dist1) {
1415 remove_node(prevEdge, &head);
1416 prevEdge = prevPrevEdge;
1417 } else {
1418 remove_node(currEdge, &head);
1419 currEdge = currNextEdge;
1420 }
1421 --offsetVertexCount;
1422 }
1423 }
1424
1425 // store all the valid intersections that aren't nearly coincident
1426 // TODO: look at the main algorithm and see if we can detect these better
1427 offsetPolygon->reset();
1428 if (!head || offsetVertexCount == 0 ||
1429 offsetVertexCount >= std::numeric_limits<uint16_t>::max()) {
1430 return false;
1431 }
1432
1433 static constexpr SkScalar kCleanupTolerance = 0.01f;
1434 offsetPolygon->reserve(offsetVertexCount);
1435 int currIndex = 0;
1436 *offsetPolygon->append() = head->fIntersection;
1437 if (polygonIndices) {
1438 *polygonIndices->append() = head->fIndex;
1439 }
1440 currEdge = head->fNext;
1441 while (currEdge != head) {
1442 if (!SkPointPriv::EqualsWithinTolerance(currEdge->fIntersection,
1443 (*offsetPolygon)[currIndex],
1444 kCleanupTolerance)) {
1445 *offsetPolygon->append() = currEdge->fIntersection;
1446 if (polygonIndices) {
1447 *polygonIndices->append() = currEdge->fIndex;
1448 }
1449 currIndex++;
1450 }
1451 currEdge = currEdge->fNext;
1452 }
1453 // make sure the first and last points aren't coincident
1454 if (currIndex >= 1 &&
1455 SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
1456 kCleanupTolerance)) {
1457 offsetPolygon->pop_back();
1458 if (polygonIndices) {
1459 polygonIndices->pop_back();
1460 }
1461 }
1462
1463 // check winding of offset polygon (it should be same as the original polygon)
1464 SkScalar offsetWinding = SkGetPolygonWinding(offsetPolygon->begin(), offsetPolygon->size());
1465
1466 return (winding*offsetWinding > 0 &&
1467 SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->size()));
1468}
static SkM44 normals(SkM44 m)
Definition 3DSlide.cpp:78
bool SkComputeRadialSteps(const SkVector &v1, const SkVector &v2, SkScalar offset, SkScalar *rotSin, SkScalar *rotCos, int *n)
bool compute_offset_vector(const SkPoint &p0, const SkPoint &p1, SkScalar offset, int side, SkPoint *vector)
static void setup_offset_edge(OffsetEdge *currEdge, const SkPoint &endpoint0, const SkPoint &endpoint1, uint16_t startIndex, uint16_t endIndex)
bool SkIsSimplePolygon(const SkPoint *polygon, int polygonSize)
static bool is_reflex_vertex(const SkPoint *inputPolygonVerts, int winding, SkScalar offset, uint16_t prevIndex, uint16_t currIndex, uint16_t nextIndex)
static T SkTAbs(T value)
Definition SkTemplates.h:43
static constexpr float HalfWidth(const SkRect &r)
Definition SkRectPriv.h:62
static constexpr float HalfHeight(const SkRect &r)
Definition SkRectPriv.h:66
bool checkIntersection(const OffsetEdge *that, SkPoint *p, SkScalar *s, SkScalar *t)
uint16_t fEnd
SkScalar computeCrossingDistance(const OffsetEdge *that)

◆ SkTriangulateSimplePolygon()

bool SkTriangulateSimplePolygon ( const SkPoint polygonVerts,
uint16_t *  indexMap,
int  polygonSize,
SkTDArray< uint16_t > *  triangleIndices 
)

Compute indices to triangulate the given polygon. The input polygon must be simple (i.e. it is not self-intersecting) and have no coincident vertices or collinear edges.

Parameters
polygonVertsArray of points representing the vertices of the polygon.
indexMapMapping from index in the given array to the final index in the triangulation.
polygonSizeNumber of vertices in the polygon.
triangleIndicesIndices of the resulting triangulation.
Returns
true if successful, false otherwise.

Definition at line 1632 of file SkPolyUtils.cpp.

1633 {
1634 if (polygonSize < 3) {
1635 return false;
1636 }
1637 // need to be able to represent all the vertices in the 16-bit indices
1638 if (polygonSize >= std::numeric_limits<uint16_t>::max()) {
1639 return false;
1640 }
1641
1642 // get bounds
1643 SkRect bounds;
1644 if (!bounds.setBoundsCheck(polygonVerts, polygonSize)) {
1645 return false;
1646 }
1647 // get winding direction
1648 // TODO: we do this for all the polygon routines -- might be better to have the client
1649 // compute it and pass it in
1650 int winding = SkGetPolygonWinding(polygonVerts, polygonSize);
1651 if (0 == winding) {
1652 return false;
1653 }
1654
1655 // Set up vertices
1656 AutoSTArray<64, TriangulationVertex> triangulationVertices(polygonSize);
1657 int prevIndex = polygonSize - 1;
1658 SkVector v0 = polygonVerts[0] - polygonVerts[prevIndex];
1659 for (int currIndex = 0; currIndex < polygonSize; ++currIndex) {
1660 int nextIndex = (currIndex + 1) % polygonSize;
1661
1662 triangulationVertices[currIndex] = TriangulationVertex{};
1663 triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
1664 triangulationVertices[currIndex].fIndex = currIndex;
1665 triangulationVertices[currIndex].fPrevIndex = prevIndex;
1666 triangulationVertices[currIndex].fNextIndex = nextIndex;
1667 SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
1668 if (winding*v0.cross(v1) > SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
1669 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
1670 } else {
1671 triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
1672 }
1673
1674 prevIndex = currIndex;
1675 v0 = v1;
1676 }
1677
1678 // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
1679 // TODO: possibly sort the convexList in some way to get better triangles
1681 ReflexHash reflexHash;
1682 if (!reflexHash.init(bounds, polygonSize)) {
1683 return false;
1684 }
1685 prevIndex = polygonSize - 1;
1686 for (int currIndex = 0; currIndex < polygonSize; prevIndex = currIndex, ++currIndex) {
1687 TriangulationVertex::VertexType currType = triangulationVertices[currIndex].fVertexType;
1689 int nextIndex = (currIndex + 1) % polygonSize;
1690 TriangulationVertex::VertexType prevType = triangulationVertices[prevIndex].fVertexType;
1691 TriangulationVertex::VertexType nextType = triangulationVertices[nextIndex].fVertexType;
1692 // We prioritize clipping vertices with neighboring reflex vertices.
1693 // The intent here is that it will cull reflex vertices more quickly.
1696 convexList.addToHead(&triangulationVertices[currIndex]);
1697 } else {
1698 convexList.addToTail(&triangulationVertices[currIndex]);
1699 }
1700 } else {
1701 // We treat near collinear vertices as reflex
1702 reflexHash.add(&triangulationVertices[currIndex]);
1703 }
1704 }
1705
1706 // The general concept: We are trying to find three neighboring vertices where
1707 // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
1708 // that ear off, and then repeat on the new polygon. Once we get down to three vertices
1709 // we have triangulated the entire polygon.
1710 // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
1711 // noting that only convex vertices can be potential ears, and we only need to check whether
1712 // any reflex vertices lie inside the ear.
1713 triangleIndices->reserve(triangleIndices->size() + 3 * (polygonSize - 2));
1714 int vertexCount = polygonSize;
1715 while (vertexCount > 3) {
1716 bool success = false;
1717 TriangulationVertex* earVertex = nullptr;
1718 TriangulationVertex* p0 = nullptr;
1719 TriangulationVertex* p2 = nullptr;
1720 // find a convex vertex to clip
1721 for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
1722 convexIter != convexList.end(); ++convexIter) {
1723 earVertex = *convexIter;
1725
1726 p0 = &triangulationVertices[earVertex->fPrevIndex];
1727 p2 = &triangulationVertices[earVertex->fNextIndex];
1728
1729 // see if any reflex vertices are inside the ear
1730 bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
1731 p2->fPosition, p0->fIndex, p2->fIndex);
1732 if (failed) {
1733 continue;
1734 }
1735
1736 // found one we can clip
1737 success = true;
1738 break;
1739 }
1740 // If we can't find any ears to clip, this probably isn't a simple polygon
1741 if (!success) {
1742 return false;
1743 }
1744
1745 // add indices
1746 auto indices = triangleIndices->append(3);
1747 indices[0] = indexMap[p0->fIndex];
1748 indices[1] = indexMap[earVertex->fIndex];
1749 indices[2] = indexMap[p2->fIndex];
1750
1751 // clip the ear
1752 convexList.remove(earVertex);
1753 --vertexCount;
1754
1755 // reclassify reflex verts
1756 p0->fNextIndex = earVertex->fNextIndex;
1757 reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
1758
1759 p2->fPrevIndex = earVertex->fPrevIndex;
1760 reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
1761 }
1762
1763 // output indices
1764 for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
1765 vertexIter != convexList.end(); ++vertexIter) {
1766 TriangulationVertex* vertex = *vertexIter;
1767 *triangleIndices->append() = indexMap[vertex->fIndex];
1768 }
1769
1770 return true;
1771}
static void reclassify_vertex(TriangulationVertex *p, const SkPoint *polygonVerts, int winding, ReflexHash *reflexHash, SkTInternalLList< TriangulationVertex > *convexList)
bool init(const SkRect &bounds, int vertexCount)
bool checkTriangle(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, uint16_t ignoreIndex0, uint16_t ignoreIndex1) const
void add(TriangulationVertex *v)
void remove(T *entry)
void addToHead(T *entry)
void addToTail(T *entry)
Optional< SkRect > bounds
Definition SkRecords.h:189