Skip to content

Commit cf1949d

Browse files
petrasvestartasjf---
authored andcommitted
numpy downgrade
1 parent 03f2b56 commit cf1949d

3 files changed

Lines changed: 66 additions & 6 deletions

File tree

docs/examples/example_booleans_with_face_source.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from compas_viewer import Viewer
88

99
from compas_cgal.booleans import boolean_chain_with_face_source
10+
from compas_cgal.booleans import split_by_source
1011

1112

1213
# =============================================================================
@@ -52,15 +53,16 @@ def cylinder_along(axis, radius=0.8):
5253
exact=False,
5354
)
5455

55-
mesh = Mesh.from_vertices_and_faces(V, F)
56-
5756
# =============================================================================
5857
# Color the output by source mesh:
5958
# mesh_id == 0 -> cube (red)
6059
# mesh_id == 1 -> sphere (blue) — rounded corners/edges
6160
# mesh_id == 2 -> cyl_x (green) — X-axis through-hole walls
6261
# mesh_id == 3 -> cyl_y (yellow) — Y-axis through-hole walls
6362
# mesh_id == 4 -> cyl_z (magenta) — Z-axis through-hole walls
63+
#
64+
# Two equivalent visualization paths are shown below. Pick whichever fits
65+
# your downstream code; the C++ output is identical in both cases.
6466
# =============================================================================
6567

6668
palette = {
@@ -71,12 +73,36 @@ def cylinder_along(axis, radius=0.8):
7173
4: Color(0.80, 0.40, 0.85),
7274
}
7375

76+
# Option A — single connected mesh + per-face color dict (keeps shared
77+
# vertices intact at source boundaries; best for further processing).
78+
mesh = Mesh.from_vertices_and_faces(V, F)
7479
facecolor = {fkey: palette[mesh_id] for fkey, (mesh_id, _) in zip(mesh.faces(), S.tolist())}
7580

81+
# Option B — split into one mesh per source via split_by_source. Each
82+
# submesh is independent (boundary vertices are duplicated across submeshes)
83+
# and gets its own scene object with a single color. Convenient for viewers
84+
# that prefer one material/layer per object.
85+
submeshes = {
86+
mesh_id: Mesh.from_vertices_and_faces(Vs, Fs)
87+
for mesh_id, (Vs, Fs) in split_by_source(V, F, S).items()
88+
}
89+
7690
# =============================================================================
77-
# Visualize
91+
# Visualize — toggle USE_SPLIT to compare the two paths.
7892
# =============================================================================
7993

94+
USE_SPLIT = True
95+
8096
viewer = Viewer()
81-
viewer.scene.add(mesh, facecolor=facecolor, lineswidth=1, show_points=False, show_lines=True)
97+
if USE_SPLIT:
98+
for mesh_id, submesh in submeshes.items():
99+
viewer.scene.add(
100+
submesh,
101+
facecolor=palette[mesh_id],
102+
lineswidth=1,
103+
show_points=False,
104+
show_lines=True,
105+
)
106+
else:
107+
viewer.scene.add(mesh, facecolor=facecolor, lineswidth=1, show_points=False, show_lines=True)
82108
viewer.show()

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ CMAKE_POLICY_DEFAULT_CMP0135 = "NEW"
8282
[tool.cibuildwheel]
8383
# build = ["cp38-*", "cp39-*", "cp31?-*"] # Build for specific Python versions.
8484
build-verbosity = 3
85-
test-requires = ["numpy>=2.4", "compas>=2.15,<3", "pytest", "build"]
86-
test-command = "pip install 'numpy>=2.4' 'compas>=2.15,<3' && pip list && pytest {project}/tests"
85+
test-requires = ["numpy>=1.24", "compas>=2.15,<3", "pytest", "build"]
86+
test-command = "pip install 'numpy>=1.24' 'compas>=2.15,<3' && pip list && pytest {project}/tests"
8787
build-frontend = "pip"
8888
manylinux-x86_64-image = "manylinux_2_28"
8989
skip = ["*_i686", "*-musllinux_*", "*-win32", "pp*"]

src/compas_cgal/booleans.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,40 @@ def boolean_chain_with_face_source(
440440
return _booleans.boolean_chain_with_face_source(V_flat, F_flat, v_counts, f_counts, op_codes)
441441

442442

443+
def split_by_source(
444+
V: np.ndarray,
445+
F: np.ndarray,
446+
S: np.ndarray,
447+
) -> dict[int, tuple[np.ndarray, np.ndarray]]:
448+
"""Split a face-source-tagged boolean result into one mesh per source.
449+
450+
Given ``(V, F, S)`` from :func:`boolean_chain_with_face_source`, returns a
451+
dict mapping each ``mesh_id`` present in ``S[:, 0]`` to its own
452+
``(V_sub, F_sub)`` pair. ``V_sub`` contains only the vertices referenced
453+
by that submesh's faces and ``F_sub`` is reindexed accordingly.
454+
455+
Note: vertices on the boundary between two source regions are duplicated
456+
across the resulting submeshes (each submesh gets its own copy), so the
457+
submeshes are no longer connected at cut boundaries. The original ``(V,
458+
F, S)`` remains the canonical output — use this helper only when a
459+
per-source mesh layout is convenient (e.g. assigning a single colour or
460+
material to a viewer scene object).
461+
"""
462+
V = np.asarray(V)
463+
F = np.asarray(F)
464+
S = np.asarray(S)
465+
466+
out: dict[int, tuple[np.ndarray, np.ndarray]] = {}
467+
for mesh_id in np.unique(S[:, 0]):
468+
face_mask = S[:, 0] == mesh_id
469+
F_sub = F[face_mask]
470+
used = np.unique(F_sub.reshape(-1))
471+
remap = np.full(V.shape[0], -1, dtype=np.int64)
472+
remap[used] = np.arange(used.shape[0])
473+
out[int(mesh_id)] = (V[used], remap[F_sub].astype(F.dtype))
474+
return out
475+
476+
443477
def boolean_difference_mesh_meshes(A: VerticesFaces, Bs: Iterable[VerticesFaces]) -> VerticesFacesNumpy:
444478
"""Subtract many meshes from A in a single corefinement.
445479

0 commit comments

Comments
 (0)