Introduce ghost mode for elements #85
4 changed files with 40 additions and 7 deletions
|
@ -90,6 +90,10 @@ summary > div, .regulator {
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.element > input {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.element-switch {
|
.element-switch {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
|
|
|
@ -101,6 +101,7 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
|
||||||
fn id(&self) -> &String;
|
fn id(&self) -> &String;
|
||||||
fn label(&self) -> &String;
|
fn label(&self) -> &String;
|
||||||
fn representation(&self) -> Signal<DVector<f64>>;
|
fn representation(&self) -> Signal<DVector<f64>>;
|
||||||
|
fn ghost(&self) -> Signal<bool>;
|
||||||
|
|
||||||
// the regulators the element is subject to. the assembly that owns the
|
// the regulators the element is subject to. the assembly that owns the
|
||||||
// element is responsible for keeping this set up to date
|
// element is responsible for keeping this set up to date
|
||||||
|
@ -154,6 +155,7 @@ pub struct Sphere {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub color: ElementColor,
|
pub color: ElementColor,
|
||||||
pub representation: Signal<DVector<f64>>,
|
pub representation: Signal<DVector<f64>>,
|
||||||
|
pub ghost: Signal<bool>,
|
||||||
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
||||||
serial: u64,
|
serial: u64,
|
||||||
column_index: Cell<Option<usize>>
|
column_index: Cell<Option<usize>>
|
||||||
|
@ -173,6 +175,7 @@ impl Sphere {
|
||||||
label: label,
|
label: label,
|
||||||
color: color,
|
color: color,
|
||||||
representation: create_signal(representation),
|
representation: create_signal(representation),
|
||||||
|
ghost: create_signal(false),
|
||||||
regulators: create_signal(BTreeSet::new()),
|
regulators: create_signal(BTreeSet::new()),
|
||||||
serial: Self::next_serial(),
|
serial: Self::next_serial(),
|
||||||
column_index: None.into()
|
column_index: None.into()
|
||||||
|
@ -210,6 +213,10 @@ impl Element for Sphere {
|
||||||
self.representation
|
self.representation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ghost(&self) -> Signal<bool> {
|
||||||
|
self.ghost
|
||||||
|
}
|
||||||
|
|
||||||
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
|
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
|
||||||
self.regulators
|
self.regulators
|
||||||
}
|
}
|
||||||
|
@ -244,6 +251,7 @@ pub struct Point {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub color: ElementColor,
|
pub color: ElementColor,
|
||||||
pub representation: Signal<DVector<f64>>,
|
pub representation: Signal<DVector<f64>>,
|
||||||
|
pub ghost: Signal<bool>,
|
||||||
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
||||||
serial: u64,
|
serial: u64,
|
||||||
column_index: Cell<Option<usize>>
|
column_index: Cell<Option<usize>>
|
||||||
|
@ -263,6 +271,7 @@ impl Point {
|
||||||
label,
|
label,
|
||||||
color,
|
color,
|
||||||
representation: create_signal(representation),
|
representation: create_signal(representation),
|
||||||
|
ghost: create_signal(false),
|
||||||
regulators: create_signal(BTreeSet::new()),
|
regulators: create_signal(BTreeSet::new()),
|
||||||
serial: Self::next_serial(),
|
serial: Self::next_serial(),
|
||||||
column_index: None.into()
|
column_index: None.into()
|
||||||
|
@ -296,6 +305,10 @@ impl Element for Point {
|
||||||
self.representation
|
self.representation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ghost(&self) -> Signal<bool> {
|
||||||
|
self.ghost
|
||||||
|
}
|
||||||
|
|
||||||
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
|
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
|
||||||
self.regulators
|
self.regulators
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ use crate::{
|
||||||
struct SceneSpheres {
|
struct SceneSpheres {
|
||||||
representations: Vec<DVector<f64>>,
|
representations: Vec<DVector<f64>>,
|
||||||
colors: Vec<ElementColor>,
|
colors: Vec<ElementColor>,
|
||||||
|
opacities: Vec<f32>,
|
||||||
highlights: Vec<f32>
|
highlights: Vec<f32>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ impl SceneSpheres {
|
||||||
SceneSpheres {
|
SceneSpheres {
|
||||||
representations: Vec::new(),
|
representations: Vec::new(),
|
||||||
colors: Vec::new(),
|
colors: Vec::new(),
|
||||||
|
opacities: Vec::new(),
|
||||||
highlights: Vec::new()
|
highlights: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,9 +43,10 @@ impl SceneSpheres {
|
||||||
self.representations.len().try_into().expect("Number of spheres must fit in a 32-bit integer")
|
self.representations.len().try_into().expect("Number of spheres must fit in a 32-bit integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, representation: DVector<f64>, color: ElementColor, highlight: f32) {
|
fn push(&mut self, representation: DVector<f64>, color: ElementColor, opacity: f32, highlight: f32) {
|
||||||
self.representations.push(representation);
|
self.representations.push(representation);
|
||||||
self.colors.push(color);
|
self.colors.push(color);
|
||||||
|
self.opacities.push(opacity);
|
||||||
self.highlights.push(highlight);
|
self.highlights.push(highlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,11 +101,16 @@ pub trait DisplayItem {
|
||||||
|
|
||||||
impl DisplayItem for Sphere {
|
impl DisplayItem for Sphere {
|
||||||
fn show(&self, scene: &mut Scene, selected: bool) {
|
fn show(&self, scene: &mut Scene, selected: bool) {
|
||||||
const HIGHLIGHT: f32 = 0.2; /* SCAFFOLDING */
|
/* SCAFFOLDING */
|
||||||
|
const DEFAULT_OPACITY: f32 = 0.5;
|
||||||
|
const GHOST_OPACITY: f32 = 0.2;
|
||||||
|
const HIGHLIGHT: f32 = 0.2;
|
||||||
|
|
||||||
let representation = self.representation.get_clone_untracked();
|
let representation = self.representation.get_clone_untracked();
|
||||||
let color = if selected { self.color.map(|channel| 0.2 + 0.8*channel) } else { self.color };
|
let color = if selected { self.color.map(|channel| 0.2 + 0.8*channel) } else { self.color };
|
||||||
|
let opacity = if self.ghost.get() { GHOST_OPACITY } else { DEFAULT_OPACITY };
|
||||||
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
||||||
scene.spheres.push(representation, color, highlight);
|
scene.spheres.push(representation, color, opacity, highlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method should be kept synchronized with `sphere_cast` in
|
// this method should be kept synchronized with `sphere_cast` in
|
||||||
|
@ -365,6 +373,7 @@ pub fn Display() -> View {
|
||||||
state.assembly.elements.with(|elts| {
|
state.assembly.elements.with(|elts| {
|
||||||
for elt in elts {
|
for elt in elts {
|
||||||
elt.representation().track();
|
elt.representation().track();
|
||||||
|
elt.ghost().track();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
state.selection.track();
|
state.selection.track();
|
||||||
|
@ -395,7 +404,6 @@ pub fn Display() -> View {
|
||||||
const SHRINKING_SPEED: f64 = 0.15; // in length units per second
|
const SHRINKING_SPEED: f64 = 0.15; // in length units per second
|
||||||
|
|
||||||
// display parameters
|
// display parameters
|
||||||
const OPACITY: f32 = 0.5; /* SCAFFOLDING */
|
|
||||||
const LAYER_THRESHOLD: i32 = 0; /* DEBUG */
|
const LAYER_THRESHOLD: i32 = 0; /* DEBUG */
|
||||||
const DEBUG_MODE: i32 = 0; /* DEBUG */
|
const DEBUG_MODE: i32 = 0; /* DEBUG */
|
||||||
|
|
||||||
|
@ -648,7 +656,7 @@ pub fn Display() -> View {
|
||||||
|
|
||||||
let sphere_color = &mut [0.0; 4];
|
let sphere_color = &mut [0.0; 4];
|
||||||
sphere_color[..3].copy_from_slice(&scene.spheres.colors[n]);
|
sphere_color[..3].copy_from_slice(&scene.spheres.colors[n]);
|
||||||
sphere_color[3] = OPACITY;
|
sphere_color[3] = scene.spheres.opacities[n];
|
||||||
|
|
||||||
ctx.uniform3fv_with_f32_array(
|
ctx.uniform3fv_with_f32_array(
|
||||||
sphere_sp_locs[n].as_ref(),
|
sphere_sp_locs[n].as_ref(),
|
||||||
|
@ -854,7 +862,11 @@ pub fn Display() -> View {
|
||||||
let (dir, pixel_size) = event_dir(&event);
|
let (dir, pixel_size) = event_dir(&event);
|
||||||
console::log_1(&JsValue::from(dir.to_string()));
|
console::log_1(&JsValue::from(dir.to_string()));
|
||||||
let mut clicked: Option<(Rc<dyn Element>, f64)> = None;
|
let mut clicked: Option<(Rc<dyn Element>, f64)> = None;
|
||||||
for elt in state.assembly.elements.get_clone_untracked() {
|
let tangible_elts = state.assembly.elements
|
||||||
|
.get_clone_untracked()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|elt| !elt.ghost().get());
|
||||||
|
for elt in tangible_elts {
|
||||||
match assembly_to_world.with(|asm_to_world| elt.cast(dir, asm_to_world, pixel_size)) {
|
match assembly_to_world.with(|asm_to_world| elt.cast(dir, asm_to_world, pixel_size)) {
|
||||||
Some(depth) => match clicked {
|
Some(depth) => match clicked {
|
||||||
Some((_, best_depth)) => {
|
Some((_, best_depth)) => {
|
||||||
|
|
|
@ -202,7 +202,11 @@ fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
||||||
) {
|
) {
|
||||||
div(class="element-label") { (label) }
|
div(class="element-label") { (label) }
|
||||||
div(class="element-representation") { (rep_components) }
|
div(class="element-representation") { (rep_components) }
|
||||||
div(class="status")
|
input(
|
||||||
|
|||||||
|
r#type="checkbox",
|
||||||
|
bind:checked=element.ghost(),
|
||||||
|
on:click=|event: MouseEvent| event.stop_propagation()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul(class="regulators") {
|
ul(class="regulators") {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue
Did we intentionally remove this indicator, which flags an element with an invalidly specified regulator when the regulator list is closed? The removal looks like a regression—especially because we didn't remove the associated CSS—but I wanted to check in case we decided on it in a meeting and I forgot to write it down.
I think I never responded to this. If you think there should be some indication of a regulator with bad input (or something like that) and there currently is no such indication, absolutely feel free to file an issue. I don't recall any discussion that we should not have such an indication. (And hopefully filing issues/recording notes in some permanent online fashion immediately after our meetings will help with confidence on such matters!)