rive_rs/
transform_component.rs
1use 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}