<commit_before>//! Handling of `transform` values.
//!
//! This module handles `transform` values [per the SVG specification][spec].
//!
//! [spec]:  https://www.w3.org/TR/SVG11/coords.html#TransformAttribute

use cssparser::{Parser, Token};

use crate::angle::Angle;
use crate::error::*;
use crate::parsers::{optional_comma, Parse};
use crate::rect::Rect;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Transform {
    pub xx: f64,
    pub yx: f64,
    pub xy: f64,
    pub yy: f64,
    pub x0: f64,
    pub y0: f64,
}

impl Transform {
    #[inline]
    pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
        Self {
            xx,
            xy,
            x0,
            yx,
            yy,
            y0,
        }
    }

    #[inline]
    pub fn identity() -> Self {
        Self::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
    }

    #[inline]
    pub fn new_translate(tx: f64, ty: f64) -> Self {
        Self::new(1.0, 0.0, 0.0, 1.0, tx, ty)
    }

    #[inline]
    pub fn new_scale(sx: f64, sy: f64) -> Self {
        Self::new(sx, 0.0, 0.0, sy, 0.0, 0.0)
    }

    #[inline]
    pub fn new_rotate(a: Angle) -> Self {
        let (s, c) = a.radians().sin_cos();
        Self::new(c, s, -s, c, 0.0, 0.0)
    }

    #[inline]
    pub fn new_skew(ax: Angle, ay: Angle) -> Self {
        Self::new(1.0, ay.radians().tan(), ax.radians().tan(), 1.0, 0.0, 0.0)
    }

    #[must_use]
    pub fn multiply(t1: &Transform, t2: &Transform) -> Self {
        Transform {
            xx: t1.xx * t2.xx + t1.yx * t2.xy,
            yx: t1.xx * t2.yx + t1.yx * t2.yy,
            xy: t1.xy * t2.xx + t1.yy * t2.xy,
            yy: t1.xy * t2.yx + t1.yy * t2.yy,
            x0: t1.x0 * t2.xx + t1.y0 * t2.xy + t2.x0,
            y0: t1.x0 * t2.yx + t1.y0 * t2.yy + t2.y0,
        }
    }

    #[inline]
    pub fn pre_transform(&self, t: &Transform) -> Self {
        Self::multiply(t, self)
    }

    #[inline]
    pub fn post_transform(&self, t: &Transform) -> Self {
        Self::multiply(self, t)
    }

    #[inline]
    pub fn pre_translate(&self, x: f64, y: f64) -> Self {
        self.pre_transform(&Transform::new_translate(x, y))
    }

    #[inline]
    pub fn pre_scale(&self, sx: f64, sy: f64) -> Self {
        self.pre_transform(&Transform::new_scale(sx, sy))
    }

    #[inline]
    pub fn pre_rotate(&self, angle: Angle) -> Self {
        self.pre_transform(&Transform::new_rotate(angle))
    }

    #[inline]
    pub fn post_translate(&self, x: f64, y: f64) -> Self {
        self.post_transform(&Transform::new_translate(x, y))
    }

    #[inline]
    pub fn post_scale(&self, sx: f64, sy: f64) -> Self {
        self.post_transform(&Transform::new_scale(sx, sy))
    }

    #[inline]
    pub fn post_rotate(&self, angle: Angle) -> Self {
        self.post_transform(&Transform::new_rotate(angle))
    }

    #[inline]
    fn determinant(&self) -> f64 {
        self.xx * self.yy - self.xy * self.yx
    }

    #[inline]
    pub fn is_invertible(&self) -> bool {
        let det = self.determinant();

        det != 0.0 && det.is_finite()
    }

    #[must_use]
    pub fn invert(&self) -> Option<Self> {
        let det = self.determinant();

        if det == 0.0 || !det.is_finite() {
            return None;
        }

        let inv_det = 1.0 / det;

        Some(Transform::new(
            inv_det * self.yy,
            inv_det * (-self.yx),
            inv_det * (-self.xy),
            inv_det * self.xx,
            inv_det * (self.xy * self.y0 - self.yy * self.x0),
            inv_det * (self.yx * self.x0 - self.xx * self.y0),
        ))
    }

    #[inline]
    pub fn transform_distance(&self, dx: f64, dy: f64) -> (f64, f64) {
        (dx * self.xx + dy * self.xy, dx * self.yx + dy * self.yy)
    }

    #[inline]
    pub fn transform_point(&self, px: f64, py: f64) -> (f64, f64) {
        let (x, y) = self.transform_distance(px, py);
        (x + self.x0, y + self.y0)
    }

    pub fn transform_rect(&self, rect: &Rect) -> Rect {
        let points = vec![
            self.transform_point(rect.x0, rect.y0),
            self.transform_point(rect.x1, rect.y0),
            self.transform_point(rect.x0, rect.y1),
            self.transform_point(rect.x1, rect.y1),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().take(4).skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;<commit_msg>transform_rect() - Avoid an allocation and simplify the loop a bit<commit_after>//! Handling of `transform` values.
//!
//! This module handles `transform` values [per the SVG specification][spec].
//!
//! [spec]:  https://www.w3.org/TR/SVG11/coords.html#TransformAttribute

use cssparser::{Parser, Token};

use crate::angle::Angle;
use crate::error::*;
use crate::parsers::{optional_comma, Parse};
use crate::rect::Rect;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Transform {
    pub xx: f64,
    pub yx: f64,
    pub xy: f64,
    pub yy: f64,
    pub x0: f64,
    pub y0: f64,
}

impl Transform {
    #[inline]
    pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
        Self {
            xx,
            xy,
            x0,
            yx,
            yy,
            y0,
        }
    }

    #[inline]
    pub fn identity() -> Self {
        Self::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
    }

    #[inline]
    pub fn new_translate(tx: f64, ty: f64) -> Self {
        Self::new(1.0, 0.0, 0.0, 1.0, tx, ty)
    }

    #[inline]
    pub fn new_scale(sx: f64, sy: f64) -> Self {
        Self::new(sx, 0.0, 0.0, sy, 0.0, 0.0)
    }

    #[inline]
    pub fn new_rotate(a: Angle) -> Self {
        let (s, c) = a.radians().sin_cos();
        Self::new(c, s, -s, c, 0.0, 0.0)
    }

    #[inline]
    pub fn new_skew(ax: Angle, ay: Angle) -> Self {
        Self::new(1.0, ay.radians().tan(), ax.radians().tan(), 1.0, 0.0, 0.0)
    }

    #[must_use]
    pub fn multiply(t1: &Transform, t2: &Transform) -> Self {
        Transform {
            xx: t1.xx * t2.xx + t1.yx * t2.xy,
            yx: t1.xx * t2.yx + t1.yx * t2.yy,
            xy: t1.xy * t2.xx + t1.yy * t2.xy,
            yy: t1.xy * t2.yx + t1.yy * t2.yy,
            x0: t1.x0 * t2.xx + t1.y0 * t2.xy + t2.x0,
            y0: t1.x0 * t2.yx + t1.y0 * t2.yy + t2.y0,
        }
    }

    #[inline]
    pub fn pre_transform(&self, t: &Transform) -> Self {
        Self::multiply(t, self)
    }

    #[inline]
    pub fn post_transform(&self, t: &Transform) -> Self {
        Self::multiply(self, t)
    }

    #[inline]
    pub fn pre_translate(&self, x: f64, y: f64) -> Self {
        self.pre_transform(&Transform::new_translate(x, y))
    }

    #[inline]
    pub fn pre_scale(&self, sx: f64, sy: f64) -> Self {
        self.pre_transform(&Transform::new_scale(sx, sy))
    }

    #[inline]
    pub fn pre_rotate(&self, angle: Angle) -> Self {
        self.pre_transform(&Transform::new_rotate(angle))
    }

    #[inline]
    pub fn post_translate(&self, x: f64, y: f64) -> Self {
        self.post_transform(&Transform::new_translate(x, y))
    }

    #[inline]
    pub fn post_scale(&self, sx: f64, sy: f64) -> Self {
        self.post_transform(&Transform::new_scale(sx, sy))
    }

    #[inline]
    pub fn post_rotate(&self, angle: Angle) -> Self {
        self.post_transform(&Transform::new_rotate(angle))
    }

    #[inline]
    fn determinant(&self) -> f64 {
        self.xx * self.yy - self.xy * self.yx
    }

    #[inline]
    pub fn is_invertible(&self) -> bool {
        let det = self.determinant();

        det != 0.0 && det.is_finite()
    }

    #[must_use]
    pub fn invert(&self) -> Option<Self> {
        let det = self.determinant();

        if det == 0.0 || !det.is_finite() {
            return None;
        }

        let inv_det = 1.0 / det;

        Some(Transform::new(
            inv_det * self.yy,
            inv_det * (-self.yx),
            inv_det * (-self.xy),
            inv_det * self.xx,
            inv_det * (self.xy * self.y0 - self.yy * self.x0),
            inv_det * (self.yx * self.x0 - self.xx * self.y0),
        ))
    }

    #[inline]
    pub fn transform_distance(&self, dx: f64, dy: f64) -> (f64, f64) {
        (dx * self.xx + dy * self.xy, dx * self.yx + dy * self.yy)
    }

    #[inline]
    pub fn transform_point(&self, px: f64, py: f64) -> (f64, f64) {
        let (x, y) = self.transform_distance(px, py);
        (x + self.x0, y + self.y0)
    }

    pub fn transform_rect(&self, rect: &Rect) -> Rect {
        let points = [
            self.transform_point(rect.x0, rect.y0),
            self.transform_point(rect.x1, rect.y0),
            self.transform_point(rect.x0, rect.y1),
            self.transform_point(rect.x1, rect.y1),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;
{'fraction_non_alphanumeric': 0.16640186420929987, 'fraction_numerical': 0.03675458108251244, 'mean_word_length': 1.6892623184277984, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 51, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2207489', 'n_tokens_mistral': 3772, 'n_tokens_neox': 3581, 'n_words': 843}