truchet 0.1.1

Library for truchet tiling
Documentation
use svg::{node::element::{Group, Path, path::Data}, Node};

use crate::{to_svg::ToSVG, vec2::Vec2};

use super::traits::Tile;

#[derive(Clone, Copy)]
pub struct ElasticCircleTile {
    flipped: bool,
    half_width: f32,
    radius: f32
}

impl ElasticCircleTile {
    pub fn new(flipped: bool, width: f32, radius: f32) -> Self {
        let width = width.clamp(0.0, 1.0) / 2.0;
        return Self { flipped, half_width: width, radius };
    }

    pub fn flipped(mut self) -> Self {
        self.flipped = !self.flipped;
        return self;
    }

    fn upper_arc(self) -> (Vec2<f32>, Vec2<f32>) {
        return ( 
            Vec2::new(0.0, 0.5),
            Vec2::new(0.5, 0.0)
        );
    }

    fn lower_arc(self) -> (Vec2<f32>, Vec2<f32>) {
        return (
            Vec2::new(1.0, 0.5),
            Vec2::new(0.5, 1.0)
        );
    }
}

impl Default for ElasticCircleTile {
    #[inline]
    fn default() -> Self {
        return Self::new(false, 0.1, 0.5);
    }
}

impl Tile for ElasticCircleTile {
    #[inline]
    fn set_brightness(&mut self, brightness: f32) {
        self.half_width = (1.0 - brightness) / 4.0;
    }
}

impl ToSVG for ElasticCircleTile {
    fn to_svg_node(&self) -> Box<dyn svg::Node> {
        let (upper_start, upper_end) = self.upper_arc();
        let (lower_start, lower_end) = self.lower_arc();

        let r_plus_hw = self.radius + self.half_width;
        let r_minus_hw = self.radius - self.half_width;

        let c1_data = Data::new()
            .move_to((upper_start.x(), upper_start.y() + self.half_width))
            .elliptical_arc_to((
                r_plus_hw, r_plus_hw,
                0, 0, 0, 
                upper_end.x() + self.half_width, upper_end.y()
            ))
            .line_to((upper_end.x() - self.half_width, upper_end.y()))
            .elliptical_arc_to((
                r_minus_hw, r_minus_hw,
                0, 0, 1, 
                upper_start.x(), upper_start.y() - self.half_width
            ))
            .line_to((upper_start.x(), upper_start.y() + self.half_width));

        let c2_data = Data::new()
            .move_to((lower_start.x(), lower_start.y() - self.half_width))
            .elliptical_arc_to((
                r_plus_hw, r_plus_hw,
                0, 0, 0, 
                lower_end.x() - self.half_width, lower_end.y()
            ))
            .line_to((lower_end.x() + self.half_width, lower_end.y()))
            .elliptical_arc_to((
                r_minus_hw, r_minus_hw,
                0, 0, 1, 
                lower_start.x(), lower_start.y() + self.half_width
            ))
            .line_to((lower_start.x(), lower_start.y() - self.half_width));

        let c1 = Path::new()
            .set("stroke", "black")
            .set("stroke-width", "0.01")
            .set("d", c1_data);


        let c2 = c1.clone().set("d", c2_data);

        let mut g = Group::new()
            .add(c1)
            .add(c2);

        if self.flipped {
            g.assign("transform", "rotate(90, 0.5, 0.5)");
        }

        return Box::new(g);
    }
}