use std::{fmt, ops};
use zng_var::{animation::Transitionable, impl_from_and_into_var};
use super::{Factor2d, LayoutMask, Length, Point, Px, PxPoint, PxRect};
#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
pub struct Line {
pub start: Point,
pub end: Point,
}
impl fmt::Debug for Line {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("Line").field("start", &self.start).field("end", &self.end).finish()
} else {
write!(f, "{:?}.to{:?}", self.start, self.end)
}
}
}
impl fmt::Display for Line {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(p) = f.precision() {
write!(f, "{:.p$} to {:.p$}", self.start, self.end, p = p)
} else {
write!(f, "{} to {}", self.start, self.end)
}
}
}
impl Line {
pub fn new<S: Into<Point>, E: Into<Point>>(start: S, end: E) -> Self {
Line {
start: start.into(),
end: end.into(),
}
}
pub fn zero() -> Line {
Line {
start: Point::zero(),
end: Point::zero(),
}
}
pub fn to_top() -> Line {
Line {
start: Point::bottom(),
end: Point::top(),
}
}
pub fn to_bottom() -> Line {
Line {
start: Point::top(),
end: Point::bottom(),
}
}
pub fn to_right() -> Line {
Line {
start: Point::left(),
end: Point::right(),
}
}
pub fn to_left() -> Line {
Line {
start: Point::right(),
end: Point::left(),
}
}
pub fn to_top_left() -> Line {
Line {
start: Point::bottom_right(),
end: Point::top_left(),
}
}
pub fn to_top_right() -> Line {
Line {
start: Point::bottom_left(),
end: Point::top_right(),
}
}
pub fn to_bottom_left() -> Line {
Line {
start: Point::top_right(),
end: Point::bottom_left(),
}
}
pub fn to_bottom_right() -> Line {
Line {
start: Point::top_left(),
end: Point::bottom_right(),
}
}
}
impl super::Layout2d for Line {
type Px = PxLine;
fn layout_dft(&self, default: Self::Px) -> Self::Px {
PxLine {
start: self.start.layout_dft(default.start),
end: self.end.layout_dft(default.end),
}
}
fn affect_mask(&self) -> LayoutMask {
self.start.affect_mask() | self.end.affect_mask()
}
}
impl_from_and_into_var! {
fn from(line: PxLine) -> Line {
Line::new(line.start, line.end)
}
}
#[derive(Clone, Default, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct PxLine {
pub start: PxPoint,
pub end: PxPoint,
}
impl PxLine {
pub fn new(start: PxPoint, end: PxPoint) -> Self {
Self { start, end }
}
pub fn zero() -> Self {
Self::new(PxPoint::zero(), PxPoint::zero())
}
pub fn length(self) -> Px {
let s = self.start.cast::<f32>();
let e = self.end.cast::<f32>();
Px(s.distance_to(e).round() as i32)
}
pub fn bounds(self) -> PxRect {
PxRect::from_points([self.start, self.end])
}
pub fn normalize(self) -> PxLine {
let start = self.start.min(self.end);
let end = self.start.max(self.end);
PxLine { start, end }
}
}
pub trait LineFromTuplesBuilder {
fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line;
}
impl<X1: Into<Length>, Y1: Into<Length>> LineFromTuplesBuilder for (X1, Y1) {
fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line {
Line::new(self, (x2, y2))
}
}
impl<S: Into<Factor2d>> ops::Mul<S> for Line {
type Output = Self;
fn mul(mut self, rhs: S) -> Self {
self *= rhs;
self
}
}
impl<S: Into<Factor2d>> ops::Mul<S> for &Line {
type Output = Line;
fn mul(self, rhs: S) -> Self::Output {
self.clone() * rhs
}
}
impl<S: Into<Factor2d>> ops::MulAssign<S> for Line {
fn mul_assign(&mut self, rhs: S) {
let rhs = rhs.into();
self.start *= rhs;
self.end *= rhs;
}
}
impl<S: Into<Factor2d>> ops::Div<S> for Line {
type Output = Self;
fn div(mut self, rhs: S) -> Self {
self /= rhs;
self
}
}
impl<S: Into<Factor2d>> ops::Div<S> for &Line {
type Output = Line;
fn div(self, rhs: S) -> Self::Output {
self.clone() / rhs
}
}
impl<S: Into<Factor2d>> ops::DivAssign<S> for Line {
fn div_assign(&mut self, rhs: S) {
let rhs = rhs.into();
self.start /= rhs;
self.end /= rhs;
}
}
impl ops::Add for Line {
type Output = Self;
fn add(mut self, rhs: Self) -> Self {
self += rhs;
self
}
}
impl ops::AddAssign for Line {
fn add_assign(&mut self, rhs: Self) {
self.start += rhs.start;
self.end += rhs.end;
}
}
impl ops::Sub for Line {
type Output = Self;
fn sub(mut self, rhs: Self) -> Self {
self -= rhs;
self
}
}
impl ops::SubAssign for Line {
fn sub_assign(&mut self, rhs: Self) {
self.start -= rhs.start;
self.end -= rhs.end;
}
}