Skip to main content

ferroui_base/media/
poly_bezier_segment.rs

1use crate::media::{PathSegment, PathSegmentImpl, Points};
2use crate::platform::IGeometryContext;
3use crate::{
4    ferro_class, ferro_impl_classes, ferro_property, instantiate, DirectProperty, FerroObjectImpl, FerroProperty,
5    Point, Ref,
6};
7use std::cell::RefCell;
8
9/// Represents one or more cubic Bezier curves: every three points define the
10/// two control points and the end point of one curve.
11#[repr(C)]
12pub struct PolyBezierSegment {
13    base: PathSegment,
14    points: RefCell<Option<Points>>,
15}
16
17ferro_class!(PolyBezierSegment: PathSegment);
18crate::ferro_class_info!(PolyBezierSegment { new: PolyBezierSegment::new });
19ferro_impl_classes!(PolyBezierSegment: FerroObjectImpl);
20
21impl PathSegmentImpl for PolyBezierSegment {
22    fn apply_to(this: &Self, ctx: &mut dyn IGeometryContext) {
23        let is_stroked = this.is_stroked();
24        if let Some(points) = this.points() {
25            let points = points.to_vec();
26            // A trailing group of fewer than three points is invalid and is
27            // ignored.
28            for curve in points.chunks_exact(3) {
29                ctx.cubic_bezier_to(curve[0], curve[1], curve[2], is_stroked);
30            }
31        }
32    }
33
34    fn to_string(this: &Self) -> String {
35        match this.points() {
36            Some(points) if !points.is_empty() => {
37                let points: Vec<String> = points.iter().map(|point| point.to_string()).collect();
38                format!("C {}", points.join(" "))
39            }
40            _ => String::new(),
41        }
42    }
43}
44
45crate::ferro_properties! { impl PolyBezierSegment {
46    ferro_property!(pub fn points_property() -> DirectProperty<PolyBezierSegment, Option<Points>> {
47        FerroProperty::register_direct::<PolyBezierSegment, _>(
48            "Points",
49            |o| o.points.borrow().clone(),
50            Some(|o, v| o.set_points(v)),
51            None,
52        )
53    });
54} }
55
56impl PolyBezierSegment {
57    /// Creates the class data.
58    pub fn construct() -> Self {
59        Self { base: PathSegment::construct(), points: RefCell::new(Some(Points::new())) }
60    }
61
62    /// Creates a segment with an empty points collection.
63    pub fn new() -> Ref<Self> {
64        instantiate(Self::construct())
65    }
66
67    /// Creates a segment with a copy of the given points.
68    pub fn with_points(points: impl IntoIterator<Item = Point>, is_stroked: bool) -> Ref<Self> {
69        let result = Self::new();
70        result.set_points(Some(Points::from_items(points)));
71        result.set_is_stroked(is_stroked);
72        result
73    }
74
75    /// The points that define the curves.
76    pub fn points(&self) -> Option<Points> {
77        self.points.borrow().clone()
78    }
79
80    pub fn set_points(&self, value: Option<Points>) {
81        self.set_and_raise(Self::points_property(), &self.points, value);
82    }
83}