quadrs 1.0.0

Experimental quad remeshing library and tools based on Instant Meshes
Documentation
use crate::meshio::Vec3;
use glam::DVec2;

const EPS: f64 = 1e-12;

pub fn quad_is_valid(vertices: &[Vec3], face: [usize; 4]) -> bool {
    let points = face.map(|index| vertices[index]);
    let normal = (points[1] - points[0]).cross(points[2] - points[0])
        + (points[2] - points[0]).cross(points[3] - points[0]);
    if normal.length_squared() <= EPS {
        return false;
    }

    let axis = dominant_axis(normal);
    let projected = points.map(|point| project_2d(point, axis));
    if segments_intersect(projected[0], projected[1], projected[2], projected[3])
        || segments_intersect(projected[1], projected[2], projected[3], projected[0])
    {
        return false;
    }

    triangle_area(points[0], points[1], points[2]) > EPS
        && triangle_area(points[0], points[2], points[3]) > EPS
}

pub fn triangle_area(a: Vec3, b: Vec3, c: Vec3) -> f64 {
    0.5 * (b - a).cross(c - a).length()
}

fn dominant_axis(normal: Vec3) -> usize {
    let x = normal.x.abs();
    let y = normal.y.abs();
    let z = normal.z.abs();
    if x >= y && x >= z {
        0
    } else if y >= z {
        1
    } else {
        2
    }
}

fn project_2d(point: Vec3, axis: usize) -> DVec2 {
    match axis {
        0 => DVec2::new(point.y, point.z),
        1 => DVec2::new(point.x, point.z),
        _ => DVec2::new(point.x, point.y),
    }
}

fn segments_intersect(
    a0: DVec2,
    a1: DVec2,
    b0: DVec2,
    b1: DVec2,
) -> bool {
    let o1 = orient2d(a0, a1, b0);
    let o2 = orient2d(a0, a1, b1);
    let o3 = orient2d(b0, b1, a0);
    let o4 = orient2d(b0, b1, a1);
    o1 * o2 < -EPS && o3 * o4 < -EPS
}

fn orient2d(a: DVec2, b: DVec2, c: DVec2) -> f64 {
    (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
}