1 Commits

Author SHA1 Message Date
bklronin a1361ecc58 - added contrain context menu
- improved line pickability.
2026-07-21 20:22:17 +02:00
6 changed files with 307 additions and 67 deletions
+32 -10
View File
@@ -4,16 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="8f0bafd6-58a0-4b20-aa2b-ddc3ba278873" name="Changes" comment="- Render improvements, camera plane, update"> <list default="true" id="8f0bafd6-58a0-4b20-aa2b-ddc3ba278873" name="Changes" comment="- added contrain context menu&#10;- improved line pickability.">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/gui.ui" beforeDir="false" afterPath="$PROJECT_DIR$/gui.ui" afterDir="false" /> <change beforePath="$PROJECT_DIR$/gui.ui" beforeDir="false" afterPath="$PROJECT_DIR$/gui.ui" afterDir="false" />
<change beforePath="$PROJECT_DIR$/gui_ui.py" beforeDir="false" afterPath="$PROJECT_DIR$/gui_ui.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/gui_ui.py" beforeDir="false" afterPath="$PROJECT_DIR$/gui_ui.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/geometry_occ/kernel.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/geometry_occ/kernel.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/fluency/geometry_occ/kernel.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/geometry_occ/kernel.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/models/data_model.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/models/data_model.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/fluency/ui/dialogs.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/ui/dialogs.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/rendering/mitsuba_backend.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/rendering/mitsuba_backend.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/rendering/occ_renderer.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/rendering/occ_renderer.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/ui/main_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/ui/main_window.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/fluency/ui/main_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/ui/main_window.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fluency/ui/render_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/ui/render_window.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -72,6 +69,7 @@
"Python.side_fluency.executor": "Run", "Python.side_fluency.executor": "Run",
"Python.simple_mesh.executor": "Run", "Python.simple_mesh.executor": "Run",
"Python.sketch.executor": "Run", "Python.sketch.executor": "Run",
"Python.surface_modifier.executor": "Run",
"Python.vtk_widget.executor": "Run", "Python.vtk_widget.executor": "Run",
"Python.vulkan.executor": "Run", "Python.vulkan.executor": "Run",
"RunOnceActivity.OpenProjectViewOnStart": "true", "RunOnceActivity.OpenProjectViewOnStart": "true",
@@ -80,7 +78,7 @@
"RunOnceActivity.git.unshallow": "true", "RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true", "RunOnceActivity.typescript.service.memoryLimit.init": "true",
"codeWithMe.voiceChat.enabledByDefault": "false", "codeWithMe.voiceChat.enabledByDefault": "false",
"git-widget-placeholder": "feature/occ-migration", "git-widget-placeholder": "feature/surface-modifier-pyramid-patterns",
"last_opened_file_path": "/Volumes/Data_drive/Programming/fluency", "last_opened_file_path": "/Volumes/Data_drive/Programming/fluency",
"node.js.detected.package.eslint": "true", "node.js.detected.package.eslint": "true",
"node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.eslint": "(autodetect)",
@@ -442,7 +440,31 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1783976498520</updated> <updated>1783976498520</updated>
</task> </task>
<option name="localTasksCounter" value="41" /> <task id="LOCAL-00041" summary="Improved render previews">
<option name="closed" value="true" />
<created>1784408804224</created>
<option name="number" value="00041" />
<option name="presentableId" value="LOCAL-00041" />
<option name="project" value="LOCAL" />
<updated>1784408804224</updated>
</task>
<task id="LOCAL-00042" summary="- added contrain context menu&#10;- improved line pickability.">
<option name="closed" value="true" />
<created>1784452692776</created>
<option name="number" value="00042" />
<option name="presentableId" value="LOCAL-00042" />
<option name="project" value="LOCAL" />
<updated>1784452692776</updated>
</task>
<task id="LOCAL-00043" summary="- added contrain context menu&#10;- improved line pickability.">
<option name="closed" value="true" />
<created>1784470032428</created>
<option name="number" value="00043" />
<option name="presentableId" value="LOCAL-00043" />
<option name="project" value="LOCAL" />
<updated>1784470032428</updated>
</task>
<option name="localTasksCounter" value="44" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -463,8 +485,6 @@
<ignored-roots> <ignored-roots>
<path value="$PROJECT_DIR$/pythonProject" /> <path value="$PROJECT_DIR$/pythonProject" />
</ignored-roots> </ignored-roots>
<MESSAGE value="- Sketch projection partly works again :)" />
<MESSAGE value="- Added new componnt controls" />
<MESSAGE value="- changing compos for sketches works" /> <MESSAGE value="- changing compos for sketches works" />
<MESSAGE value="- changing compos including sketches and bodies" /> <MESSAGE value="- changing compos including sketches and bodies" />
<MESSAGE value="- Drawing bodys depending on the selected compo&#10;- Cut working&#10;- Edit sketch working" /> <MESSAGE value="- Drawing bodys depending on the selected compo&#10;- Cut working&#10;- Edit sketch working" />
@@ -488,6 +508,8 @@
<MESSAGE value="- added renderer" /> <MESSAGE value="- added renderer" />
<MESSAGE value="- added renderer&#10;- Added undo" /> <MESSAGE value="- added renderer&#10;- Added undo" />
<MESSAGE value="- Render improvements, camera plane, update" /> <MESSAGE value="- Render improvements, camera plane, update" />
<option name="LAST_COMMIT_MESSAGE" value="- Render improvements, camera plane, update" /> <MESSAGE value="Improved render previews" />
<MESSAGE value="- added contrain context menu&#10;- improved line pickability." />
<option name="LAST_COMMIT_MESSAGE" value="- added contrain context menu&#10;- improved line pickability." />
</component> </component>
</project> </project>
+36 -26
View File
@@ -501,6 +501,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QPushButton" name="pb_surface_mod">
<property name="toolTip">
<string>Apply surface modifier (pyramid pattern for grips)</string>
</property>
<property name="text">
<string>SrfMod</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -1098,32 +1108,6 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="0" column="3" rowspan="5">
<widget class="QGroupBox" name="gl_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="title">
<string>Model Viewer</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
</layout>
</widget>
</item>
<item row="4" column="4"> <item row="4" column="4">
<widget class="QGroupBox" name="groupBox_12"> <widget class="QGroupBox" name="groupBox_12">
<property name="sizePolicy"> <property name="sizePolicy">
@@ -1312,6 +1296,32 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="0" column="2" rowspan="5">
<widget class="QGroupBox" name="gl_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="title">
<string>Model Viewer</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
+29 -20
View File
@@ -289,6 +289,11 @@ class Ui_fluencyCAD(object):
self.gridLayout_3.addWidget(self.pb_moveop, 1, 1, 1, 1) self.gridLayout_3.addWidget(self.pb_moveop, 1, 1, 1, 1)
self.pb_surface_mod = QPushButton(self.groupBox)
self.pb_surface_mod.setObjectName(u"pb_surface_mod")
self.gridLayout_3.addWidget(self.pb_surface_mod, 3, 0, 1, 1)
self.gridLayout.addWidget(self.groupBox, 0, 4, 1, 1) self.gridLayout.addWidget(self.groupBox, 0, 4, 1, 1)
@@ -601,25 +606,6 @@ class Ui_fluencyCAD(object):
self.gridLayout.addWidget(self.InputTab, 0, 1, 5, 1) self.gridLayout.addWidget(self.InputTab, 0, 1, 5, 1)
self.gl_box = QGroupBox(self.centralwidget)
self.gl_box.setObjectName(u"gl_box")
sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy5.setHorizontalStretch(0)
sizePolicy5.setVerticalStretch(4)
sizePolicy5.setHeightForWidth(self.gl_box.sizePolicy().hasHeightForWidth())
self.gl_box.setSizePolicy(sizePolicy5)
font = QFont()
font.setPointSize(12)
self.gl_box.setFont(font)
self.horizontalLayout_4 = QHBoxLayout(self.gl_box)
#ifndef Q_OS_MAC
self.horizontalLayout_4.setSpacing(-1)
#endif
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.horizontalLayout_4.setContentsMargins(12, -1, -1, -1)
self.gridLayout.addWidget(self.gl_box, 0, 3, 5, 1)
self.groupBox_12 = QGroupBox(self.centralwidget) self.groupBox_12 = QGroupBox(self.centralwidget)
self.groupBox_12.setObjectName(u"groupBox_12") self.groupBox_12.setObjectName(u"groupBox_12")
sizePolicy2.setHeightForWidth(self.groupBox_12.sizePolicy().hasHeightForWidth()) sizePolicy2.setHeightForWidth(self.groupBox_12.sizePolicy().hasHeightForWidth())
@@ -706,6 +692,25 @@ class Ui_fluencyCAD(object):
self.gridLayout.addWidget(self.groupBox_10, 3, 4, 1, 1) self.gridLayout.addWidget(self.groupBox_10, 3, 4, 1, 1)
self.gl_box = QGroupBox(self.centralwidget)
self.gl_box.setObjectName(u"gl_box")
sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy5.setHorizontalStretch(0)
sizePolicy5.setVerticalStretch(4)
sizePolicy5.setHeightForWidth(self.gl_box.sizePolicy().hasHeightForWidth())
self.gl_box.setSizePolicy(sizePolicy5)
font = QFont()
font.setPointSize(12)
self.gl_box.setFont(font)
self.horizontalLayout_4 = QHBoxLayout(self.gl_box)
#ifndef Q_OS_MAC
self.horizontalLayout_4.setSpacing(-1)
#endif
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.horizontalLayout_4.setContentsMargins(12, -1, -1, -1)
self.gridLayout.addWidget(self.gl_box, 0, 2, 5, 1)
fluencyCAD.setCentralWidget(self.centralwidget) fluencyCAD.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(fluencyCAD) self.menubar = QMenuBar(fluencyCAD)
self.menubar.setObjectName(u"menubar") self.menubar.setObjectName(u"menubar")
@@ -863,6 +868,10 @@ class Ui_fluencyCAD(object):
self.pb_cutop.setText(QCoreApplication.translate("fluencyCAD", u"Cut", None)) self.pb_cutop.setText(QCoreApplication.translate("fluencyCAD", u"Cut", None))
self.pb_combop.setText(QCoreApplication.translate("fluencyCAD", u"Comb", None)) self.pb_combop.setText(QCoreApplication.translate("fluencyCAD", u"Comb", None))
self.pb_moveop.setText(QCoreApplication.translate("fluencyCAD", u"Mve", None)) self.pb_moveop.setText(QCoreApplication.translate("fluencyCAD", u"Mve", None))
#if QT_CONFIG(tooltip)
self.pb_surface_mod.setToolTip(QCoreApplication.translate("fluencyCAD", u"Apply surface modifier (pyramid pattern for grips)", None))
#endif // QT_CONFIG(tooltip)
self.pb_surface_mod.setText(QCoreApplication.translate("fluencyCAD", u"SrfMod", None))
self.compo_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Components", None)) self.compo_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Components", None))
self.compo_tool_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Component Tools", None)) self.compo_tool_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Component Tools", None))
self.pb_new_compo.setText(QCoreApplication.translate("fluencyCAD", u"New", None)) self.pb_new_compo.setText(QCoreApplication.translate("fluencyCAD", u"New", None))
@@ -902,7 +911,6 @@ class Ui_fluencyCAD(object):
self.pb_apply_code.setText(QCoreApplication.translate("fluencyCAD", u"Apply Code", None)) self.pb_apply_code.setText(QCoreApplication.translate("fluencyCAD", u"Apply Code", None))
self.pushButton.setText(QCoreApplication.translate("fluencyCAD", u"Delete Code", None)) self.pushButton.setText(QCoreApplication.translate("fluencyCAD", u"Delete Code", None))
self.InputTab.setTabText(self.InputTab.indexOf(self.code_tab), QCoreApplication.translate("fluencyCAD", u"Code", None)) self.InputTab.setTabText(self.InputTab.indexOf(self.code_tab), QCoreApplication.translate("fluencyCAD", u"Code", None))
self.gl_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Model Viewer", None))
self.groupBox_12.setTitle(QCoreApplication.translate("fluencyCAD", u"Component Connections", None)) self.groupBox_12.setTitle(QCoreApplication.translate("fluencyCAD", u"Component Connections", None))
self.groupBox_13.setTitle(QCoreApplication.translate("fluencyCAD", u"Tools", None)) self.groupBox_13.setTitle(QCoreApplication.translate("fluencyCAD", u"Tools", None))
self.pb_del_connection.setText(QCoreApplication.translate("fluencyCAD", u"Del", None)) self.pb_del_connection.setText(QCoreApplication.translate("fluencyCAD", u"Del", None))
@@ -913,6 +921,7 @@ class Ui_fluencyCAD(object):
self.pb_del_body.setText(QCoreApplication.translate("fluencyCAD", u"Del", None)) self.pb_del_body.setText(QCoreApplication.translate("fluencyCAD", u"Del", None))
self.pb_update_body.setText(QCoreApplication.translate("fluencyCAD", u"Upd", None)) self.pb_update_body.setText(QCoreApplication.translate("fluencyCAD", u"Upd", None))
self.pb_edt_sktch_3.setText(QCoreApplication.translate("fluencyCAD", u"Nothing", None)) self.pb_edt_sktch_3.setText(QCoreApplication.translate("fluencyCAD", u"Nothing", None))
self.gl_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Model Viewer", None))
self.menuFile.setTitle(QCoreApplication.translate("fluencyCAD", u"File", None)) self.menuFile.setTitle(QCoreApplication.translate("fluencyCAD", u"File", None))
self.menuSettings.setTitle(QCoreApplication.translate("fluencyCAD", u"Settings", None)) self.menuSettings.setTitle(QCoreApplication.translate("fluencyCAD", u"Settings", None))
# retranslateUi # retranslateUi
+64
View File
@@ -780,3 +780,67 @@ class OCGeometryKernel(GeometryKernel):
cg = props.CentreOfMass() cg = props.CentreOfMass()
return Point3D(cg.X(), cg.Y(), cg.Z()) return Point3D(cg.X(), cg.Y(), cg.Z())
def apply_surface_modifier(
self,
body_geometry: GeometryObject,
modifier_type: str = 'pyramid',
height: float = 2.0,
radius: float = 3.0,
spacing: float = 8.0,
num_rings: int = 3,
) -> Optional[GeometryObject]:
"""Apply a surface modifier to a body geometry.
Args:
body_geometry: The OCCGeometryObject to modify
modifier_type: Type of modifier ('pyramid', 'bump')
height: Height/depth of the pattern features
radius: Base radius of pattern features
spacing: Distance between pattern features
num_rings: Number of concentric rings
Returns:
Modified geometry, or None on failure
"""
from fluency.geometry_occ.surface_modifier import SurfaceModifier
shape = self._get_shape(body_geometry)
if shape is None:
logger.error("No geometry found in body")
return None
modifier = SurfaceModifier()
try:
if modifier_type == 'pyramid':
result_shape = modifier.apply_pyramid_pattern(
shape,
pyramid_height=height,
base_radius=radius,
spacing=spacing,
num_rings=num_rings,
direction=(0, 0, 1),
)
elif modifier_type == 'bump':
result_shape = modifier.apply_bump_pattern(
shape,
bump_height=height,
bump_radius=radius,
spacing=spacing,
num_rings=num_rings,
)
else:
logger.error(f"Unknown modifier type: {modifier_type}")
return None
if result_shape is None:
logger.error("Surface modifier application failed")
return None
# Return the modified shape wrapped in OCCGeometryObject
return OCCGeometryObject(result_shape)
except Exception as e:
logger.error(f"Error applying surface modifier: {e}", exc_info=True)
return None
+90 -8
View File
@@ -4,16 +4,12 @@ from __future__ import annotations
import logging import logging
import math import math
from typing import Any, Dict, List, Optional, Tuple from typing import Tuple
from PySide6.QtCore import Qt, QPoint, QPointF
from PySide6.QtGui import QColor, QFont, QKeySequence
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QButtonGroup, QButtonGroup,
QCheckBox, QCheckBox,
QComboBox,
QDialog, QDialog,
QDialogButtonBox,
QDoubleSpinBox, QDoubleSpinBox,
QFormLayout, QFormLayout,
QFrame, QFrame,
@@ -24,7 +20,6 @@ from PySide6.QtWidgets import (
QPushButton, QPushButton,
QRadioButton, QRadioButton,
QVBoxLayout, QVBoxLayout,
QWidget,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -265,6 +260,95 @@ class OffsetDialog(QDialog):
return (self.distance_input.value(), self.inward_checkbox.isChecked()) return (self.distance_input.value(), self.inward_checkbox.isChecked())
class SurfaceModifierDialog(QDialog):
"""Dialog for surface modifier settings."""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Surface Modifier")
self.setMinimumWidth(400)
layout = QVBoxLayout(self)
# Pattern type selection
pattern_layout = QGridLayout()
self._pattern_group = QButtonGroup(self)
patterns = [
("Pyramid Pattern", "pyramid"),
("Bump Pattern", "bump"),
]
for idx, (label, pattern_type) in enumerate(patterns):
btn = QRadioButton(label)
btn.setChecked(idx == 0)
self._pattern_group.addButton(btn, idx)
btn.pattern_type = pattern_type
pattern_layout.addWidget(btn, 0, idx)
layout.addLayout(pattern_layout)
# Parameters for pyramid/bump patterns
param_layout = QFormLayout()
self.height_input = QDoubleSpinBox()
self.height_input.setDecimals(2)
self.height_input.setRange(0.1, 50.0)
self.height_input.setValue(2.0)
self.height_input.setSuffix(" mm")
param_layout.addRow("Height:", self.height_input)
self.radius_input = QDoubleSpinBox()
self.radius_input.setDecimals(2)
self.radius_input.setRange(0.5, 20.0)
self.radius_input.setValue(3.0)
self.radius_input.setSuffix(" mm")
param_layout.addRow("Base Radius:", self.radius_input)
self.spacing_input = QDoubleSpinBox()
self.spacing_input.setDecimals(1)
self.spacing_input.setRange(1.0, 50.0)
self.spacing_input.setValue(8.0)
self.spacing_input.setSuffix(" mm")
param_layout.addRow("Spacing:", self.spacing_input)
layout.addLayout(param_layout)
# Number of rings
self.rings_input = QSpinBox()
self.rings_input.setMinimum(1)
self.rings_input.setMaximum(20)
self.rings_input.setValue(3)
param_layout.addRow("Number of Rings:", self.rings_input)
line = QFrame()
line.setFrameShape(QFrame.HLine)
layout.addWidget(line)
button_layout = QHBoxLayout()
ok_button = QPushButton("Apply")
ok_button.clicked.connect(self.accept)
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(self.reject)
button_layout.addWidget(ok_button)
button_layout.addWidget(cancel_button)
layout.addLayout(button_layout)
def get_values(self) -> dict:
"""Return the modifier settings."""
pattern_type = None
for btn in self._pattern_group.buttons():
if btn.isChecked():
pattern_type = getattr(btn, 'pattern_type', None)
break
return {
'type': pattern_type or 'pyramid',
'height': self.height_input.value(),
'radius': self.radius_input.value(),
'spacing': self.spacing_input.value(),
'num_rings': self.rings_input.value(),
}
class WorkplaneOrientationDialog(QDialog): class WorkplaneOrientationDialog(QDialog):
"""Modal dialog to choose the orientation of a new workplane. """Modal dialog to choose the orientation of a new workplane.
@@ -404,7 +488,6 @@ class WorkplaneOrientationDialog(QDialog):
def _on_ok(self): def _on_ok(self):
"""Compute the final orientation and accept.""" """Compute the final orientation and accept."""
import numpy as np import numpy as np
import math
if self._custom_radio.isChecked(): if self._custom_radio.isChecked():
# Custom: start from XY normal and rotate by the two angles. # Custom: start from XY normal and rotate by the two angles.
@@ -454,7 +537,6 @@ class WorkplaneOrientationDialog(QDialog):
whether called before or after ``_on_ok``. whether called before or after ``_on_ok``.
""" """
import numpy as np import numpy as np
import math
if self._custom_radio.isChecked(): if self._custom_radio.isChecked():
ax = math.radians(self._angle_x.value()) ax = math.radians(self._angle_x.value())
+56 -3
View File
@@ -717,6 +717,8 @@ class MainWindow(QMainWindow):
self._btn_move = ui.pb_moveop self._btn_move = ui.pb_moveop
self._btn_revolve = ui.pb_revop self._btn_revolve = ui.pb_revop
self._btn_array = ui.pb_arrayop self._btn_array = ui.pb_arrayop
# ── Surface Modifier ──
self._btn_surface_mod = ui.pb_surface_mod
# ── Export ── # ── Export ──
self._btn_export_stl = ui.pushButton_2 self._btn_export_stl = ui.pushButton_2
self._btn_export_step = ui.pb_export_step self._btn_export_step = ui.pb_export_step
@@ -772,14 +774,14 @@ class MainWindow(QMainWindow):
if panel == "viewer": if panel == "viewer":
# Viewer 2/3, sketch 1/3 — more room for 3D work, sketch stays visible. # Viewer 2/3, sketch 1/3 — more room for 3D work, sketch stays visible.
self._grid.setColumnStretch(1, 1) self._grid.setColumnStretch(1, 1)
self._grid.setColumnStretch(2, 2) self._grid.setColumnStretch(3, 2)
elif panel == "sketch": elif panel == "sketch":
# Sketch 2/3, viewer 1/3 — comfortable sketching, 3D stays visible. # Sketch 2/3, viewer 1/3 — comfortable sketching, 3D stays visible.
self._grid.setColumnStretch(1, 2) self._grid.setColumnStretch(1, 2)
self._grid.setColumnStretch(2, 1) self._grid.setColumnStretch(3, 1)
else: # equal else: # equal
self._grid.setColumnStretch(1, 1) self._grid.setColumnStretch(1, 1)
self._grid.setColumnStretch(2, 1) self._grid.setColumnStretch(3, 1)
logger.info(f"Panel focus -> {self._panel_focus}") logger.info(f"Panel focus -> {self._panel_focus}")
def keyPressEvent(self, event): def keyPressEvent(self, event):
@@ -832,6 +834,7 @@ class MainWindow(QMainWindow):
self._btn_cut.clicked.connect(self._boolean_cut) self._btn_cut.clicked.connect(self._boolean_cut)
self._btn_combine.clicked.connect(self._boolean_union) self._btn_combine.clicked.connect(self._boolean_union)
self._btn_revolve.clicked.connect(self._revolve_sketch) self._btn_revolve.clicked.connect(self._revolve_sketch)
self._btn_surface_mod.clicked.connect(self._open_surface_modifier_dialog)
self._btn_add_sketch.clicked.connect(self._add_sketch_to_component) self._btn_add_sketch.clicked.connect(self._add_sketch_to_component)
self._btn_edit_sketch.clicked.connect(self._edit_sketch) self._btn_edit_sketch.clicked.connect(self._edit_sketch)
@@ -3994,6 +3997,56 @@ class MainWindow(QMainWindow):
logger.exception(f"Boolean union failed: {e}") logger.exception(f"Boolean union failed: {e}")
QMessageBox.critical(self, "Error", f"Boolean union failed: {e}") QMessageBox.critical(self, "Error", f"Boolean union failed: {e}")
def _open_surface_modifier_dialog(self):
"""Open the surface modifier dialog and apply to selected body."""
if not self._current_component or not self._selected_body:
QMessageBox.warning(self, "No Selection", "Please select a body first")
return
# Create and show the dialog
dialog = SurfaceModifierDialog(self)
accepted = dialog.exec()
if not accepted:
logger.info("Surface modifier dialog cancelled")
return
# Get the settings from the dialog
settings = dialog.get_values()
logger.info(f"Surface modifier settings: {settings}")
try:
# Apply the surface modifier to the selected body
result_geom = self._kernel.apply_surface_modifier(
self._selected_body.geometry,
**settings
)
if result_geom is not None:
# Update the body geometry
self._selected_body.geometry = result_geom
self._mark_dirty()
logger.debug("Adding shape to OCC viewer")
shape = self._kernel._get_shape(result_geom)
self._selected_body.render_object = self._viewer_3d.show_shape(
shape, self._selected_body.color, self._selected_body.name
)
logger.info(f"Render object: {self._selected_body.render_object}")
self._refresh_lists()
self._update_component_thumbnail(self._get_active_component_index())
self._viewer_3d.fit_camera()
logger.info("Surface modifier applied successfully")
else:
QMessageBox.warning(
self, "Error", "Failed to apply surface modifier"
)
except Exception as e:
logger.exception(f"Surface modifier failed: {e}")
QMessageBox.critical(self, "Error", f"Surface modifier failed: {e}")
def _delete_body(self): def _delete_body(self):
selected = self._body_list.currentItem() selected = self._body_list.currentItem()
if not selected or not self._current_component: if not selected or not self._current_component: