rive_rs/shapes/
path_composer.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 crate::component::Component;
6use crate::component_dirt::ComponentDirt;
7use crate::core::{Core, Object, ObjectRef, OnAdded};
8use crate::option_cell::OptionCell;
9use crate::shapes::command_path::{CommandPath, CommandPathBuilder};
10use crate::shapes::{PathSpace, Shape};
11use crate::TransformComponent;
12
13#[derive(Debug, Default)]
14pub struct PathComposer {
15    component: Component,
16    pub(crate) shape: OptionCell<Object<Shape>>,
17    local_path: OptionCell<CommandPath>,
18    world_path: OptionCell<CommandPath>,
19}
20
21impl ObjectRef<'_, PathComposer> {
22    pub(crate) fn with_local_path(&self, f: impl FnMut(Option<&CommandPath>)) {
23        self.local_path.with(f);
24    }
25
26    pub(crate) fn with_world_path(&self, f: impl FnMut(Option<&CommandPath>)) {
27        self.world_path.with(f);
28    }
29
30    pub fn build_dependencies(&self) {
31        let shape = self.shape.get().expect("shape should already be set in Path");
32        let shape = shape.as_ref();
33
34        shape.cast::<Component>().push_dependent(self.as_object().cast());
35
36        for path in shape.paths() {
37            path.cast::<Component>().as_ref().push_dependent(self.as_object().cast());
38        }
39    }
40
41    pub fn update(&self, value: ComponentDirt) {
42        if !Component::value_has_dirt(value, ComponentDirt::PATH) {
43            return;
44        }
45
46        let shape = self.shape.get().expect("shape should already be set in Path");
47        let shape = shape.as_ref();
48
49        let space = shape.path_space();
50
51        if space & PathSpace::LOCAL == PathSpace::LOCAL {
52            let mut builder = CommandPathBuilder::new();
53
54            let world = shape.cast::<TransformComponent>().world_transform();
55            let inverse_world = world.invert().unwrap_or_default();
56
57            for path in shape.paths() {
58                let path = path.as_ref();
59
60                let local_transform = inverse_world * path.transform();
61                path.with_command_path(|command_path| {
62                    builder.path(
63                        command_path.expect("command_path should already be set"),
64                        Some(local_transform),
65                    );
66                });
67            }
68
69            self.local_path.set(Some(builder.build()));
70        }
71
72        if space & PathSpace::WORLD == PathSpace::WORLD {
73            let mut builder = CommandPathBuilder::new();
74
75            for path in shape.paths() {
76                let path = path.as_ref();
77
78                let transform = path.transform();
79                path.with_command_path(|command_path| {
80                    builder.path(
81                        command_path.expect("command_path should already be set"),
82                        Some(transform),
83                    );
84                });
85            }
86
87            self.world_path.set(Some(builder.build()));
88        }
89    }
90}
91
92impl Core for PathComposer {
93    parent_types![(component, Component)];
94
95    properties!(component);
96}
97
98impl OnAdded for ObjectRef<'_, PathComposer> {
99    on_added!(Component);
100}