use crate::{Error, ErrorCode, Rect};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Affine([f64; 6]);
impl Affine {
pub const IDENTITY: Self = Self([1., 0., 0., 1., 0., 0.]);
pub fn values(self) -> [f64; 6] {
self.0
}
pub fn new(values: [f64; 6]) -> Result<Self, Error> {
if values.iter().any(|value| !value.is_finite()) {
return Err(invalid("变换矩阵包含非有限值"));
}
Ok(Self(values))
}
pub fn point(self, point: [f64; 2]) -> Result<[f64; 2], Error> {
let [a, b, c, d, e, f] = self.0;
let [x, y] = point;
let result = [a * x + c * y + e, b * x + d * y + f];
if result.iter().any(|value| !value.is_finite()) {
return Err(invalid("点变换产生非有限坐标"));
}
Ok(result)
}
pub fn then(self, next: Self) -> Result<Self, Error> {
let [a, b, c, d, e, f] = self.0;
let [g, h, i, j, k, l] = next.0;
Self::new([
g * a + i * b,
h * a + j * b,
g * c + i * d,
h * c + j * d,
g * e + i * f + k,
h * e + j * f + l,
])
}
pub fn inverse(self) -> Result<Self, Error> {
let [a, b, c, d, e, f] = self.0;
let determinant = a * d - b * c;
if determinant == 0. || !determinant.is_finite() {
return Err(invalid("变换矩阵不可逆"));
}
Self::new([
d / determinant,
-b / determinant,
-c / determinant,
a / determinant,
(c * f - d * e) / determinant,
(b * e - a * f) / determinant,
])
}
pub fn rect(self, rect: Rect) -> Result<Rect, Error> {
let [left, bottom, right, top] = rect.into();
bounds(&[
self.point([left, bottom])?,
self.point([left, top])?,
self.point([right, bottom])?,
self.point([right, top])?,
])
}
}
pub fn page_transform(bbox: Rect, rotation: u16) -> Result<Affine, Error> {
let [left, bottom, right, top] = bbox.into();
if right <= left || top <= bottom {
return Err(invalid("页面框必须有正面积"));
}
Affine::new(match rotation {
0 => [1., 0., 0., -1., -left, top],
90 => [0., 1., 1., 0., -bottom, -left],
180 => [-1., 0., 0., 1., right, -bottom],
270 => [0., -1., -1., 0., top, right],
_ => return Err(invalid("页面旋转必须为 0/90/180/270")),
})
}
pub fn bounds(points: &[[f64; 2]]) -> Result<Rect, Error> {
let first = points.first().ok_or_else(|| invalid("几何点集为空"))?;
let mut values = [first[0], first[1], first[0], first[1]];
for [x, y] in points {
if !x.is_finite() || !y.is_finite() {
return Err(invalid("点集包含非有限值"));
}
values[0] = values[0].min(*x);
values[1] = values[1].min(*y);
values[2] = values[2].max(*x);
values[3] = values[3].max(*y);
}
Rect::try_from(values)
}
pub fn intersect(first: Rect, second: Rect) -> Result<Rect, Error> {
let [a, b, c, d] = first.into();
let [e, f, g, h] = second.into();
let left = a.max(e);
let bottom = b.max(f);
Rect::try_from([left, bottom, c.min(g).max(left), d.min(h).max(bottom)])
}
fn invalid(message: &str) -> Error {
Error {
code: ErrorCode::InvalidInput,
message: message.into(),
}
}