use euclid::default::Point2D;
use euclid::{Angle, Translation2D, Trig, Vector2D};
use num_traits::{Float, FromPrimitive};
use crate::core::_c;
#[derive(Clone, Debug, PartialEq)]
pub struct Line<F: Float + Trig> {
pub start_point: Point2D<F>,
pub end_point: Point2D<F>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierQuadratic<F: Float + Trig> {
pub start: Point2D<F>,
pub cp: Point2D<F>,
pub end: Point2D<F>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BezierCubic<F: Float + Trig> {
pub start: Point2D<F>,
pub cp1: Point2D<F>,
pub cp2: Point2D<F>,
pub end: Point2D<F>,
}
impl<F: Float + Trig> Line<F> {
pub fn from(points: &[Point2D<F>]) -> Self {
Line { start_point: points[0], end_point: points[1] }
}
pub fn as_points(&self) -> Vec<Point2D<F>> {
return vec![self.start_point, self.end_point];
}
pub fn length(&self) -> F {
(self.end_point - self.start_point).length()
}
pub fn rotate(&mut self, center: &Point2D<F>, degrees: F) {
let angle = Angle::radians(degrees.to_radians());
let translation = Translation2D::new(-center.x, -center.y);
let transformation = translation
.to_transform()
.then_rotate(angle)
.then_translate(Vector2D::new(center.x, center.y));
self.start_point = transformation.transform_point(self.start_point);
self.end_point = transformation.transform_point(self.end_point);
}
}
pub fn rotate_points<F: Float + Trig>(
points: &[Point2D<F>],
center: &Point2D<F>,
degrees: F,
) -> Vec<Point2D<F>> {
let angle = Angle::radians(degrees.to_radians());
let translation = Translation2D::new(-center.x, -center.y);
let transformation = translation
.to_transform()
.then_rotate(angle)
.then_translate(Vector2D::new(center.x, center.y));
return points
.iter()
.map(|&p| transformation.transform_point(p))
.collect::<Vec<Point2D<F>>>();
}
pub fn rotate_lines<F: Float + Trig>(
lines: &[Line<F>],
center: &Point2D<F>,
degrees: F,
) -> Vec<Line<F>> {
lines
.iter()
.cloned()
.map(|mut l| {
l.rotate(center, degrees);
l
})
.collect::<Vec<Line<F>>>()
}
pub fn convert_bezier_quadratic_to_cubic<F: Float + FromPrimitive + Trig>(
bezier_quadratic: BezierQuadratic<F>,
) -> BezierCubic<F> {
let cubic_x1 = bezier_quadratic.start.x
+ _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.x - bezier_quadratic.start.x);
let cubic_y1 = bezier_quadratic.start.y
+ _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.y - bezier_quadratic.start.y);
let cubic_x2 = bezier_quadratic.end.x
+ _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.x - bezier_quadratic.end.x);
let cubic_y2 = bezier_quadratic.end.y
+ _c::<F>(2.0 / 3.0) * (bezier_quadratic.cp.y - bezier_quadratic.end.y);
BezierCubic {
start: bezier_quadratic.start,
cp1: Point2D::new(cubic_x1, cubic_y1),
cp2: Point2D::new(cubic_x2, cubic_y2),
end: bezier_quadratic.end,
}
}
#[cfg(test)]
mod tests {
use euclid::default::Point2D;
#[test]
fn line_length() {
let l = super::Line::from(&[Point2D::new(1.0, 1.0), Point2D::new(2.0, 2.0)]);
assert_eq!(l.length(), f32::sqrt(2.0));
}
}