Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion embedded-graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@
#![doc(
html_logo_url = "https://raw.githubusercontent.com/jamwaffles/embedded-graphics/191fe7f8a0fedc713f9722b9dc59208dacadee7e/assets/logo.svg?sanitize=true"
)]
#![no_std]
// #![no_std]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(missing_copy_implementations)]
Expand Down
72 changes: 65 additions & 7 deletions embedded-graphics/src/primitives/arc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ mod points;
mod styled;

use crate::{
geometry::{Angle, Dimensions, Point, Size},
primitives::{Circle, Primitive, Rectangle},
geometry::{Angle, Dimensions, Point, Real, Trigonometry},
primitives::{Circle, OffsetOutline, Primitive, Rectangle},
transform::Transform,
};
pub(in crate::primitives) use linear_equation::LinearEquation;
Expand Down Expand Up @@ -111,7 +111,31 @@ impl Arc {

/// Return the center point of the arc.
pub fn center(&self) -> Point {
self.bounding_box().center()
self.to_circle().center()
}

/// Return the end angle of the arc
fn angle_end(&self) -> Angle {
self.angle_start + self.angle_sweep
}

/// Return a point on the arc from a given angle
pub(in crate::primitives) fn point_from_angle(&self, angle: Angle) -> Point {
let center = self.center();
let radius = Real::from(self.diameter.saturating_sub(1)) / 2.into();

Point::new(
center.x + i32::from(angle.cos() * radius),
center.y - i32::from(angle.sin() * radius),
)
}
}

impl OffsetOutline for Arc {
fn offset(&self, offset: i32) -> Self {
let circle = self.to_circle().offset(offset);

Self::from_circle(circle, self.angle_start, self.angle_sweep)
}
}

Expand All @@ -124,8 +148,42 @@ impl Primitive for Arc {
}

impl Dimensions for Arc {
// https://stackoverflow.com/questions/1336663/2d-bounding-box-of-a-sector
fn bounding_box(&self) -> Rectangle {
Rectangle::new(self.top_left, Size::new(self.diameter, self.diameter))
let quadrants = [
self.point_from_angle(Angle::from_degrees(0.0)),
self.point_from_angle(Angle::from_degrees(90.0)),
self.point_from_angle(Angle::from_degrees(180.0)),
self.point_from_angle(Angle::from_degrees(270.0)),
self.point_from_angle(Angle::from_degrees(360.0)),
];

let start = self.point_from_angle(self.angle_start);
let end = self.point_from_angle(self.angle_end());
let center = self.center();

let plane_sector = PlaneSector::new(center, self.angle_start, self.angle_sweep);

let (min, mut max) = quadrants
.iter()
.filter(|quadrant| plane_sector.contains(**quadrant))
.chain([&start, &end].iter().cloned())
.fold(
(start.component_min(end), start.component_max(end)),
|acc, point| (acc.0.component_min(*point), acc.1.component_max(*point)),
);

if min != max {
if max.x > center.x {
max.x += 1;
}

if max.y > center.y {
max.y += 1;
}
}

Rectangle::with_corners(min, max)
}
}

Expand Down Expand Up @@ -168,15 +226,15 @@ impl Transform for Arc {
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::AngleUnit;
use crate::geometry::{AngleUnit, Size};

#[test]
fn negative_dimensions() {
let arc = Arc::new(Point::new(-15, -15), 10, 0.0.deg(), 90.0.deg());

assert_eq!(
arc.bounding_box(),
Rectangle::new(Point::new(-15, -15), Size::new(10, 10))
Rectangle::new(Point::new(-11, -15), Size::new(6, 5))
);
}

Expand All @@ -186,7 +244,7 @@ mod tests {

assert_eq!(
arc.bounding_box(),
Rectangle::new(Point::new(5, 15), Size::new(10, 10))
Rectangle::new(Point::new(9, 15), Size::new(6, 5))
);
}

Expand Down
3 changes: 3 additions & 0 deletions embedded-graphics/src/primitives/arc/plane_sector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ mod tests {

let mut iter =
PlaneSectorIterator::new(&arc, arc.center(), arc.angle_start, arc.angle_sweep);

assert_eq!(iter.next(), Some(Point::new(1, 0)));
assert_eq!(iter.next(), Some(Point::new(2, 0)));
assert_eq!(iter.next(), Some(Point::new(3, 0)));
assert_eq!(iter.next(), Some(Point::new(1, 1)));
assert_eq!(iter.next(), Some(Point::new(2, 1)));
assert_eq!(iter.next(), Some(Point::new(3, 1)));
assert_eq!(iter.next(), None);
}

Expand Down
35 changes: 29 additions & 6 deletions embedded-graphics/src/primitives/arc/styled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,39 @@ where
}
}

// FIXME: Remove and just use `Option::zip` when we ugprade to at least Rust 1.46.0
fn zip<T>(a: Option<T>, b: Option<T>) -> Option<(T, T)> {
match (a, b) {
(Some(a), Some(b)) => Some((a, b)),
_ => None,
}
}

impl<C> Dimensions for Styled<Arc, PrimitiveStyle<C>>
where
C: PixelColor,
{
// FIXME: This doesn't take into account start/end angles. This should be fixed to close #405.
fn bounding_box(&self) -> Rectangle {
dbg!(self.style.effective_stroke_color().is_some());
if self.style.effective_stroke_color().is_some() {
let offset = self.style.outside_stroke_width().saturating_cast();

self.primitive.bounding_box().offset(offset)
let bb = self.primitive.offset(offset).bounding_box();

if self.style.stroke_width > 1 {
let inner_offset = self.style.inside_stroke_width().saturating_cast();

let inner_bb = self.primitive.offset(-inner_offset).bounding_box();

Rectangle::with_corners(
bb.top_left.component_min(inner_bb.top_left),
zip(bb.bottom_right(), inner_bb.bottom_right())
.map(|(bb_br, inner_bb_br)| bb_br.component_max(inner_bb_br))
.unwrap_or(bb.top_left),
)
} else {
bb
}
} else {
Rectangle::new(self.primitive.bounding_box().center(), Size::zero())
}
Expand Down Expand Up @@ -210,10 +233,10 @@ mod tests {
assert_eq!(center.bounding_box(), inside.bounding_box());
assert_eq!(outside.bounding_box(), inside.bounding_box());

// TODO: Uncomment when arc bounding box is fixed in #405
// let mut display = MockDisplay::new();
// center.draw(&mut display).unwrap();
// assert_eq!(display.affected_area(), center.bounding_box());
let mut display = MockDisplay::new();
display.set_allow_overdraw(true);
center.draw(&mut display).unwrap();
assert_eq!(display.affected_area(), center.bounding_box());
}

#[test]
Expand Down
51 changes: 46 additions & 5 deletions embedded-graphics/src/primitives/sector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ mod styled;
use crate::{
geometry::{Angle, Dimensions, Point, Real, Size, Trigonometry},
primitives::{
arc::PlaneSector, line::Line, Circle, ContainsPoint, OffsetOutline, Primitive, Rectangle,
arc::PlaneSector, line::Line, Arc, Circle, ContainsPoint, OffsetOutline, Primitive,
Rectangle,
},
transform::Transform,
};
Expand Down Expand Up @@ -120,7 +121,7 @@ impl Sector {

/// Return the center point of the sector
pub fn center(&self) -> Point {
self.bounding_box().center()
self.to_circle().center()
}

/// Return the end angle of the sector
Expand Down Expand Up @@ -170,7 +171,47 @@ impl ContainsPoint for Sector {

impl Dimensions for Sector {
fn bounding_box(&self) -> Rectangle {
Rectangle::new(self.top_left, Size::new_equal(self.diameter))
let arc = Arc::new(
self.top_left,
self.diameter,
self.angle_start,
self.angle_sweep,
);

let quadrants = [
arc.point_from_angle(Angle::from_degrees(0.0)),
arc.point_from_angle(Angle::from_degrees(90.0)),
arc.point_from_angle(Angle::from_degrees(180.0)),
arc.point_from_angle(Angle::from_degrees(270.0)),
arc.point_from_angle(Angle::from_degrees(360.0)),
];

let start = arc.point_from_angle(self.angle_start);
let end = arc.point_from_angle(self.angle_end());
let center = self.center();

let plane_sector = PlaneSector::new(center, self.angle_start, self.angle_sweep);

let (min, mut max) = quadrants
.iter()
.filter(|quadrant| plane_sector.contains(**quadrant))
.chain([&start, &end, &center].iter().cloned())
.fold(
(start.component_min(end), start.component_max(end)),
|acc, point| (acc.0.component_min(*point), acc.1.component_max(*point)),
);

if min != max {
if max.x > center.x {
max.x += 1;
}

if max.y > center.y {
max.y += 1;
}
}

Rectangle::with_corners(min, max)
}
}

Expand Down Expand Up @@ -221,7 +262,7 @@ mod tests {

assert_eq!(
sector.bounding_box(),
Rectangle::new(Point::new(-15, -15), Size::new(10, 10))
Rectangle::new(Point::new(-11, -15), Size::new(6, 5))
);
}

Expand All @@ -231,7 +272,7 @@ mod tests {

assert_eq!(
sector.bounding_box(),
Rectangle::new(Point::new(5, 15), Size::new(10, 10))
Rectangle::new(Point::new(9, 15), Size::new(6, 5))
);
}

Expand Down
34 changes: 28 additions & 6 deletions embedded-graphics/src/primitives/sector/styled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,38 @@ where
}
}

// FIXME: Remove and just use `Option::zip` when we ugprade to at least Rust 1.46.0
fn zip<T>(a: Option<T>, b: Option<T>) -> Option<(T, T)> {
match (a, b) {
(Some(a), Some(b)) => Some((a, b)),
_ => None,
}
}

impl<C> Dimensions for Styled<Sector, PrimitiveStyle<C>>
where
C: PixelColor,
{
// FIXME: This doesn't take into account start/end angles. This should be fixed to close #405.
fn bounding_box(&self) -> Rectangle {
if !self.style.is_transparent() {
let offset = self.style.outside_stroke_width().saturating_cast();

self.primitive.bounding_box().offset(offset)
let bb = self.primitive.offset(offset).bounding_box();

if self.style.stroke_width > 1 {
let inner_offset = self.style.inside_stroke_width().saturating_cast();

let inner_bb = self.primitive.offset(-inner_offset).bounding_box();

Rectangle::with_corners(
bb.top_left.component_min(inner_bb.top_left),
zip(bb.bottom_right(), inner_bb.bottom_right())
.map(|(bb_br, inner_bb_br)| bb_br.component_max(inner_bb_br))
.unwrap_or(bb.top_left),
)
} else {
bb
}
} else {
Rectangle::new(self.primitive.bounding_box().center(), Size::zero())
}
Expand Down Expand Up @@ -317,10 +339,10 @@ mod tests {
let empty = Sector::with_center(CENTER, SIZE - 4, 0.0.deg(), 90.0.deg())
.into_styled::<BinaryColor>(PrimitiveStyle::new());

// TODO: Uncomment when arc bounding box is fixed in #405
// let mut display = MockDisplay::new();
// center.draw(&mut display).unwrap();
// assert_eq!(display.affected_area(), center.bounding_box());
let mut display = MockDisplay::new();
display.set_allow_overdraw(true);
center.draw(&mut display).unwrap();
assert_eq!(display.affected_area(), center.bounding_box());

assert_eq!(empty.bounding_box(), Rectangle::new(CENTER, Size::zero()));

Expand Down
4 changes: 2 additions & 2 deletions embedded-graphics/src/style/primitive_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ where
///
/// The outside stroke width is determined by `stroke_width` and `stroke_alignment`.
/// If `stroke_color` is `None` the outside stroke width is always `0`.
pub(crate) fn outside_stroke_width(&self) -> u32 {
pub fn outside_stroke_width(&self) -> u32 {
if self.stroke_color.is_none() {
return 0;
}
Expand All @@ -91,7 +91,7 @@ where
///
/// The inside stroke width is determined by `stroke_width` and `stroke_alignment`.
/// If `stroke_color` is `None` the inside stroke width is always `0`.
pub(crate) fn inside_stroke_width(&self) -> u32 {
pub fn inside_stroke_width(&self) -> u32 {
if self.stroke_color.is_none() {
return 0;
}
Expand Down
Loading