pyvcad-rendering 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ from .render import *
2
+ from .export import *
@@ -0,0 +1,13 @@
1
+ from .gcvf_inkjet_ui import GCVFInkjetPanel
2
+ from .gcvf_inkjet_progress_ui import GCVFInkjetProgressPanel
3
+ from .direct_material_inkjet_ui import DirectMaterialInkjetPanel
4
+ from .direct_material_inkjet_progress_ui import DirectMaterialInkjetProgressPanel
5
+ from .color_inkjet_ui import ColorInkjetPanel
6
+ from .myerson_inkjet_ui import MyersonInkjetPanel
7
+ from .myerson_inkjet_progress_ui import MyersonInkjetProgressPanel
8
+ from .meshes_ui import MeshesPanel
9
+ from .meshes_progress_ui import MeshesProgressPanel
10
+ from .finite_element_mesh_ui import FiniteElementMeshPanel
11
+ from .finite_element_mesh_progress_ui import FiniteElementMeshProgressPanel
12
+ from .vat_photo_ui import VatPhotoPanel
13
+ from .vat_photo_progress_ui import VatPhotoProgressPanel
@@ -0,0 +1,9 @@
1
+ import wx
2
+
3
+ class ColorInkjetPanel(wx.Panel):
4
+ def __init__(self, parent):
5
+ super().__init__(parent)
6
+ sizer = wx.BoxSizer(wx.VERTICAL)
7
+ sizer.Add(wx.StaticText(self, label="Color Inkjet (PNG Stack)"), 0, wx.ALL, 5)
8
+ self.SetSizer(sizer)
9
+
@@ -0,0 +1,137 @@
1
+ import wx
2
+ import wx.lib.newevent
3
+ import threading
4
+ import time
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import numpy as np
9
+ from wx.lib.newevent import NewEvent
10
+ import pyvcad as pv
11
+ import pyvcad_compilers as pvc
12
+
13
+ # Custom event for progress updates
14
+ UpdateProgressEvent, EVT_UPDATE_PROGRESS = NewEvent()
15
+
16
+ class DirectMaterialCompilerWorker(threading.Thread):
17
+ def __init__(self, parent, root, voxel_size, material_defs, output_directory, file_prefix, liquid_keepout, liquid_keepout_distance):
18
+ super().__init__()
19
+ self.parent = parent
20
+ self.root = root
21
+ self.voxel_size = voxel_size
22
+ self.material_defs = material_defs
23
+ self.output_directory = output_directory
24
+ self.file_prefix = file_prefix
25
+ self.liquid_keepout = liquid_keepout
26
+ self.liquid_keepout_distance = liquid_keepout_distance
27
+ self._running = True
28
+
29
+ def run(self):
30
+ try:
31
+ voxel_size_vec3 = pv.Vec3(self.voxel_size[0], self.voxel_size[1], self.voxel_size[2])
32
+ compiler = pvc.DirectMaterialCompiler(
33
+ root=self.root,
34
+ voxel_size=voxel_size_vec3,
35
+ material_defs=self.material_defs,
36
+ output_directory=self.output_directory,
37
+ file_prefix=self.file_prefix,
38
+ liquid_keep_out=self.liquid_keepout,
39
+ liquid_keep_out_distance=self.liquid_keepout_distance
40
+ )
41
+
42
+ def progress_callback(progress):
43
+ if not self._running:
44
+ # This is a simplistic way to stop; the compiler might not support interruption.
45
+ raise Exception("Export cancelled")
46
+ evt = UpdateProgressEvent(progress=int(progress * 100), error=None, finished=False)
47
+ wx.PostEvent(self.parent, evt)
48
+
49
+ compiler.setProgressCallback(progress_callback)
50
+ compiler.compile()
51
+
52
+ if self._running:
53
+ evt = UpdateProgressEvent(progress=100, error=None, finished=True)
54
+ wx.PostEvent(self.parent, evt)
55
+
56
+ except Exception as e:
57
+ if self._running:
58
+ evt = UpdateProgressEvent(error=str(e), finished=True)
59
+ wx.PostEvent(self.parent, evt)
60
+
61
+ def stop(self):
62
+ self._running = False
63
+
64
+ class DirectMaterialInkjetProgressPanel(wx.Panel):
65
+ def __init__(self, parent, export_options):
66
+ super().__init__(parent)
67
+ self.export_options = export_options
68
+ self.worker = None
69
+ self.start_time = None
70
+
71
+ sizer = wx.BoxSizer(wx.VERTICAL)
72
+ sizer.Add(wx.StaticText(self, label="Exporting PNG Stack..."), 0, wx.ALL, 5)
73
+
74
+ self.progress_bar = wx.Gauge(self, range=100, style=wx.GA_HORIZONTAL)
75
+ sizer.Add(self.progress_bar, 0, wx.ALL | wx.EXPAND, 5)
76
+
77
+ self.elapsed_time_label = wx.StaticText(self, label="Elapsed time: 00:00:00")
78
+ sizer.Add(self.elapsed_time_label, 0, wx.ALL, 5)
79
+
80
+ self.time_estimate_label = wx.StaticText(self, label="Estimated time remaining: N/A")
81
+ sizer.Add(self.time_estimate_label, 0, wx.ALL, 5)
82
+
83
+ self.SetSizer(sizer)
84
+
85
+ self.Bind(EVT_UPDATE_PROGRESS, self.on_update_progress)
86
+ self.update_timer = wx.Timer(self)
87
+ self.Bind(wx.EVT_TIMER, self.on_update_timer, self.update_timer)
88
+
89
+ def start_export(self):
90
+ self.start_time = time.time()
91
+ self.update_timer.Start(1000)
92
+
93
+ voxel_size_mm = self.export_options['voxel_size'] / 1000.0
94
+
95
+ self.worker = DirectMaterialCompilerWorker(
96
+ self,
97
+ self.export_options['root'],
98
+ np.array([voxel_size_mm[0], voxel_size_mm[1], voxel_size_mm[2]]),
99
+ self.export_options['materials'],
100
+ self.export_options['output_directory'],
101
+ self.export_options['file_prefix'],
102
+ self.export_options['liquid_keepout'],
103
+ self.export_options['liquid_keepout_distance']
104
+ )
105
+ self.worker.start()
106
+
107
+ def on_update_progress(self, event):
108
+ if event.error:
109
+ self.update_timer.Stop()
110
+ wx.MessageBox(f"An error occurred during export: {event.error}", "Error", wx.OK | wx.ICON_ERROR)
111
+ self.GetParent().GetParent().FindWindowByLabel("Back").Enable(True)
112
+ elif event.finished:
113
+ self.update_timer.Stop()
114
+ self.progress_bar.SetValue(100)
115
+ elapsed_seconds = time.time() - self.start_time
116
+ elapsed_time_str = time.strftime('%H:%M:%S', time.gmtime(elapsed_seconds))
117
+ self.elapsed_time_label.SetLabel(f"Elapsed time: {elapsed_time_str}")
118
+
119
+ main_frame = self.GetTopLevelParent()
120
+ main_frame.on_export_complete(self.export_options['output_directory'], elapsed_time_str)
121
+ else:
122
+ self.progress_bar.SetValue(event.progress)
123
+
124
+ def on_update_timer(self, event):
125
+ elapsed_seconds = time.time() - self.start_time
126
+ self.elapsed_time_label.SetLabel(f"Elapsed time: {time.strftime('%H:%M:%S', time.gmtime(elapsed_seconds))}")
127
+
128
+ progress = self.progress_bar.GetValue()
129
+ if progress > 0:
130
+ remaining_seconds = (elapsed_seconds * (100 - progress)) / progress
131
+ self.time_estimate_label.SetLabel(f"Estimated time remaining: {time.strftime('%H:%M:%S', time.gmtime(remaining_seconds))}")
132
+
133
+ def __del__(self):
134
+ if self.worker and self.worker.is_alive():
135
+ self.worker.stop()
136
+ self.worker.join()
137
+ self.update_timer.Stop()
@@ -0,0 +1,112 @@
1
+ import wx
2
+ import numpy as np
3
+
4
+ class DirectMaterialInkjetPanel(wx.Panel):
5
+ def __init__(self, parent, root, materials):
6
+ super().__init__(parent)
7
+ self.root = root
8
+ self.materials = materials
9
+
10
+ min_bounds, max_bounds = self.root.bounding_box()
11
+ self.min_bounds = np.array([min_bounds.x, min_bounds.y, min_bounds.z])
12
+ self.max_bounds = np.array([max_bounds.x, max_bounds.y, max_bounds.z])
13
+
14
+ sizer = wx.BoxSizer(wx.VERTICAL)
15
+
16
+ # Path
17
+ path_label = wx.StaticText(self, label="Path:")
18
+ sizer.Add(path_label, 0, wx.ALL, 5)
19
+
20
+ path_sizer = wx.BoxSizer(wx.HORIZONTAL)
21
+ self.path_ctrl = wx.TextCtrl(self)
22
+ path_sizer.Add(self.path_ctrl, 1, wx.EXPAND | wx.ALL, 5)
23
+ browse_btn = wx.Button(self, label="Browse")
24
+ browse_btn.Bind(wx.EVT_BUTTON, self.on_browse)
25
+ path_sizer.Add(browse_btn, 0, wx.ALL, 5)
26
+ sizer.Add(path_sizer, 0, wx.EXPAND)
27
+
28
+ # Filename
29
+ filename_label = wx.StaticText(self, label="Filename:")
30
+ sizer.Add(filename_label, 0, wx.ALL, 5)
31
+
32
+ filename_sizer = wx.BoxSizer(wx.HORIZONTAL)
33
+ self.filename_ctrl = wx.TextCtrl(self)
34
+ filename_sizer.Add(self.filename_ctrl, 1, wx.EXPAND | wx.ALL, 5)
35
+ extension_label = wx.StaticText(self, label="XX.png")
36
+ filename_sizer.Add(extension_label, 0, wx.ALL, 5)
37
+ sizer.Add(filename_sizer, 0, wx.EXPAND)
38
+
39
+ # Liquid Keepout
40
+ self.liquid_keepout_check = wx.CheckBox(self, label="Enable Liquid Keepout")
41
+ self.liquid_keepout_check.Bind(wx.EVT_CHECKBOX, self.on_toggle_keepout)
42
+ sizer.Add(self.liquid_keepout_check, 0, wx.ALL, 5)
43
+
44
+ self.keepout_distance_ctrl = wx.SpinCtrlDouble(self, min=0.0, max=100.0, inc=0.1)
45
+ self.keepout_distance_ctrl.SetValue(0.0)
46
+ self.keepout_distance_ctrl.Enable(False)
47
+ sizer.Add(self.keepout_distance_ctrl, 0, wx.ALL | wx.EXPAND, 5)
48
+
49
+ # Voxel Size
50
+ voxel_size_label = wx.StaticText(self, label="Voxel Size (XYZ) in microns:")
51
+ sizer.Add(voxel_size_label, 0, wx.ALL, 5)
52
+
53
+ voxel_sizer = wx.BoxSizer(wx.HORIZONTAL)
54
+ self.voxel_size_x = wx.SpinCtrlDouble(self, min=1.0, max=100000.0, initial=42.3)
55
+ self.voxel_size_y = wx.SpinCtrlDouble(self, min=1.0, max=100000.0, initial=84.6)
56
+ self.voxel_size_z = wx.SpinCtrlDouble(self, min=1.0, max=100000.0, initial=27.0)
57
+ voxel_sizer.Add(self.voxel_size_x, 1, wx.EXPAND | wx.ALL, 5)
58
+ voxel_sizer.Add(self.voxel_size_y, 1, wx.EXPAND | wx.ALL, 5)
59
+ voxel_sizer.Add(self.voxel_size_z, 1, wx.EXPAND | wx.ALL, 5)
60
+ sizer.Add(voxel_sizer, 0, wx.EXPAND)
61
+
62
+ for ctrl in [self.voxel_size_x, self.voxel_size_y, self.voxel_size_z]:
63
+ ctrl.Bind(wx.EVT_SPINCTRLDOUBLE, self.on_voxel_size_change)
64
+
65
+ # Sampling Info
66
+ self.sampling_info_label = wx.StaticText(self, label="")
67
+ self.update_sampling_info()
68
+ sizer.Add(self.sampling_info_label, 0, wx.ALL, 5)
69
+
70
+ self.SetSizer(sizer)
71
+
72
+ def on_browse(self, event):
73
+ with wx.DirDialog(self, "Choose a directory:",
74
+ style=wx.DD_DEFAULT_STYLE) as dirDialog:
75
+ if dirDialog.ShowModal() == wx.ID_CANCEL:
76
+ return
77
+ self.path_ctrl.SetValue(dirDialog.GetPath())
78
+
79
+ def on_toggle_keepout(self, event):
80
+ self.keepout_distance_ctrl.Enable(event.IsChecked())
81
+
82
+ def on_voxel_size_change(self, event):
83
+ self.update_sampling_info()
84
+
85
+ def update_sampling_info(self):
86
+ size = self.max_bounds - self.min_bounds
87
+ voxel_size_mm = np.array([
88
+ self.voxel_size_x.GetValue() / 1000.0,
89
+ self.voxel_size_y.GetValue() / 1000.0,
90
+ self.voxel_size_z.GetValue() / 1000.0
91
+ ])
92
+
93
+ total_voxels = (size[0] / voxel_size_mm[0]) * (size[1] / voxel_size_mm[1]) * (size[2] / voxel_size_mm[2])
94
+
95
+ sampling_info = (f"Sample Space Size: {size[0]:.2f} x {size[1]:.2f} x {size[2]:.2f} mm\n"
96
+ f"Total Voxels: {total_voxels:,.0f}")
97
+ self.sampling_info_label.SetLabel(sampling_info)
98
+
99
+ def get_export_options(self):
100
+ return {
101
+ "output_directory": self.path_ctrl.GetValue(),
102
+ "file_prefix": self.filename_ctrl.GetValue(),
103
+ "liquid_keepout": self.liquid_keepout_check.IsChecked(),
104
+ "liquid_keepout_distance": self.keepout_distance_ctrl.GetValue(),
105
+ "voxel_size": np.array([
106
+ self.voxel_size_x.GetValue(),
107
+ self.voxel_size_y.GetValue(),
108
+ self.voxel_size_z.GetValue()
109
+ ]),
110
+ "root": self.root,
111
+ "materials": self.materials
112
+ }
@@ -0,0 +1,101 @@
1
+ import wx
2
+ import wx.lib.newevent
3
+ import threading
4
+ import time
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import numpy as np
9
+ from wx.lib.newevent import NewEvent
10
+
11
+ # Custom event for progress updates
12
+ UpdateProgressEvent, EVT_UPDATE_PROGRESS = NewEvent()
13
+
14
+ class FiniteElementMeshCompilerWorker(threading.Thread):
15
+ def __init__(self, parent, export_options):
16
+ super().__init__()
17
+ self.parent = parent
18
+ self.export_options = export_options
19
+ self._running = True
20
+
21
+ def run(self):
22
+ try:
23
+ # This is where you would call the actual compiler
24
+ # For now, we simulate the progress
25
+ for i in range(101):
26
+ if not self._running:
27
+ break
28
+ time.sleep(0.05)
29
+ evt = UpdateProgressEvent(progress=i, error=None, finished=(i == 100))
30
+ wx.PostEvent(self.parent, evt)
31
+ except Exception as e:
32
+ evt = UpdateProgressEvent(error=str(e), finished=True)
33
+ wx.PostEvent(self.parent, evt)
34
+
35
+ def stop(self):
36
+ self._running = False
37
+
38
+ class FiniteElementMeshProgressPanel(wx.Panel):
39
+ def __init__(self, parent, export_options):
40
+ super().__init__(parent)
41
+ self.export_options = export_options
42
+ self.worker = None
43
+ self.start_time = None
44
+
45
+ sizer = wx.BoxSizer(wx.VERTICAL)
46
+ sizer.Add(wx.StaticText(self, label="Exporting INP File..."), 0, wx.ALL, 5)
47
+
48
+ self.progress_bar = wx.Gauge(self, range=100, style=wx.GA_HORIZONTAL)
49
+ sizer.Add(self.progress_bar, 0, wx.ALL | wx.EXPAND, 5)
50
+
51
+ self.elapsed_time_label = wx.StaticText(self, label="Elapsed time: 00:00:00")
52
+ sizer.Add(self.elapsed_time_label, 0, wx.ALL, 5)
53
+
54
+ self.time_estimate_label = wx.StaticText(self, label="Estimated time remaining: N/A")
55
+ sizer.Add(self.time_estimate_label, 0, wx.ALL, 5)
56
+
57
+ self.SetSizer(sizer)
58
+
59
+ self.Bind(EVT_UPDATE_PROGRESS, self.on_update_progress)
60
+ self.update_timer = wx.Timer(self)
61
+ self.Bind(wx.EVT_TIMER, self.on_update_timer, self.update_timer)
62
+
63
+ def start_export(self):
64
+ self.start_time = time.time()
65
+ self.update_timer.Start(1000)
66
+
67
+ self.worker = FiniteElementMeshCompilerWorker(self, self.export_options)
68
+ self.worker.start()
69
+
70
+ def on_update_progress(self, event):
71
+ if event.error:
72
+ self.update_timer.Stop()
73
+ wx.MessageBox(f"An error occurred during export: {event.error}", "Error", wx.OK | wx.ICON_ERROR)
74
+ self.GetParent().GetParent().FindWindowByLabel("Back").Enable(True)
75
+ elif event.finished:
76
+ self.update_timer.Stop()
77
+ self.progress_bar.SetValue(100)
78
+ elapsed_seconds = time.time() - self.start_time
79
+ elapsed_time_str = time.strftime('%H:%M:%S', time.gmtime(elapsed_seconds))
80
+ self.elapsed_time_label.SetLabel(f"Elapsed time: {elapsed_time_str}")
81
+
82
+ main_frame = self.GetTopLevelParent()
83
+ main_frame.on_export_complete(self.export_options['file_path'], elapsed_time_str)
84
+ else:
85
+ self.progress_bar.SetValue(event.progress)
86
+
87
+ def on_update_timer(self, event):
88
+ elapsed_seconds = time.time() - self.start_time
89
+ self.elapsed_time_label.SetLabel(f"Elapsed time: {time.strftime('%H:%M:%S', time.gmtime(elapsed_seconds))}")
90
+
91
+ progress = self.progress_bar.GetValue()
92
+ if progress > 0:
93
+ remaining_seconds = (elapsed_seconds * (100 - progress)) / progress
94
+ self.time_estimate_label.SetLabel(f"Estimated time remaining: {time.strftime('%H:%M:%S', time.gmtime(remaining_seconds))}")
95
+
96
+ def __del__(self):
97
+ if self.worker and self.worker.is_alive():
98
+ self.worker.stop()
99
+ self.worker.join()
100
+ self.update_timer.Stop()
101
+
@@ -0,0 +1,230 @@
1
+ import wx
2
+ import numpy as np
3
+
4
+ class FiniteElementMeshPanel(wx.Panel):
5
+ def __init__(self, parent, root, materials):
6
+ super().__init__(parent)
7
+ self.root = root
8
+ self.materials = materials
9
+
10
+ sizer = wx.BoxSizer(wx.VERTICAL)
11
+
12
+ # Path
13
+ path_label = wx.StaticText(self, label="Path:")
14
+ sizer.Add(path_label, 0, wx.ALL, 5)
15
+
16
+ path_sizer = wx.BoxSizer(wx.HORIZONTAL)
17
+ self.path_ctrl = wx.TextCtrl(self)
18
+ path_sizer.Add(self.path_ctrl, 1, wx.EXPAND | wx.ALL, 5)
19
+ browse_btn = wx.Button(self, label="Browse")
20
+ browse_btn.Bind(wx.EVT_BUTTON, self.on_browse)
21
+ path_sizer.Add(browse_btn, 0, wx.ALL, 5)
22
+ sizer.Add(path_sizer, 0, wx.EXPAND)
23
+
24
+ # Element Type
25
+ element_type_label = wx.StaticText(self, label="Element Type:")
26
+ sizer.Add(element_type_label, 0, wx.ALL, 5)
27
+ self.element_type_combo = wx.ComboBox(self, choices=["Tetrahedron (C3D4)", "Brick (C3D8)"], style=wx.CB_READONLY)
28
+ self.element_type_combo.SetSelection(0)
29
+ self.element_type_combo.Bind(wx.EVT_COMBOBOX, self.on_element_type_change)
30
+ sizer.Add(self.element_type_combo, 0, wx.ALL | wx.EXPAND, 5)
31
+
32
+ # Brick Options
33
+ self.brick_group_box = wx.StaticBox(self, label="Brick Element (C3D8) Options")
34
+ self.brick_group_box.Hide()
35
+ brick_sizer = wx.StaticBoxSizer(self.brick_group_box, wx.VERTICAL)
36
+
37
+ brick_grid = wx.GridSizer(2, 3, 5, 5)
38
+ brick_grid.Add(wx.StaticText(self.brick_group_box, label="X Cells:"), 0, wx.ALIGN_CENTER_VERTICAL)
39
+ brick_grid.Add(wx.StaticText(self.brick_group_box, label="Y Cells:"), 0, wx.ALIGN_CENTER_VERTICAL)
40
+ brick_grid.Add(wx.StaticText(self.brick_group_box, label="Z Cells:"), 0, wx.ALIGN_CENTER_VERTICAL)
41
+ self.x_dim_ctrl = wx.TextCtrl(self.brick_group_box)
42
+ self.y_dim_ctrl = wx.TextCtrl(self.brick_group_box)
43
+ self.z_dim_ctrl = wx.TextCtrl(self.brick_group_box)
44
+ brick_grid.Add(self.x_dim_ctrl, 1, wx.EXPAND)
45
+ brick_grid.Add(self.y_dim_ctrl, 1, wx.EXPAND)
46
+ brick_grid.Add(self.z_dim_ctrl, 1, wx.EXPAND)
47
+ brick_sizer.Add(brick_grid, 0, wx.EXPAND | wx.ALL, 5)
48
+
49
+ self.num_elements_label = wx.StaticText(self.brick_group_box, label="Number of Elements: 0")
50
+ brick_sizer.Add(self.num_elements_label, 0, wx.ALL, 5)
51
+
52
+ self.use_volumetric_dither_check = wx.CheckBox(self.brick_group_box, label="Use Volumetric Dither")
53
+ brick_sizer.Add(self.use_volumetric_dither_check, 0, wx.ALL, 5)
54
+
55
+ for ctrl in [self.x_dim_ctrl, self.y_dim_ctrl, self.z_dim_ctrl]:
56
+ ctrl.Bind(wx.EVT_TEXT, self.on_brick_dim_change)
57
+
58
+ sizer.Add(brick_sizer, 0, wx.EXPAND | wx.ALL, 5)
59
+
60
+ # Tetrahedral Options
61
+ self.tetra_group_box = wx.StaticBox(self, label="Tetrahedron Element (C3D4) Options")
62
+ tetra_sizer = wx.StaticBoxSizer(self.tetra_group_box, wx.VERTICAL)
63
+
64
+ # Voxel Size
65
+ voxel_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
66
+ voxel_size_sizer.Add(wx.StaticText(self.tetra_group_box, label="Sample Size (mm):"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
67
+ self.voxel_size_ctrl = wx.SpinCtrlDouble(self.tetra_group_box, min=0.000001, max=100.0, initial=0.1, inc=0.000001)
68
+ self.voxel_size_ctrl.SetDigits(6)
69
+ voxel_size_sizer.Add(self.voxel_size_ctrl, 1, wx.EXPAND | wx.ALL, 5)
70
+ auto_compute_btn = wx.Button(self.tetra_group_box, label="Auto-compute")
71
+ auto_compute_btn.Bind(wx.EVT_BUTTON, self.on_auto_compute)
72
+ voxel_size_sizer.Add(auto_compute_btn, 0, wx.ALL, 5)
73
+ tetra_sizer.Add(voxel_size_sizer, 0, wx.EXPAND)
74
+
75
+ # Variable Mesh Size
76
+ self.use_variable_mesh_check = wx.CheckBox(self.tetra_group_box, label="Use Gradient-based Variable Mesh Size")
77
+ self.use_variable_mesh_check.Bind(wx.EVT_CHECKBOX, self.on_toggle_variable_mesh)
78
+ tetra_sizer.Add(self.use_variable_mesh_check, 0, wx.ALL, 5)
79
+
80
+ # Cell Size (non-variable)
81
+ self.cell_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
82
+ self.cell_size_sizer.Add(wx.StaticText(self.tetra_group_box, label="Cell Size:"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
83
+ self.cell_size_ctrl = wx.SpinCtrlDouble(self.tetra_group_box, min=0.000001, max=100.0, initial=0.1, inc=0.000001)
84
+ self.cell_size_ctrl.SetDigits(6)
85
+ self.cell_size_sizer.Add(self.cell_size_ctrl, 1, wx.EXPAND | wx.ALL, 5)
86
+ tetra_sizer.Add(self.cell_size_sizer, 0, wx.EXPAND)
87
+
88
+ # Variable Mesh Options
89
+ self.variable_mesh_group = wx.StaticBox(self.tetra_group_box, label="Variable Mesh Size Options")
90
+ self.variable_mesh_group.Hide()
91
+ variable_mesh_sizer = wx.StaticBoxSizer(self.variable_mesh_group, wx.VERTICAL)
92
+
93
+ min_cell_sizer = wx.BoxSizer(wx.HORIZONTAL)
94
+ min_cell_sizer.Add(wx.StaticText(self.variable_mesh_group, label="Minimum Cell Size:"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
95
+ self.min_cell_size_ctrl = wx.SpinCtrlDouble(self.variable_mesh_group, min=0.000001, max=100.0, inc=0.000001)
96
+ self.min_cell_size_ctrl.SetDigits(6)
97
+ min_cell_sizer.Add(self.min_cell_size_ctrl, 1, wx.EXPAND | wx.ALL, 5)
98
+ variable_mesh_sizer.Add(min_cell_sizer, 0, wx.EXPAND)
99
+
100
+ max_cell_sizer = wx.BoxSizer(wx.HORIZONTAL)
101
+ max_cell_sizer.Add(wx.StaticText(self.variable_mesh_group, label="Maximum Cell Size:"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
102
+ self.max_cell_size_ctrl = wx.SpinCtrlDouble(self.variable_mesh_group, min=0.000001, max=100.0, inc=0.000001)
103
+ self.max_cell_size_ctrl.SetDigits(6)
104
+ max_cell_sizer.Add(self.max_cell_size_ctrl, 1, wx.EXPAND | wx.ALL, 5)
105
+ variable_mesh_sizer.Add(max_cell_sizer, 0, wx.EXPAND)
106
+
107
+ variable_mesh_sizer.Add(wx.StaticText(self.variable_mesh_group, label="Cell Size Mapping Expression:"), 0, wx.ALL, 5)
108
+ self.variable_cell_size_expr_ctrl = wx.TextCtrl(self.variable_mesh_group, style=wx.TE_MULTILINE)
109
+ self.variable_cell_size_expr_ctrl.SetValue("min_cell + h * (max_cell - min_cell)")
110
+ variable_mesh_sizer.Add(self.variable_cell_size_expr_ctrl, 1, wx.EXPAND | wx.ALL, 5)
111
+ tetra_sizer.Add(variable_mesh_sizer, 0, wx.EXPAND | wx.ALL, 5)
112
+
113
+ # Facet Angle
114
+ facet_angle_sizer = wx.BoxSizer(wx.HORIZONTAL)
115
+ facet_angle_sizer.Add(wx.StaticText(self.tetra_group_box, label="Facet Angle (deg):"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
116
+ self.facet_angle_slider = wx.Slider(self.tetra_group_box, value=30, minValue=0, maxValue=90)
117
+ self.facet_angle_label = wx.StaticText(self.tetra_group_box, label="30")
118
+ self.facet_angle_slider.Bind(wx.EVT_SLIDER, lambda evt: self.facet_angle_label.SetLabel(str(self.facet_angle_slider.GetValue())))
119
+ facet_angle_sizer.Add(self.facet_angle_slider, 1, wx.EXPAND | wx.ALL, 5)
120
+ facet_angle_sizer.Add(self.facet_angle_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
121
+ tetra_sizer.Add(facet_angle_sizer, 0, wx.EXPAND)
122
+
123
+ # Facet Size
124
+ facet_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
125
+ facet_size_sizer.Add(wx.StaticText(self.tetra_group_box, label="Facet Size (mm):"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
126
+ self.facet_size_ctrl = wx.SpinCtrlDouble(self.tetra_group_box, min=0.000001, max=100.0, inc=0.000001)
127
+ self.facet_size_ctrl.SetDigits(6)
128
+ facet_size_sizer.Add(self.facet_size_ctrl, 1, wx.EXPAND | wx.ALL, 5)
129
+ tetra_sizer.Add(facet_size_sizer, 0, wx.EXPAND)
130
+
131
+ # Facet Distance
132
+ facet_dist_sizer = wx.BoxSizer(wx.HORIZONTAL)
133
+ facet_dist_sizer.Add(wx.StaticText(self.tetra_group_box, label="Facet Distance (mm):"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
134
+ self.facet_dist_ctrl = wx.SpinCtrlDouble(self.tetra_group_box, min=0.000001, max=100.0, inc=0.000001)
135
+ self.facet_dist_ctrl.SetDigits(6)
136
+ facet_dist_sizer.Add(self.facet_dist_ctrl, 1, wx.EXPAND | wx.ALL, 5)
137
+ tetra_sizer.Add(facet_dist_sizer, 0, wx.EXPAND)
138
+
139
+ # Cell Radius/Edge Ratio
140
+ cell_ratio_sizer = wx.BoxSizer(wx.HORIZONTAL)
141
+ cell_ratio_sizer.Add(wx.StaticText(self.tetra_group_box, label="Cell Radius/Edge Ratio:"), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
142
+ self.cell_ratio_ctrl = wx.SpinCtrl(self.tetra_group_box, min=1, max=10, initial=2)
143
+ cell_ratio_sizer.Add(self.cell_ratio_ctrl, 1, wx.EXPAND | wx.ALL, 5)
144
+ tetra_sizer.Add(cell_ratio_sizer, 0, wx.EXPAND)
145
+
146
+ sizer.Add(tetra_sizer, 0, wx.EXPAND | wx.ALL, 5)
147
+
148
+ self.on_auto_compute(None)
149
+ self.SetSizerAndFit(sizer)
150
+
151
+ def on_browse(self, event):
152
+ with wx.FileDialog(self, "Save INP File", wildcard="INP files (*.inp)|*.inp",
153
+ style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:
154
+ if fileDialog.ShowModal() == wx.ID_CANCEL:
155
+ return
156
+ pathname = fileDialog.GetPath()
157
+ if not pathname.lower().endswith(".inp"):
158
+ pathname += ".inp"
159
+ self.path_ctrl.SetValue(pathname)
160
+
161
+ def on_element_type_change(self, event):
162
+ is_tetra = self.element_type_combo.GetSelection() == 0
163
+ self.tetra_group_box.Show(is_tetra)
164
+ self.brick_group_box.Show(not is_tetra)
165
+ self.GetParent().Layout()
166
+
167
+ def on_brick_dim_change(self, event):
168
+ try:
169
+ x = int(self.x_dim_ctrl.GetValue())
170
+ y = int(self.y_dim_ctrl.GetValue())
171
+ z = int(self.z_dim_ctrl.GetValue())
172
+ self.num_elements_label.SetLabel(f"Number of Elements: {x*y*z:,.0f}")
173
+ except ValueError:
174
+ self.num_elements_label.SetLabel("Number of Elements: Invalid Input")
175
+
176
+ def on_toggle_variable_mesh(self, event):
177
+ use_variable = self.use_variable_mesh_check.IsChecked()
178
+ self.variable_mesh_group.Show(use_variable)
179
+ self.cell_size_sizer.Show(not use_variable)
180
+ self.GetParent().Layout()
181
+
182
+ def on_auto_compute(self, event):
183
+ if event: # Only show message box on button click
184
+ reply = wx.MessageBox("This will overwrite the current values. Are you sure you want to continue?", "Overwrite Values?", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
185
+ if reply == wx.NO:
186
+ return
187
+
188
+ voxel_size = self.voxel_size_ctrl.GetValue()
189
+ self.voxel_size_ctrl.SetIncrement(voxel_size / 10.0)
190
+
191
+ self.facet_size_ctrl.SetValue(voxel_size)
192
+ self.facet_size_ctrl.SetIncrement(voxel_size / 10.0)
193
+
194
+ self.facet_dist_ctrl.SetValue(voxel_size / 10.0)
195
+ self.facet_dist_ctrl.SetIncrement(voxel_size / 100.0)
196
+
197
+ self.cell_size_ctrl.SetValue(voxel_size)
198
+ self.cell_size_ctrl.SetIncrement(voxel_size / 10.0)
199
+
200
+ self.min_cell_size_ctrl.SetValue(voxel_size)
201
+ self.min_cell_size_ctrl.SetIncrement(voxel_size / 20.0)
202
+
203
+ self.max_cell_size_ctrl.SetValue(voxel_size * 10)
204
+ self.max_cell_size_ctrl.SetIncrement(voxel_size / 20.0)
205
+
206
+ def get_export_options(self):
207
+ options = {"file_path": self.path_ctrl.GetValue()}
208
+ if self.element_type_combo.GetSelection() == 0: # Tetra
209
+ options.update({
210
+ "element_type": "tetra",
211
+ "voxel_size": self.voxel_size_ctrl.GetValue(),
212
+ "facet_angle": self.facet_angle_slider.GetValue(),
213
+ "facet_size": self.facet_size_ctrl.GetValue(),
214
+ "facet_distance": self.facet_dist_ctrl.GetValue(),
215
+ "cell_radius_edge_ratio": self.cell_ratio_ctrl.GetValue(),
216
+ "use_variable_mesh_size": self.use_variable_mesh_check.IsChecked(),
217
+ "cell_size": self.cell_size_ctrl.GetValue(),
218
+ "min_cell_size": self.min_cell_size_ctrl.GetValue(),
219
+ "max_cell_size": self.max_cell_size_ctrl.GetValue(),
220
+ "variable_cell_size_expression": self.variable_cell_size_expr_ctrl.GetValue(),
221
+ })
222
+ else: # Brick
223
+ options.update({
224
+ "element_type": "brick",
225
+ "x_dim": int(self.x_dim_ctrl.GetValue()),
226
+ "y_dim": int(self.y_dim_ctrl.GetValue()),
227
+ "z_dim": int(self.z_dim_ctrl.GetValue()),
228
+ "use_volumetric_dither": self.use_volumetric_dither_check.IsChecked(),
229
+ })
230
+ return options