From 85b87eec644dc59f7a37ceff95d527c9bef34930 Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Thu, 14 Aug 2025 21:22:12 -0400 Subject: [PATCH 01/10] Stub transform method --- crates/opencascade/src/primitives/shape.rs | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/opencascade/src/primitives/shape.rs b/crates/opencascade/src/primitives/shape.rs index 4021d1d6c..89117c96d 100644 --- a/crates/opencascade/src/primitives/shape.rs +++ b/crates/opencascade/src/primitives/shape.rs @@ -7,7 +7,7 @@ use crate::{ Error, }; use cxx::UniquePtr; -use glam::{dvec2, dvec3, DVec3}; +use glam::{dvec2, dvec3, DMat4, DVec3}; use opencascade_sys::ffi; use std::path::Path; @@ -631,6 +631,10 @@ impl Shape { self.inner.pin_mut().set_global_translation(&location, false); } + pub fn transform(&self, transform: &DMat4) -> Self { + Self::from_shape(&self.inner) + } + pub fn mesh(&self) -> Result { self.mesh_with_tolerance(0.01) } @@ -754,3 +758,25 @@ impl ChamferMaker { Shape::from_shape(self.inner.pin_mut().Shape()) } } + +#[cfg(test)] +mod test { + use crate::{primitives::IntoShape, workplane::Workplane}; + + #[test] + fn transform_shape() { + let s = Workplane::xy().sketch().line_to(1.0, 2.0).wire().into_shape(); + + for p in s.edges() { + assert_eq!(p.start_point(), glam::dvec3(0.0, 0.0, 0.0)); + assert_eq!(p.end_point(), glam::dvec3(1.0, 2.0, 0.0)); + } + + let t = s.transform(&glam::DMat4::from_translation(glam::dvec3(2.0, 3.0, 4.0))); + + for p in t.edges() { + assert_eq!(p.start_point(), glam::dvec3(2.0, 3.0, 4.0)); + assert_eq!(p.end_point(), glam::dvec3(3.0, 5.0, 4.0)); + } + } +} From c03e313884696ab2f87d1a889ff4473d602425ea Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 11:41:01 -0400 Subject: [PATCH 02/10] Add transform module --- crates/opencascade/src/transform.rs | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 crates/opencascade/src/transform.rs diff --git a/crates/opencascade/src/transform.rs b/crates/opencascade/src/transform.rs new file mode 100644 index 000000000..0ccc53e6f --- /dev/null +++ b/crates/opencascade/src/transform.rs @@ -0,0 +1,42 @@ +use cxx::UniquePtr; +use glam::DMat4; +use opencascade_sys::ffi; + +pub fn gp_trsf(mat: &DMat4) -> UniquePtr { + let mut t = ffi::new_transform(); + t.pin_mut().SetValues( + mat.x_axis.x, + mat.y_axis.x, + mat.z_axis.x, + mat.w_axis.x, + mat.x_axis.y, + mat.y_axis.y, + mat.z_axis.y, + mat.w_axis.y, + mat.x_axis.z, + mat.y_axis.z, + mat.z_axis.z, + mat.w_axis.z, + ); + t +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn set_gp_trsf_values() { + let m = glam::dmat4( + glam::dvec4(0.0, 0.0, 1.0, 0.0), + glam::dvec4(1.0, 0.0, 0.0, 0.0), + glam::dvec4(0.0, 1.0, 0.0, 0.0), + glam::dvec4(0.0, 0.0, 0.0, 1.0), + ); + + let m = glam::DMat4::IDENTITY; + + let t = gp_trsf(&m); + + assert_eq!(t.Value(0, 0), 0.0); + } +} From 3db289c26dfa75d2d03ce36bde11cde31a4a660d Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 11:53:54 -0400 Subject: [PATCH 03/10] Pass transform test --- crates/opencascade-sys/src/lib.rs | 17 +++++++++++++++++ crates/opencascade/src/transform.rs | 12 +++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/crates/opencascade-sys/src/lib.rs b/crates/opencascade-sys/src/lib.rs index ab1411574..06ae94cdb 100644 --- a/crates/opencascade-sys/src/lib.rs +++ b/crates/opencascade-sys/src/lib.rs @@ -1027,6 +1027,21 @@ pub mod ffi { pub fn SetScale(self: Pin<&mut gp_Trsf>, point: &gp_Pnt, scale: f64); pub fn SetTranslation(self: Pin<&mut gp_Trsf>, point1: &gp_Pnt, point2: &gp_Pnt); pub fn Value(self: &gp_Trsf, the_row: i32, the_col: i32) -> f64; + pub fn SetValues( + self: Pin<&mut gp_Trsf>, + a11: f64, + a12: f64, + a13: f64, + a14: f64, + a21: f64, + a22: f64, + a23: f64, + a24: f64, + a31: f64, + a32: f64, + a33: f64, + a34: f64, + ); #[cxx_name = "SetTranslationPart"] pub fn set_translation_vec(self: Pin<&mut gp_Trsf>, translation: &gp_Vec); @@ -1403,6 +1418,8 @@ pub mod ffi { type BRepBndLib; pub fn BRepBndLib_Add(shape: &TopoDS_Shape, bb: Pin<&mut Bnd_Box>, use_triangulation: bool); + + // BRep } } diff --git a/crates/opencascade/src/transform.rs b/crates/opencascade/src/transform.rs index 0ccc53e6f..0a3aece78 100644 --- a/crates/opencascade/src/transform.rs +++ b/crates/opencascade/src/transform.rs @@ -33,10 +33,16 @@ mod test { glam::dvec4(0.0, 0.0, 0.0, 1.0), ); - let m = glam::DMat4::IDENTITY; - let t = gp_trsf(&m); - assert_eq!(t.Value(0, 0), 0.0); + // 3D diagonal is 0 + assert_eq!(t.Value(1, 1), 0.0); + assert_eq!(t.Value(2, 2), 0.0); + assert_eq!(t.Value(3, 3), 0.0); + + // Permuted axes are preserved + assert_eq!(t.Value(1, 2), 1.0); + assert_eq!(t.Value(3, 1), 1.0); + assert_eq!(t.Value(2, 3), 1.0); } } From 6033b71b3538c3dd16791625429326a962902273 Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 11:56:46 -0400 Subject: [PATCH 04/10] Document transform function --- crates/opencascade/src/transform.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/opencascade/src/transform.rs b/crates/opencascade/src/transform.rs index 0a3aece78..1f6e15bc9 100644 --- a/crates/opencascade/src/transform.rs +++ b/crates/opencascade/src/transform.rs @@ -2,6 +2,13 @@ use cxx::UniquePtr; use glam::DMat4; use opencascade_sys::ffi; +/// Create a `gp_Trsf` from a `DMat4`. +/// +/// Note that OCC only allows setting values for the upper 3x4 matrix. I.e. the +/// XYZ components of each column. +/// +/// Additionally, OCC ensures orthogonality of the matrix before the method +/// returns. pub fn gp_trsf(mat: &DMat4) -> UniquePtr { let mut t = ffi::new_transform(); t.pin_mut().SetValues( @@ -26,6 +33,7 @@ mod test { use super::*; #[test] fn set_gp_trsf_values() { + // Matrix with permuted axes let m = glam::dmat4( glam::dvec4(0.0, 0.0, 1.0, 0.0), glam::dvec4(1.0, 0.0, 0.0, 0.0), From a16cecbb682e2b649b327f91f08098042eefc9a8 Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 13:14:57 -0400 Subject: [PATCH 05/10] Add transform lib --- crates/opencascade/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/opencascade/src/lib.rs b/crates/opencascade/src/lib.rs index af659c389..df759141a 100644 --- a/crates/opencascade/src/lib.rs +++ b/crates/opencascade/src/lib.rs @@ -6,6 +6,7 @@ pub mod kicad; pub mod mesh; pub mod primitives; pub mod section; +pub mod transform; pub mod workplane; mod law_function; From 2a17091a0efc163d23ef6a6e61a802d046d23fcf Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 13:19:45 -0400 Subject: [PATCH 06/10] Add comments --- crates/opencascade/src/primitives/shape.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/opencascade/src/primitives/shape.rs b/crates/opencascade/src/primitives/shape.rs index 89117c96d..c0d7148af 100644 --- a/crates/opencascade/src/primitives/shape.rs +++ b/crates/opencascade/src/primitives/shape.rs @@ -631,8 +631,12 @@ impl Shape { self.inner.pin_mut().set_global_translation(&location, false); } + /// Copy `self` using the upper 3x4 matrix of `transform`. pub fn transform(&self, transform: &DMat4) -> Self { - Self::from_shape(&self.inner) + let t = crate::transform::gp_trsf(transform); + // NOTE: Setting the copy argument does not seem to have an effect. + let mut res = ffi::BRepBuilderAPI_Transform_ctor(&self.inner, &t, true); + Self::from_shape(res.pin_mut().Shape()) } pub fn mesh(&self) -> Result { From 8b3108770ac1af3a808e6097ec680ad278cb3081 Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 13:20:12 -0400 Subject: [PATCH 07/10] Rearrange test to ensure state validity --- crates/opencascade/src/primitives/shape.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/opencascade/src/primitives/shape.rs b/crates/opencascade/src/primitives/shape.rs index c0d7148af..5719aabc4 100644 --- a/crates/opencascade/src/primitives/shape.rs +++ b/crates/opencascade/src/primitives/shape.rs @@ -770,14 +770,13 @@ mod test { #[test] fn transform_shape() { let s = Workplane::xy().sketch().line_to(1.0, 2.0).wire().into_shape(); + let t = s.transform(&glam::DMat4::from_translation(glam::dvec3(2.0, 3.0, 4.0))); for p in s.edges() { assert_eq!(p.start_point(), glam::dvec3(0.0, 0.0, 0.0)); assert_eq!(p.end_point(), glam::dvec3(1.0, 2.0, 0.0)); } - let t = s.transform(&glam::DMat4::from_translation(glam::dvec3(2.0, 3.0, 4.0))); - for p in t.edges() { assert_eq!(p.start_point(), glam::dvec3(2.0, 3.0, 4.0)); assert_eq!(p.end_point(), glam::dvec3(3.0, 5.0, 4.0)); From fd708b7a4ed3cb12de3dcbdc9d932c37a37a6894 Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 13:34:26 -0400 Subject: [PATCH 08/10] Use more complex matrix in test --- crates/opencascade/src/primitives/shape.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/opencascade/src/primitives/shape.rs b/crates/opencascade/src/primitives/shape.rs index 5719aabc4..b7f16bc05 100644 --- a/crates/opencascade/src/primitives/shape.rs +++ b/crates/opencascade/src/primitives/shape.rs @@ -766,20 +766,31 @@ impl ChamferMaker { #[cfg(test)] mod test { use crate::{primitives::IntoShape, workplane::Workplane}; + const TOLERANCE: f64 = 1e-10; #[test] fn transform_shape() { + let m = glam::dmat4( + glam::dvec4(0.0, 0.0, 1.0, 0.0), + glam::dvec4(1.0, 0.0, 0.0, 0.0), + glam::dvec4(0.0, 1.0, 0.0, 0.0), + glam::dvec4(5.0, 6.0, 7.0, 1.0), + ); let s = Workplane::xy().sketch().line_to(1.0, 2.0).wire().into_shape(); - let t = s.transform(&glam::DMat4::from_translation(glam::dvec3(2.0, 3.0, 4.0))); + let t = s.transform(&m); for p in s.edges() { - assert_eq!(p.start_point(), glam::dvec3(0.0, 0.0, 0.0)); - assert_eq!(p.end_point(), glam::dvec3(1.0, 2.0, 0.0)); + assert!(p.start_point().abs_diff_eq(glam::dvec3(0.0, 0.0, 0.0), TOLERANCE)); + assert!(p.end_point().abs_diff_eq(glam::dvec3(1.0, 2.0, 0.0), TOLERANCE)); } for p in t.edges() { - assert_eq!(p.start_point(), glam::dvec3(2.0, 3.0, 4.0)); - assert_eq!(p.end_point(), glam::dvec3(3.0, 5.0, 4.0)); + // The start point includes only offset + assert!(p.start_point().abs_diff_eq(glam::dvec3(5.0, 6.0, 7.0), TOLERANCE)); + assert!(p + .end_point() + // The end point includes permutation and offset + .abs_diff_eq(glam::dvec3(2.0 + 5.0, 0.0 + 6.0, 1.0 + 7.0), TOLERANCE)); } } } From a87a19d789c820566023775275337a02a559f2dd Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Fri, 15 Aug 2025 13:41:12 -0400 Subject: [PATCH 09/10] Remove unused comment section --- crates/opencascade-sys/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/opencascade-sys/src/lib.rs b/crates/opencascade-sys/src/lib.rs index 06ae94cdb..f1bc8e60c 100644 --- a/crates/opencascade-sys/src/lib.rs +++ b/crates/opencascade-sys/src/lib.rs @@ -1418,8 +1418,6 @@ pub mod ffi { type BRepBndLib; pub fn BRepBndLib_Add(shape: &TopoDS_Shape, bb: Pin<&mut Bnd_Box>, use_triangulation: bool); - - // BRep } } From 28885f6b4f5bc95c6e45b3d31dd4cd2fd51d8f7a Mon Sep 17 00:00:00 2001 From: Tyler Kvochick Date: Thu, 21 Aug 2025 12:56:48 -0400 Subject: [PATCH 10/10] Allow too many arguments for cpp SetValues wrapper --- crates/opencascade-sys/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/opencascade-sys/src/lib.rs b/crates/opencascade-sys/src/lib.rs index f1bc8e60c..66814f387 100644 --- a/crates/opencascade-sys/src/lib.rs +++ b/crates/opencascade-sys/src/lib.rs @@ -1027,6 +1027,7 @@ pub mod ffi { pub fn SetScale(self: Pin<&mut gp_Trsf>, point: &gp_Pnt, scale: f64); pub fn SetTranslation(self: Pin<&mut gp_Trsf>, point1: &gp_Pnt, point2: &gp_Pnt); pub fn Value(self: &gp_Trsf, the_row: i32, the_col: i32) -> f64; + #[allow(clippy::too_many_arguments)] pub fn SetValues( self: Pin<&mut gp_Trsf>, a11: f64,