diff -r 51bf56ba3c10 -r 0bbb006204fc printrun-src/printrun/stlplater.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/printrun-src/printrun/stlplater.py Fri Jun 03 09:16:07 2016 +0200
@@ -0,0 +1,524 @@
+#!/usr/bin/env python
+
+# This file is part of the Printrun suite.
+#
+# Printrun is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Printrun is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Printrun. If not, see .
+
+import os
+
+# Set up Internationalization using gettext
+# searching for installed locales on /usr/share; uses relative folder if not found (windows)
+from .utils import install_locale
+install_locale('pronterface')
+
+import wx
+import time
+import logging
+import threading
+import math
+import sys
+import re
+import traceback
+import subprocess
+from copy import copy
+
+from printrun import stltool
+from printrun.objectplater import make_plater, PlaterPanel
+
+glview = False
+if "-nogl" not in sys.argv:
+ try:
+ from printrun import stlview
+ glview = True
+ except:
+ logging.warning("Could not load 3D viewer for plater:"
+ + "\n" + traceback.format_exc())
+
+
+def evalme(s):
+ return eval(s[s.find("(") + 1:s.find(")")])
+
+def transformation_matrix(model):
+ matrix = stltool.I
+ if any(model.centeroffset):
+ matrix = model.translation_matrix(model.centeroffset).dot(matrix)
+ if model.rot:
+ matrix = model.rotation_matrix([0, 0, model.rot]).dot(matrix)
+ if any(model.offsets):
+ matrix = model.translation_matrix(model.offsets).dot(matrix)
+ return matrix
+
+class showstl(wx.Window):
+ def __init__(self, parent, size, pos):
+ wx.Window.__init__(self, parent, size = size, pos = pos)
+ self.i = 0
+ self.parent = parent
+ self.previ = 0
+ self.Bind(wx.EVT_MOUSEWHEEL, self.rot)
+ self.Bind(wx.EVT_MOUSE_EVENTS, self.move)
+ self.Bind(wx.EVT_PAINT, self.repaint)
+ self.Bind(wx.EVT_KEY_DOWN, self.keypress)
+ self.triggered = 0
+ self.initpos = None
+ self.prevsel = -1
+
+ def prepare_model(self, m, scale):
+ m.bitmap = wx.EmptyBitmap(800, 800, 32)
+ dc = wx.MemoryDC()
+ dc.SelectObject(m.bitmap)
+ dc.SetBackground(wx.Brush((0, 0, 0, 0)))
+ dc.SetBrush(wx.Brush((0, 0, 0, 255)))
+ dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
+ dc.SetPen(wx.Pen(wx.Colour(128, 128, 128)))
+ for i in m.facets:
+ dc.DrawPolygon([wx.Point(400 + scale * p[0], (400 - scale * p[1])) for p in i[1]])
+ dc.SelectObject(wx.NullBitmap)
+ m.bitmap.SetMask(wx.Mask(m.bitmap, wx.Colour(0, 0, 0, 255)))
+
+ def move_shape(self, delta):
+ """moves shape (selected in l, which is list ListBox of shapes)
+ by an offset specified in tuple delta.
+ Positive numbers move to (rigt, down)"""
+ name = self.parent.l.GetSelection()
+ if name == wx.NOT_FOUND:
+ return False
+ name = self.parent.l.GetString(name)
+ model = self.parent.models[name]
+ model.offsets = [model.offsets[0] + delta[0],
+ model.offsets[1] + delta[1],
+ model.offsets[2]
+ ]
+ self.Refresh()
+ return True
+
+ def move(self, event):
+ if event.ButtonUp(wx.MOUSE_BTN_LEFT):
+ if self.initpos is not None:
+ currentpos = event.GetPositionTuple()
+ delta = (0.5 * (currentpos[0] - self.initpos[0]),
+ -0.5 * (currentpos[1] - self.initpos[1])
+ )
+ self.move_shape(delta)
+ self.Refresh()
+ self.initpos = None
+ elif event.ButtonDown(wx.MOUSE_BTN_RIGHT):
+ self.parent.right(event)
+ elif event.Dragging():
+ if self.initpos is None:
+ self.initpos = event.GetPositionTuple()
+ self.Refresh()
+ dc = wx.ClientDC(self)
+ p = event.GetPositionTuple()
+ dc.DrawLine(self.initpos[0], self.initpos[1], p[0], p[1])
+ del dc
+ else:
+ event.Skip()
+
+ def rotate_shape(self, angle):
+ """rotates acive shape
+ positive angle is clockwise
+ """
+ self.i += angle
+ if not self.triggered:
+ self.triggered = 1
+ threading.Thread(target = self.cr).start()
+
+ def keypress(self, event):
+ """gets keypress events and moves/rotates acive shape"""
+ keycode = event.GetKeyCode()
+ step = 5
+ angle = 18
+ if event.ControlDown():
+ step = 1
+ angle = 1
+ # h
+ if keycode == 72:
+ self.move_shape((-step, 0))
+ # l
+ if keycode == 76:
+ self.move_shape((step, 0))
+ # j
+ if keycode == 75:
+ self.move_shape((0, step))
+ # k
+ if keycode == 74:
+ self.move_shape((0, -step))
+ # [
+ if keycode == 91:
+ self.rotate_shape(-angle)
+ # ]
+ if keycode == 93:
+ self.rotate_shape(angle)
+ event.Skip()
+
+ def rotateafter(self):
+ if self.i != self.previ:
+ i = self.parent.l.GetSelection()
+ if i != wx.NOT_FOUND:
+ self.parent.models[self.parent.l.GetString(i)].rot -= 5 * (self.i - self.previ)
+ self.previ = self.i
+ self.Refresh()
+
+ def cr(self):
+ time.sleep(0.01)
+ wx.CallAfter(self.rotateafter)
+ self.triggered = 0
+
+ def rot(self, event):
+ z = event.GetWheelRotation()
+ s = self.parent.l.GetSelection()
+ if self.prevsel != s:
+ self.i = 0
+ self.prevsel = s
+ if z < 0:
+ self.rotate_shape(-1)
+ else:
+ self.rotate_shape(1)
+
+ def repaint(self, event):
+ dc = wx.PaintDC(self)
+ self.paint(dc = dc)
+
+ def paint(self, coord1 = "x", coord2 = "y", dc = None):
+ if dc is None:
+ dc = wx.ClientDC(self)
+ scale = 2
+ dc.SetPen(wx.Pen(wx.Colour(100, 100, 100)))
+ for i in xrange(20):
+ dc.DrawLine(0, i * scale * 10, 400, i * scale * 10)
+ dc.DrawLine(i * scale * 10, 0, i * scale * 10, 400)
+ dc.SetPen(wx.Pen(wx.Colour(0, 0, 0)))
+ for i in xrange(4):
+ dc.DrawLine(0, i * scale * 50, 400, i * scale * 50)
+ dc.DrawLine(i * scale * 50, 0, i * scale * 50, 400)
+ dc.SetBrush(wx.Brush(wx.Colour(128, 255, 128)))
+ dc.SetPen(wx.Pen(wx.Colour(128, 128, 128)))
+ dcs = wx.MemoryDC()
+ for m in self.parent.models.values():
+ b = m.bitmap
+ im = b.ConvertToImage()
+ imgc = wx.Point(im.GetWidth() / 2, im.GetHeight() / 2)
+ im = im.Rotate(math.radians(m.rot), imgc, 0)
+ bm = wx.BitmapFromImage(im)
+ dcs.SelectObject(bm)
+ bsz = bm.GetSize()
+ dc.Blit(scale * m.offsets[0] - bsz[0] / 2, 400 - (scale * m.offsets[1] + bsz[1] / 2), bsz[0], bsz[1], dcs, 0, 0, useMask = 1)
+ del dc
+
+class StlPlaterPanel(PlaterPanel):
+
+ load_wildcard = _("STL files (*.stl;*.STL)|*.stl;*.STL|OpenSCAD files (*.scad)|*.scad")
+ save_wildcard = _("STL files (*.stl;*.STL)|*.stl;*.STL")
+
+ def prepare_ui(self, filenames = [], callback = None,
+ parent = None, build_dimensions = None, circular_platform = False,
+ simarrange_path = None, antialias_samples = 0):
+ super(StlPlaterPanel, self).prepare_ui(filenames, callback, parent, build_dimensions)
+ self.cutting = False
+ self.cutting_axis = None
+ self.cutting_dist = None
+ if glview:
+ viewer = stlview.StlViewPanel(self, (580, 580),
+ build_dimensions = self.build_dimensions,
+ circular = circular_platform,
+ antialias_samples = antialias_samples)
+ # Cutting tool
+ nrows = self.menusizer.GetRows()
+ self.menusizer.Add(wx.StaticText(self.menupanel, -1, _("Cut along:")),
+ pos = (nrows, 0), span = (1, 1), flag = wx.ALIGN_CENTER)
+ cutconfirmbutton = wx.Button(self.menupanel, label = _("Confirm cut"))
+ cutconfirmbutton.Bind(wx.EVT_BUTTON, self.cut_confirm)
+ cutconfirmbutton.Disable()
+ self.cutconfirmbutton = cutconfirmbutton
+ self.menusizer.Add(cutconfirmbutton, pos = (nrows, 1), span = (1, 1), flag = wx.EXPAND)
+ cutpanel = wx.Panel(self.menupanel, -1)
+ cutsizer = self.cutsizer = wx.BoxSizer(wx.HORIZONTAL)
+ cutpanel.SetSizer(cutsizer)
+ cutxplusbutton = wx.ToggleButton(cutpanel, label = _(">X"), style = wx.BU_EXACTFIT)
+ cutxplusbutton.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.start_cutting_tool(event, "x", 1))
+ cutsizer.Add(cutxplusbutton, 1, flag = wx.EXPAND)
+ cutzplusbutton = wx.ToggleButton(cutpanel, label = _(">Y"), style = wx.BU_EXACTFIT)
+ cutzplusbutton.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.start_cutting_tool(event, "y", 1))
+ cutsizer.Add(cutzplusbutton, 1, flag = wx.EXPAND)
+ cutzplusbutton = wx.ToggleButton(cutpanel, label = _(">Z"), style = wx.BU_EXACTFIT)
+ cutzplusbutton.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.start_cutting_tool(event, "z", 1))
+ cutsizer.Add(cutzplusbutton, 1, flag = wx.EXPAND)
+ cutxminusbutton = wx.ToggleButton(cutpanel, label = _(" 0:
+ logging.error(_("Plate full, please remove some objects"))
+ break
+ if "File:" in line:
+ bits = pos_regexp.match(line).groups()
+ filename = bits[0]
+ x = float(bits[1])
+ y = float(bits[2])
+ rot = -float(bits[3])
+ for name, model in models.items():
+ # FIXME: not sure this is going to work superwell with utf8
+ if model.filename == filename:
+ model.offsets[0] = x + self.build_dimensions[3]
+ model.offsets[1] = y + self.build_dimensions[4]
+ model.rot = rot
+ del models[name]
+ break
+ if p.wait() != 0:
+ raise RuntimeError(_("simarrange failed"))
+
+StlPlater = make_plater(StlPlaterPanel)