194 lines
5.1 KiB
Rust
194 lines
5.1 KiB
Rust
//! World-space primitives. A sprite is a list of these; they are projected,
|
|
//! culled and shaded only when the scene is rendered for a rotation.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub type P2 = [f64; 2];
|
|
pub type P3 = [f64; 3];
|
|
|
|
/// Height of a prism's bottom or top: one value, or one per footprint vertex
|
|
/// (sloped roofs, stair flights).
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum Zs {
|
|
One(f64),
|
|
Each(Vec<f64>),
|
|
}
|
|
|
|
impl Zs {
|
|
#[inline]
|
|
pub fn at(&self, i: usize) -> f64 {
|
|
match self {
|
|
Zs::One(z) => *z,
|
|
Zs::Each(v) => v.get(i).copied().unwrap_or(f64::NAN),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<f64> for Zs {
|
|
fn from(z: f64) -> Self {
|
|
Zs::One(z)
|
|
}
|
|
}
|
|
|
|
/// Classes and switches of a solid. `mat` is the material class for every
|
|
/// face; `side` / `top` override it for the side faces / the top.
|
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
|
pub struct Style {
|
|
pub mat: String,
|
|
pub side: Option<String>,
|
|
pub top: Option<String>,
|
|
pub no_side: bool,
|
|
pub no_top: bool,
|
|
/// Pick id on the faces themselves (otherwise the sprite's id applies).
|
|
pub id: Option<String>,
|
|
}
|
|
|
|
impl Style {
|
|
pub fn mat(mat: impl Into<String>) -> Self {
|
|
Style {
|
|
mat: mat.into(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
pub fn id(mut self, id: impl Into<String>) -> Self {
|
|
self.id = Some(id.into());
|
|
self
|
|
}
|
|
pub fn top(mut self, top: impl Into<String>) -> Self {
|
|
self.top = Some(top.into());
|
|
self
|
|
}
|
|
pub fn side(mut self, side: impl Into<String>) -> Self {
|
|
self.side = Some(side.into());
|
|
self
|
|
}
|
|
pub fn no_top(mut self) -> Self {
|
|
self.no_top = true;
|
|
self
|
|
}
|
|
pub fn side_cls(&self) -> &str {
|
|
self.side.as_deref().unwrap_or(&self.mat)
|
|
}
|
|
pub fn top_cls(&self) -> &str {
|
|
self.top.as_deref().unwrap_or(&self.mat)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
pub enum Prim {
|
|
/// Vertical prism over a convex counter-clockwise footprint.
|
|
Prism {
|
|
pts: Vec<P2>,
|
|
z0: Zs,
|
|
z1: Zs,
|
|
st: Style,
|
|
},
|
|
/// Upright cylinder (two side faces and an elliptic top).
|
|
Cyl {
|
|
c: P2,
|
|
r: f64,
|
|
z0: f64,
|
|
z1: f64,
|
|
st: Style,
|
|
},
|
|
/// Frustum (cone) with n sides: radius r0 at z0, r1 at z1.
|
|
Frust {
|
|
c: P2,
|
|
r0: f64,
|
|
r1: f64,
|
|
z0: f64,
|
|
z1: f64,
|
|
n: u32,
|
|
st: Style,
|
|
},
|
|
/// Openings (windows, doors) on the vertical face through a -> b; the
|
|
/// outward normal is on the right of a -> b. Each entry is
|
|
/// [u0, u1, z0, z1]: distance along the face and heights.
|
|
Open {
|
|
a: P2,
|
|
b: P2,
|
|
list: Vec<[f64; 4]>,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Flat polygon(s) at height z; later polygons cut holes (even-odd).
|
|
Flat {
|
|
polys: Vec<Vec<P2>>,
|
|
z: f64,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Line segments [x0, y0, x1, y1] at height z.
|
|
Lines {
|
|
segs: Vec<[f64; 4]>,
|
|
z: f64,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Flat ellipse (a circle on the ground plane) at height z.
|
|
Disc {
|
|
c: P2,
|
|
z: f64,
|
|
r: f64,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Planar polygon in 3D (roof slopes, gables, ramps), shaded by its
|
|
/// normal: mostly upward = top, else left/centre/right by direction.
|
|
/// One-sided faces (counter-clockwise seen from outside) are culled
|
|
/// when turned away; two-sided faces always draw.
|
|
Face {
|
|
pts: Vec<P3>,
|
|
mat: String,
|
|
id: Option<String>,
|
|
two_sided: bool,
|
|
},
|
|
/// 3D polygons merged into one path with a fixed class (PV modules,
|
|
/// signs, markings on slopes): no shading, no culling.
|
|
Poly {
|
|
polys: Vec<Vec<P3>>,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Screen-space circle of radius r (screen units) at a world point,
|
|
/// shifted by off (screen units): heads, lamps, LEDs, tree crowns.
|
|
Circle {
|
|
at: P3,
|
|
r: f64,
|
|
off: P2,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Screen-space vertical strip of half width w between two heights:
|
|
/// trunks, thin poles.
|
|
Stem {
|
|
at: P2,
|
|
z0: f64,
|
|
z1: f64,
|
|
w: f64,
|
|
cls: String,
|
|
id: Option<String>,
|
|
},
|
|
/// Rising steam or smoke puffs (CSS animation).
|
|
Puffs {
|
|
at: P3,
|
|
kind: String,
|
|
n: u32,
|
|
dur: f64,
|
|
},
|
|
/// Spinning fan lying in the plane at height at.z (CSS animation).
|
|
Fan { at: P3, r: f64 },
|
|
/// Raw SVG translated to a world point (or untranslated without one).
|
|
/// `{uid}` in the text is replaced by the view's unique id.
|
|
Raw { at: Option<P3>, svg: String },
|
|
/// `<g class style>` around nested primitives (animation, ghosting).
|
|
Group {
|
|
cls: String,
|
|
style: String,
|
|
items: Vec<Prim>,
|
|
},
|
|
/// Items drawn back to front by the depth of their plan anchor.
|
|
Seq { items: Vec<(P2, Vec<Prim>)> },
|
|
}
|