1use crate::derives::*;
8use crate::typed_om::{
9 KeywordValue, MatrixComponent, NumericValue, PerspectiveComponent, PerspectiveValue,
10 RotateComponent, ScaleComponent, SkewComponent, ToTyped, TransformComponent,
11 TranslateComponent, TypedValue,
12};
13use crate::values::computed::length::Length as ComputedLength;
14use crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;
15use crate::values::computed::transform::Matrix3D as ComputedMatrix3D;
16use crate::values::specified::angle::Angle as SpecifiedAngle;
17use crate::values::specified::length::Length as SpecifiedLength;
18use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
19use crate::values::specified::number::Number as SpecifiedNumber;
20use crate::values::{computed, CSSFloat};
21use crate::{One, Zero, ZeroNoPercent};
22use euclid::default::{Rect, Transform3D};
23use std::fmt::{self, Write};
24use std::ops::Neg;
25use style_traits::{CssString, CssWriter, ToCss};
26use thin_vec::ThinVec;
27
28#[allow(missing_docs)]
30#[derive(
31 Clone,
32 Copy,
33 Debug,
34 Deserialize,
35 MallocSizeOf,
36 PartialEq,
37 Serialize,
38 SpecifiedValueInfo,
39 ToAnimatedValue,
40 ToComputedValue,
41 ToCss,
42 ToResolvedValue,
43 ToShmem,
44)]
45#[css(comma, function = "matrix")]
46#[repr(C)]
47pub struct GenericMatrix<T> {
48 pub a: T,
49 pub b: T,
50 pub c: T,
51 pub d: T,
52 pub e: T,
53 pub f: T,
54}
55
56pub use self::GenericMatrix as Matrix;
57
58#[allow(missing_docs)]
59#[rustfmt::skip]
60#[derive(
61 Clone,
62 Copy,
63 Debug,
64 Deserialize,
65 MallocSizeOf,
66 PartialEq,
67 Serialize,
68 SpecifiedValueInfo,
69 ToAnimatedValue,
70 ToComputedValue,
71 ToCss,
72 ToResolvedValue,
73 ToShmem,
74)]
75#[css(comma, function = "matrix3d")]
76#[repr(C)]
77pub struct GenericMatrix3D<T> {
78 pub m11: T, pub m12: T, pub m13: T, pub m14: T,
79 pub m21: T, pub m22: T, pub m23: T, pub m24: T,
80 pub m31: T, pub m32: T, pub m33: T, pub m34: T,
81 pub m41: T, pub m42: T, pub m43: T, pub m44: T,
82}
83
84pub use self::GenericMatrix3D as Matrix3D;
85
86#[rustfmt::skip]
87impl<T: ToFloat> TryFrom<Matrix<T>> for Transform3D<f64> {
88 type Error = ();
89
90 #[inline]
91 fn try_from(m: Matrix<T>) -> Result<Self, Self::Error> {
92 Ok(Transform3D::new(
93 m.a.to_f64()?, m.b.to_f64()?, 0.0, 0.0,
94 m.c.to_f64()?, m.d.to_f64()?, 0.0, 0.0,
95 0.0, 0.0, 1.0, 0.0,
96 m.e.to_f64()?, m.f.to_f64()?, 0.0, 1.0,
97 ))
98 }
99}
100
101#[rustfmt::skip]
102impl<T: ToFloat> TryFrom<Matrix3D<T>> for Transform3D<f64> {
103 type Error = ();
104
105 #[inline]
106 fn try_from(m: Matrix3D<T>) -> Result<Self, Self::Error> {
107 Ok(Transform3D::new(
108 m.m11.to_f64()?, m.m12.to_f64()?, m.m13.to_f64()?, m.m14.to_f64()?,
109 m.m21.to_f64()?, m.m22.to_f64()?, m.m23.to_f64()?, m.m24.to_f64()?,
110 m.m31.to_f64()?, m.m32.to_f64()?, m.m33.to_f64()?, m.m34.to_f64()?,
111 m.m41.to_f64()?, m.m42.to_f64()?, m.m43.to_f64()?, m.m44.to_f64()?,
112 ))
113 }
114}
115
116#[derive(
118 Animate,
119 Clone,
120 ComputeSquaredDistance,
121 Copy,
122 Debug,
123 MallocSizeOf,
124 PartialEq,
125 SpecifiedValueInfo,
126 ToAnimatedValue,
127 ToAnimatedZero,
128 ToComputedValue,
129 ToCss,
130 ToResolvedValue,
131 ToShmem,
132 ToTyped,
133)]
134#[repr(C)]
135#[typed(todo_derive_fields)]
136pub struct GenericTransformOrigin<H, V, Depth> {
137 pub horizontal: H,
139 pub vertical: V,
141 pub depth: Depth,
143}
144
145pub use self::GenericTransformOrigin as TransformOrigin;
146
147impl<H, V, D> TransformOrigin<H, V, D> {
148 pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
150 Self {
151 horizontal,
152 vertical,
153 depth,
154 }
155 }
156}
157
158fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
159 x == y
160}
161
162#[derive(
165 Clone,
166 Debug,
167 Deserialize,
168 MallocSizeOf,
169 PartialEq,
170 Serialize,
171 SpecifiedValueInfo,
172 ToAnimatedValue,
173 ToComputedValue,
174 ToCss,
175 ToResolvedValue,
176 ToShmem,
177 ToTyped,
178)]
179#[repr(C, u8)]
180pub enum GenericPerspectiveFunction<L> {
181 None,
183 Length(L),
185}
186
187impl<L> GenericPerspectiveFunction<L> {
188 pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
190 match *self {
191 Self::None => f32::INFINITY,
192 Self::Length(ref l) => f(l),
193 }
194 }
195}
196
197pub use self::GenericPerspectiveFunction as PerspectiveFunction;
198
199#[derive(
200 Clone,
201 Debug,
202 Deserialize,
203 MallocSizeOf,
204 PartialEq,
205 Serialize,
206 SpecifiedValueInfo,
207 ToAnimatedValue,
208 ToComputedValue,
209 ToCss,
210 ToResolvedValue,
211 ToShmem,
212)]
213#[repr(C, u8)]
214pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
216where
217 Angle: Zero,
218 LengthPercentage: Zero + ZeroNoPercent,
219 Number: PartialEq,
220{
221 Matrix(GenericMatrix<Number>),
223 Matrix3D(GenericMatrix3D<Number>),
225 #[css(comma, function)]
231 Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
232 #[css(function = "skewX")]
234 SkewX(Angle),
235 #[css(function = "skewY")]
237 SkewY(Angle),
238 #[css(comma, function)]
240 Translate(
241 LengthPercentage,
242 #[css(skip_if = "ZeroNoPercent::is_zero_no_percent")] LengthPercentage,
243 ),
244 #[css(function = "translateX")]
246 TranslateX(LengthPercentage),
247 #[css(function = "translateY")]
249 TranslateY(LengthPercentage),
250 #[css(function = "translateZ")]
252 TranslateZ(Length),
253 #[css(comma, function = "translate3d")]
255 Translate3D(LengthPercentage, LengthPercentage, Length),
256 #[css(comma, function)]
260 Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
261 #[css(function = "scaleX")]
263 ScaleX(Number),
264 #[css(function = "scaleY")]
266 ScaleY(Number),
267 #[css(function = "scaleZ")]
269 ScaleZ(Number),
270 #[css(comma, function = "scale3d")]
272 Scale3D(Number, Number, Number),
273 #[css(function)]
277 Rotate(Angle),
278 #[css(function = "rotateX")]
280 RotateX(Angle),
281 #[css(function = "rotateY")]
283 RotateY(Angle),
284 #[css(function = "rotateZ")]
286 RotateZ(Angle),
287 #[css(comma, function = "rotate3d")]
291 Rotate3D(Number, Number, Number, Angle),
292 #[css(function)]
299 Perspective(GenericPerspectiveFunction<Length>),
300 #[allow(missing_docs)]
302 #[css(comma, function = "interpolatematrix")]
303 InterpolateMatrix {
304 from_list: GenericTransform<
305 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
306 >,
307 to_list: GenericTransform<
308 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
309 >,
310 progress: computed::Percentage,
311 },
312 #[allow(missing_docs)]
314 #[css(comma, function = "accumulatematrix")]
315 AccumulateMatrix {
316 from_list: GenericTransform<
317 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
318 >,
319 to_list: GenericTransform<
320 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
321 >,
322 count: Integer,
323 },
324}
325
326pub use self::GenericTransformOperation as TransformOperation;
327
328pub trait ToTransformComponent {
330 fn to_transform_component(&self, _dest: &mut ThinVec<TransformComponent>) -> Result<(), ()>;
336}
337
338impl<Angle, Number, Length, Integer, LengthPercentage> ToTransformComponent
339 for TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
340where
341 Angle: Zero + ToTyped,
342 Number: PartialEq + ToFloat + ToTyped,
343 Length: ToTyped,
344 LengthPercentage: Zero + ToTyped + ZeroNoPercent,
345{
346 fn to_transform_component(&self, dest: &mut ThinVec<TransformComponent>) -> Result<(), ()> {
347 use self::TransformOperation::*;
348
349 let component = match *self {
351 Matrix(ref m) => TransformComponent::Matrix(MatrixComponent {
352 #[rustfmt::skip]
353 matrix: ComputedMatrix3D {
354 m11: m.a.to_f32()?, m12: m.b.to_f32()?, m13: 0.0, m14: 0.0,
355 m21: m.c.to_f32()?, m22: m.d.to_f32()?, m23: 0.0, m24: 0.0,
356 m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
357 m41: m.e.to_f32()?, m42: m.f.to_f32()?, m43: 0.0, m44: 1.0,
358 },
359 is_2d: true,
360 }),
361 Matrix3D(ref m) => TransformComponent::Matrix(MatrixComponent {
362 #[rustfmt::skip]
363 matrix: ComputedMatrix3D {
364 m11: m.m11.to_f32()?, m12: m.m12.to_f32()?, m13: m.m13.to_f32()?, m14: m.m14.to_f32()?,
365 m21: m.m21.to_f32()?, m22: m.m22.to_f32()?, m23: m.m23.to_f32()?, m24: m.m24.to_f32()?,
366 m31: m.m31.to_f32()?, m32: m.m32.to_f32()?, m33: m.m33.to_f32()?, m34: m.m34.to_f32()?,
367 m41: m.m41.to_f32()?, m42: m.m42.to_f32()?, m43: m.m43.to_f32()?, m44: m.m44.to_f32()?,
368 },
369 is_2d: false,
370 }),
371 Skew(ref theta_x, ref theta_y) => TransformComponent::Skew(SkewComponent {
372 ax: theta_x.to_numeric_value().ok_or(())?,
373 ay: theta_y.to_numeric_value().ok_or(())?,
374 }),
375 SkewX(ref theta) => TransformComponent::SkewX(theta.to_numeric_value().ok_or(())?),
376 SkewY(ref theta) => TransformComponent::SkewY(theta.to_numeric_value().ok_or(())?),
377 Translate(ref tx, ref ty) => TransformComponent::Translate(TranslateComponent {
378 x: tx.to_numeric_value().ok_or(())?,
379 y: ty.to_numeric_value().ok_or(())?,
380 z: NumericValue::zero_px(),
381 is_2d: true,
382 }),
383 TranslateX(ref t) => TransformComponent::Translate(TranslateComponent {
384 x: t.to_numeric_value().ok_or(())?,
385 y: NumericValue::zero_px(),
386 z: NumericValue::zero_px(),
387 is_2d: true,
388 }),
389 TranslateY(ref t) => TransformComponent::Translate(TranslateComponent {
390 x: NumericValue::zero_px(),
391 y: t.to_numeric_value().ok_or(())?,
392 z: NumericValue::zero_px(),
393 is_2d: true,
394 }),
395 TranslateZ(ref t) => TransformComponent::Translate(TranslateComponent {
396 x: NumericValue::zero_px(),
397 y: NumericValue::zero_px(),
398 z: t.to_numeric_value().ok_or(())?,
399 is_2d: false,
400 }),
401 Translate3D(ref tx, ref ty, ref tz) => {
402 TransformComponent::Translate(TranslateComponent {
403 x: tx.to_numeric_value().ok_or(())?,
404 y: ty.to_numeric_value().ok_or(())?,
405 z: tz.to_numeric_value().ok_or(())?,
406 is_2d: false,
407 })
408 },
409 Scale(ref sx, ref sy) => TransformComponent::Scale(ScaleComponent {
410 x: sx.to_numeric_value().ok_or(())?,
411 y: sy.to_numeric_value().ok_or(())?,
412 z: NumericValue::one(),
413 is_2d: true,
414 }),
415 ScaleX(ref s) => TransformComponent::Scale(ScaleComponent {
416 x: s.to_numeric_value().ok_or(())?,
417 y: NumericValue::one(),
418 z: NumericValue::one(),
419 is_2d: true,
420 }),
421 ScaleY(ref s) => TransformComponent::Scale(ScaleComponent {
422 x: NumericValue::one(),
423 y: s.to_numeric_value().ok_or(())?,
424 z: NumericValue::one(),
425 is_2d: true,
426 }),
427 ScaleZ(ref s) => TransformComponent::Scale(ScaleComponent {
428 x: NumericValue::one(),
429 y: NumericValue::one(),
430 z: s.to_numeric_value().ok_or(())?,
431 is_2d: false,
432 }),
433 Scale3D(ref sx, ref sy, ref sz) => TransformComponent::Scale(ScaleComponent {
434 x: sx.to_numeric_value().ok_or(())?,
435 y: sy.to_numeric_value().ok_or(())?,
436 z: sz.to_numeric_value().ok_or(())?,
437 is_2d: false,
438 }),
439 Rotate(ref theta) => TransformComponent::Rotate(RotateComponent {
440 angle: theta.to_numeric_value().ok_or(())?,
441 x: NumericValue::zero(),
442 y: NumericValue::zero(),
443 z: NumericValue::one(),
444 is_2d: true,
445 }),
446 RotateX(ref theta) => TransformComponent::Rotate(RotateComponent {
447 angle: theta.to_numeric_value().ok_or(())?,
448 x: NumericValue::one(),
449 y: NumericValue::zero(),
450 z: NumericValue::zero(),
451 is_2d: false,
452 }),
453 RotateY(ref theta) => TransformComponent::Rotate(RotateComponent {
454 angle: theta.to_numeric_value().ok_or(())?,
455 x: NumericValue::zero(),
456 y: NumericValue::one(),
457 z: NumericValue::zero(),
458 is_2d: false,
459 }),
460 RotateZ(ref theta) => TransformComponent::Rotate(RotateComponent {
461 angle: theta.to_numeric_value().ok_or(())?,
462 x: NumericValue::zero(),
463 y: NumericValue::zero(),
464 z: NumericValue::one(),
465 is_2d: false,
466 }),
467 Rotate3D(ref ax, ref ay, ref az, ref theta) => {
468 TransformComponent::Rotate(RotateComponent {
469 angle: theta.to_numeric_value().ok_or(())?,
470 x: ax.to_numeric_value().ok_or(())?,
471 y: ay.to_numeric_value().ok_or(())?,
472 z: az.to_numeric_value().ok_or(())?,
473 is_2d: false,
474 })
475 },
476 Perspective(ref p) => {
477 let length = match p.to_typed_value().ok_or(())? {
478 TypedValue::Numeric(value) => PerspectiveValue::Numeric(value),
479 TypedValue::Keyword(value) => PerspectiveValue::Keyword(value),
480 _ => return Err(()),
481 };
482 TransformComponent::Perspective(PerspectiveComponent { length })
483 },
484 _ => return Err(()),
485 };
486
487 dest.push(component);
488 Ok(())
489 }
490}
491
492#[derive(
493 Clone,
494 Debug,
495 Deserialize,
496 MallocSizeOf,
497 PartialEq,
498 Serialize,
499 SpecifiedValueInfo,
500 ToAnimatedValue,
501 ToComputedValue,
502 ToCss,
503 ToResolvedValue,
504 ToShmem,
505)]
506#[repr(C)]
507pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
509
510pub use self::GenericTransform as Transform;
511
512impl<T: ToTransformComponent> ToTyped for Transform<T> {
513 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
514 if self.0.is_empty() {
515 dest.push(TypedValue::Keyword(KeywordValue(CssString::from("none"))));
516 return Ok(());
517 }
518
519 let mut values = ThinVec::new();
521
522 let ops: &[T] = &self.0;
523 for item in ops {
524 item.to_transform_component(&mut values)?;
525 }
526
527 dest.push(TypedValue::Transform(values));
528 Ok(())
529 }
530}
531
532impl<Angle, Number, Length, Integer, LengthPercentage>
533 TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
534where
535 Angle: Zero,
536 LengthPercentage: Zero + ZeroNoPercent,
537 Number: PartialEq,
538{
539 pub fn is_rotate(&self) -> bool {
541 use self::TransformOperation::*;
542 matches!(
543 *self,
544 Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)
545 )
546 }
547
548 pub fn is_translate(&self) -> bool {
550 use self::TransformOperation::*;
551 match *self {
552 Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
553 true
554 },
555 _ => false,
556 }
557 }
558
559 pub fn is_scale(&self) -> bool {
561 use self::TransformOperation::*;
562 match *self {
563 Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,
564 _ => false,
565 }
566 }
567}
568
569pub trait ToAbsoluteLength {
571 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
573}
574
575impl ToAbsoluteLength for SpecifiedLength {
576 #[inline]
580 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
581 self.to_computed_pixel_length_without_context()
582 }
583}
584
585impl ToAbsoluteLength for SpecifiedLengthPercentage {
586 #[inline]
590 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
591 use self::SpecifiedLengthPercentage::*;
592 match *self {
593 Length(len) => len.to_computed_pixel_length_without_context(),
594 Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
595 Percentage(..) => Err(()),
596 }
597 }
598}
599
600impl ToAbsoluteLength for ComputedLength {
601 #[inline]
602 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
603 Ok(self.px())
604 }
605}
606
607impl ToAbsoluteLength for ComputedLengthPercentage {
608 #[inline]
609 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
610 Ok(self
611 .maybe_percentage_relative_to(containing_len)
612 .ok_or(())?
613 .px())
614 }
615}
616
617pub trait ToMatrix {
619 fn is_3d(&self) -> bool;
621
622 fn to_3d_matrix(
624 &self,
625 reference_box: Option<&Rect<ComputedLength>>,
626 ) -> Result<Transform3D<f64>, ()>;
627}
628
629pub trait ToRadians {
631 fn radians64(&self) -> Result<f64, ()>;
633}
634
635impl ToRadians for computed::angle::Angle {
636 #[inline]
637 fn radians64(&self) -> Result<f64, ()> {
638 Ok(computed::angle::Angle::radians64(self))
639 }
640}
641
642impl ToRadians for SpecifiedAngle {
643 #[inline]
644 fn radians64(&self) -> Result<f64, ()> {
645 let degrees = self.degrees().ok_or(())?;
646 Ok(computed::angle::Angle::from_degrees(degrees).radians64())
647 }
648}
649
650pub trait ToFloat {
652 fn to_f32(&self) -> Result<f32, ()>;
654
655 fn to_f64(&self) -> Result<f64, ()>;
657}
658
659impl ToFloat for SpecifiedNumber {
660 #[inline]
661 fn to_f32(&self) -> Result<f32, ()> {
662 self.resolve().ok_or(())
663 }
664
665 #[inline]
666 fn to_f64(&self) -> Result<f64, ()> {
667 self.resolve().map(|v| v as f64).ok_or(())
668 }
669}
670
671impl ToFloat for computed::Number {
672 #[inline]
673 fn to_f32(&self) -> Result<f32, ()> {
674 Ok(*self)
675 }
676
677 #[inline]
678 fn to_f64(&self) -> Result<f64, ()> {
679 Ok(*self as f64)
680 }
681}
682
683impl<Angle, Number, Length, Integer, LoP> ToMatrix
684 for TransformOperation<Angle, Number, Length, Integer, LoP>
685where
686 Angle: Zero + ToRadians + Clone,
687 Number: PartialEq + Clone + ToFloat + ToFloat,
688 Length: ToAbsoluteLength,
689 LoP: Zero + ToAbsoluteLength + ZeroNoPercent,
690{
691 #[inline]
692 fn is_3d(&self) -> bool {
693 use self::TransformOperation::*;
694 match *self {
695 Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..)
696 | RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
697 _ => false,
698 }
699 }
700
701 #[inline]
706 fn to_3d_matrix(
707 &self,
708 reference_box: Option<&Rect<ComputedLength>>,
709 ) -> Result<Transform3D<f64>, ()> {
710 use self::TransformOperation::*;
711
712 let reference_width = reference_box.map(|v| v.size.width);
713 let reference_height = reference_box.map(|v| v.size.height);
714 let matrix = match *self {
715 Rotate3D(ref ax, ref ay, ref az, ref theta) => {
716 let theta = theta.radians64()?;
717 let (ax, ay, az, theta) = get_normalized_vector_and_angle(
718 ax.to_f32()?,
719 ay.to_f32()?,
720 az.to_f32()?,
721 theta,
722 );
723 Transform3D::rotation(
724 ax as f64,
725 ay as f64,
726 az as f64,
727 euclid::Angle::radians(theta),
728 )
729 },
730 RotateX(ref theta) => {
731 let theta = euclid::Angle::radians(theta.radians64()?);
732 Transform3D::rotation(1., 0., 0., theta)
733 },
734 RotateY(ref theta) => {
735 let theta = euclid::Angle::radians(theta.radians64()?);
736 Transform3D::rotation(0., 1., 0., theta)
737 },
738 RotateZ(ref theta) | Rotate(ref theta) => {
739 let theta = euclid::Angle::radians(theta.radians64()?);
740 Transform3D::rotation(0., 0., 1., theta)
741 },
742 Perspective(ref p) => {
743 let px = match p {
744 PerspectiveFunction::None => f32::INFINITY,
745 PerspectiveFunction::Length(p) => p.to_pixel_length(None)?,
746 };
747 create_perspective_matrix(px).cast()
748 },
749 Scale3D(ref sx, ref sy, ref sz) => {
750 Transform3D::scale(sx.to_f64()?, sy.to_f64()?, sz.to_f64()?)
751 },
752 Scale(ref sx, ref sy) => Transform3D::scale(sx.to_f64()?, sy.to_f64()?, 1.),
753 ScaleX(ref s) => Transform3D::scale(s.to_f64()?, 1., 1.),
754 ScaleY(ref s) => Transform3D::scale(1., s.to_f64()?, 1.),
755 ScaleZ(ref s) => Transform3D::scale(1., 1., s.to_f64()?),
756 Translate3D(ref tx, ref ty, ref tz) => {
757 let tx = tx.to_pixel_length(reference_width)? as f64;
758 let ty = ty.to_pixel_length(reference_height)? as f64;
759 Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)
760 },
761 Translate(ref tx, ref ty) => {
762 let tx = tx.to_pixel_length(reference_width)? as f64;
763 let ty = ty.to_pixel_length(reference_height)? as f64;
764 Transform3D::translation(tx, ty, 0.)
765 },
766 TranslateX(ref t) => {
767 let t = t.to_pixel_length(reference_width)? as f64;
768 Transform3D::translation(t, 0., 0.)
769 },
770 TranslateY(ref t) => {
771 let t = t.to_pixel_length(reference_height)? as f64;
772 Transform3D::translation(0., t, 0.)
773 },
774 TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),
775 Skew(ref theta_x, ref theta_y) => Transform3D::skew(
776 euclid::Angle::radians(theta_x.radians64()?),
777 euclid::Angle::radians(theta_y.radians64()?),
778 ),
779 SkewX(ref theta) => Transform3D::skew(
780 euclid::Angle::radians(theta.radians64()?),
781 euclid::Angle::radians(0.),
782 ),
783 SkewY(ref theta) => Transform3D::skew(
784 euclid::Angle::radians(0.),
785 euclid::Angle::radians(theta.radians64()?),
786 ),
787 Matrix3D(ref m) => m.clone().try_into()?,
788 Matrix(ref m) => m.clone().try_into()?,
789 InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
790 Transform3D::identity()
797 },
798 };
799 Ok(matrix)
800 }
801}
802
803impl<T> Transform<T> {
804 pub fn none() -> Self {
806 Transform(Default::default())
807 }
808}
809
810impl<T: ToMatrix> Transform<T> {
811 #[rustfmt::skip]
816 pub fn to_transform_3d_matrix(
817 &self,
818 reference_box: Option<&Rect<ComputedLength>>
819 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
820 Self::components_to_transform_3d_matrix(&self.0, reference_box)
821 }
822
823 #[rustfmt::skip]
825 pub fn components_to_transform_3d_matrix(
826 ops: &[T],
827 reference_box: Option<&Rect<ComputedLength>>,
828 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
829 let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
830 let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
831 Transform3D::new(
832 cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
833 cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
834 cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
835 cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
836 )
837 };
838
839 let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
840 Ok((cast_3d_transform(m), is_3d))
841 }
842
843 pub fn to_transform_3d_matrix_f64(
845 &self,
846 reference_box: Option<&Rect<ComputedLength>>,
847 ) -> Result<(Transform3D<f64>, bool), ()> {
848 Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
849 }
850
851 fn components_to_transform_3d_matrix_f64(
853 ops: &[T],
854 reference_box: Option<&Rect<ComputedLength>>,
855 ) -> Result<(Transform3D<f64>, bool), ()> {
856 let mut transform = Transform3D::<f64>::identity();
863 let mut contain_3d = false;
864
865 for operation in ops {
866 let matrix = operation.to_3d_matrix(reference_box)?;
867 contain_3d = contain_3d || operation.is_3d();
868 transform = matrix.then(&transform);
869 }
870
871 Ok((transform, contain_3d))
872 }
873}
874
875#[inline]
877pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
878 if d.is_finite() {
879 Transform3D::perspective(d.max(1.))
880 } else {
881 Transform3D::identity()
882 }
883}
884
885pub fn get_normalized_vector_and_angle<T: Zero>(
887 x: CSSFloat,
888 y: CSSFloat,
889 z: CSSFloat,
890 angle: T,
891) -> (CSSFloat, CSSFloat, CSSFloat, T) {
892 use crate::values::computed::transform::DirectionVector;
893 use euclid::approxeq::ApproxEq;
894 let vector = DirectionVector::new(x, y, z);
895 if vector.square_length().approx_eq(&f32::zero()) {
896 (0., 0., 1., T::zero())
900 } else {
901 let vector = vector.robust_normalize();
902 (vector.x, vector.y, vector.z, angle)
903 }
904}
905
906#[derive(
907 Clone,
908 Copy,
909 Debug,
910 Deserialize,
911 MallocSizeOf,
912 PartialEq,
913 Serialize,
914 SpecifiedValueInfo,
915 ToAnimatedValue,
916 ToAnimatedZero,
917 ToComputedValue,
918 ToResolvedValue,
919 ToShmem,
920 ToTyped,
921)]
922#[repr(C, u8)]
923#[typed(todo_derive_fields)]
924pub enum GenericRotate<Number, Angle> {
928 None,
930 Rotate(Angle),
932 Rotate3D(Number, Number, Number, Angle),
934}
935
936pub use self::GenericRotate as Rotate;
937
938pub trait IsParallelTo {
941 fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
943}
944
945impl<Number, Angle> ToCss for Rotate<Number, Angle>
946where
947 Number: Clone + PartialOrd + ToCss + Zero,
948 Angle: Clone + Neg<Output = Angle> + ToCss + Zero,
949 (Number, Number, Number): IsParallelTo,
950{
951 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
952 where
953 W: fmt::Write,
954 {
955 use crate::values::computed::transform::DirectionVector;
956 match *self {
957 Rotate::None => dest.write_str("none"),
958 Rotate::Rotate(ref angle) => angle.to_css(dest),
959 Rotate::Rotate3D(ref x, ref y, ref z, ref angle) => {
960 let v = (x.clone(), y.clone(), z.clone());
969 let (axis, angle) = if v.0.is_zero() && v.1.is_zero() && v.2.is_zero() {
970 (None, angle.clone())
977 } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
978 (
979 Some("x "),
980 if v.0 < Number::zero() {
981 -angle.clone()
982 } else {
983 angle.clone()
984 },
985 )
986 } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
987 (
988 Some("y "),
989 if v.1 < Number::zero() {
990 -angle.clone()
991 } else {
992 angle.clone()
993 },
994 )
995 } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
996 let angle = if v.2 < Number::zero() {
998 -angle.clone()
999 } else {
1000 angle.clone()
1001 };
1002 return angle.to_css(dest);
1003 } else {
1004 (None, angle.clone())
1005 };
1006 match axis {
1007 Some(a) => dest.write_str(a)?,
1008 None => {
1009 x.to_css(dest)?;
1010 dest.write_char(' ')?;
1011 y.to_css(dest)?;
1012 dest.write_char(' ')?;
1013 z.to_css(dest)?;
1014 dest.write_char(' ')?;
1015 },
1016 }
1017 angle.to_css(dest)
1018 },
1019 }
1020 }
1021}
1022
1023#[derive(
1024 Clone,
1025 Copy,
1026 Debug,
1027 Deserialize,
1028 MallocSizeOf,
1029 PartialEq,
1030 Serialize,
1031 SpecifiedValueInfo,
1032 ToAnimatedValue,
1033 ToAnimatedZero,
1034 ToComputedValue,
1035 ToResolvedValue,
1036 ToShmem,
1037 ToTyped,
1038)]
1039#[repr(C, u8)]
1040pub enum GenericScale<Number> {
1044 None,
1046 Scale(Number, Number, Number),
1048}
1049
1050pub use self::GenericScale as Scale;
1051
1052impl<Number> ToCss for Scale<Number>
1053where
1054 Number: ToCss + PartialEq + Clone + ToFloat,
1055{
1056 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1057 where
1058 W: fmt::Write,
1059 {
1060 match *self {
1061 Scale::None => dest.write_str("none"),
1062 Scale::Scale(ref x, ref y, ref z) => {
1063 x.to_css(dest)?;
1064
1065 let serialize_z = z.to_f32() != Ok(1.0);
1066 if serialize_z || x != y {
1067 dest.write_char(' ')?;
1068 y.to_css(dest)?;
1069 }
1070
1071 if serialize_z {
1072 dest.write_char(' ')?;
1073 z.to_css(dest)?;
1074 }
1075 Ok(())
1076 },
1077 }
1078 }
1079}
1080
1081#[inline]
1082fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
1083 _: &LengthPercentage,
1084 y: &LengthPercentage,
1085 z: &Length,
1086) -> bool {
1087 y.is_zero_no_percent() && z.is_zero()
1088}
1089
1090#[derive(
1091 Clone,
1092 Debug,
1093 Deserialize,
1094 MallocSizeOf,
1095 PartialEq,
1096 Serialize,
1097 SpecifiedValueInfo,
1098 ToAnimatedValue,
1099 ToAnimatedZero,
1100 ToComputedValue,
1101 ToCss,
1102 ToResolvedValue,
1103 ToShmem,
1104 ToTyped,
1105)]
1106#[repr(C, u8)]
1107pub enum GenericTranslate<LengthPercentage, Length>
1121where
1122 LengthPercentage: Zero + ZeroNoPercent,
1123 Length: Zero,
1124{
1125 None,
1127 Translate(
1129 LengthPercentage,
1130 #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
1131 #[css(skip_if = "Zero::is_zero")] Length,
1132 ),
1133}
1134
1135pub use self::GenericTranslate as Translate;
1136
1137#[allow(missing_docs)]
1138#[derive(
1139 Clone,
1140 Copy,
1141 Debug,
1142 MallocSizeOf,
1143 Parse,
1144 PartialEq,
1145 SpecifiedValueInfo,
1146 ToComputedValue,
1147 ToCss,
1148 ToResolvedValue,
1149 ToShmem,
1150 ToTyped,
1151)]
1152#[repr(u8)]
1153pub enum TransformStyle {
1154 Flat,
1155 #[css(keyword = "preserve-3d")]
1156 Preserve3d,
1157}