use super::FixedPoint;
use super::FixedVector;
use super::FixedMatrix;
use super::linalg::compute_tier_dot_raw;
use crate::fixed_point::universal::fasc::stack_evaluator::BinaryStorage;
use crate::fixed_point::core_types::errors::OverflowDetected;
pub fn to_homogeneous(v: &FixedVector) -> FixedVector {
let n = v.len();
let mut h = FixedVector::new(n + 1);
for i in 0..n {
h[i] = v[i];
}
h[n] = FixedPoint::one();
h
}
pub fn from_homogeneous(h: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let n = h.len();
if n == 0 { return Err(OverflowDetected::DomainError); }
let w = h[n - 1];
if w.is_zero() {
return Err(OverflowDetected::DomainError);
}
let mut v = FixedVector::new(n - 1);
for i in 0..n - 1 {
v[i] = h[i] / w;
}
Ok(v)
}
pub fn is_at_infinity(h: &FixedVector, tol: FixedPoint) -> bool {
let n = h.len();
if n == 0 { return true; }
h[n - 1].abs() < tol
}
pub fn projective_transform(
h_matrix: &FixedMatrix,
point: &FixedVector,
) -> Result<FixedVector, OverflowDetected> {
let hp = to_homogeneous(point);
let transformed = h_matrix.mul_vector(&hp);
from_homogeneous(&transformed)
}
pub fn projective_transform_homogeneous(
h_matrix: &FixedMatrix,
point: &FixedVector,
) -> FixedVector {
h_matrix.mul_vector(point)
}
pub fn compose_projective(
h1: &FixedMatrix,
h2: &FixedMatrix,
) -> FixedMatrix {
h1 * h2
}
pub fn cross_ratio_1d(
a: FixedPoint,
b: FixedPoint,
c: FixedPoint,
d: FixedPoint,
) -> Result<FixedPoint, OverflowDetected> {
let ac = a - c;
let bd = b - d;
let ad = a - d;
let bc = b - c;
let denom = ad * bc;
if denom.is_zero() {
return Err(OverflowDetected::DomainError);
}
Ok((ac * bd) / denom)
}
pub fn cross_ratio(
a: &FixedVector,
b: &FixedVector,
c: &FixedVector,
d: &FixedVector,
) -> Result<FixedPoint, OverflowDetected> {
let dir = b - a;
let dir_sq = dir.dot_precise(&dir);
if dir_sq.is_zero() {
return Err(OverflowDetected::DomainError);
}
let ta = FixedPoint::ZERO;
let tb = dir_sq.sqrt(); let tc = (c - a).dot_precise(&dir) / dir_sq * tb;
let td = (d - a).dot_precise(&dir) / dir_sq * tb;
cross_ratio_1d(ta, tb, tc, td)
}
pub fn stereo_project(p: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let n = p.len();
if n < 2 { return Err(OverflowDetected::DomainError); }
let pn = p[n - 1];
let denom = FixedPoint::one() - pn;
if denom.is_zero() {
return Err(OverflowDetected::DomainError); }
let mut x = FixedVector::new(n - 1);
for i in 0..n - 1 {
x[i] = p[i] / denom;
}
Ok(x)
}
pub fn stereo_unproject(x: &FixedVector) -> FixedVector {
let n = x.len();
let one = FixedPoint::one();
let two = FixedPoint::from_int(2);
let x_raw: Vec<BinaryStorage> = (0..n).map(|i| x[i].raw()).collect();
let x_sq = FixedPoint::from_raw(compute_tier_dot_raw(&x_raw, &x_raw));
let denom = one + x_sq; let mut p = FixedVector::new(n + 1);
for i in 0..n {
p[i] = two * x[i] / denom;
}
p[n] = (x_sq - one) / denom;
p
}
#[derive(Clone, Copy, Debug)]
pub struct Moebius {
pub a: FixedPoint,
pub b: FixedPoint,
pub c: FixedPoint,
pub d: FixedPoint,
}
impl Moebius {
pub fn new(a: FixedPoint, b: FixedPoint, c: FixedPoint, d: FixedPoint) -> Self {
Self { a, b, c, d }
}
pub fn identity() -> Self {
Self {
a: FixedPoint::one(),
b: FixedPoint::ZERO,
c: FixedPoint::ZERO,
d: FixedPoint::one(),
}
}
pub fn apply(&self, x: FixedPoint) -> Result<FixedPoint, OverflowDetected> {
let denom = self.c * x + self.d;
if denom.is_zero() {
return Err(OverflowDetected::DomainError);
}
Ok((self.a * x + self.b) / denom)
}
pub fn compose(&self, other: &Moebius) -> Moebius {
Moebius {
a: self.a * other.a + self.b * other.c,
b: self.a * other.b + self.b * other.d,
c: self.c * other.a + self.d * other.c,
d: self.c * other.b + self.d * other.d,
}
}
pub fn inverse(&self) -> Moebius {
Moebius {
a: self.d,
b: -self.b,
c: -self.c,
d: self.a,
}
}
pub fn determinant(&self) -> FixedPoint {
self.a * self.d - self.b * self.c
}
pub fn to_matrix(&self) -> FixedMatrix {
FixedMatrix::from_slice(2, 2, &[self.a, self.b, self.c, self.d])
}
}
#[derive(Clone, Copy, Debug)]
pub struct MoebiusComplex {
pub a: (FixedPoint, FixedPoint), pub b: (FixedPoint, FixedPoint),
pub c: (FixedPoint, FixedPoint),
pub d: (FixedPoint, FixedPoint),
}
fn complex_mul(
a: (FixedPoint, FixedPoint),
b: (FixedPoint, FixedPoint),
) -> (FixedPoint, FixedPoint) {
(a.0 * b.0 - a.1 * b.1, a.0 * b.1 + a.1 * b.0)
}
fn complex_add(
a: (FixedPoint, FixedPoint),
b: (FixedPoint, FixedPoint),
) -> (FixedPoint, FixedPoint) {
(a.0 + b.0, a.1 + b.1)
}
fn complex_div(
a: (FixedPoint, FixedPoint),
b: (FixedPoint, FixedPoint),
) -> Result<(FixedPoint, FixedPoint), OverflowDetected> {
let denom = b.0 * b.0 + b.1 * b.1;
if denom.is_zero() {
return Err(OverflowDetected::DomainError);
}
Ok((
(a.0 * b.0 + a.1 * b.1) / denom,
(a.1 * b.0 - a.0 * b.1) / denom,
))
}
impl MoebiusComplex {
pub fn new(
a: (FixedPoint, FixedPoint),
b: (FixedPoint, FixedPoint),
c: (FixedPoint, FixedPoint),
d: (FixedPoint, FixedPoint),
) -> Self {
Self { a, b, c, d }
}
pub fn apply(
&self,
z: (FixedPoint, FixedPoint),
) -> Result<(FixedPoint, FixedPoint), OverflowDetected> {
let numer = complex_add(complex_mul(self.a, z), self.b);
let denom = complex_add(complex_mul(self.c, z), self.d);
complex_div(numer, denom)
}
pub fn compose(&self, other: &MoebiusComplex) -> MoebiusComplex {
MoebiusComplex {
a: complex_add(complex_mul(self.a, other.a), complex_mul(self.b, other.c)),
b: complex_add(complex_mul(self.a, other.b), complex_mul(self.b, other.d)),
c: complex_add(complex_mul(self.c, other.a), complex_mul(self.d, other.c)),
d: complex_add(complex_mul(self.c, other.b), complex_mul(self.d, other.d)),
}
}
pub fn inverse(&self) -> MoebiusComplex {
MoebiusComplex {
a: self.d,
b: (-self.b.0, -self.b.1),
c: (-self.c.0, -self.c.1),
d: self.a,
}
}
}