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 |