@codexo/exojs-physics 0.15.3 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -23
- package/dist/esm/Aabb.d.ts +6 -10
- package/dist/esm/Aabb.d.ts.map +1 -0
- package/dist/esm/Aabb.js +9 -5
- package/dist/esm/Aabb.js.map +1 -1
- package/dist/esm/Collider.d.ts +62 -10
- package/dist/esm/Collider.d.ts.map +1 -0
- package/dist/esm/Collider.js +263 -157
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +51 -5
- package/dist/esm/ContactGraph.d.ts.map +1 -0
- package/dist/esm/ContactGraph.js +282 -223
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/ContactModifier.d.ts +85 -0
- package/dist/esm/ContactModifier.d.ts.map +1 -0
- package/dist/esm/ContactModifier.js +49 -0
- package/dist/esm/ContactModifier.js.map +1 -0
- package/dist/esm/PhysicsBody.d.ts +56 -22
- package/dist/esm/PhysicsBody.d.ts.map +1 -0
- package/dist/esm/PhysicsBody.js +461 -443
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +276 -43
- package/dist/esm/PhysicsWorld.d.ts.map +1 -0
- package/dist/esm/PhysicsWorld.js +890 -626
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/TimeStepper.d.ts +3 -2
- package/dist/esm/TimeStepper.d.ts.map +1 -0
- package/dist/esm/TimeStepper.js +56 -67
- package/dist/esm/TimeStepper.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +8 -4
- package/dist/esm/backend/NativePhysicsBackend.d.ts.map +1 -0
- package/dist/esm/backend/NativePhysicsBackend.js +48 -41
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -2
- package/dist/esm/backend/PhysicsBackend.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.d.ts +25 -4
- package/dist/esm/binding/BindingRegistry.d.ts.map +1 -0
- package/dist/esm/binding/BindingRegistry.js +70 -36
- package/dist/esm/binding/BindingRegistry.js.map +1 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +22 -5
- package/dist/esm/binding/PhysicsBinding.d.ts.map +1 -0
- package/dist/esm/binding/PhysicsBinding.js +46 -21
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts +68 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js +149 -0
- package/dist/esm/broadphase/AabbTreeBroadPhase.js.map +1 -0
- package/dist/esm/broadphase/BroadPhase.d.ts +7 -4
- package/dist/esm/broadphase/BroadPhase.d.ts.map +1 -0
- package/dist/esm/broadphase/index.d.ts +2 -1
- package/dist/esm/broadphase/index.d.ts.map +1 -0
- package/dist/esm/collision/CollisionProxy.d.ts +3 -2
- package/dist/esm/collision/CollisionProxy.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.d.ts +5 -4
- package/dist/esm/collision/Manifold.d.ts.map +1 -0
- package/dist/esm/collision/Manifold.js +34 -25
- package/dist/esm/collision/Manifold.js.map +1 -1
- package/dist/esm/collision/chainAdjacency.d.ts +29 -0
- package/dist/esm/collision/chainAdjacency.d.ts.map +1 -0
- package/dist/esm/collision/chainAdjacency.js +44 -0
- package/dist/esm/collision/chainAdjacency.js.map +1 -0
- package/dist/esm/collision/dispatch.d.ts +35 -0
- package/dist/esm/collision/dispatch.d.ts.map +1 -0
- package/dist/esm/collision/dispatch.js +58 -0
- package/dist/esm/collision/dispatch.js.map +1 -0
- package/dist/esm/collision/index.d.ts +2 -0
- package/dist/esm/collision/index.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.d.ts +5 -2
- package/dist/esm/collision/narrowphase.d.ts.map +1 -0
- package/dist/esm/collision/narrowphase.js +524 -399
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/collision/segments.d.ts +19 -0
- package/dist/esm/collision/segments.d.ts.map +1 -0
- package/dist/esm/collision/segments.js +36 -0
- package/dist/esm/collision/segments.js.map +1 -0
- package/dist/esm/collision/sweep.d.ts +35 -0
- package/dist/esm/collision/sweep.d.ts.map +1 -0
- package/dist/esm/collision/sweep.js +437 -0
- package/dist/esm/collision/sweep.js.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +23 -4
- package/dist/esm/debug/PhysicsDebugDraw.d.ts.map +1 -0
- package/dist/esm/debug/PhysicsDebugDraw.js +230 -188
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/debug/index.d.ts +1 -0
- package/dist/esm/debug/index.d.ts.map +1 -0
- package/dist/esm/debug/index.js +3 -2
- package/dist/esm/events.d.ts +1 -0
- package/dist/esm/events.d.ts.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +24 -19
- package/dist/esm/joints/DistanceJoint.d.ts +4 -3
- package/dist/esm/joints/DistanceJoint.d.ts.map +1 -0
- package/dist/esm/joints/DistanceJoint.js +160 -172
- package/dist/esm/joints/DistanceJoint.js.map +1 -1
- package/dist/esm/joints/Joint.d.ts +5 -4
- package/dist/esm/joints/Joint.d.ts.map +1 -0
- package/dist/esm/joints/Joint.js +23 -21
- package/dist/esm/joints/Joint.js.map +1 -1
- package/dist/esm/joints/MouseJoint.d.ts +8 -7
- package/dist/esm/joints/MouseJoint.d.ts.map +1 -0
- package/dist/esm/joints/MouseJoint.js +132 -133
- package/dist/esm/joints/MouseJoint.js.map +1 -1
- package/dist/esm/joints/PrismaticJoint.d.ts +5 -4
- package/dist/esm/joints/PrismaticJoint.d.ts.map +1 -0
- package/dist/esm/joints/PrismaticJoint.js +211 -240
- package/dist/esm/joints/PrismaticJoint.js.map +1 -1
- package/dist/esm/joints/RevoluteJoint.d.ts +4 -3
- package/dist/esm/joints/RevoluteJoint.d.ts.map +1 -0
- package/dist/esm/joints/RevoluteJoint.js +191 -213
- package/dist/esm/joints/RevoluteJoint.js.map +1 -1
- package/dist/esm/joints/WeldJoint.d.ts +3 -2
- package/dist/esm/joints/WeldJoint.d.ts.map +1 -0
- package/dist/esm/joints/WeldJoint.js +156 -152
- package/dist/esm/joints/WeldJoint.js.map +1 -1
- package/dist/esm/joints/WheelJoint.d.ts +4 -3
- package/dist/esm/joints/WheelJoint.d.ts.map +1 -0
- package/dist/esm/joints/WheelJoint.js +228 -255
- package/dist/esm/joints/WheelJoint.js.map +1 -1
- package/dist/esm/math.d.ts +6 -9
- package/dist/esm/math.d.ts.map +1 -0
- package/dist/esm/math.js +37 -39
- package/dist/esm/math.js.map +1 -1
- package/dist/esm/physicsBuildInfo.d.ts +1 -0
- package/dist/esm/physicsBuildInfo.d.ts.map +1 -0
- package/dist/esm/physicsBuildInfo.js +6 -4
- package/dist/esm/physicsBuildInfo.js.map +1 -1
- package/dist/esm/public.d.ts +8 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.d.ts +56 -12
- package/dist/esm/query/QueryEngine.d.ts.map +1 -0
- package/dist/esm/query/QueryEngine.js +494 -237
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/query/SpatialIndex.d.ts +34 -0
- package/dist/esm/query/SpatialIndex.d.ts.map +1 -0
- package/dist/esm/shapes/AnyShape.d.ts +9 -4
- package/dist/esm/shapes/AnyShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.d.ts +2 -1
- package/dist/esm/shapes/BoxShape.d.ts.map +1 -0
- package/dist/esm/shapes/BoxShape.js +37 -25
- package/dist/esm/shapes/BoxShape.js.map +1 -1
- package/dist/esm/shapes/CapsuleShape.d.ts +30 -0
- package/dist/esm/shapes/CapsuleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CapsuleShape.js +88 -0
- package/dist/esm/shapes/CapsuleShape.js.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts +30 -0
- package/dist/esm/shapes/ChainEdgeShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainEdgeShape.js +66 -0
- package/dist/esm/shapes/ChainEdgeShape.js.map +1 -0
- package/dist/esm/shapes/ChainShape.d.ts +52 -0
- package/dist/esm/shapes/ChainShape.d.ts.map +1 -0
- package/dist/esm/shapes/ChainShape.js +85 -0
- package/dist/esm/shapes/ChainShape.js.map +1 -0
- package/dist/esm/shapes/CircleShape.d.ts +3 -4
- package/dist/esm/shapes/CircleShape.d.ts.map +1 -0
- package/dist/esm/shapes/CircleShape.js +27 -26
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +7 -8
- package/dist/esm/shapes/PolygonShape.d.ts.map +1 -0
- package/dist/esm/shapes/PolygonShape.js +131 -162
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/SegmentShape.d.ts +31 -0
- package/dist/esm/shapes/SegmentShape.d.ts.map +1 -0
- package/dist/esm/shapes/SegmentShape.js +62 -0
- package/dist/esm/shapes/SegmentShape.js.map +1 -0
- package/dist/esm/shapes/Shape.d.ts +28 -17
- package/dist/esm/shapes/Shape.d.ts.map +1 -0
- package/dist/esm/shapes/Shape.js +12 -13
- package/dist/esm/shapes/Shape.js.map +1 -1
- package/dist/esm/shapes/convexParts.d.ts +21 -0
- package/dist/esm/shapes/convexParts.d.ts.map +1 -0
- package/dist/esm/shapes/convexParts.js +26 -0
- package/dist/esm/shapes/convexParts.js.map +1 -0
- package/dist/esm/shapes/decompose.d.ts +24 -0
- package/dist/esm/shapes/decompose.d.ts.map +1 -0
- package/dist/esm/shapes/decompose.js +272 -0
- package/dist/esm/shapes/decompose.js.map +1 -0
- package/dist/esm/shapes/index.d.ts +4 -0
- package/dist/esm/shapes/index.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +12 -5
- package/dist/esm/solver/ContactSolver.d.ts.map +1 -0
- package/dist/esm/solver/ContactSolver.js +425 -471
- package/dist/esm/solver/ContactSolver.js.map +1 -1
- package/dist/esm/solver/tolerances.d.ts +34 -0
- package/dist/esm/solver/tolerances.d.ts.map +1 -0
- package/dist/esm/solver/tolerances.js +38 -0
- package/dist/esm/solver/tolerances.js.map +1 -0
- package/dist/esm/sort.d.ts +5 -4
- package/dist/esm/sort.d.ts.map +1 -0
- package/dist/esm/sort.js +41 -51
- package/dist/esm/sort.js.map +1 -1
- package/dist/esm/types.d.ts +4 -8
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +15 -17
- package/dist/esm/types.js.map +1 -1
- package/package.json +6 -6
- package/dist/esm/broadphase/SweepAndPrune.d.ts +0 -15
- package/dist/esm/broadphase/SweepAndPrune.js +0 -77
- package/dist/esm/broadphase/SweepAndPrune.js.map +0 -1
- package/dist/esm/debug/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Shape } from "./Shape.js";
|
|
2
|
+
import { ChainEdgeShape } from "./ChainEdgeShape.js";
|
|
3
|
+
|
|
4
|
+
//#region src/shapes/ChainShape.ts
|
|
5
|
+
/** Vertices closer than this (px) are welded into one. */
|
|
6
|
+
const weldEpsilon = 1e-4;
|
|
7
|
+
/**
|
|
8
|
+
* A connected boundary through a run of local-space vertices: level outlines,
|
|
9
|
+
* terrain, the ground of a side-scroller. Open (a polyline) or closed (a loop).
|
|
10
|
+
*
|
|
11
|
+
* A chain is not an array of {@link SegmentShape}s. At a shared vertex two
|
|
12
|
+
* independent segments carry two different normals, so a body sliding across the
|
|
13
|
+
* seam can snag, be launched, or receive a normal pointing into the surface. A
|
|
14
|
+
* chain owns the adjacency between its edges and uses it to suppress exactly
|
|
15
|
+
* those contacts, which is why the adjacency belongs to the engine rather than
|
|
16
|
+
* to the caller.
|
|
17
|
+
*
|
|
18
|
+
* Unlike a segment, a chain is **one-sided**: each edge collides only from the
|
|
19
|
+
* side its outward normal points to. Winding therefore decides which side is
|
|
20
|
+
* solid, following the polygon convention - a counter-clockwise run is solid on
|
|
21
|
+
* the outside (an island), a clockwise run is solid on the inside (a container).
|
|
22
|
+
* Bodies on the hollow side pass through.
|
|
23
|
+
*
|
|
24
|
+
* It has no interior, so {@link Shape.massProperties} is `null` and a `dynamic`
|
|
25
|
+
* body needs at least one mass-bearing collider alongside it. A `static` or
|
|
26
|
+
* `kinematic` body carries a chain on its own.
|
|
27
|
+
*
|
|
28
|
+
* Collinear vertices are kept. A straight run is authoring data, and dropping it
|
|
29
|
+
* would move the adjacency of the neighbouring edges.
|
|
30
|
+
*/
|
|
31
|
+
var ChainShape = class extends Shape {
|
|
32
|
+
type = "chain";
|
|
33
|
+
/** Local-space vertices, flattened, after welding coincident input. */
|
|
34
|
+
vertices;
|
|
35
|
+
/** Number of vertices in {@link vertices}. */
|
|
36
|
+
count;
|
|
37
|
+
/** Number of edges: `count` when {@link closed}, `count - 1` otherwise. */
|
|
38
|
+
edgeCount;
|
|
39
|
+
closed;
|
|
40
|
+
boundingRadius;
|
|
41
|
+
/** Always `null`: a boundary has no interior and therefore no mass. */
|
|
42
|
+
massProperties = null;
|
|
43
|
+
/**
|
|
44
|
+
* @internal - the per-edge geometry the solver works on, built once with the
|
|
45
|
+
* chain. Immutable and shared by every collider carrying this shape.
|
|
46
|
+
*/
|
|
47
|
+
edges;
|
|
48
|
+
constructor(vertices, options = {}) {
|
|
49
|
+
super();
|
|
50
|
+
const closed = options.closed ?? false;
|
|
51
|
+
const points = [];
|
|
52
|
+
for (const vertex of vertices) {
|
|
53
|
+
if (!Number.isFinite(vertex.x) || !Number.isFinite(vertex.y)) throw new RangeError(`ChainShape: vertex has a non-finite component (${vertex.x}, ${vertex.y}).`);
|
|
54
|
+
const px = points[points.length - 2];
|
|
55
|
+
const py = points[points.length - 1];
|
|
56
|
+
if (px !== void 0 && py !== void 0 && Math.hypot(vertex.x - px, vertex.y - py) < weldEpsilon) continue;
|
|
57
|
+
points.push(vertex.x, vertex.y);
|
|
58
|
+
}
|
|
59
|
+
if (closed && points.length >= 4) {
|
|
60
|
+
const firstX = points[0];
|
|
61
|
+
const firstY = points[1];
|
|
62
|
+
const lastX = points[points.length - 2];
|
|
63
|
+
const lastY = points[points.length - 1];
|
|
64
|
+
if (Math.hypot(lastX - firstX, lastY - firstY) < weldEpsilon) points.length -= 2;
|
|
65
|
+
}
|
|
66
|
+
const count = points.length / 2;
|
|
67
|
+
const minimum = closed ? 3 : 2;
|
|
68
|
+
if (count < minimum) throw new RangeError(`ChainShape: ${closed ? "a closed chain" : "a chain"} needs at least ${minimum} distinct vertices, received ${count}.`);
|
|
69
|
+
let boundingRadius = 0;
|
|
70
|
+
for (let i = 0; i < count; i++) boundingRadius = Math.max(boundingRadius, Math.hypot(points[i * 2], points[i * 2 + 1]));
|
|
71
|
+
this.vertices = Object.freeze(points);
|
|
72
|
+
this.count = count;
|
|
73
|
+
this.edgeCount = closed ? count : count - 1;
|
|
74
|
+
this.closed = closed;
|
|
75
|
+
this.boundingRadius = boundingRadius;
|
|
76
|
+
const edges = [];
|
|
77
|
+
for (let i = 0; i < this.edgeCount; i++) edges.push(new ChainEdgeShape(this, i));
|
|
78
|
+
this.edges = Object.freeze(edges);
|
|
79
|
+
Object.freeze(this);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { ChainShape };
|
|
85
|
+
//# sourceMappingURL=ChainShape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChainShape.js","names":[],"sources":["../../../src/shapes/ChainShape.ts"],"sourcesContent":["import type { PointLike } from '@codexo/exojs';\n\nimport { ChainEdgeShape } from './ChainEdgeShape';\nimport { Shape } from './Shape';\n\n/** Vertices closer than this (px) are welded into one. */\nconst weldEpsilon = 1e-4;\n\n/** Construction options for a {@link ChainShape}. */\nexport interface ChainShapeOptions {\n /** When `true`, the last vertex connects back to the first. Default `false`. */\n closed?: boolean;\n}\n\n/**\n * A connected boundary through a run of local-space vertices: level outlines,\n * terrain, the ground of a side-scroller. Open (a polyline) or closed (a loop).\n *\n * A chain is not an array of {@link SegmentShape}s. At a shared vertex two\n * independent segments carry two different normals, so a body sliding across the\n * seam can snag, be launched, or receive a normal pointing into the surface. A\n * chain owns the adjacency between its edges and uses it to suppress exactly\n * those contacts, which is why the adjacency belongs to the engine rather than\n * to the caller.\n *\n * Unlike a segment, a chain is **one-sided**: each edge collides only from the\n * side its outward normal points to. Winding therefore decides which side is\n * solid, following the polygon convention - a counter-clockwise run is solid on\n * the outside (an island), a clockwise run is solid on the inside (a container).\n * Bodies on the hollow side pass through.\n *\n * It has no interior, so {@link Shape.massProperties} is `null` and a `dynamic`\n * body needs at least one mass-bearing collider alongside it. A `static` or\n * `kinematic` body carries a chain on its own.\n *\n * Collinear vertices are kept. A straight run is authoring data, and dropping it\n * would move the adjacency of the neighbouring edges.\n */\nexport class ChainShape extends Shape {\n public readonly type = 'chain' as const;\n\n /** Local-space vertices, flattened, after welding coincident input. */\n public readonly vertices: readonly number[];\n\n /** Number of vertices in {@link vertices}. */\n public readonly count: number;\n\n /** Number of edges: `count` when {@link closed}, `count - 1` otherwise. */\n public readonly edgeCount: number;\n\n public readonly closed: boolean;\n public readonly boundingRadius: number;\n\n /** Always `null`: a boundary has no interior and therefore no mass. */\n public readonly massProperties = null;\n\n /**\n * @internal - the per-edge geometry the solver works on, built once with the\n * chain. Immutable and shared by every collider carrying this shape.\n */\n public readonly edges: readonly ChainEdgeShape[];\n\n public constructor(vertices: ReadonlyArray<Readonly<PointLike>>, options: ChainShapeOptions = {}) {\n super();\n\n const closed = options.closed ?? false;\n const points: number[] = [];\n\n for (const vertex of vertices) {\n if (!Number.isFinite(vertex.x) || !Number.isFinite(vertex.y)) {\n throw new RangeError(`ChainShape: vertex has a non-finite component (${vertex.x}, ${vertex.y}).`);\n }\n\n const px = points[points.length - 2];\n const py = points[points.length - 1];\n\n if (px !== undefined && py !== undefined && Math.hypot(vertex.x - px, vertex.y - py) < weldEpsilon) {\n continue;\n }\n\n points.push(vertex.x, vertex.y);\n }\n\n if (closed && points.length >= 4) {\n const firstX = points[0]!;\n const firstY = points[1]!;\n const lastX = points[points.length - 2]!;\n const lastY = points[points.length - 1]!;\n\n // A caller repeating the first vertex to \"close\" the loop is expressing the\n // same intent as `closed: true`; keeping it would produce a zero-length edge.\n if (Math.hypot(lastX - firstX, lastY - firstY) < weldEpsilon) {\n points.length -= 2;\n }\n }\n\n const count = points.length / 2;\n const minimum = closed ? 3 : 2;\n\n if (count < minimum) {\n throw new RangeError(`ChainShape: ${closed ? 'a closed chain' : 'a chain'} needs at least ${minimum} distinct vertices, received ${count}.`);\n }\n\n let boundingRadius = 0;\n\n for (let i = 0; i < count; i++) {\n boundingRadius = Math.max(boundingRadius, Math.hypot(points[i * 2]!, points[i * 2 + 1]!));\n }\n\n this.vertices = Object.freeze(points);\n this.count = count;\n this.edgeCount = closed ? count : count - 1;\n this.closed = closed;\n this.boundingRadius = boundingRadius;\n\n const edges: ChainEdgeShape[] = [];\n\n for (let i = 0; i < this.edgeCount; i++) {\n edges.push(new ChainEdgeShape(this, i));\n }\n\n this.edges = Object.freeze(edges);\n\n Object.freeze(this);\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;AAgCpB,IAAa,aAAb,cAAgC,MAAM;CACpC,AAAgB,OAAO;;CAGvB,AAAgB;;CAGhB,AAAgB;;CAGhB,AAAgB;CAEhB,AAAgB;CAChB,AAAgB;;CAGhB,AAAgB,iBAAiB;;;;;CAMjC,AAAgB;CAEhB,AAAO,YAAY,UAA8C,UAA6B,CAAC,GAAG;EAChG,MAAM;EAEN,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,SAAmB,CAAC;EAE1B,KAAK,MAAM,UAAU,UAAU;GAC7B,IAAI,CAAC,OAAO,SAAS,OAAO,CAAC,KAAK,CAAC,OAAO,SAAS,OAAO,CAAC,GACzD,MAAM,IAAI,WAAW,kDAAkD,OAAO,EAAE,IAAI,OAAO,EAAE,GAAG;GAGlG,MAAM,KAAK,OAAO,OAAO,SAAS;GAClC,MAAM,KAAK,OAAO,OAAO,SAAS;GAElC,IAAI,OAAO,UAAa,OAAO,UAAa,KAAK,MAAM,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE,IAAI,aACrF;GAGF,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC;EAChC;EAEA,IAAI,UAAU,OAAO,UAAU,GAAG;GAChC,MAAM,SAAS,OAAO;GACtB,MAAM,SAAS,OAAO;GACtB,MAAM,QAAQ,OAAO,OAAO,SAAS;GACrC,MAAM,QAAQ,OAAO,OAAO,SAAS;GAIrC,IAAI,KAAK,MAAM,QAAQ,QAAQ,QAAQ,MAAM,IAAI,aAC/C,OAAO,UAAU;EAErB;EAEA,MAAM,QAAQ,OAAO,SAAS;EAC9B,MAAM,UAAU,SAAS,IAAI;EAE7B,IAAI,QAAQ,SACV,MAAM,IAAI,WAAW,eAAe,SAAS,mBAAmB,UAAU,kBAAkB,QAAQ,+BAA+B,MAAM,EAAE;EAG7I,IAAI,iBAAiB;EAErB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KACzB,iBAAiB,KAAK,IAAI,gBAAgB,KAAK,MAAM,OAAO,IAAI,IAAK,OAAO,IAAI,IAAI,EAAG,CAAC;EAG1F,KAAK,WAAW,OAAO,OAAO,MAAM;EACpC,KAAK,QAAQ;EACb,KAAK,YAAY,SAAS,QAAQ,QAAQ;EAC1C,KAAK,SAAS;EACd,KAAK,iBAAiB;EAEtB,MAAM,QAA0B,CAAC;EAEjC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,WAAW,KAClC,MAAM,KAAK,IAAI,eAAe,MAAM,CAAC,CAAC;EAGxC,KAAK,QAAQ,OAAO,OAAO,KAAK;EAEhC,OAAO,OAAO,IAAI;CACpB;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ShapeMassProperties } from './Shape';
|
|
1
2
|
import { Shape } from './Shape';
|
|
2
3
|
/**
|
|
3
4
|
* A circle of the given `radius` centred on the collider's local origin.
|
|
@@ -7,9 +8,7 @@ export declare class CircleShape extends Shape {
|
|
|
7
8
|
readonly type: "circle";
|
|
8
9
|
readonly radius: number;
|
|
9
10
|
readonly boundingRadius: number;
|
|
10
|
-
readonly
|
|
11
|
-
readonly centroidX = 0;
|
|
12
|
-
readonly centroidY = 0;
|
|
13
|
-
readonly unitInertia: number;
|
|
11
|
+
readonly massProperties: ShapeMassProperties;
|
|
14
12
|
constructor(radius: number);
|
|
15
13
|
}
|
|
14
|
+
//# sourceMappingURL=CircleShape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CircleShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/CircleShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,IAAI,EAAG,QAAQ,CAAU;IACzC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC,SAAgB,cAAc,EAAE,mBAAmB,CAAC;gBAEjC,MAAM,EAAE,MAAM;CAqBlC"}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
import { Shape } from
|
|
1
|
+
import { Shape } from "./Shape.js";
|
|
2
2
|
|
|
3
|
+
//#region src/shapes/CircleShape.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
5
|
+
* A circle of the given `radius` centred on the collider's local origin.
|
|
6
|
+
* The cheapest shape for both broad- and narrow-phase.
|
|
7
|
+
*/
|
|
8
|
+
var CircleShape = class extends Shape {
|
|
9
|
+
type = "circle";
|
|
10
|
+
radius;
|
|
11
|
+
boundingRadius;
|
|
12
|
+
massProperties;
|
|
13
|
+
constructor(radius) {
|
|
14
|
+
super();
|
|
15
|
+
if (!Number.isFinite(radius) || radius <= 0) throw new RangeError(`CircleShape: radius must be a positive finite number, received ${radius}.`);
|
|
16
|
+
const area = Math.PI * radius * radius;
|
|
17
|
+
this.radius = radius;
|
|
18
|
+
this.boundingRadius = radius;
|
|
19
|
+
this.massProperties = Object.freeze({
|
|
20
|
+
area,
|
|
21
|
+
centroidX: 0,
|
|
22
|
+
centroidY: 0,
|
|
23
|
+
unitInertia: .5 * area * radius * radius
|
|
24
|
+
});
|
|
25
|
+
Object.freeze(this);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
28
|
|
|
29
|
+
//#endregion
|
|
29
30
|
export { CircleShape };
|
|
30
|
-
//# sourceMappingURL=CircleShape.js.map
|
|
31
|
+
//# sourceMappingURL=CircleShape.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CircleShape.js","sources":["
|
|
1
|
+
{"version":3,"file":"CircleShape.js","names":[],"sources":["../../../src/shapes/CircleShape.ts"],"sourcesContent":["import type { ShapeMassProperties } from './Shape';\nimport { Shape } from './Shape';\n\n/**\n * A circle of the given `radius` centred on the collider's local origin.\n * The cheapest shape for both broad- and narrow-phase.\n */\nexport class CircleShape extends Shape {\n public readonly type = 'circle' as const;\n public readonly radius: number;\n public readonly boundingRadius: number;\n public readonly massProperties: ShapeMassProperties;\n\n public constructor(radius: number) {\n super();\n\n if (!Number.isFinite(radius) || radius <= 0) {\n throw new RangeError(`CircleShape: radius must be a positive finite number, received ${radius}.`);\n }\n\n const area = Math.PI * radius * radius;\n\n this.radius = radius;\n this.boundingRadius = radius;\n this.massProperties = Object.freeze({\n area,\n centroidX: 0,\n centroidY: 0,\n // Second moment of area of a disc about its centre: ∫ r² dA = (π/2) R⁴.\n unitInertia: 0.5 * area * radius * radius,\n });\n\n Object.freeze(this);\n }\n}\n"],"mappings":";;;;;;;AAOA,IAAa,cAAb,cAAiC,MAAM;CACrC,AAAgB,OAAO;CACvB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY,QAAgB;EACjC,MAAM;EAEN,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GACxC,MAAM,IAAI,WAAW,kEAAkE,OAAO,EAAE;EAGlG,MAAM,OAAO,KAAK,KAAK,SAAS;EAEhC,KAAK,SAAS;EACd,KAAK,iBAAiB;EACtB,KAAK,iBAAiB,OAAO,OAAO;GAClC;GACA,WAAW;GACX,WAAW;GAEX,aAAa,KAAM,OAAO,SAAS;EACrC,CAAC;EAED,OAAO,OAAO,IAAI;CACpB;AACF"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PointLike } from '@codexo/exojs';
|
|
2
|
+
import type { ShapeMassProperties } from './Shape';
|
|
2
3
|
import { Shape } from './Shape';
|
|
3
4
|
/**
|
|
4
5
|
* A convex polygon defined by ≥3 local-space vertices. Input vertices may be
|
|
5
6
|
* given in either winding; they are canonicalised to counter-clockwise and the
|
|
6
7
|
* outward edge normals are precomputed. Construction throws on any non-convex,
|
|
7
|
-
* degenerate, or under-specified input
|
|
8
|
+
* degenerate, or under-specified input - there is no silent repair.
|
|
8
9
|
*
|
|
9
|
-
* Vertices and normals are exposed as flat `[x0, y0, x1, y1,
|
|
10
|
+
* Vertices and normals are exposed as flat `[x0, y0, x1, y1, ...]` arrays to keep
|
|
10
11
|
* the narrow phase allocation-free; both are frozen.
|
|
11
12
|
*/
|
|
12
13
|
export declare class PolygonShape extends Shape {
|
|
@@ -17,9 +18,7 @@ export declare class PolygonShape extends Shape {
|
|
|
17
18
|
readonly normals: readonly number[];
|
|
18
19
|
readonly count: number;
|
|
19
20
|
readonly boundingRadius: number;
|
|
20
|
-
readonly
|
|
21
|
-
|
|
22
|
-
readonly centroidY: number;
|
|
23
|
-
readonly unitInertia: number;
|
|
24
|
-
constructor(vertices: readonly VectorLike[]);
|
|
21
|
+
readonly massProperties: ShapeMassProperties;
|
|
22
|
+
constructor(vertices: ReadonlyArray<Readonly<PointLike>>);
|
|
25
23
|
}
|
|
24
|
+
//# sourceMappingURL=PolygonShape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolygonShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/PolygonShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC;;;;;;;;GAQG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,IAAI,EAAG,SAAS,CAAU;IAE1C,4CAA4C;IAC5C,SAAgB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAE5C,8EAA8E;IAC9E,SAAgB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAE3C,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC,SAAgB,cAAc,EAAE,mBAAmB,CAAC;gBAEjC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;CAyFhE"}
|
|
@@ -1,184 +1,153 @@
|
|
|
1
|
-
import { Shape } from
|
|
1
|
+
import { Shape } from "./Shape.js";
|
|
2
2
|
|
|
3
|
+
//#region src/shapes/PolygonShape.ts
|
|
3
4
|
/** Vertices closer than this (px) are treated as coincident → degenerate. */
|
|
4
5
|
const weldEpsilon = 1e-4;
|
|
5
6
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const jx = points[j * 2];
|
|
72
|
-
const jy = points[j * 2 + 1];
|
|
73
|
-
const cross = ix * jy - jx * iy;
|
|
74
|
-
cx += (ix + jx) * cross;
|
|
75
|
-
cy += (iy + jy) * cross;
|
|
76
|
-
inertiaOrigin += cross * (ix * ix + ix * jx + jx * jx + iy * iy + iy * jy + jy * jy);
|
|
77
|
-
}
|
|
78
|
-
const area = signed;
|
|
79
|
-
cx /= 6 * area;
|
|
80
|
-
cy /= 6 * area;
|
|
81
|
-
this.count = count;
|
|
82
|
-
this.vertices = Object.freeze(points);
|
|
83
|
-
this.normals = Object.freeze(normals);
|
|
84
|
-
this.area = area;
|
|
85
|
-
this.centroidX = cx;
|
|
86
|
-
this.centroidY = cy;
|
|
87
|
-
// ∫ r² dA about the origin, then shifted to the centroid (parallel axis).
|
|
88
|
-
this.unitInertia = inertiaOrigin / 12 - area * (cx * cx + cy * cy);
|
|
89
|
-
this.boundingRadius = boundingRadiusOf(points);
|
|
90
|
-
// The vertex/normal arrays are frozen; the instance itself is not, so
|
|
91
|
-
// BoxShape (and any future convex helper) can extend this class.
|
|
92
|
-
}
|
|
93
|
-
}
|
|
7
|
+
* A convex polygon defined by ≥3 local-space vertices. Input vertices may be
|
|
8
|
+
* given in either winding; they are canonicalised to counter-clockwise and the
|
|
9
|
+
* outward edge normals are precomputed. Construction throws on any non-convex,
|
|
10
|
+
* degenerate, or under-specified input - there is no silent repair.
|
|
11
|
+
*
|
|
12
|
+
* Vertices and normals are exposed as flat `[x0, y0, x1, y1, ...]` arrays to keep
|
|
13
|
+
* the narrow phase allocation-free; both are frozen.
|
|
14
|
+
*/
|
|
15
|
+
var PolygonShape = class extends Shape {
|
|
16
|
+
type = "polygon";
|
|
17
|
+
/** Local-space vertices, CCW, flattened. */
|
|
18
|
+
vertices;
|
|
19
|
+
/** Outward unit edge normals (edge `i` spans vertex `i → i+1`), flattened. */
|
|
20
|
+
normals;
|
|
21
|
+
count;
|
|
22
|
+
boundingRadius;
|
|
23
|
+
massProperties;
|
|
24
|
+
constructor(vertices) {
|
|
25
|
+
super();
|
|
26
|
+
if (vertices.length < 3) throw new RangeError(`PolygonShape: needs at least 3 vertices, received ${vertices.length}.`);
|
|
27
|
+
const points = [];
|
|
28
|
+
for (const vertex of vertices) {
|
|
29
|
+
if (!Number.isFinite(vertex.x) || !Number.isFinite(vertex.y)) throw new RangeError(`PolygonShape: vertex has a non-finite component (${vertex.x}, ${vertex.y}).`);
|
|
30
|
+
const px = points.length >= 2 ? points[points.length - 2] ?? NaN : NaN;
|
|
31
|
+
const py = points.length >= 2 ? points[points.length - 1] ?? NaN : NaN;
|
|
32
|
+
if (Math.hypot(vertex.x - px, vertex.y - py) < weldEpsilon) continue;
|
|
33
|
+
points.push(vertex.x, vertex.y);
|
|
34
|
+
}
|
|
35
|
+
dropWrapAroundDuplicate(points);
|
|
36
|
+
const count = points.length / 2;
|
|
37
|
+
if (count < 3) throw new RangeError(`PolygonShape: needs at least 3 distinct vertices after welding, received ${count}.`);
|
|
38
|
+
if (signedArea(points) < 0) reverseWinding(points);
|
|
39
|
+
const signed = signedArea(points);
|
|
40
|
+
if (signed <= weldEpsilon) throw new RangeError("PolygonShape: vertices are degenerate (zero/near-zero area).");
|
|
41
|
+
const normals = computeNormals(points);
|
|
42
|
+
assertConvex(points);
|
|
43
|
+
let cx = 0;
|
|
44
|
+
let cy = 0;
|
|
45
|
+
let inertiaOrigin = 0;
|
|
46
|
+
for (let i = 0; i < count; i++) {
|
|
47
|
+
const j = (i + 1) % count;
|
|
48
|
+
const ix = points[i * 2];
|
|
49
|
+
const iy = points[i * 2 + 1];
|
|
50
|
+
const jx = points[j * 2];
|
|
51
|
+
const jy = points[j * 2 + 1];
|
|
52
|
+
const cross = ix * jy - jx * iy;
|
|
53
|
+
cx += (ix + jx) * cross;
|
|
54
|
+
cy += (iy + jy) * cross;
|
|
55
|
+
inertiaOrigin += cross * (ix * ix + ix * jx + jx * jx + iy * iy + iy * jy + jy * jy);
|
|
56
|
+
}
|
|
57
|
+
const area = signed;
|
|
58
|
+
cx /= 6 * area;
|
|
59
|
+
cy /= 6 * area;
|
|
60
|
+
this.count = count;
|
|
61
|
+
this.vertices = Object.freeze(points);
|
|
62
|
+
this.normals = Object.freeze(normals);
|
|
63
|
+
this.massProperties = Object.freeze({
|
|
64
|
+
area,
|
|
65
|
+
centroidX: cx,
|
|
66
|
+
centroidY: cy,
|
|
67
|
+
unitInertia: inertiaOrigin / 12 - area * (cx * cx + cy * cy)
|
|
68
|
+
});
|
|
69
|
+
this.boundingRadius = boundingRadiusOf(points);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
94
72
|
/** Strip a trailing vertex that coincides with the first (wrap-around weld). */
|
|
95
73
|
const dropWrapAroundDuplicate = (points) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const lastY = points[points.length - 1] ?? NaN;
|
|
103
|
-
if (Math.hypot(firstX - lastX, firstY - lastY) < weldEpsilon) {
|
|
104
|
-
points.length -= 2;
|
|
105
|
-
}
|
|
74
|
+
if (points.length < 4) return;
|
|
75
|
+
const firstX = points[0] ?? NaN;
|
|
76
|
+
const firstY = points[1] ?? NaN;
|
|
77
|
+
const lastX = points[points.length - 2] ?? NaN;
|
|
78
|
+
const lastY = points[points.length - 1] ?? NaN;
|
|
79
|
+
if (Math.hypot(firstX - lastX, firstY - lastY) < weldEpsilon) points.length -= 2;
|
|
106
80
|
};
|
|
107
81
|
/** Signed area (positive = CCW). */
|
|
108
82
|
const signedArea = (points) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
83
|
+
const count = points.length / 2;
|
|
84
|
+
let sum = 0;
|
|
85
|
+
for (let i = 0; i < count; i++) {
|
|
86
|
+
const j = (i + 1) % count;
|
|
87
|
+
const ix = points[i * 2];
|
|
88
|
+
const iy = points[i * 2 + 1];
|
|
89
|
+
const jx = points[j * 2];
|
|
90
|
+
const jy = points[j * 2 + 1];
|
|
91
|
+
sum += ix * jy - jx * iy;
|
|
92
|
+
}
|
|
93
|
+
return sum / 2;
|
|
120
94
|
};
|
|
121
95
|
const reverseWinding = (points) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
96
|
+
const count = points.length / 2;
|
|
97
|
+
for (let i = 0; i < Math.floor(count / 2); i++) {
|
|
98
|
+
const j = count - 1 - i;
|
|
99
|
+
const ix = points[i * 2];
|
|
100
|
+
const iy = points[i * 2 + 1];
|
|
101
|
+
const jx = points[j * 2];
|
|
102
|
+
const jy = points[j * 2 + 1];
|
|
103
|
+
points[i * 2] = jx;
|
|
104
|
+
points[i * 2 + 1] = jy;
|
|
105
|
+
points[j * 2] = ix;
|
|
106
|
+
points[j * 2 + 1] = iy;
|
|
107
|
+
}
|
|
134
108
|
};
|
|
135
109
|
/** Outward unit normals for a CCW polygon: normal of edge `i→i+1` is `(eY, -eX)`. */
|
|
136
110
|
const computeNormals = (points) => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
111
|
+
const count = points.length / 2;
|
|
112
|
+
const normals = [];
|
|
113
|
+
for (let i = 0; i < count; i++) {
|
|
114
|
+
const j = (i + 1) % count;
|
|
115
|
+
const ix = points[i * 2];
|
|
116
|
+
const iy = points[i * 2 + 1];
|
|
117
|
+
const jx = points[j * 2];
|
|
118
|
+
const jy = points[j * 2 + 1];
|
|
119
|
+
const ex = jx - ix;
|
|
120
|
+
const ey = jy - iy;
|
|
121
|
+
const length = Math.hypot(ex, ey);
|
|
122
|
+
normals.push(ey / length, -ex / length);
|
|
123
|
+
}
|
|
124
|
+
return normals;
|
|
151
125
|
};
|
|
152
126
|
/** Throw unless every interior turn has the same (CCW) orientation. */
|
|
153
127
|
const assertConvex = (points) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (e1x * e2y - e1y * e2x <= 0) {
|
|
171
|
-
throw new RangeError('PolygonShape: vertices are not strictly convex (or contain collinear edges).');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
128
|
+
const count = points.length / 2;
|
|
129
|
+
for (let i = 0; i < count; i++) {
|
|
130
|
+
const a = i;
|
|
131
|
+
const b = (i + 1) % count;
|
|
132
|
+
const c = (i + 2) % count;
|
|
133
|
+
const ax = points[a * 2];
|
|
134
|
+
const ay = points[a * 2 + 1];
|
|
135
|
+
const bx = points[b * 2];
|
|
136
|
+
const by = points[b * 2 + 1];
|
|
137
|
+
const cx = points[c * 2];
|
|
138
|
+
const cy = points[c * 2 + 1];
|
|
139
|
+
const e1x = bx - ax;
|
|
140
|
+
const e1y = by - ay;
|
|
141
|
+
const e2x = cx - bx;
|
|
142
|
+
if (e1x * (cy - by) - e1y * e2x <= 0) throw new RangeError("PolygonShape: vertices are not strictly convex (or contain collinear edges).");
|
|
143
|
+
}
|
|
174
144
|
};
|
|
175
145
|
const boundingRadiusOf = (points) => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
return max;
|
|
146
|
+
let max = 0;
|
|
147
|
+
for (let i = 0; i < points.length; i += 2) max = Math.max(max, Math.hypot(points[i], points[i + 1]));
|
|
148
|
+
return max;
|
|
181
149
|
};
|
|
182
150
|
|
|
151
|
+
//#endregion
|
|
183
152
|
export { PolygonShape };
|
|
184
|
-
//# sourceMappingURL=PolygonShape.js.map
|
|
153
|
+
//# sourceMappingURL=PolygonShape.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PolygonShape.js","sources":["../../../../src/shapes/PolygonShape.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PolygonShape.js","names":[],"sources":["../../../src/shapes/PolygonShape.ts"],"sourcesContent":["import type { PointLike } from '@codexo/exojs';\n\nimport type { ShapeMassProperties } from './Shape';\nimport { Shape } from './Shape';\n\n/** Vertices closer than this (px) are treated as coincident → degenerate. */\nconst weldEpsilon = 1e-4;\n\n/**\n * A convex polygon defined by ≥3 local-space vertices. Input vertices may be\n * given in either winding; they are canonicalised to counter-clockwise and the\n * outward edge normals are precomputed. Construction throws on any non-convex,\n * degenerate, or under-specified input - there is no silent repair.\n *\n * Vertices and normals are exposed as flat `[x0, y0, x1, y1, ...]` arrays to keep\n * the narrow phase allocation-free; both are frozen.\n */\nexport class PolygonShape extends Shape {\n public readonly type = 'polygon' as const;\n\n /** Local-space vertices, CCW, flattened. */\n public readonly vertices: readonly number[];\n\n /** Outward unit edge normals (edge `i` spans vertex `i → i+1`), flattened. */\n public readonly normals: readonly number[];\n\n public readonly count: number;\n public readonly boundingRadius: number;\n public readonly massProperties: ShapeMassProperties;\n\n public constructor(vertices: ReadonlyArray<Readonly<PointLike>>) {\n super();\n\n if (vertices.length < 3) {\n throw new RangeError(`PolygonShape: needs at least 3 vertices, received ${vertices.length}.`);\n }\n\n const points: number[] = [];\n\n for (const vertex of vertices) {\n if (!Number.isFinite(vertex.x) || !Number.isFinite(vertex.y)) {\n throw new RangeError(`PolygonShape: vertex has a non-finite component (${vertex.x}, ${vertex.y}).`);\n }\n\n // Reject coincident consecutive (and wrap-around) vertices.\n const px = points.length >= 2 ? (points[points.length - 2] ?? NaN) : NaN;\n const py = points.length >= 2 ? (points[points.length - 1] ?? NaN) : NaN;\n\n if (Math.hypot(vertex.x - px, vertex.y - py) < weldEpsilon) {\n continue;\n }\n\n points.push(vertex.x, vertex.y);\n }\n\n // Drop a wrap-around duplicate (last ≈ first).\n dropWrapAroundDuplicate(points);\n\n const count = points.length / 2;\n\n if (count < 3) {\n throw new RangeError(`PolygonShape: needs at least 3 distinct vertices after welding, received ${count}.`);\n }\n\n // Canonicalise to CCW (positive signed area). Screen space is +Y down, so a\n // mathematically CCW polygon appears clockwise - the math stays consistent.\n if (signedArea(points) < 0) {\n reverseWinding(points);\n }\n\n const signed = signedArea(points);\n\n if (signed <= weldEpsilon) {\n throw new RangeError('PolygonShape: vertices are degenerate (zero/near-zero area).');\n }\n\n const normals = computeNormals(points);\n\n assertConvex(points);\n\n // Area, centroid and inertia via the standard polygon integrals.\n let cx = 0;\n let cy = 0;\n let inertiaOrigin = 0;\n\n for (let i = 0; i < count; i++) {\n const j = (i + 1) % count;\n // Indices are provably in-bounds (0..count-1 over a length-2*count array);\n // the `!` is zero-cost; a violation would surface as NaN, not a silent 0.\n const ix = points[i * 2]!;\n const iy = points[i * 2 + 1]!;\n const jx = points[j * 2]!;\n const jy = points[j * 2 + 1]!;\n const cross = ix * jy - jx * iy;\n\n cx += (ix + jx) * cross;\n cy += (iy + jy) * cross;\n inertiaOrigin += cross * (ix * ix + ix * jx + jx * jx + iy * iy + iy * jy + jy * jy);\n }\n\n const area = signed;\n cx /= 6 * area;\n cy /= 6 * area;\n\n this.count = count;\n this.vertices = Object.freeze(points);\n this.normals = Object.freeze(normals);\n this.massProperties = Object.freeze({\n area,\n centroidX: cx,\n centroidY: cy,\n // ∫ r² dA about the origin, then shifted to the centroid (parallel axis).\n unitInertia: inertiaOrigin / 12 - area * (cx * cx + cy * cy),\n });\n this.boundingRadius = boundingRadiusOf(points);\n\n // The vertex/normal arrays are frozen; the instance itself is not, so\n // BoxShape (and any future convex helper) can extend this class.\n }\n}\n\n/** Strip a trailing vertex that coincides with the first (wrap-around weld). */\nconst dropWrapAroundDuplicate = (points: number[]): void => {\n if (points.length < 4) {\n return;\n }\n\n const firstX = points[0] ?? NaN;\n const firstY = points[1] ?? NaN;\n const lastX = points[points.length - 2] ?? NaN;\n const lastY = points[points.length - 1] ?? NaN;\n\n if (Math.hypot(firstX - lastX, firstY - lastY) < weldEpsilon) {\n points.length -= 2;\n }\n};\n\n/** Signed area (positive = CCW). */\nconst signedArea = (points: number[]): number => {\n const count = points.length / 2;\n let sum = 0;\n\n for (let i = 0; i < count; i++) {\n const j = (i + 1) % count;\n const ix = points[i * 2]!;\n const iy = points[i * 2 + 1]!;\n const jx = points[j * 2]!;\n const jy = points[j * 2 + 1]!;\n sum += ix * jy - jx * iy;\n }\n\n return sum / 2;\n};\n\nconst reverseWinding = (points: number[]): void => {\n const count = points.length / 2;\n\n for (let i = 0; i < Math.floor(count / 2); i++) {\n const j = count - 1 - i;\n const ix = points[i * 2]!;\n const iy = points[i * 2 + 1]!;\n const jx = points[j * 2]!;\n const jy = points[j * 2 + 1]!;\n\n points[i * 2] = jx;\n points[i * 2 + 1] = jy;\n points[j * 2] = ix;\n points[j * 2 + 1] = iy;\n }\n};\n\n/** Outward unit normals for a CCW polygon: normal of edge `i→i+1` is `(eY, -eX)`. */\nconst computeNormals = (points: number[]): number[] => {\n const count = points.length / 2;\n const normals: number[] = [];\n\n for (let i = 0; i < count; i++) {\n const j = (i + 1) % count;\n const ix = points[i * 2]!;\n const iy = points[i * 2 + 1]!;\n const jx = points[j * 2]!;\n const jy = points[j * 2 + 1]!;\n const ex = jx - ix;\n const ey = jy - iy;\n const length = Math.hypot(ex, ey);\n\n normals.push(ey / length, -ex / length);\n }\n\n return normals;\n};\n\n/** Throw unless every interior turn has the same (CCW) orientation. */\nconst assertConvex = (points: number[]): void => {\n const count = points.length / 2;\n\n for (let i = 0; i < count; i++) {\n const a = i;\n const b = (i + 1) % count;\n const c = (i + 2) % count;\n const ax = points[a * 2]!;\n const ay = points[a * 2 + 1]!;\n const bx = points[b * 2]!;\n const by = points[b * 2 + 1]!;\n const cx = points[c * 2]!;\n const cy = points[c * 2 + 1]!;\n const e1x = bx - ax;\n const e1y = by - ay;\n const e2x = cx - bx;\n const e2y = cy - by;\n\n // CCW polygon ⇒ every cross product must be ≥ 0 (strictly > 0 for no collinear run).\n if (e1x * e2y - e1y * e2x <= 0) {\n throw new RangeError('PolygonShape: vertices are not strictly convex (or contain collinear edges).');\n }\n }\n};\n\nconst boundingRadiusOf = (points: number[]): number => {\n let max = 0;\n\n for (let i = 0; i < points.length; i += 2) {\n max = Math.max(max, Math.hypot(points[i]!, points[i + 1]!));\n }\n\n return max;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Shape } from './Shape';
|
|
2
|
+
/**
|
|
3
|
+
* A zero-thickness boundary between two local-space endpoints: level edges,
|
|
4
|
+
* one-off walls, the geometry a body may not cross.
|
|
5
|
+
*
|
|
6
|
+
* It has no interior, so {@link Shape.massProperties} is `null` and the shape
|
|
7
|
+
* contributes collision only - a `dynamic` body needs at least one mass-bearing
|
|
8
|
+
* collider alongside it.
|
|
9
|
+
*
|
|
10
|
+
* A segment blocks from **both** sides. There is no inside for it to be outside
|
|
11
|
+
* of, and a one-sided segment would be a gameplay rule in a geometry costume:
|
|
12
|
+
* express that with the world's `contactModifier`, which decides per contact and
|
|
13
|
+
* per step whether it is solved.
|
|
14
|
+
*
|
|
15
|
+
* Being zero-thickness makes it the worst case for discrete detection - a body
|
|
16
|
+
* that crosses it entirely within one fixed step misses it. Continuous collision
|
|
17
|
+
* covers a fast body against a segment; a fast segment is not swept.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SegmentShape extends Shape {
|
|
20
|
+
readonly type: "segment";
|
|
21
|
+
/** Local endpoints, flattened - the same layout a polygon uses. */
|
|
22
|
+
readonly vertices: readonly number[];
|
|
23
|
+
/** The two opposite outward unit normals, flattened. */
|
|
24
|
+
readonly normals: readonly number[];
|
|
25
|
+
readonly length: number;
|
|
26
|
+
readonly boundingRadius: number;
|
|
27
|
+
/** Always `null`: a boundary has no interior and therefore no mass. */
|
|
28
|
+
readonly massProperties: null;
|
|
29
|
+
constructor(x0: number, y0: number, x1: number, y1: number);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=SegmentShape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentShape.d.ts","sourceRoot":"","sources":["../../../src/shapes/SegmentShape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,IAAI,EAAG,SAAS,CAAU;IAE1C,mEAAmE;IACnE,SAAgB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAE5C,wDAAwD;IACxD,SAAgB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAE3C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,cAAc,EAAE,MAAM,CAAC;IAEvC,uEAAuE;IACvE,SAAgB,cAAc,OAAQ;gBAEnB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;CA+BlE"}
|