@edadma/logo 0.2.2 → 0.2.4
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/dist/main.js +1808 -1748
- package/dist/main.js.map +3 -3
- package/package.json +5 -25
- package/src/main/scala/io/github/edadma/logo/LogoJS.scala +67 -11
- package/dist/react/LogoCanvas.js +0 -74
- package/dist/react/index.js +0 -2
- package/react/LogoCanvas.tsx +0 -174
- package/react/index.ts +0 -2
package/package.json
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edadma/logo",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Logo programming language interpreter
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Logo programming language interpreter",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"module": "dist/main.js",
|
|
7
7
|
"types": "src/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"src"
|
|
11
|
-
"react"
|
|
10
|
+
"src"
|
|
12
11
|
],
|
|
13
12
|
"exports": {
|
|
14
13
|
".": {
|
|
15
14
|
"import": "./dist/main.js",
|
|
16
15
|
"types": "./src/index.d.ts"
|
|
17
|
-
},
|
|
18
|
-
"./react": {
|
|
19
|
-
"import": "./react/index.ts",
|
|
20
|
-
"types": "./react/index.ts"
|
|
21
16
|
}
|
|
22
17
|
},
|
|
23
18
|
"scripts": {
|
|
24
|
-
"build": "
|
|
25
|
-
"build:scala": "cd .. && sbt logoJS/fullLinkJS && cp js/target/scala-3.7.4/logo-opt/main.js js/target/scala-3.7.4/logo-opt/main.js.map js/dist/",
|
|
26
|
-
"build:react": "tsc --project tsconfig.react.json",
|
|
19
|
+
"build": "cd .. && sbt logoJS/fullLinkJS && cp js/target/scala-3.7.4/logo-opt/main.js js/target/scala-3.7.4/logo-opt/main.js.map js/dist/",
|
|
27
20
|
"prepublishOnly": "npm run build"
|
|
28
21
|
},
|
|
29
22
|
"keywords": [
|
|
@@ -32,7 +25,6 @@
|
|
|
32
25
|
"turtle",
|
|
33
26
|
"graphics",
|
|
34
27
|
"education",
|
|
35
|
-
"react",
|
|
36
28
|
"canvas"
|
|
37
29
|
],
|
|
38
30
|
"author": "Edward A. Maxedon, Sr.",
|
|
@@ -44,17 +36,5 @@
|
|
|
44
36
|
"bugs": {
|
|
45
37
|
"url": "https://github.com/edadma/logo/issues"
|
|
46
38
|
},
|
|
47
|
-
"homepage": "https://github.com/edadma/logo#readme"
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"react": ">=17.0.0"
|
|
50
|
-
},
|
|
51
|
-
"peerDependenciesMeta": {
|
|
52
|
-
"react": {
|
|
53
|
-
"optional": true
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@types/react": "^18.2.0",
|
|
58
|
-
"typescript": "^5.0.0"
|
|
59
|
-
}
|
|
39
|
+
"homepage": "https://github.com/edadma/logo#readme"
|
|
60
40
|
}
|
|
@@ -137,6 +137,12 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
|
|
|
137
137
|
def clearEventHandler(): Unit =
|
|
138
138
|
eventHandler = None
|
|
139
139
|
|
|
140
|
+
/** Set a global variable */
|
|
141
|
+
def setVariable(name: String, value: Any): Unit = logo.setVariable(name, value)
|
|
142
|
+
|
|
143
|
+
/** Get a global variable */
|
|
144
|
+
def getVariable(name: String): Option[LogoValue] = logo.getVariable(name)
|
|
145
|
+
|
|
140
146
|
/** Force a render */
|
|
141
147
|
def render(): Unit =
|
|
142
148
|
val width = canvas.width
|
|
@@ -306,22 +312,72 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
|
|
|
306
312
|
ctx.stroke()
|
|
307
313
|
|
|
308
314
|
private def drawTurtle(x: Double, y: Double, heading: Double): Unit =
|
|
309
|
-
val w = 15.0
|
|
310
|
-
val h = 20.0
|
|
311
|
-
|
|
312
315
|
ctx.save()
|
|
313
316
|
ctx.translate(x, y)
|
|
314
|
-
ctx.rotate(heading
|
|
317
|
+
ctx.rotate(heading + Pi / 2)
|
|
315
318
|
|
|
319
|
+
// Tail (behind shell)
|
|
316
320
|
ctx.beginPath()
|
|
317
|
-
ctx.moveTo(0,
|
|
318
|
-
ctx.lineTo(
|
|
319
|
-
ctx.
|
|
320
|
-
ctx.lineTo(w / 2, h / 2)
|
|
321
|
-
ctx.closePath()
|
|
322
|
-
|
|
323
|
-
ctx.strokeStyle = "green"
|
|
321
|
+
ctx.moveTo(0, 10)
|
|
322
|
+
ctx.lineTo(0, 14)
|
|
323
|
+
ctx.strokeStyle = "#4a7a44"
|
|
324
324
|
ctx.lineWidth = 2
|
|
325
|
+
ctx.lineCap = "round"
|
|
326
|
+
ctx.stroke()
|
|
327
|
+
|
|
328
|
+
// Legs (behind shell)
|
|
329
|
+
ctx.fillStyle = "#4a7a44"
|
|
330
|
+
ctx.strokeStyle = "#1a3a18"
|
|
331
|
+
ctx.lineWidth = 1
|
|
332
|
+
// Front legs
|
|
333
|
+
ctx.beginPath()
|
|
334
|
+
ctx.ellipse(-7, -6, 3, 5, 0.4, 0, 2 * Pi)
|
|
335
|
+
ctx.fill()
|
|
336
|
+
ctx.stroke()
|
|
337
|
+
ctx.beginPath()
|
|
338
|
+
ctx.ellipse(7, -6, 3, 5, -0.4, 0, 2 * Pi)
|
|
339
|
+
ctx.fill()
|
|
340
|
+
ctx.stroke()
|
|
341
|
+
// Back legs
|
|
342
|
+
ctx.beginPath()
|
|
343
|
+
ctx.ellipse(-6, 6, 3, 4, 0.3, 0, 2 * Pi)
|
|
344
|
+
ctx.fill()
|
|
345
|
+
ctx.stroke()
|
|
346
|
+
ctx.beginPath()
|
|
347
|
+
ctx.ellipse(6, 6, 3, 4, -0.3, 0, 2 * Pi)
|
|
348
|
+
ctx.fill()
|
|
349
|
+
ctx.stroke()
|
|
350
|
+
|
|
351
|
+
// Head (behind shell)
|
|
352
|
+
ctx.beginPath()
|
|
353
|
+
ctx.ellipse(0, -14, 4, 5, 0, 0, 2 * Pi)
|
|
354
|
+
ctx.fillStyle = "#4a7a44"
|
|
355
|
+
ctx.fill()
|
|
356
|
+
ctx.strokeStyle = "#1a3a18"
|
|
357
|
+
ctx.lineWidth = 1.5
|
|
358
|
+
ctx.stroke()
|
|
359
|
+
|
|
360
|
+
// Eyes
|
|
361
|
+
ctx.fillStyle = "black"
|
|
362
|
+
ctx.beginPath()
|
|
363
|
+
ctx.arc(-1.5, -15, 1, 0, 2 * Pi)
|
|
364
|
+
ctx.arc(1.5, -15, 1, 0, 2 * Pi)
|
|
365
|
+
ctx.fill()
|
|
366
|
+
|
|
367
|
+
// Shell (on top)
|
|
368
|
+
ctx.beginPath()
|
|
369
|
+
ctx.ellipse(0, 0, 8, 10, 0, 0, 2 * Pi)
|
|
370
|
+
ctx.fillStyle = "#2d5a27"
|
|
371
|
+
ctx.fill()
|
|
372
|
+
ctx.strokeStyle = "#1a3a18"
|
|
373
|
+
ctx.lineWidth = 1.5
|
|
374
|
+
ctx.stroke()
|
|
375
|
+
|
|
376
|
+
// Shell pattern
|
|
377
|
+
ctx.strokeStyle = "#3d7a37"
|
|
378
|
+
ctx.lineWidth = 1
|
|
379
|
+
ctx.beginPath()
|
|
380
|
+
ctx.ellipse(0, 0, 5, 6, 0, 0, 2 * Pi)
|
|
325
381
|
ctx.stroke()
|
|
326
382
|
|
|
327
383
|
ctx.restore()
|
package/dist/react/LogoCanvas.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useEffect, useImperativeHandle, forwardRef } from 'react';
|
|
3
|
-
export const LogoCanvas = forwardRef(({ width = 600, height = 400, program, pathRendering = true, onError, onComplete, className, style, }, ref) => {
|
|
4
|
-
const canvasRef = useRef(null);
|
|
5
|
-
const logoRef = useRef(null);
|
|
6
|
-
// Initialize Logo instance
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (canvasRef.current && typeof Logo !== 'undefined') {
|
|
9
|
-
logoRef.current = new Logo(canvasRef.current);
|
|
10
|
-
logoRef.current.setPathRendering(pathRendering);
|
|
11
|
-
}
|
|
12
|
-
}, []);
|
|
13
|
-
// Update path rendering setting
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (logoRef.current) {
|
|
16
|
-
logoRef.current.setPathRendering(pathRendering);
|
|
17
|
-
}
|
|
18
|
-
}, [pathRendering]);
|
|
19
|
-
// Run program when it changes
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (logoRef.current && program !== undefined) {
|
|
22
|
-
try {
|
|
23
|
-
logoRef.current.clear();
|
|
24
|
-
logoRef.current.run(program);
|
|
25
|
-
onComplete?.();
|
|
26
|
-
}
|
|
27
|
-
catch (e) {
|
|
28
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}, [program, onError, onComplete]);
|
|
32
|
-
// Expose methods via ref
|
|
33
|
-
useImperativeHandle(ref, () => ({
|
|
34
|
-
execute: (command) => {
|
|
35
|
-
if (logoRef.current) {
|
|
36
|
-
try {
|
|
37
|
-
logoRef.current.execute(command);
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
run: (prog) => {
|
|
45
|
-
if (logoRef.current) {
|
|
46
|
-
try {
|
|
47
|
-
logoRef.current.run(prog);
|
|
48
|
-
onComplete?.();
|
|
49
|
-
}
|
|
50
|
-
catch (e) {
|
|
51
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
clear: () => {
|
|
56
|
-
logoRef.current?.clear();
|
|
57
|
-
},
|
|
58
|
-
getDrawing: () => {
|
|
59
|
-
return logoRef.current?.getDrawing() ?? { lines: [], labels: [] };
|
|
60
|
-
},
|
|
61
|
-
getTurtle: () => {
|
|
62
|
-
return (logoRef.current?.getTurtle() ?? {
|
|
63
|
-
x: 0,
|
|
64
|
-
y: 0,
|
|
65
|
-
heading: Math.PI / 2,
|
|
66
|
-
visible: true,
|
|
67
|
-
});
|
|
68
|
-
},
|
|
69
|
-
getLogo: () => logoRef.current,
|
|
70
|
-
}));
|
|
71
|
-
return (_jsx("canvas", { ref: canvasRef, width: width, height: height, className: className, style: { backgroundColor: 'white', ...style } }));
|
|
72
|
-
});
|
|
73
|
-
LogoCanvas.displayName = 'LogoCanvas';
|
|
74
|
-
export default LogoCanvas;
|
package/dist/react/index.js
DELETED
package/react/LogoCanvas.tsx
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import React, { useRef, useEffect, useImperativeHandle, forwardRef } from 'react';
|
|
2
|
-
|
|
3
|
-
// Type definitions for the Logo class from Scala.js
|
|
4
|
-
interface LogoDrawing {
|
|
5
|
-
lines: Array<{
|
|
6
|
-
x1: number;
|
|
7
|
-
y1: number;
|
|
8
|
-
x2: number;
|
|
9
|
-
y2: number;
|
|
10
|
-
color: string;
|
|
11
|
-
width: number;
|
|
12
|
-
}>;
|
|
13
|
-
labels: Array<{
|
|
14
|
-
x: number;
|
|
15
|
-
y: number;
|
|
16
|
-
heading: number;
|
|
17
|
-
text: string;
|
|
18
|
-
}>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface TurtleState {
|
|
22
|
-
x: number;
|
|
23
|
-
y: number;
|
|
24
|
-
heading: number;
|
|
25
|
-
visible: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface LogoInstance {
|
|
29
|
-
run(program: string): void;
|
|
30
|
-
execute(command: string): void;
|
|
31
|
-
clear(): void;
|
|
32
|
-
render(): void;
|
|
33
|
-
setPathRendering(enabled: boolean): void;
|
|
34
|
-
setAutoRender(enabled: boolean): void;
|
|
35
|
-
getDrawing(): LogoDrawing;
|
|
36
|
-
getTurtle(): TurtleState;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
declare const Logo: new (canvas: HTMLCanvasElement) => LogoInstance;
|
|
40
|
-
|
|
41
|
-
export interface LogoCanvasProps {
|
|
42
|
-
/** Width of the canvas in pixels */
|
|
43
|
-
width?: number;
|
|
44
|
-
/** Height of the canvas in pixels */
|
|
45
|
-
height?: number;
|
|
46
|
-
/** Logo program to run */
|
|
47
|
-
program?: string;
|
|
48
|
-
/** Whether to use path-based rendering (smoother curves) */
|
|
49
|
-
pathRendering?: boolean;
|
|
50
|
-
/** Callback when an error occurs */
|
|
51
|
-
onError?: (error: Error) => void;
|
|
52
|
-
/** Callback when program execution completes */
|
|
53
|
-
onComplete?: () => void;
|
|
54
|
-
/** Additional CSS class for the canvas */
|
|
55
|
-
className?: string;
|
|
56
|
-
/** Additional inline styles for the canvas */
|
|
57
|
-
style?: React.CSSProperties;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface LogoCanvasRef {
|
|
61
|
-
/** Execute a Logo command */
|
|
62
|
-
execute: (command: string) => void;
|
|
63
|
-
/** Run a full Logo program */
|
|
64
|
-
run: (program: string) => void;
|
|
65
|
-
/** Clear the canvas and reset turtle */
|
|
66
|
-
clear: () => void;
|
|
67
|
-
/** Get the current drawing data */
|
|
68
|
-
getDrawing: () => LogoDrawing;
|
|
69
|
-
/** Get the current turtle state */
|
|
70
|
-
getTurtle: () => TurtleState;
|
|
71
|
-
/** Get the underlying Logo instance */
|
|
72
|
-
getLogo: () => LogoInstance | null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const LogoCanvas = forwardRef<LogoCanvasRef, LogoCanvasProps>(
|
|
76
|
-
(
|
|
77
|
-
{
|
|
78
|
-
width = 600,
|
|
79
|
-
height = 400,
|
|
80
|
-
program,
|
|
81
|
-
pathRendering = true,
|
|
82
|
-
onError,
|
|
83
|
-
onComplete,
|
|
84
|
-
className,
|
|
85
|
-
style,
|
|
86
|
-
},
|
|
87
|
-
ref
|
|
88
|
-
) => {
|
|
89
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
90
|
-
const logoRef = useRef<LogoInstance | null>(null);
|
|
91
|
-
|
|
92
|
-
// Initialize Logo instance
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (canvasRef.current && typeof Logo !== 'undefined') {
|
|
95
|
-
logoRef.current = new Logo(canvasRef.current);
|
|
96
|
-
logoRef.current.setPathRendering(pathRendering);
|
|
97
|
-
}
|
|
98
|
-
}, []);
|
|
99
|
-
|
|
100
|
-
// Update path rendering setting
|
|
101
|
-
useEffect(() => {
|
|
102
|
-
if (logoRef.current) {
|
|
103
|
-
logoRef.current.setPathRendering(pathRendering);
|
|
104
|
-
}
|
|
105
|
-
}, [pathRendering]);
|
|
106
|
-
|
|
107
|
-
// Run program when it changes
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
if (logoRef.current && program !== undefined) {
|
|
110
|
-
try {
|
|
111
|
-
logoRef.current.clear();
|
|
112
|
-
logoRef.current.run(program);
|
|
113
|
-
onComplete?.();
|
|
114
|
-
} catch (e) {
|
|
115
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}, [program, onError, onComplete]);
|
|
119
|
-
|
|
120
|
-
// Expose methods via ref
|
|
121
|
-
useImperativeHandle(ref, () => ({
|
|
122
|
-
execute: (command: string) => {
|
|
123
|
-
if (logoRef.current) {
|
|
124
|
-
try {
|
|
125
|
-
logoRef.current.execute(command);
|
|
126
|
-
} catch (e) {
|
|
127
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
run: (prog: string) => {
|
|
132
|
-
if (logoRef.current) {
|
|
133
|
-
try {
|
|
134
|
-
logoRef.current.run(prog);
|
|
135
|
-
onComplete?.();
|
|
136
|
-
} catch (e) {
|
|
137
|
-
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
clear: () => {
|
|
142
|
-
logoRef.current?.clear();
|
|
143
|
-
},
|
|
144
|
-
getDrawing: () => {
|
|
145
|
-
return logoRef.current?.getDrawing() ?? { lines: [], labels: [] };
|
|
146
|
-
},
|
|
147
|
-
getTurtle: () => {
|
|
148
|
-
return (
|
|
149
|
-
logoRef.current?.getTurtle() ?? {
|
|
150
|
-
x: 0,
|
|
151
|
-
y: 0,
|
|
152
|
-
heading: Math.PI / 2,
|
|
153
|
-
visible: true,
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
},
|
|
157
|
-
getLogo: () => logoRef.current,
|
|
158
|
-
}));
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<canvas
|
|
162
|
-
ref={canvasRef}
|
|
163
|
-
width={width}
|
|
164
|
-
height={height}
|
|
165
|
-
className={className}
|
|
166
|
-
style={{ backgroundColor: 'white', ...style }}
|
|
167
|
-
/>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
LogoCanvas.displayName = 'LogoCanvas';
|
|
173
|
-
|
|
174
|
-
export default LogoCanvas;
|
package/react/index.ts
DELETED