Skip to main content

gled_model/patching/definition/
model_presets.rs

1//! Ready-made models, so a definition can be drawn before anyone opens a
2//! vector editor. The SVGs are white on transparent: the preview tints them.
3
4use crate::patching::definition::Model;
5
6/// Millimetres an SVG view box spans for a size in metres, at least one so a
7/// zero size still makes a valid drawing.
8fn view_box_mm(size_m: [f32; 2]) -> [f32; 2] {
9    size_m.map(|metres| (metres * 1000.0).round().max(1.0))
10}
11
12impl Model {
13    /// A filled circle `diameter_m` across: a single LED.
14    pub fn circle(name: impl Into<String>, diameter_m: f32) -> Self {
15        Self {
16            name: name.into(),
17            size_m: [diameter_m; 2],
18            svg: r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><circle cx="5" cy="5" r="4.5" fill="#ffffff"/></svg>"##
19                .to_string(),
20        }
21    }
22
23    /// A filled rectangle with rounded corners: an LED chip or a tube segment.
24    pub fn rounded_rect(name: impl Into<String>, size_m: [f32; 2]) -> Self {
25        let [width, height] = view_box_mm(size_m);
26        let radius = 0.2 * width.min(height);
27        Self {
28            name: name.into(),
29            size_m,
30            svg: format!(
31                r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}"><rect width="{width}" height="{height}" rx="{radius}" fill="#ffffff"/></svg>"##
32            ),
33        }
34    }
35
36    /// The edge of a rectangle with rounded corners, unfilled: a fixture's
37    /// outline, which leaves its pixels visible.
38    pub fn frame(name: impl Into<String>, size_m: [f32; 2]) -> Self {
39        let [width, height] = view_box_mm(size_m);
40        let stroke = (0.04 * width.min(height)).clamp(1.0, 16.0);
41        let inset = stroke / 2.0;
42        let radius = 0.1 * width.min(height);
43        let inner_width = width - stroke;
44        let inner_height = height - stroke;
45        Self {
46            name: name.into(),
47            size_m,
48            svg: format!(
49                r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}"><rect x="{inset}" y="{inset}" width="{inner_width}" height="{inner_height}" rx="{radius}" fill="none" stroke="#ffffff" stroke-width="{stroke}"/></svg>"##
50            ),
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn a_rounded_rect_spans_its_size_in_millimetres() {
61        let model = Model::rounded_rect("chip", [0.014, 0.09]);
62        assert!(model.svg.contains(r#"viewBox="0 0 14 90""#));
63    }
64
65    #[test]
66    fn a_zero_sized_frame_still_has_a_view_box() {
67        let model = Model::frame("body", [0.0, 0.0]);
68        assert!(model.svg.contains(r#"viewBox="0 0 1 1""#));
69    }
70}