File indexing completed on 2025-08-03 03:49:55
0001 /* 0002 * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com 0003 * 0004 * This software is provided 'as-is', without any express or implied 0005 * warranty. In no event will the authors be held liable for any damages 0006 * arising from the use of this software. 0007 * Permission is granted to anyone to use this software for any purpose, 0008 * including commercial applications, and to alter it and redistribute it 0009 * freely, subject to the following restrictions: 0010 * 1. The origin of this software must not be misrepresented; you must not 0011 * claim that you wrote the original software. If you use this software 0012 * in a product, an acknowledgment in the product documentation would be 0013 * appreciated but is not required. 0014 * 2. Altered source versions must be plainly marked as such, and must not be 0015 * misrepresented as being the original software. 0016 * 3. This notice may not be removed or altered from any source distribution. 0017 */ 0018 0019 #include <Box2D/Collision/Shapes/b2PolygonShape.h> 0020 #include <new> 0021 0022 b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const 0023 { 0024 void* mem = allocator->Allocate(sizeof(b2PolygonShape)); 0025 b2PolygonShape* clone = new (mem) b2PolygonShape; 0026 *clone = *this; 0027 return clone; 0028 } 0029 0030 void b2PolygonShape::SetAsBox(qreal hx, qreal hy) 0031 { 0032 m_vertexCount = 4; 0033 m_vertices[0].Set(-hx, -hy); 0034 m_vertices[1].Set( hx, -hy); 0035 m_vertices[2].Set( hx, hy); 0036 m_vertices[3].Set(-hx, hy); 0037 m_normals[0].Set(0.0f, -1.0f); 0038 m_normals[1].Set(1.0f, 0.0f); 0039 m_normals[2].Set(0.0f, 1.0f); 0040 m_normals[3].Set(-1.0f, 0.0f); 0041 m_centroid.SetZero(); 0042 } 0043 0044 void b2PolygonShape::SetAsBox(qreal hx, qreal hy, const b2Vec2& center, qreal angle) 0045 { 0046 m_vertexCount = 4; 0047 m_vertices[0].Set(-hx, -hy); 0048 m_vertices[1].Set( hx, -hy); 0049 m_vertices[2].Set( hx, hy); 0050 m_vertices[3].Set(-hx, hy); 0051 m_normals[0].Set(0.0f, -1.0f); 0052 m_normals[1].Set(1.0f, 0.0f); 0053 m_normals[2].Set(0.0f, 1.0f); 0054 m_normals[3].Set(-1.0f, 0.0f); 0055 m_centroid = center; 0056 0057 b2Transform xf; 0058 xf.position = center; 0059 xf.R.Set(angle); 0060 0061 // Transform vertices and normals. 0062 for (int32 i = 0; i < m_vertexCount; ++i) 0063 { 0064 m_vertices[i] = b2Mul(xf, m_vertices[i]); 0065 m_normals[i] = b2Mul(xf.R, m_normals[i]); 0066 } 0067 } 0068 0069 int32 b2PolygonShape::GetChildCount() const 0070 { 0071 return 1; 0072 } 0073 0074 static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count) 0075 { 0076 b2Assert(count >= 3); 0077 0078 b2Vec2 c; c.Set(0.0f, 0.0f); 0079 qreal area = 0.0f; 0080 0081 // pRef is the reference point for forming triangles. 0082 // It's location doesn't change the result (except for rounding error). 0083 b2Vec2 pRef(0.0f, 0.0f); 0084 #if 0 0085 // This code would put the reference point inside the polygon. 0086 for (int32 i = 0; i < count; ++i) 0087 { 0088 pRef += vs[i]; 0089 } 0090 pRef *= 1.0f / count; 0091 #endif 0092 0093 const qreal inv3 = 1.0f / 3.0f; 0094 0095 for (int32 i = 0; i < count; ++i) 0096 { 0097 // Triangle vertices. 0098 b2Vec2 p1 = pRef; 0099 b2Vec2 p2 = vs[i]; 0100 b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0]; 0101 0102 b2Vec2 e1 = p2 - p1; 0103 b2Vec2 e2 = p3 - p1; 0104 0105 qreal D = b2Cross(e1, e2); 0106 0107 qreal triangleArea = qAbs(0.5f * D); 0108 area += triangleArea; 0109 0110 // Area weighted centroid 0111 c += triangleArea * inv3 * (p1 + p2 + p3); 0112 } 0113 0114 // Centroid 0115 b2Assert(area > b2_epsilon); 0116 c *= 1.0f / area; 0117 return c; 0118 } 0119 0120 void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) 0121 { 0122 b2Assert(3 <= count && count <= b2_maxPolygonVertices); 0123 m_vertexCount = count; 0124 0125 // Copy vertices. 0126 for (int32 i = 0; i < m_vertexCount; ++i) 0127 { 0128 m_vertices[i] = vertices[i]; 0129 } 0130 0131 // Compute normals. Ensure the edges have non-zero length. 0132 for (int32 i = 0; i < m_vertexCount; ++i) 0133 { 0134 int32 i1 = i; 0135 int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0; 0136 b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; 0137 b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon); 0138 m_normals[i] = b2Cross(edge, 1.0f); 0139 m_normals[i].Normalize(); 0140 } 0141 0142 #ifdef _DEBUG 0143 // Ensure the polygon is convex and the interior 0144 // is to the left of each edge. 0145 for (int32 i = 0; i < m_vertexCount; ++i) 0146 { 0147 int32 i1 = i; 0148 int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0; 0149 b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; 0150 0151 for (int32 j = 0; j < m_vertexCount; ++j) 0152 { 0153 // Don't check vertices on the current edge. 0154 if (j == i1 || j == i2) 0155 { 0156 continue; 0157 } 0158 0159 b2Vec2 r = m_vertices[j] - m_vertices[i1]; 0160 0161 // Your polygon is non-convex (it has an indentation) or 0162 // has collinear edges. 0163 qreal s = b2Cross(edge, r); 0164 b2Assert(s > 0.0f); 0165 } 0166 } 0167 #endif 0168 0169 // Compute the polygon centroid. 0170 m_centroid = ComputeCentroid(m_vertices, m_vertexCount); 0171 } 0172 0173 bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const 0174 { 0175 b2Vec2 pLocal = b2MulT(xf.R, p - xf.position); 0176 0177 for (int32 i = 0; i < m_vertexCount; ++i) 0178 { 0179 qreal dot = b2Dot(m_normals[i], pLocal - m_vertices[i]); 0180 if (dot > 0.0f) 0181 { 0182 return false; 0183 } 0184 } 0185 0186 return true; 0187 } 0188 0189 bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, 0190 const b2Transform& xf, int32 childIndex) const 0191 { 0192 B2_NOT_USED(childIndex); 0193 0194 // Put the ray into the polygon's frame of reference. 0195 b2Vec2 p1 = b2MulT(xf.R, input.p1 - xf.position); 0196 b2Vec2 p2 = b2MulT(xf.R, input.p2 - xf.position); 0197 b2Vec2 d = p2 - p1; 0198 0199 qreal lower = 0.0f, upper = input.maxFraction; 0200 0201 int32 index = -1; 0202 0203 for (int32 i = 0; i < m_vertexCount; ++i) 0204 { 0205 // p = p1 + a * d 0206 // dot(normal, p - v) = 0 0207 // dot(normal, p1 - v) + a * dot(normal, d) = 0 0208 qreal numerator = b2Dot(m_normals[i], m_vertices[i] - p1); 0209 qreal denominator = b2Dot(m_normals[i], d); 0210 0211 if (denominator == 0.0f) 0212 { 0213 if (numerator < 0.0f) 0214 { 0215 return false; 0216 } 0217 } 0218 else 0219 { 0220 // Note: we want this predicate without division: 0221 // lower < numerator / denominator, where denominator < 0 0222 // Since denominator < 0, we have to flip the inequality: 0223 // lower < numerator / denominator <==> denominator * lower > numerator. 0224 if (denominator < 0.0f && numerator < lower * denominator) 0225 { 0226 // Increase lower. 0227 // The segment enters this half-space. 0228 lower = numerator / denominator; 0229 index = i; 0230 } 0231 else if (denominator > 0.0f && numerator < upper * denominator) 0232 { 0233 // Decrease upper. 0234 // The segment exits this half-space. 0235 upper = numerator / denominator; 0236 } 0237 } 0238 0239 // The use of epsilon here causes the assert on lower to trip 0240 // in some cases. Apparently the use of epsilon was to make edge 0241 // shapes work, but now those are handled separately. 0242 //if (upper < lower - b2_epsilon) 0243 if (upper < lower) 0244 { 0245 return false; 0246 } 0247 } 0248 0249 b2Assert(0.0f <= lower && lower <= input.maxFraction); 0250 0251 if (index >= 0) 0252 { 0253 output->fraction = lower; 0254 output->normal = b2Mul(xf.R, m_normals[index]); 0255 return true; 0256 } 0257 0258 return false; 0259 } 0260 0261 void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const 0262 { 0263 B2_NOT_USED(childIndex); 0264 0265 b2Vec2 lower = b2Mul(xf, m_vertices[0]); 0266 b2Vec2 upper = lower; 0267 0268 for (int32 i = 1; i < m_vertexCount; ++i) 0269 { 0270 b2Vec2 v = b2Mul(xf, m_vertices[i]); 0271 lower = b2Min(lower, v); 0272 upper = b2Max(upper, v); 0273 } 0274 0275 b2Vec2 r(m_radius, m_radius); 0276 aabb->lowerBound = lower - r; 0277 aabb->upperBound = upper + r; 0278 } 0279 0280 void b2PolygonShape::ComputeMass(b2MassData* massData, qreal density) const 0281 { 0282 // Polygon mass, centroid, and inertia. 0283 // Let rho be the polygon density in mass per unit area. 0284 // Then: 0285 // mass = rho * int(dA) 0286 // centroid.x = (1/mass) * rho * int(x * dA) 0287 // centroid.y = (1/mass) * rho * int(y * dA) 0288 // I = rho * int((x*x + y*y) * dA) 0289 // 0290 // We can compute these integrals by summing all the integrals 0291 // for each triangle of the polygon. To evaluate the integral 0292 // for a single triangle, we make a change of variables to 0293 // the (u,v) coordinates of the triangle: 0294 // x = x0 + e1x * u + e2x * v 0295 // y = y0 + e1y * u + e2y * v 0296 // where 0 <= u && 0 <= v && u + v <= 1. 0297 // 0298 // We integrate u from [0,1-v] and then v from [0,1]. 0299 // We also need to use the Jacobian of the transformation: 0300 // D = cross(e1, e2) 0301 // 0302 // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) 0303 // 0304 // The rest of the derivation is handled by computer algebra. 0305 0306 b2Assert(m_vertexCount >= 3); 0307 0308 b2Vec2 center; center.Set(0.0f, 0.0f); 0309 qreal area = 0.0f; 0310 qreal I = 0.0f; 0311 0312 // pRef is the reference point for forming triangles. 0313 // It's location doesn't change the result (except for rounding error). 0314 b2Vec2 pRef(0.0f, 0.0f); 0315 #if 0 0316 // This code would put the reference point inside the polygon. 0317 for (int32 i = 0; i < m_vertexCount; ++i) 0318 { 0319 pRef += m_vertices[i]; 0320 } 0321 pRef *= 1.0f / count; 0322 #endif 0323 0324 const qreal k_inv3 = 1.0f / 3.0f; 0325 0326 for (int32 i = 0; i < m_vertexCount; ++i) 0327 { 0328 // Triangle vertices. 0329 b2Vec2 p1 = pRef; 0330 b2Vec2 p2 = m_vertices[i]; 0331 b2Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i+1] : m_vertices[0]; 0332 0333 b2Vec2 e1 = p2 - p1; 0334 b2Vec2 e2 = p3 - p1; 0335 0336 qreal D = b2Cross(e1, e2); 0337 0338 qreal triangleArea = 0.5f * D; 0339 area += triangleArea; 0340 0341 // Area weighted centroid 0342 center += triangleArea * k_inv3 * (p1 + p2 + p3); 0343 0344 qreal px = p1.x, py = p1.y; 0345 qreal ex1 = e1.x, ey1 = e1.y; 0346 qreal ex2 = e2.x, ey2 = e2.y; 0347 0348 qreal intx2 = k_inv3 * (0.25f * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5f*px*px; 0349 qreal inty2 = k_inv3 * (0.25f * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5f*py*py; 0350 0351 I += D * (intx2 + inty2); 0352 } 0353 0354 // Total mass 0355 massData->mass = density * area; 0356 0357 // Center of mass 0358 b2Assert(area > b2_epsilon); 0359 center *= 1.0f / area; 0360 massData->center = center; 0361 0362 // Inertia tensor relative to the local origin. 0363 massData->I = density * I; 0364 }