-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
324 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/bin/bash | ||
set -eux | ||
|
||
source venv/bin/activate | ||
rm -rf dist | ||
rm -rf python/target | ||
|
||
mkdir -p python/target | ||
cd python/target | ||
cmake -DCMAKE_BUILD_TYPE=Release .. | ||
make -j8 | ||
cd ../.. | ||
|
||
python python/setup.py bdist_wheel | ||
deactivate | ||
|
||
cd python/target | ||
python -m venv venv_test | ||
source venv_test/bin/activate | ||
pip install ../../dist/*.whl | ||
cd .. | ||
python example.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
cmake_minimum_required(VERSION 3.12) | ||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../VERSION.txt" VERSION_CONTENTS) | ||
project(IsoOctree VERSION ${VERSION_CONTENTS}) | ||
|
||
option(IsoOctree_PYTHON "Build python bindings" OFF) | ||
set(IsoOctree_PYTHON ON) | ||
|
||
add_subdirectory(pybind11) | ||
add_subdirectory(.. ${CMAKE_BINARY_DIR}/IsoOctreeNative_build) | ||
|
||
pybind11_add_module(IsoOctree bindings.cpp) | ||
target_include_directories(IsoOctree PRIVATE | ||
${CMAKE_CURRENT_SOURCE_DIR}/../include) | ||
target_link_libraries(IsoOctree PRIVATE IsoOctreeNative) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
#include <memory> | ||
#include <pybind11/pybind11.h> | ||
#include <pybind11/numpy.h> | ||
#include <pybind11/functional.h> | ||
#include <pybind11/stl.h> | ||
#include "IsoOctree/IsoOctree.hpp" | ||
#include <iostream> | ||
|
||
#define DEF_READONLY(klass, name, doc) def_readonly(#name, &klass::name, doc) | ||
#define DEF_COPY_READONLY(klass, name, doc) def_property_readonly(\ | ||
#name,\ | ||
[](const klass &self) { return self.name; },\ | ||
py::return_value_policy::copy, doc) | ||
|
||
|
||
namespace py = pybind11; | ||
|
||
namespace { | ||
using Real = double; | ||
using Octree = isoOctree::Octree<Real>; | ||
using Voxel = Octree::Voxel; | ||
using Point3D = isoOctree::Point3D<Real>; | ||
using CornerValues = Octree::CornerValues; | ||
using RootInfo = Octree::RootInfo; | ||
using MeshInfo = isoOctree::MeshInfo<Real>; | ||
|
||
using ShouldExpandCallback = std::function<bool(const Voxel &, const CornerValues &corners)>; | ||
using GetValueCallback = std::function<float(const Point3D &)>; | ||
|
||
class CallbackTraverser : public Octree::Traverser { | ||
private: | ||
RootInfo rootInfo; | ||
ShouldExpandCallback shouldExpandCallback; | ||
GetValueCallback getValueCallback; | ||
|
||
public: | ||
CallbackTraverser( | ||
const RootInfo &rootInfo, | ||
const ShouldExpandCallback &shouldExpandCallback, | ||
const GetValueCallback &getValueCallback) | ||
: | ||
rootInfo(rootInfo), | ||
shouldExpandCallback(shouldExpandCallback), | ||
getValueCallback(getValueCallback) | ||
{} | ||
|
||
const RootInfo &root() final { return rootInfo; } | ||
bool shouldExpand(const Voxel &voxel, const CornerValues &corners) final { | ||
return shouldExpandCallback(voxel, corners); | ||
} | ||
|
||
float isoValue(const Point3D &point) final { | ||
return getValueCallback(point); | ||
} | ||
}; | ||
} | ||
|
||
PYBIND11_MODULE(IsoOctree, m) { | ||
m.doc() = "IsoOctree Python wrapper"; | ||
|
||
py::class_<Voxel>(m, "Voxel") | ||
|
||
.DEF_READONLY(Voxel, index, | ||
"Level-specific octree node index triplet (i,j,k), each in the range [0, 2^depth-1]") | ||
|
||
.DEF_READONLY(Voxel, depth, | ||
"Octree level: 0 is root, 1 is the first level, etc.") | ||
|
||
.DEF_COPY_READONLY(Voxel, minCorner, | ||
"Voxel minimum coordinate corner in world coordinates") | ||
|
||
.DEF_READONLY(Voxel, width, | ||
"Voxel cube width"); | ||
|
||
py::class_<MeshInfo, std::unique_ptr<MeshInfo>>(m, "MeshInfo") | ||
|
||
.def_property_readonly("vertices", | ||
[](MeshInfo &self) { | ||
return py::array_t<Real>( | ||
std::vector<ptrdiff_t> { static_cast<long>(self.vertices.size()), 3 }, | ||
reinterpret_cast<const Real*>(self.vertices.data()) | ||
); | ||
}, | ||
"Vertices numpy array of shape (N, 3)") | ||
|
||
.def_property_readonly("triangles", | ||
[](MeshInfo &self) { | ||
return py::array_t<isoOctree::MeshIndex>( | ||
std::vector<ptrdiff_t> { static_cast<long>(self.triangles.size()), 3 }, | ||
reinterpret_cast<const isoOctree::MeshIndex*>(self.triangles.data()) | ||
); | ||
}, | ||
"Faces (all triangles) as an index array of shape (M, 3), each element\n" | ||
"is an index to the rows in vertices, and thus in the range (0, N-1)"); | ||
|
||
m.def("buildMesh", | ||
[]( | ||
GetValueCallback isoFunction, | ||
ShouldExpandCallback expandFunction, | ||
Point3D center, | ||
Real width, | ||
int maxDepth) | ||
{ | ||
Octree::RootInfo rootInfo; | ||
rootInfo.center = center; | ||
rootInfo.width = width; | ||
rootInfo.maxDepth = maxDepth; | ||
CallbackTraverser traverser(rootInfo, expandFunction, isoFunction); | ||
std::unique_ptr<isoOctree::MeshInfo<Real>> output = std::make_unique<isoOctree::MeshInfo<Real>>(); | ||
isoOctree::buildMesh(traverser, *output); | ||
return std::move(output); | ||
}, | ||
py::arg("isoFunction"), | ||
py::arg("expandFunction"), | ||
py::kw_only(), | ||
py::arg("center") = Point3D { 0, 0, 0 }, | ||
py::arg("width") = 1.0, | ||
py::arg("maxDepth") = 10, | ||
"Mesh an implicit function using the Iso-Octree algorithm \n" | ||
"\n" | ||
"Applied to a cubical region [cx-w/2, cx+w/2] x [cy-w/2, cy+w/2] x [cz-w/2, cz+w/2]\n" | ||
"called the (octree) root voxel.\n" | ||
"\n" | ||
"Arguments:\n" | ||
"\t isoFunction: Function f that defines the surface (f < 0 is inside)\n" | ||
"\t expandFunction: Function that decides whether to expand a Voxel\n" | ||
"\t center: Center (cx, cy, cz) of the root voxel\n" | ||
"\t width: Width of the root voxel w\n" | ||
"\t maxDepth: Maximum octree depth d. The smallest possible voxel dimension is w/2^d\n" | ||
"\n" | ||
"Returns: a MeshInfo object"); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import IsoOctree | ||
|
||
def getValue(p): | ||
x, y, z = p | ||
return x*x*x*x - 5*x*x + y*y*y*y - 5*y*y + z*z*z*z - 5*z*z + 11.8 | ||
|
||
def shouldExpand(node, corners): | ||
BASE_DEPTH = 3 | ||
if node.depth < BASE_DEPTH: return True | ||
|
||
nNonNegative = 0 | ||
nNegative = 0 | ||
for v in corners: | ||
if v < 0.0: nNegative += 1 | ||
else: nNonNegative += 1 | ||
|
||
if nNonNegative == 0 or nNegative == 0: return False | ||
|
||
# Note: this logic is rather flawed for adaptive refinement, | ||
# but results to leaf nodes at different level, which is a good | ||
# test to the IsoOctree algorithm | ||
return nNonNegative != nNegative | ||
|
||
def writeMeshAsObj(mesh, filename): | ||
with open(filename, 'wt') as f: | ||
for v in mesh.vertices: | ||
f.write('v %f %f %f\n' % (v[0], v[1], v[2])) | ||
for t in mesh.triangles: | ||
f.write('f %d %d %d\n' % (t[0]+1, t[1]+1, t[2]+1)) | ||
|
||
if __name__ == '__main__': | ||
mesh = IsoOctree.buildMesh( | ||
getValue, | ||
shouldExpand, | ||
center = [0.1, 0.2, 0.3], | ||
width = 6.0, | ||
maxDepth = 6) | ||
|
||
writeMeshAsObj(mesh, 'example.obj') |
Oops, something went wrong.