| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::{error::Error, fmt}; |
| |
| use crate::{path::MAX_ERROR, AffineTransform, Point, MAX_HEIGHT, MAX_WIDTH}; |
| |
| const MAX_SCALING_FACTOR_X: f32 = 1.0 + MAX_ERROR as f32 / MAX_WIDTH as f32; |
| const MAX_SCALING_FACTOR_Y: f32 = 1.0 + MAX_ERROR as f32 / MAX_HEIGHT as f32; |
| |
| #[derive(Debug, Eq, PartialEq)] |
| pub enum GeomPresTransformError { |
| ExceededScalingFactor { x: bool, y: bool }, |
| } |
| |
| impl fmt::Display for GeomPresTransformError { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match self { |
| GeomPresTransformError::ExceededScalingFactor { x: true, y: false } => { |
| write!(f, "exceeded scaling factor on the X axis (-1.0 to 1.0)") |
| } |
| GeomPresTransformError::ExceededScalingFactor { x: false, y: true } => { |
| write!(f, "exceeded scaling factor on the Y axis (-1.0 to 1.0)") |
| } |
| GeomPresTransformError::ExceededScalingFactor { x: true, y: true } => { |
| write!(f, "exceeded scaling factor on both axis (-1.0 to 1.0)") |
| } |
| _ => panic!("cannot display invalid GeomPresTransformError"), |
| } |
| } |
| } |
| |
| impl Error for GeomPresTransformError {} |
| |
| #[derive(Default, Clone, Copy, Debug, Eq, PartialEq)] |
| pub struct GeomPresTransform(pub(crate) AffineTransform); |
| |
| impl GeomPresTransform { |
| /// ```text |
| /// [ x' ] [ t.0 t.1 t.4 ] [ x ] |
| /// [ y' ] = [ t.2 t.3 t.5 ] [ y ] |
| /// [ 1 ] [ 0 0 1 ] [ 1 ] |
| /// ``` |
| #[inline] |
| pub fn new(mut transform: [f32; 9]) -> Option<Self> { |
| (transform[6].abs() <= f32::EPSILON && transform[7].abs() <= f32::EPSILON) |
| .then(|| { |
| if (transform[8] - 1.0).abs() > f32::EPSILON { |
| let recip = transform[8].recip(); |
| for val in &mut transform[..6] { |
| *val *= recip; |
| } |
| } |
| |
| Self::try_from(AffineTransform { |
| ux: transform[0], |
| vx: transform[1], |
| uy: transform[3], |
| vy: transform[4], |
| tx: transform[2], |
| ty: transform[5], |
| }) |
| .ok() |
| }) |
| .flatten() |
| } |
| |
| pub fn is_identity(&self) -> bool { |
| self.0.is_identity() |
| } |
| |
| pub(crate) fn transform(&self, point: Point) -> Point { |
| self.0.transform(point) |
| } |
| |
| #[inline] |
| pub fn as_slice(&self) -> [f32; 6] { |
| [self.0.ux, self.0.vx, self.0.uy, self.0.vy, self.0.tx, self.0.ty] |
| } |
| } |
| |
| impl TryFrom<[f32; 6]> for GeomPresTransform { |
| type Error = GeomPresTransformError; |
| fn try_from(transform: [f32; 6]) -> Result<Self, Self::Error> { |
| GeomPresTransform::try_from(AffineTransform::from(transform)) |
| } |
| } |
| |
| impl TryFrom<AffineTransform> for GeomPresTransform { |
| type Error = GeomPresTransformError; |
| |
| fn try_from(t: AffineTransform) -> Result<Self, Self::Error> { |
| let scales_up_x = t.ux * t.ux + t.uy * t.uy > MAX_SCALING_FACTOR_X; |
| let scales_up_y = t.vx * t.vx + t.vy * t.vy > MAX_SCALING_FACTOR_Y; |
| |
| (!scales_up_x && !scales_up_y) |
| .then(|| Self(t)) |
| .ok_or(GeomPresTransformError::ExceededScalingFactor { x: scales_up_x, y: scales_up_y }) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn default_identity() { |
| let transform = GeomPresTransform::default(); |
| |
| assert_eq!(transform.transform(Point::new(2.0, 3.0)), Point::new(2.0, 3.0)); |
| } |
| |
| #[test] |
| fn as_slice() { |
| let slice = [0.1, 0.5, 0.4, 0.3, 0.7, 0.9]; |
| |
| assert_eq!(slice, GeomPresTransform::try_from(slice).unwrap().as_slice()); |
| } |
| |
| #[test] |
| fn scale_translate() { |
| let transform = GeomPresTransform::try_from([0.1, 0.5, 0.4, 0.3, 0.5, 0.6]).unwrap(); |
| |
| assert_eq!(transform.transform(Point::new(2.0, 3.0)), Point::new(2.2, 2.3)); |
| } |
| |
| #[test] |
| fn wrong_scaling_factor() { |
| let transform = |
| [0.1, MAX_SCALING_FACTOR_Y.sqrt(), MAX_SCALING_FACTOR_X.sqrt(), 0.1, 0.5, 0.0]; |
| |
| assert_eq!( |
| GeomPresTransform::try_from(transform), |
| Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: true }) |
| ); |
| } |
| |
| #[test] |
| fn wrong_scaling_factor_x() { |
| let transform = [0.1, 0.0, MAX_SCALING_FACTOR_X.sqrt(), 0.0, 0.5, 0.0]; |
| |
| assert_eq!( |
| GeomPresTransform::try_from(transform), |
| Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: false }) |
| ); |
| } |
| |
| #[test] |
| fn wrong_scaling_factor_y() { |
| let transform = [0.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.1, 0.5, 0.0]; |
| |
| assert_eq!( |
| GeomPresTransform::try_from(transform), |
| Err(GeomPresTransformError::ExceededScalingFactor { x: false, y: true }) |
| ); |
| } |
| |
| #[test] |
| fn correct_scaling_factor() { |
| let transform = [1.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.0, 0.5, 0.0]; |
| |
| assert_eq!(transform, GeomPresTransform::try_from(transform).unwrap().as_slice()); |
| } |
| } |