quadrs 1.0.0

Experimental quad remeshing library and tools based on Instant Meshes
Documentation
use crate::field::BoundaryConstraint;
use crate::hierarchy::HierarchyLevel;
use crate::meshio::Vec3;
use crate::rotational_symmetry::rotate_vector_into_plane;
use crate::topology::{DirectedEdges, TriMesh, INVALID};

pub fn build_boundary_constraints(
    mesh: &TriMesh,
    dedges: &DirectedEdges,
    normals: &[Vec3],
) -> Vec<Option<BoundaryConstraint>> {
    let mut constraints = vec![None; mesh.vertices.len()];
    for edge in 0..dedges.e2e.len() {
        if dedges.e2e[edge] != INVALID {
            continue;
        }
        let face = mesh.faces[edge / 3];
        let i0 = face[edge % 3];
        let i1 = face[(edge + 1) % 3];
        let direction = mesh.vertices[i1] - mesh.vertices[i0];
        if direction.length_squared() <= 1e-12 {
            continue;
        }
        let tangent0 = (direction - normals[i0] * normals[i0].dot(direction)).normalize();
        let tangent1 = (direction - normals[i1] * normals[i1].dot(direction)).normalize();
        constraints[i0] = Some(BoundaryConstraint {
            origin: mesh.vertices[i0],
            tangent: tangent0,
            weight: 1.0,
        });
        constraints[i1] = Some(BoundaryConstraint {
            origin: mesh.vertices[i1],
            tangent: tangent1,
            weight: 1.0,
        });
    }
    constraints
}

pub fn build_boundary_hierarchy(
    levels: &[HierarchyLevel],
    fine_boundary: Vec<Option<BoundaryConstraint>>,
) -> Vec<Vec<Option<BoundaryConstraint>>> {
    let mut hierarchy = Vec::with_capacity(levels.len());
    hierarchy.push(fine_boundary);
    for level_idx in 0..levels.len().saturating_sub(1) {
        let fine = &levels[level_idx];
        let coarse = &levels[level_idx + 1];
        let to_coarser = fine.to_coarser.as_ref().unwrap();
        let mut origins = vec![Vec3::ZERO; coarse.positions.len()];
        let mut tangents = vec![Vec3::ZERO; coarse.positions.len()];
        let mut weights = vec![0.0; coarse.positions.len()];
        for (i, constraint) in hierarchy[level_idx].iter().enumerate() {
            let Some(constraint) = constraint else {
                continue;
            };
            let parent = to_coarser[i];
            let weight = fine.areas[i].max(1e-12) * constraint.weight.max(1e-12);
            origins[parent] += constraint.origin * weight;
            tangents[parent] += rotate_vector_into_plane(
                constraint.tangent,
                fine.normals[i],
                coarse.normals[parent],
            ) * weight;
            weights[parent] += weight;
        }

        let mut coarse_boundary = vec![None; coarse.positions.len()];
        for i in 0..coarse.positions.len() {
            if weights[i] == 0.0 {
                continue;
            }
            let normal = coarse.normals[i];
            let position = coarse.positions[i];
            let mut origin = origins[i] / weights[i];
            origin -= normal * normal.dot(origin - position);
            let mut tangent = tangents[i];
            tangent -= normal * normal.dot(tangent);
            if tangent.length_squared() <= 1e-12 {
                continue;
            }
            coarse_boundary[i] = Some(BoundaryConstraint {
                origin,
                tangent: tangent.normalize(),
                weight: 1.0,
            });
        }
        hierarchy.push(coarse_boundary);
    }
    hierarchy
}