extern crate nalgebra as na;
use core::panic;
use egui::plot::PlotPoints;
use na::{Matrix3, Matrix4, Vector2, Vector3};
use ordered_float::NotNan;
use std::fmt;
pub trait TriangleLogic<V, E, T> {
fn area(&self) -> f64;
fn area3d(&self) -> f64;
fn edges(&self) -> Vec<E>;
fn has_edge(&self, edge: &E) -> bool;
fn has_vertex(&self, vertex: &V) -> bool;
fn is_neighbor(&self, other: &T) -> Option<E>;
fn regularity(&self, other: &V) -> f64;
fn is_regular(&self, vertex: &V) -> bool;
fn is_eps_regular(&self, vertex: &V, epsilon: f64) -> bool;
fn orientation(&self) -> f64;
fn vertices(&self) -> Vec<WeightedVertex>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WeightedVertex {
x: NotNan<f64>,
y: NotNan<f64>,
w: NotNan<f64>,
}
impl WeightedVertex {
pub fn new(x: f64, y: f64, weight: f64) -> Self {
Self {
x: NotNan::new(x).unwrap(),
y: NotNan::new(y).unwrap(),
w: NotNan::new(weight).unwrap(),
}
}
pub fn as_vec2(&self) -> Vector2<f64> {
Vector2::new(self.x(), self.y())
}
pub fn lift(&self) -> Vector3<f64> {
Vector3::new(self.x(), self.y(), self.x().powi(2) + self.y().powi(2) - self.w())
}
pub fn w(&self) -> f64 {
self.w.into_inner()
}
pub fn x(&self) -> f64 {
self.x.into_inner()
}
pub fn y(&self) -> f64 {
self.y.into_inner()
}
}
impl fmt::Display for WeightedVertex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {}, w: {})", self.x(), self.y(), self.w())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WeightedEdge {
pub a: WeightedVertex,
pub b: WeightedVertex,
triangles: [Option<WeightedTriangle>; 2], }
impl WeightedEdge {
pub fn new(a: WeightedVertex, b: WeightedVertex) -> Self {
let mut vertices = vec![a, b];
vertices.sort_unstable();
Self {
a: vertices[0],
b: vertices[1],
triangles: [None, None],
}
}
pub fn with_triangles(
a: WeightedVertex,
b: WeightedVertex,
triangles: [Option<WeightedTriangle>; 2],
) -> Self {
let mut vertices = vec![a, b];
vertices.sort_unstable();
Self {
a: vertices[0],
b: vertices[1],
triangles,
}
}
pub fn set_triangles(&mut self, triangles: [Option<WeightedTriangle>; 2]) {
self.triangles = triangles;
}
pub fn vertices(&self) -> Vec<WeightedVertex> {
vec![self.a, self.b]
}
}
impl fmt::Display for WeightedEdge {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Edge {{ {}, {} }}", self.a, self.b)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WeightedTriangle {
pub a: WeightedVertex,
pub b: WeightedVertex,
pub c: WeightedVertex,
}
impl WeightedTriangle {
pub fn new(a: WeightedVertex, b: WeightedVertex, c: WeightedVertex) -> Self {
let mut vertices = vec![a, b, c];
vertices.sort_unstable();
Self {
a: vertices[0],
b: vertices[1],
c: vertices[2],
}
}
pub fn get_third_point(&self, edge: &WeightedEdge) -> WeightedVertex {
if self.a != edge.a && self.a != edge.b {
self.a
} else if self.b != edge.a && self.b != edge.b {
self.b
} else {
self.c
}
}
}
impl TriangleLogic<WeightedVertex, WeightedEdge, WeightedTriangle> for WeightedTriangle {
fn area(&self) -> f64 {
((self.a.x() * (self.b.y() - self.c.y())
+ self.b.x() * (self.c.y() - self.a.y())
+ self.c.x() * (self.a.y() - self.b.y()))
/ 2.0)
.abs()
}
fn area3d(&self) -> f64 {
let ab = self.b.lift() - self.a.lift();
let ac = self.c.lift() - self.a.lift();
let cross = ab.cross(&ac);
let cross_norm = cross.norm();
cross_norm / 2.0
}
fn edges(&self) -> Vec<WeightedEdge> {
vec![
WeightedEdge::new(self.a, self.b),
WeightedEdge::new(self.b, self.c),
WeightedEdge::new(self.c, self.a),
]
}
fn has_edge(&self, edge: &WeightedEdge) -> bool {
for e in self.edges() {
if e == *edge {
return true;
}
}
false
}
fn has_vertex(&self, vertex: &WeightedVertex) -> bool {
self.a == *vertex || self.b == *vertex || self.c == *vertex
}
fn is_regular(&self, vertex: &WeightedVertex) -> bool {
self.regularity(vertex) < 0.0
}
fn is_eps_regular(&self, vertex: &WeightedVertex, epsilon: f64) -> bool {
self.regularity(vertex) - epsilon < 0.0
}
fn orientation(&self) -> f64 {
let matrix = Matrix3::new(
self.a.x(),
self.a.y(),
1.0,
self.b.x(),
self.b.y(),
1.0,
self.c.x(),
self.c.y(),
1.0,
);
match matrix.determinant() {
d if d > 0.0 => {
1.0 }
d if d < 0.0 => {
-1.0 }
_ => panic!("Triangle consists of three collinear points"),
}
}
fn regularity(&self, vertex: &WeightedVertex) -> f64 {
let orientation = self.orientation();
let matrix = Matrix4::new(
self.a.x(),
self.a.y(),
self.a.x().powi(2) + self.a.y().powi(2) - self.a.w(),
1.0,
self.b.x(),
self.b.y(),
self.b.x().powi(2) + self.b.y().powi(2) - self.b.w(),
1.0,
self.c.x(),
self.c.y(),
self.c.x().powi(2) + self.c.y().powi(2) - self.c.w(),
1.0,
vertex.x(),
vertex.y(),
vertex.x().powi(2) + vertex.y().powi(2) - vertex.w(),
1.0,
);
matrix.determinant() * orientation
}
fn is_neighbor(&self, other: &WeightedTriangle) -> Option<WeightedEdge> {
let self_edges = self.edges();
let other_edges = other.edges();
for self_edge in &self_edges {
for other_edge in &other_edges {
if *self_edge == *other_edge {
return Some(*self_edge);
}
}
}
None
}
fn vertices(&self) -> Vec<WeightedVertex> {
vec![self.a, self.b, self.c]
}
}
impl From<&WeightedTriangle> for PlotPoints {
fn from(triangle: &WeightedTriangle) -> Self {
let points: Vec<[f64; 2]> = triangle
.vertices()
.iter()
.map(|v| [v.x(), v.y()])
.collect();
PlotPoints::from(points)
}
}
impl fmt::Display for WeightedTriangle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{ {}, {}, {} }}", self.a, self.b, self.c)
}
}
pub struct WeightedTriangulation<'a>(pub &'a Vec<WeightedTriangle>);
impl fmt::Display for WeightedTriangulation<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, triangle) in self.0.iter().enumerate() {
writeln!(f, "{}: {}", index, triangle)?;
}
Ok(())
}
}
#[derive(PartialEq)]
pub struct Triangulation {
pub triangles: Vec<WeightedTriangle>,
pub used_vertices: Vec<WeightedVertex>,
pub ignored_vertices: Vec<WeightedVertex>,
pub cache: Vec<Vec<WeightedTriangle>>
}
impl Triangulation {
pub fn empty() -> Self {
Self {
triangles: vec![],
used_vertices: vec![],
ignored_vertices: vec![],
cache: vec![],
}
}
pub fn new(starting_triangle: WeightedTriangle) -> Self {
Self {
triangles: vec![starting_triangle],
used_vertices: vec![],
ignored_vertices: vec![],
cache: vec![],
}
}
pub fn triangles(&self) -> &Vec<WeightedTriangle> {
&self.triangles
}
pub fn triangles_mut(&mut self) -> &mut Vec<WeightedTriangle> {
&mut self.triangles
}
pub fn used_vertices(&self) -> &Vec<WeightedVertex> {
&self.used_vertices
}
pub fn used_vertices_mut(&mut self) -> &mut Vec<WeightedVertex> {
&mut self.used_vertices
}
pub fn ignored_vertices(&self) -> &Vec<WeightedVertex> {
&self.ignored_vertices
}
pub fn ignored_vertices_mut(&mut self) -> &mut Vec<WeightedVertex> {
&mut self.ignored_vertices
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn edges_equal() {
let e0 = WeightedEdge::new(
WeightedVertex::new(0.0, 1.0, 0.0),
WeightedVertex::new(3.0, 4.0, 0.0),
);
let e1 = WeightedEdge::new(
WeightedVertex::new(3.0, 4.0, 0.0),
WeightedVertex::new(0.0, 1.0, 0.0),
);
let e2 = WeightedEdge::new(
WeightedVertex::new(0.0, 0.0, 0.0),
WeightedVertex::new(1.0, 1.0, 0.0),
);
assert_eq!(e0, e1);
assert_ne!(e0, e2);
assert_ne!(e1, e2);
}
#[test]
fn triangles_equal() {
let t0 = WeightedTriangle::new(
WeightedVertex::new(0.0, 1.0, 0.0),
WeightedVertex::new(1.0, 0.0, 0.0),
WeightedVertex::new(-1.0, 0.0, 0.0),
);
let t1 = WeightedTriangle::new(
WeightedVertex::new(1.0, 0.0, 0.0),
WeightedVertex::new(-1.0, 0.0, 0.0),
WeightedVertex::new(0.0, 1.0, 0.0),
);
assert_eq!(t0, t1);
}
#[test]
fn triangle_and_point_regular() {
let vertex = WeightedVertex::new(0.6984975495419887, 0.6787937052516924, 0.0);
let tri = WeightedTriangle::new(
WeightedVertex::new(0.5222943585280901, 0.9101950968457111, 0.0),
WeightedVertex::new(0.7504630819229956, 0.42525042963771664, 0.0),
WeightedVertex::new(0.9030435560273877, 0.6666048060052796, 0.0),
);
assert!(!tri.is_regular(&vertex));
assert!(tri.regularity(&vertex) - 0.1 < 0.0);
}
#[test]
fn neighbors() {
let t0 = WeightedTriangle::new(
WeightedVertex::new(2.0, 0.0, 0.0),
WeightedVertex::new(2.8, 2.8, 0.0),
WeightedVertex::new(2.4, 2.4, 0.0),
);
let t1 = WeightedTriangle::new(
WeightedVertex::new(3.0, 2.0, 0.0),
WeightedVertex::new(2.8, 2.8, 0.0),
WeightedVertex::new(2.4, 2.4, 0.0),
);
assert_eq!(
t0.is_neighbor(&t1),
Some(WeightedEdge::new(
WeightedVertex::new(2.8, 2.8, 0.0),
WeightedVertex::new(2.4, 2.4, 0.0)
))
);
assert_eq!(
t1.is_neighbor(&t0),
Some(WeightedEdge::new(
WeightedVertex::new(2.8, 2.8, 0.0),
WeightedVertex::new(2.4, 2.4, 0.0)
))
);
}
}