- arc improvements, fillets, operations, bodys
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user