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);
23 FxAABB(
float mnX,
float mnY,
float mxX,
float mxY) :
60 for (
size_t i = 0; i < verts.
size(); ++i) {
62 float d2 = v.
x() * v.
x() + v.
y() * v.
y();
63 if (d2 > maxSq) maxSq = d2;
65 return std::sqrt(maxSq);
70 size_t n = verts.
size();
71 bool gotPos =
false, gotNeg =
false;
72 for (
size_t i = 0; i < n; ++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;
90 if (
radius <= 1e-6f)
throw std::invalid_argument(
"FxShape: radius must be > 0");
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;
115 if ((b - a).norm() <= 1e-6f)
116 throw std::invalid_argument(
"FxShape: edge endpoints must be distinct");
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");
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");
148 if (std::fabs(
area) <= minArea)
throw std::invalid_argument(
"FxShape: area ≤ 2e-6");
154 std::reverse(verts.
begin(), verts.
end());
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;
170 if (hx * hy <= 1e-6f)
throw std::runtime_error(
"FxShape: degenerate rectangle");
172 m_vertices = {{-hx, -hy}, {-hx, hy}, {hx, hy}, {hx, -hy}};
182 const size_t n = verts.
size();
183 for (
size_t i = 0; i < n; ++i) {
185 const FxVec2f& b = verts[(i + 1) % n];
186 sum += double(a.
x()) * b.
y() - double(b.
x()) * a.
y();
188 return float(0.5 * sum);
237 bool all_left =
true, all_right =
true;
238 for (
size_t i = 0; i < n; ++i) {
242 const float len = edge.norm();
243 if (len < 1e-8f)
continue;
244 const float side = edge.
cross(p - a) / len;
248 return all_left || all_right;
270 for (std::size_t i = 0, n =
V.size(); i < n; ++i) {
271 perim += (
V[(i + 1) % n] -
V[i]).norm();
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);
294 const float I_rect = m_rect * (
L *
L + 4.0f * r * r) / 12.0f;
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;
301 float signed_twice_area = 0.0f;
303 for (std::size_t i = 0; i < n; ++i) {
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);
312 float core_area = std::abs(signed_twice_area * 0.5f);
313 if (core_area < 1e-6f)
return 0.0f;
316 const float density = mass / core_area;
317 return (density / 12.0f) * std::abs(accum);
321 const float total_area =
area();
322 const float density = mass / total_area;
323 const float I_core = (density / 12.0f) * std::abs(accum);
325 const float m_skin = mass - density * core_area;
327 return I_core + m_skin * std::max(0.0f, r_eff_sq);
351 out_aabb[0] = pX - r;
352 out_aabb[1] = pY - r;
353 out_aabb[2] = pX + r;
354 out_aabb[3] = pY + r;
366 const float cos_t = std::cos(theta), sin_t = std::sin(theta);
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) {
373 const float x = xi * cos_t - yi * sin_t + cx;
374 const float y = xi * sin_t + yi * cos_t + cy;
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;
424 if (n == 0)
return {0, FxInfinityf};
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) {
435 const float projection = (px - ox) * ax + (py - oy) * ay;
436 if (projection < best) {
441 return {best_index, best};
449 if (v.dot(v) < 1e-6f) dir =
FxVec2f(1.0f, 0.0f);
450 else dir = v.normalized();
458 float len2 = ab.dot(ab);
460 (len2 < 1e-6f) ? a : a + std::clamp((point - a).dot(ab) / len2, 0.0f, 1.0f) * ab;
462 float vlen = v.norm();
468 auto dist = (shifted).dot(shifted);
469 auto [min_ind, min_value] = dist.argmin();
478 std::shared_ptr<FxEntity>
entity =
nullptr;
FxArray< FxVec2f > FxVec2fArray
size_t size() const noexcept
A named rigid body with state, material properties, geometry, and forces.
Single-precision 2D vector.
float cross(const FxVec2f &other) const
Single-precision three-component vector, including poses.
void set_theta(float val)
void set_xy(const FxVec2f &v2)
Axis-aligned bounding box used by broad-phase operations.
FxAABB(float mnX, float mnY, float mxX, float mxY)
bool contains(const FxAABB &inner) const
FxAABB fatten(float margin) const
bool overlaps(const FxAABB &o) const
static FxAABB combine(const FxAABB &a, const FxAABB &b)
Result from a ray cast against the scene.
std::shared_ptr< FxEntity > entity
Unified circle, capsule, polygon, edge, and chain representation.
void set_offset_pose(const FxVec3f &o_pose)
float calc_inertia(float mass) const
static bool is_convex(const FxVec2fArray &verts)
FxVec3f offset_pose() const
void set_world_pose(const FxVec3f &world_pose, FxArray< float > &out_aabb)
FxShape(const FxVec2f &a, const FxVec2f &b)
FxVec3f world_pose() const
void set_position(const FxVec2f &pos)
const FxVec2fArray & vertices() const
FxShape(float length, float radius)
void rotate(float delta_theta)
FxShape(const FxVec2f &size, float skin_radius=0.0f)
FxVec2f get_closest_vertex(const FxVec2f &point) const
FxShape segment(std::size_t i) const
FxVec2fArray m_world_vertices
static float calc_radius(const FxVec2fArray &verts)
FxVec2fArray __vertices() const
float skin_radius() const
FxShape(const FxVec2fArray &vertices, float skin_radius=0.0f)
static FxShape make_chain(const FxVec2fArray &points)
void set_rotation(float theta)
void move(const FxVec2f &delta)
std::size_t segment_count() const
bool contains(const FxVec2f &p) const
FxShape(const FxVec2fArray &points, ChainTag)
FxShapeType shape_type() const
static float polygon_area(const FxVec2fArray &verts)
FxArray< float > set_world_pose(const FxVec3f &world_pose)
std::pair< std::size_t, float > min_projection(const FxVec2f &axis, const FxVec2f &origin) const