ferroui_vello/
geometry_group_impl.rs1use crate::geometry_impl::{
2 impl_geometry_impl, register, try_get_geometry_impl, FillPath, GeometryImpl, GeometryImplBase, VelloPath,
3};
4use ferroui_base::media::FillRule;
5use ferroui_base::platform::IGeometryImpl;
6use ferroui_base::Rect;
7use kurbo::BezPath;
8use std::sync::Arc;
9
10pub struct GeometryGroupImpl {
12 base: GeometryImplBase,
13 bounds: Rect,
14 stroke_path: VelloPath,
15 fill: FillPath,
16}
17
18impl GeometryGroupImpl {
19 pub fn new(fill_rule: FillRule, children: &[Arc<dyn IGeometryImpl>]) -> Arc<Self> {
22 let children: Vec<&dyn GeometryImpl> =
23 children.iter().filter_map(|child| try_get_geometry_impl(&**child)).collect();
24
25 let mut stroke = BezPath::new();
26 let mut requires_fill_pass = false;
27
28 for geo in &children {
29 if let Some(stroke_path) = geo.stroke_path() {
30 stroke.extend(stroke_path.path().iter());
31 }
32
33 if !geo.is_fill_same_as_stroke() {
34 requires_fill_pass = true;
35 }
36 }
37
38 let stroke_path = VelloPath::new(stroke, fill_rule);
39
40 let fill = if requires_fill_pass {
41 let mut fill = BezPath::new();
42
43 for geo in &children {
44 if let Some(fill_path) = geo.fill_path() {
45 fill.extend(fill_path.path().iter());
46 }
47 }
48
49 FillPath::Separate(VelloPath::new(fill, fill_rule))
50 } else {
51 FillPath::SameAsStroke
52 };
53
54 let bounds = stroke_path.tight_bounds();
55
56 register(Self { base: GeometryImplBase::new(), bounds, stroke_path, fill })
57 }
58
59 fn geometry_bounds(&self) -> Rect {
60 self.bounds
61 }
62}
63
64impl GeometryImpl for GeometryGroupImpl {
65 fn base(&self) -> &GeometryImplBase {
66 &self.base
67 }
68
69 fn stroke_path(&self) -> Option<VelloPath> {
70 Some(self.stroke_path.clone())
71 }
72
73 fn fill(&self) -> FillPath {
74 self.fill.clone()
75 }
76}
77
78impl_geometry_impl!(GeometryGroupImpl);