loop-cgal 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loop_cgal-0.0.1/.github/workflows/conda.yml +54 -0
- loop_cgal-0.0.1/.github/workflows/pypi.yml +40 -0
- loop_cgal-0.0.1/.github/workflows/release-please.yml +42 -0
- loop_cgal-0.0.1/.gitignore +42 -0
- loop_cgal-0.0.1/CMakeLists.txt +40 -0
- loop_cgal-0.0.1/PKG-INFO +56 -0
- loop_cgal-0.0.1/README.md +38 -0
- loop_cgal-0.0.1/examples/clip_test.py +35 -0
- loop_cgal-0.0.1/examples/test.py +15 -0
- loop_cgal-0.0.1/examples/test2.py +20 -0
- loop_cgal-0.0.1/loop_cgal/__init__.py +35 -0
- loop_cgal-0.0.1/loop_cgal/bindings.cpp +20 -0
- loop_cgal-0.0.1/pyproject.toml +85 -0
- loop_cgal-0.0.1/src/clip.cpp +119 -0
- loop_cgal-0.0.1/src/clip.h +15 -0
- loop_cgal-0.0.1/src/numpymesh.h +9 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: "🐍 Build and upload Conda packages"
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
jobs:
|
|
5
|
+
conda-deploy:
|
|
6
|
+
name: Building conda package for python ${{ matrix.os }})
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}}
|
|
12
|
+
steps:
|
|
13
|
+
- uses: conda-incubator/setup-miniconda@v3
|
|
14
|
+
with:
|
|
15
|
+
auto-update-conda: true
|
|
16
|
+
python-version: ${{ matrix.python-version }}
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: update submodules
|
|
19
|
+
run: |
|
|
20
|
+
git submodule update --init --recursive
|
|
21
|
+
- name: Conda build
|
|
22
|
+
env:
|
|
23
|
+
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
|
|
24
|
+
shell: bash -l {0}
|
|
25
|
+
run: |
|
|
26
|
+
conda install -c conda-forge "conda-build<25" scikit-build numpy cython anaconda-client conda-libmamba-solver -y
|
|
27
|
+
conda build -c conda-forge -c loop3d --output-folder conda conda --python ${{matrix.python-version}}
|
|
28
|
+
conda convert -p all conda/linux-64/*.tar.bz2 -f -o conda
|
|
29
|
+
- name: upload artifacts
|
|
30
|
+
uses: actions/upload-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: conda-build-${{ matrix.python-version }}
|
|
33
|
+
path: conda
|
|
34
|
+
upload_to_conda:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
needs: conda-deploy
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: conda-build-${{ matrix.python-version }}
|
|
45
|
+
path: conda
|
|
46
|
+
- uses: conda-incubator/setup-miniconda@v3
|
|
47
|
+
- name: upload all files to conda-forge
|
|
48
|
+
shell: bash -l {0}
|
|
49
|
+
env:
|
|
50
|
+
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
|
|
51
|
+
run: |
|
|
52
|
+
conda install -c anaconda anaconda-client -y
|
|
53
|
+
anaconda upload --label main conda/*/*.tar.bz2
|
|
54
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Deploy to PYPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
jobs:
|
|
6
|
+
|
|
7
|
+
sdist:
|
|
8
|
+
name: Build sdist
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
- name: Install dependencies
|
|
14
|
+
run: |
|
|
15
|
+
python -m pip install --upgrade pip
|
|
16
|
+
pip install build
|
|
17
|
+
- name: Build sdist
|
|
18
|
+
run: python -m build --sdist
|
|
19
|
+
- uses: actions/upload-artifact@v4
|
|
20
|
+
with:
|
|
21
|
+
name: dist
|
|
22
|
+
path: ./dist/*.tar.gz
|
|
23
|
+
|
|
24
|
+
publish:
|
|
25
|
+
name: Publish wheels to pypi
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
permissions:
|
|
28
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
29
|
+
id-token: write
|
|
30
|
+
needs: sdist
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/download-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist
|
|
36
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
37
|
+
with:
|
|
38
|
+
skip-existing: true
|
|
39
|
+
verbose: true
|
|
40
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
branches:
|
|
4
|
+
- master
|
|
5
|
+
env:
|
|
6
|
+
PACKAGE_NAME: loop_cgal
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
issues: write
|
|
11
|
+
|
|
12
|
+
name: release-please
|
|
13
|
+
jobs:
|
|
14
|
+
release-please:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: GoogleCloudPlatform/release-please-action@v4
|
|
18
|
+
id: release
|
|
19
|
+
with:
|
|
20
|
+
path: loop_cgal
|
|
21
|
+
|
|
22
|
+
outputs:
|
|
23
|
+
release_created: ${{ steps.release.outputs.releases_created }}
|
|
24
|
+
package:
|
|
25
|
+
needs: release-please
|
|
26
|
+
if: ${{ needs.release-please.outputs.release_created == 'true'}}
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- name: Trigger build for pypi and upload
|
|
30
|
+
run: |
|
|
31
|
+
curl -X POST \
|
|
32
|
+
-H "Authorization: token ${{ secrets.GH_PAT }}" \
|
|
33
|
+
-H "Accept: application/vnd.github.v3+json" \
|
|
34
|
+
https://api.github.com/repos/Loop3d/${{env.PACKAGE_NAME}}/actions/workflows/pypi.yml/dispatches \
|
|
35
|
+
-d '{"ref":"master"}'
|
|
36
|
+
- name: Trigger build for conda and upload
|
|
37
|
+
run: |
|
|
38
|
+
curl -X POST \
|
|
39
|
+
-H "Authorization: token ${{ secrets.GH_PAT }}" \
|
|
40
|
+
-H "Accept: application/vnd.github.v3+json" \
|
|
41
|
+
https://api.github.com/repos/Loop3d/${{env.PACKAGE_NAME}}/actions/workflows/conda.yml/dispatches \
|
|
42
|
+
-d '{"ref":"master"}'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# C++ artifacts
|
|
2
|
+
*.o
|
|
3
|
+
*.so
|
|
4
|
+
*.a
|
|
5
|
+
*.lib
|
|
6
|
+
*.dll
|
|
7
|
+
*.dylib
|
|
8
|
+
*.exe
|
|
9
|
+
*.out
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*.egg
|
|
15
|
+
*.egg-info/
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
_skbuild/
|
|
19
|
+
*.whl
|
|
20
|
+
|
|
21
|
+
# IDE and editors
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
*~
|
|
26
|
+
|
|
27
|
+
# CMake
|
|
28
|
+
CMakeFiles/
|
|
29
|
+
CMakeCache.txt
|
|
30
|
+
cmake_install.cmake
|
|
31
|
+
Makefile
|
|
32
|
+
*.cmake
|
|
33
|
+
!CMakeLists.txt
|
|
34
|
+
|
|
35
|
+
# Project specific
|
|
36
|
+
_deps/
|
|
37
|
+
external/
|
|
38
|
+
third_party/
|
|
39
|
+
|
|
40
|
+
# OS specific
|
|
41
|
+
.DS_Store
|
|
42
|
+
Thumbs.db
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Minimum CMake version
|
|
2
|
+
cmake_minimum_required(VERSION 3.15)
|
|
3
|
+
|
|
4
|
+
# Project name and version
|
|
5
|
+
project(loop-cgal VERSION 1.0 LANGUAGES CXX)
|
|
6
|
+
|
|
7
|
+
# Set C++ standard
|
|
8
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
9
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
10
|
+
|
|
11
|
+
# Find CGAL package
|
|
12
|
+
find_package(CGAL REQUIRED)
|
|
13
|
+
|
|
14
|
+
# Add the include directory for your project
|
|
15
|
+
include_directories(${CMAKE_SOURCE_DIR}/src)
|
|
16
|
+
|
|
17
|
+
# Add the executable for standalone testing
|
|
18
|
+
# add_executable(loop-cgal-exe
|
|
19
|
+
# src/marching_cubes.cpp
|
|
20
|
+
# )
|
|
21
|
+
|
|
22
|
+
# Link CGAL to the executable
|
|
23
|
+
# target_link_libraries(loop-cgal-exe PRIVATE CGAL::CGAL)
|
|
24
|
+
|
|
25
|
+
# Find pybind11
|
|
26
|
+
find_package(pybind11 REQUIRED)
|
|
27
|
+
|
|
28
|
+
# Add the Python module
|
|
29
|
+
add_library(loop_cgal MODULE
|
|
30
|
+
loop_cgal/bindings.cpp
|
|
31
|
+
src/clip.cpp
|
|
32
|
+
|
|
33
|
+
)
|
|
34
|
+
target_link_libraries(loop_cgal PRIVATE pybind11::module CGAL::CGAL)
|
|
35
|
+
target_include_directories(loop_cgal PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
|
36
|
+
set_target_properties(loop_cgal PROPERTIES PREFIX "" SUFFIX ".so")
|
|
37
|
+
# Install the Python module to the correct location
|
|
38
|
+
install(TARGETS loop_cgal
|
|
39
|
+
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/loop_cgal
|
|
40
|
+
)
|
loop_cgal-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: loop_cgal
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: python bindings for cgal for implicit model meshing
|
|
5
|
+
Author-Email: Lachlan Grose <lachlan.grose@monash.edu>
|
|
6
|
+
Classifier: Development Status :: 4 - Beta
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Requires-Dist: pyvista
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# Loop-cgal
|
|
20
|
+
|
|
21
|
+
Loop-cgal is a Python package for mesh processing operations using the CGAL (Computational Geometry Algorithms Library). It is designed for efficient geometric computations using pyvista objects.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- Python bindings for CGAL using `pybind11`.
|
|
26
|
+
- Current features:
|
|
27
|
+
- clipping of 3D triangular surfaces
|
|
28
|
+
- Future features:
|
|
29
|
+
- Marching cubes algorithm for isosurface extraction
|
|
30
|
+
- Boolean operations on marching cube meshes.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### Prerequisites
|
|
35
|
+
|
|
36
|
+
- C++17 or later
|
|
37
|
+
- Python 3.11 or later
|
|
38
|
+
- CGAL library
|
|
39
|
+
- Boost library
|
|
40
|
+
- CMake 3.15 or later
|
|
41
|
+
- pybind11
|
|
42
|
+
- scikit-build
|
|
43
|
+
- pyvista
|
|
44
|
+
|
|
45
|
+
### Build and Install
|
|
46
|
+
|
|
47
|
+
1. Clone the repository:
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/Loop3D/loop-cgal.git
|
|
50
|
+
cd loop-cgal
|
|
51
|
+
pip install .
|
|
52
|
+
```
|
|
53
|
+
2. Alternatively, you can install it directly from PyPI:
|
|
54
|
+
```bash
|
|
55
|
+
pip install loop-cgal
|
|
56
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Loop-cgal
|
|
2
|
+
|
|
3
|
+
Loop-cgal is a Python package for mesh processing operations using the CGAL (Computational Geometry Algorithms Library). It is designed for efficient geometric computations using pyvista objects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Python bindings for CGAL using `pybind11`.
|
|
8
|
+
- Current features:
|
|
9
|
+
- clipping of 3D triangular surfaces
|
|
10
|
+
- Future features:
|
|
11
|
+
- Marching cubes algorithm for isosurface extraction
|
|
12
|
+
- Boolean operations on marching cube meshes.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Prerequisites
|
|
17
|
+
|
|
18
|
+
- C++17 or later
|
|
19
|
+
- Python 3.11 or later
|
|
20
|
+
- CGAL library
|
|
21
|
+
- Boost library
|
|
22
|
+
- CMake 3.15 or later
|
|
23
|
+
- pybind11
|
|
24
|
+
- scikit-build
|
|
25
|
+
- pyvista
|
|
26
|
+
|
|
27
|
+
### Build and Install
|
|
28
|
+
|
|
29
|
+
1. Clone the repository:
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/Loop3D/loop-cgal.git
|
|
32
|
+
cd loop-cgal
|
|
33
|
+
pip install .
|
|
34
|
+
```
|
|
35
|
+
2. Alternatively, you can install it directly from PyPI:
|
|
36
|
+
```bash
|
|
37
|
+
pip install loop-cgal
|
|
38
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import loop_cgal
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pyvista as pv
|
|
4
|
+
from LoopStructural.datatypes import BoundingBox
|
|
5
|
+
def test():
|
|
6
|
+
bb = BoundingBox(np.zeros(3), np.ones(3))
|
|
7
|
+
grid = bb.structured_grid().vtk()
|
|
8
|
+
print(grid)
|
|
9
|
+
grid["scalars"] = grid.points[:,0]
|
|
10
|
+
surface = grid.contour([.5])
|
|
11
|
+
surface_1_tri = surface.faces.reshape(-1, 4)[:, 1:].copy()
|
|
12
|
+
surface_1_verts = surface.points.copy()
|
|
13
|
+
grid["scalars"] = grid.points[:,1]
|
|
14
|
+
surface_2 = grid.contour([.5])
|
|
15
|
+
surface_2_tri = surface_2.faces.reshape(-1, 4)[:, 1:].copy()
|
|
16
|
+
surface_2_verts = surface_2.points.copy()
|
|
17
|
+
|
|
18
|
+
surface_clipped = loop_cgal.clip_pyvista_polydata(
|
|
19
|
+
surface, surface_2
|
|
20
|
+
) # surface_1_tri, surface_1_verts, surface_2_tri, surface_2_verts
|
|
21
|
+
# )
|
|
22
|
+
print(surface_clipped)
|
|
23
|
+
# print(mesh.vertices.shape)
|
|
24
|
+
# print(surface_1_verts.shape)
|
|
25
|
+
# print(mesh.triangles)
|
|
26
|
+
# print(mesh.triangles, mesh.vertices)
|
|
27
|
+
# surface_clipped = pv.PolyData.from_regular_faces(mesh.vertices, mesh.triangles)
|
|
28
|
+
p = pv.Plotter()
|
|
29
|
+
# p.add_mesh(surface)
|
|
30
|
+
p.add_mesh(surface_2, color="blue", show_edges=True)
|
|
31
|
+
p.add_mesh(surface_clipped, color="red", show_edges=True)
|
|
32
|
+
# p.add_points(mesh.vertices, render_points_as_spheres=True, point_size=5)
|
|
33
|
+
p.show()
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
test()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import loop_cgal
|
|
2
|
+
import numpy as np
|
|
3
|
+
def test():
|
|
4
|
+
|
|
5
|
+
X,Y,Z = np.meshgrid(np.linspace(0, 1, 10),
|
|
6
|
+
np.linspace(0, 1, 10),
|
|
7
|
+
np.linspace(0, 1, 10))
|
|
8
|
+
|
|
9
|
+
step_vector = np.array([.1, .1, .1])
|
|
10
|
+
nsteps = np.ones(3)*10
|
|
11
|
+
points, tri = loop_cgal.generate_mesh_from_numpy(Z,np.zeros(3),step_vector,nsteps,.6)
|
|
12
|
+
print("tri", tri)
|
|
13
|
+
print("points", points)
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
test()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import loop_cgal
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test():
|
|
6
|
+
|
|
7
|
+
X, Y, Z = np.meshgrid(
|
|
8
|
+
np.linspace(0, 1, 10), np.linspace(0, 1, 10), np.linspace(0, 1, 10)
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
step_vector = np.array([0.1, 0.1, 0.1])
|
|
12
|
+
nsteps = np.ones(3) * 10
|
|
13
|
+
result = loop_cgal.compute_optimized_intersection(
|
|
14
|
+
Z,X,0.5,.5,0,0,0,0.1,0.1,0.1
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
test()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .loop_cgal import clip_surface, NumpyMesh
|
|
2
|
+
import pyvista as pv
|
|
3
|
+
import numpy as np
|
|
4
|
+
def clip_pyvista_polydata(
|
|
5
|
+
surface_1: pv.PolyData,
|
|
6
|
+
surface_2: pv.PolyData,
|
|
7
|
+
) -> pv.PolyData:
|
|
8
|
+
"""
|
|
9
|
+
Clip two pyvista PolyData objects using the CGAL library.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
surface_1 : pyvista.PolyData
|
|
14
|
+
The first surface to be clipped.
|
|
15
|
+
surface_2 : pyvista.PolyData
|
|
16
|
+
The second surface to be used for clipping.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
pyvista.PolyData
|
|
21
|
+
The resulting clipped surface.
|
|
22
|
+
"""
|
|
23
|
+
surface_1 = surface_1.triangulate()
|
|
24
|
+
surface_2 = surface_2.triangulate()
|
|
25
|
+
tm = NumpyMesh()
|
|
26
|
+
tm.vertices = np.array(surface_1.points).copy()
|
|
27
|
+
tm.triangles = surface_1.faces.reshape(-1, 4)[:, 1:].copy()
|
|
28
|
+
clipper = NumpyMesh()
|
|
29
|
+
clipper.vertices = np.array(surface_2.points).copy()
|
|
30
|
+
clipper.triangles = surface_2.faces.reshape(-1, 4)[:, 1:].copy()
|
|
31
|
+
mesh = clip_surface(
|
|
32
|
+
tm,
|
|
33
|
+
clipper,
|
|
34
|
+
)
|
|
35
|
+
return pv.PolyData.from_regular_faces(mesh.vertices, mesh.triangles)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include <pybind11/pybind11.h>
|
|
2
|
+
#include <pybind11/stl.h>
|
|
3
|
+
|
|
4
|
+
#include "clip.h" // Include the API implementation
|
|
5
|
+
|
|
6
|
+
namespace py = pybind11;
|
|
7
|
+
|
|
8
|
+
PYBIND11_MODULE(loop_cgal, m)
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
m.def("clip_surface", &clip_surface,
|
|
12
|
+
py::arg("tm"),
|
|
13
|
+
py::arg("clipper"),
|
|
14
|
+
"Clip one surface with another.");
|
|
15
|
+
py::class_<NumpyMesh>(m, "NumpyMesh")
|
|
16
|
+
.def(py::init<>())
|
|
17
|
+
.def_readwrite("vertices", &NumpyMesh::vertices)
|
|
18
|
+
.def_readwrite("triangles", &NumpyMesh::triangles);
|
|
19
|
+
|
|
20
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"scikit-build-core>=0.3.3",
|
|
4
|
+
"pybind11>=2.10.0",
|
|
5
|
+
"oldest-supported-numpy",
|
|
6
|
+
]
|
|
7
|
+
build-backend = "scikit_build_core.build"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
authors = [{ name = "Lachlan Grose", email = "lachlan.grose@monash.edu" }]
|
|
12
|
+
name = "loop_cgal"
|
|
13
|
+
version = "0.0.1"
|
|
14
|
+
description = "python bindings for cgal for implicit model meshing"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.7"
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.7",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = ['pyvista']
|
|
30
|
+
|
|
31
|
+
[tool.scikit-build]
|
|
32
|
+
wheel.expand-macos-universal-tags = true
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
[tool.cibuildwheel.pyodide]
|
|
37
|
+
environment.CFLAGS = "-fexceptions"
|
|
38
|
+
environment.LDFLAGS = "-fexceptions"
|
|
39
|
+
build-frontend = { name = "build", args = ["--exports", "whole_archive"] }
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
src = ["src"]
|
|
43
|
+
|
|
44
|
+
[tool.ruff.lint]
|
|
45
|
+
extend-select = [
|
|
46
|
+
"B", # flake8-bugbear
|
|
47
|
+
"I", # isort
|
|
48
|
+
"ARG", # flake8-unused-arguments
|
|
49
|
+
"C4", # flake8-comprehensions
|
|
50
|
+
"EM", # flake8-errmsg
|
|
51
|
+
"ICN", # flake8-import-conventions
|
|
52
|
+
"G", # flake8-logging-format
|
|
53
|
+
"PGH", # pygrep-hooks
|
|
54
|
+
"PIE", # flake8-pie
|
|
55
|
+
"PL", # pylint
|
|
56
|
+
"PT", # flake8-pytest-style
|
|
57
|
+
"PTH", # flake8-use-pathlib
|
|
58
|
+
"RET", # flake8-return
|
|
59
|
+
"RUF", # Ruff-specific
|
|
60
|
+
"SIM", # flake8-simplify
|
|
61
|
+
"T20", # flake8-print
|
|
62
|
+
"UP", # pyupgrade
|
|
63
|
+
"YTT", # flake8-2020
|
|
64
|
+
"EXE", # flake8-executable
|
|
65
|
+
"NPY", # NumPy specific rules
|
|
66
|
+
"PD", # pandas-vet
|
|
67
|
+
]
|
|
68
|
+
ignore = [
|
|
69
|
+
"PLR09", # Too many X
|
|
70
|
+
"PLR2004", # Magic comparison
|
|
71
|
+
]
|
|
72
|
+
isort.required-imports = ["from __future__ import annotations"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"tests/**" = ["T20"]
|
|
76
|
+
|
|
77
|
+
[tool.cibuildwheel]
|
|
78
|
+
build = "cp39-* cp310-* cp311-* cp312-* cp313-*"
|
|
79
|
+
skip = "*-win32 *-manylinux_i686 *-musllinux_*"
|
|
80
|
+
|
|
81
|
+
[tool.cibuildwheel.linux]
|
|
82
|
+
before-all = "yum install python3-devel -y"
|
|
83
|
+
|
|
84
|
+
[tool.cibuildwheel.macos]
|
|
85
|
+
archs = ["x86_64", "arm64"]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#include "clip.h"
|
|
2
|
+
#include "numpymesh.h"
|
|
3
|
+
#include <CGAL/Simple_cartesian.h>
|
|
4
|
+
#include <CGAL/Polygon_mesh_processing/clip.h>
|
|
5
|
+
#include <CGAL/Surface_mesh.h>
|
|
6
|
+
|
|
7
|
+
NumpyMesh clip_surface(NumpyMesh tm, NumpyMesh clipper)
|
|
8
|
+
{
|
|
9
|
+
auto vertices1_buf = tm.vertices.unchecked<2>();
|
|
10
|
+
auto triangles1_buf = tm.triangles.unchecked<2>();
|
|
11
|
+
auto vertices2_buf = clipper.vertices.unchecked<2>();
|
|
12
|
+
auto triangles2_buf = clipper.triangles.unchecked<2>();
|
|
13
|
+
|
|
14
|
+
TriangleMesh _tm;
|
|
15
|
+
TriangleMesh _clipper;
|
|
16
|
+
std::vector<TriangleMesh::Vertex_index> vertex_indices;
|
|
17
|
+
// assemble cgal mesh objects from numpy/pybind11 arrays
|
|
18
|
+
for (ssize_t i = 0; i < vertices1_buf.shape(0); ++i)
|
|
19
|
+
{
|
|
20
|
+
vertex_indices.push_back(_tm.add_vertex(Point(vertices1_buf(i, 0), vertices1_buf(i, 1), vertices1_buf(i, 2))));
|
|
21
|
+
}
|
|
22
|
+
for (ssize_t i = 0; i < triangles1_buf.shape(0); ++i)
|
|
23
|
+
{
|
|
24
|
+
_tm.add_face(vertex_indices[triangles1_buf(i, 0)], vertex_indices[triangles1_buf(i, 1)], vertex_indices[triangles1_buf(i, 2)]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
vertex_indices.clear();
|
|
28
|
+
for (ssize_t i = 0; i < vertices2_buf.shape(0); ++i)
|
|
29
|
+
{
|
|
30
|
+
vertex_indices.push_back(_clipper.add_vertex(Point(vertices2_buf(i, 0), vertices2_buf(i, 1), vertices2_buf(i, 2))));
|
|
31
|
+
}
|
|
32
|
+
for (ssize_t i = 0; i < triangles2_buf.shape(0); ++i)
|
|
33
|
+
{
|
|
34
|
+
_clipper.add_face(vertex_indices[triangles2_buf(i, 0)], vertex_indices[triangles2_buf(i, 1)], vertex_indices[triangles2_buf(i, 2)]);
|
|
35
|
+
}
|
|
36
|
+
for (const auto &vertex : _tm.vertices())
|
|
37
|
+
{
|
|
38
|
+
const auto &point = _tm.point(vertex);
|
|
39
|
+
}
|
|
40
|
+
for (const auto &vertex : _clipper.vertices())
|
|
41
|
+
{
|
|
42
|
+
const auto &point = _clipper.point(vertex);
|
|
43
|
+
}
|
|
44
|
+
if (!CGAL::is_valid_polygon_mesh(_tm))
|
|
45
|
+
{
|
|
46
|
+
std::cerr << "tm is invalid!" << std::endl;
|
|
47
|
+
}
|
|
48
|
+
if (!CGAL::is_valid_polygon_mesh(_clipper))
|
|
49
|
+
{
|
|
50
|
+
std::cerr << "clipper is invalid!" << std::endl;
|
|
51
|
+
}
|
|
52
|
+
// make sure the meshes actually intersect. If they don't, just return mesh 1
|
|
53
|
+
bool intersection = CGAL::Polygon_mesh_processing::do_intersect(_tm, _clipper);
|
|
54
|
+
if (intersection)
|
|
55
|
+
{
|
|
56
|
+
// Clip tm with clipper
|
|
57
|
+
bool flag = CGAL::Polygon_mesh_processing::clip(_tm, _clipper);
|
|
58
|
+
if (!flag)
|
|
59
|
+
{
|
|
60
|
+
std::cerr << "Clipping failed." << std::endl;
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
std::cout << "Meshes do not intersect. Returning tm." << std::endl;
|
|
67
|
+
}
|
|
68
|
+
// store the result in a numpymesh object for sending back to Python
|
|
69
|
+
std::vector<std::array<double, 3>> vertices;
|
|
70
|
+
std::vector<std::array<int, 3>> triangles;
|
|
71
|
+
std::map<TriangleMesh::Vertex_index, int> vertex_index_map;
|
|
72
|
+
int index = 0;
|
|
73
|
+
for (const auto &vertex : _tm.vertices())
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
const auto &point = _tm.point(vertex);
|
|
77
|
+
vertices.push_back({point.x(), point.y(), point.z()});
|
|
78
|
+
vertex_index_map[vertex] = index++;
|
|
79
|
+
}
|
|
80
|
+
for (const auto &face : _tm.faces())
|
|
81
|
+
{
|
|
82
|
+
std::array<int, 3> triangle;
|
|
83
|
+
int i = 0;
|
|
84
|
+
for (auto halfedge : CGAL::halfedges_around_face(_tm.halfedge(face), _tm))
|
|
85
|
+
{
|
|
86
|
+
triangle[i++] = vertex_index_map[CGAL::target(halfedge, _tm)]; // Assuming `idx()` gives the vertex index
|
|
87
|
+
}
|
|
88
|
+
triangles.push_back(triangle);
|
|
89
|
+
}
|
|
90
|
+
// Convert vertices to a NumPy array
|
|
91
|
+
pybind11::array_t<double> vertices_array({static_cast<int>(vertices.size()), 3});
|
|
92
|
+
auto vertices_buf = vertices_array.mutable_unchecked<2>();
|
|
93
|
+
for (size_t i = 0; i < vertices.size(); ++i)
|
|
94
|
+
{
|
|
95
|
+
vertices_buf(i, 0) = vertices[i][0];
|
|
96
|
+
vertices_buf(i, 1) = vertices[i][1];
|
|
97
|
+
vertices_buf(i, 2) = vertices[i][2];
|
|
98
|
+
}
|
|
99
|
+
// Convert triangles to a NumPy array
|
|
100
|
+
pybind11::array_t<int> triangles_array({static_cast<int>(triangles.size()), 3});
|
|
101
|
+
auto triangles_buf = triangles_array.mutable_unchecked<2>();
|
|
102
|
+
for (size_t i = 0; i < triangles.size(); ++i)
|
|
103
|
+
{
|
|
104
|
+
triangles_buf(i, 0) = triangles[i][0];
|
|
105
|
+
triangles_buf(i, 1) = triangles[i][1];
|
|
106
|
+
triangles_buf(i, 2) = triangles[i][2];
|
|
107
|
+
}
|
|
108
|
+
// Create NumpyMesh object
|
|
109
|
+
NumpyMesh result;
|
|
110
|
+
result.vertices = vertices_array;
|
|
111
|
+
result.triangles = triangles_array;
|
|
112
|
+
// Return the NumpyMesh object
|
|
113
|
+
for (const auto &vertex : _tm.vertices())
|
|
114
|
+
{
|
|
115
|
+
const auto &point = _tm.point(vertex);
|
|
116
|
+
}
|
|
117
|
+
// Return the NumpyMesh object
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#ifndef CLIP_H
|
|
2
|
+
#define CLIP_H
|
|
3
|
+
#include <pybind11/numpy.h>
|
|
4
|
+
#include "numpymesh.h"
|
|
5
|
+
#include <CGAL/Simple_cartesian.h>
|
|
6
|
+
#include <CGAL/Surface_mesh.h>
|
|
7
|
+
|
|
8
|
+
typedef CGAL::Simple_cartesian<double> Kernel;
|
|
9
|
+
typedef Kernel::Point_3 Point;
|
|
10
|
+
typedef CGAL::Surface_mesh<Point> TriangleMesh;
|
|
11
|
+
NumpyMesh clip_surface(
|
|
12
|
+
NumpyMesh tm,
|
|
13
|
+
NumpyMesh clipper
|
|
14
|
+
);
|
|
15
|
+
#endif
|