bring pkg directory under git
This commit is contained in:
parent
80eb81c6a8
commit
c647cc0ea5
2
pkg/.gitignore
vendored
Normal file
2
pkg/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.wasm
|
||||
*.wasm*
|
385
pkg/ludus.js
Normal file
385
pkg/ludus.js
Normal file
|
@ -0,0 +1,385 @@
|
|||
const mod = require("./rudus.js");
|
||||
|
||||
let res = null
|
||||
|
||||
let code = null
|
||||
|
||||
function run (source) {
|
||||
code = source
|
||||
const output = mod.run(source)
|
||||
res = JSON.parse(output)
|
||||
return res
|
||||
}
|
||||
|
||||
function stdout () {
|
||||
if (!res) return ""
|
||||
return res.io.stdout.data
|
||||
}
|
||||
|
||||
function turtle_commands () {
|
||||
if (!res) return []
|
||||
return res.io.turtle.data
|
||||
}
|
||||
|
||||
function result () {
|
||||
return res
|
||||
}
|
||||
|
||||
const turtle_init = {
|
||||
position: [0, 0],
|
||||
heading: 0,
|
||||
pendown: true,
|
||||
pencolor: "white",
|
||||
penwidth: 1,
|
||||
visible: true
|
||||
}
|
||||
|
||||
const colors = {
|
||||
black: [0, 0, 0, 255],
|
||||
silver: [192, 192, 192, 255],
|
||||
gray: [128, 128, 128, 255],
|
||||
white: [255, 255, 255, 255],
|
||||
maroon: [128, 0, 0, 255],
|
||||
red: [255, 0, 0, 255],
|
||||
purple: [128, 0, 128, 255],
|
||||
fuchsia: [255, 0, 255, 255],
|
||||
green: [0, 128, 0, 255],
|
||||
lime: [0, 255, 0, 255],
|
||||
olive: [128, 128, 0, 255],
|
||||
yellow: [255, 255, 0, 255],
|
||||
navy: [0, 0, 128, 255],
|
||||
blue: [0, 0, 255, 255],
|
||||
teal: [0, 128, 128, 255],
|
||||
aqua: [0, 255, 255, 255],
|
||||
}
|
||||
|
||||
function resolve_color (color) {
|
||||
if (typeof color === 'string') return colors[color]
|
||||
if (typeof color === 'number') return [color, color, color, 255]
|
||||
if (Array.isArray(color)) return color
|
||||
return [0, 0, 0, 255] // default to black?
|
||||
}
|
||||
|
||||
let background_color = "black"
|
||||
|
||||
function add (v1, v2) {
|
||||
const [x1, y1] = v1
|
||||
const [x2, y2] = v2
|
||||
return [x1 + x2, y1 + y2]
|
||||
}
|
||||
|
||||
function mult (vector, scalar) {
|
||||
const [x, y] = vector
|
||||
return [x * scalar, y * scalar]
|
||||
}
|
||||
|
||||
function unit_of (heading) {
|
||||
const turns = -heading + 0.25
|
||||
const radians = turn_to_rad(turns)
|
||||
return [Math.cos(radians), Math.sin(radians)]
|
||||
}
|
||||
|
||||
function command_to_state (prev_state, curr_command) {
|
||||
const verb = curr_command[0]
|
||||
switch (verb) {
|
||||
case "goto": {
|
||||
const [_, x, y] = curr_command
|
||||
return {...prev_state, position: [x, y]}
|
||||
}
|
||||
case "home": {
|
||||
return {...prev_state, position: [0, 0], heading: 0}
|
||||
}
|
||||
case "right": {
|
||||
const [_, angle] = curr_command
|
||||
const {heading} = prev_state
|
||||
return {...prev_state, heading: heading + angle}
|
||||
}
|
||||
case "left": {
|
||||
const [_, angle] = curr_command
|
||||
const {heading} = prev_state
|
||||
return {...prev_state, heading: heading - angle}
|
||||
}
|
||||
case "forward": {
|
||||
const [_, steps] = curr_command
|
||||
const {heading, position} = prev_state
|
||||
const unit = unit_of(heading)
|
||||
const move = mult(unit, steps)
|
||||
return {...prev_state, position: add(position, move)}
|
||||
}
|
||||
case "back": {
|
||||
const [_, steps] = curr_command
|
||||
const {heading, position} = prev_state
|
||||
const unit = unit_of(heading)
|
||||
const move = mult(unit, -steps)
|
||||
return {...prev_state, position: add(position, move)}
|
||||
}
|
||||
case "penup": {
|
||||
return {...prev_state, pendown: false}
|
||||
}
|
||||
case "pendown": {
|
||||
return {...prev_state, pendown: true}
|
||||
}
|
||||
case "penwidth": {
|
||||
const [_, width] = curr_command
|
||||
return {...prev_state, penwidth: width}
|
||||
}
|
||||
case "pencolor": {
|
||||
const [_, color] = curr_command
|
||||
return {...prev_state, pencolor: color}
|
||||
}
|
||||
case "setheading": {
|
||||
const [_, heading] = curr_command
|
||||
return {...prev_state, heading: heading}
|
||||
}
|
||||
case "loadstate": {
|
||||
// console.log("LOADSTATE: ", curr_command)
|
||||
const [_, [x, y], heading, visible, pendown, penwidth, pencolor] = curr_command
|
||||
return {position: [x, y], heading, visible, pendown, penwidth, pencolor}
|
||||
}
|
||||
case "show": {
|
||||
return {...prev_state, visible: true}
|
||||
}
|
||||
case "hide": {
|
||||
return {...prev_state, visible: false}
|
||||
}
|
||||
case "background": {
|
||||
background_color = curr_command[1]
|
||||
return prev_state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function eq_vect (v1, v2) {
|
||||
const [x1, y1] = v1
|
||||
const [x2, y2] = v2
|
||||
return (x1 === x2) && (y1 === y2)
|
||||
}
|
||||
|
||||
function eq_color (c1, c2) {
|
||||
if (c1 === c2) return true
|
||||
const res1 = resolve_color(c1)
|
||||
const res2 = resolve_color(c2)
|
||||
for (let i = 0; i < res1.length; ++i) {
|
||||
if (res1[i] !== res2[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function states_to_call (prev, curr) {
|
||||
const calls = []
|
||||
// whose state should we use?
|
||||
// pen states will only differ on more than one property
|
||||
// if we use `loadstate`
|
||||
// my sense is `prev`, but that may change
|
||||
if (prev.pendown && !eq_vect(prev.position, curr.position)) {
|
||||
calls.push(["line", prev.position[0], prev.position[1], curr.position[0], curr.position[1]])
|
||||
}
|
||||
if (!eq_color(curr.pencolor, prev.pencolor)) {
|
||||
calls.push(["stroke", ...resolve_color(curr.pencolor)])
|
||||
}
|
||||
if (curr.penwidth !== prev.penwidth) {
|
||||
calls.push(["strokeWeight", curr.penwidth])
|
||||
}
|
||||
return calls
|
||||
}
|
||||
|
||||
const turtle_radius = 20
|
||||
|
||||
const turtle_angle = 0.385
|
||||
|
||||
let turtle_color = [255, 255, 255, 150]
|
||||
|
||||
function p5_call_root () {
|
||||
return [
|
||||
["background", ...resolve_color(background_color)],
|
||||
["push"],
|
||||
["rotate", Math.PI],
|
||||
["scale", -1, 1],
|
||||
["stroke", ...resolve_color(turtle_init.pencolor)],
|
||||
]
|
||||
}
|
||||
|
||||
function rotate (vector, heading) {
|
||||
const radians = turn_to_rad(heading)
|
||||
const [x, y] = vector
|
||||
return [
|
||||
(x * Math.cos (radians)) - (y * Math.sin (radians)),
|
||||
(x * Math.sin (radians)) + (y * Math.cos (radians))
|
||||
]
|
||||
}
|
||||
|
||||
function turn_to_rad (heading) {
|
||||
return (heading % 1) * 2 * Math.PI
|
||||
}
|
||||
|
||||
function turn_to_deg (heading) {
|
||||
return (heading % 1) * 360
|
||||
}
|
||||
|
||||
function hex (n) {
|
||||
return n.toString(16).padStart(2, "0")
|
||||
}
|
||||
|
||||
function svg_render_line (prev, curr) {
|
||||
if (!prev.pendown) return ""
|
||||
if (eq_vect(prev.position, curr.position)) return ""
|
||||
const {position: [x1, y1], pencolor, penwidth} = prev
|
||||
const {position: [x2, y2]} = curr
|
||||
const [r, g, b, a] = resolve_color(pencolor)
|
||||
return `
|
||||
<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#${hex(r)}${hex(g)}${hex(b)}" stroke-linecap="square" stroke-opacity="${a/255}" stroke-width="${penwidth}"/>
|
||||
`
|
||||
}
|
||||
|
||||
function escape_svg (svg) {
|
||||
return svg
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
}
|
||||
|
||||
function extract_ludus (svg) {
|
||||
const code = svg.split("<ludus>")[1]?.split("</ludus>")[0] ?? ""
|
||||
return code
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, `"`)
|
||||
.replace(/'/g, `'`)
|
||||
}
|
||||
|
||||
function svg_render_path (states) {
|
||||
const path = []
|
||||
for (let i = 1; i < states.length; ++i) {
|
||||
const prev = states[i - 1]
|
||||
const curr = states[i]
|
||||
path.push(svg_render_line(prev, curr))
|
||||
}
|
||||
return path.join("")
|
||||
}
|
||||
|
||||
function svg_render_turtle (state) {
|
||||
if (!state.visible) return ""
|
||||
const [fr, fg, fb, fa] = turtle_color
|
||||
const fill_alpha = fa/255
|
||||
const {heading, pencolor, position: [x, y], pendown, penwidth} = state
|
||||
const origin = [0, turtle_radius]
|
||||
const [x1, y1] = origin
|
||||
const [x2, y2] = rotate(origin, turtle_angle)
|
||||
const [x3, y3] = rotate(origin, -turtle_angle)
|
||||
const [pr, pg, pb, pa] = resolve_color(pencolor)
|
||||
const pen_alpha = pa/255
|
||||
const ink = pendown ? `<line x1="${x1}" y1="${y1}" x2="0" y2="0" stroke="#${hex(pr)}${hex(pg)}${hex(pb)}" stroke-linecap="round" stroke-opacity="${pen_alpha}" stroke-width="${penwidth}" />` : ""
|
||||
return `
|
||||
<g transform="translate(${x}, ${y})rotate(${-turn_to_deg(heading)})">
|
||||
<polygon points="${x1} ${y1} ${x2} ${y2} ${x3} ${y3}" stroke="none" fill="#${hex(fr)}${hex(fg)}${hex(fb)})" fill-opacity="${fill_alpha}"/>
|
||||
${ink}
|
||||
</g>
|
||||
`
|
||||
}
|
||||
|
||||
function svg (commands) {
|
||||
// console.log(commands)
|
||||
const states = [turtle_init]
|
||||
commands.reduce((prev_state, command) => {
|
||||
const new_state = command_to_state(prev_state, command)
|
||||
states.push(new_state)
|
||||
return new_state
|
||||
}, turtle_init)
|
||||
// console.log(states)
|
||||
const {maxX, maxY, minX, minY} = states.reduce((accum, {position: [x, y]}) => {
|
||||
accum.maxX = Math.max(accum.maxX, x)
|
||||
accum.maxY = Math.max(accum.maxY, y)
|
||||
accum.minX = Math.min(accum.minX, x)
|
||||
accum.minY = Math.min(accum.minY, y)
|
||||
return accum
|
||||
|
||||
}, {maxX: 0, maxY: 0, minX: 0, minY: 0})
|
||||
const [r, g, b, a] = resolve_color(background_color)
|
||||
if ((r+g+b)/3 > 128) turtle_color = [0, 0, 0, 150]
|
||||
const view_width = (maxX - minX) * 1.2
|
||||
const view_height = (maxY - minY) * 1.2
|
||||
const margin = Math.max(view_width, view_height) * 0.1
|
||||
const x_origin = minX - margin
|
||||
const y_origin = -maxY - margin
|
||||
const path = svg_render_path(states)
|
||||
const turtle = svg_render_turtle(states[states.length - 1])
|
||||
return `<?xml version="1.0" standalone="no"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="${x_origin} ${y_origin} ${view_width} ${view_height}" width="10in" height="8in">
|
||||
|
||||
<rect x="${x_origin - 5}" y="${y_origin - 5}" width="${view_width + 10}" height="${view_height + 10}" fill="#${hex(r)}${hex(g)}${hex(b)}" stroke-width="0" paint-order="fill" />
|
||||
|
||||
<g transform="scale(-1, 1) rotate(180)">
|
||||
${path}
|
||||
${turtle}
|
||||
</g>
|
||||
|
||||
<ludus>
|
||||
${escape_svg(code)}
|
||||
</ludus>
|
||||
</svg>
|
||||
`
|
||||
}
|
||||
|
||||
function p5_render_turtle (state, calls) {
|
||||
if (!state.visible) return
|
||||
calls.push(["push"])
|
||||
const [r, g, b, a] = turtle_color
|
||||
calls.push(["fill", r, g, b, a])
|
||||
const {heading, pencolor, position: [x, y], pendown, penwidth} = state
|
||||
const origin = [0, turtle_radius]
|
||||
const [x1, y1] = origin
|
||||
const [x2, y2] = rotate(origin, turtle_angle)
|
||||
const [x3, y3] = rotate(origin, -turtle_angle)
|
||||
calls.push(["translate", x, y])
|
||||
// need negative turtle rotation with the other p5 translations
|
||||
calls.push(["rotate", -turn_to_rad(heading)])
|
||||
calls.push(["noStroke"])
|
||||
calls.push(["beginShape"])
|
||||
calls.push(["vertex", x1, y1])
|
||||
calls.push(["vertex", x2, y2])
|
||||
calls.push(["vertex", x3, y3])
|
||||
calls.push(["endShape"])
|
||||
calls.push(["strokeWeight", penwidth])
|
||||
calls.push(["stroke", ...resolve_color(pencolor)])
|
||||
if (pendown) calls.push(["line", 0, 0, x1, y1])
|
||||
calls.push(["pop"])
|
||||
return calls
|
||||
}
|
||||
|
||||
function p5 (commands) {
|
||||
const states = [turtle_init]
|
||||
commands.reduce((prev_state, command) => {
|
||||
const new_state = command_to_state(prev_state, command)
|
||||
states.push(new_state)
|
||||
return new_state
|
||||
}, turtle_init)
|
||||
// console.log(states)
|
||||
const [r, g, b, _] = resolve_color(background_color)
|
||||
if ((r + g + b)/3 > 128) turtle_color = [0, 0, 0, 150]
|
||||
const p5_calls = [...p5_call_root()]
|
||||
for (let i = 1; i < states.length; ++i) {
|
||||
const prev = states[i - 1]
|
||||
const curr = states[i]
|
||||
const calls = states_to_call(prev, curr)
|
||||
for (const call of calls) {
|
||||
p5_calls.push(call)
|
||||
}
|
||||
}
|
||||
p5_calls[0] = ["background", ...resolve_color(background_color)]
|
||||
p5_render_turtle(states[states.length - 1], p5_calls)
|
||||
p5_calls.push(["pop"])
|
||||
return p5_calls
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
run,
|
||||
stdout,
|
||||
turtle_commands,
|
||||
result,
|
||||
extract_ludus,
|
||||
p5,
|
||||
svg,
|
||||
}
|
3
pkg/rudus.d.ts
vendored
Normal file
3
pkg/rudus.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export function run(src: string): string;
|
119
pkg/rudus.js
Normal file
119
pkg/rudus.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
|
||||
let imports = {};
|
||||
imports['__wbindgen_placeholder__'] = module.exports;
|
||||
let wasm;
|
||||
const { TextEncoder, TextDecoder } = require(`util`);
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedUint8ArrayMemory0 = null;
|
||||
|
||||
function getUint8ArrayMemory0() {
|
||||
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
||||
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8ArrayMemory0;
|
||||
}
|
||||
|
||||
let cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length, 1) >>> 0;
|
||||
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len, 1) >>> 0;
|
||||
|
||||
const mem = getUint8ArrayMemory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
/**
|
||||
* @param {string} src
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports.run = function(src) {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.run(ptr0, len0);
|
||||
deferred2_0 = ret[0];
|
||||
deferred2_1 = ret[1];
|
||||
return getStringFromWasm0(ret[0], ret[1]);
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.__wbindgen_init_externref_table = function() {
|
||||
const table = wasm.__wbindgen_export_0;
|
||||
const offset = table.grow(4);
|
||||
table.set(0, undefined);
|
||||
table.set(offset + 0, undefined);
|
||||
table.set(offset + 1, null);
|
||||
table.set(offset + 2, true);
|
||||
table.set(offset + 3, false);
|
||||
;
|
||||
};
|
||||
|
||||
const path = require('path').join(__dirname, 'rudus_bg.wasm');
|
||||
const bytes = require('fs').readFileSync(path);
|
||||
|
||||
const wasmModule = new WebAssembly.Module(bytes);
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
|
||||
wasm = wasmInstance.exports;
|
||||
module.exports.__wasm = wasm;
|
||||
|
||||
wasm.__wbindgen_start();
|
||||
|
Loading…
Reference in New Issue
Block a user