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#[cfg_attr(rustfmt, 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#[cfg_attr(rustfmt, 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#[cfg_attr(rustfmt, 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 #[cfg_attr(rustfmt, 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 #[cfg_attr(rustfmt, 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(ref 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 #[cfg_attr(rustfmt, 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 #[cfg_attr(rustfmt, 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 use std::{f32, f64};
831 let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
832 Transform3D::new(
833 cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
834 cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
835 cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
836 cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
837 )
838 };
839
840 let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
841 Ok((cast_3d_transform(m), is_3d))
842 }
843
844 pub fn to_transform_3d_matrix_f64(
846 &self,
847 reference_box: Option<&Rect<ComputedLength>>,
848 ) -> Result<(Transform3D<f64>, bool), ()> {
849 Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
850 }
851
852 fn components_to_transform_3d_matrix_f64(
854 ops: &[T],
855 reference_box: Option<&Rect<ComputedLength>>,
856 ) -> Result<(Transform3D<f64>, bool), ()> {
857 let mut transform = Transform3D::<f64>::identity();
864 let mut contain_3d = false;
865
866 for operation in ops {
867 let matrix = operation.to_3d_matrix(reference_box)?;
868 contain_3d = contain_3d || operation.is_3d();
869 transform = matrix.then(&transform);
870 }
871
872 Ok((transform, contain_3d))
873 }
874}
875
876#[inline]
878pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
879 if d.is_finite() {
880 Transform3D::perspective(d.max(1.))
881 } else {
882 Transform3D::identity()
883 }
884}
885
886pub fn get_normalized_vector_and_angle<T: Zero>(
888 x: CSSFloat,
889 y: CSSFloat,
890 z: CSSFloat,
891 angle: T,
892) -> (CSSFloat, CSSFloat, CSSFloat, T) {
893 use crate::values::computed::transform::DirectionVector;
894 use euclid::approxeq::ApproxEq;
895 let vector = DirectionVector::new(x, y, z);
896 if vector.square_length().approx_eq(&f32::zero()) {
897 (0., 0., 1., T::zero())
901 } else {
902 let vector = vector.robust_normalize();
903 (vector.x, vector.y, vector.z, angle)
904 }
905}
906
907#[derive(
908 Clone,
909 Copy,
910 Debug,
911 Deserialize,
912 MallocSizeOf,
913 PartialEq,
914 Serialize,
915 SpecifiedValueInfo,
916 ToAnimatedValue,
917 ToAnimatedZero,
918 ToComputedValue,
919 ToResolvedValue,
920 ToShmem,
921 ToTyped,
922)]
923#[repr(C, u8)]
924#[typed(todo_derive_fields)]
925pub enum GenericRotate<Number, Angle> {
929 None,
931 Rotate(Angle),
933 Rotate3D(Number, Number, Number, Angle),
935}
936
937pub use self::GenericRotate as Rotate;
938
939pub trait IsParallelTo {
942 fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
944}
945
946impl<Number, Angle> ToCss for Rotate<Number, Angle>
947where
948 Number: Clone + PartialOrd + ToCss + Zero,
949 Angle: Clone + Neg<Output = Angle> + ToCss + Zero,
950 (Number, Number, Number): IsParallelTo,
951{
952 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
953 where
954 W: fmt::Write,
955 {
956 use crate::values::computed::transform::DirectionVector;
957 match *self {
958 Rotate::None => dest.write_str("none"),
959 Rotate::Rotate(ref angle) => angle.to_css(dest),
960 Rotate::Rotate3D(ref x, ref y, ref z, ref angle) => {
961 let v = (x.clone(), y.clone(), z.clone());
970 let (axis, angle) = if v.0.is_zero() && v.1.is_zero() && v.2.is_zero() {
971 (None, angle.clone())
978 } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
979 (
980 Some("x "),
981 if v.0 < Number::zero() {
982 -angle.clone()
983 } else {
984 angle.clone()
985 },
986 )
987 } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
988 (
989 Some("y "),
990 if v.1 < Number::zero() {
991 -angle.clone()
992 } else {
993 angle.clone()
994 },
995 )
996 } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
997 let angle = if v.2 < Number::zero() {
999 -angle.clone()
1000 } else {
1001 angle.clone()
1002 };
1003 return angle.to_css(dest);
1004 } else {
1005 (None, angle.clone())
1006 };
1007 match axis {
1008 Some(a) => dest.write_str(a)?,
1009 None => {
1010 x.to_css(dest)?;
1011 dest.write_char(' ')?;
1012 y.to_css(dest)?;
1013 dest.write_char(' ')?;
1014 z.to_css(dest)?;
1015 dest.write_char(' ')?;
1016 },
1017 }
1018 angle.to_css(dest)
1019 },
1020 }
1021 }
1022}
1023
1024#[derive(
1025 Clone,
1026 Copy,
1027 Debug,
1028 Deserialize,
1029 MallocSizeOf,
1030 PartialEq,
1031 Serialize,
1032 SpecifiedValueInfo,
1033 ToAnimatedValue,
1034 ToAnimatedZero,
1035 ToComputedValue,
1036 ToResolvedValue,
1037 ToShmem,
1038 ToTyped,
1039)]
1040#[repr(C, u8)]
1041pub enum GenericScale<Number> {
1045 None,
1047 Scale(Number, Number, Number),
1049}
1050
1051pub use self::GenericScale as Scale;
1052
1053impl<Number> ToCss for Scale<Number>
1054where
1055 Number: ToCss + PartialEq + Clone + ToFloat,
1056{
1057 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1058 where
1059 W: fmt::Write,
1060 {
1061 match *self {
1062 Scale::None => dest.write_str("none"),
1063 Scale::Scale(ref x, ref y, ref z) => {
1064 x.to_css(dest)?;
1065
1066 let serialize_z = z.to_f32() != Ok(1.0);
1067 if serialize_z || x != y {
1068 dest.write_char(' ')?;
1069 y.to_css(dest)?;
1070 }
1071
1072 if serialize_z {
1073 dest.write_char(' ')?;
1074 z.to_css(dest)?;
1075 }
1076 Ok(())
1077 },
1078 }
1079 }
1080}
1081
1082#[inline]
1083fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
1084 _: &LengthPercentage,
1085 y: &LengthPercentage,
1086 z: &Length,
1087) -> bool {
1088 y.is_zero_no_percent() && z.is_zero()
1089}
1090
1091#[derive(
1092 Clone,
1093 Debug,
1094 Deserialize,
1095 MallocSizeOf,
1096 PartialEq,
1097 Serialize,
1098 SpecifiedValueInfo,
1099 ToAnimatedValue,
1100 ToAnimatedZero,
1101 ToComputedValue,
1102 ToCss,
1103 ToResolvedValue,
1104 ToShmem,
1105 ToTyped,
1106)]
1107#[repr(C, u8)]
1108pub enum GenericTranslate<LengthPercentage, Length>
1122where
1123 LengthPercentage: Zero + ZeroNoPercent,
1124 Length: Zero,
1125{
1126 None,
1128 Translate(
1130 LengthPercentage,
1131 #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
1132 #[css(skip_if = "Zero::is_zero")] Length,
1133 ),
1134}
1135
1136pub use self::GenericTranslate as Translate;
1137
1138#[allow(missing_docs)]
1139#[derive(
1140 Clone,
1141 Copy,
1142 Debug,
1143 MallocSizeOf,
1144 Parse,
1145 PartialEq,
1146 SpecifiedValueInfo,
1147 ToComputedValue,
1148 ToCss,
1149 ToResolvedValue,
1150 ToShmem,
1151 ToTyped,
1152)]
1153#[repr(u8)]
1154pub enum TransformStyle {
1155 Flat,
1156 #[css(keyword = "preserve-3d")]
1157 Preserve3d,
1158}