From ab830b194e656522a6d48707de951346a3367cc8 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 6 Sep 2024 18:57:59 -0700 Subject: [PATCH] Use circles in triangle as low-curvature construction In the process, add a way to build a sphere by offset and curvature. --- app-proto/inversive-display/src/engine.rs | 15 +++ app-proto/inversive-display/src/main.rs | 135 +++++++++++++++++----- 2 files changed, 121 insertions(+), 29 deletions(-) diff --git a/app-proto/inversive-display/src/engine.rs b/app-proto/inversive-display/src/engine.rs index 7fbcd03..79668bb 100644 --- a/app-proto/inversive-display/src/engine.rs +++ b/app-proto/inversive-display/src/engine.rs @@ -1,5 +1,6 @@ use nalgebra::DVector; +// the sphere with the given center and radius, with inward-pointing normals pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64) -> DVector { let center_norm_sq = center_x * center_x + center_y * center_y + center_z * center_z; DVector::from_column_slice(&[ @@ -9,4 +10,18 @@ pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64) -> DVect 0.5 / radius, 0.5 * (center_norm_sq / radius - radius) ]) +} + +// the sphere of curvature `curv` whose closest point to the origin has position +// `off * dir` and normal `dir`, where `dir` is a unit vector. setting the +// curvature to zero gives a plane +pub fn sphere_with_offset(dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f64) -> DVector { + let norm_sp = 1.0 + off * curv; + DVector::from_column_slice(&[ + norm_sp * dir_x, + norm_sp * dir_y, + norm_sp * dir_z, + 0.5 * curv, + off * (1.0 + 0.5 * off * curv) + ]) } \ No newline at end of file diff --git a/app-proto/inversive-display/src/main.rs b/app-proto/inversive-display/src/main.rs index 1fbef8d..8afe8ef 100644 --- a/app-proto/inversive-display/src/main.rs +++ b/app-proto/inversive-display/src/main.rs @@ -10,7 +10,6 @@ extern crate js_sys; use core::array; use nalgebra::{DMatrix, DVector}; -use std::f64::consts::FRAC_1_SQRT_2; use sycamore::{prelude::*, motion::create_raf, rt::{JsCast, JsValue}}; use web_sys::{console, window, WebGl2RenderingContext, WebGlProgram, WebGlShader, WebGlUniformLocation}; @@ -86,30 +85,59 @@ fn bind_vertex_attrib( fn push_gen_construction( sphere_vec: &mut Vec>, + color_vec: &mut Vec<[f32; 3]>, construction_to_world: &DMatrix, ctrl_x: f64, ctrl_y: f64, radius_x: f64, radius_y: f64 ) { + // push spheres sphere_vec.push(construction_to_world * engine::sphere(0.5, 0.5, ctrl_x, radius_x)); sphere_vec.push(construction_to_world * engine::sphere(-0.5, -0.5, ctrl_y, radius_y)); sphere_vec.push(construction_to_world * engine::sphere(-0.5, 0.5, 0.0, 0.75)); sphere_vec.push(construction_to_world * engine::sphere(0.5, -0.5, 0.0, 0.5)); sphere_vec.push(construction_to_world * engine::sphere(0.0, 0.15, 1.0, 0.25)); sphere_vec.push(construction_to_world * engine::sphere(0.0, -0.15, -1.0, 0.25)); + + // push colors + color_vec.push([1.00_f32, 0.25_f32, 0.00_f32]); + color_vec.push([0.00_f32, 0.25_f32, 1.00_f32]); + color_vec.push([0.25_f32, 0.00_f32, 1.00_f32]); + color_vec.push([0.25_f32, 1.00_f32, 0.00_f32]); + color_vec.push([0.75_f32, 0.75_f32, 0.00_f32]); + color_vec.push([0.00_f32, 0.75_f32, 0.50_f32]); } fn push_low_curv_construction( sphere_vec: &mut Vec>, + color_vec: &mut Vec<[f32; 3]>, construction_to_world: &DMatrix, - curv_x: f64, - curv_y: f64 + off1: f64, + off2: f64, + off3: f64, + curv1: f64, + curv2: f64, + curv3: f64, ) { - sphere_vec.push(construction_to_world * DVector::from_column_slice(&[0.0, -1.0, 0.0, 0.5*curv_x, 0.0])); - sphere_vec.push(construction_to_world * DVector::from_column_slice(&[-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2, 0.5*curv_y, 0.0])); - sphere_vec.push(construction_to_world * engine::sphere(0.5, 0.0, 0.5, FRAC_1_SQRT_2)); - sphere_vec.push(construction_to_world * engine::sphere(-0.5, 0.0, -0.5, FRAC_1_SQRT_2)); + // push spheres + let a = 0.75_f64.sqrt(); + sphere_vec.push(construction_to_world * engine::sphere(0.0, 0.0, 0.0, 1.0)); + sphere_vec.push(construction_to_world * engine::sphere_with_offset(1.0, 0.0, 0.0, off1, curv1)); + sphere_vec.push(construction_to_world * engine::sphere_with_offset(-0.5, a, 0.0, off2, curv2)); + sphere_vec.push(construction_to_world * engine::sphere_with_offset(-0.5, -a, 0.0, off3, curv3)); + sphere_vec.push(construction_to_world * engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0)); + sphere_vec.push(construction_to_world * engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0)); + sphere_vec.push(construction_to_world * engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0)); + + // push colors + color_vec.push([0.75_f32, 0.75_f32, 0.75_f32]); + color_vec.push([1.00_f32, 0.00_f32, 0.25_f32]); + color_vec.push([0.25_f32, 1.00_f32, 0.00_f32]); + color_vec.push([0.00_f32, 0.25_f32, 1.00_f32]); + color_vec.push([0.75_f32, 0.75_f32, 0.75_f32]); + color_vec.push([0.75_f32, 0.75_f32, 0.75_f32]); + color_vec.push([0.75_f32, 0.75_f32, 0.75_f32]); } #[derive(Clone, Copy, PartialEq)] @@ -136,8 +164,12 @@ fn main() { // controls for low-curvature example let low_curv_controls = create_node_ref(); - let curv_x = create_signal(0.0); - let curv_y = create_signal(0.0); + let curv1 = create_signal(0.0); + let curv2 = create_signal(0.0); + let curv3 = create_signal(0.0); + let off1 = create_signal(1.0); + let off2 = create_signal(1.0); + let off3 = create_signal(1.0); // shared controls let opacity = create_signal(0.5); @@ -168,8 +200,12 @@ fn main() { radius_y.track(); // track controls for low-curvature example - curv_x.track(); - curv_y.track(); + curv1.track(); + curv2.track(); + curv3.track(); + off1.track(); + off2.track(); + off3.track(); // track shared controls opacity.track(); @@ -199,17 +235,10 @@ fn main() { } }); - // list construction elements + // create list of construction elements const SPHERE_MAX: usize = 200; let mut sphere_vec = Vec::>::new(); - let color_vec = vec![ - [1.00_f32, 0.25_f32, 0.00_f32], - [0.00_f32, 0.25_f32, 1.00_f32], - [0.25_f32, 0.00_f32, 1.00_f32], - [0.25_f32, 1.00_f32, 0.00_f32], - [0.75_f32, 0.75_f32, 0.00_f32], - [0.00_f32, 0.75_f32, 0.50_f32], - ]; + let mut color_vec = Vec::<[f32; 3]>::new(); // timing let mut last_time = 0.0; @@ -365,17 +394,21 @@ fn main() { // update the construction sphere_vec.clear(); + color_vec.clear(); match tab_selection.get() { Tab::GenTab => push_gen_construction( &mut sphere_vec, + &mut color_vec, &construction_to_world, ctrl_x.get(), ctrl_y.get(), radius_x.get(), radius_y.get() ), Tab::LowCurvTab => push_low_curv_construction( &mut sphere_vec, + &mut color_vec, &construction_to_world, - curv_x.get(), curv_y.get() + off1.get(), off2.get(), off3.get(), + curv1.get(), curv2.get(), curv3.get(), ) }; @@ -496,25 +529,69 @@ fn main() { } div(ref=low_curv_controls) { div(class="control") { - label(for="curv-x") { "Sphere 0 curvature" } + label(for="off-1") { "Sphere 1 offset" } input( type="range", - id="curv-x", - min=0.0, - max=2.0, + id="off-1", + min=-1.0, + max=1.0, step=0.001, - bind:valueAsNumber=curv_x + bind:valueAsNumber=off1 ) } div(class="control") { - label(for="curv-y") { "Sphere 1 curvature" } + label(for="off-2") { "Sphere 2 offset" } input( type="range", - id="curv-y", + id="off-2", + min=-1.0, + max=1.0, + step=0.001, + bind:valueAsNumber=off2 + ) + } + div(class="control") { + label(for="off-3") { "Sphere 3 offset" } + input( + type="range", + id="off-3", + min=-1.0, + max=1.0, + step=0.001, + bind:valueAsNumber=off3 + ) + } + div(class="control") { + label(for="curv-1") { "Sphere 1 curvature" } + input( + type="range", + id="curv-1", min=0.0, max=2.0, step=0.001, - bind:valueAsNumber=curv_y + bind:valueAsNumber=curv1 + ) + } + div(class="control") { + label(for="curv-2") { "Sphere 2 curvature" } + input( + type="range", + id="curv-2", + min=0.0, + max=2.0, + step=0.001, + bind:valueAsNumber=curv2 + ) + } + div(class="control") { + label(for="curv-3") { "Sphere 3 curvature" } + input( + type="range", + id="curv-3", + min=0.0, + max=2.0, + step=0.001, + bind:valueAsNumber=curv3 ) } }