andrea-dev #1
@@ -120,6 +120,7 @@ include(${ROOT_USE_FILE})
|
|||||||
find_package(VTK REQUIRED)
|
find_package(VTK REQUIRED)
|
||||||
# include(${VTK_USE_FILE})
|
# include(${VTK_USE_FILE})
|
||||||
|
|
||||||
|
find_package(pybind11 REQUIRED)
|
||||||
|
|
||||||
|
|
||||||
option(CENTOS_SUPPORT "VTK definitions for CentOS" OFF)
|
option(CENTOS_SUPPORT "VTK definitions for CentOS" OFF)
|
||||||
@@ -212,6 +213,8 @@ add_subdirectory(${SRC_DIR}/Root)
|
|||||||
include_directories(${SRC_DIR}/Vtk)
|
include_directories(${SRC_DIR}/Vtk)
|
||||||
add_subdirectory(${SRC_DIR}/Vtk)
|
add_subdirectory(${SRC_DIR}/Vtk)
|
||||||
|
|
||||||
|
add_subdirectory(${SRC_DIR}/Python)
|
||||||
|
|
||||||
#add_subdirectory("${SRC_DIR}/utils/make_recipe")
|
#add_subdirectory("${SRC_DIR}/utils/make_recipe")
|
||||||
|
|
||||||
## Documentation and packages
|
## Documentation and packages
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[requires]
|
[requires]
|
||||||
eigen/3.4.0
|
eigen/3.4.0
|
||||||
boost/1.83.0
|
boost/1.83.0
|
||||||
|
pybind11/3.0.2
|
||||||
|
|
||||||
[generators]
|
[generators]
|
||||||
CMakeDeps
|
CMakeDeps
|
||||||
|
|||||||
44
src/Python/CMakeLists.txt
Normal file
44
src/Python/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
set(HEADERS "")
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
module.cpp
|
||||||
|
core_bindings.cpp
|
||||||
|
math_bindings.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use pybind11 to add the python module
|
||||||
|
pybind11_add_module(uLib_python module.cpp core_bindings.cpp math_bindings.cpp)
|
||||||
|
|
||||||
|
# Link against our C++ libraries
|
||||||
|
target_link_libraries(uLib_python PRIVATE
|
||||||
|
${PACKAGE_LIBPREFIX}Core
|
||||||
|
${PACKAGE_LIBPREFIX}Math
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include directories from Core and Math are automatically handled if target_include_directories were set appropriately,
|
||||||
|
# but we might need to manually include them if they aren't INTERFACE includes.
|
||||||
|
target_include_directories(uLib_python PRIVATE
|
||||||
|
${PROJECT_SOURCE_DIR}/src
|
||||||
|
${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Python Tests ---------------------------------------------------------- #
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||||
|
|
||||||
|
add_test(NAME pybind_general
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/pybind_test.py)
|
||||||
|
set_tests_properties(pybind_general PROPERTIES
|
||||||
|
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>")
|
||||||
|
|
||||||
|
add_test(NAME pybind_core
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/core_pybind_test.py)
|
||||||
|
set_tests_properties(pybind_core PROPERTIES
|
||||||
|
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>")
|
||||||
|
|
||||||
|
add_test(NAME pybind_math
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/math_pybind_test.py)
|
||||||
|
set_tests_properties(pybind_math PROPERTIES
|
||||||
|
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>")
|
||||||
|
endif()
|
||||||
30
src/Python/core_bindings.cpp
Normal file
30
src/Python/core_bindings.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
|
#include "Core/Object.h"
|
||||||
|
#include "Core/Timer.h"
|
||||||
|
#include "Core/Options.h"
|
||||||
|
#include "Core/Uuid.h"
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
void init_core(py::module_ &m) {
|
||||||
|
py::class_<Object, std::shared_ptr<Object>>(m, "Object")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("DeepCopy", &Object::DeepCopy);
|
||||||
|
|
||||||
|
py::class_<Timer>(m, "Timer")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("Start", &Timer::Start)
|
||||||
|
.def("StopWatch", &Timer::StopWatch);
|
||||||
|
|
||||||
|
py::class_<Options>(m, "Options")
|
||||||
|
.def(py::init<const char*>(), py::arg("str") = "Program options")
|
||||||
|
.def("parse_config_file", py::overload_cast<const char*>(&Options::parse_config_file))
|
||||||
|
.def("save_config_file", &Options::save_config_file)
|
||||||
|
.def("count", &Options::count);
|
||||||
|
|
||||||
|
py::class_<TypeRegister>(m, "TypeRegister")
|
||||||
|
.def_static("Controller", &TypeRegister::Controller, py::return_value_policy::reference_internal);
|
||||||
|
}
|
||||||
152
src/Python/math_bindings.cpp
Normal file
152
src/Python/math_bindings.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/eigen.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
|
#include "Math/Dense.h"
|
||||||
|
#include "Math/Transform.h"
|
||||||
|
#include "Math/Geometry.h"
|
||||||
|
#include "Math/ContainerBox.h"
|
||||||
|
#include "Math/StructuredData.h"
|
||||||
|
#include "Math/StructuredGrid.h"
|
||||||
|
#include "Math/Structured2DGrid.h"
|
||||||
|
#include "Math/Structured4DGrid.h"
|
||||||
|
#include "Math/TriangleMesh.h"
|
||||||
|
#include "Math/VoxRaytracer.h"
|
||||||
|
#include "Math/Accumulator.h"
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
void init_math(py::module_ &m) {
|
||||||
|
|
||||||
|
// Math/Transform.h
|
||||||
|
py::class_<AffineTransform>(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<const Matrix3f &>(&AffineTransform::Rotate))
|
||||||
|
.def("Rotate", py::overload_cast<const Vector3f>(&AffineTransform::Rotate))
|
||||||
|
.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)
|
||||||
|
.def("GetOrigin", &ContainerBox::GetOrigin)
|
||||||
|
.def("SetSize", &ContainerBox::SetSize)
|
||||||
|
.def("GetSize", &ContainerBox::GetSize)
|
||||||
|
.def("GetWorldMatrix", &ContainerBox::GetWorldMatrix)
|
||||||
|
.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)
|
||||||
|
.value("XZY", StructuredData::XZY)
|
||||||
|
.value("YXZ", StructuredData::YXZ)
|
||||||
|
.value("YZX", StructuredData::YZX)
|
||||||
|
.value("ZXY", StructuredData::ZXY)
|
||||||
|
.value("ZYX", StructuredData::ZYX)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
py::class_<StructuredData>(m, "StructuredData")
|
||||||
|
.def(py::init<const Vector3i &>())
|
||||||
|
.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);
|
||||||
|
|
||||||
|
// Math/StructuredGrid.h
|
||||||
|
py::class_<StructuredGrid, ContainerBox, StructuredData>(m, "StructuredGrid")
|
||||||
|
.def(py::init<const Vector3i &>())
|
||||||
|
.def("SetSpacing", &StructuredGrid::SetSpacing)
|
||||||
|
.def("GetSpacing", &StructuredGrid::GetSpacing)
|
||||||
|
.def("IsInsideBounds", &StructuredGrid::IsInsideBounds)
|
||||||
|
.def("Find", [](StructuredGrid &self, Vector3f pt) {
|
||||||
|
return self.Find(HPoint3f(pt));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Math/Structured2DGrid.h
|
||||||
|
py::class_<Structured2DGrid>(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);
|
||||||
|
|
||||||
|
// Math/Structured4DGrid.h
|
||||||
|
py::class_<Structured4DGrid>(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);
|
||||||
|
|
||||||
|
// Math/TriangleMesh.h
|
||||||
|
py::class_<TriangleMesh>(m, "TriangleMesh")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("AddPoint", &TriangleMesh::AddPoint)
|
||||||
|
.def("AddTriangle", py::overload_cast<const Vector3i &>(&TriangleMesh::AddTriangle))
|
||||||
|
.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)
|
||||||
|
.def_readwrite("L", &VoxRaytracer::RayData::Element::L);
|
||||||
|
|
||||||
|
py::class_<VoxRaytracer::RayData>(m, "VoxRaytracerRayData")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("AppendRay", &VoxRaytracer::RayData::AppendRay)
|
||||||
|
.def("Count", &VoxRaytracer::RayData::Count)
|
||||||
|
.def("TotalLength", &VoxRaytracer::RayData::TotalLength)
|
||||||
|
.def("SetCount", &VoxRaytracer::RayData::SetCount)
|
||||||
|
.def("SetTotalLength", &VoxRaytracer::RayData::SetTotalLength);
|
||||||
|
|
||||||
|
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_));
|
||||||
|
|
||||||
|
}
|
||||||
18
src/Python/module.cpp
Normal file
18
src/Python/module.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
void init_core(py::module_ &m);
|
||||||
|
void init_math(py::module_ &m);
|
||||||
|
|
||||||
|
PYBIND11_MODULE(uLib_python, m) {
|
||||||
|
m.doc() = "Python bindings for uLib Core and Math libraries";
|
||||||
|
|
||||||
|
// Core submodule
|
||||||
|
py::module_ core = m.def_submodule("Core", "Core library bindings");
|
||||||
|
init_core(core);
|
||||||
|
|
||||||
|
// Math submodule
|
||||||
|
py::module_ math = m.def_submodule("Math", "Math library bindings");
|
||||||
|
init_math(math);
|
||||||
|
}
|
||||||
33
src/Python/testing/core_pybind_test.py
Normal file
33
src/Python/testing/core_pybind_test.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
|
||||||
|
import uLib_python
|
||||||
|
|
||||||
|
class TestCoreOptions(unittest.TestCase):
|
||||||
|
def test_options(self):
|
||||||
|
opt = uLib_python.Core.Options("Test Options")
|
||||||
|
|
||||||
|
# Test basic config file parsing
|
||||||
|
with open("test_configuration.ini", "w") as f:
|
||||||
|
f.write("[Section]\n")
|
||||||
|
|
||||||
|
opt.parse_config_file("test_configuration.ini")
|
||||||
|
os.remove("test_configuration.ini")
|
||||||
|
|
||||||
|
class TestCoreObject(unittest.TestCase):
|
||||||
|
def test_object(self):
|
||||||
|
obj = uLib_python.Core.Object()
|
||||||
|
self.assertIsNotNone(obj)
|
||||||
|
|
||||||
|
class TestCoreTimer(unittest.TestCase):
|
||||||
|
def test_timer(self):
|
||||||
|
timer = uLib_python.Core.Timer()
|
||||||
|
timer.Start()
|
||||||
|
time.sleep(0.1)
|
||||||
|
val = timer.StopWatch()
|
||||||
|
self.assertGreater(val, 0.09)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
62
src/Python/testing/math_pybind_test.py
Normal file
62
src/Python/testing/math_pybind_test.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
import uLib_python
|
||||||
|
|
||||||
|
def vector4f0(v, target):
|
||||||
|
diff = np.array(v) - np.array(target)
|
||||||
|
diff[3] = 0 # ignoring w
|
||||||
|
return np.all(np.abs(diff) < 0.001)
|
||||||
|
|
||||||
|
class TestMathGeometry(unittest.TestCase):
|
||||||
|
def test_geometry(self):
|
||||||
|
Geo = uLib_python.Math.Geometry()
|
||||||
|
|
||||||
|
Geo.SetPosition([1, 1, 1])
|
||||||
|
|
||||||
|
pt = Geo.GetLocalPoint([2, 3, 2, 1])
|
||||||
|
wp = Geo.GetWorldPoint(pt)
|
||||||
|
|
||||||
|
self.assertTrue(vector4f0(wp, [2, 3, 2, 1]))
|
||||||
|
|
||||||
|
Geo.Scale([2, 2, 2])
|
||||||
|
wp = Geo.GetWorldPoint([1, 1, 1, 1])
|
||||||
|
self.assertTrue(vector4f0(wp, [3, 3, 3, 1]))
|
||||||
|
|
||||||
|
class TestMathContainerBox(unittest.TestCase):
|
||||||
|
def test_container_box_local(self):
|
||||||
|
Cnt = uLib_python.Math.ContainerBox()
|
||||||
|
Cnt.SetOrigin([-1, -1, -1])
|
||||||
|
Cnt.SetSize([2, 2, 2])
|
||||||
|
|
||||||
|
size = Cnt.GetSize()
|
||||||
|
self.assertTrue(np.allclose(size, [2, 2, 2]))
|
||||||
|
|
||||||
|
def test_container_box_global(self):
|
||||||
|
Box = uLib_python.Math.ContainerBox()
|
||||||
|
Box.SetPosition([1, 1, 1])
|
||||||
|
Box.SetSize([2, 2, 2])
|
||||||
|
|
||||||
|
pt = Box.GetLocalPoint([2, 3, 2, 1])
|
||||||
|
wp = Box.GetWorldPoint(pt)
|
||||||
|
self.assertTrue(vector4f0(wp, [2, 3, 2, 1]))
|
||||||
|
|
||||||
|
class TestMathStructuredGrid(unittest.TestCase):
|
||||||
|
def test_structured_grid(self):
|
||||||
|
grid = uLib_python.Math.StructuredGrid([10, 10, 10])
|
||||||
|
grid.SetSpacing([1, 1, 1])
|
||||||
|
|
||||||
|
spacing = grid.GetSpacing()
|
||||||
|
self.assertTrue(np.allclose(spacing, [1, 1, 1]))
|
||||||
|
|
||||||
|
class TestMathAccumulator(unittest.TestCase):
|
||||||
|
def test_accumulator_mean(self):
|
||||||
|
acc = uLib_python.Math.Accumulator_Mean_f()
|
||||||
|
acc(10.0)
|
||||||
|
acc(20.0)
|
||||||
|
self.assertAlmostEqual(acc(), 15.0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
46
src/Python/testing/pybind_test.py
Normal file
46
src/Python/testing/pybind_test.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
import uLib_python
|
||||||
|
|
||||||
|
def test_core():
|
||||||
|
print("Testing Core module...")
|
||||||
|
obj = uLib_python.Core.Object()
|
||||||
|
print("Core Object created:", obj)
|
||||||
|
|
||||||
|
timer = uLib_python.Core.Timer()
|
||||||
|
timer.Start()
|
||||||
|
print("Core Timer started")
|
||||||
|
|
||||||
|
options = uLib_python.Core.Options("Test Options")
|
||||||
|
print("Core Options created:", options)
|
||||||
|
|
||||||
|
def test_math():
|
||||||
|
print("Testing Math module...")
|
||||||
|
|
||||||
|
# Test AffineTransform
|
||||||
|
transform = uLib_python.Math.AffineTransform()
|
||||||
|
print("AffineTransform created")
|
||||||
|
|
||||||
|
# Test Geometry
|
||||||
|
geom = uLib_python.Math.Geometry()
|
||||||
|
print("Geometry created")
|
||||||
|
|
||||||
|
# Test StructuredData
|
||||||
|
data = uLib_python.Math.StructuredData([10, 10, 10])
|
||||||
|
print("StructuredData created with dims:", data.GetDims())
|
||||||
|
|
||||||
|
# Test Structured2DGrid
|
||||||
|
grid2d = uLib_python.Math.Structured2DGrid()
|
||||||
|
grid2d.SetDims([100, 100])
|
||||||
|
print("Structured2DGrid created with dims:", grid2d.GetDims())
|
||||||
|
|
||||||
|
# Test TriangleMesh
|
||||||
|
mesh = uLib_python.Math.TriangleMesh()
|
||||||
|
print("TriangleMesh created")
|
||||||
|
|
||||||
|
print("All tests passed successfully!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_core()
|
||||||
|
test_math()
|
||||||
Reference in New Issue
Block a user