summaryrefslogtreecommitdiffstats
path: root/packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs
diff options
context:
space:
mode:
Diffstat (limited to 'packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs')
-rw-r--r--packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs b/packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs
new file mode 100644
index 0000000..b49c38c
--- /dev/null
+++ b/packages/svgbob/src/buffer/fragment_buffer/fragment/circle.rs
@@ -0,0 +1,161 @@
+use crate::{fragment::Bounds, util, Cell, Point};
+use nalgebra::Point2;
+use parry2d::shape::ConvexPolygon;
+use parry2d::shape::Polyline;
+use std::{cmp::Ordering, fmt};
+
+use sauron::{
+ html::attributes::*,
+ svg::{attributes::*, *},
+ Node,
+};
+
+#[derive(Debug, Clone)]
+pub struct Circle {
+ pub radius: f32,
+ pub center: Point,
+ pub is_filled: bool,
+}
+
+impl Circle {
+ pub(in crate) fn new(center: Point, radius: f32, is_filled: bool) -> Self {
+ Circle {
+ center,
+ radius,
+ is_filled,
+ }
+ }
+
+ /// the top most point of this circle for sorting.
+ /// center.y - radius
+ fn top_left_bound(&self) -> Point {
+ Point::new(self.center.x - self.radius, self.center.y - self.radius)
+ }
+ fn top_right_bound(&self) -> Point {
+ Point::new(self.center.x + self.radius, self.center.y - self.radius)
+ }
+
+ fn bottom_right_bound(&self) -> Point {
+ Point::new(self.center.x + self.radius, self.center.y + self.radius)
+ }
+
+ fn bottom_left_bound(&self) -> Point {
+ Point::new(self.center.x - self.radius, self.center.y + self.radius)
+ }
+
+ /// offset the circles parameter from the arg cell
+ pub(in crate) fn absolute_position(&self, cell: Cell) -> Self {
+ Circle {
+ center: cell.absolute_position(self.center),
+ ..*self
+ }
+ }
+
+ pub fn scale(&self, scale: f32) -> Self {
+ Circle {
+ center: self.center.scale(scale),
+ radius: self.radius * scale,
+ ..*self
+ }
+ }
+}
+
+impl Bounds for Circle {
+ fn bounds(&self) -> (Point, Point) {
+ (self.top_left_bound(), self.bottom_right_bound())
+ }
+}
+
+impl fmt::Display for Circle {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "C {} {}", self.center, self.radius)
+ }
+}
+
+impl<MSG> Into<Node<MSG>> for Circle {
+ fn into(self) -> Node<MSG> {
+ circle(
+ [
+ cx(self.center.x),
+ cy(self.center.y),
+ r(self.radius),
+ classes_flag([
+ ("filled", self.is_filled),
+ ("nofill", !self.is_filled),
+ ]),
+ ],
+ [],
+ )
+ }
+}
+
+impl Eq for Circle {}
+
+///This is needed since circle contains radius which is an f32 which rust doesn't provide trait
+///implementation for Eq
+impl Ord for Circle {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.mins()
+ .cmp(&other.mins())
+ .then(self.maxs().cmp(&other.maxs()))
+ .then(util::ord(self.radius, other.radius))
+ .then(self.is_filled.cmp(&other.is_filled))
+ }
+}
+
+impl PartialOrd for Circle {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl PartialEq for Circle {
+ fn eq(&self, other: &Self) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+
+impl Into<Polyline> for Circle {
+ fn into(self) -> Polyline {
+ let points: Vec<Point2<f32>> = extract_circle_points(self.radius, 64)
+ .into_iter()
+ .map(|p| Point2::new(p.x + self.center.x, p.y + self.center.y))
+ .collect();
+
+ Polyline::new(points, None)
+ }
+}
+
+impl Into<ConvexPolygon> for Circle {
+ fn into(self) -> ConvexPolygon {
+ let points: Vec<Point2<f32>> = extract_circle_points(self.radius, 64)
+ .into_iter()
+ .map(|p| Point2::new(p.x + self.center.x, p.y + self.center.y))
+ .collect();
+
+ ConvexPolygon::from_convex_polyline(points)
+ .expect("must create a convex polygon")
+ }
+}
+
+fn extract_circle_points(radius: f32, nsubdivs: u32) -> Vec<Point> {
+ let two_pi = std::f32::consts::TAU;
+ let dtheta = two_pi / nsubdivs as f32;
+ push_xy_arc(radius, nsubdivs, dtheta)
+}
+
+/// Pushes a discretized counterclockwise circle to a buffer.
+/// The circle is contained on the plane spanned by the `x` and `y` axis.
+fn push_xy_arc(radius: f32, nsubdiv: u32, dtheta: f32) -> Vec<Point> {
+ let mut out: Vec<Point> = vec![];
+ let mut curr_theta: f32 = 0.0;
+
+ for _ in 0..nsubdiv {
+ let x = curr_theta.cos() * radius;
+ let y = curr_theta.sin() * radius;
+ out.push(Point::new(x, y));
+
+ curr_theta = curr_theta + dtheta;
+ }
+ out
+}