rive_rs/shapes/
shape.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // Set the blend mode on all the shape paints. If we ever animate this
107        // property, we'll need to update it in the update cycle/mark dirty when the
108        // blend mode changes.
109        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}