1use cssparser::{Parser, Token};
16use std::ops::Deref;
17
18use crate::angle::Angle;
19use crate::error::*;
20use crate::length::*;
21use crate::parsers::{Parse, optional_comma};
22use crate::properties::ComputedValues;
23use crate::property_macros::Property;
24use crate::rect::Rect;
25
26#[derive(Debug, Default, Copy, Clone, PartialEq)]
31pub struct ValidTransform(Transform);
32
33impl TryFrom<Transform> for ValidTransform {
34 type Error = InvalidTransform;
35
36 fn try_from(t: Transform) -> Result<ValidTransform, InvalidTransform> {
41 if t.is_invertible() {
42 Ok(ValidTransform(t))
43 } else {
44 Err(InvalidTransform)
45 }
46 }
47}
48
49impl Deref for ValidTransform {
50 type Target = Transform;
51
52 fn deref(&self) -> &Transform {
53 &self.0
54 }
55}
56
57#[derive(Debug, Copy, Clone, PartialEq)]
59pub struct Transform {
60 pub xx: f64,
61 pub yx: f64,
62 pub xy: f64,
63 pub yy: f64,
64 pub x0: f64,
65 pub y0: f64,
66}
67
68#[derive(Debug, Default, Clone, PartialEq)]
72pub enum TransformProperty {
73 #[default]
74 None,
75 List(Vec<TransformFunction>),
76}
77
78#[derive(Copy, Clone, Default, Debug, PartialEq)]
82pub struct TransformAttribute(Transform);
83
84impl Property for TransformProperty {
85 fn inherits_automatically() -> bool {
86 false
87 }
88
89 fn compute(&self, _v: &ComputedValues) -> Self {
90 self.clone()
91 }
92}
93
94impl TransformProperty {
95 pub fn to_transform(&self) -> Transform {
96 match self {
103 TransformProperty::None => Transform::identity(),
104
105 TransformProperty::List(l) => {
106 let mut final_transform = Transform::identity();
107
108 for f in l.iter() {
109 use TransformFunction::*;
110
111 let transform_matrix = match f {
112 Matrix(trans_matrix) => *trans_matrix,
113 Translate(h, v) => Transform::new_translate(h.length, v.length),
114 TranslateX(h) => Transform::new_translate(h.length, 0.0),
115 TranslateY(v) => Transform::new_translate(0.0, v.length),
116 Scale(x, y) => Transform::new_scale(*x, *y),
117 ScaleX(x) => Transform::new_scale(*x, 1.0),
118 ScaleY(y) => Transform::new_scale(1.0, *y),
119 Rotate(a) => Transform::new_rotate(*a),
120 Skew(ax, ay) => Transform::new_skew(*ax, *ay),
121 SkewX(ax) => Transform::new_skew(*ax, Angle::new(0.0)),
122 SkewY(ay) => Transform::new_skew(Angle::new(0.0), *ay),
123 };
124 final_transform = transform_matrix.post_transform(&final_transform);
125 }
126
127 final_transform
128 }
129 }
130 }
131}
132
133#[derive(Debug, Clone, PartialEq)]
135pub enum TransformFunction {
136 Matrix(Transform),
137 Translate(Length<Horizontal>, Length<Vertical>),
138 TranslateX(Length<Horizontal>),
139 TranslateY(Length<Vertical>),
140 Scale(f64, f64),
141 ScaleX(f64),
142 ScaleY(f64),
143 Rotate(Angle),
144 Skew(Angle, Angle),
145 SkewX(Angle),
146 SkewY(Angle),
147}
148
149impl Parse for TransformProperty {
150 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TransformProperty, ParseError<'i>> {
151 if parser
152 .try_parse(|p| p.expect_ident_matching("none"))
153 .is_ok()
154 {
155 Ok(TransformProperty::None)
156 } else {
157 let t = parse_transform_prop_function_list(parser)?;
158
159 Ok(TransformProperty::List(t))
160 }
161 }
162}
163
164fn parse_transform_prop_function_list<'i>(
165 parser: &mut Parser<'i, '_>,
166) -> Result<Vec<TransformFunction>, ParseError<'i>> {
167 let mut v = Vec::<TransformFunction>::new();
168
169 loop {
170 v.push(parse_transform_prop_function_command(parser)?);
171
172 if parser.is_exhausted() {
173 break;
174 }
175 }
176
177 Ok(v)
178}
179
180fn parse_transform_prop_function_command<'i>(
181 parser: &mut Parser<'i, '_>,
182) -> Result<TransformFunction, ParseError<'i>> {
183 let loc = parser.current_source_location();
184
185 match parser.next()?.clone() {
186 Token::Function(ref name) => parse_transform_prop_function_internal(name, parser),
187 tok => Err(loc.new_unexpected_token_error(tok.clone())),
188 }
189}
190
191fn parse_transform_prop_function_internal<'i>(
192 name: &str,
193 parser: &mut Parser<'i, '_>,
194) -> Result<TransformFunction, ParseError<'i>> {
195 let loc = parser.current_source_location();
196
197 match name {
198 "matrix" => parse_prop_matrix_args(parser),
199 "translate" => parse_prop_translate_args(parser),
200 "translateX" => parse_prop_translate_x_args(parser),
201 "translateY" => parse_prop_translate_y_args(parser),
202 "scale" => parse_prop_scale_args(parser),
203 "scaleX" => parse_prop_scale_x_args(parser),
204 "scaleY" => parse_prop_scale_y_args(parser),
205 "rotate" => parse_prop_rotate_args(parser),
206 "skew" => parse_prop_skew_args(parser),
207 "skewX" => parse_prop_skew_x_args(parser),
208 "skewY" => parse_prop_skew_y_args(parser),
209 _ => Err(loc.new_custom_error(ValueErrorKind::parse_error(
210 "expected matrix|translate|translateX|translateY|scale|scaleX|scaleY|rotate|skewX|skewY",
211 ))),
212 }
213}
214
215fn parse_prop_matrix_args<'i>(
216 parser: &mut Parser<'i, '_>,
217) -> Result<TransformFunction, ParseError<'i>> {
218 parser.parse_nested_block(|p| {
219 let xx = f64::parse(p)?;
220 p.expect_comma()?;
221 let yx = f64::parse(p)?;
222 p.expect_comma()?;
223 let xy = f64::parse(p)?;
224 p.expect_comma()?;
225 let yy = f64::parse(p)?;
226 p.expect_comma()?;
227 let x0 = f64::parse(p)?;
228 p.expect_comma()?;
229 let y0 = f64::parse(p)?;
230
231 Ok(TransformFunction::Matrix(Transform::new_unchecked(
232 xx, yx, xy, yy, x0, y0,
233 )))
234 })
235}
236
237fn length_is_in_pixels<N: Normalize>(l: &Length<N>) -> bool {
238 l.unit == LengthUnit::Px
239}
240
241fn only_pixels_error<'i>(loc: cssparser::SourceLocation) -> ParseError<'i> {
242 loc.new_custom_error(ValueErrorKind::parse_error(
243 "only translations in pixels are supported for now",
244 ))
245}
246
247fn parse_prop_translate_args<'i>(
248 parser: &mut Parser<'i, '_>,
249) -> Result<TransformFunction, ParseError<'i>> {
250 parser.parse_nested_block(|p| {
251 let loc = p.current_source_location();
252
253 let tx: Length<Horizontal> = Length::parse(p)?;
254
255 let ty: Length<Vertical> = if p.try_parse(|p| p.expect_comma()).is_ok() {
256 Length::parse(p)?
257 } else {
258 Length::new(0.0, LengthUnit::Px)
259 };
260
261 if !(length_is_in_pixels(&tx) && length_is_in_pixels(&ty)) {
262 return Err(only_pixels_error(loc));
263 }
264
265 Ok(TransformFunction::Translate(tx, ty))
266 })
267}
268
269fn parse_prop_translate_x_args<'i>(
270 parser: &mut Parser<'i, '_>,
271) -> Result<TransformFunction, ParseError<'i>> {
272 parser.parse_nested_block(|p| {
273 let loc = p.current_source_location();
274
275 let tx: Length<Horizontal> = Length::parse(p)?;
276
277 if !length_is_in_pixels(&tx) {
278 return Err(only_pixels_error(loc));
279 }
280
281 Ok(TransformFunction::TranslateX(tx))
282 })
283}
284
285fn parse_prop_translate_y_args<'i>(
286 parser: &mut Parser<'i, '_>,
287) -> Result<TransformFunction, ParseError<'i>> {
288 parser.parse_nested_block(|p| {
289 let loc = p.current_source_location();
290
291 let ty: Length<Vertical> = Length::parse(p)?;
292
293 if !length_is_in_pixels(&ty) {
294 return Err(only_pixels_error(loc));
295 }
296
297 Ok(TransformFunction::TranslateY(ty))
298 })
299}
300
301fn parse_prop_scale_args<'i>(
302 parser: &mut Parser<'i, '_>,
303) -> Result<TransformFunction, ParseError<'i>> {
304 parser.parse_nested_block(|p| {
305 let x = f64::parse(p)?;
306
307 let y = if p.try_parse(|p| p.expect_comma()).is_ok() {
308 f64::parse(p)?
309 } else {
310 x
311 };
312
313 Ok(TransformFunction::Scale(x, y))
314 })
315}
316
317fn parse_prop_scale_x_args<'i>(
318 parser: &mut Parser<'i, '_>,
319) -> Result<TransformFunction, ParseError<'i>> {
320 parser.parse_nested_block(|p| {
321 let x = f64::parse(p)?;
322
323 Ok(TransformFunction::ScaleX(x))
324 })
325}
326
327fn parse_prop_scale_y_args<'i>(
328 parser: &mut Parser<'i, '_>,
329) -> Result<TransformFunction, ParseError<'i>> {
330 parser.parse_nested_block(|p| {
331 let y = f64::parse(p)?;
332
333 Ok(TransformFunction::ScaleY(y))
334 })
335}
336
337fn parse_prop_rotate_args<'i>(
338 parser: &mut Parser<'i, '_>,
339) -> Result<TransformFunction, ParseError<'i>> {
340 parser.parse_nested_block(|p| {
341 let angle = Angle::parse(p)?;
342
343 Ok(TransformFunction::Rotate(angle))
344 })
345}
346
347fn parse_prop_skew_args<'i>(
348 parser: &mut Parser<'i, '_>,
349) -> Result<TransformFunction, ParseError<'i>> {
350 parser.parse_nested_block(|p| {
351 let ax = Angle::parse(p)?;
352
353 let ay = if p.try_parse(|p| p.expect_comma()).is_ok() {
354 Angle::parse(p)?
355 } else {
356 Angle::from_degrees(0.0)
357 };
358
359 Ok(TransformFunction::Skew(ax, ay))
360 })
361}
362
363fn parse_prop_skew_x_args<'i>(
364 parser: &mut Parser<'i, '_>,
365) -> Result<TransformFunction, ParseError<'i>> {
366 parser.parse_nested_block(|p| {
367 let angle = Angle::parse(p)?;
368 Ok(TransformFunction::SkewX(angle))
369 })
370}
371
372fn parse_prop_skew_y_args<'i>(
373 parser: &mut Parser<'i, '_>,
374) -> Result<TransformFunction, ParseError<'i>> {
375 parser.parse_nested_block(|p| {
376 let angle = Angle::parse(p)?;
377 Ok(TransformFunction::SkewY(angle))
378 })
379}
380
381impl Transform {
382 #[inline]
383 pub fn new_unchecked(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
384 Self {
385 xx,
386 yx,
387 xy,
388 yy,
389 x0,
390 y0,
391 }
392 }
393
394 #[inline]
395 pub fn identity() -> Self {
396 Self::new_unchecked(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
397 }
398
399 #[inline]
400 pub fn new_translate(tx: f64, ty: f64) -> Self {
401 Self::new_unchecked(1.0, 0.0, 0.0, 1.0, tx, ty)
402 }
403
404 #[inline]
405 pub fn new_scale(sx: f64, sy: f64) -> Self {
406 Self::new_unchecked(sx, 0.0, 0.0, sy, 0.0, 0.0)
407 }
408
409 #[inline]
410 pub fn new_rotate(a: Angle) -> Self {
411 let (s, c) = a.radians().sin_cos();
412 Self::new_unchecked(c, s, -s, c, 0.0, 0.0)
413 }
414
415 #[inline]
416 pub fn new_skew(ax: Angle, ay: Angle) -> Self {
417 Self::new_unchecked(1.0, ay.radians().tan(), ax.radians().tan(), 1.0, 0.0, 0.0)
418 }
419
420 #[must_use]
421 pub fn multiply(t1: &Transform, t2: &Transform) -> Self {
422 #[allow(clippy::suspicious_operation_groupings)]
423 Transform {
424 xx: t1.xx * t2.xx + t1.yx * t2.xy,
425 yx: t1.xx * t2.yx + t1.yx * t2.yy,
426 xy: t1.xy * t2.xx + t1.yy * t2.xy,
427 yy: t1.xy * t2.yx + t1.yy * t2.yy,
428 x0: t1.x0 * t2.xx + t1.y0 * t2.xy + t2.x0,
429 y0: t1.x0 * t2.yx + t1.y0 * t2.yy + t2.y0,
430 }
431 }
432
433 #[inline]
434 pub fn pre_transform(&self, t: &Transform) -> Self {
435 Self::multiply(t, self)
436 }
437
438 #[inline]
439 pub fn post_transform(&self, t: &Transform) -> Self {
440 Self::multiply(self, t)
441 }
442
443 #[inline]
444 pub fn pre_translate(&self, x: f64, y: f64) -> Self {
445 self.pre_transform(&Transform::new_translate(x, y))
446 }
447
448 #[inline]
449 pub fn pre_scale(&self, sx: f64, sy: f64) -> Self {
450 self.pre_transform(&Transform::new_scale(sx, sy))
451 }
452
453 #[inline]
454 pub fn pre_rotate(&self, angle: Angle) -> Self {
455 self.pre_transform(&Transform::new_rotate(angle))
456 }
457
458 #[inline]
459 pub fn post_translate(&self, x: f64, y: f64) -> Self {
460 self.post_transform(&Transform::new_translate(x, y))
461 }
462
463 #[inline]
464 pub fn post_scale(&self, sx: f64, sy: f64) -> Self {
465 self.post_transform(&Transform::new_scale(sx, sy))
466 }
467
468 #[inline]
469 pub fn post_rotate(&self, angle: Angle) -> Self {
470 self.post_transform(&Transform::new_rotate(angle))
471 }
472
473 #[inline]
474 fn determinant(&self) -> f64 {
475 self.xx * self.yy - self.xy * self.yx
476 }
477
478 #[inline]
479 pub fn is_invertible(&self) -> bool {
480 let det = self.determinant();
481
482 det != 0.0 && det.is_finite()
483 }
484
485 #[must_use]
486 pub fn invert(&self) -> Option<Self> {
487 let det = self.determinant();
488
489 if det == 0.0 || !det.is_finite() {
490 return None;
491 }
492
493 let inv_det = 1.0 / det;
494
495 Some(Transform::new_unchecked(
496 inv_det * self.yy,
497 inv_det * (-self.yx),
498 inv_det * (-self.xy),
499 inv_det * self.xx,
500 inv_det * (self.xy * self.y0 - self.yy * self.x0),
501 inv_det * (self.yx * self.x0 - self.xx * self.y0),
502 ))
503 }
504
505 #[inline]
506 pub fn transform_distance(&self, dx: f64, dy: f64) -> (f64, f64) {
507 (dx * self.xx + dy * self.xy, dx * self.yx + dy * self.yy)
508 }
509
510 #[inline]
511 pub fn transform_point(&self, px: f64, py: f64) -> (f64, f64) {
512 let (x, y) = self.transform_distance(px, py);
513 (x + self.x0, y + self.y0)
514 }
515
516 pub fn transform_rect(&self, rect: &Rect) -> Rect {
517 let points = [
518 self.transform_point(rect.x0, rect.y0),
519 self.transform_point(rect.x1, rect.y0),
520 self.transform_point(rect.x0, rect.y1),
521 self.transform_point(rect.x1, rect.y1),
522 ];
523
524 let (mut xmin, mut ymin, mut xmax, mut ymax) = {
525 let (x, y) = points[0];
526
527 (x, y, x, y)
528 };
529
530 for &(x, y) in points.iter().skip(1) {
531 if x < xmin {
532 xmin = x;
533 }
534
535 if x > xmax {
536 xmax = x;
537 }
538
539 if y < ymin {
540 ymin = y;
541 }
542
543 if y > ymax {
544 ymax = y;
545 }
546 }
547
548 Rect {
549 x0: xmin,
550 y0: ymin,
551 x1: xmax,
552 y1: ymax,
553 }
554 }
555}
556
557impl Default for Transform {
558 #[inline]
559 fn default() -> Transform {
560 Transform::identity()
561 }
562}
563
564impl TransformAttribute {
565 pub fn to_transform(self) -> Transform {
566 self.0
567 }
568}
569
570impl Parse for TransformAttribute {
571 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TransformAttribute, ParseError<'i>> {
572 Ok(TransformAttribute(parse_transform_list(parser)?))
573 }
574}
575
576fn parse_transform_list<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
577 let mut t = Transform::identity();
578
579 loop {
580 if parser.is_exhausted() {
581 break;
582 }
583
584 t = parse_transform_command(parser)?.post_transform(&t);
585 optional_comma(parser);
586 }
587
588 Ok(t)
589}
590
591fn parse_transform_command<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
592 let loc = parser.current_source_location();
593
594 match parser.next()?.clone() {
595 Token::Function(ref name) => parse_transform_function(name, parser),
596
597 Token::Ident(ref name) => {
598 parser.expect_parenthesis_block()?;
599 parse_transform_function(name, parser)
600 }
601
602 tok => Err(loc.new_unexpected_token_error(tok.clone())),
603 }
604}
605
606fn parse_transform_function<'i>(
607 name: &str,
608 parser: &mut Parser<'i, '_>,
609) -> Result<Transform, ParseError<'i>> {
610 let loc = parser.current_source_location();
611
612 match name {
613 "matrix" => parse_matrix_args(parser),
614 "translate" => parse_translate_args(parser),
615 "scale" => parse_scale_args(parser),
616 "rotate" => parse_rotate_args(parser),
617 "skewX" => parse_skew_x_args(parser),
618 "skewY" => parse_skew_y_args(parser),
619 _ => Err(loc.new_custom_error(ValueErrorKind::parse_error(
620 "expected matrix|translate|scale|rotate|skewX|skewY",
621 ))),
622 }
623}
624
625fn parse_matrix_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
626 parser.parse_nested_block(|p| {
627 let xx = f64::parse(p)?;
628 optional_comma(p);
629
630 let yx = f64::parse(p)?;
631 optional_comma(p);
632
633 let xy = f64::parse(p)?;
634 optional_comma(p);
635
636 let yy = f64::parse(p)?;
637 optional_comma(p);
638
639 let x0 = f64::parse(p)?;
640 optional_comma(p);
641
642 let y0 = f64::parse(p)?;
643
644 Ok(Transform::new_unchecked(xx, yx, xy, yy, x0, y0))
645 })
646}
647
648fn parse_translate_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
649 parser.parse_nested_block(|p| {
650 let tx = f64::parse(p)?;
651
652 let ty = p
653 .try_parse(|p| {
654 optional_comma(p);
655 f64::parse(p)
656 })
657 .unwrap_or(0.0);
658
659 Ok(Transform::new_translate(tx, ty))
660 })
661}
662
663fn parse_scale_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
664 parser.parse_nested_block(|p| {
665 let x = f64::parse(p)?;
666
667 let y = p
668 .try_parse(|p| {
669 optional_comma(p);
670 f64::parse(p)
671 })
672 .unwrap_or(x);
673
674 Ok(Transform::new_scale(x, y))
675 })
676}
677
678fn parse_rotate_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
679 parser.parse_nested_block(|p| {
680 let angle = Angle::from_degrees(f64::parse(p)?);
681
682 let (tx, ty) = p
683 .try_parse(|p| -> Result<_, ParseError<'_>> {
684 optional_comma(p);
685 let tx = f64::parse(p)?;
686
687 optional_comma(p);
688 let ty = f64::parse(p)?;
689
690 Ok((tx, ty))
691 })
692 .unwrap_or((0.0, 0.0));
693
694 Ok(Transform::new_translate(tx, ty)
695 .pre_rotate(angle)
696 .pre_translate(-tx, -ty))
697 })
698}
699
700fn parse_skew_x_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
701 parser.parse_nested_block(|p| {
702 let angle = Angle::from_degrees(f64::parse(p)?);
703 Ok(Transform::new_skew(angle, Angle::new(0.0)))
704 })
705}
706
707fn parse_skew_y_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
708 parser.parse_nested_block(|p| {
709 let angle = Angle::from_degrees(f64::parse(p)?);
710 Ok(Transform::new_skew(Angle::new(0.0), angle))
711 })
712}
713
714#[cfg(test)]
715mod tests {
716 use super::*;
717 use float_cmp::ApproxEq;
718 use std::f64;
719
720 fn rotation_transform(deg: f64, tx: f64, ty: f64) -> Transform {
721 Transform::new_translate(tx, ty)
722 .pre_rotate(Angle::from_degrees(deg))
723 .pre_translate(-tx, -ty)
724 }
725
726 fn parse_transform_attr(s: &str) -> Result<Transform, ParseError<'_>> {
727 let transform_attr = TransformAttribute::parse_str(s)?;
728 Ok(transform_attr.to_transform())
729 }
730
731 fn parse_transform_prop(s: &str) -> Result<TransformProperty, ParseError<'_>> {
732 TransformProperty::parse_str(s)
733 }
734
735 fn assert_transform_eq(t1: &Transform, t2: &Transform) {
736 let epsilon = 8.0 * f64::EPSILON; assert!(t1.xx.approx_eq(t2.xx, (epsilon, 1)));
739 assert!(t1.yx.approx_eq(t2.yx, (epsilon, 1)));
740 assert!(t1.xy.approx_eq(t2.xy, (epsilon, 1)));
741 assert!(t1.yy.approx_eq(t2.yy, (epsilon, 1)));
742 assert!(t1.x0.approx_eq(t2.x0, (epsilon, 1)));
743 assert!(t1.y0.approx_eq(t2.y0, (epsilon, 1)));
744 }
745
746 #[test]
747 fn test_multiply() {
748 let t1 = Transform::identity();
749 let t2 = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
750 assert_transform_eq(&Transform::multiply(&t1, &t2), &t2);
751 assert_transform_eq(&Transform::multiply(&t2, &t1), &t2);
752
753 let t1 = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
754 let t2 = Transform::new_unchecked(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
755 let r = Transform::new_unchecked(0.0, 0.0, 0.0, 0.0, 5.0, 6.0);
756 assert_transform_eq(&Transform::multiply(&t1, &t2), &t2);
757 assert_transform_eq(&Transform::multiply(&t2, &t1), &r);
758
759 let t1 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 10.0, 10.0);
760 let t2 = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -10.0, -10.0);
761 let r1 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 0.0, 0.0);
762 let r2 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 5.0, 5.0);
763 assert_transform_eq(&Transform::multiply(&t1, &t2), &r1);
764 assert_transform_eq(&Transform::multiply(&t2, &t1), &r2);
765 }
766
767 #[test]
768 fn test_invert() {
769 let t = Transform::new_unchecked(2.0, 0.0, 0.0, 0.0, 0.0, 0.0);
770 assert!(!t.is_invertible());
771 assert!(t.invert().is_none());
772
773 let t = Transform::identity();
774 assert!(t.is_invertible());
775 assert!(t.invert().is_some());
776 let i = t.invert().unwrap();
777 assert_transform_eq(&i, &Transform::identity());
778
779 let t = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
780 assert!(t.is_invertible());
781 assert!(t.invert().is_some());
782 let i = t.invert().unwrap();
783 assert_transform_eq(&t.pre_transform(&i), &Transform::identity());
784 assert_transform_eq(&t.post_transform(&i), &Transform::identity());
785 }
786
787 #[test]
788 pub fn test_transform_point() {
789 let t = Transform::new_translate(10.0, 10.0);
790 assert_eq!((11.0, 11.0), t.transform_point(1.0, 1.0));
791 }
792
793 #[test]
794 pub fn test_transform_distance() {
795 let t = Transform::new_translate(10.0, 10.0).pre_scale(2.0, 1.0);
796 assert_eq!((2.0, 1.0), t.transform_distance(1.0, 1.0));
797 }
798
799 #[test]
800 fn parses_valid_transform() {
801 let t = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
802 let s = Transform::new_unchecked(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
803 let r = rotation_transform(30.0, 10.0, 10.0);
804
805 let a = Transform::multiply(&s, &t);
806 assert_transform_eq(
807 &parse_transform_attr("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
808 &Transform::multiply(&r, &a),
809 );
810 }
811
812 fn assert_parse_error(s: &str) {
813 assert!(parse_transform_attr(s).is_err());
814 }
815
816 #[test]
817 fn syntax_error_yields_parse_error() {
818 assert_parse_error("foo");
819 assert_parse_error("matrix (1 2 3 4 5)");
820 assert_parse_error("translate(1 2 3 4 5)");
821 assert_parse_error("translate (1,)");
822 assert_parse_error("scale (1,)");
823 assert_parse_error("skewX (1,2)");
824 assert_parse_error("skewY ()");
825 assert_parse_error("skewY");
826 }
827
828 #[test]
829 fn parses_matrix() {
830 assert_transform_eq(
831 &parse_transform_attr("matrix (1 2 3 4 5 6)").unwrap(),
832 &Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
833 );
834
835 assert_transform_eq(
836 &parse_transform_attr("matrix(1,2,3,4 5 6)").unwrap(),
837 &Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
838 );
839
840 assert_transform_eq(
841 &parse_transform_attr("matrix (1,2.25,-3.25e2,4 5 6)").unwrap(),
842 &Transform::new_unchecked(1.0, 2.25, -325.0, 4.0, 5.0, 6.0),
843 );
844 }
845
846 #[test]
847 fn parses_translate() {
848 assert_transform_eq(
849 &parse_transform_attr("translate(-1 -2)").unwrap(),
850 &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
851 );
852
853 assert_transform_eq(
854 &parse_transform_attr("translate(-1, -2)").unwrap(),
855 &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
856 );
857
858 assert_transform_eq(
859 &parse_transform_attr("translate(-1)").unwrap(),
860 &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, 0.0),
861 );
862 }
863
864 #[test]
865 fn parses_scale() {
866 assert_transform_eq(
867 &parse_transform_attr("scale (-1)").unwrap(),
868 &Transform::new_unchecked(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0),
869 );
870
871 assert_transform_eq(
872 &parse_transform_attr("scale(-1 -2)").unwrap(),
873 &Transform::new_unchecked(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
874 );
875
876 assert_transform_eq(
877 &parse_transform_attr("scale(-1, -2)").unwrap(),
878 &Transform::new_unchecked(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
879 );
880 }
881
882 #[test]
883 fn parses_rotate() {
884 assert_transform_eq(
885 &parse_transform_attr("rotate (30)").unwrap(),
886 &rotation_transform(30.0, 0.0, 0.0),
887 );
888 assert_transform_eq(
889 &parse_transform_attr("rotate (30,-1,-2)").unwrap(),
890 &rotation_transform(30.0, -1.0, -2.0),
891 );
892 assert_transform_eq(
893 &parse_transform_attr("rotate(30, -1, -2)").unwrap(),
894 &rotation_transform(30.0, -1.0, -2.0),
895 );
896 }
897
898 #[test]
899 fn parses_skew_x() {
900 assert_transform_eq(
901 &parse_transform_attr("skewX (30)").unwrap(),
902 &Transform::new_skew(Angle::from_degrees(30.0), Angle::new(0.0)),
903 );
904 }
905
906 #[test]
907 fn parses_skew_y() {
908 assert_transform_eq(
909 &parse_transform_attr("skewY (30)").unwrap(),
910 &Transform::new_skew(Angle::new(0.0), Angle::from_degrees(30.0)),
911 );
912 }
913
914 #[test]
915 fn parses_transform_list() {
916 let t = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
917 let s = Transform::new_unchecked(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
918 let r = rotation_transform(30.0, 10.0, 10.0);
919
920 assert_transform_eq(
921 &parse_transform_attr("scale(10)rotate(30, 10, 10)").unwrap(),
922 &Transform::multiply(&r, &s),
923 );
924
925 assert_transform_eq(
926 &parse_transform_attr("translate(20, 30), scale (10)").unwrap(),
927 &Transform::multiply(&s, &t),
928 );
929
930 let a = Transform::multiply(&s, &t);
931 assert_transform_eq(
932 &parse_transform_attr("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
933 &Transform::multiply(&r, &a),
934 );
935 }
936
937 #[test]
938 fn parses_empty() {
939 assert_transform_eq(&parse_transform_attr("").unwrap(), &Transform::identity());
940 }
941
942 #[test]
943 fn garbage_transform_attr_is_error() {
944 assert!(parse_transform_attr("123").is_err());
945 assert!(parse_transform_attr("translate").is_err());
946 assert!(parse_transform_attr("&").is_err());
947 assert!(parse_transform_attr("plonk()").is_err());
948 assert!(parse_transform_attr("plonk ()").is_err());
949 }
950
951 #[test]
952 fn test_parse_transform_property_none() {
953 assert_eq!(
954 parse_transform_prop("none").unwrap(),
955 TransformProperty::None
956 );
957 }
958
959 #[test]
960 fn none_transform_is_identity() {
961 assert_eq!(
962 parse_transform_prop("none").unwrap().to_transform(),
963 Transform::identity()
964 );
965 }
966
967 #[test]
968 fn empty_transform_property_is_error() {
969 assert!(parse_transform_prop("").is_err());
974 }
975
976 #[test]
977 fn garbage_transform_property_is_error() {
978 assert!(parse_transform_prop("123").is_err());
979 assert!(parse_transform_prop("translate").is_err());
980 assert!(parse_transform_prop("&").is_err());
981 assert!(parse_transform_prop("plonk()").is_err());
982 }
983
984 #[test]
985 fn test_parse_transform_property_matrix() {
986 let tp = TransformProperty::List(vec![TransformFunction::Matrix(
987 Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
988 )]);
989
990 assert_eq!(&tp, &parse_transform_prop("matrix(1,2,3,4,5,6)").unwrap());
991 assert_eq!(
992 tp.to_transform(),
993 Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
994 );
995
996 assert!(parse_transform_prop("matrix(1 2 3 4 5 6)").is_err());
997 assert!(parse_transform_prop("Matrix(1,2,3,4,5,6)").is_err());
998 }
999
1000 #[test]
1001 fn test_parse_transform_property_translate() {
1002 let tpt = TransformProperty::List(vec![TransformFunction::Translate(
1003 Length::<Horizontal>::new(100.0, LengthUnit::Px),
1004 Length::<Vertical>::new(200.0, LengthUnit::Px),
1005 )]);
1006
1007 assert_eq!(
1008 &tpt,
1009 &parse_transform_prop("translate(100px,200px)").unwrap()
1010 );
1011
1012 assert_eq!(
1013 parse_transform_prop("translate(1)").unwrap(),
1014 parse_transform_prop("translate(1, 0)").unwrap()
1015 );
1016
1017 assert_eq!(tpt.to_transform(), Transform::new_translate(100.0, 200.0));
1018
1019 assert!(parse_transform_prop("translate(100, foo)").is_err());
1020 assert!(parse_transform_prop("translate(100, )").is_err());
1021 assert!(parse_transform_prop("translate(100 200)").is_err());
1022 assert!(parse_transform_prop("translate(1px,2px,3px,4px)").is_err());
1023 }
1024
1025 #[test]
1026 fn test_parse_transform_property_translate_x() {
1027 let tptx = TransformProperty::List(vec![TransformFunction::TranslateX(
1028 Length::<Horizontal>::new(100.0, LengthUnit::Px),
1029 )]);
1030
1031 assert_eq!(&tptx, &parse_transform_prop("translateX(100px)").unwrap());
1032
1033 assert_eq!(tptx.to_transform(), Transform::new_translate(100.0, 0.0));
1034
1035 assert!(parse_transform_prop("translateX(1)").is_ok());
1036 assert!(parse_transform_prop("translateX(100 100)").is_err());
1037 assert!(parse_transform_prop("translatex(1px)").is_err());
1038 assert!(parse_transform_prop("translatex(1rad)").is_err());
1039 }
1040
1041 #[test]
1042 fn test_parse_transform_property_translate_y() {
1043 let tpty = TransformProperty::List(vec![TransformFunction::TranslateY(
1044 Length::<Vertical>::new(100.0, LengthUnit::Px),
1045 )]);
1046
1047 assert_eq!(&tpty, &parse_transform_prop("translateY(100px)").unwrap());
1048
1049 assert_eq!(tpty.to_transform(), Transform::new_translate(0.0, 100.0));
1050
1051 assert!(parse_transform_prop("translateY(1)").is_ok());
1052 assert!(parse_transform_prop("translateY(100 100)").is_err());
1053 assert!(parse_transform_prop("translatey(1px)").is_err());
1054 }
1055
1056 #[test]
1057 fn test_translate_only_supports_pixel_units() {
1058 assert!(parse_transform_prop("translate(1in, 2)").is_err());
1059 assert!(parse_transform_prop("translate(1, 2in)").is_err());
1060 assert!(parse_transform_prop("translateX(1cm)").is_err());
1061 assert!(parse_transform_prop("translateY(1cm)").is_err());
1062 }
1063
1064 #[test]
1065 fn test_parse_transform_property_scale() {
1066 let tps = TransformProperty::List(vec![TransformFunction::Scale(1.0, 10.0)]);
1067
1068 assert_eq!(&tps, &parse_transform_prop("scale(1,10)").unwrap());
1069
1070 assert_eq!(
1071 parse_transform_prop("scale(2)").unwrap(),
1072 parse_transform_prop("scale(2, 2)").unwrap()
1073 );
1074
1075 assert_eq!(tps.to_transform(), Transform::new_scale(1.0, 10.0));
1076
1077 assert!(parse_transform_prop("scale(100, foo)").is_err());
1078 assert!(parse_transform_prop("scale(100, )").is_err());
1079 assert!(parse_transform_prop("scale(1 10)").is_err());
1080 assert!(parse_transform_prop("scale(1px,10px)").is_err());
1081 assert!(parse_transform_prop("scale(1%)").is_err());
1082 }
1083
1084 #[test]
1085 fn test_parse_transform_property_scale_x() {
1086 let tpsx = TransformProperty::List(vec![TransformFunction::ScaleX(10.0)]);
1087
1088 assert_eq!(&tpsx, &parse_transform_prop("scaleX(10)").unwrap());
1089
1090 assert_eq!(tpsx.to_transform(), Transform::new_scale(10.0, 1.0));
1091
1092 assert!(parse_transform_prop("scaleX(100 100)").is_err());
1093 assert!(parse_transform_prop("scalex(10)").is_err());
1094 assert!(parse_transform_prop("scaleX(10px)").is_err());
1095 }
1096
1097 #[test]
1098 fn test_parse_transform_property_scale_y() {
1099 let tpsy = TransformProperty::List(vec![TransformFunction::ScaleY(10.0)]);
1100
1101 assert_eq!(&tpsy, &parse_transform_prop("scaleY(10)").unwrap());
1102
1103 assert_eq!(tpsy.to_transform(), Transform::new_scale(1.0, 10.0));
1104
1105 assert!(parse_transform_prop("scaleY(10 1)").is_err());
1106 assert!(parse_transform_prop("scaleY(1px)").is_err());
1107 }
1108
1109 #[test]
1110 fn test_parse_transform_property_rotate() {
1111 let tpr =
1112 TransformProperty::List(vec![TransformFunction::Rotate(Angle::from_degrees(100.0))]);
1113
1114 assert_eq!(&tpr, &parse_transform_prop("rotate(100deg)").unwrap());
1115
1116 assert_eq!(
1117 tpr.to_transform(),
1118 Transform::new_rotate(Angle::from_degrees(100.0))
1119 );
1120
1121 assert!(parse_transform_prop("rotate(100deg 100)").is_err());
1122 assert!(parse_transform_prop("rotate(3px)").is_err());
1123 }
1124
1125 #[test]
1126 fn test_parse_transform_property_skew() {
1127 let tpsk = TransformProperty::List(vec![TransformFunction::Skew(
1128 Angle::from_degrees(90.0),
1129 Angle::from_degrees(120.0),
1130 )]);
1131
1132 assert_eq!(&tpsk, &parse_transform_prop("skew(90deg,120deg)").unwrap());
1133
1134 assert_eq!(
1135 parse_transform_prop("skew(45deg)").unwrap(),
1136 parse_transform_prop("skew(45deg, 0)").unwrap()
1137 );
1138
1139 assert!(parse_transform_prop("skew(1.0,1.0)").is_ok());
1140 assert!(parse_transform_prop("skew(1rad,1rad)").is_ok());
1141
1142 assert_eq!(
1143 tpsk.to_transform(),
1144 Transform::new_skew(Angle::from_degrees(90.0), Angle::from_degrees(120.0))
1145 );
1146
1147 assert!(parse_transform_prop("skew(100, foo)").is_err());
1148 assert!(parse_transform_prop("skew(100, )").is_err());
1149 assert!(parse_transform_prop("skew(1.0px)").is_err());
1150 assert!(parse_transform_prop("skew(1.0,1.0,1deg)").is_err());
1151 }
1152
1153 #[test]
1154 fn test_parse_transform_property_skew_x() {
1155 let tpskx =
1156 TransformProperty::List(vec![TransformFunction::SkewX(Angle::from_degrees(90.0))]);
1157
1158 assert_eq!(&tpskx, &parse_transform_prop("skewX(90deg)").unwrap());
1159 assert!(parse_transform_prop("skewX(1.0)").is_ok());
1160 assert!(parse_transform_prop("skewX(1rad)").is_ok());
1161
1162 assert_eq!(
1163 tpskx.to_transform(),
1164 Transform::new_skew(Angle::from_degrees(90.0), Angle::from_degrees(0.0))
1165 );
1166
1167 assert!(parse_transform_prop("skewx(1.0)").is_err());
1168 assert!(parse_transform_prop("skewX(1.0,1.0)").is_err());
1169 }
1170
1171 #[test]
1172 fn test_parse_transform_property_skew_y() {
1173 let tpsky =
1174 TransformProperty::List(vec![TransformFunction::SkewY(Angle::from_degrees(90.0))]);
1175
1176 assert_eq!(&tpsky, &parse_transform_prop("skewY(90deg)").unwrap());
1177 assert!(parse_transform_prop("skewY(1.0)").is_ok());
1178 assert!(parse_transform_prop("skewY(1rad)").is_ok());
1179
1180 assert_eq!(
1181 tpsky.to_transform(),
1182 Transform::new_skew(Angle::from_degrees(0.0), Angle::from_degrees(90.0))
1183 );
1184
1185 assert!(parse_transform_prop("skewy(1.0)").is_err());
1186 assert!(parse_transform_prop("skewY(1.0,1.0)").is_err());
1187 }
1188}