pxt-common-packages 10.3.5 → 10.3.7

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 (40) hide show
  1. package/libs/azureiot/built/debug/binary.js +461 -461
  2. package/libs/color/built/debug/binary.js +8 -8
  3. package/libs/color-sensor/built/debug/binary.js +8 -8
  4. package/libs/controller/built/debug/binary.js +7239 -7205
  5. package/libs/controller---none/built/debug/binary.js +7219 -7185
  6. package/libs/datalogger/built/debug/binary.js +63 -63
  7. package/libs/edge-connector/built/debug/binary.js +8 -8
  8. package/libs/esp32/built/debug/binary.js +462 -462
  9. package/libs/game/animation.ts +403 -323
  10. package/libs/game/built/debug/binary.js +7158 -7124
  11. package/libs/game/controller.ts +6 -0
  12. package/libs/game/info.ts +9 -0
  13. package/libs/game/physics.ts +2 -2
  14. package/libs/lcd/built/debug/binary.js +8 -8
  15. package/libs/light-spectrum-sensor/built/debug/binary.js +8 -8
  16. package/libs/lora/built/debug/binary.js +8 -8
  17. package/libs/matrix-keypad/built/debug/binary.js +8 -8
  18. package/libs/mixer/instrument.ts +22 -3
  19. package/libs/mixer/sequencer.ts +2 -2
  20. package/libs/mqtt/built/debug/binary.js +176 -176
  21. package/libs/multiplayer/fieldEditors.ts +1 -9
  22. package/libs/multiplayer/images.ts +1 -1
  23. package/libs/multiplayer/player.ts +706 -0
  24. package/libs/multiplayer/pxt.json +1 -1
  25. package/libs/multiplayer/stateKind.ts +2 -2
  26. package/libs/net/built/debug/binary.js +176 -176
  27. package/libs/net-game/built/debug/binary.js +8747 -8713
  28. package/libs/palette/built/debug/binary.js +7153 -7119
  29. package/libs/pixel/built/debug/binary.js +8 -8
  30. package/libs/power/built/debug/binary.js +8 -8
  31. package/libs/proximity/built/debug/binary.js +8 -8
  32. package/libs/radio/built/debug/binary.js +8 -8
  33. package/libs/radio-broadcast/built/debug/binary.js +8 -8
  34. package/libs/rotary-encoder/built/debug/binary.js +8 -8
  35. package/libs/screen/built/debug/binary.js +50 -50
  36. package/libs/servo/built/debug/binary.js +8 -8
  37. package/libs/sprite-scaling/built/debug/binary.js +7157 -7123
  38. package/libs/storyboard/built/debug/binary.js +7157 -7123
  39. package/package.json +1 -1
  40. package/libs/multiplayer/mp.ts +0 -643
@@ -28,130 +28,87 @@ namespace animation {
28
28
  }
29
29
 
30
30
  export class Path {
31
- protected nodes: PathNode[];
32
- protected lastNode: number; // The index of the last node to fire
31
+ length: number;
33
32
 
34
- constructor() {
35
- this.nodes = [];
36
- this.lastNode = -1;
33
+ protected args: number[];
34
+ protected currentCommand: string;
35
+ protected lastControlX: number;
36
+ protected lastControlY: number;
37
+
38
+ protected startX: number;
39
+ protected startY: number;
40
+
41
+ protected lastX: number;
42
+ protected lastY: number;
43
+
44
+ protected strIndex: number;
45
+ protected commandIndex: number;
46
+
47
+ constructor(protected path: string) {
48
+ this.strIndex = 0;
49
+
50
+ // Run through the path once to get the length and check for errors
51
+ this.length = 0;
52
+ while (this.strIndex < this.path.length) {
53
+ this.readNextCommand();
54
+ if (this.currentCommand) this.length++;
55
+ }
56
+
57
+ this.reset();
37
58
  }
38
59
 
39
- private static generateNode(p0: Point, command: string, args: number[], metadata: [ Point, PathNode ]): PathNode {
40
- const [ pathStart, lastNode ] = metadata;
41
- let node: PathNode;
42
- switch (command) {
43
- case "M": { // M x y
44
- const p1 = new Point(args[0], args[1]);
45
- node = new MoveTo(p1);
46
- break;
47
- }
48
- case "m": { // m dx dy
49
- const p1 = new Point(p0.x + args[0], p0.y + args[1]);
50
- node = new MoveTo(p1);
51
- break;
52
- }
53
- case "L": { // L x y
54
- const p1 = new Point(args[0], args[1]);
55
- node = new LineTo(p0, p1);
56
- break;
57
- }
58
- case "l": { // l dx dy
59
- const p1 = new Point(p0.x + args[0], p0.y + args[1]);
60
- node = new LineTo(p0, p1);
61
- break;
62
- }
63
- case "H": { // H x
64
- const p1 = new Point(args[0], p0.y);
65
- node = new LineTo(p0, p1);
66
- break;
67
- }
68
- case "h": { // h dx
69
- const p1 = new Point(p0.x + args[0], p0.y);
70
- node = new LineTo(p0, p1);
71
- break;
72
- }
73
- case "V": { // V y
74
- const p1 = new Point(p0.x, args[0]);
75
- node = new LineTo(p0, p1);
76
- break;
77
- }
78
- case "v": { // v dy
79
- const p1 = new Point(p0.x, p0.y + args[0]);
80
- node = new LineTo(p0, p1);
81
- break;
82
- }
83
- case "Q": { // Q x1 y1 x2 y2
84
- const p1 = new Point(args[0], args[1]);
85
- const p2 = new Point(args[2], args[3]);
86
- node = new QuadraticCurveTo(p0, p1, p2);
87
- break;
88
- }
89
- case "q": { // q dx1 dy1 dx2 dy2
90
- const p1 = new Point(p0.x + args[0], p0.y + args[1]);
91
- const p2 = new Point(p0.x + args[2], p0.y + args[3]);
92
- node = new QuadraticCurveTo(p0, p1, p2);
93
- break;
94
- }
95
- case "T": { // T x2 y2
96
- let lastControlPoint: Point = lastNode.getLastControlPoint();
97
- if (!lastControlPoint) break;
60
+ protected readNextCommand() {
61
+ if (this.strIndex >= this.path.length) {
62
+ this.currentCommand = undefined;
63
+ return;
64
+ }
98
65
 
99
- const p1 = new Point(p0.x + (p0.x - lastControlPoint.x), p0.y + (p0.y - lastControlPoint.y));
100
- const p2 = new Point(args[0], args[1]);
101
- node = new QuadraticCurveTo(p0, p1, p2);
102
- break;
103
- }
104
- case "t": { // t dx2 dy2
105
- let lastControlPoint: Point = lastNode.getLastControlPoint();
106
- if (!lastControlPoint) break;
66
+ this.currentCommand = this.readNextToken();
107
67
 
108
- const p1 = new Point(p0.x + (p0.x - lastControlPoint.x), p0.y + (p0.y - lastControlPoint.y));
109
- const p2 = new Point(p0.x + args[0], p0.y + args[1]);
110
- node = new QuadraticCurveTo(p0, p1, p2);
111
- break;
112
- }
113
- case "C": { // C x1 y1 x2 y2 x3 y3
114
- const p1 = new Point(args[0], args[1]);
115
- const p2 = new Point(args[2], args[3]);
116
- const p3 = new Point(args[4], args[5]);
117
- node = new CubicCurveTo(p0, p1, p2, p3);
118
- break;
119
- }
120
- case "c": { // c dx1 dy1 dx2 dy2 dx3 dy3
121
- const p1 = new Point(p0.x + args[0], p0.y + args[1]);
122
- const p2 = new Point(p0.x + args[2], p0.y + args[3]);
123
- const p3 = new Point(p0.x + args[4], p0.y + args[5]);
124
- node = new CubicCurveTo(p0, p1, p2, p3);
125
- break;
126
- }
127
- case "S": { // S x2 y2 x3 y3
128
- let lastControlPoint: Point = lastNode.getLastControlPoint();
129
- if (!lastControlPoint) break;
130
-
131
- const p1 = new Point(p0.x + (p0.x - lastControlPoint.x), p0.y + (p0.y - lastControlPoint.y));
132
- const p2 = new Point(args[0], args[1]);
133
- const p3 = new Point(args[2], args[3]);
134
- node = new CubicCurveTo(p0, p1, p2, p3);
135
- break;
136
- }
137
- case "s": { // s dx2 dy2 dx3 dy3
138
- let lastControlPoint: Point = lastNode.getLastControlPoint();
139
- if (!lastControlPoint) break;
140
-
141
- const p1 = new Point(p0.x + (p0.x - lastControlPoint.x), p0.y + (p0.y - lastControlPoint.y));
142
- const p2 = new Point(p0.x + args[0], p0.y + args[1]);
143
- const p3 = new Point(p0.x + args[2], p0.y + args[3]);
144
- node = new CubicCurveTo(p0, p1, p2, p3);
145
- break;
146
- }
147
- case "Z": // Z
148
- case "z": { // z
149
- node = new LineTo(p0, pathStart);
150
- break;
151
- }
68
+ if (!this.currentCommand) return;
69
+
70
+ this.args = [];
71
+
72
+ const numArgs = Path.commandToArgCount(this.currentCommand);
73
+
74
+ if (numArgs === -1) throw "Unknown path command '" + this.currentCommand +"'";
75
+
76
+ for (let i = 0; i < numArgs; i++) {
77
+ this.args.push(parseFloat(this.readNextToken()))
78
+ }
79
+
80
+ for (const arg of this.args) {
81
+ if (Number.isNaN(arg)) throw "Invalid argument for path command '" + this.currentCommand + "'";
82
+ }
83
+ }
84
+
85
+ reset() {
86
+ this.args = undefined;
87
+ this.currentCommand = undefined;
88
+ this.lastControlX = undefined;
89
+ this.lastControlY = undefined;
90
+ this.startX = undefined;
91
+ this.startY = undefined;
92
+ this.lastX = undefined;
93
+ this.lastY = undefined;
94
+ this.strIndex = 0;
95
+ this.commandIndex = 0;
96
+ }
97
+
98
+ protected readNextToken() {
99
+ while (this.path.charCodeAt(this.strIndex) === 32 && this.strIndex < this.path.length) {
100
+ this.strIndex ++;
152
101
  }
153
102
 
154
- return node;
103
+ if (this.strIndex >= this.path.length) return undefined;
104
+
105
+ const tokenStart = this.strIndex;
106
+
107
+ while (this.path.charCodeAt(this.strIndex) !== 32 && this.strIndex < this.path.length) {
108
+ this.strIndex++;
109
+ }
110
+
111
+ return this.path.substr(tokenStart, this.strIndex - tokenStart);
155
112
  }
156
113
 
157
114
  private static commandToArgCount(command: string): number {
@@ -191,227 +148,344 @@ namespace animation {
191
148
  }
192
149
  }
193
150
 
194
- public static parse(pathStart: Point, pathString: string): Path {
195
- let path: Path = new Path();
196
- let p0: Point = pathStart;
197
-
198
- // This implementation of SVG parsing does not support the A/a commands, nor does it support exponents in arguments
199
- const digits = "0123456789";
200
- const separators = ", \t\n\r\f\v";
201
- const signs = "+-";
202
-
203
- let currentArg: string = "";
204
- let command: string = null;
205
- let args: number[] = [];
206
-
207
- for (let i = 0; i < pathString.length; i++) {
208
- const char = pathString.charAt(i);
209
- const lastNode = path.nodes[path.nodes.length - 1];
210
-
211
- // This is an SVG path parser. It's kinda complicated. For each character, evaluate the following conditions:
212
- // - if it's a digit, add it to the current argument
213
- // - else if it's whitespace or newline, finish the current argument and prepare for the next one
214
- // - else if it's a command, complete the previous argument, and prepare for the next one
215
- // - if there's sufficient data to make a node during this step, create it and continue
216
- // - else if it's a plus/minus sign, and if it's the start of a new argument, add it to allow for positive/negative numbers
217
- // - if it's the end of the string, complete the current argument before proceeding to the next step
218
- // - if there's sufficient data to make a node after all of these steps, create it
219
- if (digits.indexOf(char) > -1) { // Parses number arguments
220
- currentArg += char;
221
- } else if (separators.indexOf(char) > -1 && currentArg) { // Terminates number arguments
222
- args.push(parseInt(currentArg));
223
- currentArg = "";
224
- } else if (this.commandToArgCount(char) > -1) { // Parses command arguments
225
- if (command && currentArg) {
226
- args.push(parseInt(currentArg));
227
-
228
- // Try to finish up this node, otherwise just toss it out
229
- if (command && args.length >= this.commandToArgCount(command)) {
230
- let node: PathNode = this.generateNode(p0, command, args, [
231
- pathStart,
232
- lastNode
233
- ]);
234
- path.add(node);
235
- p0 = node.getEndPoint(); // Set the start for the next node to the end of this node
236
- if (node.setStart) pathStart = p0; // If this is a move command, then this sets the new start of the path (for the Z/z command)
237
- }
238
-
239
- // Clean up before continuing
240
- command = "";
241
- args = [];
242
- currentArg = "";
243
- }
244
- command = char;
245
- } else if (signs.indexOf(char) > -1) { // Allows for positive/negative values
246
- if (currentArg) {
247
- args.push(parseInt(currentArg));
248
- currentArg = "";
249
- }
250
- currentArg = char;
251
- }
151
+ public run(interval: number, target: Sprite, runningTime: number): boolean {
152
+ const nodeIndex = Math.floor(runningTime / interval); // The current node
153
+ const nodeTime = runningTime % interval; // The time the current node has been animating
252
154
 
253
- // If the end of the path has been reached, cleans up the last argument before continuing parsing
254
- if (i === pathString.length - 1) {
255
- if (currentArg) {
256
- args.push(parseInt(currentArg));
257
- }
258
- }
155
+ if (this.startX === undefined) {
156
+ this.startX = target.x;
157
+ this.startY = target.y;
158
+ this.lastX = target.x;
159
+ this.lastY = target.y;
160
+ this.commandIndex = 0;
161
+ this.readNextCommand();
162
+ }
259
163
 
260
- // If the command has a sufficient amount of arguments, then create a node for it
261
- if (command && args.length >= this.commandToArgCount(command)) {
262
- // Generate the node
263
- let node: PathNode = this.generateNode(p0, command, args, [
264
- pathStart,
265
- lastNode
266
- ]);
267
- path.add(node);
268
- p0 = node.getEndPoint();
269
- if (node.setStart) pathStart = p0;
270
-
271
- // Reset and prepare for the next command
272
- command = "";
273
- args = [];
274
- currentArg = "";
164
+ while (this.commandIndex < nodeIndex) {
165
+ if (this.currentCommand) {
166
+ this.runCurrentCommand(target, interval, interval);
167
+ this.lastX = target.x;
168
+ this.lastY = target.y;
275
169
  }
170
+ this.commandIndex++
171
+ this.readNextCommand();
276
172
  }
277
173
 
278
- return path;
279
- }
280
-
281
- public add(node: PathNode) {
282
- this.nodes.push(node);
283
- }
284
-
285
- get length(): number {
286
- return this.nodes.length;
287
- }
288
-
289
- public run(interval: number, target: Sprite, startedAt: number): boolean {
290
- const runningTime = control.millis() - startedAt; // The time since the start of the path
291
- const nodeIndex = Math.floor(runningTime / interval); // The current node
292
- const nodeTime = runningTime % interval; // The time the current node has been animating
293
-
294
- if (this.lastNode > -1 && this.lastNode < nodeIndex && this.nodes.length) { // If the last node hasn't been completed yet
295
- this.nodes[this.lastNode].apply(target, interval, interval); // Applies the last state of the previous node in case it was missed (this makes sure all moveTos fire)
296
-
297
- if (nodeIndex >= this.nodes.length) return true; // Once the nodeIndex is past the last item of the array, only then end the animation
174
+ if (nodeIndex >= this.length) {
175
+ return true;
298
176
  }
299
- this.lastNode = nodeIndex;
300
177
 
301
- this.nodes[nodeIndex].apply(target, nodeTime, interval);
178
+ this.runCurrentCommand(target, nodeTime, interval);
302
179
  return false;
303
180
  }
304
- }
305
-
306
- export abstract class PathNode {
307
- setStart: boolean;
308
- constructor() {
309
- this.setStart = false;
310
- }
311
-
312
- apply(target: Sprite, nodeTime: number, interval: number) {};
313
-
314
- getLastControlPoint(): Point {
315
- return null;
316
- };
317
181
 
318
- getEndPoint(): Point {
319
- return null;
320
- };
321
- }
322
-
323
- export class MoveTo extends PathNode {
324
- constructor(public p1: Point) {
325
- super();
326
-
327
- this.setStart = true;
328
- }
329
-
330
- apply(target: Sprite, nodeTime: number, interval: number) {
331
- nodeTime >= interval && target.setPosition(this.p1.x, this.p1.y);
182
+ protected runCurrentCommand(target: Sprite, nodeTime: number, intervalTime: number) {
183
+ switch (this.currentCommand) {
184
+ case "M": // M x y
185
+ this.lastControlX = undefined;
186
+ this.lastControlY = undefined;
187
+ moveTo(
188
+ target,
189
+ nodeTime,
190
+ intervalTime,
191
+ this.args[0],
192
+ this.args[1]
193
+ );
194
+ break;
195
+ case "m": // m dx dy
196
+ this.lastControlX = undefined;
197
+ this.lastControlY = undefined;
198
+ moveTo(
199
+ target,
200
+ nodeTime,
201
+ intervalTime,
202
+ this.args[0] + this.lastX,
203
+ this.args[1] + this.lastY
204
+ );
205
+ break;
206
+ case "L": // L x y
207
+ this.lastControlX = undefined;
208
+ this.lastControlY = undefined;
209
+ lineTo(
210
+ target,
211
+ nodeTime,
212
+ intervalTime,
213
+ this.lastX,
214
+ this.lastY,
215
+ this.args[0],
216
+ this.args[1]
217
+ );
218
+ break;
219
+ case "l": // l dx dy
220
+ this.lastControlX = undefined;
221
+ this.lastControlY = undefined;
222
+ lineTo(
223
+ target,
224
+ nodeTime,
225
+ intervalTime,
226
+ this.lastX,
227
+ this.lastY,
228
+ this.args[0] + this.lastX,
229
+ this.args[1] + this.lastY
230
+ );
231
+ break;
232
+ case "H": // H x
233
+ this.lastControlX = undefined;
234
+ this.lastControlY = undefined;
235
+ lineTo(
236
+ target,
237
+ nodeTime,
238
+ intervalTime,
239
+ this.lastX,
240
+ this.lastY,
241
+ this.args[0],
242
+ this.lastY
243
+ );
244
+ break;
245
+ case "h": // h dx
246
+ this.lastControlX = undefined;
247
+ this.lastControlY = undefined;
248
+ lineTo(
249
+ target,
250
+ nodeTime,
251
+ intervalTime,
252
+ this.lastX,
253
+ this.lastY,
254
+ this.args[0] + this.lastX,
255
+ this.lastY
256
+ );
257
+ break;
258
+ case "V": // V y
259
+ this.lastControlX = undefined;
260
+ this.lastControlY = undefined;
261
+ lineTo(
262
+ target,
263
+ nodeTime,
264
+ intervalTime,
265
+ this.lastX,
266
+ this.lastY,
267
+ this.lastX,
268
+ this.args[0]
269
+ );
270
+ break;
271
+ case "v": // v dy
272
+ this.lastControlX = undefined;
273
+ this.lastControlY = undefined;
274
+ lineTo(
275
+ target,
276
+ nodeTime,
277
+ intervalTime,
278
+ this.lastX,
279
+ this.lastY,
280
+ this.lastX,
281
+ this.args[0] + this.lastY
282
+ );
283
+ break;
284
+ case "Q": // Q x1 y1 x2 y2
285
+ this.lastControlX = this.args[0];
286
+ this.lastControlY = this.args[1];
287
+ quadraticCurveTo(
288
+ target,
289
+ nodeTime,
290
+ intervalTime,
291
+ this.lastX,
292
+ this.lastY,
293
+ this.args[0],
294
+ this.args[1],
295
+ this.args[2],
296
+ this.args[3]
297
+ )
298
+ break;
299
+ case "q": // q dx1 dy1 dx2 dy2
300
+ this.lastControlX = this.args[0] + this.lastX;
301
+ this.lastControlY = this.args[1] + this.lastY;
302
+ quadraticCurveTo(
303
+ target,
304
+ nodeTime,
305
+ intervalTime,
306
+ this.lastX,
307
+ this.lastY,
308
+ this.args[0] + this.lastX,
309
+ this.args[1] + this.lastY,
310
+ this.args[2] + this.lastX,
311
+ this.args[3] + this.lastY
312
+ );
313
+ break;
314
+ case "T": // T x2 y2
315
+ this.ensureControlPoint();
316
+ quadraticCurveTo(
317
+ target,
318
+ nodeTime,
319
+ intervalTime,
320
+ this.lastX,
321
+ this.lastY,
322
+ this.lastX + this.lastX - this.lastControlX,
323
+ this.lastY + this.lastY - this.lastControlY,
324
+ this.args[0],
325
+ this.args[1],
326
+ );
327
+ if (nodeTime === intervalTime) {
328
+ this.lastControlX = this.lastX + this.lastX - this.lastControlX;
329
+ this.lastControlY = this.lastY + this.lastY - this.lastControlY;
330
+ }
331
+ break;
332
+ case "t": // t dx2 dy2
333
+ this.ensureControlPoint();
334
+ quadraticCurveTo(
335
+ target,
336
+ nodeTime,
337
+ intervalTime,
338
+ this.lastX,
339
+ this.lastY,
340
+ this.lastX + this.lastX - this.lastControlX,
341
+ this.lastY + this.lastY - this.lastControlY,
342
+ this.args[0] + this.lastX,
343
+ this.args[1] + this.lastY,
344
+ );
345
+ if (nodeTime === intervalTime) {
346
+ this.lastControlX = this.lastX + this.lastX - this.lastControlX;
347
+ this.lastControlY = this.lastY + this.lastY - this.lastControlY;
348
+ }
349
+ break;
350
+ case "C": // C x1 y1 x2 y2 x3 y3
351
+ this.lastControlX = this.args[2];
352
+ this.lastControlY = this.args[3];
353
+ cubicCurveTo(
354
+ target,
355
+ nodeTime,
356
+ intervalTime,
357
+ this.lastX,
358
+ this.lastY,
359
+ this.args[0],
360
+ this.args[1],
361
+ this.args[2],
362
+ this.args[3],
363
+ this.args[4],
364
+ this.args[5],
365
+ );
366
+ break;
367
+ case "c": // c dx1 dy1 dx2 dy2 dx3 dy3
368
+ this.lastControlX = this.args[2] + this.lastX;
369
+ this.lastControlY = this.args[3] + this.lastY;
370
+ cubicCurveTo(
371
+ target,
372
+ nodeTime,
373
+ intervalTime,
374
+ this.lastX,
375
+ this.lastY,
376
+ this.args[0] + this.lastX,
377
+ this.args[1] + this.lastY,
378
+ this.args[2] + this.lastX,
379
+ this.args[3] + this.lastY,
380
+ this.args[4] + this.lastX,
381
+ this.args[5] + this.lastY,
382
+ );
383
+ break;
384
+ case "S": // S x2 y2 x3 y3
385
+ this.ensureControlPoint();
386
+ cubicCurveTo(
387
+ target,
388
+ nodeTime,
389
+ intervalTime,
390
+ this.lastX,
391
+ this.lastY,
392
+ this.lastX + this.lastX - this.lastControlX,
393
+ this.lastY + this.lastY - this.lastControlY,
394
+ this.args[0],
395
+ this.args[1],
396
+ this.args[2],
397
+ this.args[3]
398
+ );
399
+ if (nodeTime === intervalTime) {
400
+ this.lastControlX = this.args[0];
401
+ this.lastControlY = this.args[1];
402
+ }
403
+ break;
404
+ case "s": // s dx2 dy2 dx3 dy3
405
+ this.ensureControlPoint();
406
+ cubicCurveTo(
407
+ target,
408
+ nodeTime,
409
+ intervalTime,
410
+ this.lastX,
411
+ this.lastY,
412
+ this.lastX + this.lastX - this.lastControlX,
413
+ this.lastY + this.lastY - this.lastControlY,
414
+ this.args[0] + this.lastX,
415
+ this.args[1] + this.lastY,
416
+ this.args[2] + this.lastX,
417
+ this.args[3] + this.lastY,
418
+ );
419
+ if (nodeTime === intervalTime) {
420
+ this.lastControlX = this.args[0] + this.lastX;
421
+ this.lastControlY = this.args[1] + this.lastY;
422
+ }
423
+ break;
424
+ case "Z": // Z
425
+ case "z": // z
426
+ this.lastControlX = undefined;
427
+ this.lastControlY = undefined;
428
+ lineTo(
429
+ target,
430
+ nodeTime,
431
+ intervalTime,
432
+ this.lastX,
433
+ this.lastY,
434
+ this.startX,
435
+ this.startY
436
+ );
437
+ break;
438
+ }
332
439
  }
333
440
 
334
- getEndPoint(): Point {
335
- return this.p1;
441
+ protected ensureControlPoint() {
442
+ if (this.lastControlX === undefined) throw "Invalid path command. S/s and T/t must follow either Q/q or C/c"
336
443
  }
337
444
  }
338
445
 
339
- export class LineTo extends PathNode {
340
- constructor(public p0: Point, public p1: Point) {
341
- super();
342
- }
343
-
344
- apply(target: Sprite, nodeTime: number, interval: number) {
345
- const x = Math.round(((this.p1.x - this.p0.x) / interval) * nodeTime) + this.p0.x;
346
- const y = Math.round(((this.p1.y - this.p0.y) / interval) * nodeTime) + this.p0.y;
347
-
348
- target.setPosition(x, y);
349
- }
350
-
351
- getEndPoint(): Point {
352
- return this.p1;
353
- }
446
+ function moveTo(target: Sprite, nodeTime: number, interval: number, x: number, y: number) {
447
+ if (nodeTime >= interval) target.setPosition(x, y);
354
448
  }
355
449
 
356
- export class QuadraticCurveTo extends PathNode {
357
- constructor(public p0: Point, public p1: Point, public p2: Point) {
358
- super();
359
- }
360
-
361
- apply(target: Sprite, nodeTime: number, interval: number) {
362
- const progress = nodeTime / interval;
363
- const diff = 1 - progress;
364
- const a = Math.pow(diff, 2);
365
- const b = 2 * diff * progress;
366
- const c = Math.pow(progress, 2);
367
-
368
- const x = Math.round(a * this.p0.x + b * this.p1.x + c * this.p2.x);
369
- const y = Math.round(a * this.p0.y + b * this.p1.y + c * this.p2.y);
370
-
371
- target.setPosition(x, y);
372
- }
373
-
374
- getLastControlPoint(): Point {
375
- return this.p1;
376
- }
377
-
378
- getEndPoint(): Point {
379
- return this.p2;
380
- }
450
+ function lineTo(target: Sprite, nodeTime: number, interval: number, x0: number, y0: number, x1: number, y1: number) {
451
+ target.setPosition(
452
+ Math.round(((x1 - x0) / interval) * nodeTime) + x0,
453
+ Math.round(((y1 - y0) / interval) * nodeTime) + y0
454
+ );
381
455
  }
382
456
 
383
- export class CubicCurveTo extends PathNode {
384
- constructor(public p0: Point, public p1: Point, public p2: Point, public p3: Point) {
385
- super();
386
- }
387
-
388
- apply(target: Sprite, nodeTime: number, interval: number) {
389
- const progress = nodeTime / interval;
390
- const diff = 1 - progress;
391
- const a = Math.pow(diff, 3);
392
- const b = 3 * Math.pow(diff, 2) * progress;
393
- const c = 3 * diff * Math.pow(progress, 2);
394
- const d = Math.pow(progress, 3);
395
-
396
- const x = Math.round(a * this.p0.x + b * this.p1.x + c * this.p2.x + d * this.p3.x);
397
- const y = Math.round(a * this.p0.y + b * this.p1.y + c * this.p2.y + d * this.p3.y);
398
-
399
- target.setPosition(x, y);
400
- }
401
-
402
- getLastControlPoint(): Point {
403
- return this.p2;
404
- }
457
+ function quadraticCurveTo(target: Sprite, nodeTime: number, interval: number, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number) {
458
+ const progress = nodeTime / interval;
459
+ const diff = 1 - progress;
460
+ const a = diff * diff;
461
+ const b = 2 * diff * progress;
462
+ const c = progress * progress;
463
+
464
+ target.setPosition(
465
+ Math.round(a * x0 + b * x1 + c * x2),
466
+ Math.round(a * y0 + b * y1 + c * y2)
467
+ );
468
+ }
405
469
 
406
- getEndPoint(): Point {
407
- return this.p3;
408
- }
470
+ function cubicCurveTo(target: Sprite, nodeTime: number, interval: number, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
471
+ const progress = nodeTime / interval;
472
+ const diff = 1 - progress;
473
+ const a = diff * diff * diff;
474
+ const b = 3 * diff * diff * progress;
475
+ const c = 3 * diff * progress * progress;
476
+ const d = progress * progress * progress;
477
+
478
+ target.setPosition(
479
+ Math.round(a * x0 + b * x1 + c * x2 + d * x3),
480
+ Math.round(a * y0 + b * y1 + c * y2 + d * y3)
481
+ );
409
482
  }
410
483
 
411
484
  export abstract class SpriteAnimation {
412
- protected startedAt: number;
485
+ protected elapsedTime: number;
413
486
 
414
487
  constructor(public sprite: Sprite, protected loop: boolean) {
488
+ this.elapsedTime = 0;
415
489
  }
416
490
 
417
491
  public init() {
@@ -457,10 +531,9 @@ namespace animation {
457
531
  }
458
532
 
459
533
  public update(): boolean {
460
- if (this.startedAt == null)
461
- this.startedAt = control.millis();
462
- const runningTime = control.millis() - this.startedAt;
463
- const frameIndex = Math.floor(runningTime / this.frameInterval);
534
+ this.elapsedTime += game.eventContext().deltaTimeMillis;
535
+
536
+ const frameIndex = Math.floor(this.elapsedTime / this.frameInterval);
464
537
 
465
538
  if (this.lastFrame != frameIndex && this.frames.length) {
466
539
  if (!this.loop && frameIndex >= this.frames.length) {
@@ -477,19 +550,26 @@ namespace animation {
477
550
  }
478
551
 
479
552
  export class MovementAnimation extends SpriteAnimation {
553
+ protected startX: number;
554
+ protected startY: number;
555
+
480
556
  constructor(sprite: Sprite, private path: Path, private nodeInterval: number, loop?: boolean) {
481
557
  super(sprite, loop);
482
-
483
- this.loop = loop;
558
+ this.startX = sprite.x;
559
+ this.startY = sprite.y;
560
+ this.elapsedTime = 0;
484
561
  }
485
562
 
486
563
  public update(): boolean {
487
- if (this.startedAt == null) this.startedAt = control.millis();
564
+ this.elapsedTime += game.eventContext().deltaTimeMillis;
488
565
 
489
- let result = this.path.run(this.nodeInterval, this.sprite, this.startedAt);
566
+ let result = this.path.run(this.nodeInterval, this.sprite, this.elapsedTime);
490
567
  if (result) {
491
568
  if (!this.loop) return true;
492
- this.startedAt = control.millis();
569
+ this.elapsedTime = 0;
570
+ this.path.reset();
571
+ this.sprite.x = this.startX;
572
+ this.sprite.y = this.startY;
493
573
  }
494
574
  return false;
495
575
  }
@@ -524,7 +604,7 @@ namespace animation {
524
604
  //% group="Animate"
525
605
  //% help=animation/run-movement-animation
526
606
  export function runMovementAnimation(sprite: Sprite, pathString: string, duration?: number, loop?: boolean) {
527
- const path = Path.parse(new Point(sprite.x, sprite.y), pathString);
607
+ const path = new Path(pathString);
528
608
  const anim = new MovementAnimation(sprite, path, duration / path.length, !!loop);
529
609
  anim.init();
530
610
  }