use crate::{
geometry::{Angle, Dimensions, Point, Size},
primitives::{
common::PlaneSector, Circle, ContainsPoint, OffsetOutline, PointsIter, Primitive, Rectangle,
},
transform::Transform,
};
mod points;
mod styled;
pub use points::Points;
pub use styled::StyledPixelsIterator;
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct Sector {
pub top_left: Point,
pub diameter: u32,
pub angle_start: Angle,
pub angle_sweep: Angle,
}
impl Sector {
pub const fn new(
top_left: Point,
diameter: u32,
angle_start: Angle,
angle_sweep: Angle,
) -> Self {
Sector {
top_left,
diameter,
angle_start,
angle_sweep,
}
}
pub const fn with_center(
center: Point,
diameter: u32,
angle_start: Angle,
angle_sweep: Angle,
) -> Self {
let top_left = Rectangle::with_center(center, Size::new_equal(diameter)).top_left;
Sector {
top_left,
diameter,
angle_start,
angle_sweep,
}
}
pub const fn from_circle(circle: Circle, angle_start: Angle, angle_sweep: Angle) -> Self {
Sector {
top_left: circle.top_left,
diameter: circle.diameter,
angle_start,
angle_sweep,
}
}
pub const fn to_circle(&self) -> Circle {
Circle::new(self.top_left, self.diameter)
}
pub fn center(&self) -> Point {
self.bounding_box().center()
}
fn center_2x(&self) -> Point {
let radius = self.diameter.saturating_sub(1);
self.top_left * 2 + Size::new(radius, radius)
}
}
impl OffsetOutline for Sector {
fn offset(&self, offset: i32) -> Self {
let circle = self.to_circle().offset(offset);
Self::from_circle(circle, self.angle_start, self.angle_sweep)
}
}
impl Primitive for Sector {}
impl PointsIter for Sector {
type Iter = Points;
fn points(&self) -> Self::Iter {
Points::new(self)
}
}
impl ContainsPoint for Sector {
fn contains(&self, point: Point) -> bool {
if self.to_circle().contains(point) {
let delta = point * 2 - self.center_2x();
PlaneSector::new(self.angle_start, self.angle_sweep).contains(delta)
} else {
false
}
}
}
impl Dimensions for Sector {
fn bounding_box(&self) -> Rectangle {
Rectangle::new(self.top_left, Size::new_equal(self.diameter))
}
}
impl Transform for Sector {
fn translate(&self, by: Point) -> Self {
Self {
top_left: self.top_left + by,
..*self
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.top_left += by;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::AngleUnit;
#[test]
fn negative_dimensions() {
let sector = Sector::new(Point::new(-15, -15), 10, 0.0.deg(), 90.0.deg());
assert_eq!(
sector.bounding_box(),
Rectangle::new(Point::new(-15, -15), Size::new(10, 10))
);
}
#[test]
fn dimensions() {
let sector = Sector::new(Point::new(5, 15), 10, 0.0.deg(), 90.0.deg());
assert_eq!(
sector.bounding_box(),
Rectangle::new(Point::new(5, 15), Size::new(10, 10))
);
}
#[test]
fn it_handles_negative_coordinates() {
let positive = Sector::new(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg()).points();
let negative = Sector::new(Point::new(-10, -10), 5, 0.0.deg(), 90.0.deg()).points();
assert!(negative.eq(positive.map(|p| p - Point::new(20, 20))));
}
#[test]
fn center_is_correct() {
let sector = Sector::new(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg());
assert_eq!(sector.center(), Point::new(12, 12));
let sector = Sector::new(Point::new(10, 10), 6, 0.0.deg(), 90.0.deg());
assert_eq!(sector.center(), Point::new(12, 12));
let sector = Sector::with_center(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg());
assert_eq!(sector.center(), Point::new(10, 10));
let sector = Sector::with_center(Point::new(10, 10), 6, 0.0.deg(), 90.0.deg());
assert_eq!(sector.center(), Point::new(10, 10));
}
#[test]
fn contains() {
let sector = Sector::new(Point::zero(), 10, 0.0.deg(), 90.0.deg());
let contained_points = Rectangle::new(Point::new(-10, -10), Size::new(30, 30))
.points()
.filter(|p| sector.contains(*p));
assert!(contained_points.eq(sector.points()));
}
#[test]
fn offset() {
let center = Point::new(5, 7);
let sector = Sector::with_center(center, 3, 0.0.deg(), 90.0.deg());
assert_eq!(sector.offset(0), sector);
assert_eq!(
sector.offset(1),
Sector::with_center(center, 5, 0.0.deg(), 90.0.deg())
);
assert_eq!(
sector.offset(2),
Sector::with_center(center, 7, 0.0.deg(), 90.0.deg())
);
assert_eq!(
sector.offset(-1),
Sector::with_center(center, 1, 0.0.deg(), 90.0.deg())
);
assert_eq!(
sector.offset(-2),
Sector::with_center(center, 0, 0.0.deg(), 90.0.deg())
);
assert_eq!(
sector.offset(-3),
Sector::with_center(center, 0, 0.0.deg(), 90.0.deg())
);
}
}