diff --git a/src/woodshop/gui/viewport.py b/src/woodshop/gui/viewport.py index ed3904a..5c6a518 100644 --- a/src/woodshop/gui/viewport.py +++ b/src/woodshop/gui/viewport.py @@ -7,7 +7,7 @@ from PySide6.QtWidgets import (QApplication, QHBoxLayout, QPushButton, QVBoxLayout, QWidget) from ..scene import Scene -from ..viewer import _PALETTE, _part_mesh, _quiet_vtk +from ..viewer import _PALETTE, _add_feature_edges, _part_mesh, _quiet_vtk class Viewport(QWidget): @@ -64,13 +64,16 @@ class Viewport(QWidget): labels, pts = [], [] for i, part in enumerate(scene.parts): selected = part.id in selected_ids + mesh = _part_mesh(part) actor = self.plotter.add_mesh( - _part_mesh(part), + mesh, color="#f5d76e" if selected else _PALETTE[i % len(_PALETTE)], show_edges=not part.features, line_width=3 if selected else 1, edge_color="black", reset_camera=False, pickable=True, ) self._actor_to_pid[actor] = part.id + if part.features: + _add_feature_edges(self.plotter, mesh, selected) mid = [part.position_in[j] + part.axis_unit()[j] * part.length_in / 2 for j in range(3)] labels.append(part.name or part.id) pts.append(mid) diff --git a/src/woodshop/viewer.py b/src/woodshop/viewer.py index 1549959..08018c1 100644 --- a/src/woodshop/viewer.py +++ b/src/woodshop/viewer.py @@ -54,6 +54,20 @@ def _part_mesh(part: Part): return cube +def _add_feature_edges(plotter, mesh, selected: bool) -> None: + """Overlay a tessellated solid's real edges (corners/holes/chamfers) so it + reads as crisply as a plain board, without the triangle-mesh noise.""" + try: + edges = mesh.extract_feature_edges( + feature_angle=20, boundary_edges=True, feature_edges=True, + manifold_edges=False, non_manifold_edges=False) + if edges.n_points: + plotter.add_mesh(edges, color="yellow" if selected else "black", + line_width=3 if selected else 2, reset_camera=False) + except Exception: + pass + + def _quiet_vtk() -> None: """Stop VTK from spamming warnings (esp. headless) through Python logging.""" try: @@ -71,14 +85,17 @@ def _render(plotter, scene: Scene) -> None: labels, label_pts = [], [] for i, part in enumerate(scene.parts): edge = part.id == scene.selection + mesh = _part_mesh(part) plotter.add_mesh( - _part_mesh(part), + mesh, color="#f5d76e" if edge else _PALETTE[i % len(_PALETTE)], - show_edges=not part.features, # triangle mesh would look noisy with edges + show_edges=not part.features, # plain boxes: real quad edges line_width=3 if edge else 1, edge_color="black", smooth_shading=False, ) + if part.features: # tessellated: overlay only the true edges + _add_feature_edges(plotter, mesh, edge) mid = [part.position_in[j] + part.axis_unit()[j] * part.length_in / 2 for j in range(3)] labels.append(part.name or part.id) label_pts.append(mid)