Thanks to visit codestin.com
Credit goes to docs.rs

bevy_transform/components/
transform.rs

1use super::GlobalTransform;
2use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3};
3use core::ops::Mul;
4
5#[cfg(feature = "bevy-support")]
6use bevy_ecs::component::Component;
7
8#[cfg(feature = "bevy_reflect")]
9use {bevy_ecs::reflect::ReflectComponent, bevy_reflect::prelude::*};
10
11/// Checks that a vector with the given squared length is normalized.
12///
13/// Warns for small error with a length threshold of approximately `1e-4`,
14/// and panics for large error with a length threshold of approximately `1e-2`.
15#[cfg(debug_assertions)]
16fn assert_is_normalized(message: &str, length_squared: f32) {
17    use bevy_math::ops;
18    #[cfg(feature = "std")]
19    use std::eprintln;
20
21    let length_error_squared = ops::abs(length_squared - 1.0);
22
23    // Panic for large error and warn for slight error.
24    if length_error_squared > 2e-2 || length_error_squared.is_nan() {
25        // Length error is approximately 1e-2 or more.
26        panic!("Error: {message}",);
27    } else if length_error_squared > 2e-4 {
28        // Length error is approximately 1e-4 or more.
29        #[cfg(feature = "std")]
30        #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
31        {
32            eprintln!("Warning: {message}",);
33        }
34    }
35}
36
37/// Describe the position of an entity. If the entity has a parent, the position is relative
38/// to its parent position.
39///
40/// * To place or move an entity, you should set its [`Transform`].
41/// * To get the global transform of an entity, you should get its [`GlobalTransform`].
42/// * To be displayed, an entity must have both a [`Transform`] and a [`GlobalTransform`].
43///   [`GlobalTransform`] is automatically inserted whenever [`Transform`] is inserted.
44///
45/// ## [`Transform`] and [`GlobalTransform`]
46///
47/// [`Transform`] is the position of an entity relative to its parent position, or the reference
48/// frame if it doesn't have a [`ChildOf`](bevy_ecs::hierarchy::ChildOf) component.
49///
50/// [`GlobalTransform`] is the position of an entity relative to the reference frame.
51///
52/// [`GlobalTransform`] is updated from [`Transform`] in the [`TransformSystems::Propagate`]
53/// system set.
54///
55/// This system runs during [`PostUpdate`](bevy_app::PostUpdate). If you
56/// update the [`Transform`] of an entity during this set or after, you will notice a 1 frame lag
57/// before the [`GlobalTransform`] is updated.
58///
59/// [`TransformSystems::Propagate`]: crate::TransformSystems::Propagate
60///
61/// # Examples
62///
63/// - [`transform`][transform_example]
64///
65/// [transform_example]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/transform.rs
66#[derive(Debug, PartialEq, Clone, Copy)]
67#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
68#[cfg_attr(
69    feature = "bevy-support",
70    derive(Component),
71    require(GlobalTransform, TransformTreeChanged)
72)]
73#[cfg_attr(
74    feature = "bevy_reflect",
75    derive(Reflect),
76    reflect(Component, Default, PartialEq, Debug, Clone)
77)]
78#[cfg_attr(
79    all(feature = "bevy_reflect", feature = "serialize"),
80    reflect(Serialize, Deserialize)
81)]
82pub struct Transform {
83    /// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
84    ///
85    /// See the [`translations`] example for usage.
86    ///
87    /// [`translations`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/translation.rs
88    pub translation: Vec3,
89    /// Rotation of the entity.
90    ///
91    /// See the [`3d_rotation`] example for usage.
92    ///
93    /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
94    pub rotation: Quat,
95    /// Scale of the entity.
96    ///
97    /// See the [`scale`] example for usage.
98    ///
99    /// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
100    pub scale: Vec3,
101}
102
103impl Transform {
104    /// An identity [`Transform`] with no translation, rotation, and a scale of 1 on all axes.
105    pub const IDENTITY: Self = Transform {
106        translation: Vec3::ZERO,
107        rotation: Quat::IDENTITY,
108        scale: Vec3::ONE,
109    };
110
111    /// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
112    /// is used for z-ordering elements: higher `z`-value will be in front of lower
113    /// `z`-value.
114    #[inline]
115    pub const fn from_xyz(x: f32, y: f32, z: f32) -> Self {
116        Self::from_translation(Vec3::new(x, y, z))
117    }
118
119    /// Extracts the translation, rotation, and scale from `matrix`. It must be a 3d affine
120    /// transformation matrix.
121    #[inline]
122    pub fn from_matrix(world_from_local: Mat4) -> Self {
123        let (scale, rotation, translation) = world_from_local.to_scale_rotation_translation();
124
125        Transform {
126            translation,
127            rotation,
128            scale,
129        }
130    }
131
132    /// Creates a new [`Transform`], with `translation`. Rotation will be 0 and scale 1 on
133    /// all axes.
134    #[inline]
135    pub const fn from_translation(translation: Vec3) -> Self {
136        Transform {
137            translation,
138            ..Self::IDENTITY
139        }
140    }
141
142    /// Creates a new [`Transform`], with `rotation`. Translation will be 0 and scale 1 on
143    /// all axes.
144    #[inline]
145    pub const fn from_rotation(rotation: Quat) -> Self {
146        Transform {
147            rotation,
148            ..Self::IDENTITY
149        }
150    }
151
152    /// Creates a new [`Transform`], with `scale`. Translation will be 0 and rotation 0 on
153    /// all axes.
154    #[inline]
155    pub const fn from_scale(scale: Vec3) -> Self {
156        Transform {
157            scale,
158            ..Self::IDENTITY
159        }
160    }
161
162    /// Creates a new [`Transform`] that is equivalent to the given [isometry].
163    ///
164    /// [isometry]: Isometry3d
165    #[inline]
166    pub fn from_isometry(iso: Isometry3d) -> Self {
167        Transform {
168            translation: iso.translation.into(),
169            rotation: iso.rotation,
170            ..Self::IDENTITY
171        }
172    }
173
174    /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
175    /// points towards the `target` position and [`Transform::up`] points towards `up`.
176    ///
177    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
178    /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
179    /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
180    /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
181    #[inline]
182    #[must_use]
183    pub fn looking_at(mut self, target: Vec3, up: impl TryInto<Dir3>) -> Self {
184        self.look_at(target, up);
185        self
186    }
187
188    /// Returns this [`Transform`] with a new rotation so that [`Transform::forward`]
189    /// points in the given `direction` and [`Transform::up`] points towards `up`.
190    ///
191    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
192    /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Z` is used instead
193    /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
194    /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
195    #[inline]
196    #[must_use]
197    pub fn looking_to(mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) -> Self {
198        self.look_to(direction, up);
199        self
200    }
201
202    /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
203    /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
204    /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
205    /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
206    /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
207    ///
208    ///
209    /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
210    /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
211    /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
212    /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
213    ///   a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
214    ///   counterparts
215    ///
216    /// See [`Transform::align`] for additional details.
217    #[inline]
218    #[must_use]
219    pub fn aligned_by(
220        mut self,
221        main_axis: impl TryInto<Dir3>,
222        main_direction: impl TryInto<Dir3>,
223        secondary_axis: impl TryInto<Dir3>,
224        secondary_direction: impl TryInto<Dir3>,
225    ) -> Self {
226        self.align(
227            main_axis,
228            main_direction,
229            secondary_axis,
230            secondary_direction,
231        );
232        self
233    }
234
235    /// Returns this [`Transform`] with a new translation.
236    #[inline]
237    #[must_use]
238    pub const fn with_translation(mut self, translation: Vec3) -> Self {
239        self.translation = translation;
240        self
241    }
242
243    /// Returns this [`Transform`] with a new rotation.
244    #[inline]
245    #[must_use]
246    pub const fn with_rotation(mut self, rotation: Quat) -> Self {
247        self.rotation = rotation;
248        self
249    }
250
251    /// Returns this [`Transform`] with a new scale.
252    #[inline]
253    #[must_use]
254    pub const fn with_scale(mut self, scale: Vec3) -> Self {
255        self.scale = scale;
256        self
257    }
258
259    /// Computes the 3d affine transformation matrix from this transform's translation,
260    /// rotation, and scale.
261    #[inline]
262    pub fn to_matrix(&self) -> Mat4 {
263        Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
264    }
265
266    /// Returns the 3d affine transformation matrix from this transforms translation,
267    /// rotation, and scale.
268    #[inline]
269    pub fn compute_affine(&self) -> Affine3A {
270        Affine3A::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
271    }
272
273    /// Get the unit vector in the local `X` direction.
274    #[inline]
275    pub fn local_x(&self) -> Dir3 {
276        // Quat * unit vector is length 1
277        Dir3::new_unchecked(self.rotation * Vec3::X)
278    }
279
280    /// Equivalent to [`-local_x()`][Transform::local_x()]
281    #[inline]
282    pub fn left(&self) -> Dir3 {
283        -self.local_x()
284    }
285
286    /// Equivalent to [`local_x()`][Transform::local_x()]
287    #[inline]
288    pub fn right(&self) -> Dir3 {
289        self.local_x()
290    }
291
292    /// Get the unit vector in the local `Y` direction.
293    #[inline]
294    pub fn local_y(&self) -> Dir3 {
295        // Quat * unit vector is length 1
296        Dir3::new_unchecked(self.rotation * Vec3::Y)
297    }
298
299    /// Equivalent to [`local_y()`][Transform::local_y]
300    #[inline]
301    pub fn up(&self) -> Dir3 {
302        self.local_y()
303    }
304
305    /// Equivalent to [`-local_y()`][Transform::local_y]
306    #[inline]
307    pub fn down(&self) -> Dir3 {
308        -self.local_y()
309    }
310
311    /// Get the unit vector in the local `Z` direction.
312    #[inline]
313    pub fn local_z(&self) -> Dir3 {
314        // Quat * unit vector is length 1
315        Dir3::new_unchecked(self.rotation * Vec3::Z)
316    }
317
318    /// Equivalent to [`-local_z()`][Transform::local_z]
319    #[inline]
320    pub fn forward(&self) -> Dir3 {
321        -self.local_z()
322    }
323
324    /// Equivalent to [`local_z()`][Transform::local_z]
325    #[inline]
326    pub fn back(&self) -> Dir3 {
327        self.local_z()
328    }
329
330    /// Rotates this [`Transform`] by the given rotation.
331    ///
332    /// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
333    ///
334    /// # Examples
335    ///
336    /// - [`3d_rotation`]
337    ///
338    /// [`3d_rotation`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/3d_rotation.rs
339    #[inline]
340    pub fn rotate(&mut self, rotation: Quat) {
341        self.rotation = rotation * self.rotation;
342    }
343
344    /// Rotates this [`Transform`] around the given `axis` by `angle` (in radians).
345    ///
346    /// If this [`Transform`] has a parent, the `axis` is relative to the rotation of the parent.
347    ///
348    /// # Warning
349    ///
350    /// If you pass in an `axis` based on the current rotation (e.g. obtained via [`Transform::local_x`]),
351    /// floating point errors can accumulate exponentially when applying rotations repeatedly this way. This will
352    /// result in a denormalized rotation. In this case, it is recommended to normalize the [`Transform::rotation`] after
353    /// each call to this method.
354    #[inline]
355    pub fn rotate_axis(&mut self, axis: Dir3, angle: f32) {
356        #[cfg(debug_assertions)]
357        assert_is_normalized(
358            "The axis given to `Transform::rotate_axis` is not normalized. This may be a result of obtaining \
359            the axis from the transform. See the documentation of `Transform::rotate_axis` for more details.",
360            axis.length_squared(),
361        );
362        self.rotate(Quat::from_axis_angle(axis.into(), angle));
363    }
364
365    /// Rotates this [`Transform`] around the `X` axis by `angle` (in radians).
366    ///
367    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
368    #[inline]
369    pub fn rotate_x(&mut self, angle: f32) {
370        self.rotate(Quat::from_rotation_x(angle));
371    }
372
373    /// Rotates this [`Transform`] around the `Y` axis by `angle` (in radians).
374    ///
375    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
376    #[inline]
377    pub fn rotate_y(&mut self, angle: f32) {
378        self.rotate(Quat::from_rotation_y(angle));
379    }
380
381    /// Rotates this [`Transform`] around the `Z` axis by `angle` (in radians).
382    ///
383    /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent.
384    #[inline]
385    pub fn rotate_z(&mut self, angle: f32) {
386        self.rotate(Quat::from_rotation_z(angle));
387    }
388
389    /// Rotates this [`Transform`] by the given `rotation`.
390    ///
391    /// The `rotation` is relative to this [`Transform`]'s current rotation.
392    #[inline]
393    pub fn rotate_local(&mut self, rotation: Quat) {
394        self.rotation *= rotation;
395    }
396
397    /// Rotates this [`Transform`] around its local `axis` by `angle` (in radians).
398    ///
399    /// # Warning
400    ///
401    /// If you pass in an `axis` based on the current rotation (e.g. obtained via [`Transform::local_x`]),
402    /// floating point errors can accumulate exponentially when applying rotations repeatedly this way. This will
403    /// result in a denormalized rotation. In this case, it is recommended to normalize the [`Transform::rotation`] after
404    /// each call to this method.
405    #[inline]
406    pub fn rotate_local_axis(&mut self, axis: Dir3, angle: f32) {
407        #[cfg(debug_assertions)]
408        assert_is_normalized(
409            "The axis given to `Transform::rotate_axis_local` is not normalized. This may be a result of obtaining \
410            the axis from the transform. See the documentation of `Transform::rotate_axis_local` for more details.",
411            axis.length_squared(),
412        );
413        self.rotate_local(Quat::from_axis_angle(axis.into(), angle));
414    }
415
416    /// Rotates this [`Transform`] around its local `X` axis by `angle` (in radians).
417    #[inline]
418    pub fn rotate_local_x(&mut self, angle: f32) {
419        self.rotate_local(Quat::from_rotation_x(angle));
420    }
421
422    /// Rotates this [`Transform`] around its local `Y` axis by `angle` (in radians).
423    #[inline]
424    pub fn rotate_local_y(&mut self, angle: f32) {
425        self.rotate_local(Quat::from_rotation_y(angle));
426    }
427
428    /// Rotates this [`Transform`] around its local `Z` axis by `angle` (in radians).
429    #[inline]
430    pub fn rotate_local_z(&mut self, angle: f32) {
431        self.rotate_local(Quat::from_rotation_z(angle));
432    }
433
434    /// Translates this [`Transform`] around a `point` in space.
435    ///
436    /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
437    #[inline]
438    pub fn translate_around(&mut self, point: Vec3, rotation: Quat) {
439        self.translation = point + rotation * (self.translation - point);
440    }
441
442    /// Rotates this [`Transform`] around a `point` in space.
443    ///
444    /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent.
445    #[inline]
446    pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) {
447        self.translate_around(point, rotation);
448        self.rotate(rotation);
449    }
450
451    /// Rotates this [`Transform`] so that [`Transform::forward`] points towards the `target` position,
452    /// and [`Transform::up`] points towards `up`.
453    ///
454    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
455    /// * if `target` is the same as the transform translation, `Vec3::Z` is used instead
456    /// * if `up` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::Y` is used instead
457    /// * if the resulting forward direction is parallel with `up`, an orthogonal vector is used as the "right" direction
458    #[inline]
459    pub fn look_at(&mut self, target: Vec3, up: impl TryInto<Dir3>) {
460        self.look_to(target - self.translation, up);
461    }
462
463    /// Rotates this [`Transform`] so that [`Transform::forward`] points in the given `direction`
464    /// and [`Transform::up`] points towards `up`.
465    ///
466    /// In some cases it's not possible to construct a rotation. Another axis will be picked in those cases:
467    /// * if `direction` fails converting to `Dir3` (e.g if it is `Vec3::ZERO`), `Dir3::NEG_Z` is used instead
468    /// * if `up` fails converting to `Dir3`, `Dir3::Y` is used instead
469    /// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
470    #[inline]
471    pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
472        let back = -direction.try_into().unwrap_or(Dir3::NEG_Z);
473        let up = up.try_into().unwrap_or(Dir3::Y);
474        let right = up
475            .cross(back.into())
476            .try_normalize()
477            .unwrap_or_else(|| up.any_orthonormal_vector());
478        let up = back.cross(right);
479        self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, back.into()));
480    }
481
482    /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points
483    /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`.
484    ///
485    /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates
486    /// and its dorsal fin pointing in the Y-direction, then `align(Dir3::X, v, Dir3::Y, w)` will make the spaceship's
487    /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`.
488    ///
489    /// More precisely, the [`Transform::rotation`] produced will be such that:
490    /// * applying it to `main_axis` results in `main_direction`
491    /// * applying it to `secondary_axis` produces a vector that lies in the half-plane generated by `main_direction` and
492    ///   `secondary_direction` (with positive contribution by `secondary_direction`)
493    ///
494    /// [`Transform::look_to`] is recovered, for instance, when `main_axis` is `Dir3::NEG_Z` (the [`Transform::forward`]
495    /// direction in the default orientation) and `secondary_axis` is `Dir3::Y` (the [`Transform::up`] direction in the default
496    /// orientation). (Failure cases may differ somewhat.)
497    ///
498    /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases:
499    /// * if `main_axis` or `main_direction` fail converting to `Dir3` (e.g are zero), `Dir3::X` takes their place
500    /// * if `secondary_axis` or `secondary_direction` fail converting, `Dir3::Y` takes their place
501    /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`,
502    ///   a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary
503    ///   counterparts
504    ///
505    /// Example
506    /// ```
507    /// # use bevy_math::{Dir3, Vec3, Quat};
508    /// # use bevy_transform::components::Transform;
509    /// # let mut t1 = Transform::IDENTITY;
510    /// # let mut t2 = Transform::IDENTITY;
511    /// t1.align(Dir3::X, Dir3::Y, Vec3::new(1., 1., 0.), Dir3::Z);
512    /// let main_axis_image = t1.rotation * Dir3::X;
513    /// let secondary_axis_image = t1.rotation * Vec3::new(1., 1., 0.);
514    /// assert!(main_axis_image.abs_diff_eq(Vec3::Y, 1e-5));
515    /// assert!(secondary_axis_image.abs_diff_eq(Vec3::new(0., 1., 1.), 1e-5));
516    ///
517    /// t1.align(Vec3::ZERO, Dir3::Z, Vec3::ZERO, Dir3::X);
518    /// t2.align(Dir3::X, Dir3::Z, Dir3::Y, Dir3::X);
519    /// assert_eq!(t1.rotation, t2.rotation);
520    ///
521    /// t1.align(Dir3::X, Dir3::Z, Dir3::X, Dir3::Y);
522    /// assert_eq!(t1.rotation, Quat::from_rotation_arc(Vec3::X, Vec3::Z));
523    /// ```
524    #[inline]
525    pub fn align(
526        &mut self,
527        main_axis: impl TryInto<Dir3>,
528        main_direction: impl TryInto<Dir3>,
529        secondary_axis: impl TryInto<Dir3>,
530        secondary_direction: impl TryInto<Dir3>,
531    ) {
532        let main_axis = main_axis.try_into().unwrap_or(Dir3::X);
533        let main_direction = main_direction.try_into().unwrap_or(Dir3::X);
534        let secondary_axis = secondary_axis.try_into().unwrap_or(Dir3::Y);
535        let secondary_direction = secondary_direction.try_into().unwrap_or(Dir3::Y);
536
537        // The solution quaternion will be constructed in two steps.
538        // First, we start with a rotation that takes `main_axis` to `main_direction`.
539        let first_rotation = Quat::from_rotation_arc(main_axis.into(), main_direction.into());
540
541        // Let's follow by rotating about the `main_direction` axis so that the image of `secondary_axis`
542        // is taken to something that lies in the plane of `main_direction` and `secondary_direction`. Since
543        // `main_direction` is fixed by this rotation, the first criterion is still satisfied.
544        let secondary_image = first_rotation * secondary_axis;
545        let secondary_image_ortho = secondary_image
546            .reject_from_normalized(main_direction.into())
547            .try_normalize();
548        let secondary_direction_ortho = secondary_direction
549            .reject_from_normalized(main_direction.into())
550            .try_normalize();
551
552        // If one of the two weak vectors was parallel to `main_direction`, then we just do the first part
553        self.rotation = match (secondary_image_ortho, secondary_direction_ortho) {
554            (Some(secondary_img_ortho), Some(secondary_dir_ortho)) => {
555                let second_rotation =
556                    Quat::from_rotation_arc(secondary_img_ortho, secondary_dir_ortho);
557                second_rotation * first_rotation
558            }
559            _ => first_rotation,
560        };
561    }
562
563    /// Multiplies `self` with `transform` component by component, returning the
564    /// resulting [`Transform`]
565    #[inline]
566    #[must_use]
567    pub fn mul_transform(&self, transform: Transform) -> Self {
568        let translation = self.transform_point(transform.translation);
569        let rotation = self.rotation * transform.rotation;
570        let scale = self.scale * transform.scale;
571        Transform {
572            translation,
573            rotation,
574            scale,
575        }
576    }
577
578    /// Transforms the given `point`, applying scale, rotation and translation.
579    ///
580    /// If this [`Transform`] has an ancestor entity with a [`Transform`] component,
581    /// [`Transform::transform_point`] will transform a point in local space into its
582    /// parent transform's space.
583    ///
584    /// If this [`Transform`] does not have a parent, [`Transform::transform_point`] will
585    /// transform a point in local space into worldspace coordinates.
586    ///
587    /// If you always want to transform a point in local space to worldspace, or if you need
588    /// the inverse transformations, see [`GlobalTransform::transform_point()`].
589    #[inline]
590    pub fn transform_point(&self, mut point: Vec3) -> Vec3 {
591        point = self.scale * point;
592        point = self.rotation * point;
593        point += self.translation;
594        point
595    }
596
597    /// Returns `true` if, and only if, translation, rotation and scale all are
598    /// finite. If any of them contains a `NaN`, positive or negative infinity,
599    /// this will return `false`.
600    #[inline]
601    #[must_use]
602    pub fn is_finite(&self) -> bool {
603        self.translation.is_finite() && self.rotation.is_finite() && self.scale.is_finite()
604    }
605
606    /// Get the [isometry] defined by this transform's rotation and translation, ignoring scale.
607    ///
608    /// [isometry]: Isometry3d
609    #[inline]
610    pub fn to_isometry(&self) -> Isometry3d {
611        Isometry3d::new(self.translation, self.rotation)
612    }
613}
614
615impl Default for Transform {
616    fn default() -> Self {
617        Self::IDENTITY
618    }
619}
620
621/// The transform is expected to be non-degenerate and without shearing, or the output
622/// will be invalid.
623impl From<GlobalTransform> for Transform {
624    fn from(transform: GlobalTransform) -> Self {
625        transform.compute_transform()
626    }
627}
628
629impl Mul<Transform> for Transform {
630    type Output = Transform;
631
632    fn mul(self, transform: Transform) -> Self::Output {
633        self.mul_transform(transform)
634    }
635}
636
637impl Mul<GlobalTransform> for Transform {
638    type Output = GlobalTransform;
639
640    #[inline]
641    fn mul(self, global_transform: GlobalTransform) -> Self::Output {
642        GlobalTransform::from(self) * global_transform
643    }
644}
645
646impl Mul<Vec3> for Transform {
647    type Output = Vec3;
648
649    fn mul(self, value: Vec3) -> Self::Output {
650        self.transform_point(value)
651    }
652}
653
654/// An optimization for transform propagation. This ZST marker component uses change detection to
655/// mark all entities of the hierarchy as "dirty" if any of their descendants have a changed
656/// `Transform`. If this component is *not* marked `is_changed()`, propagation will halt.
657#[derive(Clone, Copy, Default, PartialEq, Debug)]
658#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
659#[cfg_attr(feature = "bevy-support", derive(Component))]
660#[cfg_attr(
661    feature = "bevy_reflect",
662    derive(Reflect),
663    reflect(Component, Default, PartialEq, Debug)
664)]
665#[cfg_attr(
666    all(feature = "bevy_reflect", feature = "serialize"),
667    reflect(Serialize, Deserialize)
668)]
669pub struct TransformTreeChanged;