Compare commits

..

5 Commits

Author SHA1 Message Date
Aaron Fenyes
336b940471 Outline: start on editor state and outline view 2024-09-12 15:24:41 -07:00
Aaron Fenyes
d3c9a08d22 Add zoom to keyboard controls 2024-09-10 04:08:49 -07:00
Aaron Fenyes
aceac5e5c4 Add roll to keyboard controls 2024-09-10 03:14:33 -07:00
Aaron Fenyes
20d072d615 Combine key-down and key-up handlers 2024-09-10 02:29:50 -07:00
Aaron Fenyes
c67f37c934 Implement keyboard navigation 2024-09-09 19:41:15 -07:00
7 changed files with 311 additions and 31 deletions

View File

@ -9,9 +9,17 @@
extern crate js_sys; extern crate js_sys;
use core::array; use core::array;
use nalgebra::{DMatrix, DVector}; use nalgebra::{DMatrix, DVector, Rotation3, Vector3};
use sycamore::{prelude::*, motion::create_raf, rt::{JsCast, JsValue}}; use sycamore::{prelude::*, motion::create_raf, rt::{JsCast, JsValue}};
use web_sys::{console, window, WebGl2RenderingContext, WebGlProgram, WebGlShader, WebGlUniformLocation}; use web_sys::{
console,
window,
KeyboardEvent,
WebGl2RenderingContext,
WebGlProgram,
WebGlShader,
WebGlUniformLocation
};
mod engine; mod engine;
@ -157,6 +165,16 @@ fn main() {
// tab selection // tab selection
let tab_selection = create_signal(Tab::GenTab); let tab_selection = create_signal(Tab::GenTab);
// navigation
let pitch_up = create_signal(0.0);
let pitch_down = create_signal(0.0);
let yaw_right = create_signal(0.0);
let yaw_left = create_signal(0.0);
let roll_ccw = create_signal(0.0);
let roll_cw = create_signal(0.0);
let zoom_in = create_signal(0.0);
let zoom_out = create_signal(0.0);
// controls for general example // controls for general example
let gen_controls = create_node_ref(); let gen_controls = create_node_ref();
let ctrl_x = create_signal(0.0); let ctrl_x = create_signal(0.0);
@ -246,8 +264,12 @@ fn main() {
let mut last_time = 0.0; let mut last_time = 0.0;
// scene parameters // scene parameters
const TURNTABLE_SPEED: f64 = 0.5; const ROT_SPEED: f64 = 0.4; // in radians per second
let mut turntable_angle = 0.0; const TURNTABLE_SPEED: f64 = 0.1; // in radians per second
const ZOOM_SPEED: f64 = 0.15;
let mut orientation = DMatrix::<f64>::identity(5, 5);
let mut rotation = DMatrix::<f64>::identity(5, 5);
let mut location_z: f64 = 5.0;
/* INSTRUMENTS */ /* INSTRUMENTS */
let performance = window().unwrap().performance().unwrap(); let performance = window().unwrap().performance().unwrap();
@ -353,11 +375,45 @@ fn main() {
let time_step = 0.001*(time - last_time); let time_step = 0.001*(time - last_time);
last_time = time; last_time = time;
// move the turntable // get the navigation state
let pitch_up_val = pitch_up.get();
let pitch_down_val = pitch_down.get();
let yaw_right_val = yaw_right.get();
let yaw_left_val = yaw_left.get();
let roll_ccw_val = roll_ccw.get();
let roll_cw_val = roll_cw.get();
let zoom_in_val = zoom_in.get();
let zoom_out_val = zoom_out.get();
let turntable_val = turntable.get(); let turntable_val = turntable.get();
// update the construction's orientation
let ang_vel = {
let pitch = pitch_up_val - pitch_down_val;
let yaw = yaw_right_val - yaw_left_val;
let roll = roll_ccw_val - roll_cw_val;
let ang_vel_from_keyboard =
if pitch != 0.0 || yaw != 0.0 || roll != 0.0 {
ROT_SPEED * Vector3::new(-pitch, yaw, roll).normalize()
} else {
Vector3::zeros()
};
let ang_vel_from_turntable =
if turntable_val { if turntable_val {
turntable_angle += TURNTABLE_SPEED * time_step; Vector3::new(0.0, TURNTABLE_SPEED, 0.0)
} } else {
Vector3::zeros()
};
ang_vel_from_keyboard + ang_vel_from_turntable
};
let mut rotation_sp = rotation.fixed_view_mut::<3, 3>(0, 0);
rotation_sp.copy_from(
Rotation3::from_scaled_axis(time_step * ang_vel).matrix()
);
orientation = &rotation * &orientation;
// update the construction's location
let zoom = zoom_out_val - zoom_in_val;
location_z *= (time_step * ZOOM_SPEED * zoom).exp();
if scene_changed.get() { if scene_changed.get() {
/* INSTRUMENTS */ /* INSTRUMENTS */
@ -369,30 +425,18 @@ fn main() {
frames_since_last_sample = 0; frames_since_last_sample = 0;
} }
// set the orientation and translation // find the map from construction space to world space
let orientation = { let location = {
let ang_cos = turntable_angle.cos(); let u = -location_z;
let ang_sin = turntable_angle.sin();
DMatrix::from_column_slice(5, 5, &[
ang_cos, 0.0, ang_sin, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
-ang_sin, 0.0, ang_cos, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 0.0, 1.0
])
};
let translation = {
const LEN: f64 = -5.0;
const LEN_SQ: f64 = LEN*LEN;
DMatrix::from_column_slice(5, 5, &[ DMatrix::from_column_slice(5, 5, &[
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0, LEN, 0.0, 0.0, 1.0, 0.0, u,
0.0, 0.0, 2.0*LEN, 1.0, LEN_SQ, 0.0, 0.0, 2.0*u, 1.0, u*u,
0.0, 0.0, 0.0, 0.0, 1.0 0.0, 0.0, 0.0, 0.0, 1.0
]) ])
}; };
let construction_to_world = &translation * orientation; let construction_to_world = &location * &orientation;
// update the construction // update the construction
sphere_vec.clear(); sphere_vec.clear();
@ -450,7 +494,17 @@ fn main() {
ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32); ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32);
// clear scene change flag // clear scene change flag
scene_changed.set(turntable_val); scene_changed.set(
pitch_up_val != 0.0
|| pitch_down_val != 0.0
|| yaw_left_val != 0.0
|| yaw_right_val != 0.0
|| roll_cw_val != 0.0
|| roll_ccw_val != 0.0
|| zoom_in_val != 0.0
|| zoom_out_val != 0.0
|| turntable_val
);
} else { } else {
frames_since_last_sample = 0; frames_since_last_sample = 0;
mean_frame_interval.set(-1.0); mean_frame_interval.set(-1.0);
@ -459,6 +513,26 @@ fn main() {
start_animation_loop(); start_animation_loop();
}); });
let set_nav_signal = move |event: KeyboardEvent, value: f64| {
let mut navigating = true;
let shift = event.shift_key();
match event.key().as_str() {
"ArrowUp" if shift => zoom_in.set(value),
"ArrowDown" if shift => zoom_out.set(value),
"ArrowUp" => pitch_up.set(value),
"ArrowDown" => pitch_down.set(value),
"ArrowRight" if shift => roll_cw.set(value),
"ArrowLeft" if shift => roll_ccw.set(value),
"ArrowRight" => yaw_right.set(value),
"ArrowLeft" => yaw_left.set(value),
_ => navigating = false
};
if navigating {
scene_changed.set(true);
event.prevent_default();
}
};
view! { view! {
div(id="app") { div(id="app") {
div(class="tab-pane") { div(class="tab-pane") {
@ -482,7 +556,48 @@ fn main() {
} }
} }
div { "Mean frame interval: " (mean_frame_interval.get()) " ms" } div { "Mean frame interval: " (mean_frame_interval.get()) " ms" }
canvas(ref=display, width=600, height=600, tabindex=0) canvas(
ref=display,
width=600,
height=600,
tabindex=0,
on:keydown=move |event: KeyboardEvent| {
if event.key() == "Shift" {
roll_cw.set(yaw_right.get());
roll_ccw.set(yaw_left.get());
zoom_in.set(pitch_up.get());
zoom_out.set(pitch_down.get());
yaw_right.set(0.0);
yaw_left.set(0.0);
pitch_up.set(0.0);
pitch_down.set(0.0);
} else {
set_nav_signal(event, 1.0);
}
},
on:keyup=move |event: KeyboardEvent| {
if event.key() == "Shift" {
yaw_right.set(roll_cw.get());
yaw_left.set(roll_ccw.get());
pitch_up.set(zoom_in.get());
pitch_down.set(zoom_out.get());
roll_cw.set(0.0);
roll_ccw.set(0.0);
zoom_in.set(0.0);
zoom_out.set(0.0);
} else {
set_nav_signal(event, 0.0);
}
},
on:blur=move |_| {
pitch_up.set(0.0);
pitch_down.set(0.0);
yaw_right.set(0.0);
yaw_left.set(0.0);
roll_ccw.set(0.0);
roll_cw.set(0.0);
}
)
div(ref=gen_controls) { div(ref=gen_controls) {
label(class="control") { label(class="control") {
span { "Sphere 0 depth" } span { "Sphere 0 depth" }

3
app-proto/sketch-outline/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target
dist
Cargo.lock

View File

@ -0,0 +1,29 @@
[package]
name = "sketch-outline"
version = "0.1.0"
authors = ["Aaron"]
edition = "2021"
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3.70"
nalgebra = "0.33.0"
sycamore = "0.9.0-beta.3"
# 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 }
[dependencies.web-sys]
version = "0.3.69"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[profile.release]
opt-level = "s" # optimize for small code size
debug = true # include debug symbols

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Sketch outline</title>
<link data-trunk rel="css" href="main.css"/>
</head>
<body></body>
</html>

View File

@ -0,0 +1,50 @@
body {
margin-left: 20px;
margin-top: 20px;
color: #fcfcfc;
background-color: #222;
}
ul {
width: 450px;
padding: 8px;
border: 1px solid #888;
border-radius: 16px;
}
li {
display: flex;
padding: 3px;
list-style-type: none;
background-color: #444;
border-radius: 8px;
}
li:not(:last-child) {
margin-bottom: 8px;
}
li > .elt-name {
flex-grow: 1;
padding: 2px 0px 2px 4px;
}
li > .elt-rep {
display: flex;
}
li > .elt-rep > div {
padding: 2px;
margin-left: 3px;
text-align: center;
width: 60px;
background-color: #333;
}
li > .elt-rep > div:first-child {
border-radius: 6px 0px 0px 6px;
}
li > .elt-rep > div:last-child {
border-radius: 0px 6px 6px 0px;
}

View File

@ -0,0 +1,61 @@
use nalgebra::DVector;
use sycamore::{prelude::*, web::tags::div};
#[derive(Clone, PartialEq)]
struct Element {
id: i64,
name: String,
rep: DVector<f64>,
color: [f32; 3]
}
struct EditorState {
elements: Signal<Vec<Element>>
}
#[component]
pub fn Editor() -> View {
let state = EditorState {
elements: create_signal(vec![
Element {
id: 1,
name: String::from("Central"),
rep: DVector::<f64>::from_column_slice(&[0.0, 0.0, 0.0, 0.25, -1.0]),
color: [0.75_f32, 0.75_f32, 0.75_f32]
},
Element {
id: 2,
name: String::from("Wing A"),
rep: DVector::<f64>::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]),
color: [1.00_f32, 0.25_f32, 0.00_f32]
},
Element {
id: 3,
name: String::from("Wing B"),
rep: DVector::<f64>::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]),
color: [1.00_f32, 0.25_f32, 0.00_f32]
}
])
};
view! {
ul {
Keyed(
list=state.elements,
view=|elt| {
let name = elt.name.clone();
let rep_components = elt.rep.iter().map(
|u| View::from(div().children(u.to_string().replace("-", "\u{2212}")))
).collect::<Vec<_>>();
view! {
li {
div(class="elt-name") { (name) }
div(class="elt-rep") { (rep_components) }
}
}
},
key=|elt| elt.id
)
}
}
}

View File

@ -0,0 +1,13 @@
use sycamore::prelude::*;
mod editor;
use editor::Editor;
fn main() {
sycamore::render(|| {
view! {
Editor {}
}
});
}