printrun-src/printrun/stlplater.py

changeset 46
cce0af6351f0
parent 15
0bbb006204fc
equal deleted inserted replaced
45:c82943fb205f 46:cce0af6351f0
1 #!/usr/bin/env python 1 #!/usr/bin/env python3
2 2
3 # This file is part of the Printrun suite. 3 # This file is part of the Printrun suite.
4 # 4 #
5 # Printrun is free software: you can redistribute it and/or modify 5 # Printrun is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by 6 # it under the terms of the GNU General Public License as published by
34 from copy import copy 34 from copy import copy
35 35
36 from printrun import stltool 36 from printrun import stltool
37 from printrun.objectplater import make_plater, PlaterPanel 37 from printrun.objectplater import make_plater, PlaterPanel
38 38
39 glview = False 39 glview = '--no-gl' not in sys.argv
40 if "-nogl" not in sys.argv: 40 if glview:
41 try: 41 try:
42 from printrun import stlview 42 from printrun import stlview
43 glview = True
44 except: 43 except:
44 glview = False
45 logging.warning("Could not load 3D viewer for plater:" 45 logging.warning("Could not load 3D viewer for plater:"
46 + "\n" + traceback.format_exc()) 46 + "\n" + traceback.format_exc())
47 47
48 48
49 def evalme(s): 49 def evalme(s):
59 matrix = model.translation_matrix(model.offsets).dot(matrix) 59 matrix = model.translation_matrix(model.offsets).dot(matrix)
60 return matrix 60 return matrix
61 61
62 class showstl(wx.Window): 62 class showstl(wx.Window):
63 def __init__(self, parent, size, pos): 63 def __init__(self, parent, size, pos):
64 wx.Window.__init__(self, parent, size = size, pos = pos) 64 super().__init__(parent, size = size, pos = pos)
65 self.i = 0 65 self.i = 0
66 self.parent = parent 66 self.parent = parent
67 self.previ = 0 67 self.previ = 0
68 self.Bind(wx.EVT_MOUSEWHEEL, self.rot) 68 self.Bind(wx.EVT_MOUSEWHEEL, self.rot)
69 self.Bind(wx.EVT_MOUSE_EVENTS, self.move) 69 self.Bind(wx.EVT_MOUSE_EVENTS, self.move)
72 self.triggered = 0 72 self.triggered = 0
73 self.initpos = None 73 self.initpos = None
74 self.prevsel = -1 74 self.prevsel = -1
75 75
76 def prepare_model(self, m, scale): 76 def prepare_model(self, m, scale):
77 m.bitmap = wx.EmptyBitmap(800, 800, 32) 77 m.bitmap = wx.Bitmap(800, 800, 32)
78 dc = wx.MemoryDC() 78 dc = wx.MemoryDC()
79 dc.SelectObject(m.bitmap) 79 dc.SelectObject(m.bitmap)
80 dc.SetBackground(wx.Brush((0, 0, 0, 0))) 80 dc.SetBackground(wx.Brush((0, 0, 0, 0)))
81 dc.SetBrush(wx.Brush((0, 0, 0, 255))) 81 dc.SetBrush(wx.Brush((0, 0, 0, 255)))
82 dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128))) 82 dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
103 return True 103 return True
104 104
105 def move(self, event): 105 def move(self, event):
106 if event.ButtonUp(wx.MOUSE_BTN_LEFT): 106 if event.ButtonUp(wx.MOUSE_BTN_LEFT):
107 if self.initpos is not None: 107 if self.initpos is not None:
108 currentpos = event.GetPositionTuple() 108 currentpos = event.GetPosition()
109 delta = (0.5 * (currentpos[0] - self.initpos[0]), 109 delta = (0.5 * (currentpos[0] - self.initpos[0]),
110 -0.5 * (currentpos[1] - self.initpos[1]) 110 -0.5 * (currentpos[1] - self.initpos[1])
111 ) 111 )
112 self.move_shape(delta) 112 self.move_shape(delta)
113 self.Refresh() 113 self.Refresh()
114 self.initpos = None 114 self.initpos = None
115 elif event.ButtonDown(wx.MOUSE_BTN_RIGHT): 115 elif event.ButtonDown(wx.MOUSE_BTN_RIGHT):
116 self.parent.right(event) 116 self.parent.right(event)
117 elif event.Dragging(): 117 elif event.Dragging():
118 if self.initpos is None: 118 if self.initpos is None:
119 self.initpos = event.GetPositionTuple() 119 self.initpos = event.GetPosition()
120 self.Refresh() 120 self.Refresh()
121 dc = wx.ClientDC(self) 121 dc = wx.ClientDC(self)
122 p = event.GetPositionTuple() 122 p = event.GetPosition()
123 dc.DrawLine(self.initpos[0], self.initpos[1], p[0], p[1]) 123 dc.DrawLine(self.initpos[0], self.initpos[1], p[0], p[1])
124 del dc 124 del dc
125 else: 125 else:
126 event.Skip() 126 event.Skip()
127 127
179 z = event.GetWheelRotation() 179 z = event.GetWheelRotation()
180 s = self.parent.l.GetSelection() 180 s = self.parent.l.GetSelection()
181 if self.prevsel != s: 181 if self.prevsel != s:
182 self.i = 0 182 self.i = 0
183 self.prevsel = s 183 self.prevsel = s
184 if z < 0: 184 self.rotate_shape(-1 if z < 0 else 1) #TEST
185 self.rotate_shape(-1)
186 else:
187 self.rotate_shape(1)
188 185
189 def repaint(self, event): 186 def repaint(self, event):
190 dc = wx.PaintDC(self) 187 dc = wx.PaintDC(self)
191 self.paint(dc = dc) 188 self.paint(dc = dc)
192 189
193 def paint(self, coord1 = "x", coord2 = "y", dc = None): 190 def paint(self, coord1 = "x", coord2 = "y", dc = None):
194 if dc is None: 191 if dc is None:
195 dc = wx.ClientDC(self) 192 dc = wx.ClientDC(self)
196 scale = 2 193 scale = 2
197 dc.SetPen(wx.Pen(wx.Colour(100, 100, 100))) 194 dc.SetPen(wx.Pen(wx.Colour(100, 100, 100)))
198 for i in xrange(20): 195 for i in range(20):
199 dc.DrawLine(0, i * scale * 10, 400, i * scale * 10) 196 dc.DrawLine(0, i * scale * 10, 400, i * scale * 10)
200 dc.DrawLine(i * scale * 10, 0, i * scale * 10, 400) 197 dc.DrawLine(i * scale * 10, 0, i * scale * 10, 400)
201 dc.SetPen(wx.Pen(wx.Colour(0, 0, 0))) 198 dc.SetPen(wx.Pen(wx.Colour(0, 0, 0)))
202 for i in xrange(4): 199 for i in range(4):
203 dc.DrawLine(0, i * scale * 50, 400, i * scale * 50) 200 dc.DrawLine(0, i * scale * 50, 400, i * scale * 50)
204 dc.DrawLine(i * scale * 50, 0, i * scale * 50, 400) 201 dc.DrawLine(i * scale * 50, 0, i * scale * 50, 400)
205 dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128))) 202 dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
206 dc.SetPen(wx.Pen(wx.Colour(128, 128, 128))) 203 dc.SetPen(wx.Pen(wx.Colour(128, 128, 128)))
207 dcs = wx.MemoryDC() 204 dcs = wx.MemoryDC()
222 save_wildcard = _("STL files (*.stl;*.STL)|*.stl;*.STL") 219 save_wildcard = _("STL files (*.stl;*.STL)|*.stl;*.STL")
223 220
224 def prepare_ui(self, filenames = [], callback = None, 221 def prepare_ui(self, filenames = [], callback = None,
225 parent = None, build_dimensions = None, circular_platform = False, 222 parent = None, build_dimensions = None, circular_platform = False,
226 simarrange_path = None, antialias_samples = 0): 223 simarrange_path = None, antialias_samples = 0):
227 super(StlPlaterPanel, self).prepare_ui(filenames, callback, parent, build_dimensions) 224 super().prepare_ui(filenames, callback, parent, build_dimensions)
228 self.cutting = False 225 self.cutting = False
229 self.cutting_axis = None 226 self.cutting_axis = None
230 self.cutting_dist = None 227 self.cutting_dist = None
231 if glview: 228 if glview:
232 viewer = stlview.StlViewPanel(self, (580, 580), 229 viewer = stlview.StlViewPanel(self, wx.DefaultSize,
233 build_dimensions = self.build_dimensions, 230 build_dimensions = self.build_dimensions,
234 circular = circular_platform, 231 circular = circular_platform,
235 antialias_samples = antialias_samples) 232 antialias_samples = antialias_samples)
236 # Cutting tool 233 # Cutting tool
237 nrows = self.menusizer.GetRows() 234 nrows = self.menusizer.GetRows()
240 cutconfirmbutton = wx.Button(self.menupanel, label = _("Confirm cut")) 237 cutconfirmbutton = wx.Button(self.menupanel, label = _("Confirm cut"))
241 cutconfirmbutton.Bind(wx.EVT_BUTTON, self.cut_confirm) 238 cutconfirmbutton.Bind(wx.EVT_BUTTON, self.cut_confirm)
242 cutconfirmbutton.Disable() 239 cutconfirmbutton.Disable()
243 self.cutconfirmbutton = cutconfirmbutton 240 self.cutconfirmbutton = cutconfirmbutton
244 self.menusizer.Add(cutconfirmbutton, pos = (nrows, 1), span = (1, 1), flag = wx.EXPAND) 241 self.menusizer.Add(cutconfirmbutton, pos = (nrows, 1), span = (1, 1), flag = wx.EXPAND)
245 cutpanel = wx.Panel(self.menupanel, -1) 242 cutpanel = wx.Panel(self.menupanel)
246 cutsizer = self.cutsizer = wx.BoxSizer(wx.HORIZONTAL) 243 cutsizer = self.cutsizer = wx.BoxSizer(wx.HORIZONTAL)
247 cutpanel.SetSizer(cutsizer) 244 cutpanel.SetSizer(cutsizer)
248 cutxplusbutton = wx.ToggleButton(cutpanel, label = _(">X"), style = wx.BU_EXACTFIT) 245 cutxplusbutton = wx.ToggleButton(cutpanel, label = _(">X"), style = wx.BU_EXACTFIT)
249 cutxplusbutton.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.start_cutting_tool(event, "x", 1)) 246 cutxplusbutton.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.start_cutting_tool(event, "x", 1))
250 cutsizer.Add(cutxplusbutton, 1, flag = wx.EXPAND) 247 cutsizer.Add(cutxplusbutton, 1, flag = wx.EXPAND)
268 viewer = showstl(self, (580, 580), (0, 0)) 265 viewer = showstl(self, (580, 580), (0, 0))
269 self.simarrange_path = simarrange_path 266 self.simarrange_path = simarrange_path
270 self.set_viewer(viewer) 267 self.set_viewer(viewer)
271 268
272 def start_cutting_tool(self, event, axis, direction): 269 def start_cutting_tool(self, event, axis, direction):
273 toggle = event.GetEventObject() 270 toggle = event.EventObject
274 if toggle.GetValue(): 271 self.cutting = toggle.Value
272 if toggle.Value:
275 # Disable the other toggles 273 # Disable the other toggles
276 for child in self.cutsizer.GetChildren(): 274 for child in self.cutsizer.Children:
277 child = child.GetWindow() 275 child = child.Window
278 if child != toggle: 276 if child != toggle:
279 child.SetValue(False) 277 child.Value = False
280 self.cutting = True
281 self.cutting_axis = axis 278 self.cutting_axis = axis
282 self.cutting_dist = None
283 self.cutting_direction = direction 279 self.cutting_direction = direction
284 else: 280 else:
285 self.cutting = False
286 self.cutting_axis = None 281 self.cutting_axis = None
287 self.cutting_dist = None
288 self.cutting_direction = None 282 self.cutting_direction = None
283 self.cutting_dist = None
289 284
290 def cut_confirm(self, event): 285 def cut_confirm(self, event):
291 name = self.l.GetSelection() 286 name = self.l.GetSelection()
292 name = self.l.GetString(name) 287 name = self.l.GetString(name)
293 model = self.models[name] 288 model = self.models[name]
333 x, y = event.GetPosition() 328 x, y = event.GetPosition()
334 ray_near, ray_far = self.s.mouse_to_ray(x, y, local_transform = True) 329 ray_near, ray_far = self.s.mouse_to_ray(x, y, local_transform = True)
335 best_match = None 330 best_match = None
336 best_facet = None 331 best_facet = None
337 best_dist = float("inf") 332 best_dist = float("inf")
338 for key, model in self.models.iteritems(): 333 for key, model in self.models.items():
339 transformation = transformation_matrix(model) 334 transformation = transformation_matrix(model)
340 transformed = model.transform(transformation) 335 transformed = model.transform(transformation)
341 if not transformed.intersect_box(ray_near, ray_far): 336 if not transformed.intersect_box(ray_near, ray_far):
342 logging.debug("Skipping %s for rebase search" % key) 337 logging.debug("Skipping %s for rebase search" % key)
343 continue 338 continue
476 471
477 def autoplate(self, event = None): 472 def autoplate(self, event = None):
478 if self.simarrange_path: 473 if self.simarrange_path:
479 try: 474 try:
480 self.autoplate_simarrange() 475 self.autoplate_simarrange()
481 except Exception, e: 476 except Exception as e:
482 logging.warning(_("Failed to use simarrange for plating, " 477 logging.warning(_("Failed to use simarrange for plating, "
483 "falling back to the standard method. " 478 "falling back to the standard method. "
484 "The error was: ") + e) 479 "The error was: ") + e)
485 super(StlPlaterPanel, self).autoplate() 480 super(StlPlaterPanel, self).autoplate()
486 else: 481 else:
492 files = [model.filename for model in models.values()] 487 files = [model.filename for model in models.values()]
493 command = [self.simarrange_path, "--dryrun", 488 command = [self.simarrange_path, "--dryrun",
494 "-m", # Pack around center 489 "-m", # Pack around center
495 "-x", str(int(self.build_dimensions[0])), 490 "-x", str(int(self.build_dimensions[0])),
496 "-y", str(int(self.build_dimensions[1]))] + files 491 "-y", str(int(self.build_dimensions[1]))] + files
497 p = subprocess.Popen(command, stdout = subprocess.PIPE) 492 p = subprocess.Popen(command, stdout = subprocess.PIPE, universal_newlines = True)
498 493
499 pos_regexp = re.compile("File: (.*) minx: ([0-9]+), miny: ([0-9]+), minrot: ([0-9]+)") 494 pos_regexp = re.compile("File: (.*) minx: ([0-9]+), miny: ([0-9]+), minrot: ([0-9]+)")
500 for line in p.stdout: 495 for line in p.stdout:
501 line = line.rstrip() 496 line = line.rstrip()
502 if "Generating plate" in line: 497 if "Generating plate" in line:
508 bits = pos_regexp.match(line).groups() 503 bits = pos_regexp.match(line).groups()
509 filename = bits[0] 504 filename = bits[0]
510 x = float(bits[1]) 505 x = float(bits[1])
511 y = float(bits[2]) 506 y = float(bits[2])
512 rot = -float(bits[3]) 507 rot = -float(bits[3])
513 for name, model in models.items(): 508 for name, model in list(models.items()):
514 # FIXME: not sure this is going to work superwell with utf8 509 # FIXME: not sure this is going to work superwell with utf8
515 if model.filename == filename: 510 if model.filename == filename:
516 model.offsets[0] = x + self.build_dimensions[3] 511 model.offsets[0] = x + self.build_dimensions[3]
517 model.offsets[1] = y + self.build_dimensions[4] 512 model.offsets[1] = y + self.build_dimensions[4]
518 model.rot = rot 513 model.rot = rot

mercurial