Fiexed highlighting of operations
This commit is contained in:
@@ -1426,14 +1426,18 @@ class OCCRenderer(Renderer):
|
||||
ais.SetColor(Quantity_Color(*color, Quantity_TOC_RGB))
|
||||
ais.SetDisplayMode(1) # shaded
|
||||
try:
|
||||
ais.SetTransparency(0.65)
|
||||
ais.SetTransparency(0.2)
|
||||
except Exception:
|
||||
logger.debug("op highlight transparency set failed", exc_info=True)
|
||||
try:
|
||||
ais.SetSelectability(0)
|
||||
except Exception:
|
||||
logger.debug("op highlight selectability set failed", exc_info=True)
|
||||
try:
|
||||
ais.SetPolygonOffsets(3, 1.0, -0.5)
|
||||
except Exception:
|
||||
logger.debug("op highlight polygon offset failed", exc_info=True)
|
||||
self._context.Display(ais, True)
|
||||
self._context.Display(ais, False)
|
||||
self._op_highlight_ais = ais
|
||||
if self._view is not None:
|
||||
self._view.Redraw()
|
||||
|
||||
+104
-12
@@ -8,7 +8,7 @@ import os
|
||||
from datetime import datetime
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from PySide6.QtCore import Qt, Slot, QSize, QSettings
|
||||
from PySide6.QtCore import Qt, Slot, QSize, QSettings, QTimer
|
||||
from PySide6.QtGui import (
|
||||
QAction,
|
||||
QColor,
|
||||
@@ -1383,6 +1383,10 @@ def _replay_body_features(
|
||||
"skipping chamfer"
|
||||
)
|
||||
continue
|
||||
geom = kernel.chamfer(geom, feat.radius, edges=edges)
|
||||
if geom is None:
|
||||
return None
|
||||
continue
|
||||
|
||||
if feat.operation in ("array", "pattern"):
|
||||
# Pattern needs no sketch — it repeats the running solid.
|
||||
@@ -1719,9 +1723,12 @@ class MainWindow(QMainWindow):
|
||||
# selectable axis / centre / plane from the 3D viewport.
|
||||
self._sketch_gizmo_selection: Optional[dict] = None
|
||||
self._selected_body: Optional[Body] = None
|
||||
# Track which body we're hiding while an operation-history highlight
|
||||
# is active, so we can restore it when the selection changes.
|
||||
self._op_highlight_body_id: Optional[str] = None
|
||||
# Auto-clears the operation-history highlight 1 s after it is shown so
|
||||
# the 3D view returns to its normal, fully-selectable state.
|
||||
self._op_highlight_timer = QTimer(self)
|
||||
self._op_highlight_timer.setSingleShot(True)
|
||||
self._op_highlight_timer.timeout.connect(self._on_op_highlight_timeout)
|
||||
# Body-highlight state: when no operation is selected we tint the
|
||||
# selected body light-blue; save its original colour to restore later.
|
||||
self._body_highlight_id: Optional[str] = None
|
||||
@@ -2685,6 +2692,12 @@ class MainWindow(QMainWindow):
|
||||
"""
|
||||
if feat.operation in ("cut", "union"):
|
||||
return self._compute_tool_shape(body, features, index, feat)
|
||||
# Fillet / chamfer: show a cube covering the corner where the op
|
||||
# was applied, not the whole body.
|
||||
if feat.operation in ("fillet", "chamfer"):
|
||||
shape = self._compute_fillet_bbox_shape(body, features, index, feat)
|
||||
if shape is not None:
|
||||
return shape
|
||||
# Default: show the intermediate body after this operation.
|
||||
geom = _replay_body_features(
|
||||
self._kernel, body, features[: index + 1],
|
||||
@@ -2748,6 +2761,80 @@ class MainWindow(QMainWindow):
|
||||
return None
|
||||
return self._kernel._get_shape(tool_geom)
|
||||
|
||||
def _compute_fillet_bbox_shape(
|
||||
self, body: Body, features: List[Feature], index: int, feat: Feature
|
||||
) -> Optional[Any]:
|
||||
"""Return a cube covering where a fillet/chamfer op was applied.
|
||||
|
||||
Replays the body *before* the op, resolves the edges the op
|
||||
touches (the same face-key / fingerprint resolution the replay
|
||||
uses), and returns a box around their bounding volume — a corner
|
||||
marker, not the whole body. Returns *None* when the op spans
|
||||
the whole body (scope "all") or no edges resolve, so the caller
|
||||
falls back to the intermediate body.
|
||||
"""
|
||||
# Scope "all" fillets every edge of the body — a corner cube is
|
||||
# meaningless there, so let the caller show the whole body.
|
||||
if feat.scope == "all":
|
||||
return None
|
||||
|
||||
try:
|
||||
pre_geom = _replay_body_features(
|
||||
self._kernel, body, features[:index],
|
||||
self._through_all_length_for_geometry,
|
||||
component=self._current_component,
|
||||
)
|
||||
except Exception:
|
||||
logger.debug("fillet bbox replay failed", exc_info=True)
|
||||
return None
|
||||
pre = self._kernel._get_shape(pre_geom) if pre_geom is not None else None
|
||||
if pre is None:
|
||||
return None
|
||||
|
||||
# Resolve the fillet/chamfer edges on the pre-op geometry, exactly
|
||||
# as the replay does (FaceKey first, fingerprint fallback).
|
||||
edges: List[Any] = []
|
||||
if feat.face_keys is not None:
|
||||
edges = _resolve_edges_by_face_keys(
|
||||
pre, feat, self._current_component
|
||||
) or []
|
||||
if not edges:
|
||||
edges = _resolve_edges_by_fingerprint(pre, feat.edge_refs)
|
||||
if not edges:
|
||||
return None
|
||||
|
||||
from OCP.BRep import BRep_Builder
|
||||
from OCP.Bnd import Bnd_Box
|
||||
from OCP.BRepBndLib import BRepBndLib
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from OCP.TopoDS import TopoDS_Compound
|
||||
from OCP.gp import gp_Pnt
|
||||
|
||||
comp = TopoDS_Compound()
|
||||
builder = BRep_Builder()
|
||||
builder.MakeCompound(comp)
|
||||
for e in edges:
|
||||
builder.Add(comp, e)
|
||||
|
||||
bbox = Bnd_Box()
|
||||
BRepBndLib.AddClose_s(comp, bbox)
|
||||
if bbox.IsVoid():
|
||||
return None
|
||||
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
|
||||
|
||||
# Pad by the fillet radius so the cube comfortably covers the
|
||||
# round/bevel, not just the sharp pre-op edge line(s).
|
||||
pad = float(feat.radius) if feat.radius else 0.0
|
||||
cx, cy, cz = (xmin + xmax) / 2.0, (ymin + ymax) / 2.0, (zmin + zmax) / 2.0
|
||||
w = max(2 * pad, (xmax - xmin) + 2 * pad)
|
||||
h = max(2 * pad, (ymax - ymin) + 2 * pad)
|
||||
d = max(2 * pad, (zmax - zmin) + 2 * pad)
|
||||
|
||||
box = BRepPrimAPI_MakeBox(
|
||||
gp_Pnt(cx - w / 2.0, cy - h / 2.0, cz - d / 2.0), w, h, d
|
||||
)
|
||||
return box.Shape()
|
||||
|
||||
|
||||
# ── Body highlight helpers ─────────────────────────────────────────
|
||||
|
||||
@@ -2777,10 +2864,9 @@ class MainWindow(QMainWindow):
|
||||
"""Enable 'Del Op' / 'Mirror Op', highlight body or selected op in 3D."""
|
||||
item = self._operations_list.currentItem()
|
||||
|
||||
# Restore the body we hid for the previous operation highlight.
|
||||
if self._op_highlight_body_id is not None:
|
||||
self._viewer_3d.set_visibility(self._op_highlight_body_id, True)
|
||||
self._op_highlight_body_id = None
|
||||
# Drop the previous highlight and any pending auto-clear so rapid
|
||||
# re-selection always starts from a clean state.
|
||||
self._op_highlight_timer.stop()
|
||||
self._viewer_3d.clear_operation_highlight()
|
||||
|
||||
# Restore the previous body colour highlight.
|
||||
@@ -2805,18 +2891,24 @@ class MainWindow(QMainWindow):
|
||||
body, features, index, feat
|
||||
)
|
||||
if shape is not None:
|
||||
if body.render_object is not None and body.visible:
|
||||
self._viewer_3d.set_visibility(
|
||||
body.render_object, False
|
||||
)
|
||||
self._op_highlight_body_id = body.render_object
|
||||
# Overlay the operation geometry on the still-visible
|
||||
# body as a hot-pink flash. The overlay is
|
||||
# non-selectable, so face/edge/vertex picking keeps
|
||||
# working while it shows.
|
||||
self._viewer_3d.highlight_operation(shape)
|
||||
# Auto-clear after 1 s so the view returns to normal
|
||||
# and the user can work on the body.
|
||||
self._op_highlight_timer.start(1000)
|
||||
except Exception:
|
||||
logger.debug("op highlight replay failed", exc_info=True)
|
||||
else:
|
||||
# Base operation (or non-selectable) — highlight body light blue.
|
||||
self._highlight_selected_body_light_blue()
|
||||
|
||||
def _on_op_highlight_timeout(self) -> None:
|
||||
"""1 s elapsed — drop the operation highlight, restoring the normal view."""
|
||||
self._viewer_3d.clear_operation_highlight()
|
||||
|
||||
def _on_mirror_operation(self) -> None:
|
||||
"""Mirror the body at the selected operation's point in the feature history.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user