Skip to content

Commit c0490c3

Browse files
authored
Merge pull request #18 from compas-dev/mesh-split
add mesh split endpoint
2 parents 9f76e2c + fa34dc7 commit c0490c3

File tree

10 files changed

+150
-4
lines changed

10 files changed

+150
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
* Support to python 3.10.
1212
* Added Changelog check in PRs.
13+
* Exposing mesh `split` function.
1314

1415
### Changed
1516
* Updated github workflow.
131 KB
Loading

docs/examples/booleans_split.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from compas.geometry import Point
2+
from compas.geometry import Box
3+
from compas.geometry import Sphere
4+
from compas.datastructures import Mesh
5+
from compas.colors import Color
6+
from random import random
7+
8+
from compas_view2.app import App
9+
10+
from compas_cgal.booleans import split
11+
from compas.topology import connected_components
12+
13+
# ==============================================================================
14+
# Make a box and a sphere
15+
# ==============================================================================
16+
17+
box = Box.from_width_height_depth(2, 2, 2)
18+
box = Mesh.from_shape(box)
19+
box.quads_to_triangles()
20+
21+
A = box.to_vertices_and_faces()
22+
23+
sphere = Sphere(Point(1, 1, 1), 1)
24+
sphere = Mesh.from_shape(sphere, u=20, v=20)
25+
sphere.quads_to_triangles()
26+
27+
B = sphere.to_vertices_and_faces()
28+
29+
# ==============================================================================
30+
# Compute the mesh split
31+
# ==============================================================================
32+
33+
V, F = split(A, B)
34+
35+
mesh = Mesh.from_vertices_and_faces(V, F)
36+
37+
# ==============================================================================
38+
# Seperate disjoint faces and visualize
39+
# ==============================================================================
40+
41+
viewer = App()
42+
43+
fa = mesh.face_adjacency()
44+
parts = connected_components(fa)
45+
for face_indexes in connected_components(fa):
46+
mesh = Mesh.from_vertices_and_faces(V, F[face_indexes])
47+
mesh.remove_unused_vertices()
48+
viewer.add(mesh, facecolor=Color.from_i(random()))
49+
50+
viewer.run()

docs/examples/mesh_split.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
********************************************************************************
2+
Mesh Split
3+
********************************************************************************
4+
5+
.. figure:: /_images/cgal_boolean_split.jpg
6+
:figclass: figure
7+
:class: figure-img img-fluid
8+
9+
10+
.. literalinclude:: booleans_split.py
11+
:language: python

requirements-dev.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ invoke >=0.14
99
ipykernel
1010
ipython >=5.8
1111
isort
12-
m2r
12+
m2r2
1313
nbsphinx
1414
pydocstyle
15-
pytest >=3.2
15+
pytest <7.1
1616
sphinx_compas_theme >=0.15.18
17-
sphinx >=3.4
17+
sphinx ==4.5
1818
twine
1919
wheel
20+
jinja2 >= 3.0
2021
-e .

src/booleans.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "booleans.h"
22
#include <CGAL/Polygon_mesh_processing/corefinement.h>
3+
#include <CGAL/Polygon_mesh_processing/clip.h>
34

45

56
namespace PMP = CGAL::Polygon_mesh_processing;
@@ -68,6 +69,28 @@ pmp_boolean_intersection(
6869
};
6970

7071

72+
std::tuple<compas::RowMatrixXd, compas::RowMatrixXi>
73+
pmp_split(
74+
Eigen::Ref<const compas::RowMatrixXd> &VA,
75+
Eigen::Ref<const compas::RowMatrixXi> &FA,
76+
Eigen::Ref<const compas::RowMatrixXd> &VB,
77+
Eigen::Ref<const compas::RowMatrixXi> &FB)
78+
{
79+
Mesh A = compas::mesh_from_vertices_and_faces(VA, FA);
80+
Mesh B = compas::mesh_from_vertices_and_faces(VB, FB);
81+
// Mesh C;
82+
83+
// PMP::clip(A, B, CGAL::parameters::clip_volume(false), CGAL::parameters::clip_volume(false));
84+
PMP::split(A, B);
85+
86+
// Result
87+
88+
std::tuple<compas::RowMatrixXd, compas::RowMatrixXi> R = compas::mesh_to_vertices_and_faces(A);
89+
90+
return R;
91+
};
92+
93+
7194
void init_booleans(pybind11::module &m)
7295
{
7396
pybind11::module submodule = m.def_submodule("booleans");
@@ -95,4 +118,12 @@ void init_booleans(pybind11::module &m)
95118
pybind11::arg("FA").noconvert(),
96119
pybind11::arg("VB").noconvert(),
97120
pybind11::arg("FB").noconvert());
121+
122+
submodule.def(
123+
"split",
124+
&pmp_split,
125+
pybind11::arg("VA").noconvert(),
126+
pybind11::arg("FA").noconvert(),
127+
pybind11::arg("VB").noconvert(),
128+
pybind11::arg("FB").noconvert());
98129
};

src/booleans.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,12 @@ pmp_boolean_intersection(
2828
Eigen::Ref<const compas::RowMatrixXi> &FB);
2929

3030

31+
std::tuple<compas::RowMatrixXd, compas::RowMatrixXi>
32+
pmp_clip(
33+
Eigen::Ref<const compas::RowMatrixXd> &VA,
34+
Eigen::Ref<const compas::RowMatrixXi> &FA,
35+
Eigen::Ref<const compas::RowMatrixXd> &VB,
36+
Eigen::Ref<const compas::RowMatrixXi> &FB);
37+
38+
3139
#endif /* COMPAS_BOOLEANS_H */

src/compas_cgal/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
booleans.boolean_union
1616
booleans.boolean_difference
1717
booleans.boolean_intersection
18+
booleans.split
1819
1920
Intersections
2021
=============

src/compas_cgal/booleans.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def _boolean(A, B, operation):
4141
result = booleans.boolean_difference(VA, FA, VB, FB)
4242
elif operation == 'intersection':
4343
result = booleans.boolean_intersection(VA, FA, VB, FB)
44+
elif operation == 'split':
45+
result = booleans.split(VA, FA, VB, FB)
4446
else:
4547
raise NotImplementedError
4648

@@ -159,3 +161,41 @@ def boolean_intersection(A, B):
159161
160162
"""
161163
return _boolean(A, B, 'intersection')
164+
165+
166+
@plugin(category='booleans', pluggable_name='split_mesh_mesh')
167+
def split(A, B):
168+
"""Split one mesh with another.
169+
170+
Parameters
171+
----------
172+
A : tuple[Sequence[[float, float, float] | :class:`~compas.geometry.Point`], Sequence[[int, int, int]]]
173+
Mesh A.
174+
B : tuple[Sequence[[float, float, float] | :class:`~compas.geometry.Point`], Sequence[[int, int, int]]]
175+
Mesh B.
176+
operation : Literal['union', 'difference', 'intersection']
177+
The type of boolean operation.
178+
179+
Returns
180+
-------
181+
NDArray[(Any, 3), np.float64]
182+
The vertices of the boolean mesh.
183+
NDArray[(Any, 3), np.int32]
184+
The faces of the boolean mesh.
185+
186+
Examples
187+
--------
188+
>>> from compas.geometry import Box, Sphere, Polyhedron
189+
>>> from compas_cgal.booleans import split
190+
191+
>>> box = Box.from_width_height_depth(1, 1, 1)
192+
>>> sphere = Sphere([1, 1, 1], 0.5)
193+
194+
>>> A = box.to_vertices_and_faces(triangulated=True)
195+
>>> B = sphere.to_vertices_and_faces(u=32, v=32, triangulated=True)
196+
197+
>>> V, F = split(A, B)
198+
>>> mesh = Mesh.from_vertices_and_faces(V, F)
199+
200+
"""
201+
return _boolean(A, B, 'split')

src/compas_cgal/booleans.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ from compas_cgal.types import VerticesFaces
44
from compas_cgal.types import VerticesFacesNumpy
55

66

7-
def _boolean(A: VerticesFaces, B: VerticesFaces, operation: Literal['union', 'difference', 'intersection']) -> VerticesFacesNumpy:
7+
def _boolean(A: VerticesFaces, B: VerticesFaces, operation: Literal['union', 'difference', 'intersection', 'split']) -> VerticesFacesNumpy:
88
pass
99

1010

@@ -18,3 +18,6 @@ def boolean_difference(A: VerticesFaces, B: VerticesFaces) -> VerticesFacesNumpy
1818

1919
def boolean_intersection(A: VerticesFaces, B: VerticesFaces) -> VerticesFacesNumpy:
2020
pass
21+
22+
def split(A: VerticesFaces, B: VerticesFaces) -> VerticesFacesNumpy:
23+
pass

0 commit comments

Comments
 (0)