Skip to content

Commit a37904f

Browse files
committed
begin converting EPSILON
1 parent fbfbdcc commit a37904f

File tree

4 files changed

+78
-64
lines changed

4 files changed

+78
-64
lines changed

src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use csgrs::mesh::plane::Plane;
66
use csgrs::traits::CSG;
77
use crate::math_ndsp::{Point3, Vector3};
8-
use crate::math_ndsp::consts{Real};
98

109
use std::fs;
1110

src/math_ndsp.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,33 @@ impl<T> Scalar for T where
3838
{}
3939

4040
/// A small generic epsilon helper
41-
#[inline]
42-
fn eps<T: Scalar>() -> T { T::mixed_from(1e-9) }
41+
#[inline] pub fn eps<T: Scalar>() -> T { T::mixed_from(1e-9) }
42+
#[inline] pub fn zero<T: Scalar>() -> T { T::mixed_zero() }
43+
#[inline] pub fn one<T: Scalar>() -> T { T::mixed_one() }
44+
#[inline] pub fn two<T: Scalar>() -> T { T::mixed_from(2.0) }
45+
#[inline] pub fn deg_to_rad<T: Scalar>(deg: T) -> T {
46+
deg * (T::mixed_pi() / T::mixed_from(180.0))
47+
}
4348

44-
pub mod consts {
45-
// We keep the library on f64 for now (simple migration).
46-
pub type Real = f64;
49+
// Integer power (avoid `.powi()` calls)
50+
#[inline] pub fn powi<T: Scalar>(mut x: T, mut e: i32) -> T {
51+
if e == 0 { return one() }
52+
let mut base = x;
53+
let mut acc = one();
54+
let neg = e < 0;
55+
if neg { e = -e; }
56+
while e > 0 { if (e & 1) != 0 { acc = acc * base; } base = base * base; e >>= 1; }
57+
if neg { one::<T>() / acc } else { acc }
58+
}
59+
60+
// Generic min/max (avoid f64::min/max and NaN surprises)
61+
#[inline] pub fn min<T: Scalar + PartialOrd>(a: T, b: T) -> T { if a <= b { a } else { b } }
62+
#[inline] pub fn max<T: Scalar + PartialOrd>(a: T, b: T) -> T { if a >= b { a } else { b } }
4763

48-
pub const EPSILON: Real = 1.0e-8;
49-
pub const PI: Real = core::f64::consts::PI;
50-
pub const TAU: Real = core::f64::consts::TAU;
51-
pub const FRAC_PI_2: Real = core::f64::consts::FRAC_PI_2;
64+
pub mod consts {
65+
pub const PI: f64 = core::f64::consts::PI;
66+
pub const TAU: f64 = core::f64::consts::TAU;
67+
pub const FRAC_PI_2: f64 = core::f64::consts::FRAC_PI_2;
5268
}
5369

5470
/* ----------------------------- Vector2 / Point2 ----------------------------- */

src/tests.rs

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::errors::ValidationError;
2-
use crate::math_ndsp::consts::{EPSILON, FRAC_PI_2, PI, Real};
2+
use crate::math_ndsp::consts::{FRAC_PI_2, PI};
33
use crate::mesh::Mesh;
44
use crate::mesh::bsp::Node;
55
use crate::mesh::plane::Plane;
@@ -178,12 +178,12 @@ fn test_vertex_interpolate() {
178178
let v1 = Vertex::new(Point3::origin(), Vector3::x());
179179
let v2 = Vertex::new(Point3::new(2.0, 2.0, 2.0), Vector3::y());
180180
let v_mid = v1.interpolate(&v2, 0.5);
181-
assert!(approx_eq(v_mid.pos.x, 1.0, EPSILON));
182-
assert!(approx_eq(v_mid.pos.y, 1.0, EPSILON));
183-
assert!(approx_eq(v_mid.pos.z, 1.0, EPSILON));
184-
assert!(approx_eq(v_mid.normal.x, 0.5, EPSILON));
185-
assert!(approx_eq(v_mid.normal.y, 0.5, EPSILON));
186-
assert!(approx_eq(v_mid.normal.z, 0.0, EPSILON));
181+
assert!(approx_eq(v_mid.pos.x, 1.0, eps::<T>()));
182+
assert!(approx_eq(v_mid.pos.y, 1.0, eps::<T>()));
183+
assert!(approx_eq(v_mid.pos.z, 1.0, eps::<T>()));
184+
assert!(approx_eq(v_mid.normal.x, 0.5, eps::<T>()));
185+
assert!(approx_eq(v_mid.normal.y, 0.5, eps::<T>()));
186+
assert!(approx_eq(v_mid.normal.z, 0.0, eps::<T>()));
187187
}
188188

189189
// ------------------------------------------------------------
@@ -229,11 +229,11 @@ fn test_plane_split_polygon() {
229229

230230
// Quick check: all front vertices should have y >= 0 (within an epsilon).
231231
for v in &front_poly.vertices {
232-
assert!(v.pos.y >= -EPSILON);
232+
assert!(v.pos.y >= -eps::<T>());
233233
}
234234
// All back vertices should have y <= 0 (within an epsilon).
235235
for v in &back_poly.vertices {
236-
assert!(v.pos.y <= EPSILON);
236+
assert!(v.pos.y <= eps::<T>());
237237
}
238238
}
239239

@@ -251,9 +251,9 @@ fn test_polygon_new() {
251251
assert_eq!(poly.vertices.len(), 3);
252252
assert_eq!(poly.metadata, None);
253253
// Plane normal should be +Z for the above points
254-
assert!(approx_eq(poly.plane.normal().x, 0.0, EPSILON));
255-
assert!(approx_eq(poly.plane.normal().y, 0.0, EPSILON));
256-
assert!(approx_eq(poly.plane.normal().z, 1.0, EPSILON));
254+
assert!(approx_eq(poly.plane.normal().x, 0.0, eps::<T>()));
255+
assert!(approx_eq(poly.plane.normal().y, 0.0, eps::<T>()));
256+
assert!(approx_eq(poly.plane.normal().z, 1.0, eps::<T>()));
257257
}
258258

259259
#[test]
@@ -273,17 +273,17 @@ fn test_polygon_flip() {
273273
assert!(approx_eq(
274274
poly.plane.normal().x,
275275
-plane_normal_before.x,
276-
EPSILON
276+
eps::<T>()
277277
));
278278
assert!(approx_eq(
279279
poly.plane.normal().y,
280280
-plane_normal_before.y,
281-
EPSILON
281+
eps::<T>()
282282
));
283283
assert!(approx_eq(
284284
poly.plane.normal().z,
285285
-plane_normal_before.z,
286-
EPSILON
286+
eps::<T>()
287287
));
288288
}
289289

@@ -339,11 +339,11 @@ fn test_polygon_recalc_plane_and_normals() {
339339
None,
340340
);
341341
poly.set_new_normal();
342-
assert!(approx_eq(poly.plane.normal().z, 1.0, EPSILON));
342+
assert!(approx_eq(poly.plane.normal().z, 1.0, eps::<T>()));
343343
for v in &poly.vertices {
344-
assert!(approx_eq(v.normal.x, 0.0, EPSILON));
345-
assert!(approx_eq(v.normal.y, 0.0, EPSILON));
346-
assert!(approx_eq(v.normal.z, 1.0, EPSILON));
344+
assert!(approx_eq(v.normal.x, 0.0, eps::<T>()));
345+
assert!(approx_eq(v.normal.y, 0.0, eps::<T>()));
346+
assert!(approx_eq(v.normal.z, 1.0, eps::<T>()));
347347
}
348348
}
349349

@@ -385,9 +385,9 @@ fn test_node_invert() {
385385
node.invert();
386386
// The plane normal should be flipped, polygons should be flipped, and front/back swapped (they were None).
387387
let flipped_normal = node.plane.as_ref().unwrap().normal();
388-
assert!(approx_eq(flipped_normal.x, -original_normal.x, EPSILON));
389-
assert!(approx_eq(flipped_normal.y, -original_normal.y, EPSILON));
390-
assert!(approx_eq(flipped_normal.z, -original_normal.z, EPSILON));
388+
assert!(approx_eq(flipped_normal.x, -original_normal.x, eps::<T>()));
389+
assert!(approx_eq(flipped_normal.y, -original_normal.y, eps::<T>()));
390+
assert!(approx_eq(flipped_normal.z, -original_normal.z, eps::<T>()));
391391
// We shouldn't lose polygons by inverting
392392
assert_eq!(node.polygons.len(), original_count);
393393
// If we invert back, we should get the same geometry
@@ -590,10 +590,10 @@ fn test_csg_intersect() {
590590
// The intersection bounding box should be smaller than or equal to each
591591
let bb_cube = c1.bounding_box();
592592
let bb_sphere = c2.bounding_box();
593-
assert!(bb_isect.mins.x >= bb_cube.mins.x - EPSILON);
594-
assert!(bb_isect.mins.x >= bb_sphere.mins.x - EPSILON);
595-
assert!(bb_isect.maxs.x <= bb_cube.maxs.x + EPSILON);
596-
assert!(bb_isect.maxs.x <= bb_sphere.maxs.x + EPSILON);
593+
assert!(bb_isect.mins.x >= bb_cube.mins.x - eps::<T>());
594+
assert!(bb_isect.mins.x >= bb_sphere.mins.x - eps::<T>());
595+
assert!(bb_isect.maxs.x <= bb_cube.maxs.x + eps::<T>());
596+
assert!(bb_isect.maxs.x <= bb_sphere.maxs.x + eps::<T>());
597597
}
598598

599599
#[test]
@@ -630,17 +630,17 @@ fn test_csg_inverse() {
630630
assert!(approx_eq(
631631
orig_poly.plane.normal().x,
632632
-inv_poly.plane.normal().x,
633-
EPSILON
633+
eps::<T>()
634634
));
635635
assert!(approx_eq(
636636
orig_poly.plane.normal().y,
637637
-inv_poly.plane.normal().y,
638-
EPSILON
638+
eps::<T>()
639639
));
640640
assert!(approx_eq(
641641
orig_poly.plane.normal().z,
642642
-inv_poly.plane.normal().z,
643-
EPSILON
643+
eps::<T>()
644644
));
645645
assert_eq!(
646646
c1.polygons.len(),
@@ -657,8 +657,8 @@ fn test_csg_cube() {
657657
assert_eq!(c.polygons.len(), 6);
658658
// Check bounding box
659659
let bb = c.bounding_box();
660-
assert!(approx_eq(bb.mins.x, 0.0, EPSILON));
661-
assert!(approx_eq(bb.maxs.x, 2.0, EPSILON));
660+
assert!(approx_eq(bb.mins.x, 0.0, eps::<T>()));
661+
assert!(approx_eq(bb.maxs.x, 2.0, eps::<T>()));
662662
}
663663

664664
// --------------------------------------------------------
@@ -731,15 +731,15 @@ fn test_csg_transform_translate_rotate_scale() {
731731

732732
// Quick bounding box checks
733733
let bb_t = translated.bounding_box();
734-
assert!(approx_eq(bb_t.mins.x, -1.0 + 1.0, EPSILON));
735-
assert!(approx_eq(bb_t.mins.y, -1.0 + 2.0, EPSILON));
736-
assert!(approx_eq(bb_t.mins.z, -1.0 + 3.0, EPSILON));
734+
assert!(approx_eq(bb_t.mins.x, -1.0 + 1.0, eps::<T>()));
735+
assert!(approx_eq(bb_t.mins.y, -1.0 + 2.0, eps::<T>()));
736+
assert!(approx_eq(bb_t.mins.z, -1.0 + 3.0, eps::<T>()));
737737

738738
let bb_s = scaled.bounding_box();
739-
assert!(approx_eq(bb_s.mins.x, -2.0, EPSILON)); // scaled by 2 in X
740-
assert!(approx_eq(bb_s.maxs.x, 2.0, EPSILON));
741-
assert!(approx_eq(bb_s.mins.y, -1.0, EPSILON));
742-
assert!(approx_eq(bb_s.maxs.y, 1.0, EPSILON));
739+
assert!(approx_eq(bb_s.mins.x, -2.0, eps::<T>())); // scaled by 2 in X
740+
assert!(approx_eq(bb_s.maxs.x, 2.0, eps::<T>()));
741+
assert!(approx_eq(bb_s.mins.y, -1.0, eps::<T>()));
742+
assert!(approx_eq(bb_s.maxs.y, 1.0, eps::<T>()));
743743

744744
// For rotated, let's just check one polygon's vertices to see if z got mapped to y, etc.
745745
// (A thorough check would be more geometry-based.)
@@ -760,8 +760,8 @@ fn test_csg_mirror() {
760760
let mirror_x = c.mirror(plane_x);
761761
let bb_mx = mirror_x.bounding_box();
762762
// The original cube was from x=0..2, so mirrored across X=0 should be -2..0
763-
assert!(approx_eq(bb_mx.mins.x, -2.0, EPSILON));
764-
assert!(approx_eq(bb_mx.maxs.x, 0.0, EPSILON));
763+
assert!(approx_eq(bb_mx.mins.x, -2.0, eps::<T>()));
764+
assert!(approx_eq(bb_mx.maxs.x, 0.0, eps::<T>()));
765765
}
766766

767767
#[test]
@@ -810,9 +810,9 @@ fn test_csg_renormalize() {
810810
// Now each polygon's vertices should match the plane's normal
811811
for poly in &cube.polygons {
812812
for v in &poly.vertices {
813-
assert!(approx_eq(v.normal.x, poly.plane.normal().x, EPSILON));
814-
assert!(approx_eq(v.normal.y, poly.plane.normal().y, EPSILON));
815-
assert!(approx_eq(v.normal.z, poly.plane.normal().z, EPSILON));
813+
assert!(approx_eq(v.normal.x, poly.plane.normal().x, eps::<T>()));
814+
assert!(approx_eq(v.normal.y, poly.plane.normal().y, eps::<T>()));
815+
assert!(approx_eq(v.normal.z, poly.plane.normal().z, eps::<T>()));
816816
}
817817
}
818818
}
@@ -827,8 +827,8 @@ fn test_csg_ray_intersections() {
827827
// Expect 2 intersections with the cube's side at x=-1 and x=1
828828
assert_eq!(hits.len(), 2);
829829
// The distances should be 1 unit from -2.0 -> -1 => t=1, and from -2.0 -> +1 => t=3
830-
assert!(approx_eq(hits[0].1, 1.0, EPSILON));
831-
assert!(approx_eq(hits[1].1, 3.0, EPSILON));
830+
assert!(approx_eq(hits[0].1, 1.0, eps::<T>()));
831+
assert!(approx_eq(hits[1].1, 3.0, eps::<T>()));
832832
}
833833

834834
#[test]
@@ -871,8 +871,8 @@ fn test_csg_extrude() {
871871
assert_eq!(extruded.polygons.len(), 8);
872872
// Check bounding box
873873
let bb = extruded.bounding_box();
874-
assert!(approx_eq(bb.mins.z, 0.0, EPSILON));
875-
assert!(approx_eq(bb.maxs.z, 5.0, EPSILON));
874+
assert!(approx_eq(bb.mins.z, 0.0, eps::<T>()));
875+
assert!(approx_eq(bb.maxs.z, 5.0, eps::<T>()));
876876
}
877877

878878
#[test]
@@ -982,7 +982,7 @@ fn test_csg_to_rigid_body() {
982982
);
983983
let rb = rb_set.get(handle).unwrap();
984984
let pos = rb.translation();
985-
assert!(approx_eq(pos.x, 10.0, EPSILON));
985+
assert!(approx_eq(pos.x, 10.0, eps::<T>()));
986986
}
987987

988988
#[test]
@@ -1515,8 +1515,8 @@ fn test_union_of_extruded_shapes() {
15151515

15161516
// Its bounding box should span at least from z=0 to z=1.5
15171517
let bbox = unioned.bounding_box();
1518-
assert!(bbox.mins.z <= 0.0 + EPSILON);
1519-
assert!(bbox.maxs.z >= 1.5 - EPSILON);
1518+
assert!(bbox.mins.z <= 0.0 + eps::<T>());
1519+
assert!(bbox.maxs.z >= 1.5 - eps::<T>());
15201520
}
15211521

15221522
#[test]
@@ -1537,7 +1537,7 @@ fn test_flatten_cube() {
15371537
let bbox = flattened.bounding_box();
15381538
let thickness = bbox.maxs.z - bbox.mins.z;
15391539
assert!(
1540-
thickness.abs() < EPSILON,
1540+
thickness.abs() < eps::<T>(),
15411541
"Flattened shape should have negligible thickness in z"
15421542
);
15431543
}

src/traits.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::aabb::Aabb;
2-
use crate::math_ndsp::consts::{Real, EPSILON};
32
use crate::mesh::plane::Plane;
4-
use crate::math_ndsp::{Matrix3, Matrix4, Rotation3, Translation3, Vector3, Scalar};
3+
use crate::math_ndsp::{Matrix3, Matrix4, Rotation3, Translation3, Vector3, Scalar, eps};
54

65
/// Boolean operations + transformations
76
pub trait CSG: Sized + Clone {
@@ -120,7 +119,7 @@ pub trait CSG: Sized + Clone {
120119
fn mirror(&self, plane: Plane<T>) -> Self {
121120
// Normal might not be unit, so compute its length:
122121
let len = plane.normal().norm();
123-
if len.abs() < EPSILON {
122+
if len.abs() < eps::<T>() {
124123
// Degenerate plane? Just return clone (no transform)
125124
return self.clone();
126125
}

0 commit comments

Comments
 (0)