- arc improvements, fillets, operations, bodys

This commit is contained in:
bklronin
2026-08-07 19:55:15 +02:00
parent 655df71cd8
commit 79ad85594e
+108 -3
View File
@@ -1696,10 +1696,19 @@ class MainWindow(QMainWindow):
ops_tools = self._ui.groupBox_8.layout() ops_tools = self._ui.groupBox_8.layout()
if ops_tools is None: if ops_tools is None:
ops_tools = QGridLayout(self._ui.groupBox_8) ops_tools = QGridLayout(self._ui.groupBox_8)
ops_tools.addWidget(self._btn_del_op, 1, 0, 1, 2) ops_tools.addWidget(self._btn_del_op, 1, 0, 1, 1)
self._btn_mirror_op = QPushButton("Mirror Op")
self._btn_mirror_op.setEnabled(False)
self._btn_mirror_op.setToolTip(
"Mirror the body at the selected operation — inserts a mirror "
"after it so the operation's effect appears on both sides."
)
ops_tools.addWidget(self._btn_mirror_op, 1, 1, 1, 1)
self._operations_list.itemSelectionChanged.connect(self._on_operations_selection_changed) self._operations_list.itemSelectionChanged.connect(self._on_operations_selection_changed)
self._btn_del_op.clicked.connect(self._on_delete_operation) self._btn_del_op.clicked.connect(self._on_delete_operation)
self._btn_mirror_op.clicked.connect(self._on_mirror_operation)
def _setup_ui_aliases(self): def _setup_ui_aliases(self):
"""Create _btn_* aliases pointing to the UI-loaded widgets. """Create _btn_* aliases pointing to the UI-loaded widgets.
@@ -2325,6 +2334,7 @@ class MainWindow(QMainWindow):
body = self._selected_body body = self._selected_body
if body is None: if body is None:
self._btn_del_op.setEnabled(False) self._btn_del_op.setEnabled(False)
self._btn_mirror_op.setEnabled(False)
return return
features = _ensure_feature_history(body) features = _ensure_feature_history(body)
for index, feat in enumerate(features): for index, feat in enumerate(features):
@@ -2338,13 +2348,108 @@ class MainWindow(QMainWindow):
self._on_operations_selection_changed() self._on_operations_selection_changed()
def _on_operations_selection_changed(self) -> None: def _on_operations_selection_changed(self) -> None:
"""Enable 'Del Op' only when a deletable (non-base) op is selected.""" """Enable 'Del Op' / 'Mirror Op' when a non-base op is selected."""
item = self._operations_list.currentItem() item = self._operations_list.currentItem()
if item is None: if item is None:
self._btn_del_op.setEnabled(False) self._btn_del_op.setEnabled(False)
self._btn_mirror_op.setEnabled(False)
return return
index = item.data(Qt.ItemDataRole.UserRole) index = item.data(Qt.ItemDataRole.UserRole)
self._btn_del_op.setEnabled(index is not None and index > 0) deletable = index is not None and index > 0
self._btn_del_op.setEnabled(deletable)
self._btn_mirror_op.setEnabled(index is not None and index >= 0)
def _on_mirror_operation(self) -> None:
"""Mirror the body at the selected operation's point in the feature history.
Opens the MirrorDialog; on OK a ``mirror`` Feature is inserted right
after the selected operation. The body is then rebuilt from scratch
by replaying the updated feature list, so the mirror applies at
exactly that point and subsequent operations (fillets, chamfers,
etc.) run on the mirrored geometry.
"""
body = self._selected_body
if body is None or body.geometry is None:
QMessageBox.warning(
self, "No Body", "Select a body first."
)
return
item = self._operations_list.currentItem()
if item is None:
QMessageBox.information(
self, "No Operation",
"Select an operation in the list first — the mirror will "
"be inserted right after it."
)
return
index = item.data(Qt.ItemDataRole.UserRole)
features = _ensure_feature_history(body)
if not isinstance(index, int) or not (0 <= index < len(features)):
return
# Gather workplane data for the dialog.
wp_list: list = []
if self._current_component is not None:
wp_list = [
(wp.name, wp.origin, wp.normal)
for wp in self._current_component.workplanes.values()
]
dialog = MirrorDialog(self, workplanes=wp_list)
def _preview(values: Any) -> None:
if values is None:
self._viewer_3d.clear_preview()
return
try:
# Replay features up to and including the selected one.
geom = _replay_body_features(
self._kernel, body, features[: index + 1], self._through_all_length_for_geometry
)
if geom is None:
self._viewer_3d.clear_preview()
return
normal = values["mirror_plane_normal"]
origin = values["mirror_plane_origin"]
keep = values["keep_original"]
mirrored = self._kernel.mirror(geom, normal, origin)
if mirrored is None:
self._viewer_3d.clear_preview()
return
if keep:
result = self._kernel.boolean_union(geom, mirrored)
else:
result = mirrored
if result is not None:
shape = self._kernel._get_shape(result)
self._viewer_3d.show_preview(shape)
else:
self._viewer_3d.clear_preview()
except Exception:
logger.debug("mirror op preview failed", exc_info=True)
self._viewer_3d.clear_preview()
dialog.set_preview_callback(_preview)
if dialog.exec():
values = dialog.get_values()
feat = Feature(
operation="mirror",
mirror_plane_origin=values["mirror_plane_origin"],
mirror_plane_normal=values["mirror_plane_normal"],
keep_original=values["keep_original"],
)
# Insert the mirror feature right after the selected operation.
features.insert(index + 1, feat)
body.needs_update = True
body.modified_at = datetime.now()
self._mark_dirty()
logger.info(
f"Body '{body.name}': inserted mirror after operation {index}"
)
self._update_and_redraw()
self._viewer_3d.clear_preview()
def _on_delete_operation(self) -> None: def _on_delete_operation(self) -> None:
"""Delete the selected operation; the body reverts to before it. """Delete the selected operation; the body reverts to before it.