Skip to main content

ferroui_controls/shapes/
sector.rs

1use super::{Shape, ShapeImpl};
2use crate::ControlImpl;
3use ferroui_base::input::InputElementImpl;
4use ferroui_base::interactivity::InteractiveImpl;
5use ferroui_base::layout::LayoutableImpl;
6use ferroui_base::media::{EllipseGeometry, Geometry, StreamGeometry, SweepDirection};
7use ferroui_base::platform::IGeometryContext;
8use ferroui_base::utilities::MathUtilities;
9use ferroui_base::{
10    ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroProperty,
11    Point, Rect, Ref, Size, StyledElementImpl, StyledProperty, Visual, VisualImpl,
12};
13
14/// Represents a circular or elliptical sector (a pie-shaped closed region of
15/// a circle or ellipse).
16#[repr(C)]
17pub struct Sector {
18    base: Shape,
19}
20
21ferro_class!(Sector: Shape);
22ferroui_base::ferro_class_info!(Sector { new: Sector::new });
23ferro_impl_classes!(
24    Sector: StyledElementImpl,
25    VisualImpl,
26    LayoutableImpl,
27    InteractiveImpl,
28    InputElementImpl,
29    ControlImpl
30);
31
32ferroui_base::ferro_impl_classes!(Sector: FerroObjectImpl);
33
34impl ShapeImpl for Sector {
35    fn create_defining_geometry(this: &Self) -> Option<Ref<Geometry>> {
36        let rect = Rect::from_size(this.bounds().size());
37        let deflated_rect = rect.deflate(this.stroke_thickness() * 0.5);
38        let sweep = this.sweep_angle();
39
40        if sweep >= 360.0 || sweep <= -360.0 {
41            return Some(EllipseGeometry::with_rect(deflated_rect).upcast());
42        }
43
44        if sweep == 0.0 {
45            return Some(StreamGeometry::new().upcast());
46        }
47
48        let (start_angle, end_angle) = MathUtilities::get_min_max_from_delta(
49            MathUtilities::deg2rad(this.start_angle()),
50            MathUtilities::deg2rad(sweep),
51        );
52
53        let centre = Point::new(rect.width * 0.5, rect.height * 0.5);
54        let radius_x = deflated_rect.width * 0.5;
55        let radius_y = deflated_rect.height * 0.5;
56        let start_curve_point = MathUtilities::get_ellipse_point(centre, radius_x, radius_y, start_angle);
57        let end_curve_point = MathUtilities::get_ellipse_point(centre, radius_x, radius_y, end_angle);
58        let size = Size::new(radius_x, radius_y);
59
60        let stream_geometry = StreamGeometry::new();
61        {
62            let mut context = stream_geometry.open();
63            context.begin_figure(start_curve_point, true);
64            context.arc_to(end_curve_point, size, 0.0, sweep.abs() > 180.0, SweepDirection::Clockwise, true);
65            context.line_to(centre, true);
66            context.end_figure(true);
67            context.dispose();
68        }
69
70        Some(stream_geometry.upcast())
71    }
72}
73
74ferroui_base::ferro_properties! { impl Sector {
75    ferro_property!(
76        /// Defines the `StartAngle` property.
77        pub fn start_angle_property() -> StyledProperty<f64> {
78            FerroProperty::register::<Sector, _>("StartAngle", 0.0)
79        }
80    );
81
82    ferro_property!(
83        /// Defines the `SweepAngle` property.
84        pub fn sweep_angle_property() -> StyledProperty<f64> {
85            FerroProperty::register::<Sector, _>("SweepAngle", 0.0)
86        }
87    );
88} }
89
90impl Sector {
91    fn static_constructor() {
92        Shape::stroke_thickness_property().override_default_value::<Sector>(1.0);
93        Shape::affects_geometry::<Sector>(&[
94            Visual::bounds_property().as_property(),
95            Shape::stroke_thickness_property().as_property(),
96            Self::start_angle_property().as_property(),
97            Self::sweep_angle_property().as_property(),
98        ]);
99    }
100
101    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
102    pub fn construct() -> Self {
103        Self { base: Shape::construct() }
104    }
105
106    pub fn new() -> Ref<Self> {
107        instantiate(Self::construct())
108    }
109
110    /// Gets the angle at which the sector's arc starts, in degrees.
111    pub fn start_angle(&self) -> f64 {
112        self.get_value(Self::start_angle_property())
113    }
114
115    /// Sets the angle at which the sector's arc starts, in degrees.
116    pub fn set_start_angle(&self, value: f64) {
117        self.set_value(Self::start_angle_property(), value)
118    }
119
120    /// Gets the angle, in degrees, added to the `StartAngle` defining where
121    /// the sector's arc ends. A positive value is clockwise, negative is
122    /// counter-clockwise.
123    pub fn sweep_angle(&self) -> f64 {
124        self.get_value(Self::sweep_angle_property())
125    }
126
127    /// Sets the angle, in degrees, added to the `StartAngle` defining where
128    /// the sector's arc ends. A positive value is clockwise, negative is
129    /// counter-clockwise.
130    pub fn set_sweep_angle(&self, value: f64) {
131        self.set_value(Self::sweep_angle_property(), value)
132    }
133}