-from __future__ import absolute_import\r
-import __init__\r
-\r
-import wx, os, platform, types, webbrowser, math, subprocess, threading, time, re, shutil\r
-import ConfigParser\r
-import numpy\r
-\r
-from wx import glcanvas\r
-try:\r
- import OpenGL\r
- OpenGL.ERROR_CHECKING = False\r
- from OpenGL.GLU import *\r
- from OpenGL.GL import *\r
- hasOpenGLlibs = True\r
-except:\r
- print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"\r
- hasOpenGLlibs = False\r
-\r
-from gui import opengl\r
-from gui import toolbarUtil\r
-from gui import icon\r
-from gui import configBase\r
-from gui import printWindow\r
-from gui import dropTarget\r
-from gui import taskbar\r
-from util import validators\r
-from util import profile\r
-from util import util3d\r
-from util import meshLoader\r
-from util import stl\r
-from util import mesh\r
-from util import sliceRun\r
-from util import gcodeInterpreter\r
-from util import exporer\r
-\r
-class Action(object):\r
- pass\r
-\r
-class ProjectObject(object):\r
- def __init__(self, parent, filename):\r
- super(ProjectObject, self).__init__()\r
-\r
- self.mesh = meshLoader.loadMesh(filename)\r
-\r
- self.parent = parent\r
- self.filename = filename\r
- self.scale = 1.0\r
- self.rotate = 0.0\r
- self.flipX = False\r
- self.flipY = False\r
- self.flipZ = False\r
- self.swapXZ = False\r
- self.swapYZ = False\r
- self.extruder = 0\r
- self.profile = None\r
- \r
- self.modelDisplayList = None\r
- self.modelDirty = False\r
-\r
- self.mesh.getMinimumZ()\r
- \r
- self.centerX = -self.getMinimum()[0] + 5\r
- self.centerY = -self.getMinimum()[1] + 5\r
- \r
- self.updateModelTransform()\r
-\r
- self.centerX = -self.getMinimum()[0] + 5\r
- self.centerY = -self.getMinimum()[1] + 5\r
-\r
- def isSameExceptForPosition(self, other):\r
- if self.filename != other.filename:\r
- return False\r
- if self.scale != other.scale:\r
- return False\r
- if self.rotate != other.rotate:\r
- return False\r
- if self.flipX != other.flipX:\r
- return False\r
- if self.flipY != other.flipY:\r
- return False\r
- if self.flipZ != other.flipZ:\r
- return False\r
- if self.swapXZ != other.swapXZ:\r
- return False\r
- if self.swapYZ != other.swapYZ:\r
- return False\r
- if self.extruder != other.extruder:\r
- return False\r
- if self.profile != other.profile:\r
- return False\r
- return True\r
-\r
- def updateModelTransform(self):\r
- self.mesh.setRotateMirror(self.rotate, self.flipX, self.flipY, self.flipZ, self.swapXZ, self.swapYZ)\r
- minZ = self.mesh.getMinimumZ()\r
- minV = self.getMinimum()\r
- maxV = self.getMaximum()\r
- self.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minZ])\r
- minZ = self.mesh.getMinimumZ()\r
- self.modelDirty = True\r
- \r
- def getMinimum(self):\r
- return self.mesh.getMinimum()\r
- def getMaximum(self):\r
- return self.mesh.getMaximum()\r
- def getSize(self):\r
- return self.mesh.getSize()\r
- \r
- def clone(self):\r
- p = ProjectObject(self.parent, self.filename)\r
-\r
- p.centerX = self.centerX + 5\r
- p.centerY = self.centerY + 5\r
- \r
- p.filename = self.filename\r
- p.scale = self.scale\r
- p.rotate = self.rotate\r
- p.flipX = self.flipX\r
- p.flipY = self.flipY\r
- p.flipZ = self.flipZ\r
- p.swapXZ = self.swapXZ\r
- p.swapYZ = self.swapYZ\r
- p.extruder = self.extruder\r
- p.profile = self.profile\r
- \r
- p.updateModelTransform()\r
- \r
- return p\r
- \r
- def clampXY(self):\r
- if self.centerX < -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]:\r
- self.centerX = -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]\r
- if self.centerY < -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]:\r
- self.centerY = -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]\r
- if self.centerX > self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale:\r
- self.centerX = self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale\r
- if self.centerY > self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale:\r
- self.centerY = self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale\r
-\r
-class projectPlanner(wx.Frame):\r
- "Main user interface window"\r
- def __init__(self):\r
- super(projectPlanner, self).__init__(None, title='Cura - Project Planner')\r
- \r
- wx.EVT_CLOSE(self, self.OnClose)\r
- self.panel = wx.Panel(self, -1)\r
- self.SetSizer(wx.BoxSizer(wx.VERTICAL))\r
- self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)\r
- #self.SetIcon(icon.getMainIcon())\r
- \r
- self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))\r
- \r
- self.list = []\r
- self.selection = None\r
- self.printMode = 0\r
- self.alwaysAutoPlace = True\r
-\r
- self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])\r
- self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])\r
- self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])\r
-\r
- self.extruderOffset = [\r
- numpy.array([0,0,0]),\r
- numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),\r
- numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),\r
- numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]\r
-\r
- self.toolbar = toolbarUtil.Toolbar(self.panel)\r
-\r
- toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')\r
- toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')\r
- self.toolbar.AddSeparator()\r
- group = []\r
- toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick).SetValue(self.alwaysAutoPlace)\r
- toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(not self.alwaysAutoPlace)\r
- self.toolbar.AddSeparator()\r
- toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')\r
- self.toolbar.AddSeparator()\r
- toolbarUtil.NormalButton(self.toolbar, self.OnCutMesh, 'cut-mesh.png', 'Cut a plate STL into multiple STL files, and add those files to the project.\nNote: Splitting up plates sometimes takes a few minutes.')\r
- toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')\r
- self.toolbar.AddSeparator()\r
- group = []\r
- self.printOneAtATime = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Print one object at a time', callback=self.OnPrintTypeChange)\r
- self.printAllAtOnce = toolbarUtil.RadioButton(self.toolbar, group, 'all-at-once-on.png', 'all-at-once-off.png', 'Print all the objects at once', callback=self.OnPrintTypeChange)\r
- self.toolbar.AddSeparator()\r
- toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')\r
- \r
- self.toolbar.Realize()\r
-\r
- self.toolbar2 = toolbarUtil.Toolbar(self.panel)\r
-\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')\r
- self.toolbar2.AddSeparator()\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to prepare a specific object.')\r
- self.toolbar2.AddSeparator()\r
- if not self.alwaysAutoPlace:\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')\r
- toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Prepare to project into a gcode file.')\r
- self.toolbar2.Realize()\r
-\r
- self.toolbar3 = toolbarUtil.Toolbar(self.panel)\r
- self.mirrorX = toolbarUtil.ToggleButton(self.toolbar3, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.OnMirrorChange)\r
- self.mirrorY = toolbarUtil.ToggleButton(self.toolbar3, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.OnMirrorChange)\r
- self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar3, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.OnMirrorChange)\r
- self.toolbar3.AddSeparator()\r
-\r
- # Swap\r
- self.swapXZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.OnMirrorChange)\r
- self.swapYZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.OnMirrorChange)\r
- self.toolbar3.Realize()\r
- \r
- sizer = wx.GridBagSizer(2,2)\r
- self.panel.SetSizer(sizer)\r
- self.preview = PreviewGLCanvas(self.panel, self)\r
- self.listbox = wx.ListBox(self.panel, -1, choices=[])\r
- self.addButton = wx.Button(self.panel, -1, "Add")\r
- self.remButton = wx.Button(self.panel, -1, "Remove")\r
- self.sliceButton = wx.Button(self.panel, -1, "Prepare")\r
- if not self.alwaysAutoPlace:\r
- self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")\r
- \r
- sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)\r
- sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)\r
- sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)\r
- sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)\r
- sizer.Add(self.toolbar3, (2,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)\r
- sizer.Add(self.addButton, (3,1), span=(1,1))\r
- sizer.Add(self.remButton, (3,2), span=(1,1))\r
- sizer.Add(self.sliceButton, (4,1), span=(1,1))\r
- if not self.alwaysAutoPlace:\r
- sizer.Add(self.autoPlaceButton, (4,2), span=(1,1))\r
- sizer.AddGrowableCol(0)\r
- sizer.AddGrowableRow(1)\r
- \r
- self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)\r
- self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)\r
- self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)\r
- if not self.alwaysAutoPlace:\r
- self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)\r
- self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)\r
-\r
- panel = wx.Panel(self.panel, -1)\r
- sizer.Add(panel, (5,1), span=(1,2))\r
- \r
- sizer = wx.GridBagSizer(2,2)\r
- panel.SetSizer(sizer)\r
- \r
- self.scaleCtrl = wx.TextCtrl(panel, -1, '')\r
- self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)\r
- self.rotateCtrl.SetRange(0, 360)\r
-\r
- sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)\r
- sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
- sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)\r
- sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
-\r
- if int(profile.getPreference('extruder_amount')) > 1:\r
- self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)\r
- sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)\r
- sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
- self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)\r
-\r
- self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)\r
- self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)\r
-\r
- self.SetSize((800,600))\r
-\r
- def OnClose(self, e):\r
- self.Destroy()\r
-\r
- def OnQuit(self, e):\r
- self.Close()\r
- \r
- def OnPreferences(self, e):\r
- prefDialog = preferencesDialog(self)\r
- prefDialog.Centre()\r
- prefDialog.Show(True)\r
- \r
- def OnCutMesh(self, e):\r
- dlg=wx.FileDialog(self, "Open file to cut", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)\r
- dlg.SetWildcard(meshLoader.wildcardFilter())\r
- if dlg.ShowModal() == wx.ID_OK:\r
- filename = dlg.GetPath()\r
- model = meshLoader.loadMesh(filename)\r
- pd = wx.ProgressDialog('Splitting model.', 'Splitting model into multiple parts.', model.vertexCount, self, wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH)\r
- parts = model.splitToParts(pd.Update)\r
- for part in parts:\r
- partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))\r
- stl.saveAsSTL(part, partFilename)\r
- item = ProjectObject(self, partFilename)\r
- self.list.append(item)\r
- self.selection = item\r
- self._updateListbox()\r
- self.OnListSelect(None)\r
- pd.Destroy()\r
- self.preview.Refresh()\r
- dlg.Destroy()\r
- \r
- def OnDropFiles(self, filenames):\r
- for filename in filenames:\r
- item = ProjectObject(self, filename)\r
- profile.putPreference('lastFile', item.filename)\r
- self.list.append(item)\r
- self.selection = item\r
- self._updateListbox()\r
- self.OnListSelect(None)\r
- self.preview.Refresh()\r
-\r
- def OnPrintTypeChange(self):\r
- self.printMode = 0\r
- if self.printAllAtOnce.GetValue():\r
- self.printMode = 1\r
- if self.alwaysAutoPlace:\r
- self.OnAutoPlace(None)\r
- self.preview.Refresh()\r
- \r
- def OnSaveCombinedSTL(self, e):\r
- dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)\r
- dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")\r
- if dlg.ShowModal() == wx.ID_OK:\r
- self._saveCombinedSTL(dlg.GetPath())\r
- dlg.Destroy()\r
- \r
- def _saveCombinedSTL(self, filename):\r
- totalCount = 0\r
- for item in self.list:\r
- totalCount += item.mesh.vertexCount\r
- output = mesh.mesh()\r
- output._prepareVertexCount(totalCount)\r
- for item in self.list:\r
- offset = numpy.array([item.centerX, item.centerY, 0])\r
- for v in item.mesh.vertexes:\r
- v0 = v * item.scale + offset\r
- output.addVertex(v0[0], v0[1], v0[2])\r
- stl.saveAsSTL(output, filename)\r
- \r
- def OnSaveProject(self, e):\r
- dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)\r
- dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")\r
- if dlg.ShowModal() == wx.ID_OK:\r
- cp = ConfigParser.ConfigParser()\r
- i = 0\r
- for item in self.list:\r
- section = 'model_%d' % (i)\r
- cp.add_section(section)\r
- cp.set(section, 'filename', item.filename.encode("utf-8"))\r
- cp.set(section, 'centerX', str(item.centerX))\r
- cp.set(section, 'centerY', str(item.centerY))\r
- cp.set(section, 'scale', str(item.scale))\r
- cp.set(section, 'rotate', str(item.rotate))\r
- cp.set(section, 'flipX', str(item.flipX))\r
- cp.set(section, 'flipY', str(item.flipY))\r
- cp.set(section, 'flipZ', str(item.flipZ))\r
- cp.set(section, 'swapXZ', str(item.swapXZ))\r
- cp.set(section, 'swapYZ', str(item.swapYZ))\r
- cp.set(section, 'extruder', str(item.extruder+1))\r
- if item.profile != None:\r
- cp.set(section, 'profile', item.profile)\r
- i += 1\r
- cp.write(open(dlg.GetPath(), "w"))\r
- dlg.Destroy()\r
-\r
- def OnLoadProject(self, e):\r
- dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)\r
- dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")\r
- if dlg.ShowModal() == wx.ID_OK:\r
- cp = ConfigParser.ConfigParser()\r
- cp.read(dlg.GetPath())\r
- self.list = []\r
- i = 0\r
- while cp.has_section('model_%d' % (i)):\r
- section = 'model_%d' % (i)\r
- \r
- item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))\r
- item.centerX = float(cp.get(section, 'centerX'))\r
- item.centerY = float(cp.get(section, 'centerY'))\r
- item.scale = float(cp.get(section, 'scale'))\r
- item.rotate = float(cp.get(section, 'rotate'))\r
- item.flipX = cp.get(section, 'flipX') == 'True'\r
- item.flipY = cp.get(section, 'flipY') == 'True'\r
- item.flipZ = cp.get(section, 'flipZ') == 'True'\r
- item.swapXZ = cp.get(section, 'swapXZ') == 'True'\r
- item.swapYZ = cp.get(section, 'swapYZ') == 'True'\r
- if cp.has_option(section, 'extruder'):\r
- item.extuder = int(cp.get(section, 'extruder')) - 1\r
- if cp.has_option(section, 'profile'):\r
- item.profile = cp.get(section, 'profile')\r
- item.updateModelTransform()\r
- i += 1\r
- \r
- self.list.append(item)\r
-\r
- self.selected = self.list[0]\r
- self._updateListbox() \r
- self.OnListSelect(None)\r
- self.preview.Refresh()\r
-\r
- dlg.Destroy()\r
-\r
- def On3DClick(self):\r
- self.preview.yaw = 30\r
- self.preview.pitch = 60\r
- self.preview.zoom = 300\r
- self.preview.view3D = True\r
- self.preview.Refresh()\r
-\r
- def OnTopClick(self):\r
- self.preview.view3D = False\r
- self.preview.zoom = self.machineSize[0] / 2 + 10\r
- self.preview.offsetX = 0\r
- self.preview.offsetY = 0\r
- self.preview.Refresh()\r
-\r
- def OnListSelect(self, e):\r
- if self.listbox.GetSelection() == -1:\r
- return\r
- self.selection = self.list[self.listbox.GetSelection()]\r
- self.scaleCtrl.SetValue(str(self.selection.scale))\r
- self.rotateCtrl.SetValue(int(self.selection.rotate))\r
- if int(profile.getPreference('extruder_amount')) > 1:\r
-\r
- self.extruderCtrl.SetValue(str(self.selection.extruder+1))\r
-\r
- self.mirrorX.SetValue(self.selection.flipX)\r
- self.mirrorY.SetValue(self.selection.flipY)\r
- self.mirrorZ.SetValue(self.selection.flipZ)\r
- self.swapXZ.SetValue(self.selection.swapXZ)\r
- self.swapYZ.SetValue(self.selection.swapYZ)\r
- \r
- self.preview.Refresh()\r
- \r
- def OnAddModel(self, e):\r
- dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)\r
- dlg.SetWildcard(meshLoader.wildcardFilter())\r
- if dlg.ShowModal() == wx.ID_OK:\r
- for filename in dlg.GetPaths():\r
- item = ProjectObject(self, filename)\r
- profile.putPreference('lastFile', item.filename)\r
- self.list.append(item)\r
- self.selection = item\r
- self._updateListbox()\r
- self.OnListSelect(None)\r
- self.preview.Refresh()\r
- dlg.Destroy()\r
- \r
- def OnRemModel(self, e):\r
- if self.selection == None:\r
- return\r
- self.list.remove(self.selection)\r
- self._updateListbox()\r
- self.preview.Refresh()\r
- \r
- def OnMoveUp(self, e):\r
- if self.selection == None:\r
- return\r
- i = self.listbox.GetSelection()\r
- if i == 0:\r
- return\r
- self.list.remove(self.selection)\r
- self.list.insert(i-1, self.selection)\r
- self._updateListbox()\r
- self.preview.Refresh()\r
-\r
- def OnMoveDown(self, e):\r
- if self.selection == None:\r
- return\r
- i = self.listbox.GetSelection()\r
- if i == len(self.list) - 1:\r
- return\r
- self.list.remove(self.selection)\r
- self.list.insert(i+1, self.selection)\r
- self._updateListbox()\r
- self.preview.Refresh()\r
- \r
- def OnCopy(self, e):\r
- if self.selection == None:\r
- return\r
- \r
- item = self.selection.clone()\r
- self.list.insert(self.list.index(self.selection), item)\r
- self.selection = item\r
- \r
- self._updateListbox()\r
- self.preview.Refresh()\r
- \r
- def OnSetCustomProfile(self, e):\r
- if self.selection == None:\r
- return\r
-\r
- dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)\r
- dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")\r
- if dlg.ShowModal() == wx.ID_OK:\r
- self.selection.profile = dlg.GetPath()\r
- else:\r
- self.selection.profile = None\r
- self._updateListbox()\r
- dlg.Destroy()\r
- \r
- def _updateListbox(self):\r
- self.listbox.Clear()\r
- for item in self.list:\r
- if item.profile != None:\r
- self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")\r
- else:\r
- self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])\r
- if self.selection in self.list:\r
- self.listbox.SetSelection(self.list.index(self.selection))\r
- elif len(self.list) > 0:\r
- self.selection = self.list[0]\r
- self.listbox.SetSelection(0)\r
- else:\r
- self.selection = None\r
- self.listbox.SetSelection(-1)\r
- if self.alwaysAutoPlace:\r
- self.OnAutoPlace(None)\r
-\r
- def OnAutoPlace(self, e):\r
- bestAllowedSize = int(self.machineSize[1])\r
- bestArea = self._doAutoPlace(bestAllowedSize)\r
- for i in xrange(10, int(self.machineSize[1]), 10):\r
- area = self._doAutoPlace(i)\r
- if area < bestArea:\r
- bestAllowedSize = i\r
- bestArea = area\r
- self._doAutoPlace(bestAllowedSize)\r
- for item in self.list:\r
- item.clampXY()\r
- self.preview.Refresh()\r
- \r
- def _doAutoPlace(self, allowedSizeY):\r
- extraSizeMin, extraSizeMax = self.getExtraHeadSize()\r
-\r
- if extraSizeMin[0] > extraSizeMax[0]:\r
- posX = self.machineSize[0]\r
- dirX = -1\r
- else:\r
- posX = 0\r
- dirX = 1\r
- posY = 0\r
- dirY = 1\r
- \r
- minX = self.machineSize[0]\r
- minY = self.machineSize[1]\r
- maxX = 0\r
- maxY = 0\r
- for item in self.list:\r
- item.centerX = posX + item.getMaximum()[0] * item.scale * dirX\r
- item.centerY = posY + item.getMaximum()[1] * item.scale * dirY\r
- if item.centerY + item.getSize()[1] >= allowedSizeY:\r
- if dirX < 0:\r
- posX = minX - extraSizeMax[0] - 1\r
- else:\r
- posX = maxX + extraSizeMin[0] + 1\r
- posY = 0\r
- item.centerX = posX + item.getMaximum()[0] * item.scale * dirX\r
- item.centerY = posY + item.getMaximum()[1] * item.scale * dirY\r
- posY += item.getSize()[1] * item.scale * dirY + extraSizeMin[1] + 1\r
- minX = min(minX, item.centerX - item.getSize()[0] * item.scale / 2)\r
- minY = min(minY, item.centerY - item.getSize()[1] * item.scale / 2)\r
- maxX = max(maxX, item.centerX + item.getSize()[0] * item.scale / 2)\r
- maxY = max(maxY, item.centerY + item.getSize()[1] * item.scale / 2)\r
- \r
- for item in self.list:\r
- if dirX < 0:\r
- item.centerX -= minX / 2\r
- else:\r
- item.centerX += (self.machineSize[0] - maxX) / 2\r
- item.centerY += (self.machineSize[1] - maxY) / 2\r
- \r
- if minX < 0 or maxX > self.machineSize[0]:\r
- return ((maxX - minX) + (maxY - minY)) * 100\r
- \r
- return (maxX - minX) + (maxY - minY)\r
-\r
- def OnSlice(self, e):\r
- dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)\r
- dlg.SetWildcard("GCode file (*.gcode)|*.gcode")\r
- if dlg.ShowModal() != wx.ID_OK:\r
- dlg.Destroy()\r
- return\r
- resultFilename = dlg.GetPath()\r
- dlg.Destroy()\r
-\r
- put = profile.setTempOverride\r
- oldProfile = profile.getGlobalProfileString()\r
- \r
- put('add_start_end_gcode', 'False')\r
- put('gcode_extension', 'project_tmp')\r
- if self.printMode == 0:\r
- clearZ = 0\r
- actionList = []\r
- for item in self.list:\r
- if item.profile != None and os.path.isfile(item.profile):\r
- profile.loadGlobalProfile(item.profile)\r
- put('object_center_x', item.centerX - self.extruderOffset[item.extruder][0])\r
- put('object_center_y', item.centerY - self.extruderOffset[item.extruder][1])\r
- put('model_scale', item.scale)\r
- put('flip_x', item.flipX)\r
- put('flip_y', item.flipY)\r
- put('flip_z', item.flipZ)\r
- put('model_rotate_base', item.rotate)\r
- put('swap_xz', item.swapXZ)\r
- put('swap_yz', item.swapYZ)\r
- \r
- action = Action()\r
- action.sliceCmd = sliceRun.getSliceCommand(item.filename)\r
- action.centerX = item.centerX\r
- action.centerY = item.centerY\r
- action.temperature = profile.getProfileSettingFloat('print_temperature')\r
- action.extruder = item.extruder\r
- action.filename = item.filename\r
- clearZ = max(clearZ, item.getSize()[2] * item.scale + 5.0)\r
- action.clearZ = clearZ\r
- action.leaveResultForNextSlice = False\r
- action.usePreviousSlice = False\r
- actionList.append(action)\r
-\r
- if self.list.index(item) > 0 and item.isSameExceptForPosition(self.list[self.list.index(item)-1]):\r
- actionList[-2].leaveResultForNextSlice = True\r
- actionList[-1].usePreviousSlice = True\r
-\r
- if item.profile != None:\r
- profile.loadGlobalProfileFromString(oldProfile)\r
- \r
- else:\r
- self._saveCombinedSTL(resultFilename + "_temp_.stl")\r
- put('model_scale', 1.0)\r
- put('flip_x', False)\r
- put('flip_y', False)\r
- put('flip_z', False)\r
- put('model_rotate_base', 0)\r
- put('swap_xz', False)\r
- put('swap_yz', False)\r
- actionList = []\r
- \r
- action = Action()\r
- action.sliceCmd = sliceRun.getSliceCommand(resultFilename + "_temp_.stl")\r
- action.centerX = profile.getPreferenceFloat('machine_width') / 2\r
- action.centerY = profile.getPreferenceFloat('machine_depth') / 2\r
- action.temperature = profile.getProfileSettingFloat('print_temperature')\r
- action.extruder = 0\r
- action.filename = resultFilename + "_temp_.stl"\r
- action.clearZ = 0\r
- action.leaveResultForNextSlice = False\r
- action.usePreviousSlice = False\r
-\r
- actionList.append(action)\r
- \r
- #Restore the old profile.\r
- profile.resetTempOverride()\r
- \r
- pspw = ProjectSliceProgressWindow(actionList, resultFilename)\r
- pspw.extruderOffset = self.extruderOffset\r
- pspw.Centre()\r
- pspw.Show(True)\r
- \r
- def OnScaleChange(self, e):\r
- if self.selection == None:\r
- return\r
- try:\r
- self.selection.scale = float(self.scaleCtrl.GetValue())\r
- except ValueError:\r
- self.selection.scale = 1.0\r
- self.preview.Refresh()\r
- \r
- def OnRotateChange(self, e):\r
- if self.selection == None:\r
- return\r
- self.selection.rotate = float(self.rotateCtrl.GetValue())\r
- self.selection.updateModelTransform()\r
- if self.alwaysAutoPlace:\r
- self.OnAutoPlace(None)\r
- self.preview.Refresh()\r
-\r
- def OnExtruderChange(self, e):\r
- if self.selection == None:\r
- return\r
- self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1\r
- self.preview.Refresh()\r
- \r
- def OnMirrorChange(self):\r
- if self.selection == None:\r
- return\r
- self.selection.flipX = self.mirrorX.GetValue()\r
- self.selection.flipY = self.mirrorY.GetValue()\r
- self.selection.flipZ = self.mirrorZ.GetValue()\r
- self.selection.swapXZ = self.swapXZ.GetValue()\r
- self.selection.swapYZ = self.swapYZ.GetValue()\r
- self.selection.updateModelTransform()\r
- if self.alwaysAutoPlace:\r
- self.OnAutoPlace(None)\r
- self.preview.Refresh()\r
-\r
- def getExtraHeadSize(self):\r
- extraSizeMin = self.headSizeMin\r
- extraSizeMax = self.headSizeMax\r
- if profile.getProfileSettingFloat('skirt_line_count') > 0:\r
- skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')\r
- extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])\r
- extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])\r
- if profile.getProfileSetting('enable_raft') != 'False':\r
- raftSize = profile.getProfileSettingFloat('raft_margin') * 2\r
- extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])\r
- extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])\r
- if profile.getProfileSetting('support') != 'None':\r
- extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])\r
- extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])\r
-\r
- if self.printMode == 1:\r
- extraSizeMin = numpy.array([6.0, 6.0, 0])\r
- extraSizeMax = numpy.array([6.0, 6.0, 0])\r
- \r
- return extraSizeMin, extraSizeMax\r
-\r
-class PreviewGLCanvas(glcanvas.GLCanvas):\r
- def __init__(self, parent, projectPlannerWindow):\r
- attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)\r
- glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)\r
- self.parent = projectPlannerWindow\r
- self.context = glcanvas.GLContext(self)\r
- wx.EVT_PAINT(self, self.OnPaint)\r
- wx.EVT_SIZE(self, self.OnSize)\r
- wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)\r
- wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)\r
- wx.EVT_MOTION(self, self.OnMouseMotion)\r
- wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)\r
- self.yaw = 30\r
- self.pitch = 60\r
- self.offsetX = 0\r
- self.offsetY = 0\r
- self.view3D = self.parent.alwaysAutoPlace\r
- if self.view3D:\r
- self.zoom = 300\r
- else:\r
- self.zoom = self.parent.machineSize[0] / 2 + 10\r
- self.allowDrag = False\r
-\r
- self.objColor = profile.getPreferenceColour('model_colour')\r
-\r
- def OnMouseLeftDown(self,e):\r
- self.allowDrag = True\r
- if not self.parent.alwaysAutoPlace and not self.view3D:\r
- #TODO: Translate mouse X/Y to 3D X/Y/Z\r
- for item in self.parent.list:\r
- iMin = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]\r
- iMax = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]\r
- \r
- def OnMouseMotion(self,e):\r
- if self.allowDrag and e.Dragging() and e.LeftIsDown():\r
- if self.view3D:\r
- self.yaw += e.GetX() - self.oldX\r
- self.pitch -= e.GetY() - self.oldY\r
- if self.pitch > 170:\r
- self.pitch = 170\r
- if self.pitch < 10:\r
- self.pitch = 10\r
- elif not self.parent.alwaysAutoPlace:\r
- item = self.parent.selection\r
- if item != None:\r
- item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2\r
- item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2\r
- item.clampXY()\r
- self.Refresh()\r
- else:\r
- self.allowDrag = False\r
- if e.Dragging() and e.RightIsDown():\r
- if self.view3D:\r
- self.zoom += e.GetY() - self.oldY\r
- if self.zoom < 1:\r
- self.zoom = 1\r
- self.Refresh()\r
- self.oldX = e.GetX()\r
- self.oldY = e.GetY()\r
- \r
- def OnMouseWheel(self,e):\r
- if self.view3D:\r
- self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0\r
- if self.zoom < 1.0:\r
- self.zoom = 1.0\r
- self.Refresh()\r
- \r
- def OnEraseBackground(self,event):\r
- #Workaround for windows background redraw flicker.\r
- pass\r
- \r
- def OnSize(self,event):\r
- self.Refresh()\r
-\r
- def OnPaint(self,event):\r
- dc = wx.PaintDC(self)\r
- if not hasOpenGLlibs:\r
- dc.Clear()\r
- dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)\r
- return\r
- self.SetCurrent(self.context)\r
- opengl.InitGL(self, self.view3D, self.zoom)\r
- if self.view3D:\r
- glTranslate(0,0,-self.zoom)\r
- glRotate(-self.pitch, 1,0,0)\r
- glRotate(self.yaw, 0,0,1)\r
- else:\r
- glTranslate(self.offsetX, self.offsetY, 0.0)\r
- glTranslate(-self.parent.machineSize[0]/2, -self.parent.machineSize[1]/2, 0)\r
-\r
- self.OnDraw()\r
- self.SwapBuffers()\r
-\r
- def OnDraw(self):\r
- machineSize = self.parent.machineSize\r
- extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()\r
-\r
- for item in self.parent.list:\r
- item.validPlacement = True\r
- item.gotHit = False\r
- \r
- for idx1 in xrange(0, len(self.parent.list)):\r
- item = self.parent.list[idx1]\r
- iMin1 = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin - self.parent.extruderOffset[item.extruder]\r
- iMax1 = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax - self.parent.extruderOffset[item.extruder]\r
- for idx2 in xrange(0, idx1):\r
- item2 = self.parent.list[idx2]\r
- iMin2 = (item2.getMinimum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])\r
- iMax2 = (item2.getMaximum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])\r
- if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:\r
- item.validPlacement = False\r
- item2.gotHit = True\r
- \r
- seenSelected = False\r
- for item in self.parent.list:\r
- if item == self.parent.selection:\r
- seenSelected = True\r
- if item.modelDisplayList == None:\r
- item.modelDisplayList = glGenLists(1);\r
- if item.modelDirty:\r
- item.modelDirty = False\r
- modelSize = item.getMaximum() - item.getMinimum()\r
- glNewList(item.modelDisplayList, GL_COMPILE)\r
- opengl.DrawMesh(item.mesh)\r
- glEndList()\r
- \r
- if item.validPlacement:\r
- if self.parent.selection == item:\r
- glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x + 0.2, self.objColor))\r
- glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))\r
- else:\r
- glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor)\r
- glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))\r
- else:\r
- if self.parent.selection == item:\r
- glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])\r
- glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])\r
- else:\r
- glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])\r
- glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])\r
- glPushMatrix()\r
- \r
- glEnable(GL_LIGHTING)\r
- glTranslate(item.centerX, item.centerY, 0)\r
- glPushMatrix()\r
- glScalef(item.scale, item.scale, item.scale)\r
- glCallList(item.modelDisplayList)\r
- glPopMatrix()\r
- \r
- vMin = item.getMinimum() * item.scale\r
- vMax = item.getMaximum() * item.scale\r
- vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]\r
- vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]\r
-\r
- glDisable(GL_LIGHTING)\r
-\r
- if not self.parent.alwaysAutoPlace:\r
- if self.parent.selection == item:\r
- if item.gotHit:\r
- glColor3f(1.0,0.0,0.3)\r
- else:\r
- glColor3f(1.0,0.0,1.0)\r
- opengl.DrawBox(vMin, vMax)\r
- if item.gotHit:\r
- glColor3f(1.0,0.3,0.0)\r
- else:\r
- glColor3f(1.0,1.0,0.0)\r
- opengl.DrawBox(vMinHead, vMaxHead)\r
- elif seenSelected:\r
- if item.gotHit:\r
- glColor3f(0.5,0.0,0.1)\r
- else:\r
- glColor3f(0.5,0.0,0.5)\r
- opengl.DrawBox(vMinHead, vMaxHead)\r
- else:\r
- if item.gotHit:\r
- glColor3f(0.7,0.1,0.0)\r
- else:\r
- glColor3f(0.7,0.7,0.0)\r
- opengl.DrawBox(vMin, vMax)\r
- \r
- glPopMatrix()\r
- \r
- opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))\r
- glFlush()\r
-\r
-class ProjectSliceProgressWindow(wx.Frame):\r
- def __init__(self, actionList, resultFilename):\r
- super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')\r
- self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))\r
- \r
- self.actionList = actionList\r
- self.resultFilename = resultFilename\r
- self.abort = False\r
- self.prevStep = 'start'\r
- self.totalDoneFactor = 0.0\r
- self.startTime = time.time()\r
- self.sliceStartTime = time.time()\r
- \r
- self.sizer = wx.GridBagSizer(2, 2) \r
- self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))\r
- self.progressGauge = wx.Gauge(self, -1)\r
- self.progressGauge.SetRange(10000)\r
- self.progressGauge2 = wx.Gauge(self, -1)\r
- self.progressGauge2.SetRange(len(self.actionList))\r
- self.abortButton = wx.Button(self, -1, "Abort")\r
- self.sizer.Add(self.statusText, (0,0), span=(1,5))\r
- self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)\r
- self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)\r
-\r
- self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)\r
- self.sizer.AddGrowableCol(0)\r
- self.sizer.AddGrowableRow(0)\r
-\r
- self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)\r
- self.SetSizer(self.sizer)\r
- self.Layout()\r
- self.Fit()\r
- \r
- threading.Thread(target=self.OnRun).start()\r
-\r
- def OnAbort(self, e):\r
- if self.abort:\r
- self.Close()\r
- else:\r
- self.abort = True\r
- self.abortButton.SetLabel('Close')\r
-\r
- def SetProgress(self, stepName, layer, maxLayer):\r
- if self.prevStep != stepName:\r
- self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]\r
- newTime = time.time()\r
- #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName\r
- self.startTime = newTime\r
- self.prevStep = stepName\r
- \r
- progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000\r
- self.progressGauge.SetValue(int(progresValue))\r
- self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")\r
- taskbar.setProgress(self, 10000 * self.progressGauge2.GetValue() + int(progresValue), 10000 * len(self.actionList))\r
- \r
- def OnRun(self):\r
- resultFile = open(self.resultFilename, "w")\r
- put = profile.setTempOverride\r
- self.progressLog = []\r
- for action in self.actionList:\r
- wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))\r
- if not action.usePreviousSlice:\r
- p = sliceRun.startSliceCommandProcess(action.sliceCmd)\r
- line = p.stdout.readline()\r
- \r
- maxValue = 1\r
- while(len(line) > 0):\r
- line = line.rstrip()\r
- if line[0:9] == "Progress[" and line[-1:] == "]":\r
- progress = line[9:-1].split(":")\r
- if len(progress) > 2:\r
- maxValue = int(progress[2])\r
- wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)\r
- else:\r
- self.progressLog.append(line)\r
- wx.CallAfter(self.statusText.SetLabel, line)\r
- if self.abort:\r
- p.terminate()\r
- wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")\r
- resultFile.close()\r
- return\r
- line = p.stdout.readline()\r
- self.returnCode = p.wait()\r
- \r
- put('object_center_x', action.centerX - self.extruderOffset[action.extruder][0])\r
- put('object_center_y', action.centerY - self.extruderOffset[action.extruder][1])\r
- put('clear_z', action.clearZ)\r
- put('extruder', action.extruder)\r
- put('print_temperature', action.temperature)\r
- \r
- if action == self.actionList[0]:\r
- resultFile.write(';TYPE:CUSTOM\n')\r
- resultFile.write('T%d\n' % (action.extruder))\r
- currentExtruder = action.extruder\r
- prevTemp = action.temperature\r
- startGCode = profile.getAlterationFileContents('start.gcode')\r
- startGCode = startGCode.replace('?filename?', 'Multiple files')\r
- resultFile.write(startGCode)\r
- else:\r
- #reset the extrusion length, and move to the next object center.\r
- resultFile.write(';TYPE:CUSTOM\n')\r
- if prevTemp != action.temperature and action.temperature > 0:\r
- resultFile.write('M104 S%d\n' % (int(action.temperature)))\r
- prevTemp = action.temperature\r
- resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))\r
- resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))\r
- profile.resetTempOverride()\r
- \r
- if not action.usePreviousSlice:\r
- f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")\r
- data = f.read(4096)\r
- while data != '':\r
- resultFile.write(data)\r
- data = f.read(4096)\r
- f.close()\r
- savedCenterX = action.centerX\r
- savedCenterY = action.centerY\r
- else:\r
- f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")\r
- for line in f:\r
- if line[0] != ';':\r
- if 'X' in line:\r
- line = self._adjustNumberInLine(line, 'X', action.centerX - savedCenterX)\r
- if 'Y' in line:\r
- line = self._adjustNumberInLine(line, 'Y', action.centerY - savedCenterY)\r
- resultFile.write(line)\r
- f.close()\r
-\r
- if not action.leaveResultForNextSlice:\r
- os.remove(sliceRun.getExportFilename(action.filename, "project_tmp"))\r
- \r
- wx.CallAfter(self.progressGauge.SetValue, 10000)\r
- self.totalDoneFactor = 0.0\r
- wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)\r
- \r
- resultFile.write(';TYPE:CUSTOM\n')\r
- if len(self.actionList) > 1 and self.actionList[-1].clearZ > 1:\r
- #only move to higher Z if we have sliced more then 1 object. This solves the "move into print after printing" problem with the print-all-at-once option.\r
- resultFile.write('G1 Z%f F%f\n' % (self.actionList[-1].clearZ, profile.getProfileSettingFloat('max_z_speed') * 60))\r
- resultFile.write(profile.getAlterationFileContents('end.gcode'))\r
- resultFile.close()\r
- \r
- gcode = gcodeInterpreter.gcode()\r
- gcode.load(self.resultFilename)\r
- \r
- self.abort = True\r
- sliceTime = time.time() - self.sliceStartTime\r
- status = "Build: %s" % (self.resultFilename)\r
- status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)\r
- status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)\r
- status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))\r
- cost = gcode.calculateCost()\r
- if cost != False:\r
- status += "\nCost: %s" % (cost)\r
- profile.replaceGCodeTags(self.resultFilename, gcode)\r
- wx.CallAfter(self.statusText.SetLabel, status)\r
- wx.CallAfter(self.OnSliceDone)\r
- \r
- def _adjustNumberInLine(self, line, tag, f):\r
- m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)\r
- return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'\r
- \r
- def OnSliceDone(self):\r
- self.abortButton.Destroy()\r
- self.closeButton = wx.Button(self, -1, "Close")\r
- self.printButton = wx.Button(self, -1, "Print")\r
- self.logButton = wx.Button(self, -1, "Show log")\r
- self.sizer.Add(self.closeButton, (3,0), span=(1,1))\r
- self.sizer.Add(self.printButton, (3,1), span=(1,1))\r
- self.sizer.Add(self.logButton, (3,2), span=(1,1))\r
- if exporer.hasExporer():\r
- self.openFileLocationButton = wx.Button(self, -1, "Open file location")\r
- self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)\r
- self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))\r
- if profile.getPreference('sdpath') != '':\r
- self.copyToSDButton = wx.Button(self, -1, "To SDCard")\r
- self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)\r
- self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))\r
- self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)\r
- self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)\r
- self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)\r
- self.Layout()\r
- self.Fit()\r
- taskbar.setBusy(self, False)\r
-\r
- def OnCopyToSD(self, e):\r
- filename = os.path.basename(self.resultFilename)\r
- if profile.getPreference('sdshortnames') == 'True':\r
- filename = sliceRun.getShortFilename(filename)\r
- shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))\r
- \r
- def OnOpenFileLocation(self, e):\r
- exporer.openExporer(self.resultFilename)\r
- \r
- def OnPrint(self, e):\r
- printWindow.printFile(self.resultFilename)\r
-\r
- def OnShowLog(self, e):\r
- LogWindow('\n'.join(self.progressLog))\r
-\r
-class preferencesDialog(configBase.configWindowBase):\r
- def __init__(self, parent):\r
- super(preferencesDialog, self).__init__(title="Project Planner Preferences")\r
- \r
- self.parent = parent\r
- wx.EVT_CLOSE(self, self.OnClose)\r
- \r
- extruderAmount = int(profile.getPreference('extruder_amount'))\r
- \r
- left, right, main = self.CreateConfigPanel(self)\r
- configBase.TitleRow(left, 'Machine head size')\r
- c = configBase.SettingRow(left, 'Head size - X towards home (mm)', 'extruder_head_size_min_x', '0', 'Size of your printer head in the X direction, on the Ultimaker your fan is in this direction.', type = 'preference')\r
- validators.validFloat(c, 0.1)\r
- c = configBase.SettingRow(left, 'Head size - X towards end (mm)', 'extruder_head_size_max_x', '0', 'Size of your printer head in the X direction.', type = 'preference')\r
- validators.validFloat(c, 0.1)\r
- c = configBase.SettingRow(left, 'Head size - Y towards home (mm)', 'extruder_head_size_min_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')\r
- validators.validFloat(c, 0.1)\r
- c = configBase.SettingRow(left, 'Head size - Y towards end (mm)', 'extruder_head_size_max_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')\r
- validators.validFloat(c, 0.1)\r
- c = configBase.SettingRow(left, 'Head gantry height (mm)', 'extruder_head_size_height', '0', 'The tallest object height that will always fit under your printers gantry system when the printer head is at the lowest Z position.', type = 'preference')\r
- validators.validFloat(c)\r
- \r
- self.okButton = wx.Button(left, -1, 'Ok')\r
- left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))\r
- self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)\r
- \r
- self.MakeModal(True)\r
- main.Fit()\r
- self.Fit()\r
-\r
- def OnClose(self, e):\r
- self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])\r
- self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])\r
- self.parent.Refresh()\r
-\r
- self.MakeModal(False)\r
- self.Destroy()\r
-\r
-class LogWindow(wx.Frame):\r
- def __init__(self, logText):\r
- super(LogWindow, self).__init__(None, title="Slice log")\r
- self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)\r
- self.SetSize((400,300))\r
- self.Centre()\r
- self.Show(True)\r
-\r
-def main():\r
- app = wx.App(False)\r
- projectPlanner().Show(True)\r
- app.MainLoop()\r
-\r
-if __name__ == '__main__':\r
- main()\r
+from __future__ import absolute_import
+import __init__
+
+import wx, os, platform, types, webbrowser, math, subprocess, threading, time, re, shutil
+import ConfigParser
+import numpy
+
+from wx import glcanvas
+try:
+ import OpenGL
+ OpenGL.ERROR_CHECKING = False
+ from OpenGL.GLU import *
+ from OpenGL.GL import *
+ hasOpenGLlibs = True
+except:
+ print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
+ hasOpenGLlibs = False
+
+from gui import opengl
+from gui import toolbarUtil
+from gui import icon
+from gui import configBase
+from gui import printWindow
+from gui import dropTarget
+from gui import taskbar
+from util import validators
+from util import profile
+from util import util3d
+from util import meshLoader
+from util import stl
+from util import mesh
+from util import sliceRun
+from util import gcodeInterpreter
+from util import exporer
+
+class Action(object):
+ pass
+
+class ProjectObject(object):
+ def __init__(self, parent, filename):
+ super(ProjectObject, self).__init__()
+
+ self.mesh = meshLoader.loadMesh(filename)
+
+ self.parent = parent
+ self.filename = filename
+ self.scale = 1.0
+ self.rotate = 0.0
+ self.flipX = False
+ self.flipY = False
+ self.flipZ = False
+ self.swapXZ = False
+ self.swapYZ = False
+ self.extruder = 0
+ self.profile = None
+
+ self.modelDisplayList = None
+ self.modelDirty = False
+
+ self.mesh.getMinimumZ()
+
+ self.centerX = -self.getMinimum()[0] + 5
+ self.centerY = -self.getMinimum()[1] + 5
+
+ self.updateModelTransform()
+
+ self.centerX = -self.getMinimum()[0] + 5
+ self.centerY = -self.getMinimum()[1] + 5
+
+ def isSameExceptForPosition(self, other):
+ if self.filename != other.filename:
+ return False
+ if self.scale != other.scale:
+ return False
+ if self.rotate != other.rotate:
+ return False
+ if self.flipX != other.flipX:
+ return False
+ if self.flipY != other.flipY:
+ return False
+ if self.flipZ != other.flipZ:
+ return False
+ if self.swapXZ != other.swapXZ:
+ return False
+ if self.swapYZ != other.swapYZ:
+ return False
+ if self.extruder != other.extruder:
+ return False
+ if self.profile != other.profile:
+ return False
+ return True
+
+ def updateModelTransform(self):
+ self.mesh.setRotateMirror(self.rotate, self.flipX, self.flipY, self.flipZ, self.swapXZ, self.swapYZ)
+ minZ = self.mesh.getMinimumZ()
+ minV = self.getMinimum()
+ maxV = self.getMaximum()
+ self.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minZ])
+ minZ = self.mesh.getMinimumZ()
+ self.modelDirty = True
+
+ def getMinimum(self):
+ return self.mesh.getMinimum()
+ def getMaximum(self):
+ return self.mesh.getMaximum()
+ def getSize(self):
+ return self.mesh.getSize()
+
+ def clone(self):
+ p = ProjectObject(self.parent, self.filename)
+
+ p.centerX = self.centerX + 5
+ p.centerY = self.centerY + 5
+
+ p.filename = self.filename
+ p.scale = self.scale
+ p.rotate = self.rotate
+ p.flipX = self.flipX
+ p.flipY = self.flipY
+ p.flipZ = self.flipZ
+ p.swapXZ = self.swapXZ
+ p.swapYZ = self.swapYZ
+ p.extruder = self.extruder
+ p.profile = self.profile
+
+ p.updateModelTransform()
+
+ return p
+
+ def clampXY(self):
+ if self.centerX < -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]:
+ self.centerX = -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]
+ if self.centerY < -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]:
+ self.centerY = -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]
+ if self.centerX > self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale:
+ self.centerX = self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale
+ if self.centerY > self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale:
+ self.centerY = self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale
+
+class projectPlanner(wx.Frame):
+ "Main user interface window"
+ def __init__(self):
+ super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
+
+ wx.EVT_CLOSE(self, self.OnClose)
+ self.panel = wx.Panel(self, -1)
+ self.SetSizer(wx.BoxSizer(wx.VERTICAL))
+ self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
+ #self.SetIcon(icon.getMainIcon())
+
+ self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
+
+ self.list = []
+ self.selection = None
+ self.printMode = 0
+ self.alwaysAutoPlace = True
+
+ self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
+ self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
+ self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
+
+ self.extruderOffset = [
+ numpy.array([0,0,0]),
+ numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),
+ numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),
+ numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]
+
+ self.toolbar = toolbarUtil.Toolbar(self.panel)
+
+ toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
+ toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
+ self.toolbar.AddSeparator()
+ group = []
+ toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick).SetValue(self.alwaysAutoPlace)
+ toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(not self.alwaysAutoPlace)
+ self.toolbar.AddSeparator()
+ toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
+ self.toolbar.AddSeparator()
+ toolbarUtil.NormalButton(self.toolbar, self.OnCutMesh, 'cut-mesh.png', 'Cut a plate STL into multiple STL files, and add those files to the project.\nNote: Splitting up plates sometimes takes a few minutes.')
+ toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')
+ self.toolbar.AddSeparator()
+ group = []
+ self.printOneAtATime = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Print one object at a time', callback=self.OnPrintTypeChange)
+ self.printAllAtOnce = toolbarUtil.RadioButton(self.toolbar, group, 'all-at-once-on.png', 'all-at-once-off.png', 'Print all the objects at once', callback=self.OnPrintTypeChange)
+ self.toolbar.AddSeparator()
+ toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
+
+ self.toolbar.Realize()
+
+ self.toolbar2 = toolbarUtil.Toolbar(self.panel)
+
+ toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
+ toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
+ self.toolbar2.AddSeparator()
+ toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
+ toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
+ toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
+ toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to prepare a specific object.')
+ self.toolbar2.AddSeparator()
+ if not self.alwaysAutoPlace:
+ toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
+ toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Prepare to project into a gcode file.')
+ self.toolbar2.Realize()
+
+ self.toolbar3 = toolbarUtil.Toolbar(self.panel)
+ self.mirrorX = toolbarUtil.ToggleButton(self.toolbar3, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.OnMirrorChange)
+ self.mirrorY = toolbarUtil.ToggleButton(self.toolbar3, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.OnMirrorChange)
+ self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar3, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.OnMirrorChange)
+ self.toolbar3.AddSeparator()
+
+ # Swap
+ self.swapXZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.OnMirrorChange)
+ self.swapYZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.OnMirrorChange)
+ self.toolbar3.Realize()
+
+ sizer = wx.GridBagSizer(2,2)
+ self.panel.SetSizer(sizer)
+ self.preview = PreviewGLCanvas(self.panel, self)
+ self.listbox = wx.ListBox(self.panel, -1, choices=[])
+ self.addButton = wx.Button(self.panel, -1, "Add")
+ self.remButton = wx.Button(self.panel, -1, "Remove")
+ self.sliceButton = wx.Button(self.panel, -1, "Prepare")
+ if not self.alwaysAutoPlace:
+ self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")
+
+ sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
+ sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
+ sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)
+ sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
+ sizer.Add(self.toolbar3, (2,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
+ sizer.Add(self.addButton, (3,1), span=(1,1))
+ sizer.Add(self.remButton, (3,2), span=(1,1))
+ sizer.Add(self.sliceButton, (4,1), span=(1,1))
+ if not self.alwaysAutoPlace:
+ sizer.Add(self.autoPlaceButton, (4,2), span=(1,1))
+ sizer.AddGrowableCol(0)
+ sizer.AddGrowableRow(1)
+
+ self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
+ self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
+ self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
+ if not self.alwaysAutoPlace:
+ self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
+ self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
+
+ panel = wx.Panel(self.panel, -1)
+ sizer.Add(panel, (5,1), span=(1,2))
+
+ sizer = wx.GridBagSizer(2,2)
+ panel.SetSizer(sizer)
+
+ self.scaleCtrl = wx.TextCtrl(panel, -1, '')
+ self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
+ self.rotateCtrl.SetRange(0, 360)
+
+ sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
+ sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
+ sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
+ sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
+
+ if int(profile.getPreference('extruder_amount')) > 1:
+ self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)
+ sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
+ sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
+ self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
+
+ self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
+ self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
+
+ self.SetSize((800,600))
+
+ def OnClose(self, e):
+ self.Destroy()
+
+ def OnQuit(self, e):
+ self.Close()
+
+ def OnPreferences(self, e):
+ prefDialog = preferencesDialog(self)
+ prefDialog.Centre()
+ prefDialog.Show(True)
+
+ def OnCutMesh(self, e):
+ dlg=wx.FileDialog(self, "Open file to cut", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
+ dlg.SetWildcard(meshLoader.wildcardFilter())
+ if dlg.ShowModal() == wx.ID_OK:
+ filename = dlg.GetPath()
+ model = meshLoader.loadMesh(filename)
+ pd = wx.ProgressDialog('Splitting model.', 'Splitting model into multiple parts.', model.vertexCount, self, wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH)
+ parts = model.splitToParts(pd.Update)
+ for part in parts:
+ partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))
+ stl.saveAsSTL(part, partFilename)
+ item = ProjectObject(self, partFilename)
+ self.list.append(item)
+ self.selection = item
+ self._updateListbox()
+ self.OnListSelect(None)
+ pd.Destroy()
+ self.preview.Refresh()
+ dlg.Destroy()
+
+ def OnDropFiles(self, filenames):
+ for filename in filenames:
+ item = ProjectObject(self, filename)
+ profile.putPreference('lastFile', item.filename)
+ self.list.append(item)
+ self.selection = item
+ self._updateListbox()
+ self.OnListSelect(None)
+ self.preview.Refresh()
+
+ def OnPrintTypeChange(self):
+ self.printMode = 0
+ if self.printAllAtOnce.GetValue():
+ self.printMode = 1
+ if self.alwaysAutoPlace:
+ self.OnAutoPlace(None)
+ self.preview.Refresh()
+
+ def OnSaveCombinedSTL(self, e):
+ dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
+ dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
+ if dlg.ShowModal() == wx.ID_OK:
+ self._saveCombinedSTL(dlg.GetPath())
+ dlg.Destroy()
+
+ def _saveCombinedSTL(self, filename):
+ totalCount = 0
+ for item in self.list:
+ totalCount += item.mesh.vertexCount
+ output = mesh.mesh()
+ output._prepareVertexCount(totalCount)
+ for item in self.list:
+ offset = numpy.array([item.centerX, item.centerY, 0])
+ for v in item.mesh.vertexes:
+ v0 = v * item.scale + offset
+ output.addVertex(v0[0], v0[1], v0[2])
+ stl.saveAsSTL(output, filename)
+
+ def OnSaveProject(self, e):
+ dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
+ dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
+ if dlg.ShowModal() == wx.ID_OK:
+ cp = ConfigParser.ConfigParser()
+ i = 0
+ for item in self.list:
+ section = 'model_%d' % (i)
+ cp.add_section(section)
+ cp.set(section, 'filename', item.filename.encode("utf-8"))
+ cp.set(section, 'centerX', str(item.centerX))
+ cp.set(section, 'centerY', str(item.centerY))
+ cp.set(section, 'scale', str(item.scale))
+ cp.set(section, 'rotate', str(item.rotate))
+ cp.set(section, 'flipX', str(item.flipX))
+ cp.set(section, 'flipY', str(item.flipY))
+ cp.set(section, 'flipZ', str(item.flipZ))
+ cp.set(section, 'swapXZ', str(item.swapXZ))
+ cp.set(section, 'swapYZ', str(item.swapYZ))
+ cp.set(section, 'extruder', str(item.extruder+1))
+ if item.profile != None:
+ cp.set(section, 'profile', item.profile)
+ i += 1
+ cp.write(open(dlg.GetPath(), "w"))
+ dlg.Destroy()
+
+ def OnLoadProject(self, e):
+ dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
+ dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
+ if dlg.ShowModal() == wx.ID_OK:
+ cp = ConfigParser.ConfigParser()
+ cp.read(dlg.GetPath())
+ self.list = []
+ i = 0
+ while cp.has_section('model_%d' % (i)):
+ section = 'model_%d' % (i)
+
+ item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
+ item.centerX = float(cp.get(section, 'centerX'))
+ item.centerY = float(cp.get(section, 'centerY'))
+ item.scale = float(cp.get(section, 'scale'))
+ item.rotate = float(cp.get(section, 'rotate'))
+ item.flipX = cp.get(section, 'flipX') == 'True'
+ item.flipY = cp.get(section, 'flipY') == 'True'
+ item.flipZ = cp.get(section, 'flipZ') == 'True'
+ item.swapXZ = cp.get(section, 'swapXZ') == 'True'
+ item.swapYZ = cp.get(section, 'swapYZ') == 'True'
+ if cp.has_option(section, 'extruder'):
+ item.extuder = int(cp.get(section, 'extruder')) - 1
+ if cp.has_option(section, 'profile'):
+ item.profile = cp.get(section, 'profile')
+ item.updateModelTransform()
+ i += 1
+
+ self.list.append(item)
+
+ self.selected = self.list[0]
+ self._updateListbox()
+ self.OnListSelect(None)
+ self.preview.Refresh()
+
+ dlg.Destroy()
+
+ def On3DClick(self):
+ self.preview.yaw = 30
+ self.preview.pitch = 60
+ self.preview.zoom = 300
+ self.preview.view3D = True
+ self.preview.Refresh()
+
+ def OnTopClick(self):
+ self.preview.view3D = False
+ self.preview.zoom = self.machineSize[0] / 2 + 10
+ self.preview.offsetX = 0
+ self.preview.offsetY = 0
+ self.preview.Refresh()
+
+ def OnListSelect(self, e):
+ if self.listbox.GetSelection() == -1:
+ return
+ self.selection = self.list[self.listbox.GetSelection()]
+ self.scaleCtrl.SetValue(str(self.selection.scale))
+ self.rotateCtrl.SetValue(int(self.selection.rotate))
+ if int(profile.getPreference('extruder_amount')) > 1:
+
+ self.extruderCtrl.SetValue(str(self.selection.extruder+1))
+
+ self.mirrorX.SetValue(self.selection.flipX)
+ self.mirrorY.SetValue(self.selection.flipY)
+ self.mirrorZ.SetValue(self.selection.flipZ)
+ self.swapXZ.SetValue(self.selection.swapXZ)
+ self.swapYZ.SetValue(self.selection.swapYZ)
+
+ self.preview.Refresh()
+
+ def OnAddModel(self, e):
+ dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
+ dlg.SetWildcard(meshLoader.wildcardFilter())
+ if dlg.ShowModal() == wx.ID_OK:
+ for filename in dlg.GetPaths():
+ item = ProjectObject(self, filename)
+ profile.putPreference('lastFile', item.filename)
+ self.list.append(item)
+ self.selection = item
+ self._updateListbox()
+ self.OnListSelect(None)
+ self.preview.Refresh()
+ dlg.Destroy()
+
+ def OnRemModel(self, e):
+ if self.selection == None:
+ return
+ self.list.remove(self.selection)
+ self._updateListbox()
+ self.preview.Refresh()
+
+ def OnMoveUp(self, e):
+ if self.selection == None:
+ return
+ i = self.listbox.GetSelection()
+ if i == 0:
+ return
+ self.list.remove(self.selection)
+ self.list.insert(i-1, self.selection)
+ self._updateListbox()
+ self.preview.Refresh()
+
+ def OnMoveDown(self, e):
+ if self.selection == None:
+ return
+ i = self.listbox.GetSelection()
+ if i == len(self.list) - 1:
+ return
+ self.list.remove(self.selection)
+ self.list.insert(i+1, self.selection)
+ self._updateListbox()
+ self.preview.Refresh()
+
+ def OnCopy(self, e):
+ if self.selection == None:
+ return
+
+ item = self.selection.clone()
+ self.list.insert(self.list.index(self.selection), item)
+ self.selection = item
+
+ self._updateListbox()
+ self.preview.Refresh()
+
+ def OnSetCustomProfile(self, e):
+ if self.selection == None:
+ return
+
+ dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
+ dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
+ if dlg.ShowModal() == wx.ID_OK:
+ self.selection.profile = dlg.GetPath()
+ else:
+ self.selection.profile = None
+ self._updateListbox()
+ dlg.Destroy()
+
+ def _updateListbox(self):
+ self.listbox.Clear()
+ for item in self.list:
+ if item.profile != None:
+ self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
+ else:
+ self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
+ if self.selection in self.list:
+ self.listbox.SetSelection(self.list.index(self.selection))
+ elif len(self.list) > 0:
+ self.selection = self.list[0]
+ self.listbox.SetSelection(0)
+ else:
+ self.selection = None
+ self.listbox.SetSelection(-1)
+ if self.alwaysAutoPlace:
+ self.OnAutoPlace(None)
+
+ def OnAutoPlace(self, e):
+ bestAllowedSize = int(self.machineSize[1])
+ bestArea = self._doAutoPlace(bestAllowedSize)
+ for i in xrange(10, int(self.machineSize[1]), 10):
+ area = self._doAutoPlace(i)
+ if area < bestArea:
+ bestAllowedSize = i
+ bestArea = area
+ self._doAutoPlace(bestAllowedSize)
+ for item in self.list:
+ item.clampXY()
+ self.preview.Refresh()
+
+ def _doAutoPlace(self, allowedSizeY):
+ extraSizeMin, extraSizeMax = self.getExtraHeadSize()
+
+ if extraSizeMin[0] > extraSizeMax[0]:
+ posX = self.machineSize[0]
+ dirX = -1
+ else:
+ posX = 0
+ dirX = 1
+ posY = 0
+ dirY = 1
+
+ minX = self.machineSize[0]
+ minY = self.machineSize[1]
+ maxX = 0
+ maxY = 0
+ for item in self.list:
+ item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
+ item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
+ if item.centerY + item.getSize()[1] >= allowedSizeY:
+ if dirX < 0:
+ posX = minX - extraSizeMax[0] - 1
+ else:
+ posX = maxX + extraSizeMin[0] + 1
+ posY = 0
+ item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
+ item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
+ posY += item.getSize()[1] * item.scale * dirY + extraSizeMin[1] + 1
+ minX = min(minX, item.centerX - item.getSize()[0] * item.scale / 2)
+ minY = min(minY, item.centerY - item.getSize()[1] * item.scale / 2)
+ maxX = max(maxX, item.centerX + item.getSize()[0] * item.scale / 2)
+ maxY = max(maxY, item.centerY + item.getSize()[1] * item.scale / 2)
+
+ for item in self.list:
+ if dirX < 0:
+ item.centerX -= minX / 2
+ else:
+ item.centerX += (self.machineSize[0] - maxX) / 2
+ item.centerY += (self.machineSize[1] - maxY) / 2
+
+ if minX < 0 or maxX > self.machineSize[0]:
+ return ((maxX - minX) + (maxY - minY)) * 100
+
+ return (maxX - minX) + (maxY - minY)
+
+ def OnSlice(self, e):
+ dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
+ dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
+ if dlg.ShowModal() != wx.ID_OK:
+ dlg.Destroy()
+ return
+ resultFilename = dlg.GetPath()
+ dlg.Destroy()
+
+ put = profile.setTempOverride
+ oldProfile = profile.getGlobalProfileString()
+
+ put('add_start_end_gcode', 'False')
+ put('gcode_extension', 'project_tmp')
+ if self.printMode == 0:
+ clearZ = 0
+ actionList = []
+ for item in self.list:
+ if item.profile != None and os.path.isfile(item.profile):
+ profile.loadGlobalProfile(item.profile)
+ put('object_center_x', item.centerX - self.extruderOffset[item.extruder][0])
+ put('object_center_y', item.centerY - self.extruderOffset[item.extruder][1])
+ put('model_scale', item.scale)
+ put('flip_x', item.flipX)
+ put('flip_y', item.flipY)
+ put('flip_z', item.flipZ)
+ put('model_rotate_base', item.rotate)
+ put('swap_xz', item.swapXZ)
+ put('swap_yz', item.swapYZ)
+
+ action = Action()
+ action.sliceCmd = sliceRun.getSliceCommand(item.filename)
+ action.centerX = item.centerX
+ action.centerY = item.centerY
+ action.temperature = profile.getProfileSettingFloat('print_temperature')
+ action.extruder = item.extruder
+ action.filename = item.filename
+ clearZ = max(clearZ, item.getSize()[2] * item.scale + 5.0)
+ action.clearZ = clearZ
+ action.leaveResultForNextSlice = False
+ action.usePreviousSlice = False
+ actionList.append(action)
+
+ if self.list.index(item) > 0 and item.isSameExceptForPosition(self.list[self.list.index(item)-1]):
+ actionList[-2].leaveResultForNextSlice = True
+ actionList[-1].usePreviousSlice = True
+
+ if item.profile != None:
+ profile.loadGlobalProfileFromString(oldProfile)
+
+ else:
+ self._saveCombinedSTL(resultFilename + "_temp_.stl")
+ put('model_scale', 1.0)
+ put('flip_x', False)
+ put('flip_y', False)
+ put('flip_z', False)
+ put('model_rotate_base', 0)
+ put('swap_xz', False)
+ put('swap_yz', False)
+ actionList = []
+
+ action = Action()
+ action.sliceCmd = sliceRun.getSliceCommand(resultFilename + "_temp_.stl")
+ action.centerX = profile.getPreferenceFloat('machine_width') / 2
+ action.centerY = profile.getPreferenceFloat('machine_depth') / 2
+ action.temperature = profile.getProfileSettingFloat('print_temperature')
+ action.extruder = 0
+ action.filename = resultFilename + "_temp_.stl"
+ action.clearZ = 0
+ action.leaveResultForNextSlice = False
+ action.usePreviousSlice = False
+
+ actionList.append(action)
+
+ #Restore the old profile.
+ profile.resetTempOverride()
+
+ pspw = ProjectSliceProgressWindow(actionList, resultFilename)
+ pspw.extruderOffset = self.extruderOffset
+ pspw.Centre()
+ pspw.Show(True)
+
+ def OnScaleChange(self, e):
+ if self.selection == None:
+ return
+ try:
+ self.selection.scale = float(self.scaleCtrl.GetValue())
+ except ValueError:
+ self.selection.scale = 1.0
+ self.preview.Refresh()
+
+ def OnRotateChange(self, e):
+ if self.selection == None:
+ return
+ self.selection.rotate = float(self.rotateCtrl.GetValue())
+ self.selection.updateModelTransform()
+ if self.alwaysAutoPlace:
+ self.OnAutoPlace(None)
+ self.preview.Refresh()
+
+ def OnExtruderChange(self, e):
+ if self.selection == None:
+ return
+ self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
+ self.preview.Refresh()
+
+ def OnMirrorChange(self):
+ if self.selection == None:
+ return
+ self.selection.flipX = self.mirrorX.GetValue()
+ self.selection.flipY = self.mirrorY.GetValue()
+ self.selection.flipZ = self.mirrorZ.GetValue()
+ self.selection.swapXZ = self.swapXZ.GetValue()
+ self.selection.swapYZ = self.swapYZ.GetValue()
+ self.selection.updateModelTransform()
+ if self.alwaysAutoPlace:
+ self.OnAutoPlace(None)
+ self.preview.Refresh()
+
+ def getExtraHeadSize(self):
+ extraSizeMin = self.headSizeMin
+ extraSizeMax = self.headSizeMax
+ if profile.getProfileSettingFloat('skirt_line_count') > 0:
+ skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
+ extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])
+ extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])
+ if profile.getProfileSetting('enable_raft') != 'False':
+ raftSize = profile.getProfileSettingFloat('raft_margin') * 2
+ extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])
+ extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])
+ if profile.getProfileSetting('support') != 'None':
+ extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])
+ extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])
+
+ if self.printMode == 1:
+ extraSizeMin = numpy.array([6.0, 6.0, 0])
+ extraSizeMax = numpy.array([6.0, 6.0, 0])
+
+ return extraSizeMin, extraSizeMax
+
+class PreviewGLCanvas(glcanvas.GLCanvas):
+ def __init__(self, parent, projectPlannerWindow):
+ attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
+ glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
+ self.parent = projectPlannerWindow
+ self.context = glcanvas.GLContext(self)
+ wx.EVT_PAINT(self, self.OnPaint)
+ wx.EVT_SIZE(self, self.OnSize)
+ wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
+ wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
+ wx.EVT_MOTION(self, self.OnMouseMotion)
+ wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
+ self.yaw = 30
+ self.pitch = 60
+ self.offsetX = 0
+ self.offsetY = 0
+ self.view3D = self.parent.alwaysAutoPlace
+ if self.view3D:
+ self.zoom = 300
+ else:
+ self.zoom = self.parent.machineSize[0] / 2 + 10
+ self.allowDrag = False
+
+ self.objColor = profile.getPreferenceColour('model_colour')
+
+ def OnMouseLeftDown(self,e):
+ self.allowDrag = True
+ if not self.parent.alwaysAutoPlace and not self.view3D:
+ #TODO: Translate mouse X/Y to 3D X/Y/Z
+ for item in self.parent.list:
+ iMin = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]
+ iMax = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]
+
+ def OnMouseMotion(self,e):
+ if self.allowDrag and e.Dragging() and e.LeftIsDown():
+ if self.view3D:
+ self.yaw += e.GetX() - self.oldX
+ self.pitch -= e.GetY() - self.oldY
+ if self.pitch > 170:
+ self.pitch = 170
+ if self.pitch < 10:
+ self.pitch = 10
+ elif not self.parent.alwaysAutoPlace:
+ item = self.parent.selection
+ if item != None:
+ item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
+ item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
+ item.clampXY()
+ self.Refresh()
+ else:
+ self.allowDrag = False
+ if e.Dragging() and e.RightIsDown():
+ if self.view3D:
+ self.zoom += e.GetY() - self.oldY
+ if self.zoom < 1:
+ self.zoom = 1
+ self.Refresh()
+ self.oldX = e.GetX()
+ self.oldY = e.GetY()
+
+ def OnMouseWheel(self,e):
+ if self.view3D:
+ self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
+ if self.zoom < 1.0:
+ self.zoom = 1.0
+ self.Refresh()
+
+ def OnEraseBackground(self,event):
+ #Workaround for windows background redraw flicker.
+ pass
+
+ def OnSize(self,event):
+ self.Refresh()
+
+ def OnPaint(self,event):
+ dc = wx.PaintDC(self)
+ if not hasOpenGLlibs:
+ dc.Clear()
+ dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
+ return
+ self.SetCurrent(self.context)
+ opengl.InitGL(self, self.view3D, self.zoom)
+ if self.view3D:
+ glTranslate(0,0,-self.zoom)
+ glRotate(-self.pitch, 1,0,0)
+ glRotate(self.yaw, 0,0,1)
+ else:
+ glTranslate(self.offsetX, self.offsetY, 0.0)
+ glTranslate(-self.parent.machineSize[0]/2, -self.parent.machineSize[1]/2, 0)
+
+ self.OnDraw()
+ self.SwapBuffers()
+
+ def OnDraw(self):
+ machineSize = self.parent.machineSize
+ extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()
+
+ for item in self.parent.list:
+ item.validPlacement = True
+ item.gotHit = False
+
+ for idx1 in xrange(0, len(self.parent.list)):
+ item = self.parent.list[idx1]
+ iMin1 = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin - self.parent.extruderOffset[item.extruder]
+ iMax1 = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax - self.parent.extruderOffset[item.extruder]
+ for idx2 in xrange(0, idx1):
+ item2 = self.parent.list[idx2]
+ iMin2 = (item2.getMinimum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
+ iMax2 = (item2.getMaximum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
+ if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:
+ item.validPlacement = False
+ item2.gotHit = True
+
+ seenSelected = False
+ for item in self.parent.list:
+ if item == self.parent.selection:
+ seenSelected = True
+ if item.modelDisplayList == None:
+ item.modelDisplayList = glGenLists(1);
+ if item.modelDirty:
+ item.modelDirty = False
+ modelSize = item.getMaximum() - item.getMinimum()
+ glNewList(item.modelDisplayList, GL_COMPILE)
+ opengl.DrawMesh(item.mesh)
+ glEndList()
+
+ if item.validPlacement:
+ if self.parent.selection == item:
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x + 0.2, self.objColor))
+ glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
+ else:
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor)
+ glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
+ else:
+ if self.parent.selection == item:
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
+ glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
+ else:
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
+ glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
+ glPushMatrix()
+
+ glEnable(GL_LIGHTING)
+ glTranslate(item.centerX, item.centerY, 0)
+ glPushMatrix()
+ glScalef(item.scale, item.scale, item.scale)
+ glCallList(item.modelDisplayList)
+ glPopMatrix()
+
+ vMin = item.getMinimum() * item.scale
+ vMax = item.getMaximum() * item.scale
+ vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
+ vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
+
+ glDisable(GL_LIGHTING)
+
+ if not self.parent.alwaysAutoPlace:
+ if self.parent.selection == item:
+ if item.gotHit:
+ glColor3f(1.0,0.0,0.3)
+ else:
+ glColor3f(1.0,0.0,1.0)
+ opengl.DrawBox(vMin, vMax)
+ if item.gotHit:
+ glColor3f(1.0,0.3,0.0)
+ else:
+ glColor3f(1.0,1.0,0.0)
+ opengl.DrawBox(vMinHead, vMaxHead)
+ elif seenSelected:
+ if item.gotHit:
+ glColor3f(0.5,0.0,0.1)
+ else:
+ glColor3f(0.5,0.0,0.5)
+ opengl.DrawBox(vMinHead, vMaxHead)
+ else:
+ if item.gotHit:
+ glColor3f(0.7,0.1,0.0)
+ else:
+ glColor3f(0.7,0.7,0.0)
+ opengl.DrawBox(vMin, vMax)
+
+ glPopMatrix()
+
+ opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))
+ glFlush()
+
+class ProjectSliceProgressWindow(wx.Frame):
+ def __init__(self, actionList, resultFilename):
+ super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
+ self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
+
+ self.actionList = actionList
+ self.resultFilename = resultFilename
+ self.abort = False
+ self.prevStep = 'start'
+ self.totalDoneFactor = 0.0
+ self.startTime = time.time()
+ self.sliceStartTime = time.time()
+
+ self.sizer = wx.GridBagSizer(2, 2)
+ self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
+ self.progressGauge = wx.Gauge(self, -1)
+ self.progressGauge.SetRange(10000)
+ self.progressGauge2 = wx.Gauge(self, -1)
+ self.progressGauge2.SetRange(len(self.actionList))
+ self.abortButton = wx.Button(self, -1, "Abort")
+ self.sizer.Add(self.statusText, (0,0), span=(1,5))
+ self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)
+ self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)
+
+ self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)
+ self.sizer.AddGrowableCol(0)
+ self.sizer.AddGrowableRow(0)
+
+ self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
+ self.SetSizer(self.sizer)
+ self.Layout()
+ self.Fit()
+
+ threading.Thread(target=self.OnRun).start()
+
+ def OnAbort(self, e):
+ if self.abort:
+ self.Close()
+ else:
+ self.abort = True
+ self.abortButton.SetLabel('Close')
+
+ def SetProgress(self, stepName, layer, maxLayer):
+ if self.prevStep != stepName:
+ self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
+ newTime = time.time()
+ #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
+ self.startTime = newTime
+ self.prevStep = stepName
+
+ progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
+ self.progressGauge.SetValue(int(progresValue))
+ self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
+ taskbar.setProgress(self, 10000 * self.progressGauge2.GetValue() + int(progresValue), 10000 * len(self.actionList))
+
+ def OnRun(self):
+ resultFile = open(self.resultFilename, "w")
+ put = profile.setTempOverride
+ self.progressLog = []
+ for action in self.actionList:
+ wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))
+ if not action.usePreviousSlice:
+ p = sliceRun.startSliceCommandProcess(action.sliceCmd)
+ line = p.stdout.readline()
+
+ maxValue = 1
+ while(len(line) > 0):
+ line = line.rstrip()
+ if line[0:9] == "Progress[" and line[-1:] == "]":
+ progress = line[9:-1].split(":")
+ if len(progress) > 2:
+ maxValue = int(progress[2])
+ wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
+ else:
+ self.progressLog.append(line)
+ wx.CallAfter(self.statusText.SetLabel, line)
+ if self.abort:
+ p.terminate()
+ wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
+ resultFile.close()
+ return
+ line = p.stdout.readline()
+ self.returnCode = p.wait()
+
+ put('object_center_x', action.centerX - self.extruderOffset[action.extruder][0])
+ put('object_center_y', action.centerY - self.extruderOffset[action.extruder][1])
+ put('clear_z', action.clearZ)
+ put('extruder', action.extruder)
+ put('print_temperature', action.temperature)
+
+ if action == self.actionList[0]:
+ resultFile.write(';TYPE:CUSTOM\n')
+ resultFile.write('T%d\n' % (action.extruder))
+ currentExtruder = action.extruder
+ prevTemp = action.temperature
+ startGCode = profile.getAlterationFileContents('start.gcode')
+ startGCode = startGCode.replace('?filename?', 'Multiple files')
+ resultFile.write(startGCode)
+ else:
+ #reset the extrusion length, and move to the next object center.
+ resultFile.write(';TYPE:CUSTOM\n')
+ if prevTemp != action.temperature and action.temperature > 0:
+ resultFile.write('M104 S%d\n' % (int(action.temperature)))
+ prevTemp = action.temperature
+ resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
+ resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
+ profile.resetTempOverride()
+
+ if not action.usePreviousSlice:
+ f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
+ data = f.read(4096)
+ while data != '':
+ resultFile.write(data)
+ data = f.read(4096)
+ f.close()
+ savedCenterX = action.centerX
+ savedCenterY = action.centerY
+ else:
+ f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
+ for line in f:
+ if line[0] != ';':
+ if 'X' in line:
+ line = self._adjustNumberInLine(line, 'X', action.centerX - savedCenterX)
+ if 'Y' in line:
+ line = self._adjustNumberInLine(line, 'Y', action.centerY - savedCenterY)
+ resultFile.write(line)
+ f.close()
+
+ if not action.leaveResultForNextSlice:
+ os.remove(sliceRun.getExportFilename(action.filename, "project_tmp"))
+
+ wx.CallAfter(self.progressGauge.SetValue, 10000)
+ self.totalDoneFactor = 0.0
+ wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
+
+ resultFile.write(';TYPE:CUSTOM\n')
+ if len(self.actionList) > 1 and self.actionList[-1].clearZ > 1:
+ #only move to higher Z if we have sliced more then 1 object. This solves the "move into print after printing" problem with the print-all-at-once option.
+ resultFile.write('G1 Z%f F%f\n' % (self.actionList[-1].clearZ, profile.getProfileSettingFloat('max_z_speed') * 60))
+ resultFile.write(profile.getAlterationFileContents('end.gcode'))
+ resultFile.close()
+
+ gcode = gcodeInterpreter.gcode()
+ gcode.load(self.resultFilename)
+
+ self.abort = True
+ sliceTime = time.time() - self.sliceStartTime
+ status = "Build: %s" % (self.resultFilename)
+ status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
+ status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
+ status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
+ cost = gcode.calculateCost()
+ if cost != False:
+ status += "\nCost: %s" % (cost)
+ profile.replaceGCodeTags(self.resultFilename, gcode)
+ wx.CallAfter(self.statusText.SetLabel, status)
+ wx.CallAfter(self.OnSliceDone)
+
+ def _adjustNumberInLine(self, line, tag, f):
+ m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)
+ return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'
+
+ def OnSliceDone(self):
+ self.abortButton.Destroy()
+ self.closeButton = wx.Button(self, -1, "Close")
+ self.printButton = wx.Button(self, -1, "Print")
+ self.logButton = wx.Button(self, -1, "Show log")
+ self.sizer.Add(self.closeButton, (3,0), span=(1,1))
+ self.sizer.Add(self.printButton, (3,1), span=(1,1))
+ self.sizer.Add(self.logButton, (3,2), span=(1,1))
+ if exporer.hasExporer():
+ self.openFileLocationButton = wx.Button(self, -1, "Open file location")
+ self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
+ self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))
+ if profile.getPreference('sdpath') != '':
+ self.copyToSDButton = wx.Button(self, -1, "To SDCard")
+ self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
+ self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))
+ self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
+ self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
+ self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
+ self.Layout()
+ self.Fit()
+ taskbar.setBusy(self, False)
+
+ def OnCopyToSD(self, e):
+ filename = os.path.basename(self.resultFilename)
+ if profile.getPreference('sdshortnames') == 'True':
+ filename = sliceRun.getShortFilename(filename)
+ shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))
+
+ def OnOpenFileLocation(self, e):
+ exporer.openExporer(self.resultFilename)
+
+ def OnPrint(self, e):
+ printWindow.printFile(self.resultFilename)
+
+ def OnShowLog(self, e):
+ LogWindow('\n'.join(self.progressLog))
+
+class preferencesDialog(configBase.configWindowBase):
+ def __init__(self, parent):
+ super(preferencesDialog, self).__init__(title="Project Planner Preferences")
+
+ self.parent = parent
+ wx.EVT_CLOSE(self, self.OnClose)
+
+ extruderAmount = int(profile.getPreference('extruder_amount'))
+
+ left, right, main = self.CreateConfigPanel(self)
+ configBase.TitleRow(left, 'Machine head size')
+ c = configBase.SettingRow(left, 'Head size - X towards home (mm)', 'extruder_head_size_min_x', '0', 'Size of your printer head in the X direction, on the Ultimaker your fan is in this direction.', type = 'preference')
+ validators.validFloat(c, 0.1)
+ c = configBase.SettingRow(left, 'Head size - X towards end (mm)', 'extruder_head_size_max_x', '0', 'Size of your printer head in the X direction.', type = 'preference')
+ validators.validFloat(c, 0.1)
+ c = configBase.SettingRow(left, 'Head size - Y towards home (mm)', 'extruder_head_size_min_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')
+ validators.validFloat(c, 0.1)
+ c = configBase.SettingRow(left, 'Head size - Y towards end (mm)', 'extruder_head_size_max_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')
+ validators.validFloat(c, 0.1)
+ c = configBase.SettingRow(left, 'Head gantry height (mm)', 'extruder_head_size_height', '0', 'The tallest object height that will always fit under your printers gantry system when the printer head is at the lowest Z position.', type = 'preference')
+ validators.validFloat(c)
+
+ self.okButton = wx.Button(left, -1, 'Ok')
+ left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
+ self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
+
+ self.MakeModal(True)
+ main.Fit()
+ self.Fit()
+
+ def OnClose(self, e):
+ self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
+ self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
+ self.parent.Refresh()
+
+ self.MakeModal(False)
+ self.Destroy()
+
+class LogWindow(wx.Frame):
+ def __init__(self, logText):
+ super(LogWindow, self).__init__(None, title="Slice log")
+ self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
+ self.SetSize((400,300))
+ self.Centre()
+ self.Show(True)
+
+def main():
+ app = wx.App(False)
+ projectPlanner().Show(True)
+ app.MainLoop()
+
+if __name__ == '__main__':
+ main()