Fx2D
A C++20 2D rigid-body physics engine
Loading...
Searching...
No Matches
Geometry.h
Go to the documentation of this file.
1#pragma once
2
3#include "Fx2D/Math.h"
4
5#include <memory>
6
7// Geometry built on the vector and array types in Math.h: bounding boxes, the unified shape,
8// and the primitive queries that operate on them.
9
10// Closest point on segment [a,b] to p.
11static inline FxVec2f FxClosestOnSegment(const FxVec2f& a, const FxVec2f& b, const FxVec2f& p) {
12 FxVec2f ab = b - a;
13 float len2 = ab.dot(ab);
14 if (len2 < 1e-12f) return a;
15 float t = std::clamp((p - a).dot(ab) / len2, 0.0f, 1.0f);
16 return a + t * ab;
17}
18
19// Axis-aligned bounding box in 2D world coordinates
20struct FxAABB {
21 float minX = 0.0f, minY = 0.0f, maxX = 0.0f, maxY = 0.0f;
22 FxAABB() = default;
23 FxAABB(float mnX, float mnY, float mxX, float mxY) :
24 minX(mnX), minY(mnY), maxX(mxX), maxY(mxY) {}
25 static FxAABB combine(const FxAABB& a, const FxAABB& b) {
26 return {std::min(a.minX, b.minX), std::min(a.minY, b.minY), std::max(a.maxX, b.maxX),
27 std::max(a.maxY, b.maxY)};
28 }
29 FxAABB fatten(float margin) const {
30 return {minX - margin, minY - margin, maxX + margin, maxY + margin};
31 }
32 float perimeter() const { return (maxX - minX) + (maxY - minY); }
33 bool overlaps(const FxAABB& o) const {
34 return maxX >= o.minX && o.maxX >= minX && maxY >= o.minY && o.maxY >= minY;
35 }
36 bool contains(const FxAABB& inner) const {
37 return minX <= inner.minX && minY <= inner.minY && maxX >= inner.maxX && maxY >= inner.maxY;
38 }
39 bool is_valid() const { return maxX > minX && maxY > minY; }
40};
41
42// Custom 2x2 float matrix with .a(), .b(), .c(), .d() getters and corresponding setters.
43// Unified shape: vertices (0/2/N) + skin_radius (circle/capsule/edge/polygon/rounded).
45
46struct FxShape {
47 protected:
48 FxShapeType m_shape_type; // Circle, Capsule, Polygon, or Chain
49 float m_radius; // bounding radius from centroid (skin-inclusive)
50 float m_skin_radius = 0.0f; // Minkowski-sum skin (rounding) radius
51 FxVec2fArray m_vertices; // local vertices: 0 (circle), 2 (capsule), or >=3 (polygon)
52 FxVec3f m_offset_pose{0.0f, 0.0f, 0.0f}; // initial offset pose in world coordinates
53 FxVec3f m_world_pose{0.0f, 0.0f, 0.0f}; // current pose in the world
54 FxVec2f m_centroid{0.0f, 0.0f}; //
56
57 // 1) Compute the bounding radius from (0,0)
58 static float calc_radius(const FxVec2fArray& verts) {
59 float maxSq = 0.0f;
60 for (size_t i = 0; i < verts.size(); ++i) {
61 const FxVec2f& v = verts[i];
62 float d2 = v.x() * v.x() + v.y() * v.y();
63 if (d2 > maxSq) maxSq = d2;
64 }
65 return std::sqrt(maxSq);
66 }
67
68 // 3) Convexity: all cross‐products have same sign
69 static bool is_convex(const FxVec2fArray& verts) {
70 size_t n = verts.size();
71 bool gotPos = false, gotNeg = false;
72 for (size_t i = 0; i < n; ++i) {
73 const FxVec2f& A = verts[i];
74 const FxVec2f& B = verts[(i + 1) % n];
75 const FxVec2f& C = verts[(i + 2) % n];
76 float cross = (B.x() - A.x()) * (C.y() - B.y()) - (B.y() - A.y()) * (C.x() - B.x());
77 if (cross > 0) gotPos = true;
78 else if (cross < 0) gotNeg = true;
79 if (gotPos && gotNeg) return false;
80 }
81 return true;
82 }
83
84 public:
85 // default ctor
87
88 // –– Circle ctor: unified as a 0-vertex shape with skin_radius = radius
89 FxShape(float radius) {
90 if (radius <= 1e-6f) throw std::invalid_argument("FxShape: radius must be > 0");
94 }
95
96 // –– Capsule ctor: segment of given length (along x in local frame) with end-cap radius.
97 // length == 0 collapses to a circle of the same radius. radius == 0 yields a bare segment.
98 FxShape(float length, float radius) {
99 if (radius < 0.0f) throw std::invalid_argument("FxShape: capsule radius must be >= 0");
100 if (length < 0.0f) throw std::invalid_argument("FxShape: capsule length must be >= 0");
101 if (length <= 1e-6f && radius <= 1e-6f)
102 throw std::invalid_argument("FxShape: degenerate capsule (zero length and radius)");
103 const float hl = length * 0.5f;
105 m_vertices = {{-hl, 0.0f}, {hl, 0.0f}};
107 m_radius = hl + radius;
109 }
110
111 // –– Edge ctor: zero-thickness segment between two local-frame endpoints.
112 // Stored as a capsule with skin_radius = 0; endpoints are kept as given so the
113 // body origin stays where the scene author placed it.
114 FxShape(const FxVec2f& a, const FxVec2f& b) {
115 if ((b - a).norm() <= 1e-6f)
116 throw std::invalid_argument("FxShape: edge endpoints must be distinct");
118 m_vertices = {a, b};
119 m_skin_radius = 0.0f;
122 }
123
124 // Chain: an open polyline of >= 2 segments, for static level geometry a convex polygon
125 // approximates badly. Like an edge it has no interior, so no area and no inertia, and like
126 // an edge its vertices are kept exactly as authored rather than recentred.
127 struct ChainTag {};
128 FxShape(const FxVec2fArray& points, ChainTag) {
129 if (points.size() < 3)
130 throw std::invalid_argument("FxShape: a chain needs at least 3 points");
131 for (std::size_t i = 1; i < points.size(); ++i) {
132 if ((points[i] - points[i - 1]).norm() <= 1e-6f)
133 throw std::invalid_argument("FxShape: chain points must be distinct");
134 }
136 m_vertices = points;
137 m_skin_radius = 0.0f;
140 }
141
142 // –– Polygon from arbitrary vertices, with optional uniform skin (rounding) radius
143 FxShape(const FxVec2fArray& vertices, float skin_radius = 0.0f) {
144 constexpr float minArea = 1e-6f;
145 if (vertices.size() < 3) throw std::invalid_argument("FxShape: less than 3 vertices");
146 if (skin_radius < 0.0f) throw std::invalid_argument("FxShape: skin radius must be >= 0");
147 float area = polygon_area(vertices);
148 if (std::fabs(area) <= minArea) throw std::invalid_argument("FxShape: area ≤ 2e-6");
149 if (!is_convex(vertices)) throw std::invalid_argument("FxShape: not convex");
151 // centroid will be pushed to {0.0f, 0.0f}
152 FxVec2fArray verts = vertices;
153 if (area > 0.0f) { // saved in CCW order only
154 std::reverse(verts.begin(), verts.end());
155 }
156 m_vertices = verts - verts.mean();
160 }
161
162 // –– Rectangle centered at origin, width=size.x(), height=size.y(); optional rounded corners
163 FxShape(const FxVec2f& size, float skin_radius = 0.0f) {
164 if (size.x() <= 0.0f || size.y() <= 0.0f)
165 throw std::invalid_argument("FxShape: dimensions must be > 0");
166 if (skin_radius < 0.0f) throw std::invalid_argument("FxShape: skin radius must be >= 0");
167 float hx = size.x() * 0.5f;
168 float hy = size.y() * 0.5f;
169 // Check for valid area
170 if (hx * hy <= 1e-6f) throw std::runtime_error("FxShape: degenerate rectangle");
171 // build CCW rectangle around (0, 0)
172 m_vertices = {{-hx, -hy}, {-hx, hy}, {hx, hy}, {hx, -hy}};
175 m_radius = std::sqrt(hx * hx + hy * hy) + skin_radius;
177 }
178
179 // Shoelace signed area of a vertex loop: >0 counter-clockwise, <0 clockwise.
180 static float polygon_area(const FxVec2fArray& verts) {
181 double sum = 0.0;
182 const size_t n = verts.size();
183 for (size_t i = 0; i < n; ++i) {
184 const FxVec2f& a = verts[i];
185 const FxVec2f& b = verts[(i + 1) % n];
186 sum += double(a.x()) * b.y() - double(b.x()) * a.y();
187 }
188 return float(0.5 * sum);
189 }
190
191 // getters for shape properties
193 float radius() const { return m_radius; }
194 float skin_radius() const { return m_skin_radius; }
195 // By reference: returning by value copied every vertex into a fresh aligned allocation,
196 // invisibly, because every caller writes `const auto& v = shape->vertices()`.
197 const FxVec2fArray& vertices() const { return m_world_vertices; }
199 return m_vertices;
200 } // native coordinates of vertices with centroid as (0,0)
201 FxVec2f centroid() const { return m_centroid; }
202
203 // methods to check shape type
204 bool is_circle() const { return m_shape_type == FxShapeType::Circle; }
205
206 bool is_capsule() const { return m_shape_type == FxShapeType::Capsule; }
207
208 bool is_polygon() const { return m_shape_type == FxShapeType::Polygon; }
209
210 // Builds an open polyline collider from >= 3 points.
211 static FxShape make_chain(const FxVec2fArray& points) { return FxShape(points, ChainTag{}); }
212
213 bool is_chain() const { return m_shape_type == FxShapeType::Chain; }
214
215 // Segments in a chain; 0 for every other shape.
216 std::size_t segment_count() const { return is_chain() ? m_world_vertices.size() - 1 : 0; }
217
218 // Segment i of a chain as a standalone edge, positioned in the world.
219 FxShape segment(std::size_t i) const {
220 return FxShape(m_world_vertices[i], m_world_vertices[i + 1]);
221 }
222
223 // A zero-skin capsule is a bare segment: zero area, zero inertia, static level geometry.
224 bool is_edge() const { return m_shape_type == FxShapeType::Capsule && m_skin_radius <= 1e-6f; }
225
226 // Is the world point inside this shape, skin included? Convex only, which every FxShape is.
227 bool contains(const FxVec2f& p) const {
228 if (is_chain()) return false;
229 const size_t n = m_world_vertices.size();
230 if (n == 0) return (p - m_centroid).norm() <= m_skin_radius;
231 if (n == 2) {
232 const FxVec2f closest = FxClosestOnSegment(m_world_vertices[0], m_world_vertices[1], p);
233 return (p - closest).norm() <= m_skin_radius;
234 }
235 // Same side of every edge. Accepting either sign keeps it winding-agnostic; the skin is
236 // exact away from the corners and conservative at them.
237 bool all_left = true, all_right = true;
238 for (size_t i = 0; i < n; ++i) {
239 const FxVec2f& a = m_world_vertices[i];
240 const FxVec2f& b = m_world_vertices[(i + 1) % n];
241 const FxVec2f edge = b - a;
242 const float len = edge.norm();
243 if (len < 1e-8f) continue;
244 const float side = edge.cross(p - a) / len;
245 if (side < -m_skin_radius) all_left = false;
246 if (side > m_skin_radius) all_right = false;
247 }
248 return all_left || all_right;
249 }
250
251 // Get area of the shape (handles circle, capsule, and polygon — skin radius included)
252 float area() const {
253 if (is_chain()) return 0.0f;
254 if (is_circle()) {
255 return FxPif * m_skin_radius * m_skin_radius;
256 }
257 if (is_capsule()) {
258 // Minkowski sum of segment (length L) with disc (radius r):
259 // area = pi r^2 (two end caps form one full disc) + 2 r L (central rectangle)
260 const float L = (m_vertices[1] - m_vertices[0]).norm();
261 return FxPif * m_skin_radius * m_skin_radius + 2.0f * m_skin_radius * L;
262 }
263 // Polygon: raw polygon area + (skin contribution if rounded)
264 const float core = std::abs(polygon_area(m_world_vertices));
265 if (m_skin_radius <= 0.0f) return core;
266 // Skin contribution = perimeter * r + pi r^2 (full disc from summing exterior corner
267 // angles)
268 float perim = 0.0f;
269 const auto& V = m_vertices;
270 for (std::size_t i = 0, n = V.size(); i < n; ++i) {
271 perim += (V[(i + 1) % n] - V[i]).norm();
272 }
273 return core + perim * m_skin_radius + FxPif * m_skin_radius * m_skin_radius;
274 }
275
276 // Calculate moment of inertia for given mass (uniform density)
277 float calc_inertia(float mass) const {
278 if (is_chain()) return 0.0f;
279 if (is_circle()) {
280 return 0.5f * mass * m_skin_radius * m_skin_radius;
281 }
282 if (is_capsule()) {
283 // Uniform-density capsule: rectangle (L x 2r) + full disc (radius r) split between
284 // caps.
285 const float L = (m_vertices[1] - m_vertices[0]).norm();
286 const float r = m_skin_radius;
287 const float A_rect = 2.0f * r * L;
288 const float A_caps = FxPif * r * r;
289 const float A_tot = A_rect + A_caps;
290 if (A_tot < 1e-6f) return 0.0f;
291 const float m_rect = mass * (A_rect / A_tot);
292 const float m_caps = mass * (A_caps / A_tot);
293 // Rectangle about its centroid (capsule center): I = m * (L^2 + (2r)^2) / 12
294 const float I_rect = m_rect * (L * L + 4.0f * r * r) / 12.0f;
295 // Two half-discs offset by L/2 from capsule center; parallel-axis theorem.
296 const float I_caps = 0.5f * m_caps * r * r + m_caps * (L * 0.5f) * (L * 0.5f);
297 return I_rect + I_caps;
298 }
299 // Polygon (with optional skin):
300 const std::size_t n = m_vertices.size();
301 float signed_twice_area = 0.0f;
302 float accum = 0.0f;
303 for (std::size_t i = 0; i < n; ++i) {
304 const FxVec2f& a = m_vertices[i];
305 const FxVec2f& b = m_vertices[(i + 1) % n];
306 const float cross = a.x() * b.y() - b.x() * a.y();
307 signed_twice_area += cross;
308 const float x2 = a.x() * a.x() + a.x() * b.x() + b.x() * b.x();
309 const float y2 = a.y() * a.y() + a.y() * b.y() + b.y() * b.y();
310 accum += cross * (x2 + y2);
311 }
312 float core_area = std::abs(signed_twice_area * 0.5f);
313 if (core_area < 1e-6f) return 0.0f;
314
315 if (m_skin_radius <= 0.0f) {
316 const float density = mass / core_area;
317 return (density / 12.0f) * std::abs(accum);
318 }
319 // Rounded polygon: keep the bare polygon inertia and add a uniform skin ring approximation.
320 // The skin contributes mass roughly at the average vertex radius + skin_radius.
321 const float total_area = area();
322 const float density = mass / total_area;
323 const float I_core = (density / 12.0f) * std::abs(accum);
324 // Skin mass approximated as a ring at the bounding radius.
325 const float m_skin = mass - density * core_area;
326 const float r_eff_sq = m_radius * m_radius - m_skin_radius * m_skin_radius * 0.5f;
327 return I_core + m_skin * std::max(0.0f, r_eff_sq);
328 }
329
330 // offset pose setter and getter
331 void set_offset_pose(const FxVec3f& o_pose) { m_offset_pose = o_pose; }
333
334 // Returns current axis aligned bounding box of the shape and sets world pose
336 FxArray<float> bb(4);
338 return bb;
339 }
340
341 // Allocation-free form, for the caller that runs per entity per substep: the form above
342 // builds three temporary FxArrays. `out_aabb` must already hold four elements and is
343 // written as {minX, minY, maxX, maxY}.
347 if (is_circle()) {
348 const float pX = m_centroid.x();
349 const float pY = m_centroid.y();
350 const float r = m_skin_radius;
351 out_aabb[0] = pX - r;
352 out_aabb[1] = pY - r;
353 out_aabb[2] = pX + r;
354 out_aabb[3] = pY + r;
355 return;
356 }
357
358 // Capsule and polygon: rotate local vertices into the world frame, then inflate the
359 // AABB by the skin. Same arithmetic and same order as FxVec2f::rotate_inplace_rad
360 // followed by a translate, so results are unchanged to the bit.
361 const std::size_t n = m_vertices.size();
363 if (n == 0) return;
364
365 const float theta = world_pose.theta() + m_offset_pose.theta();
366 const float cos_t = std::cos(theta), sin_t = std::sin(theta);
367 const float cx = m_centroid.x(), cy = m_centroid.y();
368
369 float min_x = FxInfinityf, min_y = FxInfinityf;
370 float max_x = -FxInfinityf, max_y = -FxInfinityf;
371 for (std::size_t i = 0; i < n; ++i) {
372 const float xi = m_vertices[i].x(), yi = m_vertices[i].y();
373 const float x = xi * cos_t - yi * sin_t + cx;
374 const float y = xi * sin_t + yi * cos_t + cy;
375 m_world_vertices[i] = FxVec2f(x, y);
376 if (x < min_x) min_x = x;
377 if (x > max_x) max_x = x;
378 if (y < min_y) min_y = y;
379 if (y > max_y) max_y = y;
380 }
381
382 out_aabb[0] = min_x - m_skin_radius;
383 out_aabb[1] = min_y - m_skin_radius;
384 out_aabb[2] = max_x + m_skin_radius;
385 out_aabb[3] = max_y + m_skin_radius;
386 }
387
388 // Getter for the current world pose of the shape
389 FxVec3f world_pose() const { return m_world_pose; }
390
391 // Set the position (xy) of the shape in world coordinates (preserving rotation)
392 void set_position(const FxVec2f& pos) {
393 m_world_pose.set_xy(pos);
395 }
396
397 // Set the rotation (theta) of the shape in world coordinates (preserving position)
398 void set_rotation(float theta) {
399 m_world_pose.set_theta(theta);
401 }
402
403 // Move the shape by a delta in world coordinates
404 void move(const FxVec2f& delta) {
407 }
408
409 // Rotate the shape by a delta angle (in radians)
410 void rotate(float delta_theta) {
411 m_world_pose.set_theta(m_world_pose.theta() + delta_theta);
413 }
414
415 // Smallest projection of (vertex - origin) onto axis, with the index that achieved it. No
416 // skin applied; SAT subtracts both skin radii explicitly. A running minimum, deliberately:
417 // the array-building spelling put ~11% of a box-stack step in malloc and free.
418 std::pair<std::size_t, float> min_projection(const FxVec2f& axis, const FxVec2f& origin) const {
419 if (is_circle()) {
420 // A circle projects as a single point; the caller applies its radius via the skin.
421 return {0, (m_centroid - origin).dot(axis)};
422 }
423 const std::size_t n = m_world_vertices.size();
424 if (n == 0) return {0, FxInfinityf};
425
426 // Scalars rather than (vertex - origin).dot(axis): the same arithmetic, but Eigen's
427 // expression templates showed up across the profile for a loop this hot. Same operands
428 // in the same order, so with FP contraction pinned off it is bit-identical.
429 const float ox = origin.x(), oy = origin.y();
430 const float ax = axis.x(), ay = axis.y();
431 std::size_t best_index = 0;
432 float best = FxInfinityf;
433 for (std::size_t i = 0; i < n; ++i) {
434 const float px = m_world_vertices[i].x(), py = m_world_vertices[i].y();
435 const float projection = (px - ox) * ax + (py - oy) * ay;
436 if (projection < best) {
437 best = projection;
438 best_index = i;
439 }
440 }
441 return {best_index, best};
442 }
443
444 // get the closest vertex of the shape from a point (returns surface point, skin-inclusive)
445 FxVec2f get_closest_vertex(const FxVec2f& point) const {
446 if (is_circle()) {
447 FxVec2f v = point - m_centroid; // vector from center to query point
448 FxVec2f dir;
449 if (v.dot(v) < 1e-6f) dir = FxVec2f(1.0f, 0.0f); // arbitrary unit vector
450 else dir = v.normalized(); // safe to normalize
451 return m_centroid + dir * m_skin_radius;
452 }
453 if (is_capsule()) {
454 // Closest point on the capsule's central segment, then pushed out by skin radius.
455 const FxVec2f& a = m_world_vertices[0];
456 const FxVec2f& b = m_world_vertices[1];
457 FxVec2f ab = b - a;
458 float len2 = ab.dot(ab);
459 FxVec2f q =
460 (len2 < 1e-6f) ? a : a + std::clamp((point - a).dot(ab) / len2, 0.0f, 1.0f) * ab;
461 FxVec2f v = point - q;
462 float vlen = v.norm();
463 FxVec2f dir = (vlen > 1e-6f) ? v / vlen : FxVec2f(1.0f, 0.0f);
464 return q + dir * m_skin_radius;
465 }
466 // Polygon
467 auto shifted = (m_world_vertices - point);
468 auto dist = (shifted).dot(shifted);
469 auto [min_ind, min_value] = dist.argmin();
470 return m_world_vertices[min_ind];
471 }
472};
473
474class FxEntity;
475
476// What a ray struck.
477struct FxRayHit {
478 std::shared_ptr<FxEntity> entity = nullptr;
479 FxVec2f point{0.0f, 0.0f}; // world-space point of impact
480 FxVec2f normal{0.0f, 0.0f}; // outward surface normal there, facing back along the ray
481 float distance = 0.0f; // travel along the ray from its origin
482
483 bool hit() const { return entity != nullptr; }
484};
FxShapeType
Definition Geometry.h:44
FxArray< FxVec2f > FxVec2fArray
Definition Math.h:1131
T * end() noexcept
Definition Math.h:830
T mean() const
Definition Math.h:885
T * begin() noexcept
Definition Math.h:828
size_t size() const noexcept
Definition Math.h:834
A named rigid body with state, material properties, geometry, and forces.
Definition Entity.h:49
Single-precision 2D vector.
Definition Math.h:47
float & y()
Definition Math.h:57
float & x()
Definition Math.h:56
float cross(const FxVec2f &other) const
Definition Math.h:89
Single-precision three-component vector, including poses.
Definition Math.h:159
void set_theta(float val)
Definition Math.h:184
FxVec2fMap xy()
Definition Math.h:186
void set_xy(const FxVec2f &v2)
Definition Math.h:189
float & theta()
Definition Math.h:171
Axis-aligned bounding box used by broad-phase operations.
Definition Geometry.h:20
float minY
Definition Geometry.h:21
FxAABB(float mnX, float mnY, float mxX, float mxY)
Definition Geometry.h:23
bool contains(const FxAABB &inner) const
Definition Geometry.h:36
FxAABB()=default
FxAABB fatten(float margin) const
Definition Geometry.h:29
float maxY
Definition Geometry.h:21
bool is_valid() const
Definition Geometry.h:39
float perimeter() const
Definition Geometry.h:32
float maxX
Definition Geometry.h:21
float minX
Definition Geometry.h:21
bool overlaps(const FxAABB &o) const
Definition Geometry.h:33
static FxAABB combine(const FxAABB &a, const FxAABB &b)
Definition Geometry.h:25
Result from a ray cast against the scene.
Definition Geometry.h:477
bool hit() const
Definition Geometry.h:483
FxVec2f normal
Definition Geometry.h:480
std::shared_ptr< FxEntity > entity
Definition Geometry.h:478
float distance
Definition Geometry.h:481
FxVec2f point
Definition Geometry.h:479
Unified circle, capsule, polygon, edge, and chain representation.
Definition Geometry.h:46
FxShapeType m_shape_type
Definition Geometry.h:48
bool is_capsule() const
Definition Geometry.h:206
void set_offset_pose(const FxVec3f &o_pose)
Definition Geometry.h:331
float calc_inertia(float mass) const
Definition Geometry.h:277
static bool is_convex(const FxVec2fArray &verts)
Definition Geometry.h:69
bool is_polygon() const
Definition Geometry.h:208
FxVec2f m_centroid
Definition Geometry.h:54
FxVec3f offset_pose() const
Definition Geometry.h:332
void set_world_pose(const FxVec3f &world_pose, FxArray< float > &out_aabb)
Definition Geometry.h:344
FxShape(const FxVec2f &a, const FxVec2f &b)
Definition Geometry.h:114
float radius() const
Definition Geometry.h:193
bool is_edge() const
Definition Geometry.h:224
FxVec3f world_pose() const
Definition Geometry.h:389
void set_position(const FxVec2f &pos)
Definition Geometry.h:392
bool is_circle() const
Definition Geometry.h:204
const FxVec2fArray & vertices() const
Definition Geometry.h:197
FxVec3f m_offset_pose
Definition Geometry.h:52
FxShape(float length, float radius)
Definition Geometry.h:98
FxVec3f m_world_pose
Definition Geometry.h:53
FxShape(float radius)
Definition Geometry.h:89
void rotate(float delta_theta)
Definition Geometry.h:410
FxShape(const FxVec2f &size, float skin_radius=0.0f)
Definition Geometry.h:163
float m_skin_radius
Definition Geometry.h:50
float m_radius
Definition Geometry.h:49
FxVec2f get_closest_vertex(const FxVec2f &point) const
Definition Geometry.h:445
FxShape segment(std::size_t i) const
Definition Geometry.h:219
FxVec2fArray m_world_vertices
Definition Geometry.h:55
static float calc_radius(const FxVec2fArray &verts)
Definition Geometry.h:58
FxVec2fArray __vertices() const
Definition Geometry.h:198
float skin_radius() const
Definition Geometry.h:194
FxVec2f centroid() const
Definition Geometry.h:201
FxShape(const FxVec2fArray &vertices, float skin_radius=0.0f)
Definition Geometry.h:143
static FxShape make_chain(const FxVec2fArray &points)
Definition Geometry.h:211
void set_rotation(float theta)
Definition Geometry.h:398
void move(const FxVec2f &delta)
Definition Geometry.h:404
std::size_t segment_count() const
Definition Geometry.h:216
bool is_chain() const
Definition Geometry.h:213
bool contains(const FxVec2f &p) const
Definition Geometry.h:227
FxShape()
Definition Geometry.h:86
FxVec2fArray m_vertices
Definition Geometry.h:51
float area() const
Definition Geometry.h:252
FxShape(const FxVec2fArray &points, ChainTag)
Definition Geometry.h:128
FxShapeType shape_type() const
Definition Geometry.h:192
static float polygon_area(const FxVec2fArray &verts)
Definition Geometry.h:180
FxArray< float > set_world_pose(const FxVec3f &world_pose)
Definition Geometry.h:335
std::pair< std::size_t, float > min_projection(const FxVec2f &axis, const FxVec2f &origin) const
Definition Geometry.h:418