use crate::mat2_col_major::add_four;
use crate::vec2::Vec2;
pub fn area<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2]) -> T
where
T: num_traits::Float,
{
let half = T::one() / (T::one() + T::one());
half * ((p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]))
}
pub fn dldw_area<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2], dldarea: T) -> ([T; 2], [T; 2], [T; 2])
where
T: num_traits::Float,
{
let two = T::one() + T::one();
let half = T::one() / two;
let dareadp0x2 = [p1[1] - p2[1], p2[0] - p1[0]];
let dareadp1x2 = [p2[1] - p0[1], p0[0] - p2[0]];
let dareadp2x2 = [p0[1] - p1[1], p1[0] - p0[0]];
(
dareadp0x2.scale(dldarea * half),
dareadp1x2.scale(dldarea * half),
dareadp2x2.scale(dldarea * half),
)
}
#[test]
fn test_dldw_area() {
let p0s = [[0.1, -0.2], [1.3, 0.2], [0.6, 0.45]];
let dldarea = 1.3f32;
let area0 = area(&p0s[0], &p0s[1], &p0s[2]);
let l0 = dldarea * area0;
let dl = dldw_area(&p0s[0], &p0s[1], &p0s[2], dldarea);
let dl = [dl.0, dl.1, dl.2];
let eps = 1.0e-4;
for inode in 0..3 {
for idim in 0..2 {
let p1s = {
let mut p1s = p0s;
p1s[inode][idim] += eps;
p1s
};
let area1 = area(&p1s[0], &p1s[1], &p1s[2]);
let l1 = dldarea * area1;
let val_num = (l1 - l0) / eps;
let val_ana = dl[inode][idim];
let diff_abs = (val_num - val_ana).abs();
assert!(diff_abs < 5.0e-4, "{}", diff_abs);
}
}
}
pub fn is_inside<Real>(
p0: &[Real; 2],
p1: &[Real; 2],
p2: &[Real; 2],
q: &[Real; 2],
sign: Real,
) -> Option<(Real, Real)>
where
Real: num_traits::Float,
{
let a0 = area(q, p1, p2) * sign;
if a0 < Real::zero() {
return None;
}
let a1 = area(q, p2, p0) * sign;
if a1 < Real::zero() {
return None;
}
let a2 = area(q, p0, p1) * sign;
if a2 < Real::zero() {
return None;
}
let sum_area_inv = Real::one() / (a0 + a1 + a2);
Some((a0 * sum_area_inv, a1 * sum_area_inv))
}
pub fn dldx<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2]) -> ([[T; 3]; 2], [T; 3])
where
T: num_traits::Float,
{
use crate::vec3::Vec3;
let half = T::one() / (T::one() + T::one());
let a0 = area(p0, p1, p2);
let tmp1: T = half / a0;
(
[
[p1[1] - p2[1], p2[1] - p0[1], p0[1] - p1[1]].scale(tmp1),
[p2[0] - p1[0], p0[0] - p2[0], p1[0] - p0[0]].scale(tmp1),
],
[
p1[0] * p2[1] - p2[0] * p1[1],
p2[0] * p0[1] - p0[0] * p2[1],
p0[0] * p1[1] - p1[0] * p0[1],
]
.scale(tmp1),
)
}
pub fn barycentric_coords<Real>(
p0: &[Real; 2],
p1: &[Real; 2],
p2: &[Real; 2],
q: &[Real; 2],
) -> Option<(Real, Real, Real)>
where
Real: num_traits::Float,
{
let a0 = area(q, p1, p2);
let a1 = area(q, p2, p0);
let a2 = area(q, p0, p1);
if (a0 + a1 + a2).is_zero() {
return None;
}
let sum_area_inv = Real::one() / (a0 + a1 + a2);
Some((a0 * sum_area_inv, a1 * sum_area_inv, a2 * sum_area_inv))
}
#[derive(Debug, Clone, Copy)]
pub struct Tri2<'a, Real> {
pub p0: &'a [Real; 2],
pub p1: &'a [Real; 2],
pub p2: &'a [Real; 2],
}
impl<Real> Tri2<'_, Real>
where
Real: num_traits::Float,
{
pub fn is_inside(&self, q: &[Real; 2], sign: Real) -> Option<(Real, Real)> {
let Tri2 { p0, p1, p2 } = self;
is_inside(p0, p1, p2, q, sign)
}
pub fn area(&self) -> Real {
let Tri2 { p0, p1, p2 } = self;
area(p0, p1, p2)
}
}
pub fn circumcenter<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2]) -> [T; 2]
where
T: num_traits::Float + Copy + std::fmt::Debug,
{
use crate::vec2::Vec2;
let a0 = p1.sub(p2).squared_norm();
let a1 = p2.sub(p0).squared_norm();
let a2 = p0.sub(p1).squared_norm();
let b0: T = a0 * (a1 + a2 - a0);
let b1: T = a1 * (a0 + a2 - a1);
let b2: T = a2 * (a0 + a1 - a2);
let sum = T::one() / (b0 + b1 + b2);
let c0 = b0 * sum;
let c1 = b1 * sum;
let c2 = b2 * sum;
[
p0[0] * c0 + p1[0] * c1 + p2[0] * c2,
p0[1] * c0 + p1[1] * c1 + p2[1] * c2,
]
}
#[test]
fn test_circumcenter() {
let p0 = [[1.3f64, 2.1], [3.2, 2.1], [1.5, 2.5]];
let cc0 = circumcenter(&p0[0], &p0[1], &p0[2]);
let d0 = cc0.sub(&p0[0]).squared_norm();
let d1 = cc0.sub(&p0[1]).squared_norm();
let d2 = cc0.sub(&p0[2]).squared_norm();
assert!((d0 - d1).abs() < d0 * 1.0e-10);
assert!((d0 - d2).abs() < d0 * 1.0e-10);
}
pub fn wdw_circumcenter<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2]) -> ([T; 2], [[T; 4]; 3])
where
T: num_traits::Float + Copy + std::fmt::Debug + num_traits::Float,
{
use crate::vec2::Vec2;
use crate::vec2::XY;
let p0 = XY { p: *p0 };
let p1 = XY { p: *p1 };
let p2 = XY { p: *p2 };
let a0 = (p1 - p2).p.squared_norm();
let a1 = (p2 - p0).p.squared_norm();
let a2 = (p0 - p1).p.squared_norm();
let b0: T = a0 * (a1 + a2 - a0);
let b1: T = a1 * (a2 + a0 - a1);
let b2: T = a2 * (a0 + a1 - a2);
let sum = b0 + b1 + b2;
let sum_inv = T::one() / sum;
let c0 = b0 * sum_inv;
let c1 = b1 * sum_inv;
let c2 = b2 * sum_inv;
let cc = crate::vec2::add_three(&p0.p.scale(c0), &p1.p.scale(c1), &p2.p.scale(c2));
let two = T::one() + T::one();
let db0 = [
(p0 - p2 + p0 - p1).p.scale(two * a0),
crate::vec2::add(
&((p1 - p0 + p2 - p1).p.scale(two * a0)),
&(p1 - p2).p.scale(two * (a1 + a2 - a0)),
),
crate::vec2::add(
&((p2 - p0 + p1 - p2).p.scale(two * a0)),
&(p2 - p1).p.scale(two * (a1 + a2 - a0)),
),
];
let db1 = [
crate::vec2::add(
&(p0 - p1 + p2 - p0).p.scale(two * a1),
&(p0 - p2).p.scale(two * (a2 + a0 - a1)),
),
(p1 - p0 + p1 - p2).p.scale(two * a1),
crate::vec2::add(
&(p2 - p1 + p0 - p2).p.scale(two * a1),
&(p2 - p0).p.scale(two * (a2 + a0 - a1)),
),
];
let db2 = [
crate::vec2::add(
&(p0 - p2 + p1 - p0).p.scale(two * a2),
&(p0 - p1).p.scale(two * (a0 + a1 - a2)),
),
crate::vec2::add(
&(p1 - p2 + p0 - p1).p.scale(two * a2),
&(p1 - p0).p.scale(two * (a0 + a1 - a2)),
),
(p2 - p1 + p2 - p0).p.scale(two * a2),
];
let tmp = -T::one() / (sum * sum);
let dsum_inv = [
crate::vec2::add_three(&db0[0], &db1[0], &db2[0]).scale(tmp),
crate::vec2::add_three(&db0[1], &db1[1], &db2[1]).scale(tmp),
crate::vec2::add_three(&db0[2], &db1[2], &db2[2]).scale(tmp),
];
use crate::mat2_col_major::{Mat2ColMajor, from_identity, from_outer_product};
use crate::vec2;
let dcc = [
add_four(
&from_identity().scale(c0),
&from_outer_product(
&p0.p,
&vec2::add(&db0[0].scale(sum_inv), &dsum_inv[0].scale(b0)),
),
&from_outer_product(
&p1.p,
&vec2::add(&db1[0].scale(sum_inv), &dsum_inv[0].scale(b1)),
),
&from_outer_product(
&p2.p,
&vec2::add(&db2[0].scale(sum_inv), &dsum_inv[0].scale(b2)),
),
),
add_four(
&from_identity().scale(c1),
&from_outer_product(
&p0.p,
&vec2::add(&db0[1].scale(sum_inv), &dsum_inv[1].scale(b0)),
),
&from_outer_product(
&p1.p,
&vec2::add(&db1[1].scale(sum_inv), &dsum_inv[1].scale(b1)),
),
&from_outer_product(
&p2.p,
&vec2::add(&db2[1].scale(sum_inv), &dsum_inv[1].scale(b2)),
),
),
add_four(
&from_identity().scale(c2),
&from_outer_product(
&p0.p,
&vec2::add(&db0[2].scale(sum_inv), &dsum_inv[2].scale(b0)),
),
&from_outer_product(
&p1.p,
&vec2::add(&db1[2].scale(sum_inv), &dsum_inv[2].scale(b1)),
),
&from_outer_product(
&p2.p,
&vec2::add(&db2[2].scale(sum_inv), &dsum_inv[2].scale(b2)),
),
),
];
(cc, dcc)
}
#[test]
fn test_dw_circumcenter() {
let p0 = [[0.1, 0.2], [1.3, 0.2], [0.3, 1.5]];
let (cc0, dcc0) = wdw_circumcenter(&p0[0], &p0[1], &p0[2]);
let eps = 1.0e-4;
for i_node in 0..3 {
for i_dim in 0..2 {
let p1 = {
let mut p1 = p0;
p1[i_node][i_dim] += eps;
p1
};
let (cc1, _dcc1) = wdw_circumcenter(&p1[0], &p1[1], &p1[2]);
let dcc_num = cc1.sub(&cc0).scale(1. / eps);
let mut b = [0f64; 2];
b[i_dim] = 1.0;
let dcc_ana = crate::mat2_col_major::mult_vec(&dcc0[i_node], &b);
let diff = dcc_num.sub(&dcc_ana).norm();
assert!(diff < 1.0e-4);
}
}
}