rive_rs/
transform_component.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;
6
7use crate::bones::{Bone, RootBone};
8use crate::component_dirt::ComponentDirt;
9use crate::container_component::ContainerComponent;
10use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property};
11use crate::math::{self, Mat};
12use crate::node::Node;
13use crate::option_cell::OptionCell;
14use crate::{Component, StatusCode};
15
16#[derive(Debug)]
17pub struct TransformComponent {
18    container_component: ContainerComponent,
19    rotation: Property<f32>,
20    scale_x: Property<f32>,
21    scale_y: Property<f32>,
22    opacity: Property<f32>,
23    transform: Cell<Mat>,
24    world_transform: Cell<Mat>,
25    render_opacity: Cell<f32>,
26    parent_transform_component: OptionCell<Object<Self>>,
27}
28
29impl ObjectRef<'_, TransformComponent> {
30    pub fn rotation(&self) -> f32 {
31        self.rotation.get()
32    }
33
34    pub fn set_rotation(&self, rotation: f32) {
35        if self.rotation() == rotation {
36            return;
37        }
38
39        self.rotation.set(rotation);
40        self.mark_transform_dirty();
41    }
42
43    pub fn scale_x(&self) -> f32 {
44        self.scale_x.get()
45    }
46
47    pub fn set_scale_x(&self, scale_x: f32) {
48        if self.scale_x() == scale_x {
49            return;
50        }
51
52        self.scale_x.set(scale_x);
53        self.mark_transform_dirty();
54    }
55
56    pub fn scale_y(&self) -> f32 {
57        self.scale_y.get()
58    }
59
60    pub fn set_scale_y(&self, scale_y: f32) {
61        if self.scale_y() == scale_y {
62            return;
63        }
64
65        self.scale_y.set(scale_y);
66        self.mark_transform_dirty();
67    }
68
69    pub fn opacity(&self) -> f32 {
70        self.opacity.get()
71    }
72
73    pub fn set_opacity(&self, opacity: f32) {
74        if self.opacity() == opacity {
75            return;
76        }
77
78        self.opacity.set(opacity);
79        self.cast::<Component>().add_dirt(ComponentDirt::RENDER_OPACITY, true);
80    }
81}
82
83impl ObjectRef<'_, TransformComponent> {
84    pub fn render_opacity(&self) -> f32 {
85        self.render_opacity.get()
86    }
87
88    pub fn world_transform(&self) -> Mat {
89        self.world_transform.get()
90    }
91
92    pub fn mark_transform_dirty(&self) {
93        if !self.cast::<Component>().add_dirt(ComponentDirt::TRANSFORM, false) {
94            return;
95        }
96
97        self.mark_world_transform_dirty();
98    }
99
100    pub fn mark_world_transform_dirty(&self) {
101        self.cast::<Component>().add_dirt(ComponentDirt::WORLD_TRANSFORM, true);
102    }
103
104    fn x(&self) -> f32 {
105        if let Some(root_bone) = self.try_cast::<RootBone>() {
106            return root_bone.x();
107        }
108
109        if let Some(bone) = self.try_cast::<Bone>() {
110            return bone.x();
111        }
112
113        if let Some(node) = self.try_cast::<Node>() {
114            return node.x();
115        }
116
117        unreachable!()
118    }
119
120    fn y(&self) -> f32 {
121        if let Some(root_bone) = self.try_cast::<RootBone>() {
122            return root_bone.y();
123        }
124
125        if let Some(bone) = self.try_cast::<Bone>() {
126            return bone.y();
127        }
128
129        if let Some(node) = self.try_cast::<Node>() {
130            return node.y();
131        }
132
133        unreachable!()
134    }
135
136    pub fn update_transform(&self) {
137        let mut transform = if self.rotation() != 0.0 {
138            Mat::from_rotation(self.rotation())
139        } else {
140            Mat::default()
141        };
142
143        transform.translate_x = self.x();
144        transform.translate_y = self.y();
145
146        transform = transform.scale(math::Vec::new(self.scale_x(), self.scale_y()));
147
148        self.transform.set(transform);
149    }
150
151    pub fn update_world_transform(&self) {
152        if let Some(parent_transform_component) = self.parent_transform_component.get() {
153            let parent_transform = parent_transform_component.as_ref().world_transform.get();
154            self.world_transform.set(parent_transform * self.transform.get());
155        } else {
156            self.world_transform.set(self.transform.get());
157        }
158    }
159
160    pub fn build_dependencies(&self) {
161        let component = self.cast::<Component>();
162        if let Some(parent) = component.parent() {
163            parent.cast::<Component>().as_ref().push_dependent(component.as_object());
164        }
165    }
166
167    pub fn update(&self, value: ComponentDirt) {
168        if Component::value_has_dirt(value, ComponentDirt::TRANSFORM) {
169            self.update_transform();
170        }
171
172        if Component::value_has_dirt(value, ComponentDirt::WORLD_TRANSFORM) {
173            self.update_world_transform();
174        }
175
176        if Component::value_has_dirt(value, ComponentDirt::RENDER_OPACITY) {
177            self.render_opacity.set(self.opacity());
178
179            if let Some(parent_transform_component) = self.parent_transform_component.get() {
180                self.render_opacity.set(
181                    self.render_opacity() * parent_transform_component.as_ref().render_opacity(),
182                );
183            }
184        }
185    }
186}
187
188impl Core for TransformComponent {
189    parent_types![(container_component, ContainerComponent)];
190
191    properties![
192        (15, rotation, set_rotation),
193        (16, scale_x, set_scale_x),
194        (17, scale_y, set_scale_y),
195        (18, opacity, set_opacity),
196        container_component,
197    ];
198}
199
200impl OnAdded for ObjectRef<'_, TransformComponent> {
201    on_added!([on_added_dirty, import], ContainerComponent);
202
203    fn on_added_clean(&self, _context: &dyn CoreContext) -> StatusCode {
204        let parent = self.cast::<Component>().parent().and_then(|parent| parent.try_cast());
205        self.parent_transform_component.set(parent);
206
207        StatusCode::Ok
208    }
209}
210
211impl Default for TransformComponent {
212    fn default() -> Self {
213        Self {
214            container_component: ContainerComponent::default(),
215            rotation: Property::new(0.0),
216            scale_x: Property::new(1.0),
217            scale_y: Property::new(1.0),
218            opacity: Property::new(1.0),
219            transform: Cell::new(Mat::default()),
220            world_transform: Cell::new(Mat::default()),
221            render_opacity: Cell::new(0.0),
222            parent_transform_component: OptionCell::new(),
223        }
224    }
225}