Coverage for tatlin/lib/ui/stl.py: 100%
194 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
1# -*- coding: utf-8 -*-
2# Copyright (C) 2011 Denis Kobozev
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software Foundation,
16# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18import wx
20from tatlin.lib.gl.model import Model
21from tatlin.lib.gl.scene import Scene
22from tatlin.lib.ui.panel import Panel
23from tatlin.lib.util import format_float
25from .view import ViewButtons
28class StlPanel(Panel):
29 def __init__(self, parent, scene: Scene):
30 super(StlPanel, self).__init__(parent, scene)
32 # ----------------------------------------------------------------------
33 # DIMENSIONS
34 # ----------------------------------------------------------------------
36 static_box_dimensions = wx.StaticBox(self, label="Dimensions")
37 sizer_dimensions = wx.StaticBoxSizer(static_box_dimensions, wx.VERTICAL)
39 label_x = wx.StaticText(self, label="X:")
40 self.entry_x = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
41 label_x_units = wx.StaticText(self, label="mm")
43 label_y = wx.StaticText(self, label="Y:")
44 self.entry_y = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
45 label_y_units = wx.StaticText(self, label="mm")
47 label_z = wx.StaticText(self, label="Z:")
48 self.entry_z = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
49 label_z_units = wx.StaticText(self, label="mm")
51 label_factor = wx.StaticText(self, label="Factor:")
52 self.entry_factor = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
54 grid_dimensions = wx.FlexGridSizer(4, 3, 5, 5)
55 grid_dimensions.Add(label_x, 0, wx.ALIGN_CENTER)
56 grid_dimensions.Add(self.entry_x, 0, wx.EXPAND)
57 grid_dimensions.Add(label_x_units, 0, wx.ALIGN_CENTER_VERTICAL)
58 grid_dimensions.Add(label_y, 0, wx.ALIGN_CENTER)
59 grid_dimensions.Add(self.entry_y, 0, wx.EXPAND)
60 grid_dimensions.Add(label_y_units, 0, wx.ALIGN_CENTER_VERTICAL)
61 grid_dimensions.Add(label_z, 0, wx.ALIGN_CENTER)
62 grid_dimensions.Add(self.entry_z, 0, wx.EXPAND)
63 grid_dimensions.Add(label_z_units, 0, wx.ALIGN_CENTER_VERTICAL)
64 grid_dimensions.Add(label_factor, 0, wx.ALIGN_CENTER)
65 grid_dimensions.Add(self.entry_factor, 0, wx.EXPAND)
66 grid_dimensions.AddGrowableCol(1)
68 sizer_dimensions.Add(grid_dimensions, 0, wx.EXPAND | wx.ALL, border=5)
70 # ----------------------------------------------------------------------
71 # MOVE
72 # ----------------------------------------------------------------------
74 static_box_move = wx.StaticBox(self, label="Move")
75 sizer_move = wx.StaticBoxSizer(static_box_move, wx.VERTICAL)
77 self.btn_center = wx.Button(self, label="Center model")
79 sizer_move.Add(self.btn_center, 0, wx.EXPAND | wx.ALL, border=5)
81 # ----------------------------------------------------------------------
82 # ROTATE
83 # ----------------------------------------------------------------------
85 static_box_rotate = wx.StaticBox(self, label="Rotate")
86 sizer_rotate = wx.StaticBoxSizer(static_box_rotate, wx.VERTICAL)
88 self.btn_x_90 = wx.Button(self, label="+90")
89 label_rotate_x = wx.StaticText(self, label="X:")
90 self.entry_rotate_x = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
91 sizer_entry_x = wx.BoxSizer(wx.HORIZONTAL)
92 sizer_entry_x.Add(self.entry_rotate_x, 1, wx.ALIGN_CENTER_VERTICAL)
94 self.btn_y_90 = wx.Button(self, label="+90")
95 label_rotate_y = wx.StaticText(self, label="Y:")
96 self.entry_rotate_y = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
97 sizer_entry_y = wx.BoxSizer(wx.HORIZONTAL)
98 sizer_entry_y.Add(self.entry_rotate_y, 1, wx.ALIGN_CENTER_VERTICAL)
100 self.btn_z_90 = wx.Button(self, label="+90")
101 label_rotate_z = wx.StaticText(self, label="Z:")
102 self.entry_rotate_z = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
103 sizer_entry_z = wx.BoxSizer(wx.HORIZONTAL)
104 sizer_entry_z.Add(self.entry_rotate_z, 1, wx.ALIGN_CENTER_VERTICAL)
106 grid_rotate = wx.FlexGridSizer(3, 3, 5, 5)
107 grid_rotate.Add(self.btn_x_90, 0)
108 grid_rotate.Add(label_rotate_x, 0, wx.ALIGN_CENTER_VERTICAL)
109 grid_rotate.Add(sizer_entry_x, 0, wx.EXPAND)
110 grid_rotate.Add(self.btn_y_90, 0)
111 grid_rotate.Add(label_rotate_y, 0, wx.ALIGN_CENTER_VERTICAL)
112 grid_rotate.Add(sizer_entry_y, 0, wx.EXPAND)
113 grid_rotate.Add(self.btn_z_90, 0)
114 grid_rotate.Add(label_rotate_z, 0, wx.ALIGN_CENTER_VERTICAL)
115 grid_rotate.Add(sizer_entry_z, 0, wx.EXPAND)
116 grid_rotate.AddGrowableCol(2)
118 sizer_rotate.Add(grid_rotate, 0, wx.EXPAND | wx.ALL, border=5)
120 # ----------------------------------------------------------------------
121 # DISPLAY
122 # ----------------------------------------------------------------------
124 static_box_display = wx.StaticBox(self, label="Display")
125 sizer_display = wx.StaticBoxSizer(static_box_display, wx.VERTICAL)
127 view_buttons = ViewButtons(self, scene)
128 self.check_ortho = wx.CheckBox(self, label="Orthographic projection")
129 self.btn_reset_view = wx.Button(self, label="Reset view")
131 box_display = wx.BoxSizer(wx.VERTICAL)
132 box_display.Add(view_buttons, 0, wx.ALIGN_CENTER | wx.TOP, border=5)
133 box_display.Add(self.check_ortho, 0, wx.EXPAND | wx.TOP, border=5)
134 box_display.Add(self.btn_reset_view, 0, wx.EXPAND | wx.TOP, border=5)
136 sizer_display.Add(box_display, 0, wx.EXPAND | wx.ALL, border=5)
138 box = wx.BoxSizer(wx.VERTICAL)
140 box.Add(sizer_dimensions, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=5)
141 box.Add(sizer_move, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=5)
142 box.Add(sizer_rotate, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=5)
143 box.Add(sizer_display, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=5)
145 self.SetSizer(box)
147 def connect_handlers(self):
148 if self._handlers_connected:
149 return
151 # ----------------------------------------------------------------------
152 # DIMENSIONS
153 # ----------------------------------------------------------------------
155 self.entry_x.Bind(wx.EVT_KILL_FOCUS, self.on_entry_x_focus_out)
156 self.entry_x.Bind(wx.EVT_TEXT_ENTER, self.on_entry_x_focus_out)
158 self.entry_y.Bind(wx.EVT_KILL_FOCUS, self.on_entry_y_focus_out)
159 self.entry_y.Bind(wx.EVT_TEXT_ENTER, self.on_entry_y_focus_out)
161 self.entry_z.Bind(wx.EVT_KILL_FOCUS, self.on_entry_z_focus_out)
162 self.entry_z.Bind(wx.EVT_TEXT_ENTER, self.on_entry_z_focus_out)
164 self.entry_factor.Bind(wx.EVT_KILL_FOCUS, self.on_entry_factor_focus_out)
165 self.entry_factor.Bind(wx.EVT_TEXT_ENTER, self.on_entry_factor_focus_out)
167 # ----------------------------------------------------------------------
168 # ROTATE
169 # ----------------------------------------------------------------------
171 self.entry_rotate_x.Bind(wx.EVT_KILL_FOCUS, self.on_entry_rotate_x_focus_out)
172 self.entry_rotate_x.Bind(wx.EVT_TEXT_ENTER, self.on_entry_rotate_x_focus_out)
174 self.entry_rotate_y.Bind(wx.EVT_KILL_FOCUS, self.on_entry_rotate_y_focus_out)
175 self.entry_rotate_y.Bind(wx.EVT_TEXT_ENTER, self.on_entry_rotate_y_focus_out)
177 self.entry_rotate_z.Bind(wx.EVT_KILL_FOCUS, self.on_entry_rotate_z_focus_out)
178 self.entry_rotate_z.Bind(wx.EVT_TEXT_ENTER, self.on_entry_rotate_z_focus_out)
180 self.btn_x_90.Bind(wx.EVT_BUTTON, self.on_x_90_clicked)
181 self.btn_y_90.Bind(wx.EVT_BUTTON, self.on_y_90_clicked)
182 self.btn_z_90.Bind(wx.EVT_BUTTON, self.on_z_90_clicked)
184 # ----------------------------------------------------------------------
185 # MOVE
186 # ----------------------------------------------------------------------
188 self.btn_center.Bind(wx.EVT_BUTTON, self.on_center_clicked)
190 # ----------------------------------------------------------------------
191 # DISPLAY
192 # ----------------------------------------------------------------------
194 self.check_ortho.Bind(wx.EVT_CHECKBOX, self.on_set_ortho)
195 self.btn_reset_view.Bind(wx.EVT_BUTTON, self.on_reset_clicked)
197 self._handlers_connected = True
199 def scaling_factor_changed(self, factor):
200 try:
201 self.scene.scale_model(float(factor))
202 self.scene.invalidate()
203 # tell all the widgets that care about model size that it has changed
204 self.model_size_changed()
205 self.GetParent().file_modified = self.scene.model_modified
206 except ValueError:
207 pass # ignore invalid values
209 def dimension_changed(self, dimension, value):
210 try:
211 self.scene.change_model_dimension(dimension, float(value))
212 self.scene.invalidate()
213 self.model_size_changed()
214 self.GetParent().file_modified = self.scene.model_modified
215 except ValueError:
216 pass # ignore invalid values
218 def rotation_changed(self, axis, angle):
219 try:
220 self.scene.model.rotate_abs(float(angle), axis)
221 self.scene.model.init()
222 self.scene.invalidate()
224 self.GetParent().file_modified = self.scene.model_modified
225 except ValueError:
226 pass # ignore invalid values
228 def on_entry_x_focus_out(self, event):
229 self.dimension_changed("width", self.entry_x.GetValue())
230 event.Skip()
232 def on_entry_y_focus_out(self, event):
233 self.dimension_changed("depth", self.entry_y.GetValue())
234 event.Skip()
236 def on_entry_z_focus_out(self, event):
237 self.dimension_changed("height", self.entry_z.GetValue())
238 event.Skip()
240 def on_entry_factor_focus_out(self, event):
241 self.scaling_factor_changed(self.entry_factor.GetValue())
242 event.Skip()
244 def on_entry_rotate_x_focus_out(self, event):
245 self.rotation_changed(Model.AXIS_X, self.entry_rotate_x.GetValue())
246 self.model_angle_changed()
247 event.Skip()
249 def on_entry_rotate_y_focus_out(self, event):
250 self.rotation_changed(Model.AXIS_Y, self.entry_rotate_y.GetValue())
251 self.model_angle_changed()
252 event.Skip()
254 def on_entry_rotate_z_focus_out(self, event):
255 self.rotation_changed(Model.AXIS_Z, self.entry_rotate_z.GetValue())
256 self.model_angle_changed()
257 event.Skip()
259 def on_x_90_clicked(self, event):
260 self.rotate_relative(Model.AXIS_X, 90)
262 def on_y_90_clicked(self, event):
263 self.rotate_relative(Model.AXIS_Y, 90)
265 def on_z_90_clicked(self, event):
266 self.rotate_relative(Model.AXIS_Z, 90)
268 def rotate_relative(self, axis, angle):
269 current_angle = self.scene.model.rotation_angle[axis]
270 angle = current_angle + angle
271 self.rotation_changed(axis, angle)
272 self.model_angle_changed()
274 def set_initial_values(self, layers_range_max, layers_value, width, height, depth):
275 self._set_size_properties()
276 self._set_rotation_properties()
278 def _set_size_properties(self):
279 self.entry_x.SetValue(format_float(self.scene.model.width))
280 self.entry_y.SetValue(format_float(self.scene.model.depth))
281 self.entry_z.SetValue(format_float(self.scene.model.height))
282 self.entry_factor.SetValue(
283 format_float(round(self.scene.model.scaling_factor, 2))
284 )
286 def _set_rotation_properties(self):
287 self.entry_rotate_x.SetValue(
288 format_float(self.scene.model.rotation_angle[Model.AXIS_X])
289 )
290 self.entry_rotate_y.SetValue(
291 format_float(self.scene.model.rotation_angle[Model.AXIS_Y])
292 )
293 self.entry_rotate_z.SetValue(
294 format_float(self.scene.model.rotation_angle[Model.AXIS_Z])
295 )
297 def model_size_changed(self):
298 self._set_size_properties()
300 def model_angle_changed(self):
301 self._set_rotation_properties()
303 def on_center_clicked(self, event):
304 self.scene.center_model()
305 self.scene.invalidate()
306 self.GetParent().file_modified = self.scene.model_modified
308 def on_reset_clicked(self, event):
309 self.scene.reset_view()
310 self.scene.invalidate()
312 def on_set_ortho(self, event):
313 self.scene.mode_ortho = event.GetEventObject().GetValue()
314 self.scene.invalidate()