like2d 2.11.1 → 2.12.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 (52) hide show
  1. package/README.md +30 -2
  2. package/assets/logo-banner-optimized.svg +15 -27
  3. package/assets/logo-banner.svg +76 -132
  4. package/assets/logo-icon.svg +33 -23
  5. package/assets/logo.svg +78 -123
  6. package/dist/engine.d.ts +2 -0
  7. package/dist/engine.js +34 -12
  8. package/dist/events.d.ts +0 -3
  9. package/dist/graphics/canvas.d.ts +15 -7
  10. package/dist/graphics/canvas.js +64 -68
  11. package/dist/graphics/graphics.d.ts +53 -38
  12. package/dist/graphics/graphics.js +98 -70
  13. package/dist/input/controllerdb.json +1 -1
  14. package/dist/input/gamepad-mapping.js +2 -1
  15. package/dist/input/input.d.ts +2 -2
  16. package/dist/input/input.js +2 -2
  17. package/dist/like.d.ts +36 -5
  18. package/dist/math/vector2.d.ts +1 -1
  19. package/dist/prefab-scenes/mapGamepad.d.ts +2 -3
  20. package/dist/prefab-scenes/mapGamepad.js +17 -23
  21. package/dist/prefab-scenes/startScreen.d.ts +2 -3
  22. package/dist/prefab-scenes/startScreen.js +41 -12
  23. package/dist/scene.d.ts +47 -7
  24. package/package.json +2 -1
  25. package/dist/__benchmarks__/vector2.bench.d.ts +0 -2
  26. package/dist/__benchmarks__/vector2.bench.d.ts.map +0 -1
  27. package/dist/__benchmarks__/vector2.bench.js +0 -74
  28. package/dist/audio/audio.d.ts.map +0 -1
  29. package/dist/audio/index.d.ts.map +0 -1
  30. package/dist/engine.d.ts.map +0 -1
  31. package/dist/events.d.ts.map +0 -1
  32. package/dist/graphics/canvas.d.ts.map +0 -1
  33. package/dist/graphics/graphics.d.ts.map +0 -1
  34. package/dist/graphics/image.d.ts.map +0 -1
  35. package/dist/graphics/index.d.ts.map +0 -1
  36. package/dist/index.d.ts.map +0 -1
  37. package/dist/input/gamepad-mapping.d.ts.map +0 -1
  38. package/dist/input/gamepad.d.ts.map +0 -1
  39. package/dist/input/index.d.ts.map +0 -1
  40. package/dist/input/input.d.ts.map +0 -1
  41. package/dist/input/keyboard.d.ts.map +0 -1
  42. package/dist/input/mouse.d.ts.map +0 -1
  43. package/dist/like.d.ts.map +0 -1
  44. package/dist/math/index.d.ts.map +0 -1
  45. package/dist/math/rect.d.ts.map +0 -1
  46. package/dist/math/vector2.d.ts.map +0 -1
  47. package/dist/prefab-scenes/index.d.ts.map +0 -1
  48. package/dist/prefab-scenes/mapGamepad.d.ts.map +0 -1
  49. package/dist/prefab-scenes/startScreen.d.ts.map +0 -1
  50. package/dist/scene.d.ts.map +0 -1
  51. package/dist/timer/index.d.ts.map +0 -1
  52. package/dist/timer/timer.d.ts.map +0 -1
@@ -26,7 +26,7 @@
26
26
  * - Y increases down
27
27
  * - Angles in radians, 0 is right, positive is clockwise
28
28
  */
29
- import { type Vector2 } from "../math/vector2";
29
+ import type { Vector2 } from "../math/vector2";
30
30
  import type { Rectangle } from "../math/rect";
31
31
  import { ImageHandle } from "./image";
32
32
  export type DrawMode = "fill" | "line";
@@ -36,23 +36,25 @@ export type DrawMode = "fill" | "line";
36
36
  * - CSS color strings also accepted: `"red"`, `"#ff0000"`, `"rgb(255,0,0)"`
37
37
  */
38
38
  export type Color = [number, number, number, number?] | string;
39
+ export type TransformProps = {
40
+ angle?: number;
41
+ scale?: number | Vector2;
42
+ origin?: Vector2;
43
+ };
39
44
  export type ShapeProps = {
40
45
  lineWidth?: number;
41
46
  lineCap?: CanvasLineCap;
42
47
  lineJoin?: CanvasLineJoin;
43
48
  miterLimit?: number;
44
- };
49
+ } & TransformProps;
45
50
  export type DrawProps = ShapeProps & {
46
51
  quad?: Rectangle;
47
- r?: number;
48
- scale?: number | Vector2;
49
- origin?: number | Vector2;
50
52
  };
51
53
  export type PrintProps = {
52
54
  font?: string;
53
55
  width?: number;
54
56
  align?: CanvasTextAlign;
55
- };
57
+ } & TransformProps;
56
58
  /**
57
59
  * LIKE's way of drawing to the screen.
58
60
  *
@@ -71,18 +73,18 @@ export type PrintProps = {
71
73
  * const pos = Vec2.div(like.canvas.getSize(), 2); // calc center of screen
72
74
  * const speed = 0.5; // rotations per second
73
75
  *
74
- * like.gfx.push();
75
- * like.gfx.translate(pos);
76
- * like.gfx.rotate(like.timer.getTime() * Math.PI * 2.0 * speed);
77
- * like.gfx.scale(size);
78
- * like.gfx.circle("fill", color1, [0, 0], 2);
79
- * // use the arc parameter to fill in a semicircle. Note that it's clockwise from {x:1, y:0}.
80
- * like.gfx.circle("fill", color2, [0, 0], 2, { arc: [Math.PI/2, Math.PI*3/2] });
81
- * like.gfx.circle("fill", color2, [0, -1], 1);
82
- * like.gfx.circle("fill", color1, [0, 1], 1);
83
- * like.gfx.circle("fill", color2, [0, 1], 1/3);
84
- * like.gfx.circle("fill", color1, [0, -1], 1/3);
85
- * like.gfx.pop();
76
+ * like.gfx.withTransform(() => {
77
+ * like.gfx.translate(pos);
78
+ * like.gfx.rotate(like.timer.getTime() * Math.PI * 2.0 * speed);
79
+ * like.gfx.scale(size);
80
+ * like.gfx.circle("fill", color1, [0, 0], 2);
81
+ * // use the arc parameter to fill in a semicircle. Note that it's clockwise from {x:1, y:0}.
82
+ * like.gfx.circle("fill", color2, [0, 0], 2, { arc: [Math.PI/2, Math.PI*3/2] });
83
+ * like.gfx.circle("fill", color2, [0, -1], 1);
84
+ * like.gfx.circle("fill", color1, [0, 1], 1);
85
+ * like.gfx.circle("fill", color2, [0, 1], 1/3);
86
+ * like.gfx.circle("fill", color1, [0, -1], 1/3);
87
+ * })
86
88
  * }
87
89
  *
88
90
  * like.draw = (like: Like) => {
@@ -93,12 +95,6 @@ export type PrintProps = {
93
95
  export declare class Graphics {
94
96
  private ctx;
95
97
  constructor(ctx: CanvasRenderingContext2D);
96
- /**
97
- * Set the 2d drawing context for graphics.
98
- *
99
- * Be aware that that `like` can set this value at any time.
100
- */
101
- setContext(ctx: CanvasRenderingContext2D): void;
102
98
  /**
103
99
  * Clears the canvas with a solid color.
104
100
  * @param color Fill color.
@@ -121,7 +117,7 @@ export declare class Graphics {
121
117
  * @param radii Radius (number) or [rx, ry] for ellipse.
122
118
  * @param props Optional arc, center, and stroke properties. Center is true by default.
123
119
  */
124
- circle(mode: DrawMode, color: Color, position: Vector2, radii: number | Vector2, props?: ShapeProps & {
120
+ circle(mode: DrawMode, color: Color, position: Vector2, radius: number, props?: ShapeProps & {
125
121
  arc?: [number, number];
126
122
  center?: boolean;
127
123
  }): void;
@@ -155,7 +151,6 @@ export declare class Graphics {
155
151
  *
156
152
  * @remarks named "draw" because it draws anything _drawable_
157
153
  * in the long run.
158
- *
159
154
 
160
155
  * @param handle Image handle from newImage.
161
156
  * @param position Draw position.
@@ -164,37 +159,32 @@ export declare class Graphics {
164
159
  draw(handle: ImageHandle, position: Vector2, props?: DrawProps): void;
165
160
  /**
166
161
  * Loads an image from a path.
167
- * Unlike built-in loading, this pretends to be synchronous.
162
+ * Unlike fetch, this pretends to be synchronous.
168
163
 
169
164
  * @param path Image file path.
170
165
  * @returns ImageHandle for use with draw.
171
166
  */
172
167
  newImage(path: string): ImageHandle;
173
- /**
174
- * Sets the clipping region.
175
-
176
- * @param rect Clipping rectangle, or full canvas if omitted.
177
- */
178
- clip(rect?: Rectangle): void;
179
168
  /**
180
169
  * Draws a polygon.
181
-
170
+
182
171
  * @param mode Fill or line.
183
172
  * @param color Fill or stroke color.
184
173
  * @param points Array of [x, y] vertices.
185
- * @param props Optional stroke properties.
174
+ * @param props Optional stroke and transform properties.
186
175
  */
187
- polygon(mode: DrawMode, color: Color, points: Vector2[], props?: ShapeProps): void;
176
+ polygon(mode: DrawMode, color: Color, position: Vector2, points: Vector2[], props?: ShapeProps): void;
188
177
  /**
189
178
  * Draws individual pixels.
190
179
 
191
180
  * @param color Fill color.
192
181
  * @param pts Array of [x, y] positions.
193
182
  */
194
- points(color: Color, pts: Vector2[]): void;
183
+ points(color: Color, pts: Vector2[], props?: TransformProps): void;
184
+ private applyTransform;
195
185
  /**
196
186
  * Saves canvas state.
197
-
187
+
198
188
  */
199
189
  push(): void;
200
190
  /**
@@ -220,5 +210,30 @@ export declare class Graphics {
220
210
  * @param factor Scale factor (number or [x, y]).
221
211
  */
222
212
  scale(factor: number | Vector2): void;
213
+ /**
214
+ * The idiomatic way to render to an external canvas.
215
+ *
216
+ * Within this scope, the target canvas has changed.
217
+ *
218
+ * Outside, it stays the same.
219
+ *
220
+ * @param canvas The canvas to draw to.
221
+ * @param callback Functions that will be called while drawing to the target.
222
+ */
223
+ withRenderTarget(context: CanvasRenderingContext2D, callback: () => void): void;
224
+ /**
225
+ * _For Expressive Purposes_
226
+ *
227
+ * A simple wrapper around push/pop (save/restore)
228
+ * that clearly allows a set of statements to have their
229
+ * own transform matrix.
230
+ *
231
+ * In other words, any `scale`, `translate`, etc.
232
+ * performed in this block does not affect the
233
+ * outside world.
234
+ *
235
+ * @param callback the drawing logic.
236
+ */
237
+ withTransform(callback: () => void): void;
223
238
  }
224
239
  //# sourceMappingURL=graphics.d.ts.map
@@ -26,7 +26,6 @@
26
26
  * - Y increases down
27
27
  * - Angles in radians, 0 is right, positive is clockwise
28
28
  */
29
- import { Vec2 } from "../math/vector2";
30
29
  import { ImageHandle } from "./image";
31
30
  function parseColor(color) {
32
31
  if (typeof color === "string")
@@ -82,18 +81,18 @@ function getFontHeight(ctx) {
82
81
  * const pos = Vec2.div(like.canvas.getSize(), 2); // calc center of screen
83
82
  * const speed = 0.5; // rotations per second
84
83
  *
85
- * like.gfx.push();
86
- * like.gfx.translate(pos);
87
- * like.gfx.rotate(like.timer.getTime() * Math.PI * 2.0 * speed);
88
- * like.gfx.scale(size);
89
- * like.gfx.circle("fill", color1, [0, 0], 2);
90
- * // use the arc parameter to fill in a semicircle. Note that it's clockwise from {x:1, y:0}.
91
- * like.gfx.circle("fill", color2, [0, 0], 2, { arc: [Math.PI/2, Math.PI*3/2] });
92
- * like.gfx.circle("fill", color2, [0, -1], 1);
93
- * like.gfx.circle("fill", color1, [0, 1], 1);
94
- * like.gfx.circle("fill", color2, [0, 1], 1/3);
95
- * like.gfx.circle("fill", color1, [0, -1], 1/3);
96
- * like.gfx.pop();
84
+ * like.gfx.withTransform(() => {
85
+ * like.gfx.translate(pos);
86
+ * like.gfx.rotate(like.timer.getTime() * Math.PI * 2.0 * speed);
87
+ * like.gfx.scale(size);
88
+ * like.gfx.circle("fill", color1, [0, 0], 2);
89
+ * // use the arc parameter to fill in a semicircle. Note that it's clockwise from {x:1, y:0}.
90
+ * like.gfx.circle("fill", color2, [0, 0], 2, { arc: [Math.PI/2, Math.PI*3/2] });
91
+ * like.gfx.circle("fill", color2, [0, -1], 1);
92
+ * like.gfx.circle("fill", color1, [0, 1], 1);
93
+ * like.gfx.circle("fill", color2, [0, 1], 1/3);
94
+ * like.gfx.circle("fill", color1, [0, -1], 1/3);
95
+ * })
97
96
  * }
98
97
  *
99
98
  * like.draw = (like: Like) => {
@@ -110,14 +109,6 @@ export class Graphics {
110
109
  value: ctx
111
110
  });
112
111
  }
113
- /**
114
- * Set the 2d drawing context for graphics.
115
- *
116
- * Be aware that that `like` can set this value at any time.
117
- */
118
- setContext(ctx) {
119
- this.ctx = ctx;
120
- }
121
112
  /**
122
113
  * Clears the canvas with a solid color.
123
114
  * @param color Fill color.
@@ -135,15 +126,21 @@ export class Graphics {
135
126
  */
136
127
  rectangle(mode, color, rect, props) {
137
128
  const c = applyColor(color);
129
+ this.ctx.save();
130
+ const [x, y, w, h] = rect;
131
+ if (mode === "line") {
132
+ setStrokeProps(this.ctx, props);
133
+ }
134
+ this.applyTransform([x, y], props);
138
135
  if (mode === "fill") {
139
136
  this.ctx.fillStyle = c;
140
- this.ctx.fillRect(...rect);
137
+ this.ctx.fillRect(0, 0, w, h);
141
138
  }
142
139
  else {
143
- setStrokeProps(this.ctx, props);
144
140
  this.ctx.strokeStyle = c;
145
- this.ctx.strokeRect(...rect);
141
+ this.ctx.strokeRect(0, 0, w, h);
146
142
  }
143
+ this.ctx.restore();
147
144
  }
148
145
  /**
149
146
  * Draws a circle or ellipse.
@@ -154,23 +151,20 @@ export class Graphics {
154
151
  * @param radii Radius (number) or [rx, ry] for ellipse.
155
152
  * @param props Optional arc, center, and stroke properties. Center is true by default.
156
153
  */
157
- circle(mode, color, position, radii, props) {
154
+ circle(mode, color, position, radius, props) {
158
155
  const center = (props && 'center' in props) ? props.center : true;
159
156
  const c = applyColor(color);
160
- const size = typeof radii === "number" ? [radii, radii] : radii;
161
157
  const [startAngle, endAngle] = props?.arc ?? [0, Math.PI * 2];
158
+ this.ctx.save();
159
+ this.applyTransform(position, props);
162
160
  if (!center) {
163
- position = Vec2.add(position, size);
161
+ this.ctx.translate(radius, radius);
164
162
  }
165
- this.ctx.save();
166
- this.ctx.translate(...position);
167
- this.ctx.scale(...size);
168
163
  this.ctx.beginPath();
169
- this.ctx.arc(0, 0, 1, startAngle, endAngle);
164
+ this.ctx.arc(0, 0, radius, startAngle, endAngle);
170
165
  if (mode == 'fill')
171
166
  this.ctx.lineTo(0, 0);
172
167
  this.ctx.closePath();
173
- this.ctx.restore();
174
168
  if (mode === "fill") {
175
169
  this.ctx.fillStyle = c;
176
170
  this.ctx.fill();
@@ -180,6 +174,7 @@ export class Graphics {
180
174
  this.ctx.strokeStyle = c;
181
175
  this.ctx.stroke();
182
176
  }
177
+ this.ctx.restore();
183
178
  }
184
179
  /**
185
180
  * Draws connected line segments.
@@ -216,10 +211,11 @@ export class Graphics {
216
211
  * @param props {@link PrintProps} Optional font, text limit, or alignment.
217
212
  */
218
213
  print(color, text, position, props) {
219
- const [x, y] = position;
220
214
  const { font = "16px sans-serif" } = props ?? {};
221
- this.ctx.fillStyle = parseColor(color);
215
+ this.ctx.save();
216
+ this.applyTransform(position, props);
222
217
  this.ctx.font = font;
218
+ this.ctx.fillStyle = parseColor(color);
223
219
  this.ctx.textAlign = props?.align ?? "left";
224
220
  if (props && 'width' in props) {
225
221
  const { width } = props;
@@ -227,20 +223,20 @@ export class Graphics {
227
223
  const lineHeight = getFontHeight(this.ctx);
228
224
  this.ctx.textBaseline = "top";
229
225
  lines.forEach((line, i) => {
230
- this.ctx.fillText(line, x, y + i * lineHeight, width);
226
+ this.ctx.fillText(line, 0, i * lineHeight, width);
231
227
  });
232
228
  this.ctx.textBaseline = "alphabetic";
233
229
  }
234
230
  else {
235
- this.ctx.fillText(text, x, y);
231
+ this.ctx.fillText(text, 0, 0);
236
232
  }
233
+ this.ctx.restore();
237
234
  }
238
235
  /**
239
236
  * Draws an image.
240
237
  *
241
238
  * @remarks named "draw" because it draws anything _drawable_
242
239
  * in the long run.
243
- *
244
240
 
245
241
  * @param handle Image handle from newImage.
246
242
  * @param position Draw position.
@@ -252,26 +248,21 @@ export class Graphics {
252
248
  const element = handle.getElement();
253
249
  if (!element)
254
250
  return;
255
- const [x, y] = position;
256
- const { r = 0, scale = 1, origin = 0, quad } = props ?? {};
257
- const [sx, sy] = typeof scale === "number" ? [scale, scale] : scale;
258
- const [ox, oy] = typeof origin === "number" ? [origin, origin] : origin;
251
+ const { quad } = props ?? {};
259
252
  this.ctx.save();
260
- this.ctx.translate(x, y);
261
- this.ctx.rotate(r);
262
- this.ctx.scale(sx, sy);
253
+ this.applyTransform(position, props);
263
254
  if (quad) {
264
- const [qx, qy, qw, qh] = quad;
265
- this.ctx.drawImage(element, qx, qy, qw, qh, -ox, -oy, qw, qh);
255
+ const [, , qw, qh] = quad;
256
+ this.ctx.drawImage(element, ...quad, 0, 0, qw, qh);
266
257
  }
267
258
  else {
268
- this.ctx.drawImage(element, -ox, -oy);
259
+ this.ctx.drawImage(element, 0, 0);
269
260
  }
270
261
  this.ctx.restore();
271
262
  }
272
263
  /**
273
264
  * Loads an image from a path.
274
- * Unlike built-in loading, this pretends to be synchronous.
265
+ * Unlike fetch, this pretends to be synchronous.
275
266
 
276
267
  * @param path Image file path.
277
268
  * @returns ImageHandle for use with draw.
@@ -279,34 +270,23 @@ export class Graphics {
279
270
  newImage(path) {
280
271
  return new ImageHandle(path);
281
272
  }
282
- /**
283
- * Sets the clipping region.
284
-
285
- * @param rect Clipping rectangle, or full canvas if omitted.
286
- */
287
- clip(rect) {
288
- this.ctx.beginPath();
289
- if (rect) {
290
- const [x, y, w, h] = rect;
291
- this.ctx.rect(x, y, w, h);
292
- }
293
- else {
294
- this.ctx.rect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
295
- }
296
- this.ctx.clip();
297
- }
298
273
  /**
299
274
  * Draws a polygon.
300
-
275
+
301
276
  * @param mode Fill or line.
302
277
  * @param color Fill or stroke color.
303
278
  * @param points Array of [x, y] vertices.
304
- * @param props Optional stroke properties.
279
+ * @param props Optional stroke and transform properties.
305
280
  */
306
- polygon(mode, color, points, props) {
281
+ polygon(mode, color, position, points, props) {
307
282
  if (points.length < 3)
308
283
  return;
309
284
  const c = applyColor(color);
285
+ this.ctx.save();
286
+ if (mode === "line") {
287
+ setStrokeProps(this.ctx, props);
288
+ }
289
+ this.applyTransform(position, props);
310
290
  this.ctx.beginPath();
311
291
  const [[x0, y0], ...rest] = points;
312
292
  this.ctx.moveTo(x0, y0);
@@ -317,10 +297,10 @@ export class Graphics {
317
297
  this.ctx.fill();
318
298
  }
319
299
  else {
320
- setStrokeProps(this.ctx, props);
321
300
  this.ctx.strokeStyle = c;
322
301
  this.ctx.stroke();
323
302
  }
303
+ this.ctx.restore();
324
304
  }
325
305
  /**
326
306
  * Draws individual pixels.
@@ -328,13 +308,27 @@ export class Graphics {
328
308
  * @param color Fill color.
329
309
  * @param pts Array of [x, y] positions.
330
310
  */
331
- points(color, pts) {
311
+ points(color, pts, props) {
312
+ this.ctx.save();
313
+ this.applyTransform([0, 0], props);
332
314
  this.ctx.fillStyle = applyColor(color);
333
315
  pts.forEach(([x, y]) => this.ctx.fillRect(x, y, 1, 1));
316
+ this.ctx.restore();
317
+ }
318
+ applyTransform(position, props) {
319
+ const { angle = 0, scale = 1, origin = [0, 0] } = props ?? {};
320
+ this.ctx.translate(...position);
321
+ if (angle !== 0)
322
+ this.ctx.rotate(angle);
323
+ if (scale !== 1) {
324
+ const [sx, sy] = typeof scale === "number" ? [scale, scale] : scale;
325
+ this.ctx.scale(sx, sy);
326
+ }
327
+ this.ctx.translate(-origin[0], -origin[1]);
334
328
  }
335
329
  /**
336
330
  * Saves canvas state.
337
-
331
+
338
332
  */
339
333
  push() {
340
334
  this.ctx.save();
@@ -372,5 +366,39 @@ export class Graphics {
372
366
  const [sx, sy] = typeof factor === "number" ? [factor, factor] : factor;
373
367
  this.ctx.scale(sx, sy);
374
368
  }
369
+ /**
370
+ * The idiomatic way to render to an external canvas.
371
+ *
372
+ * Within this scope, the target canvas has changed.
373
+ *
374
+ * Outside, it stays the same.
375
+ *
376
+ * @param canvas The canvas to draw to.
377
+ * @param callback Functions that will be called while drawing to the target.
378
+ */
379
+ withRenderTarget(context, callback) {
380
+ const oldCtx = this.ctx;
381
+ this.ctx = context;
382
+ callback();
383
+ this.ctx = oldCtx;
384
+ }
385
+ /**
386
+ * _For Expressive Purposes_
387
+ *
388
+ * A simple wrapper around push/pop (save/restore)
389
+ * that clearly allows a set of statements to have their
390
+ * own transform matrix.
391
+ *
392
+ * In other words, any `scale`, `translate`, etc.
393
+ * performed in this block does not affect the
394
+ * outside world.
395
+ *
396
+ * @param callback the drawing logic.
397
+ */
398
+ withTransform(callback) {
399
+ this.ctx.save();
400
+ callback();
401
+ this.ctx.restore();
402
+ }
375
403
  }
376
404
  ;