kei-lisp-plugin-graphics 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/index.cjs +1088 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +153 -0
- package/dist/index.d.ts +153 -0
- package/dist/index.js +1086 -0
- package/dist/index.js.map +1 -0
- package/package.json +91 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1086 @@
|
|
|
1
|
+
import { Cons, InterpretedSymbol } from "kei-lisp";
|
|
2
|
+
//#region src/GraphicsPlugin/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* @class
|
|
5
|
+
* @classdesc Canvas2D drawing plugin for the kei-lisp interpreter. Implements
|
|
6
|
+
* the `KeiLispPlugin` contract (`name` / `has` / `apply`) and
|
|
7
|
+
* exposes 43 `g…` Lisp functions that proxy to a 2D rendering
|
|
8
|
+
* context.
|
|
9
|
+
* @author Keisuke Ikeda
|
|
10
|
+
* @this {GraphicsPlugin}
|
|
11
|
+
*/
|
|
12
|
+
var GraphicsPlugin = class GraphicsPlugin extends Object {
|
|
13
|
+
/**
|
|
14
|
+
* Dispatch map from a Lisp function name (InterpretedSymbol) to the name of
|
|
15
|
+
* the GraphicsPlugin method that implements it.
|
|
16
|
+
*/
|
|
17
|
+
static buildInFunctions = GraphicsPlugin.setup();
|
|
18
|
+
/**
|
|
19
|
+
* Plugin identifier, used for diagnostics.
|
|
20
|
+
*/
|
|
21
|
+
name = "graphics";
|
|
22
|
+
canvas;
|
|
23
|
+
ctx;
|
|
24
|
+
isOpen;
|
|
25
|
+
/**
|
|
26
|
+
* Constructor.
|
|
27
|
+
* @constructor
|
|
28
|
+
* @param canvas - the canvas to draw to
|
|
29
|
+
*/
|
|
30
|
+
constructor(canvas) {
|
|
31
|
+
super();
|
|
32
|
+
this.canvas = canvas;
|
|
33
|
+
this.ctx = canvas.getContext("2d");
|
|
34
|
+
this.isOpen = false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns true if this plugin handles the given symbol.
|
|
38
|
+
* @param aSymbol - the call symbol
|
|
39
|
+
* @return true if `apply` should be called
|
|
40
|
+
*/
|
|
41
|
+
has(aSymbol) {
|
|
42
|
+
return GraphicsPlugin.buildInFunctions.has(aSymbol);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Dispatches the given symbol to the matching `g…` method.
|
|
46
|
+
* @param aSymbol - the call symbol
|
|
47
|
+
* @param args - the evaluated argument list
|
|
48
|
+
* @param _ctx - the interpreter context (unused by this plugin)
|
|
49
|
+
* @return the method's result, or `Cons.nil` if dispatch fails
|
|
50
|
+
*/
|
|
51
|
+
apply(aSymbol, args, _ctx) {
|
|
52
|
+
return this.selectProcedure(aSymbol, args);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolves the procedure name and invokes the matching method.
|
|
56
|
+
* @param procedure - the Lisp symbol
|
|
57
|
+
* @param args - the evaluated argument list
|
|
58
|
+
* @return the method's result, or `Cons.nil` if not registered
|
|
59
|
+
*/
|
|
60
|
+
selectProcedure(procedure, args) {
|
|
61
|
+
if (GraphicsPlugin.buildInFunctions.has(procedure)) return this.buildInFunction(procedure, args);
|
|
62
|
+
this._print(`I could find no procedure description for ${String(procedure)}`);
|
|
63
|
+
return Cons.nil;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Looks up and invokes the JS method that implements the given Lisp symbol.
|
|
67
|
+
* Throws `TypeError` when the dispatch table points to a method that does
|
|
68
|
+
* not exist on the instance — this matches the legacy behavior, where the
|
|
69
|
+
* Ramda `R.invoker(1, methodName)(args, this)` call would surface the same
|
|
70
|
+
* error by attempting to call `undefined`.
|
|
71
|
+
* @param procedure - the Lisp symbol
|
|
72
|
+
* @param args - the evaluated argument list
|
|
73
|
+
* @return the method's result
|
|
74
|
+
*/
|
|
75
|
+
buildInFunction(procedure, args) {
|
|
76
|
+
const methodName = GraphicsPlugin.buildInFunctions.get(procedure);
|
|
77
|
+
const method = this[methodName];
|
|
78
|
+
if (typeof method !== "function") throw new TypeError(`${this.constructor.name} does not have a method named "${methodName}"`);
|
|
79
|
+
return method.call(this, args);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks whether the canvas exposes a usable 2D context, and narrows
|
|
83
|
+
* `this.ctx` to non-null for the caller when it returns true.
|
|
84
|
+
* @return type predicate — true when ctx is non-null
|
|
85
|
+
*/
|
|
86
|
+
checkSupport() {
|
|
87
|
+
if (this.ctx === null) {
|
|
88
|
+
this._print("Unable to initialize canvas. The browser or machine may not support it.");
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Writes a diagnostic line directly to `process.stderr`, matching the
|
|
95
|
+
* convention used by kei-lisp itself (`Applier.format` writes to
|
|
96
|
+
* `process.stdout`). In a Node runtime this hits the real stderr; in a
|
|
97
|
+
* browser kei-lisp host (e.g. kei-lisp-web) the host typically swaps
|
|
98
|
+
* `process.stderr.write` for a sink that routes to the REPL output panel,
|
|
99
|
+
* so the same call reaches the user via the host's normal output channel.
|
|
100
|
+
* @param line - the line to write
|
|
101
|
+
*/
|
|
102
|
+
_print(line) {
|
|
103
|
+
process.stderr.write(line + "\n");
|
|
104
|
+
}
|
|
105
|
+
gAlpha(args) {
|
|
106
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
107
|
+
if (this.isOpen) try {
|
|
108
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
109
|
+
const aNumber = args.car <= 0 ? 0 : args.car >= 1 ? 1 : args.car;
|
|
110
|
+
this.ctx.globalAlpha = aNumber;
|
|
111
|
+
this.ctx.save();
|
|
112
|
+
return InterpretedSymbol.of("t");
|
|
113
|
+
}
|
|
114
|
+
this._print("Can not set alpha.");
|
|
115
|
+
return Cons.nil;
|
|
116
|
+
} catch {
|
|
117
|
+
this._print("Can not set alpha.");
|
|
118
|
+
return Cons.nil;
|
|
119
|
+
}
|
|
120
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
121
|
+
return Cons.nil;
|
|
122
|
+
}
|
|
123
|
+
gArc(args) {
|
|
124
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
125
|
+
if (this.isOpen) try {
|
|
126
|
+
if (args.length() === 6) {
|
|
127
|
+
const a0 = args.car;
|
|
128
|
+
const cdr1 = args.cdr;
|
|
129
|
+
const a1 = cdr1.car;
|
|
130
|
+
const cdr2 = cdr1.cdr;
|
|
131
|
+
const a2 = cdr2.car;
|
|
132
|
+
const cdr3 = cdr2.cdr;
|
|
133
|
+
const a3 = cdr3.car;
|
|
134
|
+
const cdr4 = cdr3.cdr;
|
|
135
|
+
const a4 = cdr4.car;
|
|
136
|
+
const a5 = cdr4.cdr.car;
|
|
137
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
138
|
+
const aFlag = a5 >= 0;
|
|
139
|
+
this.ctx.arc(a0, a1, a2, Math.PI / 180 * a3, Math.PI / 180 * a4, aFlag);
|
|
140
|
+
this.ctx.save();
|
|
141
|
+
return InterpretedSymbol.of("t");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
this._print("Can not draw arc.");
|
|
145
|
+
return Cons.nil;
|
|
146
|
+
} catch {
|
|
147
|
+
this._print("Can not draw arc.");
|
|
148
|
+
return Cons.nil;
|
|
149
|
+
}
|
|
150
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
151
|
+
return Cons.nil;
|
|
152
|
+
}
|
|
153
|
+
gArcTo(args) {
|
|
154
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
155
|
+
if (this.isOpen) try {
|
|
156
|
+
if (args.length() === 5) {
|
|
157
|
+
const a0 = args.car;
|
|
158
|
+
const cdr1 = args.cdr;
|
|
159
|
+
const a1 = cdr1.car;
|
|
160
|
+
const cdr2 = cdr1.cdr;
|
|
161
|
+
const a2 = cdr2.car;
|
|
162
|
+
const cdr3 = cdr2.cdr;
|
|
163
|
+
const a3 = cdr3.car;
|
|
164
|
+
const a4 = cdr3.cdr.car;
|
|
165
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4)) {
|
|
166
|
+
this.ctx.arcTo(a0, a1, a2, a3, a4);
|
|
167
|
+
this.ctx.save();
|
|
168
|
+
return InterpretedSymbol.of("t");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
this._print("Can not draw arc to.");
|
|
172
|
+
return Cons.nil;
|
|
173
|
+
} catch {
|
|
174
|
+
this._print("Can not draw arc to.");
|
|
175
|
+
return Cons.nil;
|
|
176
|
+
}
|
|
177
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
178
|
+
return Cons.nil;
|
|
179
|
+
}
|
|
180
|
+
gBezCurveTo(args) {
|
|
181
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
182
|
+
if (this.isOpen) try {
|
|
183
|
+
if (args.length() === 6) {
|
|
184
|
+
const a0 = args.car;
|
|
185
|
+
const cdr1 = args.cdr;
|
|
186
|
+
const a1 = cdr1.car;
|
|
187
|
+
const cdr2 = cdr1.cdr;
|
|
188
|
+
const a2 = cdr2.car;
|
|
189
|
+
const cdr3 = cdr2.cdr;
|
|
190
|
+
const a3 = cdr3.car;
|
|
191
|
+
const cdr4 = cdr3.cdr;
|
|
192
|
+
const a4 = cdr4.car;
|
|
193
|
+
const a5 = cdr4.cdr.car;
|
|
194
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
195
|
+
this.ctx.bezierCurveTo(a0, a1, a2, a3, a4, a5);
|
|
196
|
+
this.ctx.save();
|
|
197
|
+
return InterpretedSymbol.of("t");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this._print("Can not draw bezier curve.");
|
|
201
|
+
return Cons.nil;
|
|
202
|
+
} catch {
|
|
203
|
+
this._print("Can not draw bezier curve.");
|
|
204
|
+
return Cons.nil;
|
|
205
|
+
}
|
|
206
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
207
|
+
return Cons.nil;
|
|
208
|
+
}
|
|
209
|
+
gClear() {
|
|
210
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
211
|
+
if (this.isOpen) try {
|
|
212
|
+
this.ctx.fillStyle = "#ffffff";
|
|
213
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
214
|
+
this.ctx.fillStyle = "#000000";
|
|
215
|
+
this.ctx.save();
|
|
216
|
+
return InterpretedSymbol.of("t");
|
|
217
|
+
} catch {
|
|
218
|
+
this._print("Can not clear.");
|
|
219
|
+
return Cons.nil;
|
|
220
|
+
}
|
|
221
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
222
|
+
return Cons.nil;
|
|
223
|
+
}
|
|
224
|
+
gClose() {
|
|
225
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
226
|
+
if (this.isOpen) try {
|
|
227
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
228
|
+
this.isOpen = false;
|
|
229
|
+
return InterpretedSymbol.of("t");
|
|
230
|
+
} catch {
|
|
231
|
+
this._print("Can not close.");
|
|
232
|
+
return Cons.nil;
|
|
233
|
+
}
|
|
234
|
+
this._print("The canvas has already been closed.");
|
|
235
|
+
return Cons.nil;
|
|
236
|
+
}
|
|
237
|
+
gColor(args) {
|
|
238
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
239
|
+
if (this.isOpen) try {
|
|
240
|
+
if (args.length() >= 1) {
|
|
241
|
+
const aColor = this.selectColor(args);
|
|
242
|
+
this.ctx.fillStyle = aColor;
|
|
243
|
+
this.ctx.strokeStyle = aColor;
|
|
244
|
+
this.ctx.save();
|
|
245
|
+
return InterpretedSymbol.of("t");
|
|
246
|
+
}
|
|
247
|
+
this._print("Can not set color.");
|
|
248
|
+
return Cons.nil;
|
|
249
|
+
} catch {
|
|
250
|
+
this._print("Can not set color.");
|
|
251
|
+
return Cons.nil;
|
|
252
|
+
}
|
|
253
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
254
|
+
return Cons.nil;
|
|
255
|
+
}
|
|
256
|
+
gFill() {
|
|
257
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
258
|
+
if (this.isOpen) try {
|
|
259
|
+
this.ctx.fill();
|
|
260
|
+
this.ctx.save();
|
|
261
|
+
return InterpretedSymbol.of("t");
|
|
262
|
+
} catch {
|
|
263
|
+
this._print("Can not fill.");
|
|
264
|
+
return Cons.nil;
|
|
265
|
+
}
|
|
266
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
267
|
+
return Cons.nil;
|
|
268
|
+
}
|
|
269
|
+
gFillColor(args) {
|
|
270
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
271
|
+
if (this.isOpen) try {
|
|
272
|
+
if (args.length() >= 1) {
|
|
273
|
+
const aColor = this.selectColor(args);
|
|
274
|
+
this.ctx.fillStyle = aColor;
|
|
275
|
+
this.ctx.save();
|
|
276
|
+
return InterpretedSymbol.of("t");
|
|
277
|
+
}
|
|
278
|
+
this._print("Can not set fill color.");
|
|
279
|
+
return Cons.nil;
|
|
280
|
+
} catch {
|
|
281
|
+
this._print("Can not set fill color.");
|
|
282
|
+
return Cons.nil;
|
|
283
|
+
}
|
|
284
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
285
|
+
return Cons.nil;
|
|
286
|
+
}
|
|
287
|
+
gFillRect(args) {
|
|
288
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
289
|
+
if (this.isOpen) try {
|
|
290
|
+
if (args.length() === 4) {
|
|
291
|
+
const a0 = args.car;
|
|
292
|
+
const cdr1 = args.cdr;
|
|
293
|
+
const a1 = cdr1.car;
|
|
294
|
+
const cdr2 = cdr1.cdr;
|
|
295
|
+
const a2 = cdr2.car;
|
|
296
|
+
const a3 = cdr2.cdr.car;
|
|
297
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
298
|
+
this.ctx.fillRect(a0, a1, a2, a3);
|
|
299
|
+
this.ctx.save();
|
|
300
|
+
return InterpretedSymbol.of("t");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this._print("Can not draw fill rectangle.");
|
|
304
|
+
return Cons.nil;
|
|
305
|
+
} catch {
|
|
306
|
+
this._print("Can not draw fill rectangle.");
|
|
307
|
+
return Cons.nil;
|
|
308
|
+
}
|
|
309
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
310
|
+
return Cons.nil;
|
|
311
|
+
}
|
|
312
|
+
gFillText(args) {
|
|
313
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
314
|
+
if (this.isOpen) try {
|
|
315
|
+
if (args.length() === 3) {
|
|
316
|
+
const a0 = args.car;
|
|
317
|
+
const cdr1 = args.cdr;
|
|
318
|
+
const a1 = cdr1.car;
|
|
319
|
+
const a2 = cdr1.cdr.car;
|
|
320
|
+
if (Cons.isString(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) {
|
|
321
|
+
this.ctx.fillText(a0, a1, a2);
|
|
322
|
+
this.ctx.save();
|
|
323
|
+
return InterpretedSymbol.of("t");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
this._print("Can not draw fill text.");
|
|
327
|
+
return Cons.nil;
|
|
328
|
+
} catch {
|
|
329
|
+
this._print("Can not draw fill text.");
|
|
330
|
+
return Cons.nil;
|
|
331
|
+
}
|
|
332
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
333
|
+
return Cons.nil;
|
|
334
|
+
}
|
|
335
|
+
gFillTri(args) {
|
|
336
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
337
|
+
if (this.isOpen) try {
|
|
338
|
+
if (args.length() === 6) {
|
|
339
|
+
const a0 = args.car;
|
|
340
|
+
const cdr1 = args.cdr;
|
|
341
|
+
const a1 = cdr1.car;
|
|
342
|
+
const cdr2 = cdr1.cdr;
|
|
343
|
+
const a2 = cdr2.car;
|
|
344
|
+
const cdr3 = cdr2.cdr;
|
|
345
|
+
const a3 = cdr3.car;
|
|
346
|
+
const cdr4 = cdr3.cdr;
|
|
347
|
+
const a4 = cdr4.car;
|
|
348
|
+
const a5 = cdr4.cdr.car;
|
|
349
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
350
|
+
this.ctx.beginPath();
|
|
351
|
+
this.ctx.moveTo(a0, a1);
|
|
352
|
+
this.ctx.lineTo(a2, a3);
|
|
353
|
+
this.ctx.lineTo(a4, a5);
|
|
354
|
+
this.ctx.fill();
|
|
355
|
+
this.ctx.save();
|
|
356
|
+
return InterpretedSymbol.of("t");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
this._print("Can not draw fill triangle.");
|
|
360
|
+
return Cons.nil;
|
|
361
|
+
} catch {
|
|
362
|
+
this._print("Can not draw fill triangle.");
|
|
363
|
+
return Cons.nil;
|
|
364
|
+
}
|
|
365
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
366
|
+
return Cons.nil;
|
|
367
|
+
}
|
|
368
|
+
gFinishPath() {
|
|
369
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
370
|
+
if (this.isOpen) try {
|
|
371
|
+
this.ctx.closePath();
|
|
372
|
+
this.ctx.save();
|
|
373
|
+
return InterpretedSymbol.of("t");
|
|
374
|
+
} catch {
|
|
375
|
+
this._print("Can not finish path.");
|
|
376
|
+
return Cons.nil;
|
|
377
|
+
}
|
|
378
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
379
|
+
return Cons.nil;
|
|
380
|
+
}
|
|
381
|
+
gImage(args) {
|
|
382
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
383
|
+
if (this.isOpen) try {
|
|
384
|
+
const len = args.length();
|
|
385
|
+
if (len === 3) {
|
|
386
|
+
const a0 = args.car;
|
|
387
|
+
const cdr1 = args.cdr;
|
|
388
|
+
const a1 = cdr1.car;
|
|
389
|
+
const a2 = cdr1.cdr.car;
|
|
390
|
+
if (Cons.isString(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) {
|
|
391
|
+
const ctx = this.ctx;
|
|
392
|
+
const anImage = new Image();
|
|
393
|
+
anImage.src = a0;
|
|
394
|
+
anImage.onload = () => {
|
|
395
|
+
ctx.drawImage(anImage, a1, a2);
|
|
396
|
+
};
|
|
397
|
+
ctx.save();
|
|
398
|
+
return InterpretedSymbol.of("t");
|
|
399
|
+
}
|
|
400
|
+
} else if (len === 5) {
|
|
401
|
+
const a0 = args.car;
|
|
402
|
+
const cdr1 = args.cdr;
|
|
403
|
+
const a1 = cdr1.car;
|
|
404
|
+
const cdr2 = cdr1.cdr;
|
|
405
|
+
const a2 = cdr2.car;
|
|
406
|
+
const cdr3 = cdr2.cdr;
|
|
407
|
+
const a3 = cdr3.car;
|
|
408
|
+
const a4 = cdr3.cdr.car;
|
|
409
|
+
if (Cons.isString(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4)) {
|
|
410
|
+
const ctx = this.ctx;
|
|
411
|
+
const anImage = new Image();
|
|
412
|
+
anImage.src = a0;
|
|
413
|
+
anImage.onload = () => {
|
|
414
|
+
ctx.drawImage(anImage, a1, a2, a3, a4);
|
|
415
|
+
};
|
|
416
|
+
ctx.save();
|
|
417
|
+
return InterpretedSymbol.of("t");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
this._print("Can not draw Image.");
|
|
421
|
+
return Cons.nil;
|
|
422
|
+
} catch {
|
|
423
|
+
this._print("Can not draw Image.");
|
|
424
|
+
return Cons.nil;
|
|
425
|
+
}
|
|
426
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
427
|
+
return Cons.nil;
|
|
428
|
+
}
|
|
429
|
+
gLineTo(args) {
|
|
430
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
431
|
+
if (this.isOpen) try {
|
|
432
|
+
if (args.length() === 2) {
|
|
433
|
+
const a0 = args.car;
|
|
434
|
+
const a1 = args.cdr.car;
|
|
435
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1)) {
|
|
436
|
+
this.ctx.lineTo(a0, a1);
|
|
437
|
+
this.ctx.save();
|
|
438
|
+
return InterpretedSymbol.of("t");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
this._print("Can not draw line to");
|
|
442
|
+
return Cons.nil;
|
|
443
|
+
} catch {
|
|
444
|
+
this._print("Can not draw line to");
|
|
445
|
+
return Cons.nil;
|
|
446
|
+
}
|
|
447
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
448
|
+
return Cons.nil;
|
|
449
|
+
}
|
|
450
|
+
gLineCap(args) {
|
|
451
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
452
|
+
if (this.isOpen) try {
|
|
453
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
454
|
+
const aString = args.car === 0 ? "butt" : args.car > 0 ? "round" : "square";
|
|
455
|
+
this.ctx.lineCap = aString;
|
|
456
|
+
this.ctx.save();
|
|
457
|
+
return InterpretedSymbol.of("t");
|
|
458
|
+
}
|
|
459
|
+
this._print("Can not set line cap.");
|
|
460
|
+
return Cons.nil;
|
|
461
|
+
} catch {
|
|
462
|
+
this._print("Can not set line cap.");
|
|
463
|
+
return Cons.nil;
|
|
464
|
+
}
|
|
465
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
466
|
+
return Cons.nil;
|
|
467
|
+
}
|
|
468
|
+
gLineJoin(args) {
|
|
469
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
470
|
+
if (this.isOpen) try {
|
|
471
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
472
|
+
const aString = args.car === 0 ? "miter" : args.car > 0 ? "round" : "bevel";
|
|
473
|
+
this.ctx.lineJoin = aString;
|
|
474
|
+
this.ctx.save();
|
|
475
|
+
return InterpretedSymbol.of("t");
|
|
476
|
+
}
|
|
477
|
+
this._print("Can not set line join.");
|
|
478
|
+
return Cons.nil;
|
|
479
|
+
} catch {
|
|
480
|
+
this._print("Can not set line join.");
|
|
481
|
+
return Cons.nil;
|
|
482
|
+
}
|
|
483
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
484
|
+
return Cons.nil;
|
|
485
|
+
}
|
|
486
|
+
gLineWidth(args) {
|
|
487
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
488
|
+
if (this.isOpen) try {
|
|
489
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
490
|
+
const aNumber = args.car <= 0 ? 1 : args.car;
|
|
491
|
+
this.ctx.lineWidth = aNumber;
|
|
492
|
+
this.ctx.save();
|
|
493
|
+
return InterpretedSymbol.of("t");
|
|
494
|
+
}
|
|
495
|
+
this._print("Can not set line width.");
|
|
496
|
+
return Cons.nil;
|
|
497
|
+
} catch {
|
|
498
|
+
this._print("Can not set line width.");
|
|
499
|
+
return Cons.nil;
|
|
500
|
+
}
|
|
501
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
502
|
+
return Cons.nil;
|
|
503
|
+
}
|
|
504
|
+
gMoveTo(args) {
|
|
505
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
506
|
+
if (this.isOpen) try {
|
|
507
|
+
if (args.length() === 2) {
|
|
508
|
+
const a0 = args.car;
|
|
509
|
+
const a1 = args.cdr.car;
|
|
510
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1)) {
|
|
511
|
+
this.ctx.moveTo(a0, a1);
|
|
512
|
+
this.ctx.save();
|
|
513
|
+
return InterpretedSymbol.of("t");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
this._print("Can not move");
|
|
517
|
+
return Cons.nil;
|
|
518
|
+
} catch {
|
|
519
|
+
this._print("Can not move");
|
|
520
|
+
return Cons.nil;
|
|
521
|
+
}
|
|
522
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
523
|
+
return Cons.nil;
|
|
524
|
+
}
|
|
525
|
+
gOpen() {
|
|
526
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
527
|
+
if (!this.isOpen) try {
|
|
528
|
+
this.isOpen = true;
|
|
529
|
+
this.ctx.fillStyle = "#ffffff";
|
|
530
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
531
|
+
this.ctx.fillStyle = "#000000";
|
|
532
|
+
this.ctx.save();
|
|
533
|
+
this._print("canvas size, width : 600 height : 300");
|
|
534
|
+
return InterpretedSymbol.of("t");
|
|
535
|
+
} catch {
|
|
536
|
+
this._print("Can not open.");
|
|
537
|
+
return Cons.nil;
|
|
538
|
+
}
|
|
539
|
+
this._print("The canvas has already been opened.");
|
|
540
|
+
return Cons.nil;
|
|
541
|
+
}
|
|
542
|
+
gPattern(args) {
|
|
543
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
544
|
+
if (this.isOpen) try {
|
|
545
|
+
if (args.length() === 2) {
|
|
546
|
+
const a0 = args.car;
|
|
547
|
+
const a1 = args.cdr.car;
|
|
548
|
+
if (Cons.isString(a0) && Cons.isNumber(a1)) {
|
|
549
|
+
const aString = a1 === 0 ? "repeat" : a1 > 0 ? "repeat-x" : "repeat-y";
|
|
550
|
+
const ctx = this.ctx;
|
|
551
|
+
const anImage = new Image();
|
|
552
|
+
anImage.src = a0;
|
|
553
|
+
anImage.onload = () => {
|
|
554
|
+
ctx.fillStyle = ctx.createPattern(anImage, aString);
|
|
555
|
+
};
|
|
556
|
+
ctx.save();
|
|
557
|
+
return InterpretedSymbol.of("t");
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
this._print("Can not set pattern.");
|
|
561
|
+
return Cons.nil;
|
|
562
|
+
} catch {
|
|
563
|
+
this._print("Can not set pattern.");
|
|
564
|
+
return Cons.nil;
|
|
565
|
+
}
|
|
566
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
567
|
+
return Cons.nil;
|
|
568
|
+
}
|
|
569
|
+
gQuadCurveTo(args) {
|
|
570
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
571
|
+
if (this.isOpen) try {
|
|
572
|
+
if (args.length() === 4) {
|
|
573
|
+
const a0 = args.car;
|
|
574
|
+
const cdr1 = args.cdr;
|
|
575
|
+
const a1 = cdr1.car;
|
|
576
|
+
const cdr2 = cdr1.cdr;
|
|
577
|
+
const a2 = cdr2.car;
|
|
578
|
+
const a3 = cdr2.cdr.car;
|
|
579
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
580
|
+
this.ctx.quadraticCurveTo(a0, a1, a2, a3);
|
|
581
|
+
this.ctx.save();
|
|
582
|
+
return InterpretedSymbol.of("t");
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
this._print("Can not draw quadratic curve.");
|
|
586
|
+
return Cons.nil;
|
|
587
|
+
} catch {
|
|
588
|
+
this._print("Can not draw quadratic curve.");
|
|
589
|
+
return Cons.nil;
|
|
590
|
+
}
|
|
591
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
592
|
+
return Cons.nil;
|
|
593
|
+
}
|
|
594
|
+
gSaveJpeg() {
|
|
595
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
596
|
+
if (this.isOpen) try {
|
|
597
|
+
const anImage = new Image();
|
|
598
|
+
anImage.crossOrigin = "Anonymous";
|
|
599
|
+
anImage.src = this.canvas.toDataURL("image/jpeg");
|
|
600
|
+
const link = document.createElement("a");
|
|
601
|
+
link.href = anImage.src;
|
|
602
|
+
link.download = "canvas";
|
|
603
|
+
document.body.append(link);
|
|
604
|
+
link.click();
|
|
605
|
+
link.remove();
|
|
606
|
+
return InterpretedSymbol.of("t");
|
|
607
|
+
} catch {
|
|
608
|
+
this._print("Can not save jpeg. If you are using an image in the canvas, you can't save jpeg.");
|
|
609
|
+
return Cons.nil;
|
|
610
|
+
}
|
|
611
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
612
|
+
return Cons.nil;
|
|
613
|
+
}
|
|
614
|
+
gSavePng() {
|
|
615
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
616
|
+
if (this.isOpen) try {
|
|
617
|
+
const anImage = new Image();
|
|
618
|
+
anImage.crossOrigin = "Anonymous";
|
|
619
|
+
anImage.src = this.canvas.toDataURL("image/png");
|
|
620
|
+
const link = document.createElement("a");
|
|
621
|
+
link.href = anImage.src;
|
|
622
|
+
link.download = "canvas";
|
|
623
|
+
document.body.append(link);
|
|
624
|
+
link.click();
|
|
625
|
+
link.remove();
|
|
626
|
+
return InterpretedSymbol.of("t");
|
|
627
|
+
} catch {
|
|
628
|
+
this._print("Can not save png. If you are using an image in the canvas, you can't save png.");
|
|
629
|
+
return Cons.nil;
|
|
630
|
+
}
|
|
631
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
632
|
+
return Cons.nil;
|
|
633
|
+
}
|
|
634
|
+
gScale(args) {
|
|
635
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
636
|
+
if (this.isOpen) try {
|
|
637
|
+
if (args.length() === 2) {
|
|
638
|
+
const a0 = args.car;
|
|
639
|
+
const a1 = args.cdr.car;
|
|
640
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1)) {
|
|
641
|
+
this.ctx.scale(a0, a1);
|
|
642
|
+
this.ctx.save();
|
|
643
|
+
return InterpretedSymbol.of("t");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
this._print("Can not scale.");
|
|
647
|
+
return Cons.nil;
|
|
648
|
+
} catch {
|
|
649
|
+
this._print("Can not scale.");
|
|
650
|
+
return Cons.nil;
|
|
651
|
+
}
|
|
652
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
653
|
+
return Cons.nil;
|
|
654
|
+
}
|
|
655
|
+
gShadowBlur(args) {
|
|
656
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
657
|
+
if (this.isOpen) {
|
|
658
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
659
|
+
this.ctx.Blur = args.car;
|
|
660
|
+
this.ctx.save();
|
|
661
|
+
return InterpretedSymbol.of("t");
|
|
662
|
+
}
|
|
663
|
+
this._print("Can not set shadow blur.");
|
|
664
|
+
return Cons.nil;
|
|
665
|
+
}
|
|
666
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
667
|
+
return Cons.nil;
|
|
668
|
+
}
|
|
669
|
+
gShadowColor(args) {
|
|
670
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
671
|
+
if (this.isOpen) try {
|
|
672
|
+
if (args.length() === 1) {
|
|
673
|
+
const aColor = this.selectColor(args);
|
|
674
|
+
this.ctx.shadowColor = aColor;
|
|
675
|
+
this.ctx.save();
|
|
676
|
+
return InterpretedSymbol.of("t");
|
|
677
|
+
}
|
|
678
|
+
this._print("Can not set shadow color.");
|
|
679
|
+
return Cons.nil;
|
|
680
|
+
} catch {
|
|
681
|
+
this._print("Can not set shadow color.");
|
|
682
|
+
return Cons.nil;
|
|
683
|
+
}
|
|
684
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
685
|
+
return Cons.nil;
|
|
686
|
+
}
|
|
687
|
+
gShadowOffsetX(args) {
|
|
688
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
689
|
+
if (this.isOpen) {
|
|
690
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
691
|
+
this.ctx.shadowOffsetX = args.car;
|
|
692
|
+
this.ctx.save();
|
|
693
|
+
return InterpretedSymbol.of("t");
|
|
694
|
+
}
|
|
695
|
+
this._print("Can not set shadow offsetX.");
|
|
696
|
+
return Cons.nil;
|
|
697
|
+
}
|
|
698
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
699
|
+
return Cons.nil;
|
|
700
|
+
}
|
|
701
|
+
gShadowOffsetY(args) {
|
|
702
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
703
|
+
if (this.isOpen) {
|
|
704
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
705
|
+
this.ctx.shadowOffsetY = args.car;
|
|
706
|
+
this.ctx.save();
|
|
707
|
+
return InterpretedSymbol.of("t");
|
|
708
|
+
}
|
|
709
|
+
this._print("Can not set shadow offsetY.");
|
|
710
|
+
return Cons.nil;
|
|
711
|
+
}
|
|
712
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
713
|
+
return Cons.nil;
|
|
714
|
+
}
|
|
715
|
+
gSleep(args) {
|
|
716
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
717
|
+
if (this.isOpen) {
|
|
718
|
+
const sleep = (ms) => {
|
|
719
|
+
const time = Date.now() + ms;
|
|
720
|
+
while (Date.now() < time);
|
|
721
|
+
};
|
|
722
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
723
|
+
sleep(args.car);
|
|
724
|
+
return InterpretedSymbol.of("t");
|
|
725
|
+
}
|
|
726
|
+
this._print("Can not sleep");
|
|
727
|
+
return Cons.nil;
|
|
728
|
+
}
|
|
729
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
730
|
+
return Cons.nil;
|
|
731
|
+
}
|
|
732
|
+
gStartPath() {
|
|
733
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
734
|
+
if (this.isOpen) try {
|
|
735
|
+
this.ctx.beginPath();
|
|
736
|
+
this.ctx.save();
|
|
737
|
+
return InterpretedSymbol.of("t");
|
|
738
|
+
} catch {
|
|
739
|
+
this._print("Can not start path.");
|
|
740
|
+
return Cons.nil;
|
|
741
|
+
}
|
|
742
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
743
|
+
return Cons.nil;
|
|
744
|
+
}
|
|
745
|
+
gStroke() {
|
|
746
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
747
|
+
if (this.isOpen) try {
|
|
748
|
+
this.ctx.stroke();
|
|
749
|
+
this.ctx.save();
|
|
750
|
+
return InterpretedSymbol.of("t");
|
|
751
|
+
} catch {
|
|
752
|
+
this._print("Can not stroke.");
|
|
753
|
+
return Cons.nil;
|
|
754
|
+
}
|
|
755
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
756
|
+
return Cons.nil;
|
|
757
|
+
}
|
|
758
|
+
gStrokeColor(args) {
|
|
759
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
760
|
+
if (this.isOpen) try {
|
|
761
|
+
if (args.length() >= 1) {
|
|
762
|
+
const aColor = this.selectColor(args);
|
|
763
|
+
this.ctx.strokeStyle = aColor;
|
|
764
|
+
this.ctx.save();
|
|
765
|
+
return InterpretedSymbol.of("t");
|
|
766
|
+
}
|
|
767
|
+
this._print("Can not set stroke color");
|
|
768
|
+
return Cons.nil;
|
|
769
|
+
} catch {
|
|
770
|
+
this._print("Can not set stroke color");
|
|
771
|
+
return Cons.nil;
|
|
772
|
+
}
|
|
773
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
774
|
+
return Cons.nil;
|
|
775
|
+
}
|
|
776
|
+
gStrokeRect(args) {
|
|
777
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
778
|
+
if (this.isOpen) try {
|
|
779
|
+
if (args.length() === 4) {
|
|
780
|
+
const a0 = args.car;
|
|
781
|
+
const cdr1 = args.cdr;
|
|
782
|
+
const a1 = cdr1.car;
|
|
783
|
+
const cdr2 = cdr1.cdr;
|
|
784
|
+
const a2 = cdr2.car;
|
|
785
|
+
const a3 = cdr2.cdr.car;
|
|
786
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
787
|
+
this.ctx.strokeRect(a0, a1, a2, a3);
|
|
788
|
+
this.ctx.save();
|
|
789
|
+
return InterpretedSymbol.of("t");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
this._print("Can not draw stroke rectangle.");
|
|
793
|
+
return Cons.nil;
|
|
794
|
+
} catch {
|
|
795
|
+
this._print("Can not draw stroke rectangle.");
|
|
796
|
+
return Cons.nil;
|
|
797
|
+
}
|
|
798
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
799
|
+
return Cons.nil;
|
|
800
|
+
}
|
|
801
|
+
gStrokeText(args) {
|
|
802
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
803
|
+
if (this.isOpen) try {
|
|
804
|
+
if (args.length() === 3) {
|
|
805
|
+
const a0 = args.car;
|
|
806
|
+
const cdr1 = args.cdr;
|
|
807
|
+
const a1 = cdr1.car;
|
|
808
|
+
const a2 = cdr1.cdr.car;
|
|
809
|
+
if (Cons.isString(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) {
|
|
810
|
+
this.ctx.strokeText(a0, a1, a2);
|
|
811
|
+
this.ctx.save();
|
|
812
|
+
return InterpretedSymbol.of("t");
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
this._print("Can not draw fill text.");
|
|
816
|
+
return Cons.nil;
|
|
817
|
+
} catch {
|
|
818
|
+
this._print("Can not draw fill text.");
|
|
819
|
+
return Cons.nil;
|
|
820
|
+
}
|
|
821
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
822
|
+
return Cons.nil;
|
|
823
|
+
}
|
|
824
|
+
gStrokeTri(args) {
|
|
825
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
826
|
+
if (this.isOpen) try {
|
|
827
|
+
if (args.length() === 6) {
|
|
828
|
+
const a0 = args.car;
|
|
829
|
+
const cdr1 = args.cdr;
|
|
830
|
+
const a1 = cdr1.car;
|
|
831
|
+
const cdr2 = cdr1.cdr;
|
|
832
|
+
const a2 = cdr2.car;
|
|
833
|
+
const cdr3 = cdr2.cdr;
|
|
834
|
+
const a3 = cdr3.car;
|
|
835
|
+
const cdr4 = cdr3.cdr;
|
|
836
|
+
const a4 = cdr4.car;
|
|
837
|
+
const a5 = cdr4.cdr.car;
|
|
838
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3) && Cons.isNumber(a4) && Cons.isNumber(a5)) {
|
|
839
|
+
this.ctx.beginPath();
|
|
840
|
+
this.ctx.moveTo(a0, a1);
|
|
841
|
+
this.ctx.lineTo(a2, a3);
|
|
842
|
+
this.ctx.lineTo(a4, a5);
|
|
843
|
+
this.ctx.closePath();
|
|
844
|
+
this.ctx.stroke();
|
|
845
|
+
this.ctx.save();
|
|
846
|
+
return InterpretedSymbol.of("t");
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
this._print("Can not draw stroke triangle.");
|
|
850
|
+
return Cons.nil;
|
|
851
|
+
} catch {
|
|
852
|
+
this._print("Can not draw stroke triangle.");
|
|
853
|
+
return Cons.nil;
|
|
854
|
+
}
|
|
855
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
856
|
+
return Cons.nil;
|
|
857
|
+
}
|
|
858
|
+
gTextAlign(args) {
|
|
859
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
860
|
+
if (this.isOpen) try {
|
|
861
|
+
if (args.length() === 1 && Cons.isString(args.car)) {
|
|
862
|
+
this.ctx.textAlign = args.car;
|
|
863
|
+
this.ctx.save();
|
|
864
|
+
return InterpretedSymbol.of("t");
|
|
865
|
+
}
|
|
866
|
+
this._print("Can not set text align.");
|
|
867
|
+
return Cons.nil;
|
|
868
|
+
} catch {
|
|
869
|
+
this._print("Can not set text align.");
|
|
870
|
+
return Cons.nil;
|
|
871
|
+
}
|
|
872
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
873
|
+
return Cons.nil;
|
|
874
|
+
}
|
|
875
|
+
gTextBaseline(args) {
|
|
876
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
877
|
+
if (this.isOpen) try {
|
|
878
|
+
if (args.length() === 1 && Cons.isString(args.car)) {
|
|
879
|
+
this.ctx.textBaseline = args.car;
|
|
880
|
+
this.ctx.save();
|
|
881
|
+
return InterpretedSymbol.of("t");
|
|
882
|
+
}
|
|
883
|
+
this._print("Can not set text baseline.");
|
|
884
|
+
return Cons.nil;
|
|
885
|
+
} catch {
|
|
886
|
+
this._print("Can not set text baseline.");
|
|
887
|
+
return Cons.nil;
|
|
888
|
+
}
|
|
889
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
890
|
+
return Cons.nil;
|
|
891
|
+
}
|
|
892
|
+
gTextDirection(args) {
|
|
893
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
894
|
+
if (this.isOpen) try {
|
|
895
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
896
|
+
const aString = args.car === 0 ? "inherit" : args.car > 0 ? "rtl" : "ltr";
|
|
897
|
+
this.ctx.direction = aString;
|
|
898
|
+
this.ctx.save();
|
|
899
|
+
return InterpretedSymbol.of("t");
|
|
900
|
+
}
|
|
901
|
+
this._print("Can not set text direction.");
|
|
902
|
+
return Cons.nil;
|
|
903
|
+
} catch {
|
|
904
|
+
this._print("Can not set text direction.");
|
|
905
|
+
return Cons.nil;
|
|
906
|
+
}
|
|
907
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
908
|
+
return Cons.nil;
|
|
909
|
+
}
|
|
910
|
+
gTextFont(args) {
|
|
911
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
912
|
+
if (this.isOpen) try {
|
|
913
|
+
if (args.length() === 1 && Cons.isString(args.car)) {
|
|
914
|
+
this.ctx.font = args.car;
|
|
915
|
+
this.ctx.save();
|
|
916
|
+
return InterpretedSymbol.of("t");
|
|
917
|
+
}
|
|
918
|
+
this._print("Can not set text font.");
|
|
919
|
+
return Cons.nil;
|
|
920
|
+
} catch {
|
|
921
|
+
this._print("Can not set text font.");
|
|
922
|
+
return Cons.nil;
|
|
923
|
+
}
|
|
924
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
925
|
+
return Cons.nil;
|
|
926
|
+
}
|
|
927
|
+
gTranslate(args) {
|
|
928
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
929
|
+
if (this.isOpen) try {
|
|
930
|
+
if (args.length() === 2) {
|
|
931
|
+
const a0 = args.car;
|
|
932
|
+
const a1 = args.cdr.car;
|
|
933
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1)) {
|
|
934
|
+
this.ctx.translate(a0, a1);
|
|
935
|
+
this.ctx.save();
|
|
936
|
+
return InterpretedSymbol.of("t");
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
this._print("Can not translate.");
|
|
940
|
+
return Cons.nil;
|
|
941
|
+
} catch {
|
|
942
|
+
this._print("Can not translate.");
|
|
943
|
+
return Cons.nil;
|
|
944
|
+
}
|
|
945
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
946
|
+
return Cons.nil;
|
|
947
|
+
}
|
|
948
|
+
gRect(args) {
|
|
949
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
950
|
+
if (this.isOpen) try {
|
|
951
|
+
if (args.length() === 4) {
|
|
952
|
+
const a0 = args.car;
|
|
953
|
+
const cdr1 = args.cdr;
|
|
954
|
+
const a1 = cdr1.car;
|
|
955
|
+
const cdr2 = cdr1.cdr;
|
|
956
|
+
const a2 = cdr2.car;
|
|
957
|
+
const a3 = cdr2.cdr.car;
|
|
958
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) {
|
|
959
|
+
this.ctx.rect(a0, a1, a2, a3);
|
|
960
|
+
this.ctx.save();
|
|
961
|
+
return InterpretedSymbol.of("t");
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
this._print("Can not draw rectangle.");
|
|
965
|
+
return Cons.nil;
|
|
966
|
+
} catch {
|
|
967
|
+
this._print("Can not draw rectangle.");
|
|
968
|
+
return Cons.nil;
|
|
969
|
+
}
|
|
970
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
971
|
+
return Cons.nil;
|
|
972
|
+
}
|
|
973
|
+
gRotate(args) {
|
|
974
|
+
if (!this.checkSupport()) return Cons.nil;
|
|
975
|
+
if (this.isOpen) try {
|
|
976
|
+
if (args.length() === 1 && Cons.isNumber(args.car)) {
|
|
977
|
+
this.ctx.rotate(Math.PI / 180 * args.car);
|
|
978
|
+
this.ctx.save();
|
|
979
|
+
return InterpretedSymbol.of("t");
|
|
980
|
+
}
|
|
981
|
+
this._print("Can not rotate.");
|
|
982
|
+
return Cons.nil;
|
|
983
|
+
} catch {
|
|
984
|
+
this._print("Can not rotate.");
|
|
985
|
+
return Cons.nil;
|
|
986
|
+
}
|
|
987
|
+
this._print("The canvas is closed and cannot be executed.");
|
|
988
|
+
return Cons.nil;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Parses a color spec from the head of the argument Cons. Accepts
|
|
992
|
+
* (1 string), (3 numbers — rgb), or (4 numbers — rgba); falls back to
|
|
993
|
+
* `'black'` on anything else (legacy behavior).
|
|
994
|
+
* @param args - the argument Cons to parse
|
|
995
|
+
* @return CSS color string
|
|
996
|
+
*/
|
|
997
|
+
selectColor(args) {
|
|
998
|
+
let aColor = "black";
|
|
999
|
+
const len = args.length();
|
|
1000
|
+
const a0 = args.car;
|
|
1001
|
+
if (len === 1 && Cons.isString(a0)) aColor = a0;
|
|
1002
|
+
else if (len === 3) {
|
|
1003
|
+
const cdr1 = args.cdr;
|
|
1004
|
+
const a1 = cdr1.car;
|
|
1005
|
+
const a2 = cdr1.cdr.car;
|
|
1006
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2)) aColor = `rgb(${a0}, ${a1}, ${a2})`;
|
|
1007
|
+
else this._print("Can not set color. set color \"black\".");
|
|
1008
|
+
} else if (len === 4) {
|
|
1009
|
+
const cdr1 = args.cdr;
|
|
1010
|
+
const a1 = cdr1.car;
|
|
1011
|
+
const cdr2 = cdr1.cdr;
|
|
1012
|
+
const a2 = cdr2.car;
|
|
1013
|
+
const a3 = cdr2.cdr.car;
|
|
1014
|
+
if (Cons.isNumber(a0) && Cons.isNumber(a1) && Cons.isNumber(a2) && Cons.isNumber(a3)) aColor = `rgba(${a0}, ${a1}, ${a2}, ${a3})`;
|
|
1015
|
+
else this._print("Can not set color. set color \"black\".");
|
|
1016
|
+
} else this._print("Can not set color. set color \"black\".");
|
|
1017
|
+
return aColor;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Builds the dispatch table.
|
|
1021
|
+
* @return the dispatch table
|
|
1022
|
+
*/
|
|
1023
|
+
static setup() {
|
|
1024
|
+
const aTable = /* @__PURE__ */ new Map();
|
|
1025
|
+
aTable.set(InterpretedSymbol.of("galpha"), "gAlpha");
|
|
1026
|
+
aTable.set(InterpretedSymbol.of("garc"), "gArc");
|
|
1027
|
+
aTable.set(InterpretedSymbol.of("garc-to"), "gArcTo");
|
|
1028
|
+
aTable.set(InterpretedSymbol.of("gbezcurve-to"), "gBezCurveTo");
|
|
1029
|
+
aTable.set(InterpretedSymbol.of("gclear"), "gClear");
|
|
1030
|
+
aTable.set(InterpretedSymbol.of("gclose"), "gClose");
|
|
1031
|
+
aTable.set(InterpretedSymbol.of("gcolor"), "gColor");
|
|
1032
|
+
aTable.set(InterpretedSymbol.of("gfill"), "gFill");
|
|
1033
|
+
aTable.set(InterpretedSymbol.of("gfill-color"), "gFillColor");
|
|
1034
|
+
aTable.set(InterpretedSymbol.of("gfill-rect"), "gFillRect");
|
|
1035
|
+
aTable.set(InterpretedSymbol.of("gfill-text"), "gFillText");
|
|
1036
|
+
aTable.set(InterpretedSymbol.of("gfill-tri"), "gFillTri");
|
|
1037
|
+
aTable.set(InterpretedSymbol.of("gfinish-path"), "gFinishPath");
|
|
1038
|
+
aTable.set(InterpretedSymbol.of("gimage"), "gImage");
|
|
1039
|
+
aTable.set(InterpretedSymbol.of("gmove-to"), "gMoveTo");
|
|
1040
|
+
aTable.set(InterpretedSymbol.of("gline-to"), "gLineTo");
|
|
1041
|
+
aTable.set(InterpretedSymbol.of("gline-cap"), "gLineCap");
|
|
1042
|
+
aTable.set(InterpretedSymbol.of("gline-join"), "gLineJoin");
|
|
1043
|
+
aTable.set(InterpretedSymbol.of("gline-width"), "gLineWidth");
|
|
1044
|
+
aTable.set(InterpretedSymbol.of("gopen"), "gOpen");
|
|
1045
|
+
aTable.set(InterpretedSymbol.of("gpattern"), "gPattern");
|
|
1046
|
+
aTable.set(InterpretedSymbol.of("gquadcurve-to"), "gQuadCurveTo");
|
|
1047
|
+
aTable.set(InterpretedSymbol.of("gsave-jpeg"), "gSaveJpeg");
|
|
1048
|
+
aTable.set(InterpretedSymbol.of("gsave-png"), "gSavePng");
|
|
1049
|
+
aTable.set(InterpretedSymbol.of("gscale"), "gScale");
|
|
1050
|
+
aTable.set(InterpretedSymbol.of("gshadow-blur"), "gShadowBlur");
|
|
1051
|
+
aTable.set(InterpretedSymbol.of("gshadow-color"), "gShadowColor");
|
|
1052
|
+
aTable.set(InterpretedSymbol.of("gshadow-offsetx"), "gShadowOffsetX");
|
|
1053
|
+
aTable.set(InterpretedSymbol.of("gshadow-offsety"), "gShadowOffsetY");
|
|
1054
|
+
aTable.set(InterpretedSymbol.of("gsleep"), "gSleep");
|
|
1055
|
+
aTable.set(InterpretedSymbol.of("gstart-path"), "gStartPath");
|
|
1056
|
+
aTable.set(InterpretedSymbol.of("gstroke"), "gStroke");
|
|
1057
|
+
aTable.set(InterpretedSymbol.of("gstroke-color"), "gStrokeColor");
|
|
1058
|
+
aTable.set(InterpretedSymbol.of("gstroke-rect"), "gStrokeRect");
|
|
1059
|
+
aTable.set(InterpretedSymbol.of("gstroke-text"), "gStrokeText");
|
|
1060
|
+
aTable.set(InterpretedSymbol.of("gstroke-tri"), "gStrokeTri");
|
|
1061
|
+
aTable.set(InterpretedSymbol.of("gtext-align"), "gTextAlign");
|
|
1062
|
+
aTable.set(InterpretedSymbol.of("gtext-dire"), "gTextDirection");
|
|
1063
|
+
aTable.set(InterpretedSymbol.of("gtext-font"), "gTextFont");
|
|
1064
|
+
aTable.set(InterpretedSymbol.of("gtext-line"), "gTextBaseline");
|
|
1065
|
+
aTable.set(InterpretedSymbol.of("gtranslate"), "gTranslate");
|
|
1066
|
+
aTable.set(InterpretedSymbol.of("grect"), "gRect");
|
|
1067
|
+
aTable.set(InterpretedSymbol.of("grotate"), "gRotate");
|
|
1068
|
+
return aTable;
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
//#endregion
|
|
1072
|
+
//#region src/index.ts
|
|
1073
|
+
/**
|
|
1074
|
+
* Creates a Canvas2D drawing plugin bound to the given canvas. Register it on
|
|
1075
|
+
* a `LispInterpreter` via `interpreter.use(plugin)` to make the `g…` drawing
|
|
1076
|
+
* primitives callable from Lisp source.
|
|
1077
|
+
* @param options - the canvas to draw to
|
|
1078
|
+
* @return a KeiLispPlugin that handles the `g…` symbols
|
|
1079
|
+
*/
|
|
1080
|
+
function createGraphicsPlugin(options) {
|
|
1081
|
+
return new GraphicsPlugin(options.canvas);
|
|
1082
|
+
}
|
|
1083
|
+
//#endregion
|
|
1084
|
+
export { GraphicsPlugin, createGraphicsPlugin };
|
|
1085
|
+
|
|
1086
|
+
//# sourceMappingURL=index.js.map
|