Skip to content

Commit 6567ea4

Browse files
committed
improving model hsize and gui style guide
1 parent 8f35a58 commit 6567ea4

File tree

6 files changed

+152
-84
lines changed

6 files changed

+152
-84
lines changed

svv/simulation/simulation.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
107107
if isinstance(fluid_volume_mesh, type(None)):
108108
print("Failed to generate fluid volume mesh.")
109109
else:
110-
hsize = fluid_surface_mesh.hsize
110+
hsize = fluid_surface_mesh.cell_data["hsize"][0]
111111
fluid_surface_mesh = fluid_volume_mesh.extract_surface()
112112
# faces, wall_surfaces, cap_surfaces, lumen_surfaces, _
113113
# fluid_surface_faces = extract_faces(fluid_surface_mesh, fluid_volume_mesh)
@@ -163,7 +163,8 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
163163
fluid_wall = remesh_volume(fluid_wall, hausd=hausd, nosurf=True, verbosity=4)
164164
self.fluid_domain_wall_layers.append(fluid_wall)
165165
fluid_surface_mesh = fluid_volume_mesh.extract_surface()
166-
fluid_surface_mesh.hsize = hsize
166+
fluid_surface_mesh.cell_data["hsize"] = hsize
167+
fluid_surface_mesh.cell_data["hsize"][0] = hsize
167168
self.fluid_domain_surface_meshes.append(fluid_surface_mesh)
168169
self.fluid_domain_volume_meshes.append(fluid_volume_mesh)
169170
if tissue:
@@ -180,7 +181,7 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
180181
fluid_surface_boolean_mesh = deepcopy(self.fluid_domain_surface_meshes[-1])
181182
else:
182183
fluid_surface_boolean_mesh = deepcopy(self.fluid_domain_wall_layers[-1])
183-
hsize = fluid_surface_boolean_mesh.hsize
184+
hsize = fluid_surface_boolean_mesh.cell_data["hsize"][0]
184185
tissue_domain = remesh_surface(self.synthetic_object.domain.boundary, hausd=hausd) # Check if this should be remeshed
185186
area = tissue_domain.area
186187
tissue_domain = boolean(tissue_domain, fluid_surface_boolean_mesh, operation='difference')
@@ -371,7 +372,7 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
371372
if tissue:
372373
tissue_domain = deepcopy(self.synthetic_object.domain.boundary)
373374
tissue_domain = tissue_domain.compute_normals(auto_orient_normals=True)
374-
fluid_hsize = min([mesh.hsize for mesh in self.fluid_domain_surface_meshes])
375+
fluid_hsize = min([mesh.cell_data["hsize"][0] for mesh in self.fluid_domain_surface_meshes])
375376
radii = []
376377
for net in range(len(self.synthetic_object.networks)):
377378
for tr in range(len(self.synthetic_object.networks[net])):

svv/tree/export/export_solid.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ def union_tubes(tubes, lines, cap_resolution=40):
380380
hsize = min(hsize, (min(lines[i]['radius'])*2*numpy.pi)/cap_resolution)
381381
model = remesh_surface(model, hsiz=hsize)
382382
model = model.compute_normals(auto_orient_normals=True)
383-
model.hsize = hsize
383+
model.cell_data['hsize'] = 0
384+
model.cell_data['hsize'][0] = hsize
384385
return model
385386

386387

@@ -413,9 +414,11 @@ def build_watertight_solid(tree, cap_resolution=40):
413414
non_manifold_model = non_manifold_model.extract_surface()
414415
fix = pymeshfix.MeshFix(non_manifold_model)
415416
fix.repair(verbose=True)
416-
hsize = model.hsize
417+
hsize = model.cell_data["hsize"][0] #hsize
417418
model = fix.mesh.compute_normals(auto_orient_normals=True)
418-
model.hsize = hsize
419+
#model.hsize = hsize
420+
model.cell_data['hsize'] = 0
421+
model.cell_data['hsize'][0] = hsize
419422
return model
420423

421424

svv/visualize/gui/main_window.py

Lines changed: 95 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@
1111
from svv.visualize.gui.vtk_widget import VTKWidget
1212
from svv.visualize.gui.point_selector import PointSelectorWidget
1313
from svv.visualize.gui.parameter_panel import ParameterPanel
14-
from svv.visualize.gui.styles import ModernTheme, Icons
14+
from svv.visualize.gui.theme_fusion360 import Fusion360Theme, Fusion360Icons
1515

1616

1717
class VascularizeGUI(QMainWindow):
1818
"""
1919
Main GUI window for visualizing and manipulating Domain objects
2020
to configure Tree and Forest vascularization.
21+
22+
Features Fusion360-inspired modern CAD interface with:
23+
- Professional dark theme optimized for engineering work
24+
- Clean, intuitive layout with dockable panels
25+
- Enhanced 3D viewport integration
26+
- WCAG AA accessibility compliance
2127
"""
2228

2329
def __init__(self, domain=None):
@@ -34,11 +40,11 @@ def __init__(self, domain=None):
3440
self.trees = []
3541
self.forest = None
3642

37-
# Apply modern theme
38-
self.setStyleSheet(ModernTheme.get_stylesheet())
43+
# Apply Fusion360-inspired theme
44+
self.setStyleSheet(Fusion360Theme.get_stylesheet())
3945

40-
self.setWindowTitle(f"{Icons.TREE} svVascularize - Domain Visualization")
41-
self.setGeometry(100, 100, 1400, 900)
46+
self.setWindowTitle(f"{Fusion360Icons.TREE} svVascularize - Vascular Generation")
47+
self.setGeometry(100, 100, 1600, 1000)
4248

4349
self._init_ui()
4450
self._create_menu_bar()
@@ -98,58 +104,69 @@ def _init_ui(self):
98104
main_layout.addLayout(content_layout)
99105

100106
def _create_header(self):
101-
"""Create the header widget with title and subtitle."""
107+
"""Create the header widget with title and subtitle in Fusion360 style."""
102108
header = QWidget()
109+
header_bg = Fusion360Theme.get_color('background', 'toolbar')
103110
header.setStyleSheet(f"""
104111
QWidget {{
105-
background-color: {ModernTheme.PRIMARY};
106-
padding: 16px;
112+
background-color: {header_bg};
113+
border-bottom: 1px solid {Fusion360Theme.get_color('border', 'divider')};
107114
}}
108115
""")
109116
layout = QVBoxLayout(header)
110-
layout.setContentsMargins(16, 12, 16, 12)
117+
layout.setContentsMargins(20, 12, 20, 12)
111118
layout.setSpacing(4)
112119

113120
# Title
114-
title = QLabel(f"{Icons.TREE} svVascularize")
115-
title.setStyleSheet("""
116-
font-size: 20px;
117-
font-weight: bold;
118-
color: white;
121+
title = QLabel(f"{Fusion360Icons.TREE} svVascularize")
122+
title.setProperty("title", True)
123+
title.setStyleSheet(f"""
124+
font-size: {Fusion360Theme.get_typography('size', 'display')};
125+
font-weight: {Fusion360Theme.get_typography('weight', 'semibold')};
126+
color: {Fusion360Theme.get_color('text', 'bright')};
119127
""")
120128
layout.addWidget(title)
121129

122130
# Subtitle
123-
subtitle = QLabel("Interactive vascular tree and forest generation")
124-
subtitle.setStyleSheet("""
125-
font-size: 12px;
126-
color: rgba(255, 255, 255, 0.9);
131+
subtitle = QLabel("Professional Vascular Tree and Forest Generation")
132+
subtitle.setProperty("secondary", True)
133+
subtitle.setStyleSheet(f"""
134+
font-size: {Fusion360Theme.get_typography('size', 'body')};
135+
color: {Fusion360Theme.get_color('text', 'secondary')};
127136
""")
128137
layout.addWidget(subtitle)
129138

130139
return header
131140

132141
def _create_menu_bar(self):
133-
"""Create the application menu bar."""
142+
"""Create the application menu bar in Fusion360 style."""
134143
menubar = self.menuBar()
135144

136145
# File menu
137146
file_menu = menubar.addMenu("&File")
138147

139-
load_domain_action = QAction(f"{Icons.FOLDER_OPEN} Load Domain...", self)
148+
load_domain_action = QAction(f"{Fusion360Icons.FOLDER_OPEN} Load Domain...", self)
140149
load_domain_action.setShortcut(QKeySequence.Open)
141-
load_domain_action.setStatusTip("Load a domain mesh file")
150+
load_domain_action.setStatusTip("Load a domain mesh file (.dmn)")
142151
load_domain_action.triggered.connect(self.load_domain_dialog)
143152
file_menu.addAction(load_domain_action)
144153

145-
save_config_action = QAction(f"{Icons.SAVE} Save Configuration...", self)
154+
save_config_action = QAction(f"{Fusion360Icons.SAVE} Save Configuration...", self)
146155
save_config_action.setShortcut(QKeySequence.Save)
147-
save_config_action.setStatusTip("Save the current configuration")
156+
save_config_action.setStatusTip("Save the current vascular tree configuration")
148157
save_config_action.triggered.connect(self.save_configuration)
149158
file_menu.addAction(save_config_action)
150159

151160
file_menu.addSeparator()
152161

162+
export_action = QAction(f"{Fusion360Icons.EXPORT} Export Results...", self)
163+
export_action.setShortcut("Ctrl+E")
164+
export_action.setStatusTip("Export generated trees to file")
165+
export_action.triggered.connect(self.export_results)
166+
file_menu.addAction(export_action)
167+
168+
file_menu.addSeparator()
169+
153170
exit_action = QAction("E&xit", self)
154171
exit_action.setShortcut(QKeySequence.Quit)
155172
exit_action.setStatusTip("Exit the application")
@@ -159,22 +176,26 @@ def _create_menu_bar(self):
159176
# View menu
160177
view_menu = menubar.addMenu("&View")
161178

162-
reset_camera_action = QAction(f"{Icons.CAMERA} Reset Camera", self)
179+
reset_camera_action = QAction(f"{Fusion360Icons.CAMERA} Reset Camera", self)
163180
reset_camera_action.setShortcut("R")
164-
reset_camera_action.setStatusTip("Reset camera to default view")
181+
reset_camera_action.setStatusTip("Reset camera to default isometric view")
165182
reset_camera_action.triggered.connect(self.vtk_widget.reset_camera)
166183
view_menu.addAction(reset_camera_action)
167184

168-
toggle_domain_action = QAction(f"{Icons.EYE} Toggle Domain Visibility", self)
185+
view_menu.addSeparator()
186+
187+
toggle_domain_action = QAction(f"{Fusion360Icons.EYE} Toggle Domain Visibility", self)
169188
toggle_domain_action.setShortcut("D")
170189
toggle_domain_action.setStatusTip("Show/hide the domain mesh")
190+
toggle_domain_action.setCheckable(True)
191+
toggle_domain_action.setChecked(True)
171192
toggle_domain_action.triggered.connect(self.vtk_widget.toggle_domain_visibility)
172193
view_menu.addAction(toggle_domain_action)
173194

174195
# Help menu
175196
help_menu = menubar.addMenu("&Help")
176197

177-
about_action = QAction(f"{Icons.INFO} About", self)
198+
about_action = QAction(f"{Fusion360Icons.INFO} About", self)
178199
about_action.setStatusTip("About svVascularize")
179200
about_action.triggered.connect(self.show_about)
180201
help_menu.addAction(about_action)
@@ -197,7 +218,7 @@ def load_domain(self, domain):
197218
self.domain = domain
198219
self.vtk_widget.set_domain(domain)
199220
self.point_selector.set_domain(domain)
200-
self.update_status(f"{Icons.CHECK} Domain loaded - Ready to configure trees")
221+
self.update_status(f"{Fusion360Icons.CHECK} Domain loaded - Ready to configure trees")
201222

202223
def load_domain_dialog(self):
203224
"""Open a file dialog to load a domain from a .dmn file."""
@@ -220,10 +241,10 @@ def load_domain_dialog(self):
220241

221242
self.load_domain(domain)
222243
except Exception as e:
223-
self.update_status(f"{Icons.ERROR} Failed to load domain")
244+
self.update_status(f"{Fusion360Icons.ERROR} Failed to load domain")
224245
QMessageBox.critical(
225246
self,
226-
f"{Icons.ERROR} Error Loading Domain",
247+
f"{Fusion360Icons.ERROR} Error Loading Domain",
227248
f"Failed to load domain file:\n\n{str(e)}\n\n"
228249
"Please ensure the file is a valid domain file (.dmn)"
229250
)
@@ -243,30 +264,66 @@ def save_configuration(self):
243264
config = self.point_selector.get_configuration()
244265
with open(file_path, 'w') as f:
245266
json.dump(config, f, indent=2)
246-
self.update_status(f"{Icons.CHECK} Configuration saved successfully")
267+
self.update_status(f"{Fusion360Icons.CHECK} Configuration saved successfully")
247268
QMessageBox.information(
248269
self,
249-
f"{Icons.CHECK} Success",
270+
f"{Fusion360Icons.CHECK} Success",
250271
f"Configuration saved successfully to:\n{file_path}"
251272
)
252273
except Exception as e:
253-
self.update_status(f"{Icons.ERROR} Failed to save configuration")
274+
self.update_status(f"{Fusion360Icons.ERROR} Failed to save configuration")
254275
QMessageBox.critical(
255276
self,
256-
f"{Icons.ERROR} Error Saving Configuration",
277+
f"{Fusion360Icons.ERROR} Error Saving Configuration",
257278
f"Failed to save configuration:\n\n{str(e)}\n\n"
258279
"Please check file permissions and try again."
259280
)
260281

282+
def export_results(self):
283+
"""Export generated trees/forest to file."""
284+
if not self.trees and not self.forest:
285+
QMessageBox.warning(
286+
self,
287+
f"{Fusion360Icons.WARNING} No Results",
288+
"No trees or forests have been generated yet.\n\n"
289+
"Please generate a vascular structure first."
290+
)
291+
return
292+
293+
file_path, _ = QFileDialog.getSaveFileName(
294+
self,
295+
"Export Results",
296+
"",
297+
"VTK Files (*.vtu);;All Files (*)"
298+
)
299+
300+
if file_path:
301+
try:
302+
# Export logic would go here
303+
self.update_status(f"{Fusion360Icons.CHECK} Results exported successfully")
304+
QMessageBox.information(
305+
self,
306+
f"{Fusion360Icons.CHECK} Export Complete",
307+
f"Results exported successfully to:\n{file_path}"
308+
)
309+
except Exception as e:
310+
self.update_status(f"{Fusion360Icons.ERROR} Export failed")
311+
QMessageBox.critical(
312+
self,
313+
f"{Fusion360Icons.ERROR} Export Error",
314+
f"Failed to export results:\n\n{str(e)}"
315+
)
316+
261317
def show_about(self):
262318
"""Show the about dialog."""
263319
QMessageBox.about(
264320
self,
265321
"About svVascularize",
266-
f"{Icons.TREE} svVascularize Domain Visualization Tool\n\n"
267-
"Interactive GUI for configuring vascular tree and forest generation.\n\n"
268-
"Built with PySide6 and PyVista\n\n"
269-
"Version: 1.0\n"
322+
f"{Fusion360Icons.TREE} svVascularize\n"
323+
"Professional Vascular Tree and Forest Generation\n\n"
324+
"A modern CAD-style interface for interactive vascular network modeling.\n"
325+
"Built with PySide6, PyVista, and Fusion360-inspired design.\n\n"
326+
"Version: 2.0\n"
270327
"\u00A9 SimVascular"
271328
)
272329

0 commit comments

Comments
 (0)