1 // Copyright 2013 Howling Moon Software. All rights reserved.
2 // See http://chipmunk2d.net/legal.php for more information.
9 #include "chipmunk/chipmunk_private.h"
10 #include "chipmunk/cpPolyline.h"
13 static inline int Next(int i, int count){return (i+1)%count;}
17 #define DEFAULT_POLYLINE_CAPACITY 16
20 cpPolylineSizeForCapacity(int capacity)
22 return sizeof(cpPolyline) + capacity*sizeof(cpVect);
26 cpPolylineMake(int capacity)
28 capacity = (capacity > DEFAULT_POLYLINE_CAPACITY ? capacity : DEFAULT_POLYLINE_CAPACITY);
30 cpPolyline *line = (cpPolyline *)cpcalloc(1, cpPolylineSizeForCapacity(capacity));
32 line->capacity = capacity;
38 cpPolylineMake2(int capacity, cpVect a, cpVect b)
40 cpPolyline *line = cpPolylineMake(capacity);
49 cpPolylineShrink(cpPolyline *line)
51 line->capacity = line->count;
52 return (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(line->count));
56 cpPolylineFree(cpPolyline *line)
61 // Grow the allocated memory for a polyline.
63 cpPolylineGrow(cpPolyline *line, int count)
67 int capacity = line->capacity;
68 while(line->count > capacity) capacity *= 2;
70 if(line->capacity < capacity){
71 line->capacity = capacity;
72 line = (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(capacity));
78 // Push v onto the end of line.
80 cpPolylinePush(cpPolyline *line, cpVect v)
82 int count = line->count;
83 line = cpPolylineGrow(line, 1);
84 line->verts[count] = v;
89 // Push v onto the beginning of line.
91 cpPolylineEnqueue(cpPolyline *line, cpVect v)
93 // TODO could optimize this to grow in both directions.
94 // Probably doesn't matter though.
95 int count = line->count;
96 line = cpPolylineGrow(line, 1);
97 memmove(line->verts + 1, line->verts, count*sizeof(cpVect));
103 // Returns true if the polyline starts and ends with the same vertex.
105 cpPolylineIsClosed(cpPolyline *line)
107 return (line->count > 1 && cpveql(line->verts[0], line->verts[line->count-1]));
110 // Check if a cpPolyline is longer than a certain length
111 // Takes a range which can wrap around if the polyline is looped.
113 cpPolylineIsShort(cpVect *points, int count, int start, int end, cpFloat min)
115 cpFloat length = 0.0f;
116 for(int i=start; i!=end; i=Next(i, count)){
117 length += cpvdist(points[i], points[Next(i, count)]);
118 if(length > min) return cpFalse;
124 //MARK: Polyline Simplification
126 static inline cpFloat
127 Sharpness(cpVect a, cpVect b, cpVect c)
129 // TODO could speed this up by caching the normals instead of calculating each twice.
130 return cpvdot(cpvnormalize(cpvsub(a, b)), cpvnormalize(cpvsub(c, b)));
133 // Join similar adjacent line segments together. Works well for hard edged shapes.
134 // 'tol' is the minimum anglular difference in radians of a vertex.
136 cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol)
138 cpPolyline *reduced = cpPolylineMake2(0, line->verts[0], line->verts[1]);
140 cpFloat minSharp = -cpfcos(tol);
142 for(int i=2; i<line->count; i++){
143 cpVect vert = line->verts[i];
144 cpFloat sharp = Sharpness(reduced->verts[reduced->count - 2], reduced->verts[reduced->count - 1], vert);
146 if(sharp <= minSharp){
147 reduced->verts[reduced->count - 1] = vert;
149 reduced = cpPolylinePush(reduced, vert);
154 cpPolylineIsClosed(line) &&
155 Sharpness(reduced->verts[reduced->count - 2], reduced->verts[0], reduced->verts[1]) < minSharp
157 reduced->verts[0] = reduced->verts[reduced->count - 2];
165 // Recursive function used by cpPolylineSimplifyCurves().
168 cpVect *verts, cpPolyline *reduced,
169 int length, int start, int end,
170 cpFloat min, cpFloat tol
172 // Early exit if the points are adjacent
173 if((end - start + length)%length < 2) return reduced;
175 cpVect a = verts[start];
176 cpVect b = verts[end];
178 // Check if the length is below the threshold
179 if(cpvnear(a, b, min) && cpPolylineIsShort(verts, length, start, end, min)) return reduced;
181 // Find the maximal vertex to split and recurse on
185 cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));
186 cpFloat d = cpvdot(n, a);
188 for(int i=Next(start, length); i!=end; i=Next(i, length)){
189 cpFloat dist = fabs(cpvdot(n, verts[i]) - d);
198 reduced = DouglasPeucker(verts, reduced, length, start, maxi, min, tol);
199 reduced = cpPolylinePush(reduced, verts[maxi]);
200 reduced = DouglasPeucker(verts, reduced, length, maxi, end, min, tol);
206 // Recursively reduce the vertex count on a polyline. Works best for smooth shapes.
207 // 'tol' is the maximum error for the reduction.
208 // The reduced polyline will never be farther than this distance from the original polyline.
210 cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol)
212 cpPolyline *reduced = cpPolylineMake(line->count);
214 cpFloat min = tol/2.0f;
216 if(cpPolylineIsClosed(line)){
218 cpLoopIndexes(line->verts, line->count - 1, &start, &end);
220 reduced = cpPolylinePush(reduced, line->verts[start]);
221 reduced = DouglasPeucker(line->verts, reduced, line->count - 1, start, end, min, tol);
222 reduced = cpPolylinePush(reduced, line->verts[end]);
223 reduced = DouglasPeucker(line->verts, reduced, line->count - 1, end, start, min, tol);
224 reduced = cpPolylinePush(reduced, line->verts[start]);
226 reduced = cpPolylinePush(reduced, line->verts[0]);
227 reduced = DouglasPeucker(line->verts, reduced, line->count, 0, line->count - 1, min, tol);
228 reduced = cpPolylinePush(reduced, line->verts[line->count - 1]);
231 return cpPolylineShrink(reduced);
234 //MARK: Polyline Sets
237 cpPolylineSetAlloc(void)
239 return (cpPolylineSet *)cpcalloc(1, sizeof(cpPolylineSet));
243 cpPolylineSetInit(cpPolylineSet *set)
247 set->lines = (cpPolyline**) cpcalloc(set->capacity, sizeof(cpPolyline));
254 cpPolylineSetNew(void)
256 return cpPolylineSetInit(cpPolylineSetAlloc());
260 cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines)
263 for(int i=0; i<set->count; i++){
264 cpPolylineFree(set->lines[i]);
273 cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines)
276 cpPolylineSetDestroy(set, freePolylines);
281 // Find the polyline that ends with v.
283 cpPolylineSetFindEnds(cpPolylineSet *set, cpVect v){
284 int count = set->count;
285 cpPolyline **lines = set->lines;
287 for(int i=0; i<count; i++){
288 cpPolyline *line = lines[i];
289 if(cpveql(line->verts[line->count - 1], v)) return i;
295 // Find the polyline that starts with v.
297 cpPolylineSetFindStarts(cpPolylineSet *set, cpVect v){
298 int count = set->count;
299 cpPolyline **lines = set->lines;
301 for(int i=0; i<count; i++){
302 if(cpveql(lines[i]->verts[0], v)) return i;
308 // Add a new polyline to a polyline set.
310 cpPolylineSetPush(cpPolylineSet *set, cpPolyline *line)
314 if(set->count > set->capacity){
316 set->lines = (cpPolyline**) cprealloc(set->lines, set->capacity*sizeof(cpPolyline));
319 set->lines[set->count - 1] = line;
322 // Add a new polyline to a polyline set.
324 cpPolylineSetAdd(cpPolylineSet *set, cpVect v0, cpVect v1)
326 cpPolylineSetPush(set, cpPolylineMake2(DEFAULT_POLYLINE_CAPACITY, v0, v1));
329 // Join two cpPolylines in a polyline set together.
331 cpPolylineSetJoin(cpPolylineSet *set, int before, int after)
333 cpPolyline *lbefore = set->lines[before];
334 cpPolyline *lafter = set->lines[after];
337 int count = lbefore->count;
338 lbefore = cpPolylineGrow(lbefore, lafter->count);
339 memmove(lbefore->verts + count, lafter->verts, lafter->count*sizeof(cpVect));
340 set->lines[before] = lbefore;
344 cpPolylineFree(set->lines[after]);
345 set->lines[after] = set->lines[set->count];
348 // Add a segment to a polyline set.
349 // A segment will either start a new polyline, join two others, or add to or loop an existing polyline.
351 cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines)
353 int before = cpPolylineSetFindEnds(lines, v0);
354 int after = cpPolylineSetFindStarts(lines, v1);
356 if(before >= 0 && after >= 0){
358 // loop by pushing v1 onto before
359 lines->lines[before] = cpPolylinePush(lines->lines[before], v1);
361 // join before and after
362 cpPolylineSetJoin(lines, before, after);
364 } else if(before >= 0){
365 // push v1 onto before
366 lines->lines[before] = cpPolylinePush(lines->lines[before], v1);
367 } else if(after >= 0){
368 // enqueue v0 onto after
369 lines->lines[after] = cpPolylineEnqueue(lines->lines[after], v0);
371 // create new line from v0 and v1
372 cpPolylineSetAdd(lines, v0, v1);
376 //MARK: Convex Hull Functions
379 cpPolylineToConvexHull(cpPolyline *line, cpFloat tol)
381 cpPolyline *hull = cpPolylineMake(line->count + 1);
382 hull->count = cpConvexHull(line->count, line->verts, hull->verts, NULL, tol);
383 hull = cpPolylinePush(hull, hull->verts[0]);
385 return cpPolylineShrink(hull);
388 //MARK: Approximate Concave Decompostition
398 FindSteiner(int count, cpVect *verts, struct Notch notch)
400 cpFloat min = INFINITY;
401 cpFloat feature = -1.0;
403 for(int i=1; i<count-1; i++){
404 int index = (notch.i + i)%count;
406 cpVect seg_a = verts[index];
407 cpVect seg_b = verts[Next(index, count)];
409 cpFloat thing_a = cpvcross(notch.n, cpvsub(seg_a, notch.v));
410 cpFloat thing_b = cpvcross(notch.n, cpvsub(seg_b, notch.v));
411 if(thing_a*thing_b <= 0.0){
412 cpFloat t = thing_a/(thing_a - thing_b);
413 cpFloat dist = cpvdot(notch.n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));
415 if(dist >= 0.0 && dist <= min){
426 //FindSteiner2(cpVect *verts, int count, struct Notch notch)
428 // cpVect a = verts[(notch.i + count - 1)%count];
429 // cpVect b = verts[(notch.i + 1)%count];
430 // cpVect n = cpvnormalize(cpvadd(cpvnormalize(cpvsub(notch.v, a)), cpvnormalize(cpvsub(notch.v, b))));
432 // cpFloat min = INFINITY;
433 // cpFloat feature = -1.0;
435 // for(int i=1; i<count-1; i++){
436 // int index = (notch.i + i)%count;
438 // cpVect seg_a = verts[index];
439 // cpVect seg_b = verts[Next(index, count)];
441 // cpFloat thing_a = cpvcross(n, cpvsub(seg_a, notch.v));
442 // cpFloat thing_b = cpvcross(n, cpvsub(seg_b, notch.v));
443 // if(thing_a*thing_b <= 0.0){
444 // cpFloat t = thing_a/(thing_a - thing_b);
445 // cpFloat dist = cpvdot(n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));
447 // if(dist >= 0.0 && dist <= min){
449 // feature = index + t;
454 // cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");
458 //struct Range {cpFloat min, max;};
459 //static inline struct Range
460 //clip_range(cpVect delta_a, cpVect delta_b, cpVect clip)
462 // cpFloat da = cpvcross(delta_a, clip);
463 // cpFloat db = cpvcross(delta_b, clip);
464 // cpFloat clamp = da/(da - db);
466 // return (struct Range){-INFINITY, clamp};
467 // } else if(da < db){
468 // return (struct Range){clamp, INFINITY};
470 // return (struct Range){-INFINITY, INFINITY};
475 //FindSteiner3(cpVect *verts, int count, struct Notch notch)
477 // cpFloat min = INFINITY;
478 // cpFloat feature = -1.0;
480 // cpVect support_a = verts[(notch.i - 1 + count)%count];
481 // cpVect support_b = verts[(notch.i + 1)%count];
483 // cpVect clip_a = cpvlerp(support_a, support_b, 0.1);
484 // cpVect clip_b = cpvlerp(support_b, support_b, 0.9);
486 // for(int i=1; i<count - 1; i++){
487 // int index = (notch.i + i)%count;
488 // cpVect seg_a = verts[index];
489 // cpVect seg_b = verts[Next(index, count)];
491 // cpVect delta_a = cpvsub(seg_a, notch.v);
492 // cpVect delta_b = cpvsub(seg_b, notch.v);
494 // // Ignore if the segment faces away from the point.
495 // if(cpvcross(delta_b, delta_a) > 0.0){
496 // struct Range range1 = clip_range(delta_a, delta_b, cpvsub(notch.v, clip_a));
497 // struct Range range2 = clip_range(delta_a, delta_b, cpvsub(clip_b, notch.v));
499 // cpFloat min_t = cpfmax(0.0, cpfmax(range1.min, range2.min));
500 // cpFloat max_t = cpfmin(1.0, cpfmin(range1.max, range2.max));
502 // // Ignore if the segment has been completely clipped away.
503 // if(min_t < max_t){
504 // cpVect seg_delta = cpvsub(seg_b, seg_a);
505 // cpFloat closest_t = cpfclamp(cpvdot(seg_delta, cpvsub(notch.v, seg_a))/cpvlengthsq(seg_delta), min_t, max_t);
506 // cpVect closest = cpvlerp(seg_a, seg_b, closest_t);
508 // cpFloat dist = cpvdistsq(notch.v, closest);
511 // feature = index + closest_t;
517 // cpAssertWarn(feature >= 0.0, "Internal Error: No closest features detected.");
522 //VertexUnobscured(int count, cpVect *verts, int index, int notch_i)
524 // cpVect v = verts[notch_i];
525 // cpVect n = cpvnormalize(cpvsub(verts[index], v));
527 // for(int i=0; i<count; i++){
528 // if(i == index || i == Next(i, count) || i == notch_i || i == Next(notch_i, count)) continue;
530 // cpVect seg_a = verts[i];
531 // cpVect seg_b = verts[Next(i, count)];
533 // cpFloat thing_a = cpvcross(n, cpvsub(seg_a, v));
534 // cpFloat thing_b = cpvcross(n, cpvsub(seg_b, v));
535 // if(thing_a*thing_b <= 0.0) return cpTrue;
542 //FindSteiner4(int count, cpVect *verts, struct Notch notch, cpFloat *convexity)
544 // cpFloat min = INFINITY;
545 // cpFloat feature = -1.0;
547 // for(int i=Next(notch.b, count); i!=notch.a; i=Next(i, count)){
548 // cpVect v = verts[i];
549 // cpFloat weight = (1.0 + 0.1*convexity[i])/(1.0*cpvdist(notch.v, v));
551 // if(weight <= min && VertexUnobscured(count, verts, i, notch.i)){
557 // cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");
562 DeepestNotch(int count, cpVect *verts, int hullCount, cpVect *hullVerts, int first, cpFloat tol)
564 struct Notch notch = {};
565 int j = Next(first, count);
567 for(int i=0; i<hullCount; i++){
568 cpVect a = hullVerts[i];
569 cpVect b = hullVerts[Next(i, hullCount)];
571 // TODO use a cross check instead?
572 cpVect n = cpvnormalize(cpvrperp(cpvsub(a, b)));
573 cpFloat d = cpvdot(n, a);
576 while(!cpveql(v, b)){
577 cpFloat depth = cpvdot(n, v) - d;
596 static inline int IMAX(int a, int b){return (a > b ? a : b);}
599 ApproximateConcaveDecomposition(cpVect *verts, int count, cpFloat tol, cpPolylineSet *set)
602 cpVect *hullVerts = (cpVect*) alloca(count*sizeof(cpVect));
603 int hullCount = cpConvexHull(count, verts, hullVerts, &first, 0.0);
605 if(hullCount != count){
606 struct Notch notch = DeepestNotch(count, verts, hullCount, hullVerts, first, tol);
609 cpFloat steiner_it = FindSteiner(count, verts, notch);
611 if(steiner_it >= 0.0){
612 int steiner_i = (int)steiner_it;
613 cpVect steiner = cpvlerp(verts[steiner_i], verts[Next(steiner_i, count)], steiner_it - steiner_i);
615 // Vertex counts NOT including the steiner point.
616 int sub1_count = (steiner_i - notch.i + count)%count + 1;
617 int sub2_count = count - (steiner_i - notch.i + count)%count;
618 cpVect *scratch = (cpVect*) alloca((IMAX(sub1_count, sub2_count) + 1)*sizeof(cpVect));
620 for(int i=0; i<sub1_count; i++) scratch[i] = verts[(notch.i + i)%count];
621 scratch[sub1_count] = steiner;
622 ApproximateConcaveDecomposition(scratch, sub1_count + 1, tol, set);
624 for(int i=0; i<sub2_count; i++) scratch[i] = verts[(steiner_i + 1 + i)%count];
625 scratch[sub2_count] = steiner;
626 ApproximateConcaveDecomposition(scratch, sub2_count + 1, tol, set);
633 cpPolyline *hull = cpPolylineMake(hullCount + 1);
635 memcpy(hull->verts, hullVerts, hullCount*sizeof(cpVect));
636 hull->verts[hullCount] = hullVerts[0];
637 hull->count = hullCount + 1;
639 cpPolylineSetPush(set, hull);
643 cpPolylineConvexDecomposition_BETA(cpPolyline *line, cpFloat tol)
645 cpAssertSoft(cpPolylineIsClosed(line), "Cannot decompose an open polygon.");
646 cpAssertSoft(cpAreaForPoly(line->count, line->verts, 0.0) >= 0.0, "Winding is backwards. (Are you passing a hole?)");
648 cpPolylineSet *set = cpPolylineSetNew();
649 ApproximateConcaveDecomposition(line->verts, line->count - 1, tol, set);