kei-lisp-plugin-graphics 3.0.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -13
- package/dist/index.cjs +303 -640
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -28
- package/dist/index.d.ts +28 -28
- package/dist/index.js +304 -641
- package/dist/index.js.map +1 -1
- package/lisp/animation.lisp +17 -0
- package/lisp/grid.lisp +26 -0
- package/lisp/palette.lisp +28 -0
- package/package.json +11 -10
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cons, InterpretedSymbol } from "kei-lisp";
|
|
1
|
+
import { Cons, EvalError, InterpretedSymbol, Numeric } from "kei-lisp";
|
|
2
2
|
//#region src/GraphicsPlugin/index.ts
|
|
3
3
|
const LINE_CAPS = /* @__PURE__ */ new Set([
|
|
4
4
|
"butt",
|
|
@@ -99,8 +99,15 @@ const COMPOSITE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
99
99
|
* @class
|
|
100
100
|
* @classdesc Canvas2D drawing plugin for the kei-lisp interpreter. Implements
|
|
101
101
|
* the `KeiLispPlugin` contract (`name` / `has` / `apply`) and
|
|
102
|
-
* exposes 75 `g…` Lisp functions
|
|
103
|
-
*
|
|
102
|
+
* exposes 75 `g…` Lisp functions that proxy to a 2D rendering
|
|
103
|
+
* context. Failures (wrong arity,
|
|
104
|
+
* type mismatch, closed canvas, canvas-level errors) signal an
|
|
105
|
+
* `EvalError` that Lisp callers can intercept with
|
|
106
|
+
* `(handler-case … (eval-error (e) …))`. Two exceptions still go
|
|
107
|
+
* to `process.stderr` as diagnostics: asynchronous work (image
|
|
108
|
+
* loading, OffscreenCanvas file writes), which fails after the
|
|
109
|
+
* call has returned, and `selectColor`'s best-effort color
|
|
110
|
+
* parsing, which falls back to black instead of failing.
|
|
104
111
|
* @author Keisuke Ikeda
|
|
105
112
|
*/
|
|
106
113
|
var GraphicsPlugin = class GraphicsPlugin {
|
|
@@ -163,8 +170,6 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
163
170
|
aTable.set(InterpretedSymbol.of("gtext-direction"), "gTextDirection");
|
|
164
171
|
aTable.set(InterpretedSymbol.of("gtext-font"), "gTextFont");
|
|
165
172
|
aTable.set(InterpretedSymbol.of("gtext-baseline"), "gTextBaseline");
|
|
166
|
-
aTable.set(InterpretedSymbol.of("gtext-dire"), "gTextDirection");
|
|
167
|
-
aTable.set(InterpretedSymbol.of("gtext-line"), "gTextBaseline");
|
|
168
173
|
aTable.set(InterpretedSymbol.of("gtranslate"), "gTranslate");
|
|
169
174
|
aTable.set(InterpretedSymbol.of("grect"), "gRect");
|
|
170
175
|
aTable.set(InterpretedSymbol.of("grotate"), "gRotate");
|
|
@@ -228,11 +233,13 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
228
233
|
/**
|
|
229
234
|
* Writes a diagnostic line to `process.stderr`, matching the convention
|
|
230
235
|
* used by kei-lisp itself (`Applier.format` writes to `process.stdout`).
|
|
231
|
-
*
|
|
232
|
-
* (
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
+
* Used only where an `EvalError` cannot reach the caller: asynchronous
|
|
237
|
+
* work (image loading), best-effort color parsing, and the informational
|
|
238
|
+
* line printed by `gopen`. In a Node runtime this hits the real stderr; in
|
|
239
|
+
* a browser kei-lisp host (e.g. kei-lisp-web) the host typically swaps
|
|
240
|
+
* `process.stderr.write` for a sink that routes to the REPL output panel.
|
|
241
|
+
* In a plain browser with no `process` shim at all, the line falls back to
|
|
242
|
+
* `console.error` instead of throwing.
|
|
236
243
|
* @param line - the line to write
|
|
237
244
|
*/
|
|
238
245
|
#print(line) {
|
|
@@ -246,7 +253,9 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
246
253
|
/**
|
|
247
254
|
* Resolves an image for the given source and runs `draw` with it —
|
|
248
255
|
* synchronously when the image is already loaded, on its `load` event
|
|
249
|
-
* otherwise. A load failure prints a diagnostic once
|
|
256
|
+
* otherwise. A load failure prints a diagnostic once (the failure happens
|
|
257
|
+
* asynchronously, after the calling `g…` function has already returned, so
|
|
258
|
+
* it cannot signal an `EvalError`).
|
|
250
259
|
* @param source - the image URL / data URI
|
|
251
260
|
* @param draw - the drawing action to run once the image is available
|
|
252
261
|
*/
|
|
@@ -269,26 +278,37 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
269
278
|
});
|
|
270
279
|
}
|
|
271
280
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
|
|
281
|
+
* Returns the 2D context, signaling an `EvalError` when the canvas exposes
|
|
282
|
+
* no usable context or has not been opened with `gopen`.
|
|
283
|
+
* @return the 2D rendering context
|
|
284
|
+
*/
|
|
285
|
+
#requireOpenContext() {
|
|
286
|
+
if (this.ctx === null) throw new EvalError("Unable to initialize canvas. The browser or machine may not support it.");
|
|
287
|
+
if (!this.isOpen) throw new EvalError("The canvas is closed and cannot be executed.");
|
|
288
|
+
return this.ctx;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Shared guard-and-dispatch skeleton for the `g…` methods: requires an
|
|
292
|
+
* open canvas context, runs `body`, and signals an `EvalError` carrying
|
|
293
|
+
* `failureMessage` when `body` returns `null` (bad arguments) or throws a
|
|
294
|
+
* canvas-level error. The error propagates through the evaluator, where
|
|
295
|
+
* Lisp callers can intercept it with
|
|
296
|
+
* `(handler-case … (eval-error (e) …))`.
|
|
297
|
+
* @param failureMessage - the message of the signaled `EvalError`
|
|
276
298
|
* @param body - the drawing action; returns the Lisp result, or `null` on
|
|
277
299
|
* bad arguments
|
|
278
|
-
* @return the body's result
|
|
300
|
+
* @return the body's result
|
|
279
301
|
*/
|
|
280
302
|
#execute(failureMessage, body) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
284
|
-
return Cons.nil;
|
|
285
|
-
}
|
|
303
|
+
const context = this.#requireOpenContext();
|
|
304
|
+
let result;
|
|
286
305
|
try {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
306
|
+
result = body(context);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
throw error instanceof EvalError ? error : new EvalError(failureMessage);
|
|
309
|
+
}
|
|
310
|
+
if (result === null) throw new EvalError(failureMessage);
|
|
311
|
+
return result;
|
|
292
312
|
}
|
|
293
313
|
/**
|
|
294
314
|
* Reads a single string argument and validates it against an allowlist.
|
|
@@ -302,7 +322,9 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
302
322
|
return allowed.has(arguments_.car) ? arguments_.car : null;
|
|
303
323
|
}
|
|
304
324
|
/**
|
|
305
|
-
* Reads exactly `count` numbers from the argument list.
|
|
325
|
+
* Reads exactly `count` numbers from the argument list. kei-lisp v3
|
|
326
|
+
* evaluates integers to `bigint` and exact division to `Rational`, so each
|
|
327
|
+
* value is converted to a JS float for the Canvas 2D API.
|
|
306
328
|
* @param arguments_ - the evaluated argument list
|
|
307
329
|
* @param count - the expected argument count
|
|
308
330
|
* @return the numbers, or `null` on wrong arity or a non-number argument
|
|
@@ -313,7 +335,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
313
335
|
let rest = arguments_;
|
|
314
336
|
for (let index = 0; index < count; index++) {
|
|
315
337
|
if (!Cons.isNumber(rest.car)) return null;
|
|
316
|
-
values.push(rest.car);
|
|
338
|
+
values.push(Numeric.toFloat(rest.car));
|
|
317
339
|
rest = rest.cdr;
|
|
318
340
|
}
|
|
319
341
|
return values;
|
|
@@ -358,7 +380,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
358
380
|
const offset = stops[index];
|
|
359
381
|
const color = stops[index + 1];
|
|
360
382
|
if (!Cons.isNumber(offset) || !Cons.isString(color)) return false;
|
|
361
|
-
gradient.addColorStop(offset, color);
|
|
383
|
+
gradient.addColorStop(Numeric.toFloat(offset), color);
|
|
362
384
|
}
|
|
363
385
|
return true;
|
|
364
386
|
}
|
|
@@ -383,13 +405,10 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
383
405
|
* no `document` — those callers must pass a file path instead.
|
|
384
406
|
* @param mimeType - the image MIME type to encode
|
|
385
407
|
* @param label - the format name used in diagnostics ("jpeg" / "png")
|
|
386
|
-
* @return `t` on success
|
|
408
|
+
* @return `t` on success
|
|
387
409
|
*/
|
|
388
410
|
#downloadCanvas(mimeType, label) {
|
|
389
|
-
if (typeof document === "undefined" || !("toDataURL" in this.canvas)) {
|
|
390
|
-
this.#print(`Can not save ${label}. Browser download needs a DOM and an HTMLCanvasElement; pass a file path to save on Node.js.`);
|
|
391
|
-
return Cons.nil;
|
|
392
|
-
}
|
|
411
|
+
if (typeof document === "undefined" || !("toDataURL" in this.canvas)) throw new EvalError(`Can not save ${label}. Browser download needs a DOM and an HTMLCanvasElement; pass a file path to save on Node.js.`);
|
|
393
412
|
try {
|
|
394
413
|
const link = document.createElement("a");
|
|
395
414
|
link.href = this.canvas.toDataURL(mimeType);
|
|
@@ -399,8 +418,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
399
418
|
link.remove();
|
|
400
419
|
return InterpretedSymbol.of("t");
|
|
401
420
|
} catch {
|
|
402
|
-
|
|
403
|
-
return Cons.nil;
|
|
421
|
+
throw new EvalError(`Can not save ${label}. If you are using an image in the canvas, you can't save ${label}.`);
|
|
404
422
|
}
|
|
405
423
|
}
|
|
406
424
|
/**
|
|
@@ -413,14 +431,12 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
413
431
|
* @param path - the destination file path
|
|
414
432
|
* @param mimeType - the image MIME type to encode
|
|
415
433
|
* @param label - the format name used in diagnostics ("jpeg" / "png")
|
|
416
|
-
* @return `t` on success
|
|
434
|
+
* @return `t` on success
|
|
417
435
|
*/
|
|
418
436
|
#writeCanvasToFile(path, mimeType, label) {
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return Cons.nil;
|
|
423
|
-
}
|
|
437
|
+
const getBuiltinModule = globalThis.process?.getBuiltinModule;
|
|
438
|
+
const fs = typeof getBuiltinModule === "function" ? getBuiltinModule("node:fs") : void 0;
|
|
439
|
+
if (fs === void 0) throw new EvalError(`Can not save ${label}. Saving to a file path requires Node.js.`);
|
|
424
440
|
try {
|
|
425
441
|
if ("toDataURL" in this.canvas) {
|
|
426
442
|
const dataUrl = this.canvas.toDataURL(mimeType);
|
|
@@ -430,11 +446,12 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
430
446
|
}
|
|
431
447
|
this.canvas.convertToBlob({ type: mimeType }).then(async (blob) => {
|
|
432
448
|
fs.writeFileSync(path, new Uint8Array(await blob.arrayBuffer()));
|
|
449
|
+
}).catch(() => {
|
|
450
|
+
this.#print(`Can not save ${label}.`);
|
|
433
451
|
});
|
|
434
452
|
return InterpretedSymbol.of("t");
|
|
435
453
|
} catch {
|
|
436
|
-
|
|
437
|
-
return Cons.nil;
|
|
454
|
+
throw new EvalError(`Can not save ${label}.`);
|
|
438
455
|
}
|
|
439
456
|
}
|
|
440
457
|
/**
|
|
@@ -450,127 +467,45 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
450
467
|
* @param aSymbol - the call symbol
|
|
451
468
|
* @param arguments_ - the evaluated argument list
|
|
452
469
|
* @param _context - the interpreter context (unused by this plugin)
|
|
453
|
-
* @return the method's result
|
|
470
|
+
* @return the method's result
|
|
454
471
|
*/
|
|
455
472
|
apply(aSymbol, arguments_, _context) {
|
|
456
473
|
const methodName = GraphicsPlugin.#builtInFunctions.get(aSymbol);
|
|
457
|
-
if (methodName === void 0) {
|
|
458
|
-
this.#print(`I could find no procedure description for ${String(aSymbol)}`);
|
|
459
|
-
return Cons.nil;
|
|
460
|
-
}
|
|
474
|
+
if (methodName === void 0) throw new EvalError(`I could find no procedure description for ${String(aSymbol)}`);
|
|
461
475
|
return this[methodName].call(this, arguments_);
|
|
462
476
|
}
|
|
463
|
-
/**
|
|
464
|
-
* Checks whether the canvas exposes a usable 2D context, and narrows
|
|
465
|
-
* `this.ctx` to non-null for the caller when it returns true.
|
|
466
|
-
* @return type predicate — true when context is non-null
|
|
467
|
-
*/
|
|
468
|
-
checkSupport() {
|
|
469
|
-
if (this.ctx === null) {
|
|
470
|
-
this.#print("Unable to initialize canvas. The browser or machine may not support it.");
|
|
471
|
-
return false;
|
|
472
|
-
}
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
477
|
gAlpha(arguments_) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
this.#print("Can not set alpha.");
|
|
484
|
-
return Cons.nil;
|
|
485
|
-
} catch {
|
|
486
|
-
this.#print("Can not set alpha.");
|
|
487
|
-
return Cons.nil;
|
|
488
|
-
}
|
|
489
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
490
|
-
return Cons.nil;
|
|
478
|
+
return this.#execute("Can not set alpha.", (context) => {
|
|
479
|
+
const a = this.#numbers(arguments_, 1);
|
|
480
|
+
if (a === null) return null;
|
|
481
|
+
context.globalAlpha = a[0] <= 0 ? 0 : a[0] >= 1 ? 1 : a[0];
|
|
482
|
+
return InterpretedSymbol.of("t");
|
|
483
|
+
});
|
|
491
484
|
}
|
|
492
485
|
gArc(arguments_) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const a2 = cdr2.car;
|
|
501
|
-
const cdr3 = cdr2.cdr;
|
|
502
|
-
const a3 = cdr3.car;
|
|
503
|
-
const cdr4 = cdr3.cdr;
|
|
504
|
-
const a4 = cdr4.car;
|
|
505
|
-
const a5 = cdr4.cdr.car;
|
|
506
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
507
|
-
const isAFlag = a5 >= 0;
|
|
508
|
-
this.ctx.arc(a0, a1, a2, Math.PI / 180 * a3, Math.PI / 180 * a4, isAFlag);
|
|
509
|
-
return InterpretedSymbol.of("t");
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
this.#print("Can not draw arc.");
|
|
513
|
-
return Cons.nil;
|
|
514
|
-
} catch {
|
|
515
|
-
this.#print("Can not draw arc.");
|
|
516
|
-
return Cons.nil;
|
|
517
|
-
}
|
|
518
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
519
|
-
return Cons.nil;
|
|
486
|
+
return this.#execute("Can not draw arc.", (context) => {
|
|
487
|
+
const a = this.#numbers(arguments_, 6);
|
|
488
|
+
if (a === null) return null;
|
|
489
|
+
const isAFlag = a[5] >= 0;
|
|
490
|
+
context.arc(a[0], a[1], a[2], Math.PI / 180 * a[3], Math.PI / 180 * a[4], isAFlag);
|
|
491
|
+
return InterpretedSymbol.of("t");
|
|
492
|
+
});
|
|
520
493
|
}
|
|
521
494
|
gArcTo(arguments_) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const cdr2 = cdr1.cdr;
|
|
529
|
-
const a2 = cdr2.car;
|
|
530
|
-
const cdr3 = cdr2.cdr;
|
|
531
|
-
const a3 = cdr3.car;
|
|
532
|
-
const a4 = cdr3.cdr.car;
|
|
533
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4)) {
|
|
534
|
-
this.ctx.arcTo(a0, a1, a2, a3, a4);
|
|
535
|
-
return InterpretedSymbol.of("t");
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
this.#print("Can not draw arc to.");
|
|
539
|
-
return Cons.nil;
|
|
540
|
-
} catch {
|
|
541
|
-
this.#print("Can not draw arc to.");
|
|
542
|
-
return Cons.nil;
|
|
543
|
-
}
|
|
544
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
545
|
-
return Cons.nil;
|
|
495
|
+
return this.#execute("Can not draw arc to.", (context) => {
|
|
496
|
+
const a = this.#numbers(arguments_, 5);
|
|
497
|
+
if (a === null) return null;
|
|
498
|
+
context.arcTo(a[0], a[1], a[2], a[3], a[4]);
|
|
499
|
+
return InterpretedSymbol.of("t");
|
|
500
|
+
});
|
|
546
501
|
}
|
|
547
502
|
gBezCurveTo(arguments_) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const cdr2 = cdr1.cdr;
|
|
555
|
-
const a2 = cdr2.car;
|
|
556
|
-
const cdr3 = cdr2.cdr;
|
|
557
|
-
const a3 = cdr3.car;
|
|
558
|
-
const cdr4 = cdr3.cdr;
|
|
559
|
-
const a4 = cdr4.car;
|
|
560
|
-
const a5 = cdr4.cdr.car;
|
|
561
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
562
|
-
this.ctx.bezierCurveTo(a0, a1, a2, a3, a4, a5);
|
|
563
|
-
return InterpretedSymbol.of("t");
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
this.#print("Can not draw bezier curve.");
|
|
567
|
-
return Cons.nil;
|
|
568
|
-
} catch {
|
|
569
|
-
this.#print("Can not draw bezier curve.");
|
|
570
|
-
return Cons.nil;
|
|
571
|
-
}
|
|
572
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
573
|
-
return Cons.nil;
|
|
503
|
+
return this.#execute("Can not draw bezier curve.", (context) => {
|
|
504
|
+
const a = this.#numbers(arguments_, 6);
|
|
505
|
+
if (a === null) return null;
|
|
506
|
+
context.bezierCurveTo(a[0], a[1], a[2], a[3], a[4], a[5]);
|
|
507
|
+
return InterpretedSymbol.of("t");
|
|
508
|
+
});
|
|
574
509
|
}
|
|
575
510
|
gClear(arguments_) {
|
|
576
511
|
return this.#execute("Can not clear.", (context) => {
|
|
@@ -582,89 +517,48 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
582
517
|
return InterpretedSymbol.of("t");
|
|
583
518
|
});
|
|
584
519
|
}
|
|
585
|
-
gClose() {
|
|
586
|
-
if (
|
|
587
|
-
if (
|
|
520
|
+
gClose(arguments_ = Cons.nil) {
|
|
521
|
+
if (this.ctx === null) throw new EvalError("Unable to initialize canvas. The browser or machine may not support it.");
|
|
522
|
+
if (arguments_.length() !== 0) throw new EvalError("Can not close.");
|
|
523
|
+
if (!this.isOpen) throw new EvalError("The canvas has already been closed.");
|
|
524
|
+
try {
|
|
588
525
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
589
|
-
this.isOpen = false;
|
|
590
|
-
return InterpretedSymbol.of("t");
|
|
591
526
|
} catch {
|
|
592
|
-
|
|
593
|
-
return Cons.nil;
|
|
527
|
+
throw new EvalError("Can not close.");
|
|
594
528
|
}
|
|
595
|
-
this
|
|
596
|
-
return
|
|
529
|
+
this.isOpen = false;
|
|
530
|
+
return InterpretedSymbol.of("t");
|
|
597
531
|
}
|
|
598
532
|
gColor(arguments_) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
this.#print("Can not set color.");
|
|
608
|
-
return Cons.nil;
|
|
609
|
-
} catch {
|
|
610
|
-
this.#print("Can not set color.");
|
|
611
|
-
return Cons.nil;
|
|
612
|
-
}
|
|
613
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
614
|
-
return Cons.nil;
|
|
533
|
+
return this.#execute("Can not set color.", (context) => {
|
|
534
|
+
if (arguments_.length() < 1) return null;
|
|
535
|
+
const aColor = this.selectColor(arguments_);
|
|
536
|
+
context.fillStyle = aColor;
|
|
537
|
+
context.strokeStyle = aColor;
|
|
538
|
+
return InterpretedSymbol.of("t");
|
|
539
|
+
});
|
|
615
540
|
}
|
|
616
|
-
gFill() {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
541
|
+
gFill(arguments_ = Cons.nil) {
|
|
542
|
+
return this.#execute("Can not fill.", (context) => {
|
|
543
|
+
if (arguments_.length() !== 0) return null;
|
|
544
|
+
context.fill();
|
|
620
545
|
return InterpretedSymbol.of("t");
|
|
621
|
-
}
|
|
622
|
-
this.#print("Can not fill.");
|
|
623
|
-
return Cons.nil;
|
|
624
|
-
}
|
|
625
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
626
|
-
return Cons.nil;
|
|
546
|
+
});
|
|
627
547
|
}
|
|
628
548
|
gFillColor(arguments_) {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return InterpretedSymbol.of("t");
|
|
635
|
-
}
|
|
636
|
-
this.#print("Can not set fill color.");
|
|
637
|
-
return Cons.nil;
|
|
638
|
-
} catch {
|
|
639
|
-
this.#print("Can not set fill color.");
|
|
640
|
-
return Cons.nil;
|
|
641
|
-
}
|
|
642
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
643
|
-
return Cons.nil;
|
|
549
|
+
return this.#execute("Can not set fill color.", (context) => {
|
|
550
|
+
if (arguments_.length() < 1) return null;
|
|
551
|
+
context.fillStyle = this.selectColor(arguments_);
|
|
552
|
+
return InterpretedSymbol.of("t");
|
|
553
|
+
});
|
|
644
554
|
}
|
|
645
555
|
gFillRect(arguments_) {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const cdr2 = cdr1.cdr;
|
|
653
|
-
const a2 = cdr2.car;
|
|
654
|
-
const a3 = cdr2.cdr.car;
|
|
655
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
656
|
-
this.ctx.fillRect(a0, a1, a2, a3);
|
|
657
|
-
return InterpretedSymbol.of("t");
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
this.#print("Can not draw fill rectangle.");
|
|
661
|
-
return Cons.nil;
|
|
662
|
-
} catch {
|
|
663
|
-
this.#print("Can not draw fill rectangle.");
|
|
664
|
-
return Cons.nil;
|
|
665
|
-
}
|
|
666
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
667
|
-
return Cons.nil;
|
|
556
|
+
return this.#execute("Can not draw fill rectangle.", (context) => {
|
|
557
|
+
const a = this.#numbers(arguments_, 4);
|
|
558
|
+
if (a === null) return null;
|
|
559
|
+
context.fillRect(a[0], a[1], a[2], a[3]);
|
|
560
|
+
return InterpretedSymbol.of("t");
|
|
561
|
+
});
|
|
668
562
|
}
|
|
669
563
|
gFillText(arguments_) {
|
|
670
564
|
return this.#execute("Can not draw fill text.", (context) => {
|
|
@@ -674,54 +568,29 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
674
568
|
if (!Cons.isString(a0) || !Cons.isNumber(a1) || !Cons.isNumber(a2)) return null;
|
|
675
569
|
if (length_ === 4) {
|
|
676
570
|
if (!Cons.isNumber(a3)) return null;
|
|
677
|
-
context.fillText(a0, a1, a2, a3);
|
|
678
|
-
} else context.fillText(a0, a1, a2);
|
|
571
|
+
context.fillText(a0, Numeric.toFloat(a1), Numeric.toFloat(a2), Numeric.toFloat(a3));
|
|
572
|
+
} else context.fillText(a0, Numeric.toFloat(a1), Numeric.toFloat(a2));
|
|
679
573
|
return InterpretedSymbol.of("t");
|
|
680
574
|
});
|
|
681
575
|
}
|
|
682
576
|
gFillTri(arguments_) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const cdr4 = cdr3.cdr;
|
|
694
|
-
const a4 = cdr4.car;
|
|
695
|
-
const a5 = cdr4.cdr.car;
|
|
696
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
697
|
-
this.ctx.beginPath();
|
|
698
|
-
this.ctx.moveTo(a0, a1);
|
|
699
|
-
this.ctx.lineTo(a2, a3);
|
|
700
|
-
this.ctx.lineTo(a4, a5);
|
|
701
|
-
this.ctx.fill();
|
|
702
|
-
return InterpretedSymbol.of("t");
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
this.#print("Can not draw fill triangle.");
|
|
706
|
-
return Cons.nil;
|
|
707
|
-
} catch {
|
|
708
|
-
this.#print("Can not draw fill triangle.");
|
|
709
|
-
return Cons.nil;
|
|
710
|
-
}
|
|
711
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
712
|
-
return Cons.nil;
|
|
577
|
+
return this.#execute("Can not draw fill triangle.", (context) => {
|
|
578
|
+
const a = this.#numbers(arguments_, 6);
|
|
579
|
+
if (a === null) return null;
|
|
580
|
+
context.beginPath();
|
|
581
|
+
context.moveTo(a[0], a[1]);
|
|
582
|
+
context.lineTo(a[2], a[3]);
|
|
583
|
+
context.lineTo(a[4], a[5]);
|
|
584
|
+
context.fill();
|
|
585
|
+
return InterpretedSymbol.of("t");
|
|
586
|
+
});
|
|
713
587
|
}
|
|
714
|
-
gFinishPath() {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
588
|
+
gFinishPath(arguments_ = Cons.nil) {
|
|
589
|
+
return this.#execute("Can not finish path.", (context) => {
|
|
590
|
+
if (arguments_.length() !== 0) return null;
|
|
591
|
+
context.closePath();
|
|
718
592
|
return InterpretedSymbol.of("t");
|
|
719
|
-
}
|
|
720
|
-
this.#print("Can not finish path.");
|
|
721
|
-
return Cons.nil;
|
|
722
|
-
}
|
|
723
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
724
|
-
return Cons.nil;
|
|
593
|
+
});
|
|
725
594
|
}
|
|
726
595
|
gImage(arguments_) {
|
|
727
596
|
return this.#execute("Can not draw Image.", (context) => {
|
|
@@ -732,33 +601,21 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
732
601
|
if (length_ === 5) {
|
|
733
602
|
if (!Cons.isNumber(a3) || !Cons.isNumber(a4)) return null;
|
|
734
603
|
this.#withImage(a0, (image) => {
|
|
735
|
-
context.drawImage(image, a1, a2, a3, a4);
|
|
604
|
+
context.drawImage(image, Numeric.toFloat(a1), Numeric.toFloat(a2), Numeric.toFloat(a3), Numeric.toFloat(a4));
|
|
736
605
|
});
|
|
737
606
|
} else this.#withImage(a0, (image) => {
|
|
738
|
-
context.drawImage(image, a1, a2);
|
|
607
|
+
context.drawImage(image, Numeric.toFloat(a1), Numeric.toFloat(a2));
|
|
739
608
|
});
|
|
740
609
|
return InterpretedSymbol.of("t");
|
|
741
610
|
});
|
|
742
611
|
}
|
|
743
612
|
gLineTo(arguments_) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
this.ctx.lineTo(a0, a1);
|
|
751
|
-
return InterpretedSymbol.of("t");
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
this.#print("Can not draw line to");
|
|
755
|
-
return Cons.nil;
|
|
756
|
-
} catch {
|
|
757
|
-
this.#print("Can not draw line to");
|
|
758
|
-
return Cons.nil;
|
|
759
|
-
}
|
|
760
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
761
|
-
return Cons.nil;
|
|
613
|
+
return this.#execute("Can not draw line to", (context) => {
|
|
614
|
+
const a = this.#numbers(arguments_, 2);
|
|
615
|
+
if (a === null) return null;
|
|
616
|
+
context.lineTo(a[0], a[1]);
|
|
617
|
+
return InterpretedSymbol.of("t");
|
|
618
|
+
});
|
|
762
619
|
}
|
|
763
620
|
gLineCap(arguments_) {
|
|
764
621
|
return this.#execute("Can not set line cap. Expected \"butt\" / \"round\" / \"square\".", (context) => {
|
|
@@ -777,57 +634,35 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
777
634
|
});
|
|
778
635
|
}
|
|
779
636
|
gLineWidth(arguments_) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
787
|
-
this.#print("Can not set line width.");
|
|
788
|
-
return Cons.nil;
|
|
789
|
-
} catch {
|
|
790
|
-
this.#print("Can not set line width.");
|
|
791
|
-
return Cons.nil;
|
|
792
|
-
}
|
|
793
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
794
|
-
return Cons.nil;
|
|
637
|
+
return this.#execute("Can not set line width.", (context) => {
|
|
638
|
+
const a = this.#numbers(arguments_, 1);
|
|
639
|
+
if (a === null) return null;
|
|
640
|
+
context.lineWidth = a[0] <= 0 ? 1 : a[0];
|
|
641
|
+
return InterpretedSymbol.of("t");
|
|
642
|
+
});
|
|
795
643
|
}
|
|
796
644
|
gMoveTo(arguments_) {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
this.ctx.moveTo(a0, a1);
|
|
804
|
-
return InterpretedSymbol.of("t");
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
this.#print("Can not move");
|
|
808
|
-
return Cons.nil;
|
|
809
|
-
} catch {
|
|
810
|
-
this.#print("Can not move");
|
|
811
|
-
return Cons.nil;
|
|
812
|
-
}
|
|
813
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
814
|
-
return Cons.nil;
|
|
645
|
+
return this.#execute("Can not move", (context) => {
|
|
646
|
+
const a = this.#numbers(arguments_, 2);
|
|
647
|
+
if (a === null) return null;
|
|
648
|
+
context.moveTo(a[0], a[1]);
|
|
649
|
+
return InterpretedSymbol.of("t");
|
|
650
|
+
});
|
|
815
651
|
}
|
|
816
|
-
gOpen() {
|
|
817
|
-
if (
|
|
818
|
-
if (
|
|
819
|
-
|
|
652
|
+
gOpen(arguments_ = Cons.nil) {
|
|
653
|
+
if (this.ctx === null) throw new EvalError("Unable to initialize canvas. The browser or machine may not support it.");
|
|
654
|
+
if (arguments_.length() !== 0) throw new EvalError("Can not open.");
|
|
655
|
+
if (this.isOpen) throw new EvalError("The canvas has already been opened.");
|
|
656
|
+
try {
|
|
820
657
|
this.ctx.fillStyle = "#ffffff";
|
|
821
658
|
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
822
659
|
this.ctx.fillStyle = "#000000";
|
|
823
|
-
this.#print(`canvas size, width : ${this.canvas.width} height : ${this.canvas.height}`);
|
|
824
|
-
return InterpretedSymbol.of("t");
|
|
825
660
|
} catch {
|
|
826
|
-
|
|
827
|
-
return Cons.nil;
|
|
661
|
+
throw new EvalError("Can not open.");
|
|
828
662
|
}
|
|
829
|
-
this
|
|
830
|
-
|
|
663
|
+
this.isOpen = true;
|
|
664
|
+
this.#print(`canvas size, width : ${this.canvas.width} height : ${this.canvas.height}`);
|
|
665
|
+
return InterpretedSymbol.of("t");
|
|
831
666
|
}
|
|
832
667
|
gPattern(arguments_) {
|
|
833
668
|
return this.#execute("Can not set pattern. Expected an image source and a repetition (\"repeat\" / \"repeat-x\" / \"repeat-y\" / \"no-repeat\").", (context) => {
|
|
@@ -846,28 +681,12 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
846
681
|
});
|
|
847
682
|
}
|
|
848
683
|
gQuadCurveTo(arguments_) {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
const cdr2 = cdr1.cdr;
|
|
856
|
-
const a2 = cdr2.car;
|
|
857
|
-
const a3 = cdr2.cdr.car;
|
|
858
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
859
|
-
this.ctx.quadraticCurveTo(a0, a1, a2, a3);
|
|
860
|
-
return InterpretedSymbol.of("t");
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
this.#print("Can not draw quadratic curve.");
|
|
864
|
-
return Cons.nil;
|
|
865
|
-
} catch {
|
|
866
|
-
this.#print("Can not draw quadratic curve.");
|
|
867
|
-
return Cons.nil;
|
|
868
|
-
}
|
|
869
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
870
|
-
return Cons.nil;
|
|
684
|
+
return this.#execute("Can not draw quadratic curve.", (context) => {
|
|
685
|
+
const a = this.#numbers(arguments_, 4);
|
|
686
|
+
if (a === null) return null;
|
|
687
|
+
context.quadraticCurveTo(a[0], a[1], a[2], a[3]);
|
|
688
|
+
return InterpretedSymbol.of("t");
|
|
689
|
+
});
|
|
871
690
|
}
|
|
872
691
|
gSaveJpeg(arguments_) {
|
|
873
692
|
return this.saveCanvas(arguments_, "image/jpeg", "jpeg");
|
|
@@ -882,177 +701,91 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
882
701
|
* @param arguments_ - the evaluated argument list (empty, or one path string)
|
|
883
702
|
* @param mimeType - the image MIME type to encode
|
|
884
703
|
* @param label - the format name used in diagnostics ("jpeg" / "png")
|
|
885
|
-
* @return `t` on success
|
|
704
|
+
* @return `t` on success
|
|
886
705
|
*/
|
|
887
706
|
saveCanvas(arguments_, mimeType, label) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
this.#print(`Can not save ${label}.`);
|
|
894
|
-
return Cons.nil;
|
|
895
|
-
}
|
|
896
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
897
|
-
return Cons.nil;
|
|
707
|
+
this.#requireOpenContext();
|
|
708
|
+
const length_ = arguments_.length();
|
|
709
|
+
if (length_ === 0) return this.#downloadCanvas(mimeType, label);
|
|
710
|
+
if (length_ === 1 && Cons.isString(arguments_.car)) return this.#writeCanvasToFile(arguments_.car, mimeType, label);
|
|
711
|
+
throw new EvalError(`Can not save ${label}.`);
|
|
898
712
|
}
|
|
899
713
|
gScale(arguments_) {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
if (
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
this.ctx.scale(a0, a1);
|
|
907
|
-
return InterpretedSymbol.of("t");
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
this.#print("Can not scale.");
|
|
911
|
-
return Cons.nil;
|
|
912
|
-
} catch {
|
|
913
|
-
this.#print("Can not scale.");
|
|
914
|
-
return Cons.nil;
|
|
915
|
-
}
|
|
916
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
917
|
-
return Cons.nil;
|
|
714
|
+
return this.#execute("Can not scale.", (context) => {
|
|
715
|
+
const a = this.#numbers(arguments_, 2);
|
|
716
|
+
if (a === null) return null;
|
|
717
|
+
context.scale(a[0], a[1]);
|
|
718
|
+
return InterpretedSymbol.of("t");
|
|
719
|
+
});
|
|
918
720
|
}
|
|
919
721
|
gShadowBlur(arguments_) {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
this.#print("Can not set shadow blur.");
|
|
927
|
-
return Cons.nil;
|
|
928
|
-
}
|
|
929
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
930
|
-
return Cons.nil;
|
|
722
|
+
return this.#execute("Can not set shadow blur.", (context) => {
|
|
723
|
+
const a = this.#numbers(arguments_, 1);
|
|
724
|
+
if (a === null) return null;
|
|
725
|
+
context.shadowBlur = a[0];
|
|
726
|
+
return InterpretedSymbol.of("t");
|
|
727
|
+
});
|
|
931
728
|
}
|
|
932
729
|
gShadowColor(arguments_) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
return InterpretedSymbol.of("t");
|
|
939
|
-
}
|
|
940
|
-
this.#print("Can not set shadow color.");
|
|
941
|
-
return Cons.nil;
|
|
942
|
-
} catch {
|
|
943
|
-
this.#print("Can not set shadow color.");
|
|
944
|
-
return Cons.nil;
|
|
945
|
-
}
|
|
946
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
947
|
-
return Cons.nil;
|
|
730
|
+
return this.#execute("Can not set shadow color.", (context) => {
|
|
731
|
+
if (arguments_.length() < 1) return null;
|
|
732
|
+
context.shadowColor = this.selectColor(arguments_);
|
|
733
|
+
return InterpretedSymbol.of("t");
|
|
734
|
+
});
|
|
948
735
|
}
|
|
949
736
|
gShadowOffsetX(arguments_) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
if (
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
this.#print("Can not set shadow offsetX.");
|
|
957
|
-
return Cons.nil;
|
|
958
|
-
}
|
|
959
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
960
|
-
return Cons.nil;
|
|
737
|
+
return this.#execute("Can not set shadow offsetX.", (context) => {
|
|
738
|
+
const a = this.#numbers(arguments_, 1);
|
|
739
|
+
if (a === null) return null;
|
|
740
|
+
context.shadowOffsetX = a[0];
|
|
741
|
+
return InterpretedSymbol.of("t");
|
|
742
|
+
});
|
|
961
743
|
}
|
|
962
744
|
gShadowOffsetY(arguments_) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
if (
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
this.#print("Can not set shadow offsetY.");
|
|
970
|
-
return Cons.nil;
|
|
971
|
-
}
|
|
972
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
973
|
-
return Cons.nil;
|
|
745
|
+
return this.#execute("Can not set shadow offsetY.", (context) => {
|
|
746
|
+
const a = this.#numbers(arguments_, 1);
|
|
747
|
+
if (a === null) return null;
|
|
748
|
+
context.shadowOffsetY = a[0];
|
|
749
|
+
return InterpretedSymbol.of("t");
|
|
750
|
+
});
|
|
974
751
|
}
|
|
975
752
|
gSleep(arguments_) {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
sleep(arguments_.car);
|
|
984
|
-
return InterpretedSymbol.of("t");
|
|
985
|
-
}
|
|
986
|
-
this.#print("Can not sleep");
|
|
987
|
-
return Cons.nil;
|
|
988
|
-
}
|
|
989
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
990
|
-
return Cons.nil;
|
|
753
|
+
return this.#execute("Can not sleep", () => {
|
|
754
|
+
const a = this.#numbers(arguments_, 1);
|
|
755
|
+
if (a === null) return null;
|
|
756
|
+
const time = Date.now() + a[0];
|
|
757
|
+
while (Date.now() < time);
|
|
758
|
+
return InterpretedSymbol.of("t");
|
|
759
|
+
});
|
|
991
760
|
}
|
|
992
|
-
gStartPath() {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
761
|
+
gStartPath(arguments_ = Cons.nil) {
|
|
762
|
+
return this.#execute("Can not start path.", (context) => {
|
|
763
|
+
if (arguments_.length() !== 0) return null;
|
|
764
|
+
context.beginPath();
|
|
996
765
|
return InterpretedSymbol.of("t");
|
|
997
|
-
}
|
|
998
|
-
this.#print("Can not start path.");
|
|
999
|
-
return Cons.nil;
|
|
1000
|
-
}
|
|
1001
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1002
|
-
return Cons.nil;
|
|
766
|
+
});
|
|
1003
767
|
}
|
|
1004
|
-
gStroke() {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
768
|
+
gStroke(arguments_ = Cons.nil) {
|
|
769
|
+
return this.#execute("Can not stroke.", (context) => {
|
|
770
|
+
if (arguments_.length() !== 0) return null;
|
|
771
|
+
context.stroke();
|
|
1008
772
|
return InterpretedSymbol.of("t");
|
|
1009
|
-
}
|
|
1010
|
-
this.#print("Can not stroke.");
|
|
1011
|
-
return Cons.nil;
|
|
1012
|
-
}
|
|
1013
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1014
|
-
return Cons.nil;
|
|
773
|
+
});
|
|
1015
774
|
}
|
|
1016
775
|
gStrokeColor(arguments_) {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
return InterpretedSymbol.of("t");
|
|
1023
|
-
}
|
|
1024
|
-
this.#print("Can not set stroke color");
|
|
1025
|
-
return Cons.nil;
|
|
1026
|
-
} catch {
|
|
1027
|
-
this.#print("Can not set stroke color");
|
|
1028
|
-
return Cons.nil;
|
|
1029
|
-
}
|
|
1030
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1031
|
-
return Cons.nil;
|
|
776
|
+
return this.#execute("Can not set stroke color", (context) => {
|
|
777
|
+
if (arguments_.length() < 1) return null;
|
|
778
|
+
context.strokeStyle = this.selectColor(arguments_);
|
|
779
|
+
return InterpretedSymbol.of("t");
|
|
780
|
+
});
|
|
1032
781
|
}
|
|
1033
782
|
gStrokeRect(arguments_) {
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
const cdr2 = cdr1.cdr;
|
|
1041
|
-
const a2 = cdr2.car;
|
|
1042
|
-
const a3 = cdr2.cdr.car;
|
|
1043
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
1044
|
-
this.ctx.strokeRect(a0, a1, a2, a3);
|
|
1045
|
-
return InterpretedSymbol.of("t");
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
this.#print("Can not draw stroke rectangle.");
|
|
1049
|
-
return Cons.nil;
|
|
1050
|
-
} catch {
|
|
1051
|
-
this.#print("Can not draw stroke rectangle.");
|
|
1052
|
-
return Cons.nil;
|
|
1053
|
-
}
|
|
1054
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1055
|
-
return Cons.nil;
|
|
783
|
+
return this.#execute("Can not draw stroke rectangle.", (context) => {
|
|
784
|
+
const a = this.#numbers(arguments_, 4);
|
|
785
|
+
if (a === null) return null;
|
|
786
|
+
context.strokeRect(a[0], a[1], a[2], a[3]);
|
|
787
|
+
return InterpretedSymbol.of("t");
|
|
788
|
+
});
|
|
1056
789
|
}
|
|
1057
790
|
gStrokeText(arguments_) {
|
|
1058
791
|
return this.#execute("Can not draw stroke text.", (context) => {
|
|
@@ -1062,43 +795,23 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1062
795
|
if (!Cons.isString(a0) || !Cons.isNumber(a1) || !Cons.isNumber(a2)) return null;
|
|
1063
796
|
if (length_ === 4) {
|
|
1064
797
|
if (!Cons.isNumber(a3)) return null;
|
|
1065
|
-
context.strokeText(a0, a1, a2, a3);
|
|
1066
|
-
} else context.strokeText(a0, a1, a2);
|
|
798
|
+
context.strokeText(a0, Numeric.toFloat(a1), Numeric.toFloat(a2), Numeric.toFloat(a3));
|
|
799
|
+
} else context.strokeText(a0, Numeric.toFloat(a1), Numeric.toFloat(a2));
|
|
1067
800
|
return InterpretedSymbol.of("t");
|
|
1068
801
|
});
|
|
1069
802
|
}
|
|
1070
803
|
gStrokeTri(arguments_) {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if (
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
const a4 = cdr4.car;
|
|
1083
|
-
const a5 = cdr4.cdr.car;
|
|
1084
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
1085
|
-
this.ctx.beginPath();
|
|
1086
|
-
this.ctx.moveTo(a0, a1);
|
|
1087
|
-
this.ctx.lineTo(a2, a3);
|
|
1088
|
-
this.ctx.lineTo(a4, a5);
|
|
1089
|
-
this.ctx.closePath();
|
|
1090
|
-
this.ctx.stroke();
|
|
1091
|
-
return InterpretedSymbol.of("t");
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
this.#print("Can not draw stroke triangle.");
|
|
1095
|
-
return Cons.nil;
|
|
1096
|
-
} catch {
|
|
1097
|
-
this.#print("Can not draw stroke triangle.");
|
|
1098
|
-
return Cons.nil;
|
|
1099
|
-
}
|
|
1100
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1101
|
-
return Cons.nil;
|
|
804
|
+
return this.#execute("Can not draw stroke triangle.", (context) => {
|
|
805
|
+
const a = this.#numbers(arguments_, 6);
|
|
806
|
+
if (a === null) return null;
|
|
807
|
+
context.beginPath();
|
|
808
|
+
context.moveTo(a[0], a[1]);
|
|
809
|
+
context.lineTo(a[2], a[3]);
|
|
810
|
+
context.lineTo(a[4], a[5]);
|
|
811
|
+
context.closePath();
|
|
812
|
+
context.stroke();
|
|
813
|
+
return InterpretedSymbol.of("t");
|
|
814
|
+
});
|
|
1102
815
|
}
|
|
1103
816
|
gTextAlign(arguments_) {
|
|
1104
817
|
return this.#execute("Can not set text align. Expected \"left\" / \"right\" / \"center\" / \"start\" / \"end\".", (context) => {
|
|
@@ -1125,80 +838,35 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1125
838
|
});
|
|
1126
839
|
}
|
|
1127
840
|
gTextFont(arguments_) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1134
|
-
this.#print("Can not set text font.");
|
|
1135
|
-
return Cons.nil;
|
|
1136
|
-
} catch {
|
|
1137
|
-
this.#print("Can not set text font.");
|
|
1138
|
-
return Cons.nil;
|
|
1139
|
-
}
|
|
1140
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1141
|
-
return Cons.nil;
|
|
841
|
+
return this.#execute("Can not set text font.", (context) => {
|
|
842
|
+
if (arguments_.length() !== 1 || !Cons.isString(arguments_.car)) return null;
|
|
843
|
+
context.font = arguments_.car;
|
|
844
|
+
return InterpretedSymbol.of("t");
|
|
845
|
+
});
|
|
1142
846
|
}
|
|
1143
847
|
gTranslate(arguments_) {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
this.ctx.translate(a0, a1);
|
|
1151
|
-
return InterpretedSymbol.of("t");
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
this.#print("Can not translate.");
|
|
1155
|
-
return Cons.nil;
|
|
1156
|
-
} catch {
|
|
1157
|
-
this.#print("Can not translate.");
|
|
1158
|
-
return Cons.nil;
|
|
1159
|
-
}
|
|
1160
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1161
|
-
return Cons.nil;
|
|
848
|
+
return this.#execute("Can not translate.", (context) => {
|
|
849
|
+
const a = this.#numbers(arguments_, 2);
|
|
850
|
+
if (a === null) return null;
|
|
851
|
+
context.translate(a[0], a[1]);
|
|
852
|
+
return InterpretedSymbol.of("t");
|
|
853
|
+
});
|
|
1162
854
|
}
|
|
1163
855
|
gRect(arguments_) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
if (
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const cdr2 = cdr1.cdr;
|
|
1171
|
-
const a2 = cdr2.car;
|
|
1172
|
-
const a3 = cdr2.cdr.car;
|
|
1173
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
1174
|
-
this.ctx.rect(a0, a1, a2, a3);
|
|
1175
|
-
return InterpretedSymbol.of("t");
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
this.#print("Can not draw rectangle.");
|
|
1179
|
-
return Cons.nil;
|
|
1180
|
-
} catch {
|
|
1181
|
-
this.#print("Can not draw rectangle.");
|
|
1182
|
-
return Cons.nil;
|
|
1183
|
-
}
|
|
1184
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1185
|
-
return Cons.nil;
|
|
856
|
+
return this.#execute("Can not draw rectangle.", (context) => {
|
|
857
|
+
const a = this.#numbers(arguments_, 4);
|
|
858
|
+
if (a === null) return null;
|
|
859
|
+
context.rect(a[0], a[1], a[2], a[3]);
|
|
860
|
+
return InterpretedSymbol.of("t");
|
|
861
|
+
});
|
|
1186
862
|
}
|
|
1187
863
|
gRotate(arguments_) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if (
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
this.#print("Can not rotate.");
|
|
1195
|
-
return Cons.nil;
|
|
1196
|
-
} catch {
|
|
1197
|
-
this.#print("Can not rotate.");
|
|
1198
|
-
return Cons.nil;
|
|
1199
|
-
}
|
|
1200
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1201
|
-
return Cons.nil;
|
|
864
|
+
return this.#execute("Can not rotate.", (context) => {
|
|
865
|
+
const a = this.#numbers(arguments_, 1);
|
|
866
|
+
if (a === null) return null;
|
|
867
|
+
context.rotate(Math.PI / 180 * a[0]);
|
|
868
|
+
return InterpretedSymbol.of("t");
|
|
869
|
+
});
|
|
1202
870
|
}
|
|
1203
871
|
/**
|
|
1204
872
|
* Pushes the current drawing state (styles, transform, clip) onto the
|
|
@@ -1206,36 +874,26 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1206
874
|
* state explicitly. Replaces the legacy per-method `context.save()` calls, which
|
|
1207
875
|
* pushed state on every draw with no matching `restore()` and grew the stack
|
|
1208
876
|
* unbounded.
|
|
1209
|
-
* @return `t` on success
|
|
877
|
+
* @return `t` on success
|
|
1210
878
|
*/
|
|
1211
|
-
gSave() {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
879
|
+
gSave(arguments_ = Cons.nil) {
|
|
880
|
+
return this.#execute("Can not save.", (context) => {
|
|
881
|
+
if (arguments_.length() !== 0) return null;
|
|
882
|
+
context.save();
|
|
1215
883
|
return InterpretedSymbol.of("t");
|
|
1216
|
-
}
|
|
1217
|
-
this.#print("Can not save.");
|
|
1218
|
-
return Cons.nil;
|
|
1219
|
-
}
|
|
1220
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1221
|
-
return Cons.nil;
|
|
884
|
+
});
|
|
1222
885
|
}
|
|
1223
886
|
/**
|
|
1224
887
|
* Pops the most recently saved drawing state off the context's state stack.
|
|
1225
888
|
* Pairs with `gSave`. Popping an empty stack is a no-op per the Canvas spec.
|
|
1226
|
-
* @return `t` on success
|
|
889
|
+
* @return `t` on success
|
|
1227
890
|
*/
|
|
1228
|
-
gRestore() {
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
891
|
+
gRestore(arguments_ = Cons.nil) {
|
|
892
|
+
return this.#execute("Can not restore.", (context) => {
|
|
893
|
+
if (arguments_.length() !== 0) return null;
|
|
894
|
+
context.restore();
|
|
1232
895
|
return InterpretedSymbol.of("t");
|
|
1233
|
-
}
|
|
1234
|
-
this.#print("Can not restore.");
|
|
1235
|
-
return Cons.nil;
|
|
1236
|
-
}
|
|
1237
|
-
this.#print("The canvas is closed and cannot be executed.");
|
|
1238
|
-
return Cons.nil;
|
|
896
|
+
});
|
|
1239
897
|
}
|
|
1240
898
|
gEllipse(arguments_) {
|
|
1241
899
|
return this.#execute("Can not draw ellipse.", (context) => {
|
|
@@ -1257,6 +915,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1257
915
|
return this.#execute("Can not set line dash.", (context) => {
|
|
1258
916
|
const segments = this.#numbers(arguments_, arguments_.length());
|
|
1259
917
|
if (segments === null) return null;
|
|
918
|
+
if (segments.some((segment) => !Number.isFinite(segment) || segment < 0)) return null;
|
|
1260
919
|
context.setLineDash(segments);
|
|
1261
920
|
return InterpretedSymbol.of("t");
|
|
1262
921
|
});
|
|
@@ -1277,8 +936,9 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1277
936
|
return InterpretedSymbol.of("t");
|
|
1278
937
|
});
|
|
1279
938
|
}
|
|
1280
|
-
gClip() {
|
|
939
|
+
gClip(arguments_ = Cons.nil) {
|
|
1281
940
|
return this.#execute("Can not clip.", (context) => {
|
|
941
|
+
if (arguments_.length() !== 0) return null;
|
|
1282
942
|
context.clip();
|
|
1283
943
|
return InterpretedSymbol.of("t");
|
|
1284
944
|
});
|
|
@@ -1313,8 +973,9 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1313
973
|
return InterpretedSymbol.of("t");
|
|
1314
974
|
});
|
|
1315
975
|
}
|
|
1316
|
-
gResetTransform() {
|
|
976
|
+
gResetTransform(arguments_ = Cons.nil) {
|
|
1317
977
|
return this.#execute("Can not reset transform.", (context) => {
|
|
978
|
+
if (arguments_.length() !== 0) return null;
|
|
1318
979
|
context.resetTransform();
|
|
1319
980
|
return InterpretedSymbol.of("t");
|
|
1320
981
|
});
|
|
@@ -1406,17 +1067,18 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1406
1067
|
return InterpretedSymbol.of("t");
|
|
1407
1068
|
});
|
|
1408
1069
|
}
|
|
1409
|
-
gReset() {
|
|
1070
|
+
gReset(arguments_ = Cons.nil) {
|
|
1410
1071
|
return this.#execute("Can not reset.", (context) => {
|
|
1072
|
+
if (arguments_.length() !== 0) return null;
|
|
1411
1073
|
context.reset();
|
|
1412
1074
|
return InterpretedSymbol.of("t");
|
|
1413
1075
|
});
|
|
1414
1076
|
}
|
|
1415
|
-
gWidth() {
|
|
1416
|
-
return this.#execute("Can not get width.", () => this.canvas.width);
|
|
1077
|
+
gWidth(arguments_ = Cons.nil) {
|
|
1078
|
+
return this.#execute("Can not get width.", () => arguments_.length() === 0 ? BigInt(this.canvas.width) : null);
|
|
1417
1079
|
}
|
|
1418
|
-
gHeight() {
|
|
1419
|
-
return this.#execute("Can not get height.", () => this.canvas.height);
|
|
1080
|
+
gHeight(arguments_ = Cons.nil) {
|
|
1081
|
+
return this.#execute("Can not get height.", () => arguments_.length() === 0 ? BigInt(this.canvas.height) : null);
|
|
1420
1082
|
}
|
|
1421
1083
|
gPixel(arguments_) {
|
|
1422
1084
|
return this.#execute("Can not read pixel.", (context) => {
|
|
@@ -1424,10 +1086,10 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1424
1086
|
if (a === null) return null;
|
|
1425
1087
|
const data = context.getImageData(a[0], a[1], 1, 1).data;
|
|
1426
1088
|
return this.#toList([
|
|
1427
|
-
data[0],
|
|
1428
|
-
data[1],
|
|
1429
|
-
data[2],
|
|
1430
|
-
data[3]
|
|
1089
|
+
BigInt(data[0]),
|
|
1090
|
+
BigInt(data[1]),
|
|
1091
|
+
BigInt(data[2]),
|
|
1092
|
+
BigInt(data[3])
|
|
1431
1093
|
]);
|
|
1432
1094
|
});
|
|
1433
1095
|
}
|
|
@@ -1448,27 +1110,28 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1448
1110
|
return this.#execute("Can not set linear gradient.", (context) => {
|
|
1449
1111
|
const [x0, y0, x1, y1, ...stops] = this.#listValues(arguments_);
|
|
1450
1112
|
if (stops.length < 2 || !Cons.isNumber(x0) || !Cons.isNumber(y0) || !Cons.isNumber(x1) || !Cons.isNumber(y1)) return null;
|
|
1451
|
-
return this.#installGradient(context, context.createLinearGradient(x0, y0, x1, y1), stops);
|
|
1113
|
+
return this.#installGradient(context, context.createLinearGradient(Numeric.toFloat(x0), Numeric.toFloat(y0), Numeric.toFloat(x1), Numeric.toFloat(y1)), stops);
|
|
1452
1114
|
});
|
|
1453
1115
|
}
|
|
1454
1116
|
gRadialGradient(arguments_) {
|
|
1455
1117
|
return this.#execute("Can not set radial gradient.", (context) => {
|
|
1456
1118
|
const [x0, y0, r0, x1, y1, r1, ...stops] = this.#listValues(arguments_);
|
|
1457
1119
|
if (stops.length < 2 || !Cons.isNumber(x0) || !Cons.isNumber(y0) || !Cons.isNumber(r0) || !Cons.isNumber(x1) || !Cons.isNumber(y1) || !Cons.isNumber(r1)) return null;
|
|
1458
|
-
return this.#installGradient(context, context.createRadialGradient(x0, y0, r0, x1, y1, r1), stops);
|
|
1120
|
+
return this.#installGradient(context, context.createRadialGradient(Numeric.toFloat(x0), Numeric.toFloat(y0), Numeric.toFloat(r0), Numeric.toFloat(x1), Numeric.toFloat(y1), Numeric.toFloat(r1)), stops);
|
|
1459
1121
|
});
|
|
1460
1122
|
}
|
|
1461
1123
|
gConicGradient(arguments_) {
|
|
1462
1124
|
return this.#execute("Can not set conic gradient.", (context) => {
|
|
1463
1125
|
const [angle, x, y, ...stops] = this.#listValues(arguments_);
|
|
1464
1126
|
if (stops.length < 2 || !Cons.isNumber(angle) || !Cons.isNumber(x) || !Cons.isNumber(y)) return null;
|
|
1465
|
-
return this.#installGradient(context, context.createConicGradient(Math.PI / 180 * angle, x, y), stops);
|
|
1127
|
+
return this.#installGradient(context, context.createConicGradient(Math.PI / 180 * Numeric.toFloat(angle), Numeric.toFloat(x), Numeric.toFloat(y)), stops);
|
|
1466
1128
|
});
|
|
1467
1129
|
}
|
|
1468
1130
|
/**
|
|
1469
1131
|
* Parses a color spec from the head of the argument Cons. Accepts
|
|
1470
1132
|
* (1 string), (3 numbers — rgb), or (4 numbers — rgba); falls back to
|
|
1471
|
-
* `'black'` on anything else (legacy behavior
|
|
1133
|
+
* `'black'` on anything else (legacy behavior — a best-effort color parse
|
|
1134
|
+
* that prints a diagnostic instead of signaling an error).
|
|
1472
1135
|
* @param arguments_ - the argument Cons to parse
|
|
1473
1136
|
* @return CSS color string
|
|
1474
1137
|
*/
|
|
@@ -1481,7 +1144,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1481
1144
|
const cdr1 = arguments_.cdr;
|
|
1482
1145
|
const a1 = cdr1.car;
|
|
1483
1146
|
const a2 = cdr1.cdr.car;
|
|
1484
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) aColor = `rgb(${a0}, ${a1}, ${a2})`;
|
|
1147
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) aColor = `rgb(${Numeric.toFloat(a0)}, ${Numeric.toFloat(a1)}, ${Numeric.toFloat(a2)})`;
|
|
1485
1148
|
else this.#print("Can not set color. set color \"black\".");
|
|
1486
1149
|
} else if (length_ === 4) {
|
|
1487
1150
|
const cdr1 = arguments_.cdr;
|
|
@@ -1489,7 +1152,7 @@ var GraphicsPlugin = class GraphicsPlugin {
|
|
|
1489
1152
|
const cdr2 = cdr1.cdr;
|
|
1490
1153
|
const a2 = cdr2.car;
|
|
1491
1154
|
const a3 = cdr2.cdr.car;
|
|
1492
|
-
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) aColor = `rgba(${a0}, ${a1}, ${a2}, ${a3})`;
|
|
1155
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) aColor = `rgba(${Numeric.toFloat(a0)}, ${Numeric.toFloat(a1)}, ${Numeric.toFloat(a2)}, ${Numeric.toFloat(a3)})`;
|
|
1493
1156
|
else this.#print("Can not set color. set color \"black\".");
|
|
1494
1157
|
} else this.#print("Can not set color. set color \"black\".");
|
|
1495
1158
|
return aColor;
|