gled-model 2.32.0

The data model of gled: projects, scenes, effects, animations, curves and assets
Documentation
//! Ready-made models, so a definition can be drawn before anyone opens a
//! vector editor. The SVGs are white on transparent: the preview tints them.

use crate::patching::definition::Model;

/// Millimetres an SVG view box spans for a size in metres, at least one so a
/// zero size still makes a valid drawing.
fn view_box_mm(size_m: [f32; 2]) -> [f32; 2] {
    size_m.map(|metres| (metres * 1000.0).round().max(1.0))
}

impl Model {
    /// A filled circle `diameter_m` across: a single LED.
    pub fn circle(name: impl Into<String>, diameter_m: f32) -> Self {
        Self {
            name: name.into(),
            size_m: [diameter_m; 2],
            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>"##
                .to_string(),
        }
    }

    /// A filled rectangle with rounded corners: an LED chip or a tube segment.
    pub fn rounded_rect(name: impl Into<String>, size_m: [f32; 2]) -> Self {
        let [width, height] = view_box_mm(size_m);
        let radius = 0.2 * width.min(height);
        Self {
            name: name.into(),
            size_m,
            svg: format!(
                r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}"><rect width="{width}" height="{height}" rx="{radius}" fill="#ffffff"/></svg>"##
            ),
        }
    }

    /// The edge of a rectangle with rounded corners, unfilled: a fixture's
    /// outline, which leaves its pixels visible.
    pub fn frame(name: impl Into<String>, size_m: [f32; 2]) -> Self {
        let [width, height] = view_box_mm(size_m);
        let stroke = (0.04 * width.min(height)).clamp(1.0, 16.0);
        let inset = stroke / 2.0;
        let radius = 0.1 * width.min(height);
        let inner_width = width - stroke;
        let inner_height = height - stroke;
        Self {
            name: name.into(),
            size_m,
            svg: format!(
                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>"##
            ),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn a_rounded_rect_spans_its_size_in_millimetres() {
        let model = Model::rounded_rect("chip", [0.014, 0.09]);
        assert!(model.svg.contains(r#"viewBox="0 0 14 90""#));
    }

    #[test]
    fn a_zero_sized_frame_still_has_a_view_box() {
        let model = Model::frame("body", [0.0, 0.0]);
        assert!(model.svg.contains(r#"viewBox="0 0 1 1""#));
    }
}