forked from StudioInfinity/dyna3
Introduce an element trait
For now, this is just a thin wrapper around the old element structure, which was renamed to `Sphere` in the previous commit. The biggest organizational change is moving `cast` into the `DisplayItem` trait.
This commit is contained in:
parent
a1e23543cb
commit
f9df459a0d
3 changed files with 165 additions and 98 deletions
|
@ -56,7 +56,7 @@ impl ScenePoints {
|
|||
}
|
||||
}
|
||||
|
||||
struct Scene {
|
||||
pub struct Scene {
|
||||
spheres: SceneSpheres,
|
||||
points: ScenePoints
|
||||
}
|
||||
|
@ -70,8 +70,13 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
trait DisplayItem {
|
||||
pub trait DisplayItem {
|
||||
fn show(&self, scene: &mut Scene, selected: bool);
|
||||
|
||||
// the smallest positive depth, represented as a multiple of `dir`, where
|
||||
// the line generated by `dir` hits the element. returns `None` if the line
|
||||
// misses the element
|
||||
fn cast(&self, dir: Vector3<f64>, assembly_to_world: &DMatrix<f64>) -> Option<f64>;
|
||||
}
|
||||
|
||||
impl DisplayItem for Sphere {
|
||||
|
@ -82,6 +87,46 @@ impl DisplayItem for Sphere {
|
|||
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
||||
scene.spheres.push(representation, color, highlight);
|
||||
}
|
||||
|
||||
// this method should be kept synchronized with `sphere_cast` in
|
||||
// `spheres.frag`, which does essentially the same thing on the GPU side
|
||||
fn cast(&self, dir: Vector3<f64>, assembly_to_world: &DMatrix<f64>) -> Option<f64> {
|
||||
// if `a/b` is less than this threshold, we approximate
|
||||
// `a*u^2 + b*u + c` by the linear function `b*u + c`
|
||||
const DEG_THRESHOLD: f64 = 1e-9;
|
||||
|
||||
let rep = self.representation.with_untracked(|rep| assembly_to_world * rep);
|
||||
let a = -rep[3] * dir.norm_squared();
|
||||
let b = rep.rows_range(..3).dot(&dir);
|
||||
let c = -rep[4];
|
||||
|
||||
let adjust = 4.0*a*c/(b*b);
|
||||
if adjust < 1.0 {
|
||||
// as long as `b` is non-zero, the linear approximation of
|
||||
//
|
||||
// a*u^2 + b*u + c
|
||||
//
|
||||
// at `u = 0` will reach zero at a finite depth `u_lin`. the root of
|
||||
// the quadratic adjacent to `u_lin` is stored in `lin_root`. if
|
||||
// both roots have the same sign, `lin_root` will be the one closer
|
||||
// to `u = 0`
|
||||
let square_rect_ratio = 1.0 + (1.0 - adjust).sqrt();
|
||||
let lin_root = -(2.0*c)/b / square_rect_ratio;
|
||||
if a.abs() > DEG_THRESHOLD * b.abs() {
|
||||
if lin_root > 0.0 {
|
||||
Some(lin_root)
|
||||
} else {
|
||||
let other_root = -b/(2.*a) * square_rect_ratio;
|
||||
(other_root > 0.0).then_some(other_root)
|
||||
}
|
||||
} else {
|
||||
(lin_root > 0.0).then_some(lin_root)
|
||||
}
|
||||
} else {
|
||||
// the line through `dir` misses the sphere completely
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- WebGL utilities ---
|
||||
|
@ -264,7 +309,7 @@ pub fn Display() -> View {
|
|||
create_effect(move || {
|
||||
state.assembly.elements.with(|elts| {
|
||||
for (_, elt) in elts {
|
||||
elt.representation.track();
|
||||
elt.representation().track();
|
||||
}
|
||||
});
|
||||
state.selection.track();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue