use std::rc::Rc;
use svg::{self, node::element as svg_element};
use super::ShapeDefinition;
use crate::{
color::Color,
draw::{StrokeDefinition, TextDefinition, text_positioning::TextPositioningStrategy},
geometry::{Insets, Point, Size},
};
#[derive(Debug, Clone)]
pub struct OvalDefinition {
fill_color: Option<Color>,
stroke: Rc<StrokeDefinition>,
text: Rc<TextDefinition>,
}
impl OvalDefinition {
pub fn new() -> Self {
Self::default()
}
fn fill_color(&self) -> Option<Color> {
self.fill_color
}
}
impl Default for OvalDefinition {
fn default() -> Self {
Self {
fill_color: None,
stroke: Rc::new(StrokeDefinition::default_solid()),
text: Rc::new(TextDefinition::default()),
}
}
}
impl ShapeDefinition for OvalDefinition {
fn supports_content(&self) -> bool {
true
}
fn find_intersection(&self, a: Point, b: Point, a_size: Size) -> Point {
let half_width = a_size.width() / 2.0;
let half_height = a_size.height() / 2.0;
let dist = b.sub_point(a);
let length = dist.hypot(); if length < 0.001 {
return b;
}
let dx_norm = dist.x() / length;
let dy_norm = dist.y() / length;
let angle = dy_norm.atan2(dx_norm);
let cos_angle = angle.cos();
let sin_angle = angle.sin();
let radius =
(half_width * half_height) / (half_height * cos_angle).hypot(half_width * sin_angle);
Point::new(
dx_norm.mul_add(radius, a.x()), dy_norm.mul_add(radius, a.y()), )
}
fn calculate_inner_size(&self, content_size: Size, padding: Insets) -> Size {
let min_size = Size::new(10.0, 10.0);
let sqrt_2 = 2.0_f32.sqrt();
content_size
.scale(sqrt_2)
.add_padding(padding)
.max(min_size)
}
fn clone_box(&self) -> Box<dyn ShapeDefinition> {
Box::new(self.clone())
}
fn stroke(&self) -> &Rc<StrokeDefinition> {
&self.stroke
}
fn set_fill_color(&mut self, color: Option<Color>) -> Result<(), &'static str> {
self.fill_color = color;
Ok(())
}
fn text(&self) -> &Rc<TextDefinition> {
&self.text
}
fn set_text(&mut self, text: Rc<TextDefinition>) {
self.text = text;
}
fn set_stroke(&mut self, stroke: Rc<StrokeDefinition>) {
self.stroke = stroke;
}
fn text_positioning_strategy(&self) -> TextPositioningStrategy {
TextPositioningStrategy::InContent
}
fn render_to_svg(&self, size: Size, position: Point) -> Box<dyn svg::Node> {
let rx = size.width() / 2.0;
let ry = size.height() / 2.0;
let ellipse = svg_element::Ellipse::new()
.set("cx", position.x())
.set("cy", position.y())
.set("rx", rx)
.set("ry", ry)
.set("fill", "white");
let mut ellipse = crate::apply_stroke!(ellipse, &self.stroke);
if let Some(fill_color) = self.fill_color() {
ellipse = ellipse
.set("fill", fill_color.to_string())
.set("fill-opacity", fill_color.alpha());
}
ellipse.into()
}
}