1use std::collections::BTreeMap;
9use crate::linalg::{Vec2, Vec3, Vec4, Mat3, Mat3x4};
10use crate::math;
11
12#[path = "types_bounds.rs"]
13mod types_bounds;
14pub use types_bounds::{Box, Rect};
15
16#[path = "types_meshgl.rs"]
17mod types_meshgl;
18pub use types_meshgl::{MeshGLP, MeshGL, MeshGL64, MeshIndex, MeshPrecision};
19
20pub const K_PI: f64 = std::f64::consts::PI;
25pub const K_TWO_PI: f64 = std::f64::consts::TAU;
26pub const K_HALF_PI: f64 = std::f64::consts::FRAC_PI_2;
27pub const K_PRECISION: f64 = 1e-12;
29
30pub const DEFAULT_SEGMENTS: i32 = 0;
31pub const DEFAULT_ANGLE: f64 = 10.0;
32pub const DEFAULT_LENGTH: f64 = 1.0;
33
34#[inline]
39pub fn radians(a: f64) -> f64 {
40 a * K_PI / 180.0
41}
42
43#[inline]
44pub fn degrees(a: f64) -> f64 {
45 a * 180.0 / K_PI
46}
47
48#[inline]
50pub fn smoothstep(edge0: f64, edge1: f64, a: f64) -> f64 {
51 let x = ((a - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
52 x * x * (3.0 - 2.0 * x)
53}
54
55pub fn sind(x: f64) -> f64 {
64 if !x.is_finite() {
65 return f64::NAN;
66 }
67 if x < 0.0 {
68 return -sind(-x);
69 }
70 let mut quo = (x / 90.0).round_ties_even() as i64;
75 let mut r = x - quo as f64 * 90.0;
76 if r > 45.0 {
77 quo += 1;
78 r -= 90.0;
79 } else if r < -45.0 {
80 quo -= 1;
81 r += 90.0;
82 } else if r == 45.0 && quo % 2 != 0 {
83 quo += 1;
85 r = -45.0;
86 } else if r == -45.0 && quo % 2 != 0 {
87 quo -= 1;
88 r = 45.0;
89 }
90 match ((quo % 4) + 4) % 4 {
91 0 => math::sin(radians(r)),
92 1 => math::cos(radians(r)),
93 2 => -math::sin(radians(r)),
94 3 => -math::cos(radians(r)),
95 _ => 0.0,
96 }
97}
98
99#[inline]
101pub fn cosd(x: f64) -> f64 {
102 sind(x + 90.0)
103}
104
105pub type SimplePolygon = Vec<Vec2>;
111
112pub type Polygons = Vec<SimplePolygon>;
114
115#[derive(Clone, Copy, Debug, PartialEq)]
117pub struct PolyVert {
118 pub pos: Vec2,
119 pub idx: i32,
120}
121
122pub type SimplePolygonIdx = Vec<PolyVert>;
124
125pub type PolygonsIdx = Vec<SimplePolygonIdx>;
127
128#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
133pub enum OpType {
134 Add,
135 Subtract,
136 Intersect,
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
144pub enum Error {
145 NoError,
146 NonFiniteVertex,
147 NotManifold,
148 VertexOutOfBounds,
149 PropertiesWrongLength,
150 MissingPositionProperties,
151 MergeVectorsDifferentLengths,
152 MergeIndexOutOfBounds,
153 TransformWrongLength,
154 RunIndexWrongLength,
155 FaceIdWrongLength,
156 InvalidConstruction,
157 ResultTooLarge,
158 InvalidTangents,
159 Cancelled,
167}
168
169impl Error {
170 pub fn to_str(self) -> &'static str {
171 match self {
172 Error::NoError => "No Error",
173 Error::NonFiniteVertex => "Non-Finite Vertex",
174 Error::NotManifold => "Not Manifold",
175 Error::VertexOutOfBounds => "Vertex Out of Bounds",
176 Error::PropertiesWrongLength => "Properties Wrong Length",
177 Error::MissingPositionProperties => "Missing Position Properties",
178 Error::MergeVectorsDifferentLengths => "Merge Vectors Different Lengths",
179 Error::MergeIndexOutOfBounds => "Merge Index Out of Bounds",
180 Error::TransformWrongLength => "Transform Wrong Length",
181 Error::RunIndexWrongLength => "Run Index Wrong Length",
182 Error::FaceIdWrongLength => "Face ID Wrong Length",
183 Error::InvalidConstruction => "Invalid Construction",
184 Error::ResultTooLarge => "Result Too Large",
185 Error::InvalidTangents => "Invalid Tangents",
186 Error::Cancelled => "Cancelled",
187 }
188 }
189}
190
191impl std::fmt::Display for Error {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 write!(f, "{}", self.to_str())
194 }
195}
196
197use std::sync::OnceLock;
202use std::sync::Mutex;
203
204struct QualityState {
205 min_circular_angle: f64,
206 min_circular_edge_length: f64,
207 circular_segments: i32,
208}
209
210static QUALITY_STATE: OnceLock<Mutex<QualityState>> = OnceLock::new();
211
212fn quality_state() -> &'static Mutex<QualityState> {
213 QUALITY_STATE.get_or_init(|| {
214 Mutex::new(QualityState {
215 min_circular_angle: DEFAULT_ANGLE,
216 min_circular_edge_length: DEFAULT_LENGTH,
217 circular_segments: DEFAULT_SEGMENTS,
218 })
219 })
220}
221
222pub struct Quality;
223
224impl Quality {
225 pub fn set_min_circular_angle(angle: f64) {
226 quality_state().lock().unwrap().min_circular_angle = angle;
227 }
228
229 pub fn set_min_circular_edge_length(length: f64) {
230 quality_state().lock().unwrap().min_circular_edge_length = length;
231 }
232
233 pub fn set_circular_segments(n: i32) {
234 quality_state().lock().unwrap().circular_segments = n;
235 }
236
237 pub fn get_circular_segments(radius: f64) -> i32 {
238 let q = quality_state().lock().unwrap();
239 if q.circular_segments > 0 {
240 return q.circular_segments;
241 }
242 let n_seg_a = (360.0 / q.min_circular_angle) as i32;
244 let n_seg_l = (2.0 * radius.abs() * K_PI / q.min_circular_edge_length) as i32;
245 let mut n_seg = n_seg_a.min(n_seg_l) + 3;
246 n_seg -= n_seg % 4;
247 n_seg.max(4)
248 }
249
250 pub fn reset_to_defaults() {
251 let mut q = quality_state().lock().unwrap();
252 q.min_circular_angle = DEFAULT_ANGLE;
253 q.min_circular_edge_length = DEFAULT_LENGTH;
254 q.circular_segments = DEFAULT_SEGMENTS;
255 }
256}
257
258#[derive(Clone, Debug)]
263pub struct ExecutionParams {
264 pub intermediate_checks: bool,
265 pub self_intersection_checks: bool,
266 pub process_overlaps: bool,
267 pub suppress_errors: bool,
268 pub cleanup_triangles: bool,
269 pub verbose: i32,
270}
271
272impl Default for ExecutionParams {
273 fn default() -> Self {
274 ExecutionParams {
275 intermediate_checks: false,
276 self_intersection_checks: false,
277 process_overlaps: true,
278 suppress_errors: false,
279 cleanup_triangles: true,
280 verbose: 0,
281 }
282 }
283}
284
285#[derive(Clone, Copy, Debug, PartialEq)]
290pub struct Smoothness {
291 pub halfedge: usize,
293 pub smoothness: f64,
295}
296
297#[derive(Clone, Debug, Default)]
303pub struct RayHit {
304 pub face_id: u64,
306 pub distance: f64,
309 pub position: Vec3,
311 pub normal: Vec3,
313}
314
315#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
320pub struct Halfedge {
321 pub start_vert: i32,
322 pub end_vert: i32,
323 pub paired_halfedge: i32,
324 pub prop_vert: i32,
325}
326
327impl Halfedge {
328 pub fn is_forward(&self) -> bool {
329 self.start_vert < self.end_vert
330 }
331}
332
333impl PartialOrd for Halfedge {
334 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
335 Some(self.cmp(other))
336 }
337}
338
339impl Ord for Halfedge {
340 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
341 if self.start_vert == other.start_vert {
342 self.end_vert.cmp(&other.end_vert)
343 } else {
344 self.start_vert.cmp(&other.start_vert)
345 }
346 }
347}
348
349#[derive(Clone, Copy, Debug, PartialEq)]
354pub struct Barycentric {
355 pub tri: i32,
356 pub uvw: Vec4,
357}
358
359#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
364pub struct TriRef {
365 pub mesh_id: i32,
367 pub original_id: i32,
369 pub face_id: i32,
371 pub coplanar_id: i32,
373}
374
375impl TriRef {
376 pub fn same_face(&self, other: &TriRef) -> bool {
377 self.mesh_id == other.mesh_id
378 && self.coplanar_id == other.coplanar_id
379 && self.face_id == other.face_id
380 }
381}
382
383#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
388pub struct TmpEdge {
389 pub first: i32,
390 pub second: i32,
391 pub halfedge_idx: i32,
392}
393
394impl TmpEdge {
395 pub fn new(start: i32, end: i32, idx: i32) -> Self {
396 TmpEdge {
397 first: start.min(end),
398 second: start.max(end),
399 halfedge_idx: idx,
400 }
401 }
402}
403
404impl PartialOrd for TmpEdge {
405 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
406 Some(self.cmp(other))
407 }
408}
409
410impl Ord for TmpEdge {
411 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
412 if self.first == other.first {
413 self.second.cmp(&other.second)
414 } else {
415 self.first.cmp(&other.first)
416 }
417 }
418}
419
420#[derive(Clone, Debug)]
426pub struct Relation {
427 pub original_id: i32,
428 pub transform: Mat3x4,
429 pub back_side: bool,
430 pub has_normals: bool,
435}
436
437impl Default for Relation {
438 fn default() -> Self {
439 Relation {
440 original_id: -1,
441 transform: Mat3x4::identity(),
442 back_side: false,
443 has_normals: false,
444 }
445 }
446}
447
448impl Relation {
449 pub fn get_normal_transform(&self) -> Mat3 {
453 let sign = if self.back_side { -1.0 } else { 1.0 };
454 self.transform.rotation().transpose().inverse() * sign
456 }
457
458 pub fn get_inverse_normal_transform(&self) -> Mat3 {
462 let sign = if self.back_side { -1.0 } else { 1.0 };
463 self.transform.rotation().transpose() * sign
465 }
466}
467
468#[derive(Clone, Debug, Default)]
470pub struct MeshRelationD {
471 pub original_id: i32,
473 pub mesh_id_transform: BTreeMap<i32, Relation>,
477 pub tri_ref: Vec<TriRef>,
478}
479
480impl MeshRelationD {
481 pub fn new() -> Self {
482 MeshRelationD {
483 original_id: -1,
484 mesh_id_transform: BTreeMap::new(),
485 tri_ref: Vec::new(),
486 }
487 }
488}
489
490#[inline]
496pub fn next_halfedge(current: i32) -> i32 {
497 let n = current + 1;
498 if n % 3 == 0 { n - 3 } else { n }
499}
500
501pub fn prev_halfedge(current: i32) -> i32 {
504 let base = current - (current % 3);
505 let pos = (current % 3 + 2) % 3;
506 base + pos
507}
508
509#[inline]
511pub fn next3(i: i32) -> i32 {
512 let n = i + 1;
513 if n == 3 { 0 } else { n }
514}
515
516#[cfg(test)]
518#[path = "types_tests.rs"]
519mod tests;