1 /* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 #include "chipmunk/chipmunk_private.h"
23 #include "chipmunk/chipmunk_unsafe.h"
26 cpPolyShapeAlloc(void)
28 return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape));
32 cpPolyShapeDestroy(cpPolyShape *poly)
34 if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){
40 cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)
42 int count = poly->count;
43 struct cpSplittingPlane *dst = poly->planes;
44 struct cpSplittingPlane *src = dst + count;
46 cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;
47 cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;
49 for(int i=0; i<count; i++){
50 cpVect v = cpTransformPoint(transform, src[i].v0);
51 cpVect n = cpTransformVect(transform, src[i].n);
62 cpFloat radius = poly->r;
63 return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius));
67 cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){
68 int count = poly->count;
69 struct cpSplittingPlane *planes = poly->planes;
72 cpVect v0 = planes[count - 1].v0;
73 cpFloat minDist = INFINITY;
74 cpVect closestPoint = cpvzero;
75 cpVect closestNormal = cpvzero;
76 cpBool outside = cpFalse;
78 for(int i=0; i<count; i++){
79 cpVect v1 = planes[i].v0;
80 outside = outside || (cpvdot(planes[i].n, cpvsub(p,v1)) > 0.0f);
82 cpVect closest = cpClosetPointOnSegment(p, v0, v1);
84 cpFloat dist = cpvdist(p, closest);
87 closestPoint = closest;
88 closestNormal = planes[i].n;
94 cpFloat dist = (outside ? minDist : -minDist);
95 cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist);
97 info->shape = (cpShape *)poly;
98 info->point = cpvadd(closestPoint, cpvmult(g, r));
99 info->distance = dist - r;
101 // Use the normal of the closest segment if the distance is small.
102 info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal);
106 cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
108 struct cpSplittingPlane *planes = poly->planes;
109 int count = poly->count;
111 cpFloat rsum = r + r2;
113 for(int i=0; i<count; i++){
114 cpVect n = planes[i].n;
115 cpFloat an = cpvdot(a, n);
116 cpFloat d = an - cpvdot(planes[i].v0, n) - rsum;
117 if(d < 0.0f) continue;
119 cpFloat bn = cpvdot(b, n);
120 cpFloat t = d/(an - bn);
121 if(t < 0.0f || 1.0f < t) continue;
123 cpVect point = cpvlerp(a, b, t);
124 cpFloat dt = cpvcross(n, point);
125 cpFloat dtMin = cpvcross(n, planes[(i - 1 + count)%count].v0);
126 cpFloat dtMax = cpvcross(n, planes[i].v0);
128 if(dtMin <= dt && dt <= dtMax){
129 info->shape = (cpShape *)poly;
130 info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));
136 // Also check against the beveled vertexes.
138 for(int i=0; i<count; i++){
139 cpSegmentQueryInfo circle_info = {NULL, b, cpvzero, 1.0f};
140 CircleSegmentQuery(&poly->shape, planes[i].v0, r, a, b, r2, &circle_info);
141 if(circle_info.alpha < info->alpha) (*info) = circle_info;
147 SetVerts(cpPolyShape *poly, int count, const cpVect *verts)
150 if(count <= CP_POLY_SHAPE_INLINE_ALLOC){
151 poly->planes = poly->_planes;
153 poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane));
156 for(int i=0; i<count; i++){
157 cpVect a = verts[(i - 1 + count)%count];
159 cpVect n = cpvnormalize(cpvrperp(cpvsub(b, a)));
161 poly->planes[i + count].v0 = b;
162 poly->planes[i + count].n = n;
166 static struct cpShapeMassInfo
167 cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius)
169 // TODO moment is approximate due to radius.
171 cpVect centroid = cpCentroidForPoly(count, verts);
172 struct cpShapeMassInfo info = {
173 mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius),
175 cpAreaForPoly(count, verts, radius),
181 static const cpShapeClass polyClass = {
183 (cpShapeCacheDataImpl)cpPolyShapeCacheData,
184 (cpShapeDestroyImpl)cpPolyShapeDestroy,
185 (cpShapePointQueryImpl)cpPolyShapePointQuery,
186 (cpShapeSegmentQueryImpl)cpPolyShapeSegmentQuery,
190 cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
192 cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
194 // Transform the verts before building the hull in case of a negative scale.
195 for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
197 unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
198 return cpPolyShapeInitRaw(poly, body, hullCount, hullVerts, radius);
202 cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)
204 cpShapeInit((cpShape *)poly, &polyClass, body, cpPolyShapeMassInfo(0.0f, count, verts, radius));
206 SetVerts(poly, count, verts);
213 cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
215 return (cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), body, count, verts, transform, radius);
219 cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius)
221 return (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), body, count, verts, radius);
225 cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
227 cpFloat hw = width/2.0f;
228 cpFloat hh = height/2.0f;
230 return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius);
234 cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)
243 return cpPolyShapeInitRaw(poly, body, 4, verts, radius);
247 cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
249 return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius);
253 cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius)
255 return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius);
259 cpPolyShapeGetCount(const cpShape *shape)
261 cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
262 return ((cpPolyShape *)shape)->count;
266 cpPolyShapeGetVert(const cpShape *shape, int i)
268 cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
270 int count = cpPolyShapeGetCount(shape);
271 cpAssertHard(0 <= i && i < count, "Index out of range.");
273 return ((cpPolyShape *)shape)->planes[i + count].v0;
277 cpPolyShapeGetRadius(const cpShape *shape)
279 cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
280 return ((cpPolyShape *)shape)->r;
283 // Unsafe API (chipmunk_unsafe.h)
286 cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform)
288 cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
290 // Transform the verts before building the hull in case of a negative scale.
291 for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
293 unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
294 cpPolyShapeSetVertsRaw(shape, hullCount, hullVerts);
298 cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts)
300 cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
301 cpPolyShape *poly = (cpPolyShape *)shape;
302 cpPolyShapeDestroy(poly);
304 SetVerts(poly, count, verts);
306 cpFloat mass = shape->massInfo.m;
307 shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r);
308 if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
312 cpPolyShapeSetRadius(cpShape *shape, cpFloat radius)
314 cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
315 cpPolyShape *poly = (cpPolyShape *)shape;
319 // TODO radius is not handled by moment/area
320 // cpFloat mass = shape->massInfo.m;
321 // shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r);
322 // if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);