q5play 4.2.0 → 4.2.3

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 (4) hide show
  1. package/package.json +1 -1
  2. package/q5play.d.ts +13 -16
  3. package/q5play.js +597 -126
  4. package/q5play.pyi +12 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5play",
3
- "version": "4.2.0",
3
+ "version": "4.2.3",
4
4
  "author": "quinton-ashley",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "description": "A beginner friendly, web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
package/q5play.d.ts CHANGED
@@ -84,26 +84,26 @@ declare global {
84
84
  renderStats: boolean;
85
85
 
86
86
  /**
87
- * "Made with q5play" [splash screen](https://en.wikipedia.org/wiki/Splash_screen) displayed during
88
- * initial page load by default.
87
+ * "Made with q5play" [splash screen](https://en.wikipedia.org/wiki/Splash_screen)
88
+ * displayed during initial page load by default.
89
89
  */
90
90
  splashScreen(): Promise<void>;
91
91
 
92
92
  /**
93
- * Runs automatically before each draw function call.
93
+ * Runs automatically before each q5.draw function call.
94
94
  */
95
95
  update(): void;
96
96
 
97
97
  /**
98
- * Runs automatically after each draw function call.
98
+ * Runs automatically after each q5.draw function call.
99
99
  */
100
- postDraw(): void;
100
+ draw(): void;
101
101
  }
102
102
  const q5play: Q5Play;
103
103
 
104
104
  /**
105
- * Box2D v3 ported to WASM is the physics engine that
106
- * q5play uses for its physics simulation.
105
+ * Box2D v3 ported to WASM is used by
106
+ * q5play to simulate physics.
107
107
  *
108
108
  * This variable enables direct access to the Box2D API for
109
109
  * advanced users who want to do things that aren't wrapped
@@ -1111,8 +1111,11 @@ declare global {
1111
1111
 
1112
1112
  /**
1113
1113
  * Scales the the sprite.
1114
- * @param x scaleX or uniform scale factor
1115
- * @param y scaleY
1114
+ *
1115
+ * Components can be negative to flip/mirror the sprite on an axis.
1116
+ *
1117
+ * @param x horizontal scale factor or uniform scale factor for both axes
1118
+ * @param y vertical scale factor
1116
1119
  */
1117
1120
  scaleBy(x: number, y?: number): void;
1118
1121
 
@@ -1154,6 +1157,7 @@ declare global {
1154
1157
  /**
1155
1158
  * The sprite's speed along the surface of its collider(s),
1156
1159
  * like a conveyor belt.
1160
+ * Requires friction to be greater than 1 to have an effect.
1157
1161
  * @default 0
1158
1162
  */
1159
1163
  get surfaceSpeed(): number;
@@ -3767,13 +3771,6 @@ declare global {
3767
3771
  */
3768
3772
  y: number;
3769
3773
 
3770
- /**
3771
- * The mouse's absolute position on the canvas.
3772
- * @property {Number} x
3773
- * @property {Number} y
3774
- */
3775
- canvasPos: { x: number; y: number };
3776
-
3777
3774
  /**
3778
3775
  * The mouse's left button.
3779
3776
  */
package/q5play.js CHANGED
@@ -19,14 +19,12 @@
19
19
 
20
20
  let q5play_version = '4.2';
21
21
 
22
- if (typeof globalThis.Q5 == 'undefined') {
22
+ if (typeof globalThis.Q5 == 'undefined' && typeof globalThis.p5 == 'undefined') {
23
23
  console.error('q5play requires q5.js to be loaded first. Visit https://q5js.org to learn more.');
24
- if (typeof globalThis.p5 != 'undefined') {
25
- console.error('p5.js is not compatible with q5play. https://github.com/processing/p5.js/issues/7737');
26
- }
27
24
  }
28
25
 
29
- let box2dPromise;
26
+ let box2dPromise,
27
+ using_p5 = false;
30
28
 
31
29
  // called when a new instance of Q5 is created
32
30
  async function q5playPreSetup(q) {
@@ -150,6 +148,7 @@ async function q5playPreSetup(q) {
150
148
  b2CreateChain,
151
149
  b2Chain_GetSegmentCount,
152
150
  b2Chain_GetSegments,
151
+ b2DestroyChain,
153
152
 
154
153
  /* Body */
155
154
  b2BodyType,
@@ -249,7 +248,7 @@ async function q5playPreSetup(q) {
249
248
  this.context = 'web';
250
249
 
251
250
  this.update = () => q5playUpdate.call($, q);
252
- this.postdraw = () => q5playPostDraw.call($, q);
251
+ this.draw = () => q5playPostDraw.call($, q);
253
252
 
254
253
  if (window.matchMedia) {
255
254
  this.hasMouse = window.matchMedia('(any-hover: none)').matches ? false : true;
@@ -327,7 +326,7 @@ async function q5playPreSetup(q) {
327
326
 
328
327
  async splashScreen() {
329
328
  if (document.getElementById('made-with-q5play')) return;
330
- if (!using_p5v2) $._incrementPreload();
329
+ if (!using_p5) $._incrementPreload();
331
330
  let d = document.createElement('div');
332
331
  d.id = 'made-with-q5play';
333
332
  d.style =
@@ -359,27 +358,30 @@ async function q5playPreSetup(q) {
359
358
  d.style.display = 'none';
360
359
  d.remove();
361
360
  document.getElementById('made-with-q5play')?.remove();
362
- if (!using_p5v2) $._decrementPreload();
361
+ if (!using_p5) $._decrementPreload();
363
362
  }
364
363
  };
365
364
 
366
365
  $.q5play = new $.Q5Play();
367
366
  delete $.Q5Play;
368
367
 
369
- let using_p5v1 = !$._q5 && p5.VERSION[0] == 1;
370
- let using_p5v2 = !$._q5 && p5.VERSION[0] == 2;
368
+ using_p5 = !$._q5;
369
+ if (using_p5 && p5.VERSION[0] != 2) {
370
+ throw new Error(`q5play requires q5.js or p5.js v2. Detected version: ${p5.VERSION}. Please upgrade.`);
371
+ }
371
372
 
372
- // in q5play the default angle mode is degrees
373
373
  const DEGREES = $.DEGREES,
374
374
  DEGTORAD = Math.PI / 180,
375
375
  RADTODEG = 180 / Math.PI;
376
+
377
+ // in q5play the default angle mode is degrees
376
378
  $.angleMode(DEGREES);
377
379
 
378
380
  // in q5play the default color mode is float RGB
379
- $.colorMode($.RGB, 1);
381
+ if (!using_p5) $.colorMode($.RGB, 1);
380
382
 
381
383
  // in q5play the default image mode is center
382
- $.imageMode($.CENTER);
384
+ if (!using_p5) $.imageMode($.CENTER);
383
385
 
384
386
  const ZERO_VEC = new b2Vec2(0, 0),
385
387
  ZERO_ROT = b2MakeRot(0),
@@ -492,13 +494,13 @@ async function q5playPreSetup(q) {
492
494
  geom = this.geom;
493
495
 
494
496
  if (type == 0) {
495
- let hw = geom._hw * scaleX,
496
- hh = geom._hh * scaleY,
497
+ let hw = geom._hw * Math.abs(scaleX),
498
+ hh = geom._hh * Math.abs(scaleY),
497
499
  rr;
498
500
 
499
501
  if (!geom._rr) geom = b2MakeBox(hw, hh);
500
502
  else {
501
- rr = geom._rr * Math.min(scaleX, scaleY);
503
+ rr = geom._rr * Math.min(Math.abs(scaleX), Math.abs(scaleY));
502
504
  geom = b2MakeRoundedBox(hw, hh, rr);
503
505
  }
504
506
  b2Shape_SetPolygon(id, geom);
@@ -506,11 +508,44 @@ async function q5playPreSetup(q) {
506
508
  geom._hh = hh;
507
509
  geom._rr = rr;
508
510
  this.geom = geom;
511
+ } else if (type == 1) {
512
+ // convex polygon
513
+ geom = b2Shape_GetPolygon(id);
514
+ let verts = [];
515
+ for (let i = 0; i < geom.count; i++) {
516
+ const v = geom.GetVertex(i);
517
+ verts.push({ x: v.x * scaleX, y: v.y * scaleY });
518
+ }
519
+ let hull = b2ComputeHull(verts);
520
+ let rr = geom.radius * Math.min(scaleX, scaleY);
521
+ geom = rr ? b2MakeOffsetRoundedPolygon(hull, ZERO_VEC, ZERO_ROT, rr) : b2MakePolygon(hull, 0);
522
+ b2Shape_SetPolygon(id, geom);
523
+ this.geom = geom;
509
524
  } else if (type == 3) {
510
- geom.radius *= scaleX;
525
+ geom.radius *= Math.abs(scaleX);
511
526
  b2Shape_SetCircle(id, geom);
512
527
  this.geom = geom;
528
+ } else if (type == 4 || type == 7) {
529
+ // capsule (single or capsule chain segment)
530
+ geom = b2Shape_GetCapsule(id);
531
+ geom.center1.x *= scaleX;
532
+ geom.center1.y *= scaleY;
533
+ geom.center2.x *= scaleX;
534
+ geom.center2.y *= scaleY;
535
+ geom.radius *= Math.min(scaleX, scaleY);
536
+ b2Shape_SetCapsule(id, geom);
537
+ this.geom = geom;
538
+ } else if (type == 5) {
539
+ // segment
540
+ geom = b2Shape_GetSegment(id);
541
+ geom.point1.x *= scaleX;
542
+ geom.point1.y *= scaleY;
543
+ geom.point2.x *= scaleX;
544
+ geom.point2.y *= scaleY;
545
+ b2Shape_SetSegment(id, geom);
546
+ this.geom = geom;
513
547
  }
548
+ // type 6 (chain) - handled by sprite's scaleBy, which rescales and sets all segments
514
549
  }
515
550
 
516
551
  _enableContactEvents(val = true) {
@@ -667,6 +702,8 @@ async function q5playPreSetup(q) {
667
702
  if (typeof val == 'string') {
668
703
  if (!val.includes('.')) {
669
704
  val = new $.EmojiImage(val, this.w);
705
+ } else if (using_p5) {
706
+ return this._setP5Img(val);
670
707
  } else val = $.loadImage(val);
671
708
  }
672
709
  this._img = this._extendImage(val);
@@ -695,6 +732,13 @@ async function q5playPreSetup(q) {
695
732
  return img;
696
733
  }
697
734
 
735
+ async _setP5Img(val) {
736
+ // for p5 v2 compatibility
737
+ let img = await $.loadImage(val);
738
+ this._img = this._extendImage(img);
739
+ this._hasImagery = true;
740
+ }
741
+
698
742
  addAni() {
699
743
  let args = [...arguments];
700
744
  let ani, name;
@@ -837,7 +881,13 @@ async function q5playPreSetup(q) {
837
881
  if (flipY) ani.scale.y = -ani.scale.y;
838
882
 
839
883
  if (start < 0) start = ani.length + start;
840
- if (start !== undefined) ani._frame = start;
884
+ if (start !== undefined) {
885
+ ani._frame = start;
886
+ } else {
887
+ // reset so recycled animations don't immediately resolve
888
+ ani._frame = 0;
889
+ ani.playing = true;
890
+ }
841
891
 
842
892
  if (end !== undefined) ani.goToFrame(end);
843
893
  else if (ani._frame == ani.lastFrame) resolve();
@@ -954,7 +1004,7 @@ async function q5playPreSetup(q) {
954
1004
 
955
1005
  this._posX = x;
956
1006
  this._posY = y;
957
- this._pos = $.createVector.call($);
1007
+ this._pos = $.createVector.call($, 0, 0);
958
1008
 
959
1009
  let _this = this;
960
1010
  Object.defineProperties(this._pos, {
@@ -976,7 +1026,7 @@ async function q5playPreSetup(q) {
976
1026
  }
977
1027
  });
978
1028
 
979
- this._canvasPos = $.createVector.call($);
1029
+ this._canvasPos = $.createVector.call($, 0, 0);
980
1030
 
981
1031
  Object.defineProperties(this._canvasPos, {
982
1032
  x: {
@@ -996,16 +1046,16 @@ async function q5playPreSetup(q) {
996
1046
  });
997
1047
 
998
1048
  this._direction = 0;
999
- this._velX = 0;
1000
- this._velY = 0;
1049
+ this.vx = 0;
1050
+ this.vy = 0;
1001
1051
  this._velSynced = true;
1002
- this._vel = $.createVector.call($);
1052
+ this._vel = $.createVector.call($, 0, 0);
1003
1053
  this._vel._useCache = true;
1004
1054
 
1005
1055
  this._syncVel = () => {
1006
1056
  let v = b2Body_GetLinearVelocity(this.bdID);
1007
- this._velX = v.x;
1008
- this._velY = v.y;
1057
+ this.vx = v.x;
1058
+ this.vy = v.y;
1009
1059
  this._velSynced = true;
1010
1060
  };
1011
1061
 
@@ -1015,13 +1065,13 @@ async function q5playPreSetup(q) {
1015
1065
  if (!_this._velSynced && _this._physicsEnabled) {
1016
1066
  _this._syncVel();
1017
1067
  }
1018
- return _this._velX;
1068
+ return _this.vx;
1019
1069
  },
1020
1070
  set(val) {
1021
1071
  if (_this._physicsEnabled) {
1022
1072
  b2Body_SetLinearVelocity(_this.bdID, new b2Vec2(val, this.y));
1023
1073
  }
1024
- _this._velX = val;
1074
+ _this.vx = val;
1025
1075
  this._magCached = this._directionCached = false;
1026
1076
  }
1027
1077
  },
@@ -1030,18 +1080,42 @@ async function q5playPreSetup(q) {
1030
1080
  if (!_this._velSynced && _this._physicsEnabled) {
1031
1081
  _this._syncVel();
1032
1082
  }
1033
- return _this._velY;
1083
+ return _this.vy;
1034
1084
  },
1035
1085
  set(val) {
1036
1086
  if (_this._physicsEnabled) {
1037
1087
  b2Body_SetLinearVelocity(_this.bdID, new b2Vec2(this.x, val));
1038
1088
  }
1039
- _this._velY = val;
1089
+ _this.vy = val;
1040
1090
  this._magCached = this._directionCached = false;
1041
1091
  }
1042
1092
  }
1043
1093
  });
1044
1094
 
1095
+ if (using_p5) {
1096
+ this._vel.direction = function () {
1097
+ if (!this._directionCached) {
1098
+ const x = this.x,
1099
+ y = this.y;
1100
+ if (x || y) this._direction = $.atan2(this.y, this.x);
1101
+ else this._direction = 0;
1102
+ this._directionCached = this._useCache;
1103
+ }
1104
+ return this._direction;
1105
+ };
1106
+
1107
+ this._vel.setDirection = function (ang) {
1108
+ let mag = this.mag();
1109
+ if (mag) {
1110
+ this.x = mag * $.cos(ang);
1111
+ this.y = mag * $.sin(ang);
1112
+ }
1113
+ this._direction = ang;
1114
+ this._directionCached = this._useCache;
1115
+ return this;
1116
+ };
1117
+ }
1118
+
1045
1119
  this._heading = 'right';
1046
1120
 
1047
1121
  if (group._layer) this.layer = group._layer;
@@ -1155,8 +1229,7 @@ async function q5playPreSetup(q) {
1155
1229
  set(val) {
1156
1230
  if (!val || val == this._x) return;
1157
1231
  if (_this.watch) _this.mod[26] = true;
1158
- let scaleX = Math.abs(val / this._x);
1159
- _this.scaleBy(scaleX, 1);
1232
+ _this.scaleBy(val / this._x, 1);
1160
1233
  this._x = val;
1161
1234
  this._avg = (this._x + this._y) * 0.5;
1162
1235
  _this._shouldScale = this._avg != 1;
@@ -1170,8 +1243,7 @@ async function q5playPreSetup(q) {
1170
1243
  set(val) {
1171
1244
  if (!val || val == this._y) return;
1172
1245
  if (_this.watch) _this.mod[26] = true;
1173
- let scaleY = Math.abs(val / this._y);
1174
- _this.scaleBy(1, scaleY);
1246
+ _this.scaleBy(1, val / this._y);
1175
1247
  this._y = val;
1176
1248
  this._avg = (this._x + this._y) * 0.5;
1177
1249
  _this._shouldScale = this._avg != 1;
@@ -1514,10 +1586,10 @@ async function q5playPreSetup(q) {
1514
1586
  geom.center2 = vecs[i];
1515
1587
  geom.radius = rr ? rr / meterSize : 0.02;
1516
1588
  id = b2CreateCapsuleShape(bdID, shape.def, geom);
1517
- let shapePart = new Collider(this);
1518
- shape._init(id, 7, geom);
1519
- shapes.push(shapePart);
1520
- shapeDict[id.index1] = shapePart;
1589
+ let sh = new Collider(this);
1590
+ sh._init(id, 7, geom);
1591
+ shapes.push(sh);
1592
+ shapeDict[id.index1] = sh;
1521
1593
  }
1522
1594
  shape = null;
1523
1595
  this.isSuperFast = true;
@@ -1535,12 +1607,17 @@ async function q5playPreSetup(q) {
1535
1607
 
1536
1608
  id = b2CreateChain(bdID, shape.def);
1537
1609
  shape._init(id, 6);
1610
+ shape.friction = 0.5;
1611
+ shape._points = vecs.map((v) => ({ x: v.x, y: v.y }));
1612
+ shape._isLoopChain = vecs.isLoop;
1613
+ this._chain = shape;
1538
1614
 
1539
1615
  let count = b2Chain_GetSegmentCount(id);
1540
1616
  let segments = b2Chain_GetSegments(id, count);
1541
1617
  for (let segID of segments) {
1542
1618
  let sh = new Collider(this);
1543
1619
  sh._init(segID, 6);
1620
+ sh.friction = 0.5;
1544
1621
  shapes.push(sh);
1545
1622
  shapeDict[segID.index1] = sh;
1546
1623
  }
@@ -2090,17 +2167,85 @@ async function q5playPreSetup(q) {
2090
2167
  scaleBy(x, y) {
2091
2168
  if (y === undefined) y = x;
2092
2169
 
2170
+ const ax = Math.abs(x),
2171
+ ay = Math.abs(y);
2172
+
2093
2173
  if (this._shapes) {
2094
2174
  for (let shape of this._shapes) {
2095
2175
  shape.scaleBy(x, y);
2096
2176
  }
2097
2177
  }
2098
2178
 
2099
- this._w *= x;
2100
- this._hw *= x;
2179
+ if (this._hasChain) this._rebuildChain(x, y);
2180
+
2181
+ this._w *= ax;
2182
+ this._hw *= ax;
2101
2183
  if (this._h) {
2102
- this._h *= y;
2103
- this._hh *= y;
2184
+ this._h *= ay;
2185
+ this._hh *= ay;
2186
+ }
2187
+ }
2188
+
2189
+ _rebuildChain(scaleX, scaleY) {
2190
+ const chain = this._chain;
2191
+ const shapes = this._shapes;
2192
+
2193
+ // save properties from existing segments before removing them
2194
+ const firstCollider = this.colliders[0];
2195
+ const savedFriction = firstCollider?._friction ?? 0.5;
2196
+ const savedSurfaceSpeed = firstCollider?._tangentSpeed ?? 0;
2197
+ const savedBounciness = firstCollider?._restitution ?? 0.2;
2198
+ // negate surfaceSpeed when the chain is flipped (odd number of axes negated)
2199
+ const newSurfaceSpeed = scaleX * scaleY < 0 ? -savedSurfaceSpeed : savedSurfaceSpeed;
2200
+
2201
+ // scale the stored points (already in Box2D meter coordinates)
2202
+ for (let p of chain._points) {
2203
+ p.x *= scaleX;
2204
+ p.y *= scaleY;
2205
+ }
2206
+
2207
+ // when an odd number of axes are negated, the winding order reverses,
2208
+ // flipping the collision normal; reverse the point order to compensate
2209
+ if (scaleX * scaleY < 0) chain._points.reverse();
2210
+
2211
+ // clean up old chain segment JS references
2212
+ for (let i = shapes.length - 1; i >= 0; i--) {
2213
+ if (shapes[i].type === 6) {
2214
+ delete shapeDict[shapes[i].id.index1];
2215
+ this.colliders.splice(this.colliders.indexOf(shapes[i]), 1);
2216
+ shapes.splice(i, 1);
2217
+ }
2218
+ }
2219
+
2220
+ // destroy old chain (Box2D frees all segment physics objects)
2221
+ b2DestroyChain(chain.id);
2222
+
2223
+ // recompute packed material data
2224
+ const packedData = ((chain._isFirstShape ? 1 : 0) << 26) | this._uid;
2225
+
2226
+ // create new chain with scaled points
2227
+ const chainDef = new b2DefaultChainDef();
2228
+ chainDef.SetPoints([chain._points[0], ...chain._points, chain._points.at(-1)]);
2229
+ chainDef.isLoop = chain._isLoopChain;
2230
+ chainDef.SetMaterials([{ customColor: packedData }]);
2231
+
2232
+ const newId = b2CreateChain(this.bdID, chainDef);
2233
+ chain._init(newId, 6);
2234
+ chain.friction = savedFriction;
2235
+ chain._tangentSpeed = newSurfaceSpeed;
2236
+
2237
+ // register new chain segments with restored properties
2238
+ const count = b2Chain_GetSegmentCount(newId);
2239
+ const segments = b2Chain_GetSegments(newId, count);
2240
+ for (let segID of segments) {
2241
+ let sh = new Collider(this);
2242
+ sh._init(segID, 6);
2243
+ sh.friction = savedFriction;
2244
+ sh.bounciness = savedBounciness;
2245
+ if (newSurfaceSpeed) sh.surfaceSpeed = newSurfaceSpeed;
2246
+ shapes.push(sh);
2247
+ this.colliders.push(sh);
2248
+ shapeDict[segID.index1] = sh;
2104
2249
  }
2105
2250
  }
2106
2251
 
@@ -2124,10 +2269,7 @@ async function q5playPreSetup(q) {
2124
2269
 
2125
2270
  if (this.watch) this.mod[26] = true;
2126
2271
 
2127
- let scaleX = Math.abs(x / sc._x);
2128
- let scaleY = Math.abs(y / sc._y);
2129
-
2130
- this.scaleBy(scaleX, scaleY);
2272
+ this.scaleBy(x / sc._x, y / sc._y);
2131
2273
 
2132
2274
  sc._x = x;
2133
2275
  sc._y = y;
@@ -2181,6 +2323,10 @@ async function q5playPreSetup(q) {
2181
2323
  }
2182
2324
  set surfaceSpeed(val) {
2183
2325
  if (this.watch) this.mod[21] = true;
2326
+ if (this._hasCapsuleChain) {
2327
+ return console.error('Can not set surfaceSpeed of a capsule chain.');
2328
+ }
2329
+ if (this._hasChain) this._chain.surfaceSpeed = val;
2184
2330
  for (let collider of this.colliders) {
2185
2331
  collider.surfaceSpeed = val;
2186
2332
  }
@@ -2412,12 +2558,19 @@ async function q5playPreSetup(q) {
2412
2558
  this._setVel(val[0] ?? val.x, val[1] ?? val.y);
2413
2559
  }
2414
2560
 
2561
+ get velocity() {
2562
+ return this._vel;
2563
+ }
2564
+ set velocity(val) {
2565
+ this.vel = val;
2566
+ }
2567
+
2415
2568
  _setVel(x, y) {
2416
2569
  if (this._physicsEnabled) {
2417
2570
  b2Body_SetLinearVelocity(this.bdID, new b2Vec2(x, y));
2418
2571
  }
2419
- this._velX = x;
2420
- this._velY = y;
2572
+ this.vx = x;
2573
+ this.vy = y;
2421
2574
  this._velSynced = true;
2422
2575
  this._vel._magCached = this._vel._directionCached = false;
2423
2576
  }
@@ -2426,18 +2579,11 @@ async function q5playPreSetup(q) {
2426
2579
  if (this._physicsEnabled) {
2427
2580
  b2Body_SetLinearVelocity(this.bdID, new b2Vec2(x, y));
2428
2581
  }
2429
- this._velX = x;
2430
- this._velY = y;
2582
+ this.vx = x;
2583
+ this.vy = y;
2431
2584
  this._velSynced = true;
2432
2585
  }
2433
2586
 
2434
- get velocity() {
2435
- return this._vel;
2436
- }
2437
- set velocity(val) {
2438
- this.vel = val;
2439
- }
2440
-
2441
2587
  get grabbable() {
2442
2588
  return this._grabbable;
2443
2589
  }
@@ -2468,8 +2614,8 @@ async function q5playPreSetup(q) {
2468
2614
  if (this._life <= 0) {
2469
2615
  this.delete();
2470
2616
  } else if (!this._physicsEnabled || !usePhysics) {
2471
- this._posX += this._velX * timeScale;
2472
- this._posY += this._velY * timeScale;
2617
+ this._posX += this.vx * timeScale;
2618
+ this._posY += this.vy * timeScale;
2473
2619
  this._rotation += this._rotationSpeed * timeScale;
2474
2620
  }
2475
2621
 
@@ -2776,8 +2922,9 @@ async function q5playPreSetup(q) {
2776
2922
  args[2] = args[1];
2777
2923
  args[1] = undefined;
2778
2924
  }
2779
- let o = {};
2780
- o.forceVector = new b2Vec2(args[0], args[1]);
2925
+ const v = this._args2Vec(args[0], args[1]),
2926
+ o = {};
2927
+ o.forceVector = new b2Vec2(v.x, v.y);
2781
2928
  if (args[2] !== undefined) {
2782
2929
  o.poa = this._args2Vec(args[2], args[3]);
2783
2930
  o.poa = scaleTo(o.poa.x, o.poa.y);
@@ -2853,34 +3000,19 @@ async function q5playPreSetup(q) {
2853
3000
  wind.delete();
2854
3001
  }
2855
3002
 
2856
- moveTowards(x, y, tracking) {
2857
- if (x === undefined) return;
3003
+ move(distance, direction, speed) {
3004
+ if (!distance) return;
2858
3005
 
2859
- if (typeof x != 'number' && x !== null) {
2860
- let pos = x;
2861
- if (pos == $.mouse && !$.mouse.isActive) return;
2862
- tracking = y;
2863
- y = pos.y;
2864
- x = pos.x;
3006
+ if (typeof direction == 'string') {
3007
+ directionNamed = true;
3008
+ this._heading = direction;
3009
+ direction = this._getDirectionAngle(direction);
2865
3010
  }
2866
- tracking ??= 0.1;
2867
-
2868
- let velX, velY;
2869
-
2870
- if (x !== null && x !== undefined) {
2871
- let diffX = x - this.x;
2872
- if (!isSlop(diffX)) {
2873
- velX = diffX * tracking;
2874
- } else velX = 0;
2875
- } else velX = this._velX;
2876
- if (y !== null && y !== undefined) {
2877
- let diffY = y - this.y;
2878
- if (!isSlop(diffY)) {
2879
- velY = diffY * tracking;
2880
- } else velY = 0;
2881
- } else velY = this._velY;
3011
+ direction ??= this.direction;
2882
3012
 
2883
- this._setVel(velX, velY);
3013
+ const x = $.cos(direction) * distance + this.x,
3014
+ y = $.sin(direction) * distance + this.y;
3015
+ return this.moveTo(x, y, speed);
2884
3016
  }
2885
3017
 
2886
3018
  moveTo(x, y, speed) {
@@ -2919,12 +3051,14 @@ async function q5playPreSetup(q) {
2919
3051
  if (moveX && moveY) {
2920
3052
  this.setSpeedAndDirection(speed, $.atan2(y - this.y, x - this.x));
2921
3053
  } else if (moveX) {
2922
- this._setVel(x > this._posX ? speed : -speed, this._velY);
3054
+ this._setVel(x > this._posX ? speed : -speed, this.vy);
2923
3055
  } else {
2924
- this._setVel(this._velX, y > this._posY ? speed : -speed);
3056
+ this._setVel(this.vx, y > this._posY ? speed : -speed);
2925
3057
  }
2926
3058
  }
2927
3059
 
3060
+ if ($._py) return new Promise((resolve) => (this._destResolve = resolve));
3061
+
2928
3062
  return {
2929
3063
  then: (onFulfilled) => {
2930
3064
  this._destResolve = onFulfilled;
@@ -2932,6 +3066,36 @@ async function q5playPreSetup(q) {
2932
3066
  };
2933
3067
  }
2934
3068
 
3069
+ moveTowards(x, y, tracking) {
3070
+ if (x === undefined) return;
3071
+
3072
+ if (typeof x != 'number' && x !== null) {
3073
+ let pos = x;
3074
+ if (pos == $.mouse && !$.mouse.isActive) return;
3075
+ tracking = y;
3076
+ y = pos.y;
3077
+ x = pos.x;
3078
+ }
3079
+ tracking ??= 0.1;
3080
+
3081
+ let velX, velY;
3082
+
3083
+ if (x !== null && x !== undefined) {
3084
+ let diffX = x - this.x;
3085
+ if (!isSlop(diffX)) {
3086
+ velX = diffX * tracking;
3087
+ } else velX = 0;
3088
+ } else velX = this.vx;
3089
+ if (y !== null && y !== undefined) {
3090
+ let diffY = y - this.y;
3091
+ if (!isSlop(diffY)) {
3092
+ velY = diffY * tracking;
3093
+ } else velY = 0;
3094
+ } else velY = this.vy;
3095
+
3096
+ this._setVel(velX, velY);
3097
+ }
3098
+
2935
3099
  angleTo(x, y, facing = 0) {
2936
3100
  if (typeof x == 'object') {
2937
3101
  facing = y || 0;
@@ -3023,7 +3187,7 @@ async function q5playPreSetup(q) {
3023
3187
 
3024
3188
  this.rotationSpeed = speed;
3025
3189
 
3026
- log(this.rotation, this._destRot, angleDist, speed, this._destRotArrivalTime);
3190
+ if ($._py) return new Promise((resolve) => (this._destRotationResolve = resolve));
3027
3191
 
3028
3192
  return {
3029
3193
  then: (onFulfilled) => {
@@ -3480,6 +3644,12 @@ async function q5playPreSetup(q) {
3480
3644
 
3481
3645
  // image sequence format
3482
3646
  if (args.length == 2 && typeof args[0] == 'string' && typeof args[1] == 'string' && !args[1].includes('.')) {
3647
+ if (using_p5) {
3648
+ throw new Error(
3649
+ 'p5.js v2 does not support preloading or lazy loading images. Convert the image sequence to a sprite sheet.'
3650
+ );
3651
+ }
3652
+
3483
3653
  let file = args[0],
3484
3654
  extIndex = file.lastIndexOf('.'),
3485
3655
  digits = 0;
@@ -3634,7 +3804,9 @@ async function q5playPreSetup(q) {
3634
3804
  if (typeof sheet == 'string') {
3635
3805
  sheet = $.loadImage(sheet);
3636
3806
  }
3637
- sheet.promise.then(() => {
3807
+ let promise = !using_p5 ? sheet.promise : sheet;
3808
+ promise.then((img) => {
3809
+ if (using_p5) this.spriteSheet = sheet = img;
3638
3810
  if (!this.length) findFrames();
3639
3811
 
3640
3812
  if (this._clones) {
@@ -3656,7 +3828,11 @@ async function q5playPreSetup(q) {
3656
3828
  else {
3657
3829
  for (let i = 0; i < args.length; i++) {
3658
3830
  if (args[i] instanceof Q5.Image) this.push(args[i]);
3659
- else this.push($.loadImage(args[i]));
3831
+ else if (using_p5) {
3832
+ throw new Error(
3833
+ 'p5.js v2 does not support preloading or lazy loading images. You need to use `await load(url)`.'
3834
+ );
3835
+ } else this.push($.loadImage(args[i]));
3660
3836
  }
3661
3837
  }
3662
3838
 
@@ -4025,6 +4201,8 @@ async function q5playPreSetup(q) {
4025
4201
  if (typeof val == 'string') {
4026
4202
  if (!val.includes('.')) {
4027
4203
  val = new $.EmojiImage(val, this.w || this.width || this.d || this.diameter);
4204
+ } else if (using_p5) {
4205
+ return $.Visual.prototype._setP5Img(val);
4028
4206
  } else val = $.loadImage(val);
4029
4207
  }
4030
4208
  this._img = $.Visual.prototype._extendImage(val);
@@ -4295,7 +4473,7 @@ async function q5playPreSetup(q) {
4295
4473
  for (let vecProp of vecProps) {
4296
4474
  vecProp = '_' + vecProp;
4297
4475
  if (vecProp != 'vel') this[vecProp] = {};
4298
- else this[vecProp] = $.createVector.call($);
4476
+ else this[vecProp] = $.createVector.call($, 0, 0);
4299
4477
  this[vecProp]._x = undefined;
4300
4478
  this[vecProp]._y = undefined;
4301
4479
  for (let prop of ['x', 'y']) {
@@ -4755,6 +4933,21 @@ async function q5playPreSetup(q) {
4755
4933
  for (let s of this) {
4756
4934
  thenables.push(s.rotateTo(...arguments));
4757
4935
  }
4936
+
4937
+ if ($._py) {
4938
+ return new Promise((resolve) => {
4939
+ let pending = thenables.length;
4940
+ if (!pending) return resolve(true);
4941
+ let allReached = true;
4942
+ for (let t of thenables) {
4943
+ t.then((reached) => {
4944
+ if (!reached) allReached = false;
4945
+ if (--pending === 0) resolve(allReached);
4946
+ });
4947
+ }
4948
+ });
4949
+ }
4950
+
4758
4951
  return {
4759
4952
  then: (onFulfilled) => {
4760
4953
  let pending = thenables.length;
@@ -4775,6 +4968,21 @@ async function q5playPreSetup(q) {
4775
4968
  for (let s of this) {
4776
4969
  thenables.push(s.rotate(...arguments));
4777
4970
  }
4971
+
4972
+ if ($._py) {
4973
+ return new Promise((resolve) => {
4974
+ let pending = thenables.length;
4975
+ if (!pending) return resolve(true);
4976
+ let allReached = true;
4977
+ for (let t of thenables) {
4978
+ t.then((reached) => {
4979
+ if (!reached) allReached = false;
4980
+ if (--pending === 0) resolve(allReached);
4981
+ });
4982
+ }
4983
+ });
4984
+ }
4985
+
4778
4986
  return {
4779
4987
  then: (onFulfilled) => {
4780
4988
  let pending = thenables.length;
@@ -4795,6 +5003,21 @@ async function q5playPreSetup(q) {
4795
5003
  for (let s of this) {
4796
5004
  thenables.push(s.rotateMinTo(...arguments));
4797
5005
  }
5006
+
5007
+ if ($._py) {
5008
+ return new Promise((resolve) => {
5009
+ let pending = thenables.length;
5010
+ if (!pending) return resolve(true);
5011
+ let allReached = true;
5012
+ for (let t of thenables) {
5013
+ t.then((reached) => {
5014
+ if (!reached) allReached = false;
5015
+ if (--pending === 0) resolve(allReached);
5016
+ });
5017
+ }
5018
+ });
5019
+ }
5020
+
4798
5021
  return {
4799
5022
  then: (onFulfilled) => {
4800
5023
  let pending = thenables.length;
@@ -4857,6 +5080,20 @@ async function q5playPreSetup(q) {
4857
5080
  thenables.push(s.moveTo(tx, ty, speed));
4858
5081
  }
4859
5082
 
5083
+ if ($._py) {
5084
+ return new Promise((resolve) => {
5085
+ let pending = thenables.length;
5086
+ if (!pending) return resolve(true);
5087
+ let allReached = true;
5088
+ for (let t of thenables) {
5089
+ t.then((reached) => {
5090
+ if (!reached) allReached = false;
5091
+ if (--pending === 0) resolve(allReached);
5092
+ });
5093
+ }
5094
+ });
5095
+ }
5096
+
4860
5097
  return {
4861
5098
  then: (onFulfilled) => {
4862
5099
  let pending = thenables.length;
@@ -5686,7 +5923,7 @@ async function q5playPreSetup(q) {
5686
5923
  $.Camera = class {
5687
5924
  constructor() {
5688
5925
  // camera position
5689
- this._pos = $.createVector.call($);
5926
+ this._pos = $.createVector.call($, 0, 0);
5690
5927
 
5691
5928
  // camera translation
5692
5929
  this.__pos = { x: 0, y: 0, rounded: {} };
@@ -6956,10 +7193,18 @@ async function q5playPreSetup(q) {
6956
7193
  };
6957
7194
 
6958
7195
  $.delay = (milliseconds) => {
6959
- if (!milliseconds) return new Promise(requestAnimationFrame);
7196
+ if (!milliseconds) {
7197
+ return new Promise((resolve) => {
7198
+ requestAnimationFrame(() => {
7199
+ if (!$._removed) resolve();
7200
+ });
7201
+ });
7202
+ }
6960
7203
  // else it wraps setTimeout in a Promise
6961
7204
  return new Promise((resolve) => {
6962
- setTimeout(resolve, milliseconds);
7205
+ setTimeout(() => {
7206
+ if (!$._removed) resolve();
7207
+ }, milliseconds);
6963
7208
  });
6964
7209
  };
6965
7210
 
@@ -7009,6 +7254,8 @@ async function q5playPreSetup(q) {
7009
7254
  $.Canvas = $.createCanvas = function (w, h) {
7010
7255
  let args = [...arguments];
7011
7256
 
7257
+ if (using_p5 && !didCreateCanvas && w == 100 && h == 100) return _createCanvas.call($, ...args);
7258
+
7012
7259
  // prevent p5 v1 overriding the user's canvas with a new default canvas
7013
7260
  if (didCreateCanvas && w == 100 && h == 100) return;
7014
7261
 
@@ -7039,11 +7286,18 @@ async function q5playPreSetup(q) {
7039
7286
  let rend = _createCanvas.call($, ...args);
7040
7287
  $.ctx = $.drawingContext;
7041
7288
  let c = rend.canvas || rend;
7042
- window.canvas = c; // for p5 v2
7043
- if (rend.GL) {
7044
- c.renderer = 'webgl';
7045
- $._webgl = true;
7046
- } else if (!$._webgpu) $._c2d = true;
7289
+ if (using_p5) {
7290
+ window.canvas = c;
7291
+ if (rend.GPU) {
7292
+ c.renderer = 'webgpu';
7293
+ $._webgpu = true;
7294
+ } else if (rend.GL) {
7295
+ c.renderer = 'webgl';
7296
+ $._webgl = true;
7297
+ } else {
7298
+ $._webgpu = $._webgpuFallback = true;
7299
+ }
7300
+ }
7047
7301
  c.tabIndex = 0;
7048
7302
  c.w = args[0];
7049
7303
  c.h = args[1];
@@ -7106,7 +7360,7 @@ async function q5playPreSetup(q) {
7106
7360
  return rend;
7107
7361
  };
7108
7362
 
7109
- $.canvas = $.canvas;
7363
+ $.canvas = $.canvas; // for brython
7110
7364
 
7111
7365
  const _resizeCanvas = $.resizeCanvas;
7112
7366
 
@@ -7234,7 +7488,7 @@ async function q5playPreSetup(q) {
7234
7488
 
7235
7489
  $.allSprites = new $.Group();
7236
7490
  $.world = new $.World();
7237
- $.camera = new $.Camera();
7491
+ $.camera = $._camera = new $.Camera();
7238
7492
 
7239
7493
  $.InputDevice = class {
7240
7494
  constructor() {
@@ -7309,7 +7563,6 @@ async function q5playPreSetup(q) {
7309
7563
 
7310
7564
  this.x = 0;
7311
7565
  this.y = 0;
7312
- this.canvasPos = {};
7313
7566
  this.isOnCanvas = false;
7314
7567
  this.isActive = false;
7315
7568
  this.left = 0;
@@ -7321,7 +7574,7 @@ async function q5playPreSetup(q) {
7321
7574
  let _this = this;
7322
7575
 
7323
7576
  // this.x and this.y store the actual position values of the mouse
7324
- this._pos = $.createVector.call($);
7577
+ this._pos = $.createVector.call($, 0, 0);
7325
7578
 
7326
7579
  Object.defineProperty(this._pos, 'x', {
7327
7580
  get() {
@@ -7362,10 +7615,20 @@ async function q5playPreSetup(q) {
7362
7615
  }
7363
7616
 
7364
7617
  _update() {
7365
- let cam = $.camera;
7366
- let m = this;
7367
- m.x = $.mouseX / cam.zoom + cam.x;
7368
- m.y = $.mouseY / cam.zoom + cam.y;
7618
+ let cam = $.camera,
7619
+ m = this,
7620
+ mx = $.mouseX,
7621
+ my = $.mouseY;
7622
+
7623
+ if (using_p5) {
7624
+ if ($._webgpuFallback) {
7625
+ mx -= $.halfWidth;
7626
+ my -= $.halfHeight;
7627
+ }
7628
+ }
7629
+
7630
+ m.x = mx / cam.zoom + cam.x;
7631
+ m.y = my / cam.zoom + cam.y;
7369
7632
 
7370
7633
  if (m.scroll < 0) m.scroll = 0;
7371
7634
  if (m.scrollDelta.x == 0 && m.scrollDelta.y == 0) {
@@ -8099,7 +8362,7 @@ async function q5playPreSetup(q) {
8099
8362
  this[indexB] = tmp;
8100
8363
  if (indexA == 0 || indexB == 0) {
8101
8364
  $.contro = this[0];
8102
- if (!$._q5 && $._isGlobal) {
8365
+ if (using_p5 && $._isGlobal) {
8103
8366
  window.contro = this[0];
8104
8367
  }
8105
8368
  }
@@ -8186,7 +8449,7 @@ async function q5playPreSetup(q) {
8186
8449
  fpsPos = 0,
8187
8450
  fpsMin = 60,
8188
8451
  fpsMax = 240;
8189
- let statsColor = $.color('lime');
8452
+ let statsColor = $._q5 ? $.color('lime') : 'lime';
8190
8453
 
8191
8454
  $.renderStats = () => {
8192
8455
  let rs = $.q5play._renderStats;
@@ -8288,12 +8551,22 @@ async function q5playPreSetup(q) {
8288
8551
  shapeStack = [];
8289
8552
 
8290
8553
  const colorMax = $._colorFormat,
8291
- debugGreen = $.color(0, colorMax, 0, colorMax * 0.9),
8292
- debugGreenFill = $.color(0, colorMax, 0, colorMax * 0.1),
8293
- debugYellow = $.color(colorMax, colorMax, 0, colorMax * 0.9),
8294
- debugYellowFill = $.color(colorMax, colorMax, 0, colorMax * 0.1);
8295
-
8296
- if ($.canvas.c2d) {
8554
+ debugGreen = $._q5 ? $.color(0, colorMax, 0, colorMax * 0.9) : '#0f0e',
8555
+ debugGreenFill = $._q5 ? $.color(0, colorMax, 0, colorMax * 0.1) : '#0f02',
8556
+ debugYellow = $._q5 ? $.color(colorMax, colorMax, 0, colorMax * 0.9) : '#ff0e',
8557
+ debugYellowFill = $._q5 ? $.color(colorMax, colorMax, 0, colorMax * 0.1) : '#ff02';
8558
+
8559
+ if (using_p5) {
8560
+ $._getFillIdx = () => $._renderer.states.fillColor;
8561
+ $._setFillIdx = (v) => ($._renderer.states.fillColor = v);
8562
+ $._getStrokeIdx = () => $._renderer.states.strokeColor;
8563
+ $._setStrokeIdx = (v) => ($._renderer.states.strokeColor = v);
8564
+ $._getStrokeWeight = () => [$._renderer.states.strokeWeight];
8565
+ $._setStrokeWeight = (v) => $.strokeWeight(...v);
8566
+ $._getImageMode = () => $._renderer.states.imageMode;
8567
+ $._doFill = () => {};
8568
+ $._doStroke = () => {};
8569
+ } else if ($.canvas.c2d) {
8297
8570
  // polyfill for q5 WebGPU high efficiency functions
8298
8571
  $._getFillIdx = () => $._fill;
8299
8572
  $._setFillIdx = (v) => ($._fill = v);
@@ -8394,7 +8667,7 @@ async function q5playPreSetup(q) {
8394
8667
  transformPoint(xf, v);
8395
8668
  $.vertex(v.x, v.y);
8396
8669
  }
8397
- $.endShape(true);
8670
+ $.endShape($.CLOSE);
8398
8671
  if (rr > 0) {
8399
8672
  $._setStrokeWeight(swData);
8400
8673
  $._setStrokeIdx(ogStroke);
@@ -8440,7 +8713,7 @@ async function q5playPreSetup(q) {
8440
8713
  transformPoint(xf, v);
8441
8714
  $.vertex(v.x, v.y);
8442
8715
  }
8443
- $.endShape(true);
8716
+ $.endShape($.CLOSE);
8444
8717
  } else {
8445
8718
  let x = (cmd.data[0] + cmd.data[2]) / 2,
8446
8719
  y = (cmd.data[1] + cmd.data[3]) / 2;
@@ -8479,12 +8752,17 @@ async function q5playPreSetup(q) {
8479
8752
  };
8480
8753
 
8481
8754
  // prettier-ignore
8482
- let q5playGlobals = ['q5play','Box2D','DYN','DYNAMIC','STA','STATIC','KIN','KINEMATIC','Sprite','Group','allSprites','Ani','Anis','Visual','Visuals','camera','Joint','GlueJoint','DistanceJoint','WheelJoint','HingeJoint','SliderJoint','GrabberJoint','world','kb','keyboard','mouse','contro','contros','controllers','pointer','pointers','spriteArt','EmojiImage','getFPS','animation','parseTextureAtlas','delay'];
8755
+ let q5playGlobals = ['q5play','Box2D','DYN','DYNAMIC','STA','STATIC','KIN','KINEMATIC','Sprite','Group','allSprites','Ani','Anis','Visual','Visuals','Joint','GlueJoint','DistanceJoint','WheelJoint','HingeJoint','SliderJoint','GrabberJoint','world','camera','kb','keyboard','mouse','contro','contros','controllers','pointer','pointers','spriteArt','EmojiImage','getFPS','animation','parseTextureAtlas','delay'];
8483
8756
 
8484
8757
  // manually propagate q5play stuff to the global window object
8485
8758
  if ($._isGlobal) {
8486
8759
  for (let p of q5playGlobals) {
8487
- window[p] = $[p];
8760
+ Object.defineProperty(window, p, {
8761
+ value: $[p],
8762
+ configurable: true,
8763
+ writable: false,
8764
+ enumerable: true
8765
+ });
8488
8766
  }
8489
8767
  }
8490
8768
 
@@ -8495,10 +8773,24 @@ async function q5playPreSetup(q) {
8495
8773
  function q5playPostSetup() {
8496
8774
  const $ = this;
8497
8775
 
8498
- if ($._isGlobal && window.update) {
8499
- $.update = window.update;
8776
+ if ($._isGlobal && window.update) $.update = window.update;
8777
+
8778
+ if (using_p5) {
8500
8779
  // p5 won't run the draw loop without a draw function defined
8501
- if (!$._q5) window.draw = () => {};
8780
+ window.draw ??= () => {};
8781
+
8782
+ $.loge = $.log;
8783
+ $.log = console.log;
8784
+ $.camera = $._camera;
8785
+
8786
+ if ($._isGlobal) {
8787
+ $.camera3D = window.camera;
8788
+ for (let prop of ['log', 'loge', 'camera', 'camera3D']) {
8789
+ Object.defineProperty(window, prop, {
8790
+ value: $[prop]
8791
+ });
8792
+ }
8793
+ }
8502
8794
  }
8503
8795
 
8504
8796
  $.update ??= $.clear;
@@ -8510,9 +8802,11 @@ function q5playPostSetup() {
8510
8802
  function q5playUpdate() {
8511
8803
  const $ = this;
8512
8804
 
8513
- if (!$._q5) {
8805
+ if (using_p5) {
8514
8806
  $.q5play._preDrawFrameTime = performance.now();
8807
+ $.resetMatrix();
8515
8808
  }
8809
+
8516
8810
  $.q5play.spritesDrawn = 0;
8517
8811
 
8518
8812
  $.contros._update();
@@ -8655,7 +8949,7 @@ function q5playPostDraw() {
8655
8949
  else if ($.kb[k] > 0) $.kb[k]++;
8656
8950
  }
8657
8951
 
8658
- if (!$._q5) {
8952
+ if (using_p5) {
8659
8953
  $.q5play._postDrawFrameTime = performance.now();
8660
8954
  $.q5play._fps = Math.round(1000 / ($.q5play._postDrawFrameTime - $.q5play._preDrawFrameTime)) || 1;
8661
8955
  }
@@ -8663,6 +8957,7 @@ function q5playPostDraw() {
8663
8957
  }
8664
8958
 
8665
8959
  function q5playRemove() {
8960
+ this._removed = true;
8666
8961
  this.world?.delete();
8667
8962
  }
8668
8963
 
@@ -8747,15 +9042,19 @@ addAnis -> es:añadirAnis
8747
9042
  changeAni -> es:cambiarAni
8748
9043
  playAni -> es:reproducirAni
8749
9044
  playAnis -> es:reproducirAnis
9045
+ moveTo -> es:moverA
8750
9046
  moveTowards -> es:moverHacia
9047
+ rotateTo -> es:rotarA
8751
9048
  rotateTowards -> es:rotarHacia
9049
+ transformTowards -> es:transformarHacia
8752
9050
  applyForce -> es:aplicarFuerza
8753
9051
  applyForceScaled -> es:aplicarFuerzaEscalada
8754
9052
  attractTo -> es:atraerA
8755
9053
  repelFrom -> es:repelerDe
8756
9054
  applyTorque -> es:aplicarTorque
8757
- angleTo -> es:ánguloHacia
8758
- angleDistTo -> es:distÁnguloHacia
9055
+ applyWind -> es:aplicarViento
9056
+ angleTo -> es:ánguloA
9057
+ angleDistTo -> es:distÁnguloA
8759
9058
  setSpeedAndDirection -> es:establecerVelocidadYDirección
8760
9059
  scaleBy -> es:escalarPor
8761
9060
  resetMass -> es:reiniciarMasa
@@ -8859,6 +9158,178 @@ pressure -> es:presión
8859
9158
  };
8860
9159
  q5playClassLangs.Group += q5playClassLangs.Sprite;
8861
9160
 
9161
+ if (typeof globalThis.Q5 == 'undefined') {
9162
+ let upgrade = ' Consider upgrading to q5: https://q5js.org';
9163
+ console.warn('p5.js v2 is not fully compatible with q5play.' + upgrade);
9164
+
9165
+ p5.addHook = (hook, fn) => {
9166
+ p5.registerAddon((p5, proto, lifecycles) => {
9167
+ lifecycles[hook] = fn;
9168
+ });
9169
+ };
9170
+
9171
+ // p5.js v2 compatibility layer
9172
+ globalThis.Canvas = (...args) => {
9173
+ return new Promise((resolve) => {
9174
+ window.setup = async function () {
9175
+ const $ = p5.instance;
9176
+
9177
+ $.Canvas(...args);
9178
+
9179
+ // q5play defaults
9180
+ $.angleMode($.DEGREES);
9181
+ $.colorMode($.RGB, 1);
9182
+ $.imageMode($.CENTER);
9183
+
9184
+ $.halfWidth = width / 2;
9185
+ $.halfHeight = height / 2;
9186
+ $.jit = (v) => $.random(-v, v);
9187
+
9188
+ $._loaders = [];
9189
+ $._colorFormat = 1;
9190
+
9191
+ const imgRegex = /(jpe?g|png|gif|webp|avif|svg)/i,
9192
+ fontRegex = /(ttf|otf|woff2?|eot|json)/i,
9193
+ fontCategoryRegex = /(serif|sans-serif|monospace|cursive|fantasy)/i,
9194
+ audioRegex = /(wav|flac|mp3|ogg|m4a|aac|aiff|weba)/i;
9195
+
9196
+ $.load = function (...urls) {
9197
+ if (Array.isArray(urls[0])) urls = urls[0];
9198
+
9199
+ let promises = [];
9200
+
9201
+ for (let url of urls) {
9202
+ let ext = url.split('.').pop().toLowerCase();
9203
+
9204
+ let obj;
9205
+ if (ext == 'json') {
9206
+ if (url.includes('-msdf.')) {
9207
+ throw new Error('p5.js v2 can not load MSDF fonts.' + upgrade);
9208
+ }
9209
+ obj = $.loadJSON(url);
9210
+ } else if (ext == 'csv') {
9211
+ obj = $.loadCSV(url);
9212
+ } else if (imgRegex.test(ext)) {
9213
+ obj = $.loadImage(url);
9214
+ } else if (fontRegex.test(ext) || fontCategoryRegex.test(url)) {
9215
+ obj = $.loadFont(url);
9216
+ } else if (audioRegex.test(ext)) {
9217
+ obj = $.loadSound(url);
9218
+ } else if (ext == 'xml') {
9219
+ obj = $.loadXML(url);
9220
+ } else {
9221
+ obj = $.loadText(url);
9222
+ }
9223
+ promises.push(obj);
9224
+ }
9225
+
9226
+ if (urls.length == 1) return promises[0];
9227
+ return Promise.all(promises);
9228
+ };
9229
+
9230
+ if ($._webgpuFallback) {
9231
+ $.opacity = function (v) {
9232
+ $.ctx.globalAlpha = v;
9233
+ };
9234
+ }
9235
+
9236
+ $.pushMatrix = $.push;
9237
+ $.popMatrix = $.pop;
9238
+
9239
+ $.loadAll = function () {
9240
+ console.error('p5.js v2 does not have loadAll().' + upgrade);
9241
+ };
9242
+
9243
+ $.displayMode = () => {
9244
+ console.error('p5.js v2 does not have displayMode().' + upgrade);
9245
+ };
9246
+
9247
+ $.MAXED = 'maxed';
9248
+ $.SMOOTH = 'smooth';
9249
+ $.PIXELATED = 'pixelated';
9250
+
9251
+ if ($._isGlobal) {
9252
+ let props = [
9253
+ 'halfWidth',
9254
+ 'halfHeight',
9255
+ 'jit',
9256
+ 'load',
9257
+ 'opacity',
9258
+ 'pushMatrix',
9259
+ 'popMatrix',
9260
+ 'loadAll',
9261
+ 'displayMode',
9262
+ 'MAXED',
9263
+ 'PIXELATED'
9264
+ ];
9265
+ for (let p of props) {
9266
+ window[p] = $[p];
9267
+ }
9268
+ }
9269
+
9270
+ if ($._webgpuFallback) {
9271
+ const _resetMatrix = $.resetMatrix;
9272
+ $.resetMatrix = () => {
9273
+ _resetMatrix.call($);
9274
+ // sets origin to the center of the canvas
9275
+ $.translate($.halfWidth, $.halfHeight);
9276
+ };
9277
+ }
9278
+
9279
+ Object.defineProperty(p5, 'lang', {
9280
+ set() {
9281
+ console.error('p5.js v2 does not support changing language.' + upgrade);
9282
+ },
9283
+ get() {
9284
+ return 'en';
9285
+ },
9286
+ configurable: true
9287
+ });
9288
+
9289
+ const entryPoints = [
9290
+ 'setup',
9291
+ 'update',
9292
+ 'draw',
9293
+ 'deviceMoved',
9294
+ 'deviceTurned',
9295
+ 'deviceShaken',
9296
+ 'doubleClicked',
9297
+ 'mousePressed',
9298
+ 'mouseReleased',
9299
+ 'mouseMoved',
9300
+ 'mouseDragged',
9301
+ 'mouseClicked',
9302
+ 'mouseWheel',
9303
+ 'touchStarted',
9304
+ 'touchMoved',
9305
+ 'touchEnded',
9306
+ 'keyPressed',
9307
+ 'keyReleased',
9308
+ 'keyTyped',
9309
+ 'windowResized'
9310
+ ];
9311
+
9312
+ for (let ep of entryPoints) {
9313
+ Object.defineProperty(p5, ep, {
9314
+ set(fn) {
9315
+ $[ep] = fn;
9316
+ if ($._isGlobal) window[ep] = fn;
9317
+ },
9318
+ get() {
9319
+ return $[ep];
9320
+ },
9321
+ configurable: true
9322
+ });
9323
+ }
9324
+
9325
+ resolve();
9326
+ };
9327
+ });
9328
+ };
9329
+
9330
+ globalThis.Q5 = globalThis.q5 = p5;
9331
+ }
9332
+
8862
9333
  Q5.addHook('presetup', q5playPreSetup);
8863
9334
  Q5.addHook('postsetup', q5playPostSetup);
8864
9335
  Q5.addHook('predraw', q5playUpdate);
package/q5play.pyi CHANGED
@@ -86,25 +86,25 @@ class Q5Play:
86
86
 
87
87
  def splashScreen(self) -> Awaitable[None]:
88
88
  """
89
- "Made with q5play" [splash screen](https://en.wikipedia.org/wiki/Splash_screen) displayed during
90
- initial page load by default.
89
+ "Made with q5play" [splash screen](https://en.wikipedia.org/wiki/Splash_screen)
90
+ displayed during initial page load by default.
91
91
  """
92
92
  ...
93
93
 
94
94
  def update(self) -> None:
95
- """Runs automatically before each draw function call."""
95
+ """Runs automatically before each q5.draw function call."""
96
96
  ...
97
97
 
98
- def postDraw(self) -> None:
99
- """Runs automatically after each draw function call."""
98
+ def draw(self) -> None:
99
+ """Runs automatically after each q5.draw function call."""
100
100
  ...
101
101
 
102
102
  q5play: Q5Play
103
103
 
104
104
  Box2D: Any
105
105
  """
106
- Box2D v3 ported to WASM is the physics engine that
107
- q5play uses for its physics simulation.
106
+ Box2D v3 ported to WASM is used by
107
+ q5play to simulate physics.
108
108
 
109
109
  This variable enables direct access to the Box2D API for
110
110
  advanced users who want to do things that aren't wrapped
@@ -1271,9 +1271,11 @@ class Sprite(Visual):
1271
1271
  """
1272
1272
  Scales the the sprite.
1273
1273
 
1274
+ Components can be negative to flip/mirror the sprite on an axis.
1275
+
1274
1276
  Args:
1275
- x: scaleX or uniform scale factor
1276
- y: scaleY
1277
+ x: horizontal scale factor or uniform scale factor for both axes
1278
+ y: vertical scale factor
1277
1279
  """
1278
1280
  ...
1279
1281
 
@@ -1328,6 +1330,7 @@ class Sprite(Visual):
1328
1330
  """
1329
1331
  The sprite's speed along the surface of its collider(s),
1330
1332
  like a conveyor belt.
1333
+ Requires friction to be greater than 1 to have an effect.
1331
1334
 
1332
1335
  Default: `0`
1333
1336
  """
@@ -4398,9 +4401,6 @@ class _Mouse(InputDevice):
4398
4401
  y: float
4399
4402
  """The mouse's y position in the world."""
4400
4403
 
4401
- canvasPos: dict
4402
- """The mouse's absolute position on the canvas."""
4403
-
4404
4404
  left: float
4405
4405
  """The mouse's left button."""
4406
4406