pizarra 0.7.1

The backend for a simple vector hand-drawing application
Documentation
use crate::point::Point;
use crate::draw_commands::DrawCommand;
use super::{ShapeTrait, ShapeFinished};
use crate::color::Color;
use crate::ShapeType;

pub struct Ellipse {
    bbox: [Point; 2],
    thickness: f64,
    color: Color,
}

impl Ellipse {
    pub fn new(color: Color, initial: Point, thickness: f64) -> Ellipse {
        Ellipse {
            bbox: [initial; 2],
            thickness,
            color,
        }
    }

    #[cfg(test)]
    pub fn from_corners([corner_1, corner_2]: [Point; 2]) -> Ellipse {
        Ellipse {
            bbox: [corner_1, corner_2],
            thickness: 4.0,
            color: Color::green(),
        }
    }

    pub fn with_params(bbox: [Point; 2], color: Color, thickness: f64) -> Ellipse {
        Ellipse {
            bbox, color, thickness,
        }
    }
}

impl ShapeTrait for Ellipse {
    fn handle_mouse_moved(&mut self, pos: Point) {
        self.bbox[1] = pos;
    }

    fn handle_button_pressed(&mut self, _pos: Point) { }

    fn handle_button_released(&mut self, pos: Point) -> ShapeFinished {
        self.bbox[1] = pos;
        ShapeFinished::Yes
    }

    fn draw_commands(&self) -> DrawCommand {
        DrawCommand::Ellipse {
            thickness: self.thickness,
            color: self.color,
            bbox: self.bbox,
        }
    }

    fn bbox(&self) -> [[f64; 2]; 2] {
        [
            self.bbox[0].min(self.bbox[1]).to_a(),
            self.bbox[0].max(self.bbox[1]).to_a(),
        ]
    }

    fn shape_type(&self) -> ShapeType {
        ShapeType::Ellipse
    }

    fn intersects_circle(&self, center: Point, radius: f64) -> bool {
        let min = self.bbox[0].min(self.bbox[1]);
        let max = self.bbox[0].max(self.bbox[1]);

        let dimensions = max - min;
        let ellipse_center = dimensions/2.0 + min;
        let bigside = dimensions.x.max(dimensions.y);
        let smallside = dimensions.x.min(dimensions.y);

        let d = (bigside.powi(2) - smallside.powi(2)).sqrt() / 2.0;
        let (center1, center2) = if dimensions.x > dimensions.y {
            (ellipse_center + Point::new(d, 0.0), ellipse_center + Point::new(-d, 0.0))
        } else {
            (ellipse_center + Point::new(0.0, d), ellipse_center + Point::new(0.0, -d))
        };

        let ellipse_sum_of_distances = bigside;
        let sum_of_distances = center1.distance(center) + center2.distance(center);

        sum_of_distances <= ellipse_sum_of_distances + 2.0*radius &&
        sum_of_distances >= ellipse_sum_of_distances - 2.0*radius
    }

    fn color(&self) -> Color {
        self.color
    }
}

#[cfg(test)]
mod tests {
    use super::Ellipse;
    use crate::point::Point;
    use crate::shape::ShapeTrait;

    #[test]
    fn test_intersects_circle() {
        let ellipse = Ellipse::from_corners([Point::new(0.0, 0.0), Point::new(150.0, 100.0)]);

        let cases = [
            ((126.0, 6.0), true),
            ((5.0, 18.0), true),
            ((9.0, 72.0), true),
            ((25.0, 40.0), false),
            ((134.0, 84.0), false),
        ];

        for (coords, output) in cases.iter().map(|t| *t) {
            assert!(ellipse.intersects_circle(Point::from(coords) + Point::new(8.0, 8.0), 8.0) == output);
        }
    }
}