Skip to main content

ferroui_base/platform/
path_geometry_context.rs

1use crate::media::{
2    ArcSegment, BezierSegment, FillRule, LineSegment, PathFigure, PathFigures, PathGeometry, PathSegments,
3    QuadraticBezierSegment, SweepDirection,
4};
5use crate::platform::IGeometryContext;
6use crate::{Point, Ref, Size};
7
8/// A geometry context that records the drawing commands as the figures and
9/// segments of a [`PathGeometry`].
10pub struct PathGeometryContext {
11    current_figure: Option<Ref<PathFigure>>,
12    path_geometry: Option<Ref<PathGeometry>>,
13}
14
15impl PathGeometryContext {
16    pub fn new(path_geometry: Ref<PathGeometry>) -> Self {
17        Self { current_figure: None, path_geometry: Some(path_geometry) }
18    }
19
20    fn path_geometry(&self) -> &Ref<PathGeometry> {
21        self.path_geometry.as_ref().expect("PathGeometryContext is disposed")
22    }
23
24    fn current_figure_segments(&self) -> PathSegments {
25        let _ = self.path_geometry();
26        let Some(figure) = &self.current_figure else {
27            panic!("No figure in progress.");
28        };
29        figure.segments().expect("Current figure's segments cannot be null.")
30    }
31}
32
33impl IGeometryContext for PathGeometryContext {
34    fn arc_to(
35        &mut self,
36        point: Point,
37        size: Size,
38        rotation_angle: f64,
39        is_large_arc: bool,
40        sweep_direction: SweepDirection,
41        is_stroked: bool,
42    ) {
43        let arc_segment = ArcSegment::new();
44        arc_segment.set_size(size);
45        arc_segment.set_rotation_angle(rotation_angle);
46        arc_segment.set_is_large_arc(is_large_arc);
47        arc_segment.set_sweep_direction(sweep_direction);
48        arc_segment.set_point(point);
49        arc_segment.set_is_stroked(is_stroked);
50        self.current_figure_segments().add(arc_segment.upcast());
51    }
52
53    fn begin_figure(&mut self, start_point: Point, is_filled: bool) {
54        let path_geometry = self.path_geometry().clone();
55
56        let figure = PathFigure::new();
57        figure.set_start_point(start_point);
58        figure.set_is_closed(false);
59        figure.set_is_filled(is_filled);
60
61        let figures = match path_geometry.figures() {
62            Some(figures) => figures,
63            None => {
64                let figures = PathFigures::new();
65                path_geometry.set_figures(Some(figures.clone()));
66                figures
67            }
68        };
69        figures.add(figure.clone());
70        self.current_figure = Some(figure);
71    }
72
73    fn cubic_bezier_to(&mut self, control_point1: Point, control_point2: Point, end_point: Point, is_stroked: bool) {
74        let bezier_segment = BezierSegment::new();
75        bezier_segment.set_point1(control_point1);
76        bezier_segment.set_point2(control_point2);
77        bezier_segment.set_point3(end_point);
78        bezier_segment.set_is_stroked(is_stroked);
79        self.current_figure_segments().add(bezier_segment.upcast());
80    }
81
82    fn quadratic_bezier_to(&mut self, control_point: Point, end_point: Point, is_stroked: bool) {
83        let quadratic_bezier_segment = QuadraticBezierSegment::new();
84        quadratic_bezier_segment.set_point1(control_point);
85        quadratic_bezier_segment.set_point2(end_point);
86        quadratic_bezier_segment.set_is_stroked(is_stroked);
87        self.current_figure_segments().add(quadratic_bezier_segment.upcast());
88    }
89
90    fn line_to(&mut self, point: Point, is_stroked: bool) {
91        let line_segment = LineSegment::new();
92        line_segment.set_point(point);
93        line_segment.set_is_stroked(is_stroked);
94        self.current_figure_segments().add(line_segment.upcast());
95    }
96
97    fn end_figure(&mut self, is_closed: bool) {
98        if let Some(figure) = &self.current_figure {
99            figure.set_is_closed(is_closed);
100        }
101
102        self.current_figure = None;
103    }
104
105    fn set_fill_rule(&mut self, fill_rule: FillRule) {
106        self.path_geometry().set_fill_rule(fill_rule);
107    }
108
109    fn dispose(&mut self) {
110        self.path_geometry = None;
111    }
112}