@melonjs/matter-adapter 1.0.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/build/index.js ADDED
@@ -0,0 +1,1173 @@
1
+ /*!
2
+ * melonJS physics adapter for matter-js - 1.0.0
3
+ * http://www.melonjs.org
4
+ * @melonjs/matter-adapter is licensed under the MIT License.
5
+ * http://www.opensource.org/licenses/mit-license
6
+ * @copyright (C) 2011 - 2026 Olivier Biot (AltByte Pte Ltd)
7
+ */
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __commonJS = (cb, mod) => function __require() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ // ../../node_modules/.pnpm/poly-decomp@0.3.0/node_modules/poly-decomp/src/index.js
35
+ var require_src = __commonJS({
36
+ "../../node_modules/.pnpm/poly-decomp@0.3.0/node_modules/poly-decomp/src/index.js"(exports, module) {
37
+ module.exports = {
38
+ decomp: polygonDecomp,
39
+ quickDecomp: polygonQuickDecomp,
40
+ isSimple: polygonIsSimple,
41
+ removeCollinearPoints: polygonRemoveCollinearPoints,
42
+ removeDuplicatePoints: polygonRemoveDuplicatePoints,
43
+ makeCCW: polygonMakeCCW
44
+ };
45
+ function lineInt(l1, l2, precision) {
46
+ precision = precision || 0;
47
+ var i = [0, 0];
48
+ var a1, b1, c1, a2, b2, c2, det;
49
+ a1 = l1[1][1] - l1[0][1];
50
+ b1 = l1[0][0] - l1[1][0];
51
+ c1 = a1 * l1[0][0] + b1 * l1[0][1];
52
+ a2 = l2[1][1] - l2[0][1];
53
+ b2 = l2[0][0] - l2[1][0];
54
+ c2 = a2 * l2[0][0] + b2 * l2[0][1];
55
+ det = a1 * b2 - a2 * b1;
56
+ if (!scalar_eq(det, 0, precision)) {
57
+ i[0] = (b2 * c1 - b1 * c2) / det;
58
+ i[1] = (a1 * c2 - a2 * c1) / det;
59
+ }
60
+ return i;
61
+ }
62
+ function lineSegmentsIntersect(p1, p2, q1, q2) {
63
+ var dx = p2[0] - p1[0];
64
+ var dy = p2[1] - p1[1];
65
+ var da = q2[0] - q1[0];
66
+ var db = q2[1] - q1[1];
67
+ if (da * dy - db * dx === 0) {
68
+ return false;
69
+ }
70
+ var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx);
71
+ var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy);
72
+ return s >= 0 && s <= 1 && t >= 0 && t <= 1;
73
+ }
74
+ function triangleArea(a, b, c) {
75
+ return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
76
+ }
77
+ function isLeft(a, b, c) {
78
+ return triangleArea(a, b, c) > 0;
79
+ }
80
+ function isLeftOn(a, b, c) {
81
+ return triangleArea(a, b, c) >= 0;
82
+ }
83
+ function isRight(a, b, c) {
84
+ return triangleArea(a, b, c) < 0;
85
+ }
86
+ function isRightOn(a, b, c) {
87
+ return triangleArea(a, b, c) <= 0;
88
+ }
89
+ var tmpPoint1 = [];
90
+ var tmpPoint2 = [];
91
+ function collinear(a, b, c, thresholdAngle) {
92
+ if (!thresholdAngle) {
93
+ return triangleArea(a, b, c) === 0;
94
+ } else {
95
+ var ab = tmpPoint1, bc = tmpPoint2;
96
+ ab[0] = b[0] - a[0];
97
+ ab[1] = b[1] - a[1];
98
+ bc[0] = c[0] - b[0];
99
+ bc[1] = c[1] - b[1];
100
+ var dot = ab[0] * bc[0] + ab[1] * bc[1], magA = Math.sqrt(ab[0] * ab[0] + ab[1] * ab[1]), magB = Math.sqrt(bc[0] * bc[0] + bc[1] * bc[1]), angle = Math.acos(dot / (magA * magB));
101
+ return angle < thresholdAngle;
102
+ }
103
+ }
104
+ function sqdist(a, b) {
105
+ var dx = b[0] - a[0];
106
+ var dy = b[1] - a[1];
107
+ return dx * dx + dy * dy;
108
+ }
109
+ function polygonAt(polygon, i) {
110
+ var s = polygon.length;
111
+ return polygon[i < 0 ? i % s + s : i % s];
112
+ }
113
+ function polygonClear(polygon) {
114
+ polygon.length = 0;
115
+ }
116
+ function polygonAppend(polygon, poly, from, to) {
117
+ for (var i = from; i < to; i++) {
118
+ polygon.push(poly[i]);
119
+ }
120
+ }
121
+ function polygonMakeCCW(polygon) {
122
+ var br = 0, v = polygon;
123
+ for (var i = 1; i < polygon.length; ++i) {
124
+ if (v[i][1] < v[br][1] || v[i][1] === v[br][1] && v[i][0] > v[br][0]) {
125
+ br = i;
126
+ }
127
+ }
128
+ if (!isLeft(polygonAt(polygon, br - 1), polygonAt(polygon, br), polygonAt(polygon, br + 1))) {
129
+ polygonReverse(polygon);
130
+ return true;
131
+ } else {
132
+ return false;
133
+ }
134
+ }
135
+ function polygonReverse(polygon) {
136
+ var tmp = [];
137
+ var N = polygon.length;
138
+ for (var i = 0; i !== N; i++) {
139
+ tmp.push(polygon.pop());
140
+ }
141
+ for (var i = 0; i !== N; i++) {
142
+ polygon[i] = tmp[i];
143
+ }
144
+ }
145
+ function polygonIsReflex(polygon, i) {
146
+ return isRight(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1));
147
+ }
148
+ var tmpLine1 = [];
149
+ var tmpLine2 = [];
150
+ function polygonCanSee(polygon, a, b) {
151
+ var p, dist, l1 = tmpLine1, l2 = tmpLine2;
152
+ if (isLeftOn(polygonAt(polygon, a + 1), polygonAt(polygon, a), polygonAt(polygon, b)) && isRightOn(polygonAt(polygon, a - 1), polygonAt(polygon, a), polygonAt(polygon, b))) {
153
+ return false;
154
+ }
155
+ dist = sqdist(polygonAt(polygon, a), polygonAt(polygon, b));
156
+ for (var i = 0; i !== polygon.length; ++i) {
157
+ if ((i + 1) % polygon.length === a || i === a) {
158
+ continue;
159
+ }
160
+ if (isLeftOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i + 1)) && isRightOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i))) {
161
+ l1[0] = polygonAt(polygon, a);
162
+ l1[1] = polygonAt(polygon, b);
163
+ l2[0] = polygonAt(polygon, i);
164
+ l2[1] = polygonAt(polygon, i + 1);
165
+ p = lineInt(l1, l2);
166
+ if (sqdist(polygonAt(polygon, a), p) < dist) {
167
+ return false;
168
+ }
169
+ }
170
+ }
171
+ return true;
172
+ }
173
+ function polygonCanSee2(polygon, a, b) {
174
+ for (var i = 0; i !== polygon.length; ++i) {
175
+ if (i === a || i === b || (i + 1) % polygon.length === a || (i + 1) % polygon.length === b) {
176
+ continue;
177
+ }
178
+ if (lineSegmentsIntersect(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i), polygonAt(polygon, i + 1))) {
179
+ return false;
180
+ }
181
+ }
182
+ return true;
183
+ }
184
+ function polygonCopy(polygon, i, j, targetPoly) {
185
+ var p = targetPoly || [];
186
+ polygonClear(p);
187
+ if (i < j) {
188
+ for (var k = i; k <= j; k++) {
189
+ p.push(polygon[k]);
190
+ }
191
+ } else {
192
+ for (var k = 0; k <= j; k++) {
193
+ p.push(polygon[k]);
194
+ }
195
+ for (var k = i; k < polygon.length; k++) {
196
+ p.push(polygon[k]);
197
+ }
198
+ }
199
+ return p;
200
+ }
201
+ function polygonGetCutEdges(polygon) {
202
+ var min = [], tmp1 = [], tmp2 = [], tmpPoly = [];
203
+ var nDiags = Number.MAX_VALUE;
204
+ for (var i = 0; i < polygon.length; ++i) {
205
+ if (polygonIsReflex(polygon, i)) {
206
+ for (var j = 0; j < polygon.length; ++j) {
207
+ if (polygonCanSee(polygon, i, j)) {
208
+ tmp1 = polygonGetCutEdges(polygonCopy(polygon, i, j, tmpPoly));
209
+ tmp2 = polygonGetCutEdges(polygonCopy(polygon, j, i, tmpPoly));
210
+ for (var k = 0; k < tmp2.length; k++) {
211
+ tmp1.push(tmp2[k]);
212
+ }
213
+ if (tmp1.length < nDiags) {
214
+ min = tmp1;
215
+ nDiags = tmp1.length;
216
+ min.push([polygonAt(polygon, i), polygonAt(polygon, j)]);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ return min;
223
+ }
224
+ function polygonDecomp(polygon) {
225
+ var edges = polygonGetCutEdges(polygon);
226
+ if (edges.length > 0) {
227
+ return polygonSlice(polygon, edges);
228
+ } else {
229
+ return [polygon];
230
+ }
231
+ }
232
+ function polygonSlice(polygon, cutEdges) {
233
+ if (cutEdges.length === 0) {
234
+ return [polygon];
235
+ }
236
+ if (cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length === 2 && cutEdges[0][0] instanceof Array) {
237
+ var polys = [polygon];
238
+ for (var i = 0; i < cutEdges.length; i++) {
239
+ var cutEdge = cutEdges[i];
240
+ for (var j = 0; j < polys.length; j++) {
241
+ var poly = polys[j];
242
+ var result = polygonSlice(poly, cutEdge);
243
+ if (result) {
244
+ polys.splice(j, 1);
245
+ polys.push(result[0], result[1]);
246
+ break;
247
+ }
248
+ }
249
+ }
250
+ return polys;
251
+ } else {
252
+ var cutEdge = cutEdges;
253
+ var i = polygon.indexOf(cutEdge[0]);
254
+ var j = polygon.indexOf(cutEdge[1]);
255
+ if (i !== -1 && j !== -1) {
256
+ return [
257
+ polygonCopy(polygon, i, j),
258
+ polygonCopy(polygon, j, i)
259
+ ];
260
+ } else {
261
+ return false;
262
+ }
263
+ }
264
+ }
265
+ function polygonIsSimple(polygon) {
266
+ var path = polygon, i;
267
+ for (i = 0; i < path.length - 1; i++) {
268
+ for (var j = 0; j < i - 1; j++) {
269
+ if (lineSegmentsIntersect(path[i], path[i + 1], path[j], path[j + 1])) {
270
+ return false;
271
+ }
272
+ }
273
+ }
274
+ for (i = 1; i < path.length - 2; i++) {
275
+ if (lineSegmentsIntersect(path[0], path[path.length - 1], path[i], path[i + 1])) {
276
+ return false;
277
+ }
278
+ }
279
+ return true;
280
+ }
281
+ function getIntersectionPoint(p1, p2, q1, q2, delta) {
282
+ delta = delta || 0;
283
+ var a1 = p2[1] - p1[1];
284
+ var b1 = p1[0] - p2[0];
285
+ var c1 = a1 * p1[0] + b1 * p1[1];
286
+ var a2 = q2[1] - q1[1];
287
+ var b2 = q1[0] - q2[0];
288
+ var c2 = a2 * q1[0] + b2 * q1[1];
289
+ var det = a1 * b2 - a2 * b1;
290
+ if (!scalar_eq(det, 0, delta)) {
291
+ return [(b2 * c1 - b1 * c2) / det, (a1 * c2 - a2 * c1) / det];
292
+ } else {
293
+ return [0, 0];
294
+ }
295
+ }
296
+ function polygonQuickDecomp(polygon, result, reflexVertices, steinerPoints, delta, maxlevel, level) {
297
+ maxlevel = maxlevel || 100;
298
+ level = level || 0;
299
+ delta = delta || 25;
300
+ result = typeof result !== "undefined" ? result : [];
301
+ reflexVertices = reflexVertices || [];
302
+ steinerPoints = steinerPoints || [];
303
+ var upperInt = [0, 0], lowerInt = [0, 0], p = [0, 0];
304
+ var upperDist = 0, lowerDist = 0, d = 0, closestDist = 0;
305
+ var upperIndex = 0, lowerIndex = 0, closestIndex = 0;
306
+ var lowerPoly = [], upperPoly = [];
307
+ var poly = polygon, v = polygon;
308
+ if (v.length < 3) {
309
+ return result;
310
+ }
311
+ level++;
312
+ if (level > maxlevel) {
313
+ console.warn("quickDecomp: max level (" + maxlevel + ") reached.");
314
+ return result;
315
+ }
316
+ for (var i = 0; i < polygon.length; ++i) {
317
+ if (polygonIsReflex(poly, i)) {
318
+ reflexVertices.push(poly[i]);
319
+ upperDist = lowerDist = Number.MAX_VALUE;
320
+ for (var j = 0; j < polygon.length; ++j) {
321
+ if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j - 1))) {
322
+ p = getIntersectionPoint(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j - 1));
323
+ if (isRight(polygonAt(poly, i + 1), polygonAt(poly, i), p)) {
324
+ d = sqdist(poly[i], p);
325
+ if (d < lowerDist) {
326
+ lowerDist = d;
327
+ lowerInt = p;
328
+ lowerIndex = j;
329
+ }
330
+ }
331
+ }
332
+ if (isLeft(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j + 1)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
333
+ p = getIntersectionPoint(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j + 1));
334
+ if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), p)) {
335
+ d = sqdist(poly[i], p);
336
+ if (d < upperDist) {
337
+ upperDist = d;
338
+ upperInt = p;
339
+ upperIndex = j;
340
+ }
341
+ }
342
+ }
343
+ }
344
+ if (lowerIndex === (upperIndex + 1) % polygon.length) {
345
+ p[0] = (lowerInt[0] + upperInt[0]) / 2;
346
+ p[1] = (lowerInt[1] + upperInt[1]) / 2;
347
+ steinerPoints.push(p);
348
+ if (i < upperIndex) {
349
+ polygonAppend(lowerPoly, poly, i, upperIndex + 1);
350
+ lowerPoly.push(p);
351
+ upperPoly.push(p);
352
+ if (lowerIndex !== 0) {
353
+ polygonAppend(upperPoly, poly, lowerIndex, poly.length);
354
+ }
355
+ polygonAppend(upperPoly, poly, 0, i + 1);
356
+ } else {
357
+ if (i !== 0) {
358
+ polygonAppend(lowerPoly, poly, i, poly.length);
359
+ }
360
+ polygonAppend(lowerPoly, poly, 0, upperIndex + 1);
361
+ lowerPoly.push(p);
362
+ upperPoly.push(p);
363
+ polygonAppend(upperPoly, poly, lowerIndex, i + 1);
364
+ }
365
+ } else {
366
+ if (lowerIndex > upperIndex) {
367
+ upperIndex += polygon.length;
368
+ }
369
+ closestDist = Number.MAX_VALUE;
370
+ if (upperIndex < lowerIndex) {
371
+ return result;
372
+ }
373
+ for (var j = lowerIndex; j <= upperIndex; ++j) {
374
+ if (isLeftOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
375
+ d = sqdist(polygonAt(poly, i), polygonAt(poly, j));
376
+ if (d < closestDist && polygonCanSee2(poly, i, j)) {
377
+ closestDist = d;
378
+ closestIndex = j % polygon.length;
379
+ }
380
+ }
381
+ }
382
+ if (i < closestIndex) {
383
+ polygonAppend(lowerPoly, poly, i, closestIndex + 1);
384
+ if (closestIndex !== 0) {
385
+ polygonAppend(upperPoly, poly, closestIndex, v.length);
386
+ }
387
+ polygonAppend(upperPoly, poly, 0, i + 1);
388
+ } else {
389
+ if (i !== 0) {
390
+ polygonAppend(lowerPoly, poly, i, v.length);
391
+ }
392
+ polygonAppend(lowerPoly, poly, 0, closestIndex + 1);
393
+ polygonAppend(upperPoly, poly, closestIndex, i + 1);
394
+ }
395
+ }
396
+ if (lowerPoly.length < upperPoly.length) {
397
+ polygonQuickDecomp(lowerPoly, result, reflexVertices, steinerPoints, delta, maxlevel, level);
398
+ polygonQuickDecomp(upperPoly, result, reflexVertices, steinerPoints, delta, maxlevel, level);
399
+ } else {
400
+ polygonQuickDecomp(upperPoly, result, reflexVertices, steinerPoints, delta, maxlevel, level);
401
+ polygonQuickDecomp(lowerPoly, result, reflexVertices, steinerPoints, delta, maxlevel, level);
402
+ }
403
+ return result;
404
+ }
405
+ }
406
+ result.push(polygon);
407
+ return result;
408
+ }
409
+ function polygonRemoveCollinearPoints(polygon, precision) {
410
+ var num = 0;
411
+ for (var i = polygon.length - 1; polygon.length > 3 && i >= 0; --i) {
412
+ if (collinear(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1), precision)) {
413
+ polygon.splice(i % polygon.length, 1);
414
+ num++;
415
+ }
416
+ }
417
+ return num;
418
+ }
419
+ function polygonRemoveDuplicatePoints(polygon, precision) {
420
+ for (var i = polygon.length - 1; i >= 1; --i) {
421
+ var pi = polygon[i];
422
+ for (var j = i - 1; j >= 0; --j) {
423
+ if (points_eq(pi, polygon[j], precision)) {
424
+ polygon.splice(i, 1);
425
+ continue;
426
+ }
427
+ }
428
+ }
429
+ }
430
+ function scalar_eq(a, b, precision) {
431
+ precision = precision || 0;
432
+ return Math.abs(a - b) <= precision;
433
+ }
434
+ function points_eq(a, b, precision) {
435
+ return scalar_eq(a[0], b[0], precision) && scalar_eq(a[1], b[1], precision);
436
+ }
437
+ }
438
+ });
439
+
440
+ // src/index.ts
441
+ var decomp = __toESM(require_src(), 1);
442
+ import * as Matter from "matter-js";
443
+ import {
444
+ Ellipse,
445
+ version as melonjsVersion,
446
+ Polygon,
447
+ Rect,
448
+ state,
449
+ utils,
450
+ Vector2d
451
+ } from "melonjs";
452
+ Matter.Common.setDecomp(decomp);
453
+ var REQUIRED_MELONJS_VERSION = "19.5.0";
454
+ var MatterAdapter = class {
455
+ physicLabel = "matter";
456
+ name = "@melonjs/matter-adapter";
457
+ version = "1.0.0";
458
+ url = "https://www.npmjs.com/package/@melonjs/matter-adapter";
459
+ capabilities = {
460
+ constraints: true,
461
+ continuousCollisionDetection: true,
462
+ sleepingBodies: true,
463
+ raycasts: true,
464
+ velocityLimit: true,
465
+ isGrounded: true
466
+ };
467
+ gravity;
468
+ /**
469
+ * Raw matter-js namespace — escape hatch for matter-specific features
470
+ * the portable `PhysicsAdapter` interface doesn't cover (constraints,
471
+ * compound bodies, events on the Matter engine, queries, etc.).
472
+ *
473
+ * Saves you from adding a transitive `import * as Matter from "matter-js"`
474
+ * just to reach the factories you need. The Matter modules are accessed
475
+ * by the same names matter's own docs use, so examples from the
476
+ * matter-js docs copy-paste without renaming:
477
+ *
478
+ * ```ts
479
+ * const spring = adapter.matter.Constraint.create({
480
+ * bodyA: a.body, bodyB: b.body, stiffness: 0.04, length: 80,
481
+ * });
482
+ * adapter.matter.Composite.add(adapter.engine.world, spring);
483
+ * ```
484
+ *
485
+ * Game code that touches `adapter.matter.*` is matter-only — it will
486
+ * not work under any other physics adapter. Use the
487
+ * `PhysicsAdapter` methods for anything that should stay portable.
488
+ * @see {@link https://brm.io/matter-js/docs/ Official matter-js documentation}
489
+ * for the full module reference (`Matter.Constraint`, `Matter.Composite`,
490
+ * `Matter.Bodies`, `Matter.Events`, `Matter.Query`, `Matter.Vector`, …).
491
+ */
492
+ matter = Matter;
493
+ /** the underlying Matter engine; exposed for advanced use cases. */
494
+ engine;
495
+ /** back-reference to the owning melonJS world (set in {@link init}). */
496
+ world;
497
+ /** renderable → its matter-js body */
498
+ bodyMap = /* @__PURE__ */ new Map();
499
+ /** matter-js body → its renderable (for collision / sync) */
500
+ renderableMap = /* @__PURE__ */ new Map();
501
+ /** per-body velocity cap (Matter has no native equivalent) */
502
+ velocityLimits = /* @__PURE__ */ new Map();
503
+ /** per-body bodyDef hash kept for shape introspection / removeBody */
504
+ defMap = /* @__PURE__ */ new Map();
505
+ /**
506
+ * Per-body gravity multiplier. matter-js 0.20 doesn't honor a
507
+ * body-level gravityScale, so we emulate it by applying a counter-
508
+ * force each step (see {@link init} `beforeUpdate`). Only stored for
509
+ * bodies with scale ≠ 1; the default-1 case is the hot path and we
510
+ * skip the map lookup entirely for it.
511
+ */
512
+ bodyGravityScale = /* @__PURE__ */ new Map();
513
+ /**
514
+ * Offset between `renderable.pos` (top-left in melonJS convention)
515
+ * and `Matter.Body.position` (centroid in Matter's convention). Stored
516
+ * at addBody time so syncFromPhysics can place the sprite correctly.
517
+ */
518
+ posOffsets = /* @__PURE__ */ new Map();
519
+ matterOptions;
520
+ subSteps;
521
+ constructor(options = {}) {
522
+ if (utils.checkVersion(REQUIRED_MELONJS_VERSION, melonjsVersion) > 0) {
523
+ throw new Error(
524
+ `@melonjs/matter-adapter requires melonJS >= ${REQUIRED_MELONJS_VERSION}, but the loaded melonJS is ${melonjsVersion}.`
525
+ );
526
+ }
527
+ this.matterOptions = options.matterEngineOptions;
528
+ this.subSteps = Math.max(1, Math.floor(options.subSteps ?? 1));
529
+ const g = options.gravity ?? { x: 0, y: 1 };
530
+ this.gravity = new Vector2d(g.x, g.y);
531
+ }
532
+ /**
533
+ * Stored references to the listeners registered in {@link init}, so
534
+ * {@link destroy} can `Matter.Events.off` them. Without these stored
535
+ * refs, a destroy/re-init cycle would leak the old listeners and
536
+ * dispatch every event twice on the second cycle.
537
+ */
538
+ _matterListeners = [];
539
+ init(world) {
540
+ this.world = world;
541
+ this.engine = Matter.Engine.create(this.matterOptions);
542
+ this.engine.gravity.x = this.gravity.x;
543
+ this.engine.gravity.y = this.gravity.y;
544
+ const on = (name, fn) => {
545
+ Matter.Events.on(
546
+ this.engine,
547
+ name,
548
+ fn
549
+ );
550
+ this._matterListeners.push({ name, fn });
551
+ };
552
+ on("beforeUpdate", () => {
553
+ if (this.bodyGravityScale.size === 0) return;
554
+ const gx = this.engine.gravity.x;
555
+ const gy = this.engine.gravity.y;
556
+ const gs = this.engine.gravity.scale ?? 1e-3;
557
+ for (const [body, scale] of this.bodyGravityScale) {
558
+ if (body.isStatic || body.isSleeping) continue;
559
+ const k = (scale - 1) * body.mass * gs;
560
+ body.force.x += k * gx;
561
+ body.force.y += k * gy;
562
+ }
563
+ });
564
+ on("afterUpdate", () => {
565
+ this._clampVelocities();
566
+ this.syncFromPhysics();
567
+ });
568
+ on("collisionStart", (e) => {
569
+ this._dispatchCollisions(e.pairs, "start");
570
+ });
571
+ on("collisionActive", (e) => {
572
+ this._dispatchCollisions(e.pairs, "active");
573
+ });
574
+ on("collisionEnd", (e) => {
575
+ this._dispatchCollisions(e.pairs, "end");
576
+ });
577
+ }
578
+ destroy() {
579
+ for (const { name, fn } of this._matterListeners) {
580
+ Matter.Events.off(
581
+ this.engine,
582
+ name,
583
+ fn
584
+ );
585
+ }
586
+ this._matterListeners = [];
587
+ Matter.Composite.clear(this.engine.world, false, true);
588
+ Matter.Engine.clear(this.engine);
589
+ this.bodyMap.clear();
590
+ this.renderableMap.clear();
591
+ this.velocityLimits.clear();
592
+ this.defMap.clear();
593
+ this.bodyGravityScale.clear();
594
+ this.posOffsets.clear();
595
+ }
596
+ step(dt) {
597
+ if (state.isPaused()) {
598
+ return;
599
+ }
600
+ this.engine.gravity.x = this.gravity.x;
601
+ this.engine.gravity.y = this.gravity.y;
602
+ if (this.subSteps === 1) {
603
+ Matter.Engine.update(this.engine, dt);
604
+ } else {
605
+ const sub = dt / this.subSteps;
606
+ for (let i = 0; i < this.subSteps; i++) {
607
+ Matter.Engine.update(this.engine, sub);
608
+ }
609
+ }
610
+ }
611
+ syncFromPhysics() {
612
+ for (const [body, renderable] of this.renderableMap) {
613
+ if (!renderable.pos) {
614
+ continue;
615
+ }
616
+ const off = this.posOffsets.get(renderable);
617
+ if (off) {
618
+ renderable.pos.x = body.position.x + off.x;
619
+ renderable.pos.y = body.position.y + off.y;
620
+ } else {
621
+ renderable.pos.x = body.position.x;
622
+ renderable.pos.y = body.position.y;
623
+ }
624
+ const t = renderable.currentTransform;
625
+ const off2 = this.posOffsets.get(renderable);
626
+ const cx = off2 ? -off2.x : 0;
627
+ const cy = off2 ? -off2.y : 0;
628
+ t.identity();
629
+ if (cx !== 0 || cy !== 0) {
630
+ t.translate(cx, cy);
631
+ t.rotate(body.angle);
632
+ t.translate(-cx, -cy);
633
+ } else {
634
+ t.rotate(body.angle);
635
+ }
636
+ }
637
+ }
638
+ addBody(renderable, def) {
639
+ const baseX = renderable.pos.x;
640
+ const baseY = renderable.pos.y;
641
+ const parts = def.shapes.map((s) => this._shapeToMatter(s, baseX, baseY));
642
+ let body;
643
+ if (parts.length === 1) {
644
+ body = parts[0];
645
+ } else {
646
+ body = Matter.Body.create({ parts });
647
+ }
648
+ if (def.type === "static") {
649
+ Matter.Body.setStatic(body, true);
650
+ }
651
+ if (typeof def.density === "number") {
652
+ Matter.Body.setDensity(body, def.density);
653
+ }
654
+ if (def.frictionAir !== void 0) {
655
+ body.frictionAir = typeof def.frictionAir === "number" ? def.frictionAir : (def.frictionAir.x + def.frictionAir.y) / 2;
656
+ }
657
+ if (typeof def.restitution === "number") {
658
+ body.restitution = def.restitution;
659
+ }
660
+ if (typeof def.friction === "number") {
661
+ body.friction = def.friction;
662
+ }
663
+ if (typeof def.gravityScale === "number" && def.gravityScale !== 1) {
664
+ this.bodyGravityScale.set(body, def.gravityScale);
665
+ }
666
+ if (def.maxVelocity) {
667
+ this.velocityLimits.set(renderable, {
668
+ x: def.maxVelocity.x,
669
+ y: def.maxVelocity.y
670
+ });
671
+ }
672
+ Object.defineProperties(body, {
673
+ collisionType: {
674
+ get() {
675
+ return this.collisionFilter.category;
676
+ },
677
+ set(v) {
678
+ this.collisionFilter.category = v;
679
+ },
680
+ configurable: true,
681
+ enumerable: true
682
+ },
683
+ collisionMask: {
684
+ get() {
685
+ return this.collisionFilter.mask;
686
+ },
687
+ set(v) {
688
+ this.collisionFilter.mask = v;
689
+ },
690
+ configurable: true,
691
+ enumerable: true
692
+ }
693
+ });
694
+ if (typeof def.collisionType === "number") {
695
+ body.collisionFilter.category = def.collisionType;
696
+ }
697
+ if (typeof def.collisionMask === "number") {
698
+ body.collisionFilter.mask = def.collisionMask;
699
+ }
700
+ if (def.isSensor) {
701
+ body.isSensor = true;
702
+ }
703
+ if (def.fixedRotation !== false) {
704
+ Matter.Body.setInertia(body, Number.POSITIVE_INFINITY);
705
+ }
706
+ Matter.Composite.add(this.engine.world, body);
707
+ this.bodyMap.set(renderable, body);
708
+ this.renderableMap.set(body, renderable);
709
+ this.defMap.set(renderable, def);
710
+ this.posOffsets.set(renderable, {
711
+ x: baseX - body.position.x,
712
+ y: baseY - body.position.y
713
+ });
714
+ const helpers = {
715
+ setVelocity(x, y) {
716
+ Matter.Body.setVelocity(body, { x, y });
717
+ },
718
+ getVelocity(out) {
719
+ return (out ?? new Vector2d()).set(body.velocity.x, body.velocity.y);
720
+ },
721
+ applyForce(x, y, pointX, pointY) {
722
+ const point = typeof pointX === "number" && typeof pointY === "number" ? { x: pointX, y: pointY } : body.position;
723
+ Matter.Body.applyForce(body, point, { x, y });
724
+ },
725
+ applyImpulse(x, y) {
726
+ const invMass = body.mass > 0 ? 1 / body.mass : 0;
727
+ Matter.Body.setVelocity(body, {
728
+ x: body.velocity.x + x * invMass,
729
+ y: body.velocity.y + y * invMass
730
+ });
731
+ },
732
+ setSensor(isSensor = true) {
733
+ body.isSensor = isSensor;
734
+ },
735
+ setStatic(isStatic = true) {
736
+ Matter.Body.setStatic(body, isStatic);
737
+ },
738
+ setCollisionMask(mask) {
739
+ body.collisionFilter.mask = mask;
740
+ },
741
+ setCollisionType(type) {
742
+ body.collisionFilter.category = type;
743
+ },
744
+ setMass: (m) => {
745
+ Matter.Body.setMass(body, m);
746
+ },
747
+ setBounce(r) {
748
+ body.restitution = r;
749
+ },
750
+ setGravityScale: (scale) => {
751
+ if (scale === 1) {
752
+ this.bodyGravityScale.delete(body);
753
+ } else {
754
+ this.bodyGravityScale.set(body, scale);
755
+ }
756
+ },
757
+ setAngularVelocity(omega) {
758
+ Matter.Body.setAngularVelocity(body, omega);
759
+ },
760
+ getAngularVelocity() {
761
+ return body.angularVelocity;
762
+ },
763
+ setAngle(rad) {
764
+ Matter.Body.setAngle(body, rad);
765
+ },
766
+ getAngle() {
767
+ return body.angle;
768
+ },
769
+ applyTorque(t) {
770
+ body.torque += t;
771
+ }
772
+ };
773
+ Object.assign(body, helpers);
774
+ const adapterBody = body;
775
+ renderable.body = adapterBody;
776
+ return adapterBody;
777
+ }
778
+ removeBody(renderable) {
779
+ const body = this.bodyMap.get(renderable);
780
+ if (body) {
781
+ Matter.Composite.remove(this.engine.world, body);
782
+ this.bodyMap.delete(renderable);
783
+ this.renderableMap.delete(body);
784
+ this.velocityLimits.delete(renderable);
785
+ this.defMap.delete(renderable);
786
+ this.posOffsets.delete(renderable);
787
+ this.bodyGravityScale.delete(body);
788
+ }
789
+ }
790
+ updateShape(renderable, shapes) {
791
+ const oldDef = this.defMap.get(renderable);
792
+ if (!oldDef) {
793
+ return;
794
+ }
795
+ const oldBody = this.bodyMap.get(renderable);
796
+ const savedVel = oldBody ? { x: oldBody.velocity.x, y: oldBody.velocity.y } : void 0;
797
+ const savedAngVel = oldBody?.angularVelocity ?? 0;
798
+ this.removeBody(renderable);
799
+ const newBody = this.addBody(renderable, { ...oldDef, shapes });
800
+ if (savedVel) {
801
+ Matter.Body.setVelocity(newBody, savedVel);
802
+ Matter.Body.setAngularVelocity(newBody, savedAngVel);
803
+ }
804
+ }
805
+ getVelocity(renderable, out) {
806
+ const body = this.bodyMap.get(renderable);
807
+ const target = out ?? new Vector2d();
808
+ if (body) {
809
+ return target.set(body.velocity.x, body.velocity.y);
810
+ }
811
+ return target.set(0, 0);
812
+ }
813
+ setVelocity(renderable, v) {
814
+ const body = this.bodyMap.get(renderable);
815
+ if (body) {
816
+ Matter.Body.setVelocity(body, { x: v.x, y: v.y });
817
+ }
818
+ }
819
+ applyForce(renderable, force, point) {
820
+ const body = this.bodyMap.get(renderable);
821
+ if (!body) return;
822
+ Matter.Body.applyForce(body, point ?? body.position, {
823
+ x: force.x,
824
+ y: force.y
825
+ });
826
+ }
827
+ applyImpulse(renderable, impulse) {
828
+ const body = this.bodyMap.get(renderable);
829
+ if (!body) return;
830
+ const invMass = body.mass > 0 ? 1 / body.mass : 0;
831
+ Matter.Body.setVelocity(body, {
832
+ x: body.velocity.x + impulse.x * invMass,
833
+ y: body.velocity.y + impulse.y * invMass
834
+ });
835
+ }
836
+ setPosition(renderable, p) {
837
+ const body = this.bodyMap.get(renderable);
838
+ if (body) {
839
+ const off = this.posOffsets.get(renderable);
840
+ Matter.Body.setPosition(body, {
841
+ x: p.x - (off?.x ?? 0),
842
+ y: p.y - (off?.y ?? 0)
843
+ });
844
+ this.invalidateContactsFor(body);
845
+ }
846
+ renderable.pos.x = p.x;
847
+ renderable.pos.y = p.y;
848
+ }
849
+ invalidateContactsFor(body) {
850
+ const impulse = body.positionImpulse;
851
+ impulse.x = 0;
852
+ impulse.y = 0;
853
+ }
854
+ setAngle(renderable, angle) {
855
+ const body = this.bodyMap.get(renderable);
856
+ if (body) {
857
+ Matter.Body.setAngle(body, angle);
858
+ }
859
+ }
860
+ getAngle(renderable) {
861
+ const body = this.bodyMap.get(renderable);
862
+ return body ? body.angle : 0;
863
+ }
864
+ setAngularVelocity(renderable, omega) {
865
+ const body = this.bodyMap.get(renderable);
866
+ if (body) {
867
+ Matter.Body.setAngularVelocity(body, omega);
868
+ }
869
+ }
870
+ getAngularVelocity(renderable) {
871
+ const body = this.bodyMap.get(renderable);
872
+ return body ? body.angularVelocity : 0;
873
+ }
874
+ applyTorque(renderable, torque) {
875
+ const body = this.bodyMap.get(renderable);
876
+ if (body) {
877
+ body.torque += torque;
878
+ }
879
+ }
880
+ setStatic(renderable, isStatic) {
881
+ const body = this.bodyMap.get(renderable);
882
+ if (body) {
883
+ Matter.Body.setStatic(body, isStatic);
884
+ }
885
+ }
886
+ setGravityScale(renderable, scale) {
887
+ const body = this.bodyMap.get(renderable);
888
+ if (!body) return;
889
+ if (scale === 1) {
890
+ this.bodyGravityScale.delete(body);
891
+ } else {
892
+ this.bodyGravityScale.set(body, scale);
893
+ }
894
+ }
895
+ setSensor(renderable, isSensor) {
896
+ const body = this.bodyMap.get(renderable);
897
+ if (body) {
898
+ body.isSensor = isSensor;
899
+ }
900
+ }
901
+ setFrictionAir(renderable, friction) {
902
+ const body = this.bodyMap.get(renderable);
903
+ if (body) {
904
+ body.frictionAir = typeof friction === "number" ? friction : (friction.x + friction.y) / 2;
905
+ }
906
+ }
907
+ setMaxVelocity(renderable, limit) {
908
+ this.velocityLimits.set(renderable, { x: limit.x, y: limit.y });
909
+ }
910
+ getMaxVelocity(renderable) {
911
+ return this.velocityLimits.get(renderable) ?? { x: 0, y: 0 };
912
+ }
913
+ setCollisionType(renderable, type) {
914
+ const body = this.bodyMap.get(renderable);
915
+ if (body) {
916
+ body.collisionFilter.category = type;
917
+ }
918
+ }
919
+ setCollisionMask(renderable, mask) {
920
+ const body = this.bodyMap.get(renderable);
921
+ if (body) {
922
+ body.collisionFilter.mask = mask;
923
+ }
924
+ }
925
+ /**
926
+ * Adapter-side debug surface: the body's AABB in renderable-local
927
+ * coordinates. Matter tracks `body.bounds` in WORLD space; we
928
+ * subtract `renderable.pos` so the result matches melonJS's local-
929
+ * space convention (the debug plugin translates to the renderable
930
+ * origin before drawing, and would otherwise see the bounds drawn
931
+ * offset by the renderable's world position).
932
+ * @param renderable - the renderable whose body bounds to read
933
+ * @param out - destination `Bounds` (filled in place, also returned)
934
+ */
935
+ getBodyAABB(renderable, out) {
936
+ const body = this.bodyMap.get(renderable);
937
+ if (!body) return void 0;
938
+ const b = body.bounds;
939
+ const rp = renderable.pos;
940
+ out.setMinMax(
941
+ b.min.x - rp.x,
942
+ b.min.y - rp.y,
943
+ b.max.x - rp.x,
944
+ b.max.y - rp.y
945
+ );
946
+ return out;
947
+ }
948
+ /**
949
+ * Adapter-side debug surface: the body's collision shapes in
950
+ * renderable-local coordinates. We return the original `def.shapes`
951
+ * array — those are the input shape definitions in local space,
952
+ * unchanged by matter's body transformation (rotation is baked into
953
+ * matter's vertices, not into our local-space defs). Read-only.
954
+ * @param renderable - the renderable whose body shapes to read
955
+ */
956
+ getBodyShapes(renderable) {
957
+ return this.defMap.get(renderable)?.shapes ?? [];
958
+ }
959
+ isGrounded(renderable) {
960
+ const body = this.bodyMap.get(renderable);
961
+ if (!body) return false;
962
+ for (const pair of this.engine.pairs.list) {
963
+ if (!pair.isActive) continue;
964
+ const isA = pair.bodyA === body;
965
+ const isB = pair.bodyB === body;
966
+ if (!isA && !isB) continue;
967
+ const other = isA ? pair.bodyB : pair.bodyA;
968
+ if (other.position.y > body.position.y) {
969
+ return true;
970
+ }
971
+ }
972
+ return false;
973
+ }
974
+ raycast(from, to) {
975
+ const dx = to.x - from.x;
976
+ const dy = to.y - from.y;
977
+ if (Math.abs(dx) < 1e-9 && Math.abs(dy) < 1e-9) return null;
978
+ const bodies = Matter.Composite.allBodies(this.engine.world);
979
+ const collisions = Matter.Query.ray(bodies, from, to);
980
+ if (collisions.length === 0) return null;
981
+ let bestT = Infinity;
982
+ let bestEdgeX = 0;
983
+ let bestEdgeY = 0;
984
+ let bestBody = null;
985
+ for (const collision of collisions) {
986
+ const candidate = collision.bodyA;
987
+ const parts = candidate.parts;
988
+ const startIdx = parts.length > 1 ? 1 : 0;
989
+ for (let p = startIdx; p < parts.length; p++) {
990
+ const vertices = parts[p].vertices;
991
+ const vlen = vertices.length;
992
+ for (let i = 0; i < vlen; i++) {
993
+ const a = vertices[i];
994
+ const b = vertices[i + 1 === vlen ? 0 : i + 1];
995
+ const ex = b.x - a.x;
996
+ const ey = b.y - a.y;
997
+ const denom = dx * ey - dy * ex;
998
+ if (denom > -1e-9 && denom < 1e-9) continue;
999
+ const fx = a.x - from.x;
1000
+ const fy = a.y - from.y;
1001
+ const t = (fx * ey - fy * ex) / denom;
1002
+ if (t < 0 || t > 1 || t >= bestT) continue;
1003
+ const s = (fx * dy - fy * dx) / denom;
1004
+ if (s < 0 || s > 1) continue;
1005
+ bestT = t;
1006
+ bestEdgeX = ex;
1007
+ bestEdgeY = ey;
1008
+ bestBody = candidate;
1009
+ }
1010
+ }
1011
+ }
1012
+ if (bestBody === null) return null;
1013
+ const renderable = this.renderableMap.get(bestBody);
1014
+ if (!renderable) return null;
1015
+ let nx = bestEdgeY;
1016
+ let ny = -bestEdgeX;
1017
+ const nLen = Math.sqrt(nx * nx + ny * ny);
1018
+ if (nLen > 0) {
1019
+ nx /= nLen;
1020
+ ny /= nLen;
1021
+ }
1022
+ if (nx * dx + ny * dy > 0) {
1023
+ nx = -nx;
1024
+ ny = -ny;
1025
+ }
1026
+ return {
1027
+ renderable,
1028
+ point: new Vector2d(from.x + dx * bestT, from.y + dy * bestT),
1029
+ normal: new Vector2d(nx, ny),
1030
+ fraction: bestT
1031
+ };
1032
+ }
1033
+ queryAABB(rect) {
1034
+ const bodies = Matter.Composite.allBodies(this.engine.world);
1035
+ const matched = Matter.Query.region(bodies, {
1036
+ min: { x: rect.pos.x, y: rect.pos.y },
1037
+ max: { x: rect.pos.x + rect.width, y: rect.pos.y + rect.height }
1038
+ });
1039
+ const result = [];
1040
+ for (const b of matched) {
1041
+ const r = this.renderableMap.get(b);
1042
+ if (r) result.push(r);
1043
+ }
1044
+ return result;
1045
+ }
1046
+ // ---------------------------------------------------------------------
1047
+ // Internal helpers
1048
+ // ---------------------------------------------------------------------
1049
+ _clampVelocities() {
1050
+ for (const [renderable, limit] of this.velocityLimits) {
1051
+ const body = this.bodyMap.get(renderable);
1052
+ if (!body) continue;
1053
+ const vx = Math.max(-limit.x, Math.min(limit.x, body.velocity.x));
1054
+ const vy = Math.max(-limit.y, Math.min(limit.y, body.velocity.y));
1055
+ if (vx !== body.velocity.x || vy !== body.velocity.y) {
1056
+ Matter.Body.setVelocity(body, { x: vx, y: vy });
1057
+ }
1058
+ }
1059
+ }
1060
+ _dispatchCollisions(pairs, phase) {
1061
+ const methodForSide = (receiver) => {
1062
+ if (phase === "start") return "onCollisionStart";
1063
+ if (phase === "end") return "onCollisionEnd";
1064
+ return typeof receiver.onCollisionActive === "function" ? "onCollisionActive" : "onCollision";
1065
+ };
1066
+ for (const pair of pairs) {
1067
+ const rA = this.renderableMap.get(pair.bodyA);
1068
+ const rB = this.renderableMap.get(pair.bodyB);
1069
+ if (!rA || !rB) continue;
1070
+ const ancA = rA.ancestor;
1071
+ const ancB = rB.ancestor;
1072
+ const aDetached = ancA === null;
1073
+ const bDetached = ancB === null;
1074
+ if (phase === "end") {
1075
+ if (aDetached && bDetached) continue;
1076
+ } else if (aDetached || bDetached) {
1077
+ continue;
1078
+ }
1079
+ const collision = pair.collision;
1080
+ const depth = collision.depth;
1081
+ const normal = collision.normal;
1082
+ const responseAB = {
1083
+ a: rA,
1084
+ b: rB,
1085
+ normal: { x: normal.x, y: normal.y },
1086
+ depth,
1087
+ pair
1088
+ };
1089
+ const responseBA = {
1090
+ a: rB,
1091
+ b: rA,
1092
+ normal: { x: -normal.x, y: -normal.y },
1093
+ depth,
1094
+ pair
1095
+ };
1096
+ if (!aDetached) {
1097
+ const mA = methodForSide(rA);
1098
+ const fnA = rA[mA];
1099
+ if (typeof fnA === "function") {
1100
+ fnA.call(
1101
+ rA,
1102
+ responseAB,
1103
+ rB
1104
+ );
1105
+ }
1106
+ }
1107
+ if (!bDetached) {
1108
+ const mB = methodForSide(rB);
1109
+ const fnB = rB[mB];
1110
+ if (typeof fnB === "function") {
1111
+ fnB.call(
1112
+ rB,
1113
+ responseBA,
1114
+ rA
1115
+ );
1116
+ }
1117
+ }
1118
+ }
1119
+ }
1120
+ _shapeToMatter(shape, baseX, baseY) {
1121
+ if (shape instanceof Rect) {
1122
+ const w = shape.width;
1123
+ const h = shape.height;
1124
+ return Matter.Bodies.rectangle(
1125
+ baseX + shape.pos.x + w / 2,
1126
+ baseY + shape.pos.y + h / 2,
1127
+ w,
1128
+ h
1129
+ );
1130
+ }
1131
+ if (shape instanceof Ellipse) {
1132
+ const radius = (shape.radiusV.x + shape.radiusV.y) / 2;
1133
+ return Matter.Bodies.circle(
1134
+ baseX + shape.pos.x,
1135
+ baseY + shape.pos.y,
1136
+ radius
1137
+ );
1138
+ }
1139
+ if (shape instanceof Polygon) {
1140
+ const points = shape.points.map((p) => ({
1141
+ x: baseX + shape.pos.x + p.x,
1142
+ y: baseY + shape.pos.y + p.y
1143
+ }));
1144
+ const cx = points.reduce((s, p) => s + p.x, 0) / Math.max(1, points.length);
1145
+ const cy = points.reduce((s, p) => s + p.y, 0) / Math.max(1, points.length);
1146
+ const body = Matter.Bodies.fromVertices(cx, cy, [points]);
1147
+ if (body) {
1148
+ return body;
1149
+ }
1150
+ let minX = Number.POSITIVE_INFINITY;
1151
+ let minY = Number.POSITIVE_INFINITY;
1152
+ let maxX = Number.NEGATIVE_INFINITY;
1153
+ let maxY = Number.NEGATIVE_INFINITY;
1154
+ for (const p of points) {
1155
+ if (p.x < minX) minX = p.x;
1156
+ if (p.y < minY) minY = p.y;
1157
+ if (p.x > maxX) maxX = p.x;
1158
+ if (p.y > maxY) maxY = p.y;
1159
+ }
1160
+ const w = Math.max(1, maxX - minX);
1161
+ const h = Math.max(1, maxY - minY);
1162
+ return Matter.Bodies.rectangle(minX + w / 2, minY + h / 2, w, h);
1163
+ }
1164
+ throw new Error(
1165
+ `MatterAdapter: unsupported shape type ${shape.constructor.name}`
1166
+ );
1167
+ }
1168
+ };
1169
+ export {
1170
+ MatterAdapter,
1171
+ REQUIRED_MELONJS_VERSION
1172
+ };
1173
+ //# sourceMappingURL=index.js.map