diff options
Diffstat (limited to 'packages/svgbob/src/buffer/fragment_buffer/fragment/rect.rs')
-rw-r--r-- | packages/svgbob/src/buffer/fragment_buffer/fragment/rect.rs | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/packages/svgbob/src/buffer/fragment_buffer/fragment/rect.rs b/packages/svgbob/src/buffer/fragment_buffer/fragment/rect.rs new file mode 100644 index 0000000..2942991 --- /dev/null +++ b/packages/svgbob/src/buffer/fragment_buffer/fragment/rect.rs @@ -0,0 +1,198 @@ +use crate::{fragment::Bounds, util, Cell, Point}; +use parry2d::shape::ConvexPolygon; +use parry2d::shape::{Polyline, Segment, Shape}; +use sauron::{ + html::attributes::*, + svg::{attributes::*, *}, + Node, +}; +use std::cmp::Ordering; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct Rect { + pub start: Point, + pub end: Point, + pub is_filled: bool, + pub radius: Option<f32>, + //TODO:Make this as enum + pub is_broken: bool, +} + +impl Rect { + /// creates a new rect and reorder the points swapping the end points if necessary + /// such that the start is the most top-left and end point is the most bottom-right + pub(in crate) fn new( + start: Point, + end: Point, + is_filled: bool, + is_broken: bool, + ) -> Self { + let mut rect = Rect { + start, + end, + is_filled, + radius: None, + is_broken, + }; + rect.sort_reorder_end_points(); + rect + } + + pub(in crate) fn rounded_new( + start: Point, + end: Point, + is_filled: bool, + radius: f32, + is_broken: bool, + ) -> Self { + let mut rect = Rect { + start, + end, + is_filled, + radius: Some(radius), + is_broken, + }; + rect.sort_reorder_end_points(); + rect + } + + /// reorder the end points swap end points such that + /// start < end + pub(in crate) fn sort_reorder_end_points(&mut self) { + if self.start > self.end { + let tmp_start = self.start; + self.start = self.end; + self.end = tmp_start; + } + } + + /// recompute the rect with start and end point offset by the cell + /// location + pub(in crate) fn absolute_position(&self, cell: Cell) -> Self { + Rect { + start: cell.absolute_position(self.start), + end: cell.absolute_position(self.end), + ..*self + } + } + + pub(in crate) fn scale(&self, scale: f32) -> Self { + Rect { + start: self.start.scale(scale), + end: self.end.scale(scale), + radius: self.radius.map(|r| r * scale), + ..*self + } + } + + pub(crate) fn width(&self) -> f32 { + self.end.x - self.start.x + } + + pub(crate) fn height(&self) -> f32 { + self.end.y - self.start.y + } + + pub(crate) fn is_broken(&self) -> bool { + self.is_broken + } + + pub fn is_rounded(&self) -> bool { + if let Some(ref r) = &self.radius { + *r > 0.0 + } else { + false + } + } +} + +impl Bounds for Rect { + fn bounds(&self) -> (Point, Point) { + let aabb = Segment::new(*self.start, *self.end).local_aabb(); + (Point::from(*aabb.mins), Point::from(*aabb.maxs)) + } +} + +impl fmt::Display for Rect { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "R {} {}", self.start, self.end) + } +} + +impl Into<Polyline> for Rect { + fn into(self) -> Polyline { + Polyline::new( + vec![ + *self.start, + *Point::new(self.end.x, self.start.y), + *self.end, + *Point::new(self.start.x, self.end.y), + *self.start, + ], + None, + ) + } +} + +impl Into<ConvexPolygon> for Rect { + fn into(self) -> ConvexPolygon { + ConvexPolygon::from_convex_polyline(vec![ + *self.start, + *Point::new(self.end.x, self.start.y), + *self.end, + *Point::new(self.start.x, self.end.y), + ]) + .expect("must create a convex polygon") + } +} + +impl<MSG> Into<Node<MSG>> for Rect { + fn into(self) -> Node<MSG> { + rect( + [ + x(self.start.x), + y(self.start.y), + width(self.width()), + height(self.height()), + classes_flag([ + ("broken", self.is_broken), + ("solid", !self.is_broken), + ("filled", self.is_filled), + ("nofill", !self.is_filled), + ]), + if let Some(radius) = self.radius { + rx(radius) + } else { + rx(0) + }, + ], + [], + ) + } +} + +impl Eq for Rect {} + +impl Ord for Rect { + fn cmp(&self, other: &Self) -> Ordering { + self.start + .cmp(&other.start) + .then(self.end.cmp(&other.end)) + .then(self.is_filled.cmp(&other.is_filled)) + .then(util::opt_ord(self.radius, other.radius)) + .then(self.is_broken.cmp(&other.is_broken)) + } +} + +impl PartialOrd for Rect { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialEq for Rect { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} |