emeraldengine 2.2.1 → 3.1.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.
Files changed (208) hide show
  1. package/README.md +2198 -968
  2. package/dist/types/index.d.ts +75 -32
  3. package/dist/types/src/Animator.d.ts +50 -0
  4. package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
  5. package/dist/types/src/Camera.d.ts +122 -0
  6. package/dist/types/src/CameraController.d.ts +107 -0
  7. package/dist/types/src/CanvasText.d.ts +91 -0
  8. package/dist/types/src/CollisionLayers.d.ts +58 -0
  9. package/dist/types/{Color.d.ts → src/Color.d.ts} +6 -6
  10. package/dist/types/src/Coroutine.d.ts +111 -0
  11. package/dist/types/src/DebugOverlay.d.ts +86 -0
  12. package/dist/types/src/Drawable.d.ts +271 -0
  13. package/dist/types/src/Easing.d.ts +22 -0
  14. package/dist/types/src/Emerald.d.ts +420 -0
  15. package/dist/types/src/EmeraldDB.d.ts +159 -0
  16. package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
  17. package/dist/types/src/GLUtils.d.ts +4 -0
  18. package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
  19. package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +75 -23
  20. package/dist/types/src/Interpolator.d.ts +66 -0
  21. package/dist/types/src/Material.d.ts +87 -0
  22. package/dist/types/src/MathUtils.d.ts +81 -0
  23. package/dist/types/src/ParticleEmitter.d.ts +131 -0
  24. package/dist/types/src/Physics.d.ts +337 -0
  25. package/dist/types/src/Pool.d.ts +53 -0
  26. package/dist/types/src/PostEffects.d.ts +68 -0
  27. package/dist/types/src/PostProcessor.d.ts +124 -0
  28. package/dist/types/src/RenderTarget.d.ts +56 -0
  29. package/dist/types/src/Scene.d.ts +62 -0
  30. package/dist/types/src/ScreenEffects.d.ts +111 -0
  31. package/dist/types/src/Serializer.d.ts +86 -0
  32. package/dist/types/src/Shaders.d.ts +2 -0
  33. package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
  34. package/dist/types/src/SpatialGrid.d.ts +50 -0
  35. package/dist/types/src/SpriteBatch.d.ts +89 -0
  36. package/dist/types/src/StateMachine.d.ts +59 -0
  37. package/dist/types/src/Storage.d.ts +89 -0
  38. package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
  39. package/dist/types/src/TextureAtlas.d.ts +55 -0
  40. package/dist/types/src/Tilemap.d.ts +91 -0
  41. package/dist/types/src/Time.d.ts +53 -0
  42. package/dist/types/src/Timer.d.ts +54 -0
  43. package/dist/types/src/Transform.d.ts +94 -0
  44. package/dist/types/src/Tween.d.ts +81 -0
  45. package/dist/types/src/UI.d.ts +121 -0
  46. package/dist/types/src/components/Behaviour.d.ts +71 -0
  47. package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
  48. package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
  49. package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
  50. package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
  51. package/dist/types/src/components/Collider.d.ts +102 -0
  52. package/dist/types/src/components/GameObject.d.ts +135 -0
  53. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  54. package/dist/types/src/components/RigidBody.d.ts +456 -0
  55. package/dist/types/src/importers/Aseprite.d.ts +79 -0
  56. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  57. package/dist/types/src/importers/TiledMap.d.ts +62 -0
  58. package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
  59. package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
  60. package/dist/types/src/managers/AssetManager.d.ts +116 -0
  61. package/dist/types/src/managers/AudioManager.d.ts +259 -0
  62. package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
  63. package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
  64. package/dist/types/src/managers/GLManager.d.ts +84 -0
  65. package/dist/types/src/managers/GLState.d.ts +37 -0
  66. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  67. package/dist/types/src/managers/IDManager.d.ts +31 -0
  68. package/dist/types/src/managers/InputManager.d.ts +381 -0
  69. package/dist/types/src/managers/NetworkManager.d.ts +93 -0
  70. package/dist/types/src/managers/RenderStats.d.ts +34 -0
  71. package/dist/types/src/managers/SceneManager.d.ts +44 -0
  72. package/dist/types/src/managers/ShaderManager.d.ts +55 -0
  73. package/dist/types/src/managers/TextureManager.d.ts +102 -0
  74. package/dist/types/src/particlesystem/Particle.d.ts +64 -0
  75. package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
  76. package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
  77. package/dist/types/src/physics/AABB.d.ts +92 -0
  78. package/dist/types/src/physics/Body.d.ts +435 -0
  79. package/dist/types/src/physics/BodyType.d.ts +6 -0
  80. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  81. package/dist/types/src/physics/Collision.d.ts +102 -0
  82. package/dist/types/src/physics/Contact.d.ts +206 -0
  83. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  84. package/dist/types/src/physics/Distance.d.ts +54 -0
  85. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  86. package/dist/types/src/physics/Fixture.d.ts +221 -0
  87. package/dist/types/src/physics/Island.d.ts +52 -0
  88. package/dist/types/src/physics/Joint.d.ts +59 -0
  89. package/dist/types/src/physics/Math2D.d.ts +371 -0
  90. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  91. package/dist/types/src/physics/Settings.d.ts +22 -0
  92. package/dist/types/src/physics/Shapes.d.ts +207 -0
  93. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  94. package/dist/types/src/physics/World.d.ts +274 -0
  95. package/dist/types/src/physics/index.d.ts +34 -0
  96. package/index.js +78 -0
  97. package/package.json +73 -60
  98. package/src/Animator.js +95 -0
  99. package/src/BitmapText.js +6 -5
  100. package/src/Camera.js +183 -0
  101. package/src/CameraController.js +192 -0
  102. package/src/CanvasText.js +281 -0
  103. package/src/CollisionLayers.js +86 -0
  104. package/src/Color.js +26 -18
  105. package/src/Coroutine.js +259 -0
  106. package/src/DebugOverlay.js +246 -0
  107. package/src/Drawable.js +842 -582
  108. package/src/Easing.js +57 -0
  109. package/src/Emerald.js +1150 -459
  110. package/src/EmeraldDB.js +328 -0
  111. package/src/FPSCounter.js +43 -43
  112. package/src/GLUtils.js +60 -67
  113. package/src/Instance.js +41 -5
  114. package/src/InstancedTexture.js +308 -127
  115. package/src/Interpolator.js +124 -0
  116. package/src/Material.js +202 -0
  117. package/src/MathUtils.js +134 -0
  118. package/src/ParticleEmitter.js +284 -0
  119. package/src/Physics.js +431 -40
  120. package/src/Pool.js +85 -0
  121. package/src/PostEffects.js +296 -0
  122. package/src/PostProcessor.js +304 -0
  123. package/src/RenderTarget.js +134 -0
  124. package/src/Scene.js +115 -83
  125. package/src/ScreenEffects.js +266 -0
  126. package/src/Serializer.js +131 -0
  127. package/src/Shaders.js +150 -165
  128. package/src/Shapes.js +118 -129
  129. package/src/SpatialGrid.js +111 -0
  130. package/src/SpriteBatch.js +299 -0
  131. package/src/StateMachine.js +82 -0
  132. package/src/Storage.js +175 -47
  133. package/src/Texture.js +58 -67
  134. package/src/TextureAtlas.js +96 -0
  135. package/src/Tilemap.js +274 -0
  136. package/src/Time.js +51 -6
  137. package/src/Timer.js +99 -0
  138. package/src/Transform.js +100 -7
  139. package/src/Tween.js +160 -0
  140. package/src/UI.js +394 -0
  141. package/src/components/Behaviour.js +90 -0
  142. package/src/components/BoxCollider.js +29 -12
  143. package/src/components/BoxColliderDebug.js +3 -4
  144. package/src/components/CircleCollider.js +26 -12
  145. package/src/components/CircleColliderDebug.js +25 -24
  146. package/src/components/Collider.js +150 -34
  147. package/src/components/GameObject.js +130 -21
  148. package/src/components/PolygonCollider.js +55 -0
  149. package/src/components/RigidBody.js +556 -8
  150. package/src/importers/Aseprite.js +142 -0
  151. package/src/importers/ForgeLevel.js +581 -0
  152. package/src/importers/TiledMap.js +158 -0
  153. package/src/lights/DirectionalLight.js +6 -15
  154. package/src/lights/PointLight.js +4 -4
  155. package/src/managers/AssetManager.js +239 -0
  156. package/src/managers/AudioManager.js +565 -146
  157. package/src/managers/EventManager.js +488 -477
  158. package/src/managers/GLManager.js +57 -0
  159. package/src/managers/GLState.js +70 -0
  160. package/src/managers/Gamepad.js +126 -0
  161. package/src/managers/IDManager.js +24 -2
  162. package/src/managers/InputManager.js +905 -0
  163. package/src/managers/NetworkManager.js +178 -0
  164. package/src/managers/RenderStats.js +34 -0
  165. package/src/managers/SceneManager.js +30 -0
  166. package/src/managers/ShaderManager.js +0 -2
  167. package/src/managers/TextureManager.js +218 -0
  168. package/src/particlesystem/Particle.js +82 -7
  169. package/src/particlesystem/ParticleSettings.js +21 -3
  170. package/src/particlesystem/Particles.js +80 -31
  171. package/src/physics/AABB.js +207 -0
  172. package/src/physics/Body.js +862 -0
  173. package/src/physics/BodyType.js +16 -0
  174. package/src/physics/BroadPhase.js +641 -0
  175. package/src/physics/Collision.js +534 -0
  176. package/src/physics/Contact.js +500 -0
  177. package/src/physics/ContactSolver.js +526 -0
  178. package/src/physics/Distance.js +403 -0
  179. package/src/physics/DistanceJoint.js +227 -0
  180. package/src/physics/Fixture.js +346 -0
  181. package/src/physics/Island.js +203 -0
  182. package/src/physics/Joint.js +78 -0
  183. package/src/physics/Math2D.js +573 -0
  184. package/src/physics/RevoluteJoint.js +278 -0
  185. package/src/physics/Settings.js +78 -0
  186. package/src/physics/Shapes.js +549 -0
  187. package/src/physics/TimeOfImpact.js +87 -0
  188. package/src/physics/World.js +731 -0
  189. package/src/physics/index.js +79 -0
  190. package/dist/types/Drawable.d.ts +0 -157
  191. package/dist/types/Emerald.d.ts +0 -73
  192. package/dist/types/GLUtils.d.ts +0 -6
  193. package/dist/types/Physics.d.ts +0 -139
  194. package/dist/types/Scene.d.ts +0 -39
  195. package/dist/types/Shaders.d.ts +0 -4
  196. package/dist/types/Storage.d.ts +0 -46
  197. package/dist/types/Time.d.ts +0 -22
  198. package/dist/types/Transform.d.ts +0 -41
  199. package/dist/types/components/BoxColliderDebug.d.ts +0 -19
  200. package/dist/types/components/CircleColliderDebug.d.ts +0 -19
  201. package/dist/types/components/Collider.d.ts +0 -53
  202. package/dist/types/components/GameObject.d.ts +0 -72
  203. package/dist/types/components/RigidBody.d.ts +0 -104
  204. package/dist/types/managers/AudioManager.d.ts +0 -60
  205. package/dist/types/managers/GLManager.d.ts +0 -47
  206. package/dist/types/managers/IDManager.d.ts +0 -21
  207. package/dist/types/managers/SceneManager.d.ts +0 -22
  208. package/dist/types/particlesystem/Particle.d.ts +0 -42
@@ -0,0 +1,534 @@
1
+ import { Vec2, Rot, Transform2, EPSILON } from "./Math2D.js";
2
+ import Settings from "./Settings.js";
3
+
4
+ /**
5
+ * @enum ManifoldType
6
+ * @description How a manifold's reference frame is stored: either two circle
7
+ * centers, or a reference face on body A or on body B.
8
+ */
9
+ const ManifoldType = {
10
+ CIRCLES: 0,
11
+ FACE_A: 1,
12
+ FACE_B: 2,
13
+ };
14
+
15
+ /**
16
+ * @enum ContactFeature
17
+ * @description Whether a contact point came from a vertex or a face, used to
18
+ * build the stable per-point id that carries impulses across steps.
19
+ * @private
20
+ */
21
+ const ContactFeature = {
22
+ VERTEX: 0,
23
+ FACE: 1,
24
+ };
25
+
26
+ /**
27
+ * @function featureKey
28
+ * @description Packs a contact feature into a single integer so warm starting
29
+ * can match this step's points against the previous step's.
30
+ * @private
31
+ */
32
+ function featureKey(indexA, indexB, typeA, typeB) {
33
+ return (
34
+ (indexA & 0xff) | ((indexB & 0xff) << 8) | (typeA << 16) | (typeB << 17)
35
+ );
36
+ }
37
+
38
+ /**
39
+ * @class ManifoldPoint
40
+ * @description A single contact point, stored in the reference body's local
41
+ * frame so it stays valid as the bodies move within a step.
42
+ */
43
+ class ManifoldPoint {
44
+ constructor() {
45
+ this.localPoint = new Vec2();
46
+ this.normalImpulse = 0;
47
+ this.tangentImpulse = 0;
48
+ this.id = 0;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @class Manifold
54
+ * @description Up to two contact points plus the frame they are expressed in.
55
+ * Two points is all a 2D convex-convex overlap can produce, and it is exactly
56
+ * what a stable resting contact (a box on the ground) needs.
57
+ */
58
+ class Manifold {
59
+ constructor() {
60
+ this.type = ManifoldType.CIRCLES;
61
+ this.localNormal = new Vec2();
62
+ this.localPoint = new Vec2();
63
+ this.points = [new ManifoldPoint(), new ManifoldPoint()];
64
+ this.pointCount = 0;
65
+ }
66
+
67
+ /**
68
+ * @method reset
69
+ * @description Empties the manifold.
70
+ */
71
+ reset() {
72
+ this.pointCount = 0;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @class WorldManifold
78
+ * @description A manifold converted into world space: the collision normal
79
+ * (always pointing from body A to body B), the contact points, and how deeply
80
+ * each one is penetrating (negative means overlapping).
81
+ */
82
+ class WorldManifold {
83
+ constructor() {
84
+ this.normal = new Vec2();
85
+ this.points = [new Vec2(), new Vec2()];
86
+ this.separations = [0, 0];
87
+ this.pointCount = 0;
88
+ }
89
+
90
+ /**
91
+ * @method initialize
92
+ * @description Fills this world manifold from a local manifold.
93
+ * @param {Manifold} manifold
94
+ * @param {Transform2} xfA
95
+ * @param {number} radiusA
96
+ * @param {Transform2} xfB
97
+ * @param {number} radiusB
98
+ * @returns {WorldManifold} - this
99
+ */
100
+ initialize(manifold, xfA, radiusA, xfB, radiusB) {
101
+ this.pointCount = manifold.pointCount;
102
+ if (manifold.pointCount === 0) return this;
103
+
104
+ switch (manifold.type) {
105
+ case ManifoldType.CIRCLES: {
106
+ this.normal.set(1, 0);
107
+ const pointA = Transform2.mulVec(xfA, manifold.localPoint);
108
+ const pointB = Transform2.mulVec(xfB, manifold.points[0].localPoint);
109
+ if (Vec2.distanceSquared(pointA, pointB) > EPSILON * EPSILON) {
110
+ this.normal.set(pointB.x - pointA.x, pointB.y - pointA.y);
111
+ this.normal.normalize();
112
+ }
113
+ const cAx = pointA.x + radiusA * this.normal.x;
114
+ const cAy = pointA.y + radiusA * this.normal.y;
115
+ const cBx = pointB.x - radiusB * this.normal.x;
116
+ const cBy = pointB.y - radiusB * this.normal.y;
117
+ this.points[0].set(0.5 * (cAx + cBx), 0.5 * (cAy + cBy));
118
+ this.separations[0] =
119
+ (cBx - cAx) * this.normal.x + (cBy - cAy) * this.normal.y;
120
+ break;
121
+ }
122
+ case ManifoldType.FACE_A: {
123
+ this.normal.copy(Rot.mulVec(xfA.q, manifold.localNormal));
124
+ const planePoint = Transform2.mulVec(xfA, manifold.localPoint);
125
+ for (let i = 0; i < manifold.pointCount; i++) {
126
+ const clip = Transform2.mulVec(xfB, manifold.points[i].localPoint);
127
+ const s =
128
+ radiusA -
129
+ ((clip.x - planePoint.x) * this.normal.x +
130
+ (clip.y - planePoint.y) * this.normal.y);
131
+ const cAx = clip.x + s * this.normal.x;
132
+ const cAy = clip.y + s * this.normal.y;
133
+ const cBx = clip.x - radiusB * this.normal.x;
134
+ const cBy = clip.y - radiusB * this.normal.y;
135
+ this.points[i].set(0.5 * (cAx + cBx), 0.5 * (cAy + cBy));
136
+ this.separations[i] =
137
+ (cBx - cAx) * this.normal.x + (cBy - cAy) * this.normal.y;
138
+ }
139
+ break;
140
+ }
141
+ case ManifoldType.FACE_B: {
142
+ this.normal.copy(Rot.mulVec(xfB.q, manifold.localNormal));
143
+ const planePoint = Transform2.mulVec(xfB, manifold.localPoint);
144
+ for (let i = 0; i < manifold.pointCount; i++) {
145
+ const clip = Transform2.mulVec(xfA, manifold.points[i].localPoint);
146
+ const s =
147
+ radiusB -
148
+ ((clip.x - planePoint.x) * this.normal.x +
149
+ (clip.y - planePoint.y) * this.normal.y);
150
+ const cBx = clip.x + s * this.normal.x;
151
+ const cBy = clip.y + s * this.normal.y;
152
+ const cAx = clip.x - radiusA * this.normal.x;
153
+ const cAy = clip.y - radiusA * this.normal.y;
154
+ this.points[i].set(0.5 * (cAx + cBx), 0.5 * (cAy + cBy));
155
+ this.separations[i] =
156
+ (cAx - cBx) * this.normal.x + (cAy - cBy) * this.normal.y;
157
+ }
158
+ this.normal.neg();
159
+ break;
160
+ }
161
+ default:
162
+ break;
163
+ }
164
+ return this;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * @function mulTXf
170
+ * @description Composes transforms: returns the transform of `B` expressed in
171
+ * `A`'s frame.
172
+ * @private
173
+ */
174
+ function mulTXf(A, B) {
175
+ const out = new Transform2();
176
+ out.q.s = A.q.c * B.q.s - A.q.s * B.q.c;
177
+ out.q.c = A.q.c * B.q.c + A.q.s * B.q.s;
178
+ const px = B.p.x - A.p.x;
179
+ const py = B.p.y - A.p.y;
180
+ out.p.set(A.q.c * px + A.q.s * py, -A.q.s * px + A.q.c * py);
181
+ return out;
182
+ }
183
+
184
+ /**
185
+ * @function collideCircles
186
+ * @description Builds the manifold for two circles.
187
+ * @param {Manifold} manifold - Output manifold
188
+ * @param {CircleShape} circleA
189
+ * @param {Transform2} xfA
190
+ * @param {CircleShape} circleB
191
+ * @param {Transform2} xfB
192
+ */
193
+ function collideCircles(manifold, circleA, xfA, circleB, xfB) {
194
+ manifold.pointCount = 0;
195
+
196
+ const pA = Transform2.mulVec(xfA, circleA.p);
197
+ const pB = Transform2.mulVec(xfB, circleB.p);
198
+ const distSqr = Vec2.distanceSquared(pA, pB);
199
+ const radius = circleA.radius + circleB.radius;
200
+ if (distSqr > radius * radius) return;
201
+
202
+ manifold.type = ManifoldType.CIRCLES;
203
+ manifold.localPoint.copy(circleA.p);
204
+ manifold.localNormal.setZero();
205
+ manifold.pointCount = 1;
206
+ manifold.points[0].localPoint.copy(circleB.p);
207
+ manifold.points[0].id = 0;
208
+ }
209
+
210
+ /**
211
+ * @function collidePolygonAndCircle
212
+ * @description Builds the manifold for a polygon against a circle. The circle
213
+ * is pushed into the polygon's frame, then tested against the closest face,
214
+ * falling back to the nearest corner when it sits past the face's edge.
215
+ * @param {Manifold} manifold - Output manifold
216
+ * @param {PolygonShape} polygonA
217
+ * @param {Transform2} xfA
218
+ * @param {CircleShape} circleB
219
+ * @param {Transform2} xfB
220
+ */
221
+ function collidePolygonAndCircle(manifold, polygonA, xfA, circleB, xfB) {
222
+ manifold.pointCount = 0;
223
+
224
+ const c = Transform2.mulVec(xfB, circleB.p);
225
+ const cLocal = Transform2.mulTVec(xfA, c);
226
+
227
+ let normalIndex = 0;
228
+ let separation = -Number.MAX_VALUE;
229
+ const radius = polygonA.radius + circleB.radius;
230
+ const vertexCount = polygonA.vertices.length;
231
+ const vertices = polygonA.vertices;
232
+ const normals = polygonA.normals;
233
+
234
+ for (let i = 0; i < vertexCount; i++) {
235
+ const s =
236
+ normals[i].x * (cLocal.x - vertices[i].x) +
237
+ normals[i].y * (cLocal.y - vertices[i].y);
238
+ if (s > radius) return;
239
+ if (s > separation) {
240
+ separation = s;
241
+ normalIndex = i;
242
+ }
243
+ }
244
+
245
+ const vertIndex1 = normalIndex;
246
+ const vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
247
+ const v1 = vertices[vertIndex1];
248
+ const v2 = vertices[vertIndex2];
249
+
250
+ if (separation < EPSILON) {
251
+ manifold.pointCount = 1;
252
+ manifold.type = ManifoldType.FACE_A;
253
+ manifold.localNormal.copy(normals[normalIndex]);
254
+ manifold.localPoint.set(0.5 * (v1.x + v2.x), 0.5 * (v1.y + v2.y));
255
+ manifold.points[0].localPoint.copy(circleB.p);
256
+ manifold.points[0].id = 0;
257
+ return;
258
+ }
259
+
260
+ const u1 =
261
+ (cLocal.x - v1.x) * (v2.x - v1.x) + (cLocal.y - v1.y) * (v2.y - v1.y);
262
+ const u2 =
263
+ (cLocal.x - v2.x) * (v1.x - v2.x) + (cLocal.y - v2.y) * (v1.y - v2.y);
264
+
265
+ if (u1 <= 0) {
266
+ if (Vec2.distanceSquared(cLocal, v1) > radius * radius) return;
267
+ manifold.pointCount = 1;
268
+ manifold.type = ManifoldType.FACE_A;
269
+ manifold.localNormal.set(cLocal.x - v1.x, cLocal.y - v1.y);
270
+ manifold.localNormal.normalize();
271
+ manifold.localPoint.copy(v1);
272
+ } else if (u2 <= 0) {
273
+ if (Vec2.distanceSquared(cLocal, v2) > radius * radius) return;
274
+ manifold.pointCount = 1;
275
+ manifold.type = ManifoldType.FACE_A;
276
+ manifold.localNormal.set(cLocal.x - v2.x, cLocal.y - v2.y);
277
+ manifold.localNormal.normalize();
278
+ manifold.localPoint.copy(v2);
279
+ } else {
280
+ const faceCenterX = 0.5 * (v1.x + v2.x);
281
+ const faceCenterY = 0.5 * (v1.y + v2.y);
282
+ const s =
283
+ (cLocal.x - faceCenterX) * normals[vertIndex1].x +
284
+ (cLocal.y - faceCenterY) * normals[vertIndex1].y;
285
+ if (s > radius) return;
286
+ manifold.pointCount = 1;
287
+ manifold.type = ManifoldType.FACE_A;
288
+ manifold.localNormal.copy(normals[vertIndex1]);
289
+ manifold.localPoint.set(faceCenterX, faceCenterY);
290
+ }
291
+
292
+ manifold.points[0].localPoint.copy(circleB.p);
293
+ manifold.points[0].id = 0;
294
+ }
295
+
296
+ /**
297
+ * @function findMaxSeparation
298
+ * @description Separating-axis test: finds the face of `poly1` that pushes
299
+ * `poly2` furthest away, and how far.
300
+ * @returns {{separation:number, edgeIndex:number}}
301
+ * @private
302
+ */
303
+ function findMaxSeparation(poly1, xf1, poly2, xf2) {
304
+ const count1 = poly1.vertices.length;
305
+ const count2 = poly2.vertices.length;
306
+ const n1s = poly1.normals;
307
+ const v1s = poly1.vertices;
308
+ const v2s = poly2.vertices;
309
+ const xf = mulTXf(xf2, xf1);
310
+
311
+ let bestIndex = 0;
312
+ let maxSeparation = -Number.MAX_VALUE;
313
+
314
+ for (let i = 0; i < count1; i++) {
315
+ const n = Rot.mulVec(xf.q, n1s[i]);
316
+ const v1 = Transform2.mulVec(xf, v1s[i]);
317
+
318
+ let si = Number.MAX_VALUE;
319
+ for (let j = 0; j < count2; j++) {
320
+ const sij = n.x * (v2s[j].x - v1.x) + n.y * (v2s[j].y - v1.y);
321
+ if (sij < si) si = sij;
322
+ }
323
+
324
+ if (si > maxSeparation) {
325
+ maxSeparation = si;
326
+ bestIndex = i;
327
+ }
328
+ }
329
+
330
+ return { separation: maxSeparation, edgeIndex: bestIndex };
331
+ }
332
+
333
+ /**
334
+ * @function findIncidentEdge
335
+ * @description Picks the edge of `poly2` most anti-parallel to the reference
336
+ * face: the edge that is actually being pressed into it.
337
+ * @private
338
+ */
339
+ function findIncidentEdge(poly1, xf1, edge1, poly2, xf2) {
340
+ const normals1 = poly1.normals;
341
+ const count2 = poly2.vertices.length;
342
+ const vertices2 = poly2.vertices;
343
+ const normals2 = poly2.normals;
344
+
345
+ const normal1World = Rot.mulVec(xf1.q, normals1[edge1]);
346
+ const normal1 = Rot.mulTVec(xf2.q, normal1World);
347
+
348
+ let index = 0;
349
+ let minDot = Number.MAX_VALUE;
350
+ for (let i = 0; i < count2; i++) {
351
+ const dot = normal1.x * normals2[i].x + normal1.y * normals2[i].y;
352
+ if (dot < minDot) {
353
+ minDot = dot;
354
+ index = i;
355
+ }
356
+ }
357
+
358
+ const i1 = index;
359
+ const i2 = i1 + 1 < count2 ? i1 + 1 : 0;
360
+
361
+ return [
362
+ {
363
+ v: Transform2.mulVec(xf2, vertices2[i1]),
364
+ indexA: edge1,
365
+ indexB: i1,
366
+ typeA: ContactFeature.FACE,
367
+ typeB: ContactFeature.VERTEX,
368
+ },
369
+ {
370
+ v: Transform2.mulVec(xf2, vertices2[i2]),
371
+ indexA: edge1,
372
+ indexB: i2,
373
+ typeA: ContactFeature.FACE,
374
+ typeB: ContactFeature.VERTEX,
375
+ },
376
+ ];
377
+ }
378
+
379
+ /**
380
+ * @function clipSegmentToLine
381
+ * @description Sutherland-Hodgman clip of the incident edge against one side
382
+ * plane of the reference face.
383
+ * @returns {Array} - The surviving 0..2 clip vertices
384
+ * @private
385
+ */
386
+ function clipSegmentToLine(vIn, normal, offset, vertexIndexA) {
387
+ const vOut = [];
388
+
389
+ const d0 = normal.x * vIn[0].v.x + normal.y * vIn[0].v.y - offset;
390
+ const d1 = normal.x * vIn[1].v.x + normal.y * vIn[1].v.y - offset;
391
+
392
+ if (d0 <= 0) vOut.push(vIn[0]);
393
+ if (d1 <= 0) vOut.push(vIn[1]);
394
+
395
+ if (d0 * d1 < 0) {
396
+ const interp = d0 / (d0 - d1);
397
+ vOut.push({
398
+ v: new Vec2(
399
+ vIn[0].v.x + interp * (vIn[1].v.x - vIn[0].v.x),
400
+ vIn[0].v.y + interp * (vIn[1].v.y - vIn[0].v.y)
401
+ ),
402
+ indexA: vertexIndexA,
403
+ indexB: vIn[0].indexB,
404
+ typeA: ContactFeature.VERTEX,
405
+ typeB: ContactFeature.FACE,
406
+ });
407
+ }
408
+
409
+ return vOut;
410
+ }
411
+
412
+ /**
413
+ * @function collidePolygons
414
+ * @description Builds the manifold for two convex polygons using the
415
+ * separating-axis test to pick a reference face, then clipping the opposing
416
+ * (incident) edge against it. The result is the one- or two-point contact patch
417
+ * that makes boxes rest flat instead of rocking.
418
+ * @param {Manifold} manifold - Output manifold
419
+ * @param {PolygonShape} polyA
420
+ * @param {Transform2} xfA
421
+ * @param {PolygonShape} polyB
422
+ * @param {Transform2} xfB
423
+ */
424
+ function collidePolygons(manifold, polyA, xfA, polyB, xfB) {
425
+ manifold.pointCount = 0;
426
+ const totalRadius = polyA.radius + polyB.radius;
427
+
428
+ const edgeA = findMaxSeparation(polyA, xfA, polyB, xfB);
429
+ if (edgeA.separation > totalRadius) return;
430
+
431
+ const edgeB = findMaxSeparation(polyB, xfB, polyA, xfA);
432
+ if (edgeB.separation > totalRadius) return;
433
+
434
+ let poly1;
435
+ let poly2;
436
+ let xf1;
437
+ let xf2;
438
+ let edge1;
439
+ let flip;
440
+
441
+ const tol = 0.1 * Settings.linearSlop;
442
+ if (edgeB.separation > edgeA.separation + tol) {
443
+ poly1 = polyB;
444
+ poly2 = polyA;
445
+ xf1 = xfB;
446
+ xf2 = xfA;
447
+ edge1 = edgeB.edgeIndex;
448
+ manifold.type = ManifoldType.FACE_B;
449
+ flip = true;
450
+ } else {
451
+ poly1 = polyA;
452
+ poly2 = polyB;
453
+ xf1 = xfA;
454
+ xf2 = xfB;
455
+ edge1 = edgeA.edgeIndex;
456
+ manifold.type = ManifoldType.FACE_A;
457
+ flip = false;
458
+ }
459
+
460
+ const incidentEdge = findIncidentEdge(poly1, xf1, edge1, poly2, xf2);
461
+
462
+ const count1 = poly1.vertices.length;
463
+ const iv1 = edge1;
464
+ const iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
465
+
466
+ let v11 = poly1.vertices[iv1];
467
+ let v12 = poly1.vertices[iv2];
468
+
469
+ const localTangent = Vec2.sub(v12, v11);
470
+ localTangent.normalize();
471
+ const localNormal = new Vec2(localTangent.y, -localTangent.x);
472
+ const planePoint = new Vec2(0.5 * (v11.x + v12.x), 0.5 * (v11.y + v12.y));
473
+
474
+ const tangent = Rot.mulVec(xf1.q, localTangent);
475
+ const normal = new Vec2(tangent.y, -tangent.x);
476
+
477
+ v11 = Transform2.mulVec(xf1, v11);
478
+ v12 = Transform2.mulVec(xf1, v12);
479
+
480
+ const frontOffset = normal.x * v11.x + normal.y * v11.y;
481
+ const sideOffset1 = -(tangent.x * v11.x + tangent.y * v11.y) + totalRadius;
482
+ const sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius;
483
+
484
+ const clip1 = clipSegmentToLine(
485
+ incidentEdge,
486
+ Vec2.neg(tangent),
487
+ sideOffset1,
488
+ iv1
489
+ );
490
+ if (clip1.length < 2) return;
491
+
492
+ const clip2 = clipSegmentToLine(clip1, tangent, sideOffset2, iv2);
493
+ if (clip2.length < 2) return;
494
+
495
+ manifold.localNormal.copy(localNormal);
496
+ manifold.localPoint.copy(planePoint);
497
+
498
+ let pointCount = 0;
499
+ for (let i = 0; i < 2; i++) {
500
+ const separation =
501
+ normal.x * clip2[i].v.x + normal.y * clip2[i].v.y - frontOffset;
502
+ if (separation <= totalRadius) {
503
+ const cp = manifold.points[pointCount];
504
+ cp.localPoint.copy(Transform2.mulTVec(xf2, clip2[i].v));
505
+ cp.id = flip
506
+ ? featureKey(
507
+ clip2[i].indexB,
508
+ clip2[i].indexA,
509
+ clip2[i].typeB,
510
+ clip2[i].typeA
511
+ )
512
+ : featureKey(
513
+ clip2[i].indexA,
514
+ clip2[i].indexB,
515
+ clip2[i].typeA,
516
+ clip2[i].typeB
517
+ );
518
+ pointCount++;
519
+ }
520
+ }
521
+
522
+ manifold.pointCount = pointCount;
523
+ }
524
+
525
+ export {
526
+ Manifold,
527
+ ManifoldPoint,
528
+ ManifoldType,
529
+ WorldManifold,
530
+ collideCircles,
531
+ collidePolygonAndCircle,
532
+ collidePolygons,
533
+ mulTXf,
534
+ };