1use super::Visual;
14use crate::numerics::{Matrix4x4, Quaternion, Vector3};
15use crate::rendering::composition::animations::{ICompositionAnimationBase, ImplicitAnimationCollection};
16use crate::rendering::composition::{AsCompositionObject, CompositionVisual, Compositor};
17use crate::Matrix;
18use bitflags::bitflags;
19use std::rc::{Rc, Weak};
20use std::time::Duration;
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct ScalarTransition {
30 duration: Duration,
31}
32
33impl ScalarTransition {
34 pub const DEFAULT_DURATION: Duration = Duration::from_millis(300);
36
37 pub const fn new() -> ScalarTransition {
39 ScalarTransition { duration: Self::DEFAULT_DURATION }
40 }
41
42 pub const fn with_duration(duration: Duration) -> ScalarTransition {
44 ScalarTransition { duration }
45 }
46
47 pub fn duration(&self) -> Duration {
49 self.duration
50 }
51
52 pub fn set_duration(&mut self, value: Duration) {
54 self.duration = value;
55 }
56}
57
58impl Default for ScalarTransition {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64bitflags! {
65 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
68 pub struct Vector3TransitionComponents: u32 {
69 const X = 1;
71 const Y = 2;
73 const Z = 4;
75 }
76}
77
78impl Default for Vector3TransitionComponents {
79 fn default() -> Self {
81 Self::all()
82 }
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub struct Vector3Transition {
91 duration: Duration,
92 components: Vector3TransitionComponents,
93}
94
95impl Vector3Transition {
96 pub const fn new() -> Vector3Transition {
98 Vector3Transition { duration: ScalarTransition::DEFAULT_DURATION, components: Vector3TransitionComponents::all() }
99 }
100
101 pub const fn with(duration: Duration, components: Vector3TransitionComponents) -> Vector3Transition {
103 Vector3Transition { duration, components }
104 }
105
106 pub fn duration(&self) -> Duration {
108 self.duration
109 }
110
111 pub fn set_duration(&mut self, value: Duration) {
113 self.duration = value;
114 }
115
116 pub fn components(&self) -> Vector3TransitionComponents {
119 self.components
120 }
121
122 pub fn set_components(&mut self, value: Vector3TransitionComponents) {
124 self.components = value;
125 }
126}
127
128impl Default for Vector3Transition {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134#[derive(Default)]
137pub(super) struct CompositionFacades {
138 rotation: Option<f32>,
139 rotation_axis: Option<Vector3>,
140 scale: Option<Vector3>,
141 center_point: Option<Vector3>,
142 transform_matrix: Option<Matrix4x4>,
143 transitions: FacadeTransitions,
144 transition_animations: Option<Rc<ImplicitAnimationCollection>>,
147 fresh_visual: Option<Weak<CompositionVisual>>,
150}
151
152#[derive(Clone, Copy, Default)]
153struct FacadeTransitions {
154 opacity: Option<ScalarTransition>,
155 rotation: Option<ScalarTransition>,
156 scale: Option<Vector3Transition>,
157 translation: Option<Vector3Transition>,
158}
159
160impl FacadeTransitions {
161 fn is_empty(&self) -> bool {
162 self.opacity.is_none() && self.rotation.is_none() && self.scale.is_none() && self.translation.is_none()
163 }
164}
165
166impl Visual {
167 pub fn rotation(&self) -> f32 {
175 self.read_composition_extras(|extras| extras.facades.rotation).flatten().unwrap_or(0.0)
176 }
177
178 pub fn set_rotation(&self, value: f32) {
180 self.with_composition_extras(|extras| extras.facades.rotation = Some(value));
181 self.invalidate_visual();
182 }
183
184 pub fn rotation_axis(&self) -> Vector3 {
191 self.read_composition_extras(|extras| extras.facades.rotation_axis).flatten().unwrap_or(Vector3::UNIT_Z)
192 }
193
194 pub fn set_rotation_axis(&self, value: Vector3) {
196 self.with_composition_extras(|extras| extras.facades.rotation_axis = Some(value));
197 self.invalidate_visual();
198 }
199
200 pub fn scale(&self) -> Vector3 {
203 self.read_composition_extras(|extras| extras.facades.scale).flatten().unwrap_or(Vector3::ONE)
204 }
205
206 pub fn set_scale(&self, value: Vector3) {
208 self.with_composition_extras(|extras| extras.facades.scale = Some(value));
209 self.invalidate_visual();
210 }
211
212 pub fn center_point(&self) -> Vector3 {
216 self.read_composition_extras(|extras| extras.facades.center_point).flatten().unwrap_or(Vector3::ZERO)
217 }
218
219 pub fn set_center_point(&self, value: Vector3) {
221 self.with_composition_extras(|extras| extras.facades.center_point = Some(value));
222 self.invalidate_visual();
223 }
224
225 pub fn transform_matrix(&self) -> Matrix4x4 {
237 self.read_composition_extras(|extras| extras.facades.transform_matrix).flatten().unwrap_or(Matrix4x4::IDENTITY)
238 }
239
240 pub fn set_transform_matrix(&self, value: Matrix4x4) {
242 self.with_composition_extras(|extras| extras.facades.transform_matrix = Some(value));
243 self.invalidate_visual();
244 }
245
246 pub fn opacity_transition(&self) -> Option<ScalarTransition> {
249 self.read_composition_extras(|extras| extras.facades.transitions.opacity).flatten()
250 }
251
252 pub fn set_opacity_transition(&self, value: Option<ScalarTransition>) {
254 self.with_composition_extras(|extras| extras.facades.transitions.opacity = value);
255 self.composition_transitions_changed();
256 }
257
258 pub fn rotation_transition(&self) -> Option<ScalarTransition> {
261 self.read_composition_extras(|extras| extras.facades.transitions.rotation).flatten()
262 }
263
264 pub fn set_rotation_transition(&self, value: Option<ScalarTransition>) {
266 self.with_composition_extras(|extras| extras.facades.transitions.rotation = value);
267 self.composition_transitions_changed();
268 }
269
270 pub fn scale_transition(&self) -> Option<Vector3Transition> {
273 self.read_composition_extras(|extras| extras.facades.transitions.scale).flatten()
274 }
275
276 pub fn set_scale_transition(&self, value: Option<Vector3Transition>) {
278 self.with_composition_extras(|extras| extras.facades.transitions.scale = value);
279 self.composition_transitions_changed();
280 }
281
282 pub fn translation_transition(&self) -> Option<Vector3Transition> {
285 self.read_composition_extras(|extras| extras.facades.transitions.translation).flatten()
286 }
287
288 pub fn set_translation_transition(&self, value: Option<Vector3Transition>) {
290 self.with_composition_extras(|extras| extras.facades.transitions.translation = value);
291 self.composition_transitions_changed();
292 }
293
294 pub(super) fn composition_facade_transform_matrix(&self) -> Option<Matrix> {
296 let matrix = self.read_composition_extras(|extras| extras.facades.transform_matrix).flatten()?;
297 Some(Matrix::new(
298 matrix.m11 as f64,
299 matrix.m12 as f64,
300 matrix.m21 as f64,
301 matrix.m22 as f64,
302 matrix.m41 as f64,
303 matrix.m42 as f64,
304 ))
305 }
306
307 pub(super) fn synchronize_composition_facades(&self, comp: &Rc<CompositionVisual>) {
310 let Some((rotation, rotation_axis, scale, center_point)) = self.read_composition_extras(|extras| {
311 let facades = &extras.facades;
312 (facades.rotation, facades.rotation_axis, facades.scale, facades.center_point)
313 }) else {
314 return;
315 };
316
317 if rotation.is_some() || rotation_axis.is_some() {
318 let (angle, orientation) =
319 rotation_to_composition(rotation.unwrap_or(0.0), rotation_axis.unwrap_or(Vector3::UNIT_Z));
320 comp.set_rotation_angle(angle);
321 comp.set_orientation(orientation);
322 }
323 if let Some(scale) = scale {
324 comp.set_scale(scale.into());
325 }
326 if let Some(center_point) = center_point {
327 comp.set_center_point(center_point.into());
328 }
329 }
330
331 pub(super) fn composition_visual_created(&self, comp: &Rc<CompositionVisual>) {
335 if self.read_composition_extras(|_| ()).is_some() {
336 self.with_composition_extras(|extras| extras.facades.fresh_visual = Some(Rc::downgrade(comp)));
337 }
338 }
339
340 pub(super) fn apply_composition_transitions(&self, comp: &Rc<CompositionVisual>, after_writes: bool) {
350 let Some(fresh) = self.read_composition_extras(|extras| {
351 extras.facades.fresh_visual.as_ref().is_some_and(|visual| std::ptr::eq(visual.as_ptr(), Rc::as_ptr(comp)))
352 }) else {
353 return;
354 };
355 if fresh {
356 if !after_writes {
357 return;
358 }
359 self.with_composition_extras(|extras| extras.facades.fresh_visual = None);
360 }
361
362 if let Some(animations) = self.composition_transition_animations(comp.compositor()) {
363 if !comp.implicit_animations().is_some_and(|current| Rc::ptr_eq(¤t, &animations)) {
364 comp.set_implicit_animations(Some(animations));
365 }
366 }
367 }
368
369 fn composition_transition_animations(&self, compositor: &Rc<Compositor>) -> Option<Rc<ImplicitAnimationCollection>> {
372 let (transitions, cached) = self.read_composition_extras(|extras| {
373 (extras.facades.transitions, extras.facades.transition_animations.clone())
374 })?;
375 if transitions.is_empty() {
376 return None;
377 }
378 if let Some(cached) = cached {
379 if Rc::ptr_eq(cached.as_composition_object().compositor(), compositor) {
380 return Some(cached);
381 }
382 }
383 let built = build_transition_animations(compositor, &transitions);
384 self.with_composition_extras(|extras| extras.facades.transition_animations = Some(built.clone()));
385 Some(built)
386 }
387
388 fn composition_transitions_changed(&self) {
392 let old = self.with_composition_extras(|extras| extras.facades.transition_animations.take());
393 if let (Some(old), Some(comp)) = (old, self.composition_visual()) {
394 if comp.implicit_animations().is_some_and(|current| Rc::ptr_eq(¤t, &old)) {
395 comp.set_implicit_animations(None);
396 }
397 }
398 self.invalidate_visual();
399 }
400}
401
402fn rotation_to_composition(degrees: f32, axis: Vector3) -> (f32, Quaternion) {
406 let radians = degrees.to_radians();
407 if axis.x == 0.0 && axis.y == 0.0 {
408 let angle = if axis.z < 0.0 { -radians } else { radians };
409 (angle, Quaternion::IDENTITY)
410 } else {
411 (0.0, Quaternion::create_from_axis_angle(Vector3::normalize(axis), radians))
412 }
413}
414
415fn key_frame_duration(duration: Duration) -> Duration {
417 duration.clamp(Duration::from_millis(1), Duration::from_secs(24 * 60 * 60))
418}
419
420fn build_transition_animations(compositor: &Rc<Compositor>, transitions: &FacadeTransitions) -> Rc<ImplicitAnimationCollection> {
424 let collection = compositor.create_implicit_animation_collection();
425
426 if let Some(transition) = transitions.opacity {
427 collection.set("Opacity", scalar_animation(compositor, "Opacity", transition.duration()));
428 }
429 if let Some(transition) = transitions.rotation {
430 collection.set("RotationAngle", scalar_animation(compositor, "RotationAngle", transition.duration()));
431 let orientation = compositor.create_quaternion_key_frame_animation();
432 orientation.set_target(Some("Orientation".to_owned()));
433 orientation.insert_expression_key_frame(1.0, "this.FinalValue", None);
434 orientation.set_duration(key_frame_duration(transition.duration()));
435 let orientation: Rc<dyn ICompositionAnimationBase> = orientation;
436 collection.set("Orientation", orientation);
437 }
438 if let Some(transition) = transitions.scale {
439 if let Some(animation) = vector3_animation(compositor, "Scale", transition) {
440 collection.set("Scale", animation);
441 }
442 }
443 if let Some(transition) = transitions.translation {
444 if let Some(animation) = vector3_animation(compositor, "Translation", transition) {
445 collection.set("Translation", animation);
446 }
447 }
448 collection
449}
450
451fn scalar_animation(compositor: &Rc<Compositor>, target: &str, duration: Duration) -> Rc<dyn ICompositionAnimationBase> {
452 let animation = compositor.create_scalar_key_frame_animation();
453 animation.set_target(Some(target.to_owned()));
454 animation.insert_expression_key_frame(1.0, "this.FinalValue", None);
455 animation.set_duration(key_frame_duration(duration));
456 animation as Rc<dyn ICompositionAnimationBase>
457}
458
459fn vector3_animation(compositor: &Rc<Compositor>, target: &str, transition: Vector3Transition) -> Option<Rc<dyn ICompositionAnimationBase>> {
467 let components = transition.components();
468 if components.is_empty() {
469 return None;
470 }
471 let animation = compositor.create_vector3d_key_frame_animation();
472 animation.set_target(Some(target.to_owned()));
473 if components != Vector3TransitionComponents::all() {
474 animation.insert_expression_key_frame(0.0, &partial_start_expression(components), None);
475 }
476 animation.insert_expression_key_frame(1.0, "this.FinalValue", None);
477 animation.set_duration(key_frame_duration(transition.duration()));
478 let animation: Rc<dyn ICompositionAnimationBase> = animation;
479 Some(animation)
480}
481
482fn partial_start_expression(components: Vector3TransitionComponents) -> String {
485 let component = |flag: Vector3TransitionComponents, name: &str| {
486 if components.contains(flag) {
487 format!("this.StartingValue.{name}")
488 } else {
489 format!("this.FinalValue.{name}")
490 }
491 };
492 format!(
493 "Vector3({}, {}, {})",
494 component(Vector3TransitionComponents::X, "X"),
495 component(Vector3TransitionComponents::Y, "Y"),
496 component(Vector3TransitionComponents::Z, "Z"),
497 )
498}
499
500#[cfg(test)]
501pub(super) fn partial_start_expression_for_tests(components: Vector3TransitionComponents) -> String {
502 partial_start_expression(components)
503}