- arc improvements, fillets, operations, bodys
This commit is contained in:
@@ -1696,10 +1696,19 @@ class MainWindow(QMainWindow):
|
||||
ops_tools = self._ui.groupBox_8.layout()
|
||||
if ops_tools is None:
|
||||
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._btn_del_op.clicked.connect(self._on_delete_operation)
|
||||
self._btn_mirror_op.clicked.connect(self._on_mirror_operation)
|
||||
|
||||
def _setup_ui_aliases(self):
|
||||
"""Create _btn_* aliases pointing to the UI-loaded widgets.
|
||||
@@ -2325,6 +2334,7 @@ class MainWindow(QMainWindow):
|
||||
body = self._selected_body
|
||||
if body is None:
|
||||
self._btn_del_op.setEnabled(False)
|
||||
self._btn_mirror_op.setEnabled(False)
|
||||
return
|
||||
features = _ensure_feature_history(body)
|
||||
for index, feat in enumerate(features):
|
||||
@@ -2338,13 +2348,108 @@ class MainWindow(QMainWindow):
|
||||
self._on_operations_selection_changed()
|
||||
|
||||
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()
|
||||
if item is None:
|
||||
self._btn_del_op.setEnabled(False)
|
||||
self._btn_mirror_op.setEnabled(False)
|
||||
return
|
||||
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:
|
||||
"""Delete the selected operation; the body reverts to before it.
|
||||
|
||||
Reference in New Issue
Block a user