From d7dbee4c05b93a0cf6edda8d46eca7bb1a2ee681 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 28 Jul 2024 20:50:04 -0700 Subject: [PATCH 1/4] Stow algebraic engine prototype We're using the Gram matrix engine for the next stage of development, so the algebraic engine shouldn't be at the top level anymore. --- engine-proto/{ => alg-test}/ConstructionViewer.jl | 0 engine-proto/{ => alg-test}/Engine.Algebraic.jl | 0 engine-proto/{ => alg-test}/Engine.Numerical.jl | 0 engine-proto/{ => alg-test}/Engine.jl | 0 engine-proto/{ => alg-test}/HittingSet.jl | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename engine-proto/{ => alg-test}/ConstructionViewer.jl (100%) rename engine-proto/{ => alg-test}/Engine.Algebraic.jl (100%) rename engine-proto/{ => alg-test}/Engine.Numerical.jl (100%) rename engine-proto/{ => alg-test}/Engine.jl (100%) rename engine-proto/{ => alg-test}/HittingSet.jl (100%) diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/alg-test/ConstructionViewer.jl similarity index 100% rename from engine-proto/ConstructionViewer.jl rename to engine-proto/alg-test/ConstructionViewer.jl diff --git a/engine-proto/Engine.Algebraic.jl b/engine-proto/alg-test/Engine.Algebraic.jl similarity index 100% rename from engine-proto/Engine.Algebraic.jl rename to engine-proto/alg-test/Engine.Algebraic.jl diff --git a/engine-proto/Engine.Numerical.jl b/engine-proto/alg-test/Engine.Numerical.jl similarity index 100% rename from engine-proto/Engine.Numerical.jl rename to engine-proto/alg-test/Engine.Numerical.jl diff --git a/engine-proto/Engine.jl b/engine-proto/alg-test/Engine.jl similarity index 100% rename from engine-proto/Engine.jl rename to engine-proto/alg-test/Engine.jl diff --git a/engine-proto/HittingSet.jl b/engine-proto/alg-test/HittingSet.jl similarity index 100% rename from engine-proto/HittingSet.jl rename to engine-proto/alg-test/HittingSet.jl From 12abef4076492fb3bcc2fdf968afa57f4b10ef6d Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 28 Jul 2024 21:10:04 -0700 Subject: [PATCH 2/4] Trial a Rust engine powering a Civet interface Write a basic web app with a Rust engine, compiled to WebAssembly, powering a Civet interface. Do linear algebra in the engine using the `nalgebra` crate. --- lang-trials/rust/Cargo.toml | 29 +++++++++ lang-trials/rust/Makefile | 6 ++ lang-trials/rust/app.civet | 113 +++++++++++++++++++++++++++++++++ lang-trials/rust/app.css | 38 +++++++++++ lang-trials/rust/index.html | 21 ++++++ lang-trials/rust/src/lib.rs | 55 ++++++++++++++++ lang-trials/rust/src/utils.rs | 10 +++ lang-trials/rust/tsconfig.json | 15 +++++ 8 files changed, 287 insertions(+) create mode 100644 lang-trials/rust/Cargo.toml create mode 100644 lang-trials/rust/Makefile create mode 100644 lang-trials/rust/app.civet create mode 100644 lang-trials/rust/app.css create mode 100644 lang-trials/rust/index.html create mode 100644 lang-trials/rust/src/lib.rs create mode 100644 lang-trials/rust/src/utils.rs create mode 100644 lang-trials/rust/tsconfig.json diff --git a/lang-trials/rust/Cargo.toml b/lang-trials/rust/Cargo.toml new file mode 100644 index 0000000..01c0b86 --- /dev/null +++ b/lang-trials/rust/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "engine" +version = "0.1.0" +authors = ["Aaron"] +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +nalgebra = "0.33.0" +js-sys = "0.3.69" +wasm-bindgen = "0.2.84" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.7", optional = true } + +[dev-dependencies] +wasm-bindgen-test = "0.3.34" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/lang-trials/rust/Makefile b/lang-trials/rust/Makefile new file mode 100644 index 0000000..b94935b --- /dev/null +++ b/lang-trials/rust/Makefile @@ -0,0 +1,6 @@ +app.js: app.civet pkg/engine_bg.wasm + civet --typecheck app.civet + civet --js -c app.civet -o .js + +pkg/engine_bg.wasm: src/lib.rs + wasm-pack build --target web diff --git a/lang-trials/rust/app.civet b/lang-trials/rust/app.civet new file mode 100644 index 0000000..ab35044 --- /dev/null +++ b/lang-trials/rust/app.civet @@ -0,0 +1,113 @@ +// engine functions +{default: init, Circle, circThru} from "./pkg/engine.js" + +// === elements and state === + +// input +dataInputs .= new Array(6) +data .= new Float64Array(6) + +// output and display +let circ: Circle | null +let display: HTMLCanvasElement +let ctx: CanvasRenderingContext2D + +// === display === + +// display style +const rView = 5 +const highlightStyle = "#fcfcfc" +const gridStyle = "#404040" +const dataFillStyles = ["#ba5d09", "#0e8a06", "#8951fb"] +const dataStrokeStyles = ["#f89142", "#58c145", "#c396fc"] + +function render: void + // update resolution + res .= display.width / (2*rView) + + // set transformation + ctx.setTransform 1, 0, 0, -1, 0.5*display.width, 0.5*display.height + + // clear previous frame + ctx.clearRect -0.5*display.width, -0.5*display.height, display.width, display.height + + // draw grid + gridRange .= [Math.ceil(-rView + 0.01) .. Math.floor(rView - 0.01)] + edgeScr .= res*rView + ctx.strokeStyle = gridStyle + for t of gridRange + tScr .= res*t + + // draw horizontal grid line + ctx.beginPath() + ctx.moveTo -edgeScr, tScr + ctx.lineTo edgeScr, tScr + ctx.stroke() + + // draw vertical grid line + ctx.beginPath() + ctx.moveTo tScr, -edgeScr + ctx.lineTo tScr, edgeScr + ctx.stroke() + + // draw circle + if circ + ctx.beginPath() + ctx.strokeStyle = highlightStyle + ctx.arc res*circ.center_x, res*circ.center_y, res*circ.radius, 0, 2*Math.PI + ctx.stroke() + + // draw data points + for n of [0..2] + const ind_x = 2*n + const ind_y = ind_x + 1 + if dataInputs[ind_x].validity.valid and dataInputs[ind_y].validity.valid + ctx.beginPath() + ctx.fillStyle = dataFillStyles[n] + ctx.strokeStyle = dataStrokeStyles[n] + ctx.arc res*data[ind_x], res*data[ind_y], 3, 0, 2*Math.PI + ctx.fill() + ctx.stroke() + +// === interaction === + +// --- inputs --- + +function inputData: void + allDataValid .= true + for n of [0 .. dataInputs.length-1] + if dataInputs[n].validity.valid + data[n] = dataInputs[n].valueAsNumber + else + allDataValid = false + + if allDataValid + try + circ = circThru data + catch ex + circ = null + console.error ex + else + circ = null + + // update the display + requestAnimationFrame(render) + +// --- binding --- + +function bindDoc: Promise + // wait for the engine to load + await init() + + // set up the data inputs + for n of [0..5] + dataInputs[n] = document.querySelector(`#data-input-${n}`) as HTMLInputElement + dataInputs[n].addEventListener "input", inputData + dataInputs[n].style.borderColor = dataFillStyles[Math.floor(0.5*n)] + + // initialize the display + display = document.querySelector("#display") as HTMLCanvasElement + ctx = display.getContext("2d") as CanvasRenderingContext2D + inputData() + +document.addEventListener "DOMContentLoaded", bindDoc diff --git a/lang-trials/rust/app.css b/lang-trials/rust/app.css new file mode 100644 index 0000000..5486be3 --- /dev/null +++ b/lang-trials/rust/app.css @@ -0,0 +1,38 @@ +body { + margin-left: 20px; + margin-top: 20px; + color: #fcfcfc; + background-color: #202020; +} + +input { + color: inherit; + background-color: #020202; + border: 1px solid #606060; + min-width: 40px; + border-radius: 4px; +} + +#data-panel { + float: left; + margin-left: 20px; + display: grid; + grid-template-columns: auto auto; + gap: 10px 10px; + width: 120px; +} + +#data-panel > div { + text-align: center; +} + +#result-display { + margin-top: 10px; + font-weight: bold; +} + +canvas { + float: left; + background-color: #020202; + border-radius: 10px; +} \ No newline at end of file diff --git a/lang-trials/rust/index.html b/lang-trials/rust/index.html new file mode 100644 index 0000000..34e6d97 --- /dev/null +++ b/lang-trials/rust/index.html @@ -0,0 +1,21 @@ + + + + Lattice circle + + + + + +
+
x
+
y
+ + + + + + +
+ + diff --git a/lang-trials/rust/src/lib.rs b/lang-trials/rust/src/lib.rs new file mode 100644 index 0000000..22c20bc --- /dev/null +++ b/lang-trials/rust/src/lib.rs @@ -0,0 +1,55 @@ +mod utils; + +extern crate js_sys; + +use nalgebra::*; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct Circle { + pub center_x: f64, + pub center_y: f64, + pub radius: f64, +} + +// construct the circle through +// +// (x1, y1), (x2, y2), (x3, y3) +// +// from the array +// +// [x1, y1, x2, y2, x3, y3] +// +#[wasm_bindgen] +pub fn circThru(data_raw: js_sys::Float64Array) -> Result { + // represent the given points as the columns of a matrix + let data = Matrix2x3::from_vec(data_raw.to_vec()); + + // build the matrix that maps the circle's coefficient vector to the + // negative of the linear part of the circle's equation, evaluated at the + // given points + let neg_lin_part = stack![2.0*data.transpose(), Vector3::repeat(1.0)]; + + // find the quadrdatic part of the circle's equation, evaluated at the given + // points + let quad_part = Vector3::from_iterator( + data.column_iter().map(|v| v.dot(&v)) + ); + + // find the circle's coefficient vector, and from there its center and + // radius + match neg_lin_part.lu().solve(&quad_part) { + None => Err(JsValue::from("Couldn't solve system")), + Some(coeffs) => { + let center_x = coeffs[0]; + let center_y = coeffs[1]; + Ok(Circle { + center_x: center_x, + center_y: center_y, + radius: ( + coeffs[2] + center_x*center_x + center_y*center_y + ).sqrt(), + }) + } + } +} diff --git a/lang-trials/rust/src/utils.rs b/lang-trials/rust/src/utils.rs new file mode 100644 index 0000000..b1d7929 --- /dev/null +++ b/lang-trials/rust/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/lang-trials/rust/tsconfig.json b/lang-trials/rust/tsconfig.json new file mode 100644 index 0000000..a6c05ac --- /dev/null +++ b/lang-trials/rust/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "jsx": "preserve", + "lib": ["es2021", "dom"], + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true + }, + "ts-node": { + "transpileOnly": true, + "compilerOptions": { + "module": "ES2020" + } + } +} From 42bdfabd91ad9679a7df469de0d2f60b150be94a Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 29 Jul 2024 05:30:16 -0700 Subject: [PATCH 3/4] Rust trial: port interface to Sycamore Now we have a reactive web app written entirely in Rust. The Trunk build tool compiles it to WebAssembly and generates a little JavaScript glue. --- lang-trials/rust/.gitignore | 2 + lang-trials/rust/Cargo.toml | 15 ++- lang-trials/rust/Makefile | 6 - lang-trials/rust/app.civet | 113 ------------------ lang-trials/rust/index.html | 17 +-- lang-trials/rust/{app.css => main.css} | 12 ++ lang-trials/rust/src/lib.rs | 55 --------- lang-trials/rust/src/main.rs | 153 +++++++++++++++++++++++++ lang-trials/rust/src/utils.rs | 10 -- lang-trials/rust/tsconfig.json | 15 --- 10 files changed, 178 insertions(+), 220 deletions(-) create mode 100644 lang-trials/rust/.gitignore delete mode 100644 lang-trials/rust/Makefile delete mode 100644 lang-trials/rust/app.civet rename lang-trials/rust/{app.css => main.css} (80%) delete mode 100644 lang-trials/rust/src/lib.rs create mode 100644 lang-trials/rust/src/main.rs delete mode 100644 lang-trials/rust/src/utils.rs delete mode 100644 lang-trials/rust/tsconfig.json diff --git a/lang-trials/rust/.gitignore b/lang-trials/rust/.gitignore new file mode 100644 index 0000000..1d4e644 --- /dev/null +++ b/lang-trials/rust/.gitignore @@ -0,0 +1,2 @@ +target/* +dist/* \ No newline at end of file diff --git a/lang-trials/rust/Cargo.toml b/lang-trials/rust/Cargo.toml index 01c0b86..278a7c3 100644 --- a/lang-trials/rust/Cargo.toml +++ b/lang-trials/rust/Cargo.toml @@ -1,19 +1,15 @@ [package] -name = "engine" +name = "sycamore-trial" version = "0.1.0" authors = ["Aaron"] edition = "2021" -[lib] -crate-type = ["cdylib", "rlib"] - [features] default = ["console_error_panic_hook"] [dependencies] nalgebra = "0.33.0" -js-sys = "0.3.69" -wasm-bindgen = "0.2.84" +sycamore = "0.9.0-beta.2" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -21,6 +17,13 @@ wasm-bindgen = "0.2.84" # code size when deploying. console_error_panic_hook = { version = "0.1.7", optional = true } +[dependencies.web-sys] +version = "0.3.69" +features = [ + 'CanvasRenderingContext2d', + 'HtmlCanvasElement', +] + [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/lang-trials/rust/Makefile b/lang-trials/rust/Makefile deleted file mode 100644 index b94935b..0000000 --- a/lang-trials/rust/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -app.js: app.civet pkg/engine_bg.wasm - civet --typecheck app.civet - civet --js -c app.civet -o .js - -pkg/engine_bg.wasm: src/lib.rs - wasm-pack build --target web diff --git a/lang-trials/rust/app.civet b/lang-trials/rust/app.civet deleted file mode 100644 index ab35044..0000000 --- a/lang-trials/rust/app.civet +++ /dev/null @@ -1,113 +0,0 @@ -// engine functions -{default: init, Circle, circThru} from "./pkg/engine.js" - -// === elements and state === - -// input -dataInputs .= new Array(6) -data .= new Float64Array(6) - -// output and display -let circ: Circle | null -let display: HTMLCanvasElement -let ctx: CanvasRenderingContext2D - -// === display === - -// display style -const rView = 5 -const highlightStyle = "#fcfcfc" -const gridStyle = "#404040" -const dataFillStyles = ["#ba5d09", "#0e8a06", "#8951fb"] -const dataStrokeStyles = ["#f89142", "#58c145", "#c396fc"] - -function render: void - // update resolution - res .= display.width / (2*rView) - - // set transformation - ctx.setTransform 1, 0, 0, -1, 0.5*display.width, 0.5*display.height - - // clear previous frame - ctx.clearRect -0.5*display.width, -0.5*display.height, display.width, display.height - - // draw grid - gridRange .= [Math.ceil(-rView + 0.01) .. Math.floor(rView - 0.01)] - edgeScr .= res*rView - ctx.strokeStyle = gridStyle - for t of gridRange - tScr .= res*t - - // draw horizontal grid line - ctx.beginPath() - ctx.moveTo -edgeScr, tScr - ctx.lineTo edgeScr, tScr - ctx.stroke() - - // draw vertical grid line - ctx.beginPath() - ctx.moveTo tScr, -edgeScr - ctx.lineTo tScr, edgeScr - ctx.stroke() - - // draw circle - if circ - ctx.beginPath() - ctx.strokeStyle = highlightStyle - ctx.arc res*circ.center_x, res*circ.center_y, res*circ.radius, 0, 2*Math.PI - ctx.stroke() - - // draw data points - for n of [0..2] - const ind_x = 2*n - const ind_y = ind_x + 1 - if dataInputs[ind_x].validity.valid and dataInputs[ind_y].validity.valid - ctx.beginPath() - ctx.fillStyle = dataFillStyles[n] - ctx.strokeStyle = dataStrokeStyles[n] - ctx.arc res*data[ind_x], res*data[ind_y], 3, 0, 2*Math.PI - ctx.fill() - ctx.stroke() - -// === interaction === - -// --- inputs --- - -function inputData: void - allDataValid .= true - for n of [0 .. dataInputs.length-1] - if dataInputs[n].validity.valid - data[n] = dataInputs[n].valueAsNumber - else - allDataValid = false - - if allDataValid - try - circ = circThru data - catch ex - circ = null - console.error ex - else - circ = null - - // update the display - requestAnimationFrame(render) - -// --- binding --- - -function bindDoc: Promise - // wait for the engine to load - await init() - - // set up the data inputs - for n of [0..5] - dataInputs[n] = document.querySelector(`#data-input-${n}`) as HTMLInputElement - dataInputs[n].addEventListener "input", inputData - dataInputs[n].style.borderColor = dataFillStyles[Math.floor(0.5*n)] - - // initialize the display - display = document.querySelector("#display") as HTMLCanvasElement - ctx = display.getContext("2d") as CanvasRenderingContext2D - inputData() - -document.addEventListener "DOMContentLoaded", bindDoc diff --git a/lang-trials/rust/index.html b/lang-trials/rust/index.html index 34e6d97..04a39a0 100644 --- a/lang-trials/rust/index.html +++ b/lang-trials/rust/index.html @@ -2,20 +2,7 @@ Lattice circle - - + - - -
-
x
-
y
- - - - - - -
- + diff --git a/lang-trials/rust/app.css b/lang-trials/rust/main.css similarity index 80% rename from lang-trials/rust/app.css rename to lang-trials/rust/main.css index 5486be3..7045d73 100644 --- a/lang-trials/rust/app.css +++ b/lang-trials/rust/main.css @@ -13,6 +13,18 @@ input { border-radius: 4px; } +input.point-1 { + border-color: #ba5d09; +} + +input.point-2 { + border-color: #0e8a06; +} + +input.point-3 { + border-color: #8951fb; +} + #data-panel { float: left; margin-left: 20px; diff --git a/lang-trials/rust/src/lib.rs b/lang-trials/rust/src/lib.rs deleted file mode 100644 index 22c20bc..0000000 --- a/lang-trials/rust/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -mod utils; - -extern crate js_sys; - -use nalgebra::*; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -pub struct Circle { - pub center_x: f64, - pub center_y: f64, - pub radius: f64, -} - -// construct the circle through -// -// (x1, y1), (x2, y2), (x3, y3) -// -// from the array -// -// [x1, y1, x2, y2, x3, y3] -// -#[wasm_bindgen] -pub fn circThru(data_raw: js_sys::Float64Array) -> Result { - // represent the given points as the columns of a matrix - let data = Matrix2x3::from_vec(data_raw.to_vec()); - - // build the matrix that maps the circle's coefficient vector to the - // negative of the linear part of the circle's equation, evaluated at the - // given points - let neg_lin_part = stack![2.0*data.transpose(), Vector3::repeat(1.0)]; - - // find the quadrdatic part of the circle's equation, evaluated at the given - // points - let quad_part = Vector3::from_iterator( - data.column_iter().map(|v| v.dot(&v)) - ); - - // find the circle's coefficient vector, and from there its center and - // radius - match neg_lin_part.lu().solve(&quad_part) { - None => Err(JsValue::from("Couldn't solve system")), - Some(coeffs) => { - let center_x = coeffs[0]; - let center_y = coeffs[1]; - Ok(Circle { - center_x: center_x, - center_y: center_y, - radius: ( - coeffs[2] + center_x*center_x + center_y*center_y - ).sqrt(), - }) - } - } -} diff --git a/lang-trials/rust/src/main.rs b/lang-trials/rust/src/main.rs new file mode 100644 index 0000000..f6b97c9 --- /dev/null +++ b/lang-trials/rust/src/main.rs @@ -0,0 +1,153 @@ +use nalgebra::*; +use std::f64::consts::PI as PI; +use sycamore::{prelude::*, rt::{JsCast, JsValue}}; + +// --- interface --- + +fn main() { + // set up a config option that forwards panic messages to `console.error` + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); + + sycamore::render(|| { + let data = [-1.0, 0.0, 0.0, -1.0, 1.0, 0.0].map(|n| create_signal(n)); + let display = create_node_ref(); + + on_mount(move || { + let canvas = display + .get::() + .unchecked_into::(); + let ctx = canvas + .get_context("2d") + .unwrap() + .unwrap() + .dyn_into::() + .unwrap(); + + create_effect(move || { + // center and normalize the coordinate system + let width = canvas.width() as f64; + let height = canvas.height() as f64; + ctx.set_transform(1.0, 0.0, 0.0, -1.0, 0.5*width, 0.5*height).unwrap(); + + // clear the previous frame + ctx.clear_rect(-0.5*width, -0.5*width, width, height); + + // find the resolution + const R_DISP: f64 = 5.0; + let res = width / (2.0*R_DISP); + + // set colors + let highlight_style = JsValue::from("white"); + let grid_style = JsValue::from("#404040"); + let point_fill_styles = ["#ba5d09", "#0e8a06", "#8951fb"]; + let point_stroke_styles = ["#f89142", "#58c145", "#c396fc"]; + + // draw the grid + let r_grid = (R_DISP - 0.01).floor() as i32; + let edge_scr = res * R_DISP; + ctx.set_stroke_style(&grid_style); + for t in -r_grid ..= r_grid { + let t_scr = res * (t as f64); + + // draw horizontal grid line + ctx.begin_path(); + ctx.move_to(-edge_scr, t_scr); + ctx.line_to(edge_scr, t_scr); + ctx.stroke(); + + // draw vertical grid line + ctx.begin_path(); + ctx.move_to(t_scr, -edge_scr); + ctx.line_to(t_scr, edge_scr); + ctx.stroke(); + } + + // find and draw the circle through the given points + let data_vals = data.map(|sig| sig.get()).to_vec(); + let points = Matrix2x3::from_vec(data_vals); + if let Some(circ) = circ_thru(points) { + ctx.begin_path(); + ctx.set_stroke_style(&highlight_style); + ctx.arc( + res * circ.center_x, + res * circ.center_y, + res * circ.radius, + 0.0, 2.0*PI + ).unwrap(); + ctx.stroke(); + } + + // draw the data points + for n in 0..3 { + ctx.begin_path(); + ctx.set_fill_style(&JsValue::from(point_fill_styles[n])); + ctx.set_stroke_style(&JsValue::from(point_stroke_styles[n])); + let ind_x = 2*n; + let ind_y = ind_x + 1; + ctx.arc( + res * data[ind_x].get(), + res * data[ind_y].get(), + 3.0, + 0.0, 2.0*PI + ).unwrap(); + ctx.fill(); + ctx.stroke(); + } + }); + }); + + view! { + canvas(ref=display, width="600", height="600") + div(id="data-panel") { + div { "x" } + div { "y" } + input(type="number", class="point-1", bind:valueAsNumber=data[0]) + input(type="number", class="point-1", bind:valueAsNumber=data[1]) + input(type="number", class="point-2", bind:valueAsNumber=data[2]) + input(type="number", class="point-2", bind:valueAsNumber=data[3]) + input(type="number", class="point-3", bind:valueAsNumber=data[4]) + input(type="number", class="point-3", bind:valueAsNumber=data[5]) + } + } + }); +} + +// --- engine --- + +struct Circle { + center_x: f64, + center_y: f64, + radius: f64, +} + +// construct the circle through the points given by the columns of `points` +fn circ_thru(points: Matrix2x3) -> Option { + // build the matrix that maps the circle's coefficient vector to the + // negative of the linear part of the circle's equation, evaluated at the + // given points + let neg_lin_part = stack![2.0*points.transpose(), Vector3::repeat(1.0)]; + + // find the quadrdatic part of the circle's equation, evaluated at the given + // points + let quad_part = Vector3::from_iterator( + points.column_iter().map(|v| v.dot(&v)) + ); + + // find the circle's coefficient vector, and from there its center and + // radius + match neg_lin_part.lu().solve(&quad_part) { + None => None, + Some(coeffs) => { + let center_x = coeffs[0]; + let center_y = coeffs[1]; + Some(Circle { + center_x: center_x, + center_y: center_y, + radius: ( + coeffs[2] + center_x*center_x + center_y*center_y + ).sqrt(), + }) + } + } +} \ No newline at end of file diff --git a/lang-trials/rust/src/utils.rs b/lang-trials/rust/src/utils.rs deleted file mode 100644 index b1d7929..0000000 --- a/lang-trials/rust/src/utils.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub fn set_panic_hook() { - // When the `console_error_panic_hook` feature is enabled, we can call the - // `set_panic_hook` function at least once during initialization, and then - // we will get better error messages if our code ever panics. - // - // For more details see - // https://github.com/rustwasm/console_error_panic_hook#readme - #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); -} diff --git a/lang-trials/rust/tsconfig.json b/lang-trials/rust/tsconfig.json deleted file mode 100644 index a6c05ac..0000000 --- a/lang-trials/rust/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "jsx": "preserve", - "lib": ["es2021", "dom"], - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true - }, - "ts-node": { - "transpileOnly": true, - "compilerOptions": { - "module": "ES2020" - } - } -} From 244f222eb0b42f96eb59c88d840555580bb1d712 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 29 Jul 2024 13:14:32 -0700 Subject: [PATCH 4/4] Move the engine into a module --- lang-trials/rust/src/engine.rs | 38 ++++++++++++++++++++++++++++ lang-trials/rust/src/main.rs | 45 +++------------------------------- 2 files changed, 41 insertions(+), 42 deletions(-) create mode 100644 lang-trials/rust/src/engine.rs diff --git a/lang-trials/rust/src/engine.rs b/lang-trials/rust/src/engine.rs new file mode 100644 index 0000000..0dbee3f --- /dev/null +++ b/lang-trials/rust/src/engine.rs @@ -0,0 +1,38 @@ +use nalgebra::*; + +pub struct Circle { + pub center_x: f64, + pub center_y: f64, + pub radius: f64, +} + +// construct the circle through the points given by the columns of `points` +pub fn circ_thru(points: Matrix2x3) -> Option { + // build the matrix that maps the circle's coefficient vector to the + // negative of the linear part of the circle's equation, evaluated at the + // given points + let neg_lin_part = stack![2.0*points.transpose(), Vector3::repeat(1.0)]; + + // find the quadrdatic part of the circle's equation, evaluated at the given + // points + let quad_part = Vector3::from_iterator( + points.column_iter().map(|v| v.dot(&v)) + ); + + // find the circle's coefficient vector, and from there its center and + // radius + match neg_lin_part.lu().solve(&quad_part) { + None => None, + Some(coeffs) => { + let center_x = coeffs[0]; + let center_y = coeffs[1]; + Some(Circle { + center_x: center_x, + center_y: center_y, + radius: ( + coeffs[2] + center_x*center_x + center_y*center_y + ).sqrt(), + }) + } + } +} \ No newline at end of file diff --git a/lang-trials/rust/src/main.rs b/lang-trials/rust/src/main.rs index f6b97c9..af011a2 100644 --- a/lang-trials/rust/src/main.rs +++ b/lang-trials/rust/src/main.rs @@ -1,8 +1,8 @@ -use nalgebra::*; +use nalgebra::Matrix2x3; use std::f64::consts::PI as PI; use sycamore::{prelude::*, rt::{JsCast, JsValue}}; -// --- interface --- +mod engine; fn main() { // set up a config option that forwards panic messages to `console.error` @@ -66,7 +66,7 @@ fn main() { // find and draw the circle through the given points let data_vals = data.map(|sig| sig.get()).to_vec(); let points = Matrix2x3::from_vec(data_vals); - if let Some(circ) = circ_thru(points) { + if let Some(circ) = engine::circ_thru(points) { ctx.begin_path(); ctx.set_stroke_style(&highlight_style); ctx.arc( @@ -111,43 +111,4 @@ fn main() { } } }); -} - -// --- engine --- - -struct Circle { - center_x: f64, - center_y: f64, - radius: f64, -} - -// construct the circle through the points given by the columns of `points` -fn circ_thru(points: Matrix2x3) -> Option { - // build the matrix that maps the circle's coefficient vector to the - // negative of the linear part of the circle's equation, evaluated at the - // given points - let neg_lin_part = stack![2.0*points.transpose(), Vector3::repeat(1.0)]; - - // find the quadrdatic part of the circle's equation, evaluated at the given - // points - let quad_part = Vector3::from_iterator( - points.column_iter().map(|v| v.dot(&v)) - ); - - // find the circle's coefficient vector, and from there its center and - // radius - match neg_lin_part.lu().solve(&quad_part) { - None => None, - Some(coeffs) => { - let center_x = coeffs[0]; - let center_y = coeffs[1]; - Some(Circle { - center_x: center_x, - center_y: center_y, - radius: ( - coeffs[2] + center_x*center_x + center_y*center_y - ).sqrt(), - }) - } - } } \ No newline at end of file