#include #include #include #include #include #include "Math/Accumulator.h" #include "Math/ContainerBox.h" #include "Math/Dense.h" #include "Math/Geometry.h" #include "Math/Structured2DGrid.h" #include "Math/Structured4DGrid.h" #include "Math/StructuredData.h" #include "Math/StructuredGrid.h" #include "Math/Transform.h" #include "Math/TriangleMesh.h" #include "Math/QuadMesh.h" #include "Math/VoxImage.h" #include "Math/VoxRaytracer.h" namespace py = pybind11; using namespace uLib; PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); PYBIND11_MAKE_OPAQUE(uLib::Vector); template void bind_eigen_type(py::module_ &m, const char *name) { using Scalar = typename MatrixType::Scalar; constexpr bool is_vector = MatrixType::IsVectorAtCompileTime; // Default constructor (zeros) m.def(name, []() -> MatrixType { if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) { return MatrixType(); // Empty dynamic matrix } else { return MatrixType::Zero(); // Zero static matrix } }); // Specialized constructor for dynamic matrices if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) { m.def(name, [](int rows, int cols) -> MatrixType { MatrixType mat; mat.setZero(rows, cols); return mat; }); } // Initialize from list m.def(name, [](py::list l) -> MatrixType { MatrixType mat; if constexpr (is_vector) { mat.setZero(l.size()); for (size_t i = 0; i < l.size(); ++i) { mat(i) = l[i].cast(); } } else { int rows = MatrixType::RowsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::RowsAtCompileTime; int cols = MatrixType::ColsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::ColsAtCompileTime; mat.setZero(rows, cols); for (size_t i = 0; i < (size_t)l.size(); ++i) { mat(i / cols, i % cols) = l[i].cast(); } } return mat; }); // Initialize from py::array m.def(name, [](py::array_t arr) -> MatrixType { auto buf = arr.request(); MatrixType mat; if constexpr (is_vector) { mat.setZero(buf.size); Scalar *ptr = static_cast(buf.ptr); for (ssize_t i = 0; i < buf.size; ++i) mat(i) = ptr[i]; } else { int rows = buf.shape.size() > 0 ? (int)buf.shape[0] : 1; int cols = buf.shape.size() > 1 ? (int)buf.shape[1] : 1; mat.setZero(rows, cols); Scalar *ptr = static_cast(buf.ptr); for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { mat(i, j) = ptr[i * cols + j]; } } } return mat; }); } void init_math(py::module_ &m) { // 1. Basic Eigen Types (Vectors and Matrices) bind_eigen_type(m, "Vector1f"); bind_eigen_type(m, "Vector2f"); bind_eigen_type(m, "Vector3f"); bind_eigen_type(m, "Vector4f"); bind_eigen_type(m, "Vector1i"); bind_eigen_type(m, "Vector2i"); bind_eigen_type(m, "Vector3i"); bind_eigen_type(m, "Vector4i"); bind_eigen_type(m, "Vector1d"); bind_eigen_type(m, "Vector2d"); bind_eigen_type(m, "Vector3d"); bind_eigen_type(m, "Vector4d"); bind_eigen_type(m, "Matrix2f"); bind_eigen_type(m, "Matrix3f"); bind_eigen_type(m, "Matrix4f"); bind_eigen_type(m, "Matrix2i"); bind_eigen_type(m, "Matrix3i"); bind_eigen_type(m, "Matrix4i"); bind_eigen_type(m, "Matrix2d"); bind_eigen_type(m, "Matrix3d"); bind_eigen_type(m, "Matrix4d"); bind_eigen_type(m, "MatrixXi"); bind_eigen_type(m, "MatrixXf"); bind_eigen_type(m, "MatrixXd"); // 2. Homogeneous types py::class_(m, "HPoint3f") .def(py::init<>()) .def(py::init()) .def(py::init()); py::class_(m, "HVector3f") .def(py::init<>()) .def(py::init()) .def(py::init()); py::class_(m, "HLine3f") .def(py::init<>()) .def_readwrite("origin", &HLine3f::origin) .def_readwrite("direction", &HLine3f::direction); py::class_(m, "HError3f") .def(py::init<>()) .def_readwrite("position_error", &HError3f::position_error) .def_readwrite("direction_error", &HError3f::direction_error); // 3. Dynamic Vectors (uLib::Vector) py::bind_vector>(m, "Vector_i") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_ui") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_l") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_ul") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_f") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_d") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector3f") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector3i") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector4f") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector4i") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector3d") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Vector4d") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>(m, "Vector_Voxel") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); py::bind_vector>( m, "Vector_VoxRaytracerRayDataElement") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); // 4. Accumulators py::class_>(m, "Accumulator_Mean_f") .def(py::init<>()) .def("AddPass", &Accumulator_Mean::AddPass) .def("__call__", py::overload_cast(&Accumulator_Mean::operator())) .def("__call__", py::overload_cast<>(&Accumulator_Mean::operator(), py::const_)); py::class_>(m, "Accumulator_Mean_d") .def(py::init<>()) .def("AddPass", &Accumulator_Mean::AddPass) .def("__call__", py::overload_cast( &Accumulator_Mean::operator())) .def("__call__", py::overload_cast<>( &Accumulator_Mean::operator(), py::const_)); py::class_>(m, "Accumulator_ABTrim_f") .def(py::init<>()) .def("SetABTrim", &Accumulator_ABTrim::SetABTrim) .def("__iadd__", [](Accumulator_ABTrim &self, float val) { self += val; return &self; }) .def("__call__", &Accumulator_ABTrim::operator()); py::class_>(m, "Accumulator_ABTrim_d") .def(py::init<>()) .def("SetABTrim", &Accumulator_ABTrim::SetABTrim) .def("__iadd__", [](Accumulator_ABTrim &self, double val) { self += val; return &self; }) .def("__call__", &Accumulator_ABTrim::operator()); py::class_>(m, "Accumulator_ABClip_f") .def(py::init<>()) .def("SetABTrim", &Accumulator_ABClip::SetABTrim) .def("__iadd__", [](Accumulator_ABClip &self, float val) { self += val; return &self; }) .def("__call__", &Accumulator_ABClip::operator()); py::class_>(m, "Accumulator_ABClip_d") .def(py::init<>()) .def("SetABTrim", &Accumulator_ABClip::SetABTrim) .def("__iadd__", [](Accumulator_ABClip &self, double val) { self += val; return &self; }) .def("__call__", &Accumulator_ABClip::operator()); // 5. Core Math Structures py::class_>(m, "AffineTransform") .def(py::init<>()) .def("GetWorldMatrix", &AffineTransform::GetWorldMatrix) .def("SetPosition", &AffineTransform::SetPosition) .def("GetPosition", &AffineTransform::GetPosition) .def("Translate", &AffineTransform::Translate) .def("Scale", &AffineTransform::Scale) .def("SetRotation", &AffineTransform::SetRotation) .def("GetRotation", &AffineTransform::GetRotation) .def("Rotate", py::overload_cast(&AffineTransform::Rotate)) .def("Rotate", py::overload_cast(&AffineTransform::Rotate)) .def("Rotate", py::overload_cast(&AffineTransform::Rotate)) .def("EulerYZYRotate", &AffineTransform::EulerYZYRotate) .def("FlipAxes", &AffineTransform::FlipAxes); py::class_>(m, "Geometry") .def(py::init<>()) .def("GetWorldPoint", py::overload_cast( &Geometry::GetWorldPoint, py::const_)) .def("GetWorldPoint", py::overload_cast(&Geometry::GetWorldPoint)) .def("GetLocalPoint", py::overload_cast( &Geometry::GetLocalPoint, py::const_)) .def("GetLocalPoint", py::overload_cast(&Geometry::GetLocalPoint)); py::class_>( m, "ContainerBox") .def(py::init<>()) .def("SetOrigin", &ContainerBox::SetOrigin) .def("GetOrigin", &ContainerBox::GetOrigin) .def("SetSize", &ContainerBox::SetSize) .def("GetSize", &ContainerBox::GetSize) .def("GetLocalMatrix", &ContainerBox::GetLocalMatrix) .def("GetWorldMatrix", &ContainerBox::GetWorldMatrix) .def("GetWorldPoint", py::overload_cast( &ContainerBox::GetWorldPoint, py::const_)) .def("GetWorldPoint", py::overload_cast(&ContainerBox::GetWorldPoint)) .def("GetLocalPoint", py::overload_cast( &ContainerBox::GetLocalPoint, py::const_)) .def("GetLocalPoint", py::overload_cast(&ContainerBox::GetLocalPoint)) .def("FlipLocalAxes", &ContainerBox::FlipLocalAxes); py::enum_(m, "StructuredDataOrder") .value("CustomOrder", StructuredData::CustomOrder) .value("XYZ", StructuredData::XYZ) .value("XZY", StructuredData::XZY) .value("YXZ", StructuredData::YXZ) .value("YZX", StructuredData::YZX) .value("ZXY", StructuredData::ZXY) .value("ZYX", StructuredData::ZYX) .export_values(); py::class_>(m, "StructuredData") .def(py::init()) .def("GetDims", &StructuredData::GetDims) .def("SetDims", &StructuredData::SetDims) .def("GetIncrements", &StructuredData::GetIncrements) .def("SetIncrements", &StructuredData::SetIncrements) .def("SetDataOrder", &StructuredData::SetDataOrder) .def("GetDataOrder", &StructuredData::GetDataOrder) .def("IsInsideGrid", &StructuredData::IsInsideGrid) .def("Map", &StructuredData::Map) .def("UnMap", &StructuredData::UnMap); py::class_>(m, "StructuredGrid") .def(py::init()) .def("SetSpacing", &StructuredGrid::SetSpacing) .def("GetSpacing", &StructuredGrid::GetSpacing) .def("IsInsideBounds", &StructuredGrid::IsInsideBounds) .def("Find", [](StructuredGrid &self, Vector3f pt) { return self.Find(HPoint3f(pt)); }); py::class_(m, "Structured2DGrid") .def(py::init<>()) .def("SetDims", &Structured2DGrid::SetDims) .def("GetDims", &Structured2DGrid::GetDims) .def("IsInsideGrid", &Structured2DGrid::IsInsideGrid) .def("Map", &Structured2DGrid::Map) .def("UnMap", &Structured2DGrid::UnMap) .def("SetPhysicalSpace", &Structured2DGrid::SetPhysicalSpace) .def("GetSpacing", &Structured2DGrid::GetSpacing) .def("GetOrigin", &Structured2DGrid::GetOrigin) .def("IsInsideBounds", &Structured2DGrid::IsInsideBounds) .def("PhysicsToUnitSpace", &Structured2DGrid::PhysicsToUnitSpace) .def("UnitToPhysicsSpace", &Structured2DGrid::UnitToPhysicsSpace) .def("SetDebug", &Structured2DGrid::SetDebug); py::class_(m, "Structured4DGrid") .def(py::init<>()) .def("SetDims", &Structured4DGrid::SetDims) .def("GetDims", &Structured4DGrid::GetDims) .def("IsInsideGrid", &Structured4DGrid::IsInsideGrid) .def("Map", &Structured4DGrid::Map) .def("UnMap", &Structured4DGrid::UnMap) .def("SetPhysicalSpace", &Structured4DGrid::SetPhysicalSpace) .def("GetSpacing", &Structured4DGrid::GetSpacing) .def("GetOrigin", &Structured4DGrid::GetOrigin) .def("IsInsideBounds", &Structured4DGrid::IsInsideBounds) .def("PhysicsToUnitSpace", &Structured4DGrid::PhysicsToUnitSpace) .def("UnitToPhysicsSpace", &Structured4DGrid::UnitToPhysicsSpace) .def("SetDebug", &Structured4DGrid::SetDebug); // 6. High-level Structures py::class_(m, "Voxel") .def(py::init<>()) .def_readwrite("Value", &Voxel::Value) .def_readwrite("Count", &Voxel::Count); py::class_>(m, "AbstractVoxImage") .def("GetValue", py::overload_cast( &Abstract::VoxImage::GetValue, py::const_)) .def("GetValue", py::overload_cast( &Abstract::VoxImage::GetValue, py::const_)) .def("SetValue", py::overload_cast( &Abstract::VoxImage::SetValue)) .def("SetValue", py::overload_cast(&Abstract::VoxImage::SetValue)) .def("ExportToVtk", &Abstract::VoxImage::ExportToVtk) .def("ExportToVti", &Abstract::VoxImage::ExportToVti) .def("ImportFromVtk", &Abstract::VoxImage::ImportFromVtk) .def("ImportFromVti", &Abstract::VoxImage::ImportFromVti); py::class_, Abstract::VoxImage, std::shared_ptr>>(m, "VoxImage") .def(py::init<>()) .def(py::init()) .def("Data", &VoxImage::Data, py::return_value_policy::reference_internal) .def("InitVoxels", &VoxImage::InitVoxels) .def("Abs", &VoxImage::Abs) .def("clipImage", py::overload_cast( &VoxImage::clipImage, py::const_)) .def("clipImage", py::overload_cast( &VoxImage::clipImage, py::const_)) .def("clipImage", py::overload_cast( &VoxImage::clipImage, py::const_)) .def("maskImage", py::overload_cast( &VoxImage::maskImage, py::const_)) .def("maskImage", py::overload_cast( &VoxImage::maskImage, py::const_), py::arg("threshold"), py::arg("belowValue") = 0, py::arg("aboveValue") = 0) .def("fixVoxels", py::overload_cast( &VoxImage::fixVoxels, py::const_)) .def("__getitem__", py::overload_cast(&VoxImage::operator[])) .def("__getitem__", py::overload_cast(&VoxImage::operator[])); py::class_(m, "TriangleMesh") .def(py::init<>()) .def("AddPoint", &TriangleMesh::AddPoint) .def("AddTriangle", py::overload_cast(&TriangleMesh::AddTriangle)) .def("Points", py::overload_cast<>(&TriangleMesh::Points), py::return_value_policy::reference_internal) .def("Triangles", py::overload_cast<>(&TriangleMesh::Triangles), py::return_value_policy::reference_internal) .def("GetTriangle", &TriangleMesh::GetTriangle) .def("GetNormal", &TriangleMesh::GetNormal); py::class_(m, "QuadMesh") .def(py::init<>()) .def("AddPoint", &QuadMesh::AddPoint) .def("AddQuad", py::overload_cast(&QuadMesh::AddQuad)) .def("Points", py::overload_cast<>(&QuadMesh::Points), py::return_value_policy::reference_internal) .def("Quads", py::overload_cast<>(&QuadMesh::Quads), py::return_value_policy::reference_internal) .def("GetQuad", &QuadMesh::GetQuad) .def("GetNormal", &QuadMesh::GetNormal); py::class_(m, "VoxRaytracerRayDataElement") .def(py::init<>()) .def_readwrite("vox_id", &VoxRaytracer::RayData::Element::vox_id) .def_readwrite("L", &VoxRaytracer::RayData::Element::L); py::class_(m, "VoxRaytracerRayData") .def(py::init<>()) .def("AppendRay", &VoxRaytracer::RayData::AppendRay) .def("Data", py::overload_cast<>(&VoxRaytracer::RayData::Data), py::return_value_policy::reference_internal) .def("Count", &VoxRaytracer::RayData::Count) .def("TotalLength", &VoxRaytracer::RayData::TotalLength) .def("SetCount", &VoxRaytracer::RayData::SetCount) .def("SetTotalLength", &VoxRaytracer::RayData::SetTotalLength); py::class_(m, "VoxRaytracer") .def(py::init(), py::keep_alive<1, 2>()) .def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal) .def("TraceLine", &VoxRaytracer::TraceLine) .def("TraceBetweenPoints", &VoxRaytracer::TraceBetweenPoints); }