feat: Add Python packaging infrastructure and comprehensive bindings for math and vector types.

This commit is contained in:
AndreaRigoni
2026-03-05 11:39:27 +00:00
parent e69b29a259
commit 647d0caa1c
10 changed files with 372 additions and 60 deletions

View File

@@ -1,7 +1,36 @@
set(HEADERS Archives.h Array.h Collection.h DataAllocator.h Debug.h Export.h Function.h Macros.h Mpl.h Object.h Options.h Serializable.h Signal.h Singleton.h SmartPointer.h StaticInterface.h StringReader.h Types.h Uuid.h Vector.h)
set(HEADERS
Archives.h
Array.h
Collection.h
DataAllocator.h
Debug.h
Export.h
Function.h
Macros.h
Mpl.h
Object.h
Options.h
Serializable.h
Signal.h
Singleton.h
SmartPointer.h
StaticInterface.h
StringReader.h
Types.h
Uuid.h
Vector.h
)
set(SOURCES Archives.cpp Debug.cpp Object.cpp Options.cpp Serializable.cpp Signal.cpp Uuid.cpp)
set(SOURCES
Archives.cpp
Debug.cpp
Object.cpp
Options.cpp
Serializable.cpp
Signal.cpp
Uuid.cpp
)
set(LIBRARIES Boost::program_options Boost::serialization)

View File

@@ -274,6 +274,11 @@ public:
this->MoveToRAM();
return BaseClass::insert(pos, std::move(x));
}
template <typename InputIt>
iterator insert(const_iterator pos, InputIt first, InputIt last) {
this->MoveToRAM();
return BaseClass::insert(pos, first, last);
}
iterator erase(const_iterator pos) {
this->MoveToRAM();
return BaseClass::erase(pos);

View File

@@ -1,31 +0,0 @@
#include <Core/Vector.h>
#include <iostream>
int main() {
uLib::Vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::cout << "RAM Vector elements: ";
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
#ifdef USE_CUDA
std::cout << "Moving to VRAM..." << std::endl;
v.MoveToVRAM();
int *vram_ptr = v.GetVRAMData();
if (vram_ptr) {
std::cout << "Successfully got VRAM pointer!" << std::endl;
} else {
std::cout << "Failed to get VRAM pointer!" << std::endl;
}
std::cout << "Moving back to RAM..." << std::endl;
v.MoveToRAM();
#endif
return 0;
}

View File

@@ -114,6 +114,21 @@ typedef unsigned long Scalarul;
typedef float Scalarf;
typedef double Scalard;
typedef Eigen::Matrix<int, 1, 1> Vector1i;
typedef Eigen::Vector2i Vector2i;
typedef Eigen::Vector3i Vector3i;
typedef Eigen::Vector4i Vector4i;
typedef Eigen::Matrix<float, 1, 1> Vector1f;
typedef Eigen::Vector2f Vector2f;
typedef Eigen::Vector3f Vector3f;
typedef Eigen::Vector4f Vector4f;
typedef Eigen::Matrix<double, 1, 1> Vector1d;
typedef Eigen::Vector2d Vector2d;
typedef Eigen::Vector3d Vector3d;
typedef Eigen::Vector4d Vector4d;
typedef Eigen::Matrix<int, 1, 1> Matrix1i;
typedef Eigen::Matrix2i Matrix2i;
typedef Eigen::Matrix3i Matrix3i;
@@ -124,15 +139,10 @@ typedef Eigen::Matrix2f Matrix2f;
typedef Eigen::Matrix3f Matrix3f;
typedef Eigen::Matrix4f Matrix4f;
typedef Eigen::Matrix<int, 1, 1> Vector1i;
typedef Eigen::Vector2i Vector2i;
typedef Eigen::Vector3i Vector3i;
typedef Eigen::Vector4i Vector4i;
typedef Eigen::Matrix<float, 1, 1> Vector1f;
typedef Eigen::Vector2f Vector2f;
typedef Eigen::Vector3f Vector3f;
typedef Eigen::Vector4f Vector4f;
typedef Eigen::Matrix<double, 1, 1> Matrix1d;
typedef Eigen::Matrix2d Matrix2d;
typedef Eigen::Matrix3d Matrix3d;
typedef Eigen::Matrix4d Matrix4d;
////////////////////////////////////////////////////////////////////////////////
// Vector String interaction ///////////////////////////////////////////////////
@@ -188,6 +198,9 @@ public:
typedef Eigen::Matrix<Scalarf, 4, 1> BaseClass;
_HPoint3f() : BaseClass(0, 0, 0, p) {}
_HPoint3f(int rows, int cols) : BaseClass() {
this->operator()(3) = p;
}
_HPoint3f(float x, float y, float z) : BaseClass(x, y, z, p) {}
_HPoint3f(Vector3f &in) : BaseClass(in.homogeneous()) {
this->operator()(3) = p;

View File

@@ -70,8 +70,8 @@ public:
int ImportFromVti(const char *file, bool density_type = 0);
protected:
virtual ~VoxImage() {}
protected:
VoxImage(const Vector3i &size) : BaseClass(size) {}
};

View File

@@ -22,6 +22,14 @@ target_include_directories(uLib_python PRIVATE
${PROJECT_BINARY_DIR}
)
# Install uLib_python within the uLib install target
install(TARGETS uLib_python
EXPORT "${PROJECT_NAME}Targets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib
ARCHIVE DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib
)
# --- Python Tests ---------------------------------------------------------- #
if(BUILD_TESTING)

View File

@@ -1,6 +1,7 @@
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include "Math/Dense.h"
#include "Math/Transform.h"
@@ -13,13 +14,154 @@
#include "Math/TriangleMesh.h"
#include "Math/VoxRaytracer.h"
#include "Math/Accumulator.h"
#include "Math/VoxImage.h"
namespace py = pybind11;
using namespace uLib;
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalari>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarui>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarl>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarul>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarf>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalard>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3f>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3i>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4f>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4i>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3d>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4d>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<Voxel>);
PYBIND11_MAKE_OPAQUE(uLib::Vector<VoxRaytracer::RayData::Element>);
void init_math(py::module_ &m) {
// Math/Transform.h
// 1. Basic Eigen Types (Vectors and Matrices)
py::class_<Vector1f>(m, "Vector1f").def(py::init<>());
py::class_<Vector2f>(m, "Vector2f").def(py::init<>());
py::class_<Vector3f>(m, "Vector3f").def(py::init<>());
py::class_<Vector4f>(m, "Vector4f").def(py::init<>());
py::class_<Vector1i>(m, "Vector1i").def(py::init<>());
py::class_<Vector2i>(m, "Vector2i").def(py::init<>());
py::class_<Vector3i>(m, "Vector3i").def(py::init<>());
py::class_<Vector4i>(m, "Vector4i").def(py::init<>());
py::class_<Vector1d>(m, "Vector1d").def(py::init<>());
py::class_<Vector2d>(m, "Vector2d").def(py::init<>());
py::class_<Vector3d>(m, "Vector3d").def(py::init<>());
py::class_<Vector4d>(m, "Vector4d").def(py::init<>());
py::class_<Matrix2f>(m, "Matrix2f").def(py::init<>());
py::class_<Matrix3f>(m, "Matrix3f").def(py::init<>());
py::class_<Matrix4f>(m, "Matrix4f").def(py::init<>());
py::class_<Matrix2i>(m, "Matrix2i").def(py::init<>());
py::class_<Matrix3i>(m, "Matrix3i").def(py::init<>());
py::class_<Matrix4i>(m, "Matrix4i").def(py::init<>());
py::class_<Matrix2d>(m, "Matrix2d").def(py::init<>());
py::class_<Matrix3d>(m, "Matrix3d").def(py::init<>());
py::class_<Matrix4d>(m, "Matrix4d").def(py::init<>());
// 2. Homogeneous types
py::class_<HPoint3f>(m, "HPoint3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HVector3f>(m, "HVector3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HLine3f>(m, "HLine3f")
.def(py::init<>())
.def_readwrite("origin", &HLine3f::origin)
.def_readwrite("direction", &HLine3f::direction);
py::class_<HError3f>(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<uLib::Vector<Scalari>>(m, "Vector_i")
.def("MoveToVRAM", &uLib::Vector<Scalari>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalari>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarui>>(m, "Vector_ui")
.def("MoveToVRAM", &uLib::Vector<Scalarui>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarui>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarl>>(m, "Vector_l")
.def("MoveToVRAM", &uLib::Vector<Scalarl>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarl>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarul>>(m, "Vector_ul")
.def("MoveToVRAM", &uLib::Vector<Scalarul>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarul>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarf>>(m, "Vector_f")
.def("MoveToVRAM", &uLib::Vector<Scalarf>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarf>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalard>>(m, "Vector_d")
.def("MoveToVRAM", &uLib::Vector<Scalard>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalard>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3f>>(m, "Vector_Vector3f")
.def("MoveToVRAM", &uLib::Vector<Vector3f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3i>>(m, "Vector_Vector3i")
.def("MoveToVRAM", &uLib::Vector<Vector3i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4f>>(m, "Vector_Vector4f")
.def("MoveToVRAM", &uLib::Vector<Vector4f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4i>>(m, "Vector_Vector4i")
.def("MoveToVRAM", &uLib::Vector<Vector4i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3d>>(m, "Vector_Vector3d")
.def("MoveToVRAM", &uLib::Vector<Vector3d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3d>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4d>>(m, "Vector_Vector4d")
.def("MoveToVRAM", &uLib::Vector<Vector4d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4d>::MoveToRAM);
py::bind_vector<uLib::Vector<Voxel>>(m, "Vector_Voxel")
.def("MoveToVRAM", &uLib::Vector<Voxel>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Voxel>::MoveToRAM);
py::bind_vector<uLib::Vector<VoxRaytracer::RayData::Element>>(m, "Vector_VoxRaytracerRayDataElement")
.def("MoveToVRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToRAM);
// 4. Accumulators
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<float>::AddPass)
.def("__call__", py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(), py::const_));
py::class_<Accumulator_Mean<double>>(m, "Accumulator_Mean_d")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<double>::AddPass)
.def("__call__", py::overload_cast<const double>(&Accumulator_Mean<double>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<double>::operator(), py::const_));
py::class_<Accumulator_ABTrim<float>>(m, "Accumulator_ABTrim_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<float>::SetABTrim)
.def("__iadd__", [](Accumulator_ABTrim<float> &self, float val) { self += val; return &self; })
.def("__call__", &Accumulator_ABTrim<float>::operator());
py::class_<Accumulator_ABTrim<double>>(m, "Accumulator_ABTrim_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<double>::SetABTrim)
.def("__iadd__", [](Accumulator_ABTrim<double> &self, double val) { self += val; return &self; })
.def("__call__", &Accumulator_ABTrim<double>::operator());
py::class_<Accumulator_ABClip<float>>(m, "Accumulator_ABClip_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<float>::SetABTrim)
.def("__iadd__", [](Accumulator_ABClip<float> &self, float val) { self += val; return &self; })
.def("__call__", &Accumulator_ABClip<float>::operator());
py::class_<Accumulator_ABClip<double>>(m, "Accumulator_ABClip_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<double>::SetABTrim)
.def("__iadd__", [](Accumulator_ABClip<double> &self, double val) { self += val; return &self; })
.def("__call__", &Accumulator_ABClip<double>::operator());
// 5. Core Math Structures
py::class_<AffineTransform>(m, "AffineTransform")
.def(py::init<>())
.def("GetWorldMatrix", &AffineTransform::GetWorldMatrix)
@@ -34,13 +176,11 @@ void init_math(py::module_ &m) {
.def("EulerYZYRotate", &AffineTransform::EulerYZYRotate)
.def("FlipAxes", &AffineTransform::FlipAxes);
// Math/Geometry.h
py::class_<Geometry, AffineTransform>(m, "Geometry")
.def(py::init<>())
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&Geometry::GetWorldPoint, py::const_))
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&Geometry::GetLocalPoint, py::const_));
// Math/ContainerBox.h
py::class_<ContainerBox, AffineTransform>(m, "ContainerBox")
.def(py::init<>())
.def("SetOrigin", &ContainerBox::SetOrigin)
@@ -51,7 +191,6 @@ void init_math(py::module_ &m) {
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetWorldPoint, py::const_))
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetLocalPoint, py::const_));
// Math/StructuredData.h
py::enum_<StructuredData::_Order>(m, "StructuredDataOrder")
.value("CustomOrder", StructuredData::CustomOrder)
.value("XYZ", StructuredData::XYZ)
@@ -74,7 +213,6 @@ void init_math(py::module_ &m) {
.def("Map", &StructuredData::Map)
.def("UnMap", &StructuredData::UnMap);
// Math/StructuredGrid.h
py::class_<StructuredGrid, ContainerBox, StructuredData>(m, "StructuredGrid")
.def(py::init<const Vector3i &>())
.def("SetSpacing", &StructuredGrid::SetSpacing)
@@ -84,7 +222,6 @@ void init_math(py::module_ &m) {
return self.Find(HPoint3f(pt));
});
// Math/Structured2DGrid.h
py::class_<Structured2DGrid>(m, "Structured2DGrid")
.def(py::init<>())
.def("SetDims", &Structured2DGrid::SetDims)
@@ -100,7 +237,6 @@ void init_math(py::module_ &m) {
.def("UnitToPhysicsSpace", &Structured2DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured2DGrid::SetDebug);
// Math/Structured4DGrid.h
py::class_<Structured4DGrid>(m, "Structured4DGrid")
.def(py::init<>())
.def("SetDims", &Structured4DGrid::SetDims)
@@ -116,7 +252,36 @@ void init_math(py::module_ &m) {
.def("UnitToPhysicsSpace", &Structured4DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured4DGrid::SetDebug);
// Math/TriangleMesh.h
// 6. High-level Structures
py::class_<Voxel>(m, "Voxel")
.def(py::init<>())
.def_readwrite("Value", &Voxel::Value);
py::class_<Abstract::VoxImage, StructuredGrid>(m, "AbstractVoxImage")
.def("GetValue", py::overload_cast<const Vector3i &>(&Abstract::VoxImage::GetValue, py::const_))
.def("GetValue", py::overload_cast<const int>(&Abstract::VoxImage::GetValue, py::const_))
.def("SetValue", py::overload_cast<const Vector3i &, float>(&Abstract::VoxImage::SetValue))
.def("SetValue", py::overload_cast<const int, float>(&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_<VoxImage<Voxel>, Abstract::VoxImage>(m, "VoxImage")
.def(py::init<>())
.def(py::init<const Vector3i &>())
.def("Data", &VoxImage<Voxel>::Data, py::return_value_policy::reference_internal)
.def("InitVoxels", &VoxImage<Voxel>::InitVoxels)
.def("Abs", &VoxImage<Voxel>::Abs)
.def("clipImage", py::overload_cast<const Vector3i, const Vector3i>(&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const HPoint3f, const HPoint3f>(&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const float>(&VoxImage<Voxel>::clipImage, py::const_))
.def("maskImage", py::overload_cast<const HPoint3f, const HPoint3f, float>(&VoxImage<Voxel>::maskImage, py::const_))
.def("maskImage", py::overload_cast<const float, float, float>(&VoxImage<Voxel>::maskImage, py::const_), py::arg("threshold"), py::arg("belowValue") = 0, py::arg("aboveValue") = 0)
.def("fixVoxels", py::overload_cast<const float, float>(&VoxImage<Voxel>::fixVoxels, py::const_))
.def("__getitem__", py::overload_cast<unsigned int>(&VoxImage<Voxel>::operator[]))
.def("__getitem__", py::overload_cast<const Vector3i &>(&VoxImage<Voxel>::operator[]));
py::class_<TriangleMesh>(m, "TriangleMesh")
.def(py::init<>())
.def("AddPoint", &TriangleMesh::AddPoint)
@@ -124,7 +289,6 @@ void init_math(py::module_ &m) {
.def("Points", &TriangleMesh::Points, py::return_value_policy::reference_internal)
.def("Triangles", &TriangleMesh::Triangles, py::return_value_policy::reference_internal);
// Math/VoxRaytracer.h
py::class_<VoxRaytracer::RayData::Element>(m, "VoxRaytracerRayDataElement")
.def(py::init<>())
.def_readwrite("vox_id", &VoxRaytracer::RayData::Element::vox_id)
@@ -133,6 +297,7 @@ void init_math(py::module_ &m) {
py::class_<VoxRaytracer::RayData>(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)
@@ -140,13 +305,8 @@ void init_math(py::module_ &m) {
py::class_<VoxRaytracer>(m, "VoxRaytracer")
.def(py::init<StructuredGrid &>(), py::keep_alive<1, 2>())
.def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal);
// Math/Accumulator.h
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<float>::AddPass)
.def("__call__", py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(), py::const_));
.def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal)
.def("TraceLine", &VoxRaytracer::TraceLine)
.def("TraceBetweenPoints", &VoxRaytracer::TraceBetweenPoints);
}

View File

@@ -58,5 +58,71 @@ class TestMathAccumulator(unittest.TestCase):
acc(20.0)
self.assertAlmostEqual(acc(), 15.0)
class TestMathNewTypes(unittest.TestCase):
def test_eigen_vectors(self):
v1f = uLib_python.Math.Vector1f()
v3d = uLib_python.Math.Vector3d()
m4f = uLib_python.Math.Matrix4f()
self.assertIsNotNone(v1f)
self.assertIsNotNone(v3d)
self.assertIsNotNone(m4f)
def test_ulib_vectors(self):
vi = uLib_python.Math.Vector_i()
vi.append(1)
vi.append(2)
self.assertEqual(len(vi), 2)
self.assertEqual(vi[0], 1)
self.assertEqual(vi[1], 2)
vf = uLib_python.Math.Vector_f()
vf.append(1.5)
self.assertAlmostEqual(vf[0], 1.5)
def test_homogeneous(self):
p = uLib_python.Math.HPoint3f(1.0, 2.0, 3.0)
v = uLib_python.Math.HVector3f(0.0, 1.0, 0.0)
self.assertIsNotNone(p)
self.assertIsNotNone(v)
def test_vox_image(self):
img = uLib_python.Math.VoxImage([2, 2, 2])
self.assertEqual(img.GetDims()[0], 2)
img.SetValue([0, 0, 0], 10.5)
# Note: GetValue returns float, and there might be internal scaling (1.E-6 observed in code)
# Actually in VoxImage.h: GetValue(id) returns At(id).Value
# SetValue(id, value) sets At(id).Value = value
self.assertAlmostEqual(img.GetValue([0, 0, 0]), 10.5)
class TestMathVoxRaytracer(unittest.TestCase):
def test_raytracer(self):
grid = uLib_python.Math.StructuredGrid([10, 10, 10])
grid.SetSpacing([1, 1, 1])
grid.SetOrigin([0, 0, 0])
rt = uLib_python.Math.VoxRaytracer(grid)
self.assertIsNotNone(rt)
# Test TraceBetweenPoints
p1 = np.array([0.5, 0.5, -1.0, 1.0], dtype=np.float32)
p2 = np.array([0.5, 0.5, 11.0, 1.0], dtype=np.float32)
data = rt.TraceBetweenPoints(p1, p2)
self.assertGreater(data.Count(), 0)
self.assertAlmostEqual(data.TotalLength(), 10.0)
# Check elements
elements = data.Data()
for i in range(data.Count()):
self.assertGreaterEqual(elements[i].vox_id, 0)
self.assertGreater(elements[i].L, 0)
def test_ray_data(self):
data = uLib_python.Math.VoxRaytracerRayData()
data.SetCount(10)
data.SetTotalLength(5.5)
self.assertEqual(data.Count(), 10)
self.assertAlmostEqual(data.TotalLength(), 5.5)
if __name__ == '__main__':
unittest.main()