use std::collections::BTreeMap;
use crate::linalg::{Vec2, Vec3, Vec4, Mat3, Mat3x4};
use crate::math;
#[path = "types_bounds.rs"]
mod types_bounds;
pub use types_bounds::{Box, Rect};
#[path = "types_meshgl.rs"]
mod types_meshgl;
pub use types_meshgl::{MeshGLP, MeshGL, MeshGL64, MeshIndex, MeshPrecision};
pub const K_PI: f64 = std::f64::consts::PI;
pub const K_TWO_PI: f64 = std::f64::consts::TAU;
pub const K_HALF_PI: f64 = std::f64::consts::FRAC_PI_2;
pub const K_PRECISION: f64 = 1e-12;
pub const DEFAULT_SEGMENTS: i32 = 0;
pub const DEFAULT_ANGLE: f64 = 10.0;
pub const DEFAULT_LENGTH: f64 = 1.0;
#[inline]
pub fn radians(a: f64) -> f64 {
a * K_PI / 180.0
}
#[inline]
pub fn degrees(a: f64) -> f64 {
a * 180.0 / K_PI
}
#[inline]
pub fn smoothstep(edge0: f64, edge1: f64, a: f64) -> f64 {
let x = ((a - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
x * x * (3.0 - 2.0 * x)
}
pub fn sind(x: f64) -> f64 {
if !x.is_finite() {
return f64::NAN;
}
if x < 0.0 {
return -sind(-x);
}
let mut quo = (x / 90.0).round_ties_even() as i64;
let mut r = x - quo as f64 * 90.0;
if r > 45.0 {
quo += 1;
r -= 90.0;
} else if r < -45.0 {
quo -= 1;
r += 90.0;
} else if r == 45.0 && quo % 2 != 0 {
quo += 1;
r = -45.0;
} else if r == -45.0 && quo % 2 != 0 {
quo -= 1;
r = 45.0;
}
match ((quo % 4) + 4) % 4 {
0 => math::sin(radians(r)),
1 => math::cos(radians(r)),
2 => -math::sin(radians(r)),
3 => -math::cos(radians(r)),
_ => 0.0,
}
}
#[inline]
pub fn cosd(x: f64) -> f64 {
sind(x + 90.0)
}
pub type SimplePolygon = Vec<Vec2>;
pub type Polygons = Vec<SimplePolygon>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PolyVert {
pub pos: Vec2,
pub idx: i32,
}
pub type SimplePolygonIdx = Vec<PolyVert>;
pub type PolygonsIdx = Vec<SimplePolygonIdx>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum OpType {
Add,
Subtract,
Intersect,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Error {
NoError,
NonFiniteVertex,
NotManifold,
VertexOutOfBounds,
PropertiesWrongLength,
MissingPositionProperties,
MergeVectorsDifferentLengths,
MergeIndexOutOfBounds,
TransformWrongLength,
RunIndexWrongLength,
FaceIdWrongLength,
InvalidConstruction,
ResultTooLarge,
InvalidTangents,
Cancelled,
NotClosed,
}
impl Error {
pub fn to_str(self) -> &'static str {
match self {
Error::NoError => "No Error",
Error::NonFiniteVertex => "Non-Finite Vertex",
Error::NotManifold => "Not Manifold",
Error::VertexOutOfBounds => "Vertex Out of Bounds",
Error::PropertiesWrongLength => "Properties Wrong Length",
Error::MissingPositionProperties => "Missing Position Properties",
Error::MergeVectorsDifferentLengths => "Merge Vectors Different Lengths",
Error::MergeIndexOutOfBounds => "Merge Index Out of Bounds",
Error::TransformWrongLength => "Transform Wrong Length",
Error::RunIndexWrongLength => "Run Index Wrong Length",
Error::FaceIdWrongLength => "Face ID Wrong Length",
Error::InvalidConstruction => "Invalid Construction",
Error::ResultTooLarge => "Result Too Large",
Error::InvalidTangents => "Invalid Tangents",
Error::Cancelled => "Cancelled",
Error::NotClosed => "Not Closed",
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_str())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum BooleanEngine {
#[default]
Exact,
Robust,
Auto,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum WindingRule {
#[default]
Positive,
Nonzero,
}
static BOOLEAN_ENGINE_DEFAULT: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
pub struct BooleanConfig;
impl BooleanConfig {
pub fn set_default_engine(engine: BooleanEngine) {
let v = match engine {
BooleanEngine::Exact => 0u8,
BooleanEngine::Robust => 1,
BooleanEngine::Auto => 2,
};
BOOLEAN_ENGINE_DEFAULT.store(v, std::sync::atomic::Ordering::Relaxed);
}
pub fn default_engine() -> BooleanEngine {
match BOOLEAN_ENGINE_DEFAULT.load(std::sync::atomic::Ordering::Relaxed) {
1 => BooleanEngine::Robust,
2 => BooleanEngine::Auto,
_ => BooleanEngine::Exact,
}
}
pub fn reset_to_defaults() {
Self::set_default_engine(BooleanEngine::Exact);
}
}
use std::sync::OnceLock;
use std::sync::Mutex;
struct QualityState {
min_circular_angle: f64,
min_circular_edge_length: f64,
circular_segments: i32,
}
static QUALITY_STATE: OnceLock<Mutex<QualityState>> = OnceLock::new();
fn quality_state() -> &'static Mutex<QualityState> {
QUALITY_STATE.get_or_init(|| {
Mutex::new(QualityState {
min_circular_angle: DEFAULT_ANGLE,
min_circular_edge_length: DEFAULT_LENGTH,
circular_segments: DEFAULT_SEGMENTS,
})
})
}
pub struct Quality;
impl Quality {
pub fn set_min_circular_angle(angle: f64) {
quality_state().lock().unwrap().min_circular_angle = angle;
}
pub fn set_min_circular_edge_length(length: f64) {
quality_state().lock().unwrap().min_circular_edge_length = length;
}
pub fn set_circular_segments(n: i32) {
quality_state().lock().unwrap().circular_segments = n;
}
pub fn get_circular_segments(radius: f64) -> i32 {
let q = quality_state().lock().unwrap();
if q.circular_segments > 0 {
return q.circular_segments;
}
let n_seg_a = (360.0 / q.min_circular_angle) as i32;
let n_seg_l = (2.0 * radius.abs() * K_PI / q.min_circular_edge_length) as i32;
let mut n_seg = n_seg_a.min(n_seg_l) + 3;
n_seg -= n_seg % 4;
n_seg.max(4)
}
pub fn reset_to_defaults() {
let mut q = quality_state().lock().unwrap();
q.min_circular_angle = DEFAULT_ANGLE;
q.min_circular_edge_length = DEFAULT_LENGTH;
q.circular_segments = DEFAULT_SEGMENTS;
}
}
#[derive(Clone, Debug)]
pub struct ExecutionParams {
pub intermediate_checks: bool,
pub self_intersection_checks: bool,
pub process_overlaps: bool,
pub suppress_errors: bool,
pub cleanup_triangles: bool,
pub verbose: i32,
}
impl Default for ExecutionParams {
fn default() -> Self {
ExecutionParams {
intermediate_checks: false,
self_intersection_checks: false,
process_overlaps: true,
suppress_errors: false,
cleanup_triangles: true,
verbose: 0,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Smoothness {
pub halfedge: usize,
pub smoothness: f64,
}
#[derive(Clone, Debug, Default)]
pub struct RayHit {
pub face_id: u64,
pub distance: f64,
pub position: Vec3,
pub normal: Vec3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Halfedge {
pub start_vert: i32,
pub end_vert: i32,
pub paired_halfedge: i32,
pub prop_vert: i32,
}
impl Halfedge {
pub fn is_forward(&self) -> bool {
self.start_vert < self.end_vert
}
}
impl PartialOrd for Halfedge {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Halfedge {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.start_vert == other.start_vert {
self.end_vert.cmp(&other.end_vert)
} else {
self.start_vert.cmp(&other.start_vert)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Barycentric {
pub tri: i32,
pub uvw: Vec4,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TriRef {
pub mesh_id: i32,
pub original_id: i32,
pub face_id: i32,
pub coplanar_id: i32,
}
impl TriRef {
pub fn same_face(&self, other: &TriRef) -> bool {
self.mesh_id == other.mesh_id
&& self.coplanar_id == other.coplanar_id
&& self.face_id == other.face_id
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TmpEdge {
pub first: i32,
pub second: i32,
pub halfedge_idx: i32,
}
impl TmpEdge {
pub fn new(start: i32, end: i32, idx: i32) -> Self {
TmpEdge {
first: start.min(end),
second: start.max(end),
halfedge_idx: idx,
}
}
}
impl PartialOrd for TmpEdge {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TmpEdge {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.first == other.first {
self.second.cmp(&other.second)
} else {
self.first.cmp(&other.first)
}
}
}
#[derive(Clone, Debug)]
pub struct Relation {
pub original_id: i32,
pub transform: Mat3x4,
pub back_side: bool,
pub has_normals: bool,
}
impl Default for Relation {
fn default() -> Self {
Relation {
original_id: -1,
transform: Mat3x4::identity(),
back_side: false,
has_normals: false,
}
}
}
impl Relation {
pub fn get_normal_transform(&self) -> Mat3 {
let sign = if self.back_side { -1.0 } else { 1.0 };
self.transform.rotation().transpose().inverse() * sign
}
pub fn get_inverse_normal_transform(&self) -> Mat3 {
let sign = if self.back_side { -1.0 } else { 1.0 };
self.transform.rotation().transpose() * sign
}
}
#[derive(Clone, Debug, Default)]
pub struct MeshRelationD {
pub original_id: i32,
pub mesh_id_transform: BTreeMap<i32, Relation>,
pub tri_ref: Vec<TriRef>,
}
impl MeshRelationD {
pub fn new() -> Self {
MeshRelationD {
original_id: -1,
mesh_id_transform: BTreeMap::new(),
tri_ref: Vec::new(),
}
}
}
#[inline]
pub fn next_halfedge(current: i32) -> i32 {
let n = current + 1;
if n % 3 == 0 { n - 3 } else { n }
}
pub fn prev_halfedge(current: i32) -> i32 {
let base = current - (current % 3);
let pos = (current % 3 + 2) % 3;
base + pos
}
#[inline]
pub fn next3(i: i32) -> i32 {
let n = i + 1;
if n == 3 { 0 } else { n }
}
#[cfg(test)]
#[path = "types_tests.rs"]
mod tests;