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

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 

17 

18import wx 

19 

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 

24 

25from .view import ViewButtons 

26 

27 

28class StlPanel(Panel): 

29 def __init__(self, parent, scene: Scene): 

30 super(StlPanel, self).__init__(parent, scene) 

31 

32 # ---------------------------------------------------------------------- 

33 # DIMENSIONS 

34 # ---------------------------------------------------------------------- 

35 

36 static_box_dimensions = wx.StaticBox(self, label="Dimensions") 

37 sizer_dimensions = wx.StaticBoxSizer(static_box_dimensions, wx.VERTICAL) 

38 

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") 

42 

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") 

46 

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") 

50 

51 label_factor = wx.StaticText(self, label="Factor:") 

52 self.entry_factor = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) 

53 

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) 

67 

68 sizer_dimensions.Add(grid_dimensions, 0, wx.EXPAND | wx.ALL, border=5) 

69 

70 # ---------------------------------------------------------------------- 

71 # MOVE 

72 # ---------------------------------------------------------------------- 

73 

74 static_box_move = wx.StaticBox(self, label="Move") 

75 sizer_move = wx.StaticBoxSizer(static_box_move, wx.VERTICAL) 

76 

77 self.btn_center = wx.Button(self, label="Center model") 

78 

79 sizer_move.Add(self.btn_center, 0, wx.EXPAND | wx.ALL, border=5) 

80 

81 # ---------------------------------------------------------------------- 

82 # ROTATE 

83 # ---------------------------------------------------------------------- 

84 

85 static_box_rotate = wx.StaticBox(self, label="Rotate") 

86 sizer_rotate = wx.StaticBoxSizer(static_box_rotate, wx.VERTICAL) 

87 

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) 

93 

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) 

99 

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) 

105 

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) 

117 

118 sizer_rotate.Add(grid_rotate, 0, wx.EXPAND | wx.ALL, border=5) 

119 

120 # ---------------------------------------------------------------------- 

121 # DISPLAY 

122 # ---------------------------------------------------------------------- 

123 

124 static_box_display = wx.StaticBox(self, label="Display") 

125 sizer_display = wx.StaticBoxSizer(static_box_display, wx.VERTICAL) 

126 

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") 

130 

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) 

135 

136 sizer_display.Add(box_display, 0, wx.EXPAND | wx.ALL, border=5) 

137 

138 box = wx.BoxSizer(wx.VERTICAL) 

139 

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) 

144 

145 self.SetSizer(box) 

146 

147 def connect_handlers(self): 

148 if self._handlers_connected: 

149 return 

150 

151 # ---------------------------------------------------------------------- 

152 # DIMENSIONS 

153 # ---------------------------------------------------------------------- 

154 

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) 

157 

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) 

160 

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) 

163 

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) 

166 

167 # ---------------------------------------------------------------------- 

168 # ROTATE 

169 # ---------------------------------------------------------------------- 

170 

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) 

173 

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) 

176 

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) 

179 

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) 

183 

184 # ---------------------------------------------------------------------- 

185 # MOVE 

186 # ---------------------------------------------------------------------- 

187 

188 self.btn_center.Bind(wx.EVT_BUTTON, self.on_center_clicked) 

189 

190 # ---------------------------------------------------------------------- 

191 # DISPLAY 

192 # ---------------------------------------------------------------------- 

193 

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) 

196 

197 self._handlers_connected = True 

198 

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 

208 

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 

217 

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() 

223 

224 self.GetParent().file_modified = self.scene.model_modified 

225 except ValueError: 

226 pass # ignore invalid values 

227 

228 def on_entry_x_focus_out(self, event): 

229 self.dimension_changed("width", self.entry_x.GetValue()) 

230 event.Skip() 

231 

232 def on_entry_y_focus_out(self, event): 

233 self.dimension_changed("depth", self.entry_y.GetValue()) 

234 event.Skip() 

235 

236 def on_entry_z_focus_out(self, event): 

237 self.dimension_changed("height", self.entry_z.GetValue()) 

238 event.Skip() 

239 

240 def on_entry_factor_focus_out(self, event): 

241 self.scaling_factor_changed(self.entry_factor.GetValue()) 

242 event.Skip() 

243 

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() 

248 

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() 

253 

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() 

258 

259 def on_x_90_clicked(self, event): 

260 self.rotate_relative(Model.AXIS_X, 90) 

261 

262 def on_y_90_clicked(self, event): 

263 self.rotate_relative(Model.AXIS_Y, 90) 

264 

265 def on_z_90_clicked(self, event): 

266 self.rotate_relative(Model.AXIS_Z, 90) 

267 

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() 

273 

274 def set_initial_values(self, layers_range_max, layers_value, width, height, depth): 

275 self._set_size_properties() 

276 self._set_rotation_properties() 

277 

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 ) 

285 

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 ) 

296 

297 def model_size_changed(self): 

298 self._set_size_properties() 

299 

300 def model_angle_changed(self): 

301 self._set_rotation_properties() 

302 

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 

307 

308 def on_reset_clicked(self, event): 

309 self.scene.reset_view() 

310 self.scene.invalidate() 

311 

312 def on_set_ortho(self, event): 

313 self.scene.mode_ortho = event.GetEventObject().GetValue() 

314 self.scene.invalidate()