rive_rs/shapes/
shape.rs
1use std::cell::Cell;
6use std::rc::Rc;
7
8use crate::component::Component;
9use crate::component_dirt::ComponentDirt;
10use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded};
11use crate::drawable::Drawable;
12use crate::dyn_vec::DynVec;
13use crate::math::Mat;
14use crate::shapes::{Path, PathComposer, PathSpace, ShapePaintContainer};
15use crate::status_code::StatusCode;
16use crate::transform_component::TransformComponent;
17use crate::Renderer;
18
19#[derive(Debug, Default)]
20pub struct Shape {
21 drawable: Drawable,
22 shape_paint_container: ShapePaintContainer,
23 path_composer: Rc<PathComposer>,
24 paths: DynVec<Object<Path>>,
25 want_difference_path: Cell<bool>,
26}
27
28impl ObjectRef<'_, Shape> {
29 pub fn paths(&self) -> impl Iterator<Item = Object<Path>> + '_ {
30 self.paths.iter()
31 }
32
33 pub fn want_difference_path(&self) -> bool {
34 self.want_difference_path.get()
35 }
36
37 pub fn push_path(&self, path: Object<Path>) {
38 self.paths.push(path);
39 }
40
41 pub fn path_space(&self) -> PathSpace {
42 self.cast::<ShapePaintContainer>().path_space()
43 }
44
45 pub fn path_changed(&self) {
46 self.path_composer().cast::<Component>().add_dirt(ComponentDirt::PATH, true);
47
48 self.cast::<ShapePaintContainer>().invalidate_stroke_effects();
49 }
50
51 pub fn path_composer(&self) -> ObjectRef<'_, PathComposer> {
52 ObjectRef::from(Rc::clone(&self.path_composer))
53 }
54
55 pub fn draw(&self, renderer: &mut impl Renderer, transform: Mat) {
56 let mut is_clipped = false;
57 if let Some(path) = self.cast::<Drawable>().clip() {
58 is_clipped = true;
59
60 let layers = self
61 .cast::<ShapePaintContainer>()
62 .shape_paints()
63 .filter(|shape_paint| shape_paint.as_ref().is_visible())
64 .count();
65
66 renderer.clip(&path, transform, layers);
67 }
68
69 let path_composer = self.path_composer();
70
71 for shape_paint in self.cast::<ShapePaintContainer>().shape_paints() {
72 let shape_paint = shape_paint.as_ref();
73
74 if !shape_paint.is_visible() {
75 continue;
76 }
77
78 shape_paint.set_is_clipped(is_clipped);
79
80 if shape_paint.path_space() & PathSpace::LOCAL == PathSpace::LOCAL {
81 let transform = transform * self.cast::<TransformComponent>().world_transform();
82 path_composer.with_local_path(|path| {
83 shape_paint.draw(
84 renderer,
85 path.expect("local_path should already be set on PathComposer"),
86 transform,
87 );
88 });
89 } else {
90 path_composer.with_world_path(|path| {
91 shape_paint.draw(
92 renderer,
93 path.expect("world_path should already be set on PathComposer"),
94 transform,
95 );
96 });
97 }
98 }
99 }
100
101 pub fn build_dependencies(&self) {
102 self.path_composer().cast::<Component>().build_dependencies();
103
104 self.cast::<TransformComponent>().build_dependencies();
105
106 for paint in self.cast::<ShapePaintContainer>().shape_paints() {
110 paint.as_ref().set_blend_mode(self.cast::<Drawable>().blend_mode());
111 }
112 }
113
114 pub fn update(&self, value: ComponentDirt) {
115 self.cast::<TransformComponent>().update(value);
116
117 if Component::value_has_dirt(value, ComponentDirt::RENDER_OPACITY) {
118 for paint in self.cast::<ShapePaintContainer>().shape_paints() {
119 paint
120 .as_ref()
121 .set_render_opacity(self.cast::<TransformComponent>().render_opacity());
122 }
123 }
124 }
125}
126
127impl Core for Shape {
128 parent_types![(drawable, Drawable), (shape_paint_container, ShapePaintContainer)];
129
130 properties!(drawable);
131}
132
133impl OnAdded for ObjectRef<'_, Shape> {
134 on_added!([on_added_clean, import], Drawable);
135
136 fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode {
137 self.path_composer.shape.set(Some(self.as_object()));
138
139 self.cast::<TransformComponent>().on_added_dirty(context);
140 ObjectRef::from(Rc::clone(&self.path_composer)).on_added_dirty(context)
141 }
142}