1use super::{Property, PropertyId};
4use crate::context::PropertyHandlerContext;
5use crate::declaration::DeclarationList;
6use crate::error::{ParserError, PrinterError};
7use crate::macros::enum_property;
8use crate::prefixes::Feature;
9use crate::printer::Printer;
10use crate::traits::{Parse, PropertyHandler, ToCss, Zero};
11use crate::values::{
12 angle::Angle,
13 length::{Length, LengthPercentage},
14 percentage::NumberOrPercentage,
15};
16use crate::vendor_prefix::VendorPrefix;
17#[cfg(feature = "visitor")]
18use crate::visitor::Visit;
19use cssparser::*;
20use std::f32::consts::PI;
21
22#[derive(Debug, Clone, PartialEq, Default)]
24#[cfg_attr(feature = "visitor", derive(Visit))]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
26#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
27#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
28pub struct TransformList(pub Vec<Transform>);
29
30impl<'i> Parse<'i> for TransformList {
31 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
32 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
33 return Ok(TransformList(vec![]));
34 }
35
36 input.skip_whitespace();
37 let mut results = vec![Transform::parse(input)?];
38 loop {
39 input.skip_whitespace();
40 if let Ok(item) = input.try_parse(Transform::parse) {
41 results.push(item);
42 } else {
43 return Ok(TransformList(results));
44 }
45 }
46 }
47}
48
49impl ToCss for TransformList {
50 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
51 where
52 W: std::fmt::Write,
53 {
54 if self.0.is_empty() {
55 dest.write_str("none")?;
56 return Ok(());
57 }
58
59 self.to_css_base(dest)
121 }
122}
123
124impl TransformList {
125 fn to_css_base<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
126 where
127 W: std::fmt::Write,
128 {
129 let mut first = true;
130 for item in &self.0 {
131 if first {
132 first = false;
133 } else {
134 dest.whitespace()?;
135 }
136 item.to_css(dest)?;
137 }
138 Ok(())
139 }
140
141 pub fn to_matrix(&self) -> Option<Matrix3d<f32>> {
143 let mut matrix = Matrix3d::identity();
144 for transform in &self.0 {
145 if let Some(m) = transform.to_matrix() {
146 matrix = m.multiply(&matrix);
147 } else {
148 return None;
149 }
150 }
151 Some(matrix)
152 }
153}
154
155#[derive(Debug, Clone, PartialEq)]
157#[cfg_attr(feature = "visitor", derive(Visit))]
158#[cfg_attr(
159 feature = "serde",
160 derive(serde::Serialize, serde::Deserialize),
161 serde(tag = "type", content = "value", rename_all = "camelCase")
162)]
163#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
164#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
165pub enum Transform {
166 Translate(LengthPercentage, LengthPercentage),
168 TranslateX(LengthPercentage),
170 TranslateY(LengthPercentage),
172 TranslateZ(Length),
174 Translate3d(LengthPercentage, LengthPercentage, Length),
176 Scale(NumberOrPercentage, NumberOrPercentage),
178 ScaleX(NumberOrPercentage),
180 ScaleY(NumberOrPercentage),
182 ScaleZ(NumberOrPercentage),
184 Scale3d(NumberOrPercentage, NumberOrPercentage, NumberOrPercentage),
186 Rotate(Angle),
188 RotateX(Angle),
190 RotateY(Angle),
192 RotateZ(Angle),
194 Rotate3d(f32, f32, f32, Angle),
196 Skew(Angle, Angle),
198 SkewX(Angle),
200 SkewY(Angle),
202 Perspective(Length),
204 Matrix(Matrix<f32>),
206 Matrix3d(Matrix3d<f32>),
208}
209
210#[derive(Debug, Clone, PartialEq)]
212#[cfg_attr(feature = "visitor", derive(Visit))]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
215#[allow(missing_docs)]
216#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
217pub struct Matrix<T> {
218 pub a: T,
219 pub b: T,
220 pub c: T,
221 pub d: T,
222 pub e: T,
223 pub f: T,
224}
225
226impl Matrix<f32> {
227 pub fn to_matrix3d(&self) -> Matrix3d<f32> {
229 Matrix3d {
230 m11: self.a,
231 m12: self.b,
232 m13: 0.0,
233 m14: 0.0,
234 m21: self.c,
235 m22: self.d,
236 m23: 0.0,
237 m24: 0.0,
238 m31: 0.0,
239 m32: 0.0,
240 m33: 1.0,
241 m34: 0.0,
242 m41: self.e,
243 m42: self.f,
244 m43: 0.0,
245 m44: 1.0,
246 }
247 }
248}
249
250#[derive(Debug, Clone, PartialEq)]
252#[cfg_attr(feature = "visitor", derive(Visit))]
253#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
255#[allow(missing_docs)]
256pub struct Matrix3d<T> {
257 pub m11: T,
258 pub m12: T,
259 pub m13: T,
260 pub m14: T,
261 pub m21: T,
262 pub m22: T,
263 pub m23: T,
264 pub m24: T,
265 pub m31: T,
266 pub m32: T,
267 pub m33: T,
268 pub m34: T,
269 pub m41: T,
270 pub m42: T,
271 pub m43: T,
272 pub m44: T,
273}
274
275impl Matrix3d<f32> {
277 pub fn identity() -> Matrix3d<f32> {
279 Matrix3d {
280 m11: 1.0,
281 m12: 0.0,
282 m13: 0.0,
283 m14: 0.0,
284 m21: 0.0,
285 m22: 1.0,
286 m23: 0.0,
287 m24: 0.0,
288 m31: 0.0,
289 m32: 0.0,
290 m33: 1.0,
291 m34: 0.0,
292 m41: 0.0,
293 m42: 0.0,
294 m43: 0.0,
295 m44: 1.0,
296 }
297 }
298
299 pub fn translate(x: f32, y: f32, z: f32) -> Matrix3d<f32> {
301 Matrix3d {
302 m11: 1.0,
303 m12: 0.0,
304 m13: 0.0,
305 m14: 0.0,
306 m21: 0.0,
307 m22: 1.0,
308 m23: 0.0,
309 m24: 0.0,
310 m31: 0.0,
311 m32: 0.0,
312 m33: 1.0,
313 m34: 0.0,
314 m41: x,
315 m42: y,
316 m43: z,
317 m44: 1.0,
318 }
319 }
320
321 pub fn scale(x: f32, y: f32, z: f32) -> Matrix3d<f32> {
323 Matrix3d {
324 m11: x,
325 m12: 0.0,
326 m13: 0.0,
327 m14: 0.0,
328 m21: 0.0,
329 m22: y,
330 m23: 0.0,
331 m24: 0.0,
332 m31: 0.0,
333 m32: 0.0,
334 m33: z,
335 m34: 0.0,
336 m41: 0.0,
337 m42: 0.0,
338 m43: 0.0,
339 m44: 1.0,
340 }
341 }
342
343 pub fn rotate(x: f32, y: f32, z: f32, angle: f32) -> Matrix3d<f32> {
345 let length = (x * x + y * y + z * z).sqrt();
347 if length == 0.0 {
348 return Matrix3d::identity();
350 }
351
352 let x = x / length;
353 let y = y / length;
354 let z = z / length;
355
356 let half_angle = angle / 2.0;
357 let sin = half_angle.sin();
358 let sc = sin * half_angle.cos();
359 let sq = sin * sin;
360 let m11 = 1.0 - 2.0 * (y * y + z * z) * sq;
361 let m12 = 2.0 * (x * y * sq + z * sc);
362 let m13 = 2.0 * (x * z * sq - y * sc);
363 let m21 = 2.0 * (x * y * sq - z * sc);
364 let m22 = 1.0 - 2.0 * (x * x + z * z) * sq;
365 let m23 = 2.0 * (y * z * sq + x * sc);
366 let m31 = 2.0 * (x * z * sq + y * sc);
367 let m32 = 2.0 * (y * z * sq - x * sc);
368 let m33 = 1.0 - 2.0 * (x * x + y * y) * sq;
369 Matrix3d {
370 m11,
371 m12,
372 m13,
373 m14: 0.0,
374 m21,
375 m22,
376 m23,
377 m24: 0.0,
378 m31,
379 m32,
380 m33,
381 m34: 0.0,
382 m41: 0.0,
383 m42: 0.0,
384 m43: 0.0,
385 m44: 1.0,
386 }
387 }
388
389 pub fn skew(a: f32, b: f32) -> Matrix3d<f32> {
391 Matrix3d {
392 m11: 1.0,
393 m12: b.tan(),
394 m13: 0.0,
395 m14: 0.0,
396 m21: a.tan(),
397 m22: 1.0,
398 m23: 0.0,
399 m24: 0.0,
400 m31: 0.0,
401 m32: 0.0,
402 m33: 1.0,
403 m34: 0.0,
404 m41: 0.0,
405 m42: 0.0,
406 m43: 0.0,
407 m44: 1.0,
408 }
409 }
410
411 pub fn perspective(d: f32) -> Matrix3d<f32> {
413 Matrix3d {
414 m11: 1.0,
415 m12: 0.0,
416 m13: 0.0,
417 m14: 0.0,
418 m21: 0.0,
419 m22: 1.0,
420 m23: 0.0,
421 m24: 0.0,
422 m31: 0.0,
423 m32: 0.0,
424 m33: 1.0,
425 m34: -1.0 / d,
426 m41: 0.0,
427 m42: 0.0,
428 m43: 0.0,
429 m44: 1.0,
430 }
431 }
432
433 pub fn multiply(&self, other: &Self) -> Self {
435 Matrix3d {
436 m11: self.m11 * other.m11 + self.m12 * other.m21 + self.m13 * other.m31 + self.m14 * other.m41,
437 m12: self.m11 * other.m12 + self.m12 * other.m22 + self.m13 * other.m32 + self.m14 * other.m42,
438 m13: self.m11 * other.m13 + self.m12 * other.m23 + self.m13 * other.m33 + self.m14 * other.m43,
439 m14: self.m11 * other.m14 + self.m12 * other.m24 + self.m13 * other.m34 + self.m14 * other.m44,
440 m21: self.m21 * other.m11 + self.m22 * other.m21 + self.m23 * other.m31 + self.m24 * other.m41,
441 m22: self.m21 * other.m12 + self.m22 * other.m22 + self.m23 * other.m32 + self.m24 * other.m42,
442 m23: self.m21 * other.m13 + self.m22 * other.m23 + self.m23 * other.m33 + self.m24 * other.m43,
443 m24: self.m21 * other.m14 + self.m22 * other.m24 + self.m23 * other.m34 + self.m24 * other.m44,
444 m31: self.m31 * other.m11 + self.m32 * other.m21 + self.m33 * other.m31 + self.m34 * other.m41,
445 m32: self.m31 * other.m12 + self.m32 * other.m22 + self.m33 * other.m32 + self.m34 * other.m42,
446 m33: self.m31 * other.m13 + self.m32 * other.m23 + self.m33 * other.m33 + self.m34 * other.m43,
447 m34: self.m31 * other.m14 + self.m32 * other.m24 + self.m33 * other.m34 + self.m34 * other.m44,
448 m41: self.m41 * other.m11 + self.m42 * other.m21 + self.m43 * other.m31 + self.m44 * other.m41,
449 m42: self.m41 * other.m12 + self.m42 * other.m22 + self.m43 * other.m32 + self.m44 * other.m42,
450 m43: self.m41 * other.m13 + self.m42 * other.m23 + self.m43 * other.m33 + self.m44 * other.m43,
451 m44: self.m41 * other.m14 + self.m42 * other.m24 + self.m43 * other.m34 + self.m44 * other.m44,
452 }
453 }
454
455 pub fn is_2d(&self) -> bool {
457 self.m31 == 0.0
458 && self.m32 == 0.0
459 && self.m13 == 0.0
460 && self.m23 == 0.0
461 && self.m43 == 0.0
462 && self.m14 == 0.0
463 && self.m24 == 0.0
464 && self.m34 == 0.0
465 && self.m33 == 1.0
466 && self.m44 == 1.0
467 }
468
469 pub fn to_matrix2d(&self) -> Option<Matrix<f32>> {
472 if self.is_2d() {
473 return Some(Matrix {
474 a: self.m11,
475 b: self.m12,
476 c: self.m21,
477 d: self.m22,
478 e: self.m41,
479 f: self.m42,
480 });
481 }
482 None
483 }
484
485 pub fn scale_by_factor(&mut self, scaling_factor: f32) {
487 self.m11 *= scaling_factor;
488 self.m12 *= scaling_factor;
489 self.m13 *= scaling_factor;
490 self.m14 *= scaling_factor;
491 self.m21 *= scaling_factor;
492 self.m22 *= scaling_factor;
493 self.m23 *= scaling_factor;
494 self.m24 *= scaling_factor;
495 self.m31 *= scaling_factor;
496 self.m32 *= scaling_factor;
497 self.m33 *= scaling_factor;
498 self.m34 *= scaling_factor;
499 self.m41 *= scaling_factor;
500 self.m42 *= scaling_factor;
501 self.m43 *= scaling_factor;
502 self.m44 *= scaling_factor;
503 }
504
505 pub fn determinant(&self) -> f32 {
507 self.m14 * self.m23 * self.m32 * self.m41
508 - self.m13 * self.m24 * self.m32 * self.m41
509 - self.m14 * self.m22 * self.m33 * self.m41
510 + self.m12 * self.m24 * self.m33 * self.m41
511 + self.m13 * self.m22 * self.m34 * self.m41
512 - self.m12 * self.m23 * self.m34 * self.m41
513 - self.m14 * self.m23 * self.m31 * self.m42
514 + self.m13 * self.m24 * self.m31 * self.m42
515 + self.m14 * self.m21 * self.m33 * self.m42
516 - self.m11 * self.m24 * self.m33 * self.m42
517 - self.m13 * self.m21 * self.m34 * self.m42
518 + self.m11 * self.m23 * self.m34 * self.m42
519 + self.m14 * self.m22 * self.m31 * self.m43
520 - self.m12 * self.m24 * self.m31 * self.m43
521 - self.m14 * self.m21 * self.m32 * self.m43
522 + self.m11 * self.m24 * self.m32 * self.m43
523 + self.m12 * self.m21 * self.m34 * self.m43
524 - self.m11 * self.m22 * self.m34 * self.m43
525 - self.m13 * self.m22 * self.m31 * self.m44
526 + self.m12 * self.m23 * self.m31 * self.m44
527 + self.m13 * self.m21 * self.m32 * self.m44
528 - self.m11 * self.m23 * self.m32 * self.m44
529 - self.m12 * self.m21 * self.m33 * self.m44
530 + self.m11 * self.m22 * self.m33 * self.m44
531 }
532
533 pub fn inverse(&self) -> Option<Matrix3d<f32>> {
535 let mut det = self.determinant();
536 if det == 0.0 {
537 return None;
538 }
539
540 det = 1.0 / det;
541 Some(Matrix3d {
542 m11: det
543 * (self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42 + self.m24 * self.m32 * self.m43
544 - self.m22 * self.m34 * self.m43
545 - self.m23 * self.m32 * self.m44
546 + self.m22 * self.m33 * self.m44),
547 m12: det
548 * (self.m14 * self.m33 * self.m42 - self.m13 * self.m34 * self.m42 - self.m14 * self.m32 * self.m43
549 + self.m12 * self.m34 * self.m43
550 + self.m13 * self.m32 * self.m44
551 - self.m12 * self.m33 * self.m44),
552 m13: det
553 * (self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42 + self.m14 * self.m22 * self.m43
554 - self.m12 * self.m24 * self.m43
555 - self.m13 * self.m22 * self.m44
556 + self.m12 * self.m23 * self.m44),
557 m14: det
558 * (self.m14 * self.m23 * self.m32 - self.m13 * self.m24 * self.m32 - self.m14 * self.m22 * self.m33
559 + self.m12 * self.m24 * self.m33
560 + self.m13 * self.m22 * self.m34
561 - self.m12 * self.m23 * self.m34),
562 m21: det
563 * (self.m24 * self.m33 * self.m41 - self.m23 * self.m34 * self.m41 - self.m24 * self.m31 * self.m43
564 + self.m21 * self.m34 * self.m43
565 + self.m23 * self.m31 * self.m44
566 - self.m21 * self.m33 * self.m44),
567 m22: det
568 * (self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41 + self.m14 * self.m31 * self.m43
569 - self.m11 * self.m34 * self.m43
570 - self.m13 * self.m31 * self.m44
571 + self.m11 * self.m33 * self.m44),
572 m23: det
573 * (self.m14 * self.m23 * self.m41 - self.m13 * self.m24 * self.m41 - self.m14 * self.m21 * self.m43
574 + self.m11 * self.m24 * self.m43
575 + self.m13 * self.m21 * self.m44
576 - self.m11 * self.m23 * self.m44),
577 m24: det
578 * (self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31 + self.m14 * self.m21 * self.m33
579 - self.m11 * self.m24 * self.m33
580 - self.m13 * self.m21 * self.m34
581 + self.m11 * self.m23 * self.m34),
582 m31: det
583 * (self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41 + self.m24 * self.m31 * self.m42
584 - self.m21 * self.m34 * self.m42
585 - self.m22 * self.m31 * self.m44
586 + self.m21 * self.m32 * self.m44),
587 m32: det
588 * (self.m14 * self.m32 * self.m41 - self.m12 * self.m34 * self.m41 - self.m14 * self.m31 * self.m42
589 + self.m11 * self.m34 * self.m42
590 + self.m12 * self.m31 * self.m44
591 - self.m11 * self.m32 * self.m44),
592 m33: det
593 * (self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 + self.m14 * self.m21 * self.m42
594 - self.m11 * self.m24 * self.m42
595 - self.m12 * self.m21 * self.m44
596 + self.m11 * self.m22 * self.m44),
597 m34: det
598 * (self.m14 * self.m22 * self.m31 - self.m12 * self.m24 * self.m31 - self.m14 * self.m21 * self.m32
599 + self.m11 * self.m24 * self.m32
600 + self.m12 * self.m21 * self.m34
601 - self.m11 * self.m22 * self.m34),
602 m41: det
603 * (self.m23 * self.m32 * self.m41 - self.m22 * self.m33 * self.m41 - self.m23 * self.m31 * self.m42
604 + self.m21 * self.m33 * self.m42
605 + self.m22 * self.m31 * self.m43
606 - self.m21 * self.m32 * self.m43),
607 m42: det
608 * (self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41 + self.m13 * self.m31 * self.m42
609 - self.m11 * self.m33 * self.m42
610 - self.m12 * self.m31 * self.m43
611 + self.m11 * self.m32 * self.m43),
612 m43: det
613 * (self.m13 * self.m22 * self.m41 - self.m12 * self.m23 * self.m41 - self.m13 * self.m21 * self.m42
614 + self.m11 * self.m23 * self.m42
615 + self.m12 * self.m21 * self.m43
616 - self.m11 * self.m22 * self.m43),
617 m44: det
618 * (self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31 + self.m13 * self.m21 * self.m32
619 - self.m11 * self.m23 * self.m32
620 - self.m12 * self.m21 * self.m33
621 + self.m11 * self.m22 * self.m33),
622 })
623 }
624
625 pub fn transpose(&self) -> Self {
627 Self {
628 m11: self.m11,
629 m12: self.m21,
630 m13: self.m31,
631 m14: self.m41,
632 m21: self.m12,
633 m22: self.m22,
634 m23: self.m32,
635 m24: self.m42,
636 m31: self.m13,
637 m32: self.m23,
638 m33: self.m33,
639 m34: self.m43,
640 m41: self.m14,
641 m42: self.m24,
642 m43: self.m34,
643 m44: self.m44,
644 }
645 }
646
647 pub fn multiply_vector(&self, pin: &[f32; 4]) -> [f32; 4] {
649 [
650 pin[0] * self.m11 + pin[1] * self.m21 + pin[2] * self.m31 + pin[3] * self.m41,
651 pin[0] * self.m12 + pin[1] * self.m22 + pin[2] * self.m32 + pin[3] * self.m42,
652 pin[0] * self.m13 + pin[1] * self.m23 + pin[2] * self.m33 + pin[3] * self.m43,
653 pin[0] * self.m14 + pin[1] * self.m24 + pin[2] * self.m34 + pin[3] * self.m44,
654 ]
655 }
656
657 pub fn decompose(&self) -> Option<TransformList> {
659 let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
662 [
663 (ascl * a[0]) + (bscl * b[0]),
664 (ascl * a[1]) + (bscl * b[1]),
665 (ascl * a[2]) + (bscl * b[2]),
666 ]
667 };
668
669 let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
671
672 let cross = |row1: [f32; 3], row2: [f32; 3]| {
674 [
675 row1[1] * row2[2] - row1[2] * row2[1],
676 row1[2] * row2[0] - row1[0] * row2[2],
677 row1[0] * row2[1] - row1[1] * row2[0],
678 ]
679 };
680
681 if self.m44 == 0.0 {
682 return None;
683 }
684
685 let scaling_factor = self.m44;
686
687 let mut matrix = self.clone();
689 matrix.scale_by_factor(1.0 / scaling_factor);
690
691 let mut perspective_matrix = matrix.clone();
694 perspective_matrix.m14 = 0.0;
695 perspective_matrix.m24 = 0.0;
696 perspective_matrix.m34 = 0.0;
697 perspective_matrix.m44 = 1.0;
698
699 if perspective_matrix.determinant() == 0.0 {
700 return None;
701 }
702
703 let mut transforms = vec![];
704
705 if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
707 let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
708
709 perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
710 let perspective = perspective_matrix.multiply_vector(&right_hand_side);
711 if perspective[0] == 0.0 && perspective[1] == 0.0 && perspective[3] == 0.0 {
712 transforms.push(Transform::Perspective(Length::px(-1.0 / perspective[2])))
713 } else {
714 return None;
715 }
716 }
717
718 if matrix.m41 != 0.0 || matrix.m42 != 0.0 || matrix.m43 != 0.0 {
721 transforms.push(Transform::Translate3d(
722 LengthPercentage::px(matrix.m41),
723 LengthPercentage::px(matrix.m42),
724 Length::px(matrix.m43),
725 ));
726 }
727
728 let mut row = [
730 [matrix.m11, matrix.m12, matrix.m13],
731 [matrix.m21, matrix.m22, matrix.m23],
732 [matrix.m31, matrix.m32, matrix.m33],
733 ];
734
735 let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
737 let mut scale_x = row0len;
738 row[0] = [row[0][0] / row0len, row[0][1] / row0len, row[0][2] / row0len];
739
740 let mut skew_x = dot(row[0], row[1]);
742 row[1] = combine(row[1], row[0], 1.0, -skew_x);
743
744 let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
746 let mut scale_y = row1len;
747 row[1] = [row[1][0] / row1len, row[1][1] / row1len, row[1][2] / row1len];
748 skew_x /= scale_y;
749
750 let mut skew_y = dot(row[0], row[2]);
752 row[2] = combine(row[2], row[0], 1.0, -skew_y);
753 let mut skew_z = dot(row[1], row[2]);
754 row[2] = combine(row[2], row[1], 1.0, -skew_z);
755
756 let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
758 let mut scale_z = row2len;
759 row[2] = [row[2][0] / row2len, row[2][1] / row2len, row[2][2] / row2len];
760 skew_y /= scale_z;
761 skew_z /= scale_z;
762
763 if skew_z != 0.0 {
764 return None; }
766
767 macro_rules! round {
769 ($var: ident) => {
770 $var = ($var * 100000.0).round() / 100000.0;
771 };
772 }
773
774 round!(skew_x);
775 round!(skew_y);
776 round!(skew_z);
777
778 if skew_x != 0.0 || skew_y != 0.0 || skew_z != 0.0 {
779 transforms.push(Transform::Skew(Angle::Rad(skew_x), Angle::Rad(skew_y)));
780 }
781
782 if dot(row[0], cross(row[1], row[2])) < 0.0 {
786 scale_x = -scale_x;
787 scale_y = -scale_y;
788 scale_z = -scale_z;
789 for i in 0..3 {
790 row[i][0] *= -1.0;
791 row[i][1] *= -1.0;
792 row[i][2] *= -1.0;
793 }
794 }
795
796 round!(scale_x);
797 round!(scale_y);
798 round!(scale_z);
799
800 if scale_x != 1.0 || scale_y != 1.0 || scale_z != 1.0 {
801 transforms.push(Transform::Scale3d(
802 NumberOrPercentage::Number(scale_x),
803 NumberOrPercentage::Number(scale_y),
804 NumberOrPercentage::Number(scale_z),
805 ))
806 }
807
808 let mut rotate_x = 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0)).sqrt();
810 let mut rotate_y = 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0)).sqrt();
811 let mut rotate_z = 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0)).sqrt();
812 let rotate_w = 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0)).sqrt();
813
814 if row[2][1] > row[1][2] {
815 rotate_x = -rotate_x
816 }
817
818 if row[0][2] > row[2][0] {
819 rotate_y = -rotate_y
820 }
821
822 if row[1][0] > row[0][1] {
823 rotate_z = -rotate_z
824 }
825
826 let len = (rotate_x * rotate_x + rotate_y * rotate_y + rotate_z * rotate_z).sqrt();
827 if len != 0.0 {
828 rotate_x /= len;
829 rotate_y /= len;
830 rotate_z /= len;
831 }
832 let a = 2.0 * len.atan2(rotate_w);
833
834 let max = rotate_x.max(rotate_y).max(rotate_z);
836 rotate_x /= max;
837 rotate_y /= max;
838 rotate_z /= max;
839
840 if a != 0.0 {
841 transforms.push(Transform::Rotate3d(rotate_x, rotate_y, rotate_z, Angle::Rad(a)))
842 }
843
844 if transforms.is_empty() {
845 return None;
846 }
847
848 Some(TransformList(transforms))
849 }
850}
851
852impl<'i> Parse<'i> for Transform {
853 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
854 let function = input.expect_function()?.clone();
855 input.parse_nested_block(|input| {
856 let location = input.current_source_location();
857 match_ignore_ascii_case! { &function,
858 "matrix" => {
859 let a = f32::parse(input)?;
860 input.expect_comma()?;
861 let b = f32::parse(input)?;
862 input.expect_comma()?;
863 let c = f32::parse(input)?;
864 input.expect_comma()?;
865 let d = f32::parse(input)?;
866 input.expect_comma()?;
867 let e = f32::parse(input)?;
868 input.expect_comma()?;
869 let f = f32::parse(input)?;
870 Ok(Transform::Matrix(Matrix { a, b, c, d, e, f }))
871 },
872 "matrix3d" => {
873 let m11 = f32::parse(input)?;
874 input.expect_comma()?;
875 let m12 = f32::parse(input)?;
876 input.expect_comma()?;
877 let m13 = f32::parse(input)?;
878 input.expect_comma()?;
879 let m14 = f32::parse(input)?;
880 input.expect_comma()?;
881 let m21 = f32::parse(input)?;
882 input.expect_comma()?;
883 let m22 = f32::parse(input)?;
884 input.expect_comma()?;
885 let m23 = f32::parse(input)?;
886 input.expect_comma()?;
887 let m24 = f32::parse(input)?;
888 input.expect_comma()?;
889 let m31 = f32::parse(input)?;
890 input.expect_comma()?;
891 let m32 = f32::parse(input)?;
892 input.expect_comma()?;
893 let m33 = f32::parse(input)?;
894 input.expect_comma()?;
895 let m34 = f32::parse(input)?;
896 input.expect_comma()?;
897 let m41 = f32::parse(input)?;
898 input.expect_comma()?;
899 let m42 = f32::parse(input)?;
900 input.expect_comma()?;
901 let m43 = f32::parse(input)?;
902 input.expect_comma()?;
903 let m44 = f32::parse(input)?;
904 Ok(Transform::Matrix3d(Matrix3d {
905 m11, m12, m13, m14,
906 m21, m22, m23, m24,
907 m31, m32, m33, m34,
908 m41, m42, m43, m44
909 }))
910 },
911 "translate" => {
912 let x = LengthPercentage::parse(input)?;
913 if input.try_parse(|input| input.expect_comma()).is_ok() {
914 let y = LengthPercentage::parse(input)?;
915 Ok(Transform::Translate(x, y))
916 } else {
917 Ok(Transform::Translate(x, LengthPercentage::zero()))
918 }
919 },
920 "translatex" => {
921 let x = LengthPercentage::parse(input)?;
922 Ok(Transform::TranslateX(x))
923 },
924 "translatey" => {
925 let y = LengthPercentage::parse(input)?;
926 Ok(Transform::TranslateY(y))
927 },
928 "translatez" => {
929 let z = Length::parse(input)?;
930 Ok(Transform::TranslateZ(z))
931 },
932 "translate3d" => {
933 let x = LengthPercentage::parse(input)?;
934 input.expect_comma()?;
935 let y = LengthPercentage::parse(input)?;
936 input.expect_comma()?;
937 let z = Length::parse(input)?;
938 Ok(Transform::Translate3d(x, y, z))
939 },
940 "scale" => {
941 let x = convert_percentage_to_number(input)?;
942 if input.try_parse(|input| input.expect_comma()).is_ok() {
943 let y = convert_percentage_to_number(input)?;
944 Ok(Transform::Scale(x, y))
945 } else {
946 Ok(Transform::Scale(x.clone(), x))
947 }
948 },
949 "scalex" => {
950 let x = convert_percentage_to_number(input)?;
951 Ok(Transform::ScaleX(x))
952 },
953 "scaley" => {
954 let y = convert_percentage_to_number(input)?;
955 Ok(Transform::ScaleY(y))
956 },
957 "scalez" => {
958 let z = convert_percentage_to_number(input)?;
959 Ok(Transform::ScaleZ(z))
960 },
961 "scale3d" => {
962 let x = convert_percentage_to_number(input)?;
963 input.expect_comma()?;
964 let y = convert_percentage_to_number(input)?;
965 input.expect_comma()?;
966 let z = convert_percentage_to_number(input)?;
967 Ok(Transform::Scale3d(x, y, z))
968 },
969 "rotate" => {
970 let angle = Angle::parse_with_unitless_zero(input)?;
971 Ok(Transform::Rotate(angle))
972 },
973 "rotatex" => {
974 let angle = Angle::parse_with_unitless_zero(input)?;
975 Ok(Transform::RotateX(angle))
976 },
977 "rotatey" => {
978 let angle = Angle::parse_with_unitless_zero(input)?;
979 Ok(Transform::RotateY(angle))
980 },
981 "rotatez" => {
982 let angle = Angle::parse_with_unitless_zero(input)?;
983 Ok(Transform::RotateZ(angle))
984 },
985 "rotate3d" => {
986 let x = f32::parse(input)?;
987 input.expect_comma()?;
988 let y = f32::parse(input)?;
989 input.expect_comma()?;
990 let z = f32::parse(input)?;
991 input.expect_comma()?;
992 let angle = Angle::parse_with_unitless_zero(input)?;
993 Ok(Transform::Rotate3d(x, y, z, angle))
994 },
995 "skew" => {
996 let x = Angle::parse_with_unitless_zero(input)?;
997 if input.try_parse(|input| input.expect_comma()).is_ok() {
998 let y = Angle::parse_with_unitless_zero(input)?;
999 Ok(Transform::Skew(x, y))
1000 } else {
1001 Ok(Transform::Skew(x, Angle::Deg(0.0)))
1002 }
1003 },
1004 "skewx" => {
1005 let angle = Angle::parse_with_unitless_zero(input)?;
1006 Ok(Transform::SkewX(angle))
1007 },
1008 "skewy" => {
1009 let angle = Angle::parse_with_unitless_zero(input)?;
1010 Ok(Transform::SkewY(angle))
1011 },
1012 "perspective" => {
1013 let len = Length::parse(input)?;
1014 Ok(Transform::Perspective(len))
1015 },
1016 _ => Err(location.new_unexpected_token_error(
1017 cssparser::Token::Ident(function.clone())
1018 ))
1019 }
1020 })
1021 }
1022}
1023
1024impl ToCss for Transform {
1025 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1026 where
1027 W: std::fmt::Write,
1028 {
1029 use Transform::*;
1030 match self {
1031 Translate(x, y) => {
1032 if dest.minify && x.is_zero() && !y.is_zero() {
1033 dest.write_str("translateY(")?;
1034 y.to_css(dest)?
1035 } else {
1036 dest.write_str("translate(")?;
1037 x.to_css(dest)?;
1038 if !y.is_zero() {
1039 dest.delim(',', false)?;
1040 y.to_css(dest)?;
1041 }
1042 }
1043 dest.write_char(')')
1044 }
1045 TranslateX(x) => {
1046 dest.write_str(if dest.minify { "translate(" } else { "translateX(" })?;
1047 x.to_css(dest)?;
1048 dest.write_char(')')
1049 }
1050 TranslateY(y) => {
1051 dest.write_str("translateY(")?;
1052 y.to_css(dest)?;
1053 dest.write_char(')')
1054 }
1055 TranslateZ(z) => {
1056 dest.write_str("translateZ(")?;
1057 z.to_css(dest)?;
1058 dest.write_char(')')
1059 }
1060 Translate3d(x, y, z) => {
1061 if dest.minify && !x.is_zero() && y.is_zero() && z.is_zero() {
1062 dest.write_str("translate(")?;
1063 x.to_css(dest)?;
1064 } else if dest.minify && x.is_zero() && !y.is_zero() && z.is_zero() {
1065 dest.write_str("translateY(")?;
1066 y.to_css(dest)?;
1067 } else if dest.minify && x.is_zero() && y.is_zero() && !z.is_zero() {
1068 dest.write_str("translateZ(")?;
1069 z.to_css(dest)?;
1070 } else if dest.minify && z.is_zero() {
1071 dest.write_str("translate(")?;
1072 x.to_css(dest)?;
1073 dest.delim(',', false)?;
1074 y.to_css(dest)?;
1075 } else {
1076 dest.write_str("translate3d(")?;
1077 x.to_css(dest)?;
1078 dest.delim(',', false)?;
1079 y.to_css(dest)?;
1080 dest.delim(',', false)?;
1081 z.to_css(dest)?;
1082 }
1083 dest.write_char(')')
1084 }
1085 Scale(x, y) => {
1086 let x: f32 = x.into();
1087 let y: f32 = y.into();
1088 if dest.minify && x == 1.0 && y != 1.0 {
1089 dest.write_str("scaleY(")?;
1090 y.to_css(dest)?;
1091 } else if dest.minify && x != 1.0 && y == 1.0 {
1092 dest.write_str("scaleX(")?;
1093 x.to_css(dest)?;
1094 } else {
1095 dest.write_str("scale(")?;
1096 x.to_css(dest)?;
1097 if y != x {
1098 dest.delim(',', false)?;
1099 y.to_css(dest)?;
1100 }
1101 }
1102 dest.write_char(')')
1103 }
1104 ScaleX(x) => {
1105 let x: f32 = x.into();
1106 dest.write_str("scaleX(")?;
1107 x.to_css(dest)?;
1108 dest.write_char(')')
1109 }
1110 ScaleY(y) => {
1111 let y: f32 = y.into();
1112 dest.write_str("scaleY(")?;
1113 y.to_css(dest)?;
1114 dest.write_char(')')
1115 }
1116 ScaleZ(z) => {
1117 let z: f32 = z.into();
1118 dest.write_str("scaleZ(")?;
1119 z.to_css(dest)?;
1120 dest.write_char(')')
1121 }
1122 Scale3d(x, y, z) => {
1123 let x: f32 = x.into();
1124 let y: f32 = y.into();
1125 let z: f32 = z.into();
1126 if dest.minify && z == 1.0 && x == y {
1127 dest.write_str("scale(")?;
1129 x.to_css(dest)?;
1130 } else if dest.minify && x != 1.0 && y == 1.0 && z == 1.0 {
1131 dest.write_str("scaleX(")?;
1133 x.to_css(dest)?;
1134 } else if dest.minify && x == 1.0 && y != 1.0 && z == 1.0 {
1135 dest.write_str("scaleY(")?;
1137 y.to_css(dest)?;
1138 } else if dest.minify && x == 1.0 && y == 1.0 && z != 1.0 {
1139 dest.write_str("scaleZ(")?;
1141 z.to_css(dest)?;
1142 } else if dest.minify && z == 1.0 {
1143 dest.write_str("scale(")?;
1145 x.to_css(dest)?;
1146 dest.delim(',', false)?;
1147 y.to_css(dest)?;
1148 } else {
1149 dest.write_str("scale3d(")?;
1150 x.to_css(dest)?;
1151 dest.delim(',', false)?;
1152 y.to_css(dest)?;
1153 dest.delim(',', false)?;
1154 z.to_css(dest)?;
1155 }
1156 dest.write_char(')')
1157 }
1158 Rotate(angle) => {
1159 dest.write_str("rotate(")?;
1160 angle.to_css_with_unitless_zero(dest)?;
1161 dest.write_char(')')
1162 }
1163 RotateX(angle) => {
1164 dest.write_str("rotateX(")?;
1165 angle.to_css_with_unitless_zero(dest)?;
1166 dest.write_char(')')
1167 }
1168 RotateY(angle) => {
1169 dest.write_str("rotateY(")?;
1170 angle.to_css_with_unitless_zero(dest)?;
1171 dest.write_char(')')
1172 }
1173 RotateZ(angle) => {
1174 dest.write_str(if dest.minify { "rotate(" } else { "rotateZ(" })?;
1175 angle.to_css_with_unitless_zero(dest)?;
1176 dest.write_char(')')
1177 }
1178 Rotate3d(x, y, z, angle) => {
1179 if dest.minify && *x == 1.0 && *y == 0.0 && *z == 0.0 {
1180 dest.write_str("rotateX(")?;
1182 angle.to_css_with_unitless_zero(dest)?;
1183 } else if dest.minify && *x == 0.0 && *y == 1.0 && *z == 0.0 {
1184 dest.write_str("rotateY(")?;
1186 angle.to_css_with_unitless_zero(dest)?;
1187 } else if dest.minify && *x == 0.0 && *y == 0.0 && *z == 1.0 {
1188 dest.write_str("rotate(")?;
1190 angle.to_css_with_unitless_zero(dest)?;
1191 } else {
1192 dest.write_str("rotate3d(")?;
1193 x.to_css(dest)?;
1194 dest.delim(',', false)?;
1195 y.to_css(dest)?;
1196 dest.delim(',', false)?;
1197 z.to_css(dest)?;
1198 dest.delim(',', false)?;
1199 angle.to_css_with_unitless_zero(dest)?;
1200 }
1201 dest.write_char(')')
1202 }
1203 Skew(x, y) => {
1204 if dest.minify && x.is_zero() && !y.is_zero() {
1205 dest.write_str("skewY(")?;
1206 y.to_css_with_unitless_zero(dest)?
1207 } else {
1208 dest.write_str("skew(")?;
1209 x.to_css(dest)?;
1210 if !y.is_zero() {
1211 dest.delim(',', false)?;
1212 y.to_css_with_unitless_zero(dest)?;
1213 }
1214 }
1215 dest.write_char(')')
1216 }
1217 SkewX(angle) => {
1218 dest.write_str(if dest.minify { "skew(" } else { "skewX(" })?;
1219 angle.to_css_with_unitless_zero(dest)?;
1220 dest.write_char(')')
1221 }
1222 SkewY(angle) => {
1223 dest.write_str("skewY(")?;
1224 angle.to_css_with_unitless_zero(dest)?;
1225 dest.write_char(')')
1226 }
1227 Perspective(len) => {
1228 dest.write_str("perspective(")?;
1229 len.to_css(dest)?;
1230 dest.write_char(')')
1231 }
1232 Matrix(super::transform::Matrix { a, b, c, d, e, f }) => {
1233 dest.write_str("matrix(")?;
1234 a.to_css(dest)?;
1235 dest.delim(',', false)?;
1236 b.to_css(dest)?;
1237 dest.delim(',', false)?;
1238 c.to_css(dest)?;
1239 dest.delim(',', false)?;
1240 d.to_css(dest)?;
1241 dest.delim(',', false)?;
1242 e.to_css(dest)?;
1243 dest.delim(',', false)?;
1244 f.to_css(dest)?;
1245 dest.write_char(')')
1246 }
1247 Matrix3d(super::transform::Matrix3d {
1248 m11,
1249 m12,
1250 m13,
1251 m14,
1252 m21,
1253 m22,
1254 m23,
1255 m24,
1256 m31,
1257 m32,
1258 m33,
1259 m34,
1260 m41,
1261 m42,
1262 m43,
1263 m44,
1264 }) => {
1265 dest.write_str("matrix3d(")?;
1266 m11.to_css(dest)?;
1267 dest.delim(',', false)?;
1268 m12.to_css(dest)?;
1269 dest.delim(',', false)?;
1270 m13.to_css(dest)?;
1271 dest.delim(',', false)?;
1272 m14.to_css(dest)?;
1273 dest.delim(',', false)?;
1274 m21.to_css(dest)?;
1275 dest.delim(',', false)?;
1276 m22.to_css(dest)?;
1277 dest.delim(',', false)?;
1278 m23.to_css(dest)?;
1279 dest.delim(',', false)?;
1280 m24.to_css(dest)?;
1281 dest.delim(',', false)?;
1282 m31.to_css(dest)?;
1283 dest.delim(',', false)?;
1284 m32.to_css(dest)?;
1285 dest.delim(',', false)?;
1286 m33.to_css(dest)?;
1287 dest.delim(',', false)?;
1288 m34.to_css(dest)?;
1289 dest.delim(',', false)?;
1290 m41.to_css(dest)?;
1291 dest.delim(',', false)?;
1292 m42.to_css(dest)?;
1293 dest.delim(',', false)?;
1294 m43.to_css(dest)?;
1295 dest.delim(',', false)?;
1296 m44.to_css(dest)?;
1297 dest.write_char(')')
1298 }
1299 }
1300 }
1301}
1302
1303impl Transform {
1304 pub fn to_matrix(&self) -> Option<Matrix3d<f32>> {
1306 macro_rules! to_radians {
1307 ($angle: ident) => {{
1308 let rad = $angle.to_radians();
1314 if rad < 0.0 || rad >= 2.0 * PI {
1315 return None;
1316 }
1317
1318 rad
1319 }};
1320 }
1321
1322 match &self {
1323 Transform::Translate(LengthPercentage::Dimension(x), LengthPercentage::Dimension(y)) => {
1324 if let (Some(x), Some(y)) = (x.to_px(), y.to_px()) {
1325 return Some(Matrix3d::translate(x, y, 0.0));
1326 }
1327 }
1328 Transform::TranslateX(LengthPercentage::Dimension(x)) => {
1329 if let Some(x) = x.to_px() {
1330 return Some(Matrix3d::translate(x, 0.0, 0.0));
1331 }
1332 }
1333 Transform::TranslateY(LengthPercentage::Dimension(y)) => {
1334 if let Some(y) = y.to_px() {
1335 return Some(Matrix3d::translate(0.0, y, 0.0));
1336 }
1337 }
1338 Transform::TranslateZ(z) => {
1339 if let Some(z) = z.to_px() {
1340 return Some(Matrix3d::translate(0.0, 0.0, z));
1341 }
1342 }
1343 Transform::Translate3d(LengthPercentage::Dimension(x), LengthPercentage::Dimension(y), z) => {
1344 if let (Some(x), Some(y), Some(z)) = (x.to_px(), y.to_px(), z.to_px()) {
1345 return Some(Matrix3d::translate(x, y, z));
1346 }
1347 }
1348 Transform::Scale(x, y) => return Some(Matrix3d::scale(x.into(), y.into(), 1.0)),
1349 Transform::ScaleX(x) => return Some(Matrix3d::scale(x.into(), 1.0, 1.0)),
1350 Transform::ScaleY(y) => return Some(Matrix3d::scale(1.0, y.into(), 1.0)),
1351 Transform::ScaleZ(z) => return Some(Matrix3d::scale(1.0, 1.0, z.into())),
1352 Transform::Scale3d(x, y, z) => return Some(Matrix3d::scale(x.into(), y.into(), z.into())),
1353 Transform::Rotate(angle) | Transform::RotateZ(angle) => {
1354 return Some(Matrix3d::rotate(0.0, 0.0, 1.0, to_radians!(angle)))
1355 }
1356 Transform::RotateX(angle) => return Some(Matrix3d::rotate(1.0, 0.0, 0.0, to_radians!(angle))),
1357 Transform::RotateY(angle) => return Some(Matrix3d::rotate(0.0, 1.0, 0.0, to_radians!(angle))),
1358 Transform::Rotate3d(x, y, z, angle) => return Some(Matrix3d::rotate(*x, *y, *z, to_radians!(angle))),
1359 Transform::Skew(x, y) => return Some(Matrix3d::skew(to_radians!(x), to_radians!(y))),
1360 Transform::SkewX(x) => return Some(Matrix3d::skew(to_radians!(x), 0.0)),
1361 Transform::SkewY(y) => return Some(Matrix3d::skew(0.0, to_radians!(y))),
1362 Transform::Perspective(len) => {
1363 if let Some(len) = len.to_px() {
1364 return Some(Matrix3d::perspective(len));
1365 }
1366 }
1367 Transform::Matrix(m) => return Some(m.to_matrix3d()),
1368 Transform::Matrix3d(m) => return Some(m.clone()),
1369 _ => {}
1370 }
1371 None
1372 }
1373}
1374
1375enum_property! {
1376 #[allow(missing_docs)]
1378 pub enum TransformStyle {
1379 Flat,
1380 Preserve3d,
1381 }
1382}
1383
1384enum_property! {
1385 pub enum TransformBox {
1387 ContentBox,
1389 BorderBox,
1391 FillBox,
1393 StrokeBox,
1395 ViewBox,
1397 }
1398}
1399
1400enum_property! {
1401 #[allow(missing_docs)]
1403 pub enum BackfaceVisibility {
1404 Visible,
1405 Hidden,
1406 }
1407}
1408
1409#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
1411#[cfg_attr(feature = "visitor", derive(Visit))]
1412#[cfg_attr(
1413 feature = "serde",
1414 derive(serde::Serialize, serde::Deserialize),
1415 serde(tag = "type", content = "value", rename_all = "kebab-case")
1416)]
1417#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1418#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1419pub enum Perspective {
1420 None,
1422 Length(Length),
1424}
1425
1426#[derive(Debug, Clone, PartialEq)]
1428#[cfg_attr(feature = "visitor", derive(Visit))]
1429#[cfg_attr(
1430 feature = "serde",
1431 derive(serde::Serialize, serde::Deserialize),
1432 serde(rename_all = "lowercase")
1433)]
1434#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1435#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1436pub enum Translate {
1437 None,
1439
1440 #[cfg_attr(feature = "serde", serde(untagged))]
1442 XYZ {
1443 x: LengthPercentage,
1445 y: LengthPercentage,
1447 z: Length,
1449 },
1450}
1451
1452impl<'i> Parse<'i> for Translate {
1453 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1454 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1455 return Ok(Translate::None);
1456 }
1457
1458 let x = LengthPercentage::parse(input)?;
1459 let y = input.try_parse(LengthPercentage::parse);
1460 let z = if y.is_ok() {
1461 input.try_parse(Length::parse).ok()
1462 } else {
1463 None
1464 };
1465
1466 Ok(Translate::XYZ {
1467 x,
1468 y: y.unwrap_or(LengthPercentage::zero()),
1469 z: z.unwrap_or(Length::zero()),
1470 })
1471 }
1472}
1473
1474impl ToCss for Translate {
1475 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1476 where
1477 W: std::fmt::Write,
1478 {
1479 match self {
1480 Translate::None => {
1481 dest.write_str("none")?;
1482 }
1483 Translate::XYZ { x, y, z } => {
1484 x.to_css(dest)?;
1485 if !y.is_zero() || !z.is_zero() {
1486 dest.write_char(' ')?;
1487 y.to_css(dest)?;
1488 if !z.is_zero() {
1489 dest.write_char(' ')?;
1490 z.to_css(dest)?;
1491 }
1492 }
1493 }
1494 };
1495
1496 Ok(())
1497 }
1498}
1499
1500impl Translate {
1501 pub fn to_transform(&self) -> Transform {
1503 match self {
1504 Translate::None => {
1505 Transform::Translate3d(LengthPercentage::zero(), LengthPercentage::zero(), Length::zero())
1506 }
1507 Translate::XYZ { x, y, z } => Transform::Translate3d(x.clone(), y.clone(), z.clone()),
1508 }
1509 }
1510}
1511
1512#[derive(Debug, Clone, PartialEq)]
1514#[cfg_attr(feature = "visitor", derive(Visit))]
1515#[cfg_attr(
1516 feature = "serde",
1517 derive(serde::Serialize, serde::Deserialize),
1518 serde(rename_all = "lowercase")
1519)]
1520#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1521#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1522pub enum Rotate {
1523 None,
1525
1526 #[cfg_attr(feature = "serde", serde(untagged))]
1528 XYZ {
1529 x: f32,
1531 y: f32,
1533 z: f32,
1535 angle: Angle,
1537 },
1538}
1539
1540impl<'i> Parse<'i> for Rotate {
1541 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1542 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1547 return Ok(Rotate::None);
1548 }
1549
1550 let angle = input.try_parse(Angle::parse);
1551 let (x, y, z) = input
1552 .try_parse(|input| {
1553 let location = input.current_source_location();
1554 let ident = input.expect_ident()?;
1555 match_ignore_ascii_case! { &*ident,
1556 "x" => Ok((1.0, 0.0, 0.0)),
1557 "y" => Ok((0.0, 1.0, 0.0)),
1558 "z" => Ok((0.0, 0.0, 1.0)),
1559 _ => Err(location.new_unexpected_token_error(
1560 cssparser::Token::Ident(ident.clone())
1561 ))
1562 }
1563 })
1564 .or_else(
1565 |_: ParseError<'i, ParserError<'i>>| -> Result<_, ParseError<'i, ParserError<'i>>> {
1566 input.try_parse(|input| Ok((f32::parse(input)?, f32::parse(input)?, f32::parse(input)?)))
1567 },
1568 )
1569 .unwrap_or((0.0, 0.0, 1.0));
1570 let angle = angle.or_else(|_| Angle::parse(input))?;
1571 Ok(Rotate::XYZ { x, y, z, angle })
1572 }
1573}
1574
1575impl ToCss for Rotate {
1576 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1577 where
1578 W: std::fmt::Write,
1579 {
1580 match self {
1581 Rotate::None => dest.write_str("none"),
1582 Rotate::XYZ { x, y, z, angle } => {
1583 if *y == 0.0 && *z == 0.0 && *x != 0.0 {
1589 let angle = if *x < 0.0 { angle.clone() * -1.0 } else { angle.clone() };
1590 dest.write_str("x ")?;
1591 angle.to_css(dest)
1592 } else if *x == 0.0 && *z == 0.0 && *y != 0.0 {
1593 let angle = if *y < 0.0 { angle.clone() * -1.0 } else { angle.clone() };
1594 dest.write_str("y ")?;
1595 angle.to_css(dest)
1596 } else if *x == 0.0 && *y == 0.0 && *z != 0.0 {
1597 let angle = if *z < 0.0 { angle.clone() * -1.0 } else { angle.clone() };
1598 angle.to_css(dest)
1599 } else {
1600 x.to_css(dest)?;
1601 dest.write_char(' ')?;
1602 y.to_css(dest)?;
1603 dest.write_char(' ')?;
1604 z.to_css(dest)?;
1605 dest.write_char(' ')?;
1606 angle.to_css(dest)
1607 }
1608 }
1609 }
1610 }
1611}
1612
1613impl Rotate {
1614 pub fn to_transform(&self) -> Transform {
1616 match self {
1617 Rotate::None => Transform::Rotate3d(0.0, 0.0, 1.0, Angle::Deg(0.0)),
1618 Rotate::XYZ { x, y, z, angle } => Transform::Rotate3d(*x, *y, *z, angle.clone()),
1619 }
1620 }
1621}
1622
1623#[derive(Debug, Clone, PartialEq)]
1625#[cfg_attr(feature = "visitor", derive(Visit))]
1626#[cfg_attr(
1627 feature = "serde",
1628 derive(serde::Serialize, serde::Deserialize),
1629 serde(rename_all = "lowercase")
1630)]
1631#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1632#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1633pub enum Scale {
1634 None,
1636
1637 #[cfg_attr(feature = "serde", serde(untagged))]
1639 XYZ {
1640 x: NumberOrPercentage,
1642 y: NumberOrPercentage,
1644 z: NumberOrPercentage,
1646 },
1647}
1648
1649fn convert_percentage_to_number<'i, 't>(
1650 input: &mut Parser<'i, 't>,
1651) -> Result<NumberOrPercentage, ParseError<'i, ParserError<'i>>> {
1652 Ok(match NumberOrPercentage::parse(input)? {
1653 NumberOrPercentage::Number(number) => NumberOrPercentage::Number(number),
1654 NumberOrPercentage::Percentage(percent) => NumberOrPercentage::Number(percent.0),
1655 })
1656}
1657
1658impl<'i> Parse<'i> for Scale {
1659 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1660 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1661 return Ok(Scale::None);
1662 }
1663
1664 let x = convert_percentage_to_number(input)?;
1665 let y = input.try_parse(convert_percentage_to_number);
1666 let z = if y.is_ok() {
1667 input.try_parse(convert_percentage_to_number).ok()
1668 } else {
1669 None
1670 };
1671
1672 Ok(Scale::XYZ {
1673 x: x.clone(),
1674 y: y.unwrap_or(x),
1675 z: z.unwrap_or(NumberOrPercentage::Number(1.0)),
1676 })
1677 }
1678}
1679
1680impl ToCss for Scale {
1681 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1682 where
1683 W: std::fmt::Write,
1684 {
1685 match self {
1686 Scale::None => {
1687 dest.write_str("none")?;
1688 }
1689 Scale::XYZ { x, y, z } => {
1690 let x: f32 = x.into();
1691 let y: f32 = y.into();
1692 let z: f32 = z.into();
1693 x.to_css(dest)?;
1694 if y != x || z != 1.0 {
1695 dest.write_char(' ')?;
1696 y.to_css(dest)?;
1697 if z != 1.0 {
1698 dest.write_char(' ')?;
1699 z.to_css(dest)?;
1700 }
1701 }
1702 }
1703 }
1704
1705 Ok(())
1706 }
1707}
1708
1709impl Scale {
1710 pub fn to_transform(&self) -> Transform {
1712 match self {
1713 Scale::None => Transform::Scale3d(
1714 NumberOrPercentage::Number(1.0),
1715 NumberOrPercentage::Number(1.0),
1716 NumberOrPercentage::Number(1.0),
1717 ),
1718 Scale::XYZ { x, y, z } => Transform::Scale3d(x.clone(), y.clone(), z.clone()),
1719 }
1720 }
1721}
1722
1723#[derive(Default)]
1724pub(crate) struct TransformHandler {
1725 transform: Option<(TransformList, VendorPrefix)>,
1726 translate: Option<Translate>,
1727 rotate: Option<Rotate>,
1728 scale: Option<Scale>,
1729 has_any: bool,
1730}
1731
1732impl<'i> PropertyHandler<'i> for TransformHandler {
1733 fn handle_property(
1734 &mut self,
1735 property: &Property<'i>,
1736 dest: &mut DeclarationList<'i>,
1737 context: &mut PropertyHandlerContext<'i, '_>,
1738 ) -> bool {
1739 use Property::*;
1740
1741 macro_rules! individual_property {
1742 ($prop: ident, $val: ident) => {
1743 if let Some((transform, _)) = &mut self.transform {
1744 transform.0.push($val.to_transform())
1745 } else {
1746 self.$prop = Some($val.clone());
1747 self.has_any = true;
1748 }
1749 };
1750 }
1751
1752 match property {
1753 Transform(val, vp) => {
1754 if let Some((cur, prefixes)) = &self.transform {
1757 if cur != val && !prefixes.contains(*vp) {
1758 self.flush(dest, context);
1759 }
1760 }
1761
1762 if let Some((transform, prefixes)) = &mut self.transform {
1764 *transform = val.clone();
1765 *prefixes |= *vp;
1766 } else {
1767 self.transform = Some((val.clone(), *vp));
1768 self.has_any = true;
1769 }
1770
1771 self.translate = None;
1772 self.rotate = None;
1773 self.scale = None;
1774 }
1775 Translate(val) => individual_property!(translate, val),
1776 Rotate(val) => individual_property!(rotate, val),
1777 Scale(val) => individual_property!(scale, val),
1778 Unparsed(val)
1779 if matches!(
1780 val.property_id,
1781 PropertyId::Transform(_) | PropertyId::Translate | PropertyId::Rotate | PropertyId::Scale
1782 ) =>
1783 {
1784 self.flush(dest, context);
1785 let prop = if matches!(val.property_id, PropertyId::Transform(_)) {
1786 Property::Unparsed(val.get_prefixed(context.targets, Feature::Transform))
1787 } else {
1788 property.clone()
1789 };
1790 dest.push(prop)
1791 }
1792 _ => return false,
1793 }
1794
1795 true
1796 }
1797
1798 fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
1799 self.flush(dest, context);
1800 }
1801}
1802
1803impl TransformHandler {
1804 fn flush<'i>(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
1805 if !self.has_any {
1806 return;
1807 }
1808
1809 self.has_any = false;
1810
1811 let transform = std::mem::take(&mut self.transform);
1812 let translate = std::mem::take(&mut self.translate);
1813 let rotate = std::mem::take(&mut self.rotate);
1814 let scale = std::mem::take(&mut self.scale);
1815
1816 if let Some((transform, prefix)) = transform {
1817 let prefix = context.targets.prefixes(prefix, Feature::Transform);
1818 dest.push(Property::Transform(transform, prefix))
1819 }
1820
1821 if let Some(translate) = translate {
1822 dest.push(Property::Translate(translate))
1823 }
1824
1825 if let Some(rotate) = rotate {
1826 dest.push(Property::Rotate(rotate))
1827 }
1828
1829 if let Some(scale) = scale {
1830 dest.push(Property::Scale(scale))
1831 }
1832 }
1833}