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)