Show real edges on featured boards (fix flat/edgeless look)

Featured (tessellated) boards were drawn with show_edges off to avoid triangle
noise, which left them looking flat and edgeless. Now overlay only the true
geometric edges via pyvista extract_feature_edges (corners, hole rims, chamfer
bevels, mortise walls) — crisp like plain boards, no mesh noise. Selected
boards get yellow edges. Applied in both the standalone viewer and the GUI
viewport. Verified by render: hole, mortise, tenon, and chamfer all read clearly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
rob 2026-05-29 14:32:42 -03:00
parent 9cbff4ec78
commit d0e40cdcbc
2 changed files with 24 additions and 4 deletions

View File

@ -7,7 +7,7 @@ from PySide6.QtWidgets import (QApplication, QHBoxLayout, QPushButton,
QVBoxLayout, QWidget) QVBoxLayout, QWidget)
from ..scene import Scene 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): class Viewport(QWidget):
@ -64,13 +64,16 @@ class Viewport(QWidget):
labels, pts = [], [] labels, pts = [], []
for i, part in enumerate(scene.parts): for i, part in enumerate(scene.parts):
selected = part.id in selected_ids selected = part.id in selected_ids
mesh = _part_mesh(part)
actor = self.plotter.add_mesh( actor = self.plotter.add_mesh(
_part_mesh(part), mesh,
color="#f5d76e" if selected else _PALETTE[i % len(_PALETTE)], color="#f5d76e" if selected else _PALETTE[i % len(_PALETTE)],
show_edges=not part.features, line_width=3 if selected else 1, show_edges=not part.features, line_width=3 if selected else 1,
edge_color="black", reset_camera=False, pickable=True, edge_color="black", reset_camera=False, pickable=True,
) )
self._actor_to_pid[actor] = part.id 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)] 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) labels.append(part.name or part.id)
pts.append(mid) pts.append(mid)

View File

@ -54,6 +54,20 @@ def _part_mesh(part: Part):
return cube 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: def _quiet_vtk() -> None:
"""Stop VTK from spamming warnings (esp. headless) through Python logging.""" """Stop VTK from spamming warnings (esp. headless) through Python logging."""
try: try:
@ -71,14 +85,17 @@ def _render(plotter, scene: Scene) -> None:
labels, label_pts = [], [] labels, label_pts = [], []
for i, part in enumerate(scene.parts): for i, part in enumerate(scene.parts):
edge = part.id == scene.selection edge = part.id == scene.selection
mesh = _part_mesh(part)
plotter.add_mesh( plotter.add_mesh(
_part_mesh(part), mesh,
color="#f5d76e" if edge else _PALETTE[i % len(_PALETTE)], 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, line_width=3 if edge else 1,
edge_color="black", edge_color="black",
smooth_shading=False, 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)] 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) labels.append(part.name or part.id)
label_pts.append(mid) label_pts.append(mid)