From 1085c7ae4474ee32262182d687bc7ae61d4afb1c Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Wed, 31 Jul 2024 19:25:22 -0400 Subject: [PATCH] build out svg adapter, viewBox isn't working yet --- build/ludus.mjs | 111 +++++++++++++++++++++++++++++++++++++++++---- build/svg_test.mjs | 11 +++++ build/svg_test.svg | 26 +++++++++++ build/test.svg | 18 ++++++++ 4 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 build/svg_test.mjs create mode 100644 build/svg_test.svg create mode 100644 build/test.svg diff --git a/build/ludus.mjs b/build/ludus.mjs index 2296127..659dbe1 100644 --- a/build/ludus.mjs +++ b/build/ludus.mjs @@ -4,7 +4,10 @@ const mod = await init() let result = null +let code = null + export function run (source) { + code = source const output = mod.ludus(source).value result = JSON.parse(output) return result @@ -20,10 +23,6 @@ export function turtle_commands () { return result.io.turtle.data } -export function svg () { - // TODO -} - const turtle_init = { position: [0, 0], heading: 0, @@ -154,7 +153,7 @@ function command_to_state (prev_state, curr_command) { let color = curr_command.slice(1) if (color.lengh = 1) color = color[0] background_color = color - return + return prev_state } } } @@ -211,12 +210,106 @@ function turn_to_rad (heading) { return heading * 2 * Math.PI } -function render_turtle (state, calls) { +function turn_to_deg (heading) { + return (heading * 360) + 180 +} + +function svg_render_line (prev, curr) { + if (!prev.pendown) return "" + if (are_eq(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 ` + + ` +} + +function escape_svg (svg) { + return svg + .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 ? `` : "" + return ` + + + ${ink} + + ` +} + +export function svg (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) + 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) + const view_width = maxX - minX + const view_height = maxY - minY + const margin = Math.max(view_width, view_height) * 0.1 + const x1 = minX - margin + const y1 = minY - margin + const x2 = maxX + margin + const y2 = maxY + margin + const path = svg_render_path(states) + const turtle = svg_render_turtle(states[states.length - 1]) + return ` + + + + ${path} + ${turtle} + + + +${escape_svg(code)} + + + ` +} + +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} = state + 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) @@ -229,6 +322,7 @@ function render_turtle (state, calls) { 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"]) @@ -252,6 +346,7 @@ export function p5_calls (commands) { } } p5_calls[0] = ["background", ...resolve_color(background_color)] - render_turtle(states[states.length - 1], p5_calls) + p5_render_turtle(states[states.length - 1], p5_calls) return p5_calls } + diff --git a/build/svg_test.mjs b/build/svg_test.mjs new file mode 100644 index 0000000..cb9f4bb --- /dev/null +++ b/build/svg_test.mjs @@ -0,0 +1,11 @@ +import {run, svg} from "./ludus.mjs" + +const code = ` + forward! (100) + right! (0.25) + forward! (100) +` + +const result = run(code) + +console.log(svg(result.io.turtle.data)) diff --git a/build/svg_test.svg b/build/svg_test.svg new file mode 100644 index 0000000..23e55ab --- /dev/null +++ b/build/svg_test.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + forward! (100) + right! (0.25) + forward! (100) + + + + diff --git a/build/test.svg b/build/test.svg new file mode 100644 index 0000000..af2ad10 --- /dev/null +++ b/build/test.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + +& this is some code +forward! (100) +right! (0.25) + + +