+++ /dev/null
-from __future__ import absolute_import
-from __future__ import division
-
-import math
-import threading
-import re
-import time
-import os
-import numpy
-import traceback
-import sys
-
-from wx import glcanvas
-import wx
-import OpenGL
-OpenGL.ERROR_CHECKING = False
-from OpenGL.GLU import *
-from OpenGL.GL import *
-
-from Cura.util import profile
-from Cura.util import gcodeInterpreter
-from Cura.util import meshLoader
-from Cura.util import util3d
-from Cura.util import sliceRun
-
-from Cura.gui.util import opengl
-from Cura.gui.util import previewTools
-from Cura.gui.util import openglGui
-
-class previewObject():
- def __init__(self):
- self.mesh = None
- self.filename = None
- self.displayList = None
- self.dirty = False
-
-class previewPanel(wx.Panel):
- def __init__(self, parent):
- super(previewPanel, self).__init__(parent,-1)
-
- self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
- self.SetMinSize((440,320))
-
- self.objectList = []
- self.errorList = []
- self.gcode = None
- self.objectsMinV = None
- self.objectsMaxV = None
- self.objectsBoundaryCircleSize = None
- self.loadThread = None
- self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
- self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0)
-
- self.glCanvas = PreviewGLCanvas(self)
- #Create the popup window
- self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
- self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
- self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')
- self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)
- self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)
- self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.warningPopup.SetSizer(self.warningPopup.sizer)
- self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)
- self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
- self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
- self.warningPopup.Fit()
- self.warningPopup.Layout()
- self.warningPopup.timer = wx.Timer(self)
- self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)
-
- self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)
- self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)
- parent.Bind(wx.EVT_MOVE, self.OnMove)
- parent.Bind(wx.EVT_SIZE, self.OnMove)
-
- sizer = wx.BoxSizer(wx.VERTICAL)
- sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
- self.SetSizer(sizer)
-
- self.checkReloadFileTimer = wx.Timer(self)
- self.Bind(wx.EVT_TIMER, self.OnCheckReloadFile, self.checkReloadFileTimer)
- self.checkReloadFileTimer.Start(1000)
-
- group = []
- self.rotateToolButton = openglGui.glRadioButton(self.glCanvas, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
- self.scaleToolButton = openglGui.glRadioButton(self.glCanvas, 9, 'Scale', (1,-1), group, self.OnToolSelect)
- self.mirrorToolButton = openglGui.glRadioButton(self.glCanvas, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
-
- self.resetRotationButton = openglGui.glButton(self.glCanvas, 12, 'Reset', (0,-2), self.OnRotateReset)
- self.layFlatButton = openglGui.glButton(self.glCanvas, 16, 'Lay flat', (0,-3), self.OnLayFlat)
-
- self.resetScaleButton = openglGui.glButton(self.glCanvas, 13, 'Reset', (1,-2), self.OnScaleReset)
- self.scaleMaxButton = openglGui.glButton(self.glCanvas, 17, 'To max', (1,-3), self.OnScaleMax)
-
- self.mirrorXButton = openglGui.glButton(self.glCanvas, 14, 'Mirror X', (2,-2), lambda : self.OnMirror(0))
- self.mirrorYButton = openglGui.glButton(self.glCanvas, 18, 'Mirror Y', (2,-3), lambda : self.OnMirror(1))
- self.mirrorZButton = openglGui.glButton(self.glCanvas, 22, 'Mirror Z', (2,-4), lambda : self.OnMirror(2))
-
- self.openFileButton = openglGui.glButton(self.glCanvas, 4, 'Load', (0,0), lambda : self.GetParent().GetParent().GetParent()._showModelLoadDialog(1))
- self.sliceButton = openglGui.glButton(self.glCanvas, 5, 'Prepare', (1,0), lambda : self.GetParent().GetParent().GetParent().OnSlice(None))
- self.printButton = openglGui.glButton(self.glCanvas, 6, 'Print', (2,0), lambda : self.GetParent().GetParent().GetParent().OnPrint(None))
-
- self.rotateToolButton.setExpandArrow(True)
- self.scaleToolButton.setExpandArrow(True)
- self.mirrorToolButton.setExpandArrow(True)
-
- extruderCount = int(profile.getPreference('extruder_amount'))
- if extruderCount > 1:
- openglGui.glButton(self.glCanvas, 4, 'Load dual', (0,1), lambda : self.GetParent().GetParent().GetParent()._showModelLoadDialog(2))
- if extruderCount > 2:
- openglGui.glButton(self.glCanvas, 4, 'Load triple', (0,2), lambda : self.GetParent().GetParent().GetParent()._showModelLoadDialog(3))
- if extruderCount > 3:
- openglGui.glButton(self.glCanvas, 4, 'Load quad', (0,3), lambda : self.GetParent().GetParent().GetParent()._showModelLoadDialog(4))
-
- self.scaleForm = openglGui.glFrame(self.glCanvas, (2, -2))
- openglGui.glGuiLayoutGrid(self.scaleForm)
- openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
- self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
- openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
- self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
- openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
- self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
- openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
- self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
- openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
- self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
- openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
- self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
- openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
- self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
-
- self.viewSelection = openglGui.glComboButton(self.glCanvas, 'View mode', [7,11,15,19,23], ['Normal', 'Transparent', 'X-Ray', 'Overhang', 'Layers'], (-1,0), self.OnViewChange)
- self.layerSelect = openglGui.glSlider(self.glCanvas, 0, 0, 100, (-1,-2), lambda : self.Refresh())
-
- self.OnViewChange()
- self.OnToolSelect()
- self.updateModelTransform()
-
- self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
-
- def OnToolSelect(self):
- if self.rotateToolButton.getSelected():
- self.tool = previewTools.toolRotate(self.glCanvas)
- elif self.scaleToolButton.getSelected():
- self.tool = previewTools.toolScale(self.glCanvas)
- elif self.mirrorToolButton.getSelected():
- self.tool = previewTools.toolNone(self.glCanvas)
- else:
- self.tool = previewTools.toolNone(self.glCanvas)
- self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
- self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
- self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
- self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
- self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
- self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
- self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
- self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
- self.updateModelTransform()
-
- def OnScaleEntry(self, value, axis):
- try:
- value = float(value)
- except:
- return
- scale = numpy.linalg.norm(self.matrix[::,axis].getA().flatten())
- scale = value / scale
- if scale == 0:
- return
- if self.scaleUniform.getValue():
- matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
- else:
- matrix = [[1.0,0,0], [0, 1.0, 0], [0, 0, 1.0]]
- matrix[axis][axis] = scale
- self.matrix *= numpy.matrix(matrix, numpy.float64)
- self.updateModelTransform()
-
- def OnScaleEntryMM(self, value, axis):
- try:
- value = float(value)
- except:
- return
- scale = self.objectsSize[axis]
- scale = value / scale
- if scale == 0:
- return
- if self.scaleUniform.getValue():
- matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
- else:
- matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
- matrix[axis][axis] = scale
- self.matrix *= numpy.matrix(matrix, numpy.float64)
- self.updateModelTransform()
-
- def OnMirror(self, axis):
- matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
- matrix[axis][axis] = -1
- self.matrix *= numpy.matrix(matrix, numpy.float64)
- for obj in self.objectList:
- obj.dirty = True
- obj.steepDirty = True
- self.updateModelTransform()
-
- def OnMove(self, e = None):
- if e is not None:
- e.Skip()
- x, y = self.glCanvas.ClientToScreenXY(0, 0)
- sx, sy = self.glCanvas.GetClientSizeTuple()
- self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
-
- def OnScaleMax(self):
- if self.objectsMinV is None:
- return
- vMin = self.objectsMinV
- vMax = self.objectsMaxV
- skirtSize = 3
- if profile.getProfileSettingFloat('skirt_line_count') > 0:
- skirtSize = 3 + profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
- scaleX1 = (self.machineSize.x - self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
- scaleY1 = (self.machineSize.y - self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
- scaleX2 = (self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
- scaleY2 = (self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
- scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
- scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
- self.matrix *= numpy.matrix([[scale,0,0],[0,scale,0],[0,0,scale]], numpy.float64)
- if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
- self.setViewMode('Normal')
- self.updateModelTransform()
-
- def OnRotateReset(self):
- x = numpy.linalg.norm(self.matrix[::,0].getA().flatten())
- y = numpy.linalg.norm(self.matrix[::,1].getA().flatten())
- z = numpy.linalg.norm(self.matrix[::,2].getA().flatten())
- self.matrix = numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
- for obj in self.objectList:
- obj.dirty = True
- obj.steepDirty = True
- self.updateModelTransform()
-
- def OnScaleReset(self):
- x = 1/numpy.linalg.norm(self.matrix[::,0].getA().flatten())
- y = 1/numpy.linalg.norm(self.matrix[::,1].getA().flatten())
- z = 1/numpy.linalg.norm(self.matrix[::,2].getA().flatten())
- self.matrix *= numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
- for obj in self.objectList:
- obj.steepDirty = True
- self.updateModelTransform()
-
- def OnLayFlat(self):
- transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
- minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
- dotMin = 1.0
- dotV = None
- for v in transformedVertexes:
- diff = v - minZvertex
- len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
- if len < 5:
- continue
- dot = (diff[2] / len)
- if dotMin > dot:
- dotMin = dot
- dotV = diff
- if dotV is None:
- return
- rad = -math.atan2(dotV[1], dotV[0])
- self.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
- rad = -math.asin(dotMin)
- self.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
-
-
- transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
- minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
- dotMin = 1.0
- dotV = None
- for v in transformedVertexes:
- diff = v - minZvertex
- len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
- if len < 5:
- continue
- dot = (diff[2] / len)
- if dotMin > dot:
- dotMin = dot
- dotV = diff
- if dotV is None:
- return
- if dotV[1] < 0:
- rad = math.asin(dotMin)
- else:
- rad = -math.asin(dotMin)
- self.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
-
- for obj in self.objectList:
- obj.steepDirty = True
- self.updateModelTransform()
-
- def setViewMode(self, mode):
- if mode == "Normal":
- self.viewSelection.setValue(0)
- if mode == "GCode":
- self.viewSelection.setValue(4)
- wx.CallAfter(self.glCanvas.Refresh)
-
- def loadModelFiles(self, filelist, showWarning = False):
- while len(filelist) > len(self.objectList):
- self.objectList.append(previewObject())
- for idx in xrange(len(filelist), len(self.objectList)):
- self.objectList[idx].mesh = None
- self.objectList[idx].filename = None
- for idx in xrange(0, len(filelist)):
- obj = self.objectList[idx]
- if obj.filename != filelist[idx]:
- obj.fileTime = None
- self.gcodeFileTime = None
- self.logFileTime = None
- obj.mesh = None
- obj.filename = filelist[idx]
-
- self.deselectTool()
- self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
- self.gcode = None
- #Do the STL file loading in a background thread so we don't block the UI.
- if self.loadThread is not None and self.loadThread.isAlive():
- self.abortLoading = True
- self.loadThread.join()
- self.abortLoading = False
- self.loadThread = threading.Thread(target=self.doFileLoadThread)
- self.loadThread.daemon = True
- self.loadThread.start()
-
- if showWarning:
- if (self.matrix - numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)).any() or len(profile.getPluginConfig()) > 0:
- self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll)
-
- def OnCheckReloadFile(self, e):
- #Only show the reload popup when the window has focus, because the popup goes over other programs.
- if self.GetParent().FindFocus() is None:
- return
- for obj in self.objectList:
- if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
- self.checkReloadFileTimer.Stop()
- self.ShowWarningPopup('File changed, reload?', self.reloadModelFiles)
- if wx.TheClipboard.Open():
- data = wx.TextDataObject()
- if wx.TheClipboard.GetData(data):
- data = data.GetText()
- if re.match('^http://.*/.*$', data):
- if data.endswith(tuple(meshLoader.loadSupportedExtensions())):
- #Got an url on the clipboard with a model file.
- pass
- wx.TheClipboard.Close()
-
- def reloadModelFiles(self, filelist = None):
- if filelist is not None:
- #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
- for idx in xrange(0, len(filelist)):
- if self.objectList[idx].filename != filelist[idx]:
- return False
- else:
- filelist = []
- for idx in xrange(0, len(self.objectList)):
- filelist.append(self.objectList[idx].filename)
- self.loadModelFiles(filelist)
- return True
-
- def doFileLoadThread(self):
- for obj in self.objectList:
- if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
- obj.fileTime = os.stat(obj.filename).st_mtime
- try:
- mesh = meshLoader.loadMeshes(obj.filename)[0]
- except:
- traceback.print_exc(file=sys.stdout)
- wx.CallAfter(self.ShowWarningPopup, 'Failed to load %s' % (obj.filename))
- obj.mesh = None
- obj.filename = None
- else:
- obj.mesh = mesh
- obj.dirty = True
- obj.steepDirty = True
- self.updateModelTransform()
- if self.objectsBoundaryCircleSize is not None:
- self.glCanvas.zoom = self.objectsBoundaryCircleSize * 6.0
- self.errorList = []
- wx.CallAfter(self.updateToolbar)
- wx.CallAfter(self.glCanvas.Refresh)
- elif obj.filename is None or not os.path.isfile(obj.filename):
- obj.mesh = None
- obj.filename = None
-
- if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
- self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
- self.gcodeDirty = True
- self.gcode = gcodeInterpreter.gcode()
- self.gcode.progressCallback = self.loadProgress
- self.gcode.load(self.gcodeFilename)
-
- errorList = []
- for line in open(self.gcodeFilename, "rt"):
- res = re.search(';Model error\(([a-z ]*)\): \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\) \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\)', line)
- if res is not None:
- v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
- v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
- errorList.append([v1, v2])
- self.errorList = errorList
-
- wx.CallAfter(self.updateToolbar)
- wx.CallAfter(self.glCanvas.Refresh)
- elif not os.path.isfile(self.gcodeFilename):
- self.gcode = None
- wx.CallAfter(self.checkReloadFileTimer.Start, 1000)
-
- def loadProgress(self, progress):
- if self.gcode is None:
- return True
- if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
- self.layerSelect.setRange(1, len(self.gcode.layerList) - 1)
- self.layerSelect.setValue(self.layerSelect.getMaxValue())
- else:
- self.layerSelect.setRange(1, len(self.gcode.layerList) - 1)
- return self.abortLoading
-
- def OnResetAll(self, e = None):
- profile.putProfileSetting('model_matrix', '1,0,0,0,1,0,0,0,1')
- profile.setPluginConfig([])
- self.updateProfileToControls()
-
- def ShowWarningPopup(self, text, callback = None):
- self.warningPopup.text.SetLabel(text)
- self.warningPopup.callback = callback
- if callback is None:
- self.warningPopup.yesButton.Show(False)
- self.warningPopup.noButton.SetLabel('ok')
- else:
- self.warningPopup.yesButton.Show(True)
- self.warningPopup.noButton.SetLabel('no')
- self.warningPopup.Fit()
- self.warningPopup.Layout()
- self.OnMove()
- self.warningPopup.Show(True)
- self.warningPopup.timer.Start(5000)
-
- def OnWarningPopup(self, e):
- self.warningPopup.Show(False)
- self.warningPopup.timer.Stop()
- self.warningPopup.callback()
-
- def OnHideWarning(self, e):
- self.warningPopup.Show(False)
- self.warningPopup.timer.Stop()
-
- def updateToolbar(self):
- self.sliceButton.setDisabled(len(self.objectList) < 1 or self.objectList[0].mesh is None)
- self.printButton.setDisabled(self.gcode is None)
- self.rotateToolButton.setHidden(self.glCanvas.viewMode == "GCode")
- self.scaleToolButton.setHidden(self.glCanvas.viewMode == "GCode")
- self.mirrorToolButton.setHidden(self.glCanvas.viewMode == "GCode")
- if self.gcode is not None:
- self.layerSelect.setRange(1, len(self.gcode.layerList) - 1)
- self.Update()
-
- def OnViewChange(self):
- selection = self.viewSelection.getValue()
- self.glCanvas.drawSteepOverhang = False
- self.glCanvas.drawBorders = False
- if selection == 0:
- self.glCanvas.viewMode = "Normal"
- elif selection == 1:
- self.glCanvas.viewMode = "Transparent"
- elif selection == 2:
- self.glCanvas.viewMode = "X-Ray"
- elif selection == 3:
- self.glCanvas.viewMode = "Normal"
- self.glCanvas.drawSteepOverhang = True
- elif selection == 4:
- self.layerSelect.setValue(self.layerSelect.getMaxValue())
- self.glCanvas.viewMode = "GCode"
- elif selection == 5:
- self.glCanvas.viewMode = "Mixed"
- self.layerSelect.setHidden(self.glCanvas.viewMode != "GCode")
- self.updateToolbar()
- self.deselectTool()
- self.glCanvas.Refresh()
-
- def deselectTool(self):
- self.rotateToolButton.setSelected(False)
- self.scaleToolButton.setSelected(False)
- self.mirrorToolButton.setSelected(False)
- self.OnToolSelect()
-
- def updateModelTransform(self, f=0):
- if len(self.objectList) < 1 or self.objectList[0].mesh is None:
- return
-
- profile.putProfileSetting('model_matrix', ','.join(map(str, list(self.matrix.getA().flatten()))))
- for obj in self.objectList:
- if obj.mesh is None:
- continue
- obj.mesh.matrix = self.matrix
- obj.mesh.processMatrix()
-
- minV = self.objectList[0].mesh.getMinimum()
- maxV = self.objectList[0].mesh.getMaximum()
- objectsBoundaryCircleSize = self.objectList[0].mesh.getBoundaryCircle()
- for obj in self.objectList:
- if obj.mesh is None:
- continue
-
- minV = numpy.minimum(minV, obj.mesh.getMinimum())
- maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
- objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.getBoundaryCircle())
-
- self.objectsMaxV = maxV
- self.objectsMinV = minV
- self.objectsSize = self.objectsMaxV - self.objectsMinV
- self.objectsBoundaryCircleSize = objectsBoundaryCircleSize
-
- scaleX = numpy.linalg.norm(self.matrix[::,0].getA().flatten())
- scaleY = numpy.linalg.norm(self.matrix[::,1].getA().flatten())
- scaleZ = numpy.linalg.norm(self.matrix[::,2].getA().flatten())
- self.scaleXctrl.setValue(round(scaleX, 2))
- self.scaleYctrl.setValue(round(scaleY, 2))
- self.scaleZctrl.setValue(round(scaleZ, 2))
- self.scaleXmmctrl.setValue(round(self.objectsSize[0], 2))
- self.scaleYmmctrl.setValue(round(self.objectsSize[1], 2))
- self.scaleZmmctrl.setValue(round(self.objectsSize[2], 2))
-
- self.glCanvas.Refresh()
-
- def updateProfileToControls(self):
- self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
- self.updateModelTransform()
- for obj in self.objectList:
- obj.steepDirty = True
- self.glCanvas.updateProfileToControls()
-
-class PreviewGLCanvas(openglGui.glGuiPanel):
- def __init__(self, parent):
- super(PreviewGLCanvas, self).__init__(parent)
- wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
- self.parent = parent
- self.yaw = 30
- self.pitch = 60
- self.zoom = 300
- self.viewTarget = [parent.machineCenter.x, parent.machineCenter.y, 0.0]
- self.view3D = True
- self.gcodeDisplayList = []
- self.gcodeQuickDisplayList = []
- self.gcodeDisplayListMade = 0
- self.gcodeQuickDisplayListMade = 0
- self.objColor = [[1.0, 0.8, 0.6, 1.0], [0.2, 1.0, 0.1, 1.0], [1.0, 0.2, 0.1, 1.0], [0.1, 0.2, 1.0, 1.0]]
- self.oldX = 0
- self.oldY = 0
- self.dragType = ''
- self.tempMatrix = None
- self.viewport = None
-
- def updateProfileToControls(self):
- self.objColor[0] = profile.getPreferenceColour('model_colour')
- self.objColor[1] = profile.getPreferenceColour('model_colour2')
- self.objColor[2] = profile.getPreferenceColour('model_colour3')
- self.objColor[3] = profile.getPreferenceColour('model_colour4')
-
- def OnMouseMotion(self,e):
- if self.parent.objectsMaxV is not None and self.viewport is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
- p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
- p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
- p0 -= self.viewTarget
- p1 -= self.viewTarget
- if not e.Dragging() or self.dragType != 'tool':
- self.parent.tool.OnMouseMove(p0, p1)
- else:
- p0 = [0,0,0]
- p1 = [1,0,0]
-
- if e.Dragging() and e.LeftIsDown():
- if self.dragType == '':
- #Define the drag type depending on the cursor position.
- self.dragType = 'viewRotate'
- if self.viewMode != 'GCode' and self.viewMode != 'Mixed':
- if self.parent.tool.OnDragStart(p0, p1):
- self.dragType = 'tool'
-
- if self.dragType == 'viewRotate':
- 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
- else:
- self.viewTarget[0] -= float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
- self.viewTarget[1] += float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
- elif self.dragType == 'tool':
- self.parent.tool.OnDrag(p0, p1)
-
- #Workaround for buggy ATI cards.
- size = self.GetSizeTuple()
- self.SetSize((size[0]+1, size[1]))
- self.SetSize((size[0], size[1]))
- self.Refresh()
- else:
- if self.dragType != '':
- if self.tempMatrix is not None:
- self.parent.matrix *= self.tempMatrix
- self.parent.updateModelTransform()
- self.tempMatrix = None
- for obj in self.parent.objectList:
- obj.steepDirty = True
- self.parent.tool.OnDragEnd()
- self.dragType = ''
- if e.Dragging() and e.RightIsDown():
- self.zoom += e.GetY() - self.oldY
- if self.zoom < 1:
- self.zoom = 1
- if self.zoom > 500:
- self.zoom = 500
- self.oldX = e.GetX()
- self.oldY = e.GetY()
-
- def getObjectBoundaryCircle(self):
- return self.parent.objectsBoundaryCircleSize
-
- def getObjectSize(self):
- return self.parent.objectsSize
-
- def getObjectMatrix(self):
- return self.parent.matrix
-
- def getObjectCenterPos(self):
- return [self.parent.machineCenter.x, self.parent.machineCenter.y, self.parent.objectsSize[2] / 2 - profile.getProfileSettingFloat('object_sink')]
-
- def OnMouseWheel(self,e):
- self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
- if self.zoom < 1.0:
- self.zoom = 1.0
- if self.zoom > 500:
- self.zoom = 500
- self.Refresh()
-
- def OnKeyChar(self, keycode):
- if keycode == wx.WXK_UP:
- self.parent.layerSelect.setValue(self.parent.layerSelect.getValue() + 1)
- self.Refresh()
- elif keycode == wx.WXK_DOWN:
- self.parent.layerSelect.setValue(self.parent.layerSelect.getValue() - 1)
- self.Refresh()
- elif keycode == wx.WXK_PAGEUP:
- self.parent.layerSelect.setValue(self.parent.layerSelect.getValue() + 10)
- self.Refresh()
- elif keycode == wx.WXK_PAGEDOWN:
- self.parent.layerSelect.setValue(self.parent.layerSelect.getValue() - 10)
- self.Refresh()
-
- def OnPaint(self,e):
- opengl.InitGL(self, self.view3D, self.zoom)
- if self.view3D:
- glTranslate(0,0,-self.zoom)
- glTranslate(self.zoom/20.0,0,0)
- glRotate(-self.pitch, 1,0,0)
- glRotate(self.yaw, 0,0,1)
-
- if self.viewMode == "GCode" or self.viewMode == "Mixed":
- n = min(self.gcodeQuickDisplayListMade, self.parent.layerSelect.getValue())
- if self.parent.gcode is not None and -1 < n < len(self.parent.gcode.layerList) and len(self.parent.gcode.layerList[n]) > 0:
- self.viewTarget[2] = self.parent.gcode.layerList[n][0].list[-1].z
- else:
- if self.parent.objectsMaxV is not None:
- self.viewTarget = self.getObjectCenterPos()
- glTranslate(-self.viewTarget[0], -self.viewTarget[1], -self.viewTarget[2])
-
- self.viewport = glGetIntegerv(GL_VIEWPORT)
- self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
- self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
-
- self.OnDraw()
-
- if len(self.parent.objectList) > 0 and self.parent.objectList[0].mesh is None and self.parent.objectList[0].filename is not None:
- glDisable(GL_DEPTH_TEST)
- glLoadIdentity()
- glColor3ub(255,255,255)
- glTranslate(0, -3, -10)
- opengl.glDrawStringCenter('Loading %s ...' % (os.path.basename(self.parent.objectList[0].filename)))
-
- def OnDraw(self):
- machineSize = self.parent.machineSize
-
- if self.parent.gcode is not None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
- if self.parent.gcodeDirty:
- self.parent.gcodeDirty = False
- self.gcodeDisplayListMade = 0
- self.gcodeQuickDisplayListMade = 0
-
- if self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
- gcodeGenStartTime = time.time()
- while time.time() - gcodeGenStartTime < 0.1 and self.gcodeQuickDisplayListMade < len(self.parent.gcode.layerList):
- if len(self.gcodeQuickDisplayList) == self.gcodeQuickDisplayListMade:
- self.gcodeQuickDisplayList.append(glGenLists(1))
- glNewList(self.gcodeQuickDisplayList[self.gcodeQuickDisplayListMade], GL_COMPILE)
- opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeQuickDisplayListMade], True)
- glEndList()
- self.gcodeQuickDisplayListMade += 1
- while time.time() - gcodeGenStartTime < 0.1 and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
- if len(self.gcodeDisplayList) == self.gcodeDisplayListMade:
- self.gcodeDisplayList.append(glGenLists(1))
- glNewList(self.gcodeDisplayList[self.gcodeDisplayListMade], GL_COMPILE)
- opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade], False)
- glEndList()
- self.gcodeDisplayListMade += 1
- wx.CallAfter(self.Refresh)
-
- glPushMatrix()
- glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, -profile.getProfileSettingFloat('object_sink'))
- for obj in self.parent.objectList:
- if obj.mesh is None:
- continue
- if obj.displayList is None:
- obj.displayList = glGenLists(1)
- obj.steepDisplayList = glGenLists(1)
- obj.outlineDisplayList = glGenLists(1)
- if obj.dirty:
- obj.dirty = False
- glNewList(obj.displayList, GL_COMPILE)
- opengl.DrawMesh(obj.mesh, numpy.linalg.det(obj.mesh.matrix) < 0)
- glEndList()
- glNewList(obj.outlineDisplayList, GL_COMPILE)
- opengl.DrawMeshOutline(obj.mesh)
- glEndList()
-
- if self.viewMode == "Mixed":
- glDisable(GL_BLEND)
- glColor3f(0.0,0.0,0.0)
- self.drawModel(obj.displayList)
- glColor3f(1.0,1.0,1.0)
- glClear(GL_DEPTH_BUFFER_BIT)
-
- glPopMatrix()
-
- if self.parent.gcode is not None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
- glPushMatrix()
- if profile.getPreference('machine_center_is_zero') == 'True':
- glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
- glEnable(GL_COLOR_MATERIAL)
- glEnable(GL_LIGHTING)
- drawQuickUpToLayer = min(self.gcodeQuickDisplayListMade, self.parent.layerSelect.getValue() + 1)
- drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSelect.getValue() + 1)
-
- for i in xrange(drawQuickUpToLayer - 1, -1, -1):
- c = 1.0
- if i < self.parent.layerSelect.getValue():
- c = 0.9 - (drawQuickUpToLayer - i) * 0.1
- if c < 0.4:
- c = (0.4 + c) / 2
- if c < 0.1:
- c = 0.1
- glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
- glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
- if self.gcodeDisplayListMade > i and drawUpToLayer - i < 15:
- glCallList(self.gcodeDisplayList[i])
- else:
- glCallList(self.gcodeQuickDisplayList[i])
-
- glDisable(GL_LIGHTING)
- glDisable(GL_COLOR_MATERIAL)
- glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
- glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
- glPopMatrix()
-
- glColor3f(1.0,1.0,1.0)
- glPushMatrix()
- glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, -profile.getProfileSettingFloat('object_sink'))
- for obj in self.parent.objectList:
- if obj.mesh is None:
- continue
-
- if self.viewMode == "Transparent" or self.viewMode == "Mixed":
- glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
- glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
- #If we want transparent, then first render a solid black model to remove the printer size lines.
- if self.viewMode != "Mixed":
- glDisable(GL_BLEND)
- glColor3f(0.0,0.0,0.0)
- self.drawModel(obj.displayList)
- glColor3f(1.0,1.0,1.0)
- #After the black model is rendered, render the model again but now with lighting and no depth testing.
- glDisable(GL_DEPTH_TEST)
- glEnable(GL_LIGHTING)
- glEnable(GL_BLEND)
- glBlendFunc(GL_ONE, GL_ONE)
- glEnable(GL_LIGHTING)
- self.drawModel(obj.displayList)
- glEnable(GL_DEPTH_TEST)
- elif self.viewMode == "X-Ray":
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
- glDisable(GL_LIGHTING)
- glDisable(GL_DEPTH_TEST)
- glEnable(GL_STENCIL_TEST)
- glStencilFunc(GL_ALWAYS, 1, 1)
- glStencilOp(GL_INCR, GL_INCR, GL_INCR)
- self.drawModel(obj.displayList)
- glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP)
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
- glStencilFunc(GL_EQUAL, 0, 1)
- glColor(1, 1, 1)
- self.drawModel(obj.displayList)
- glStencilFunc(GL_EQUAL, 1, 1)
- glColor(1, 0, 0)
- self.drawModel(obj.displayList)
-
- glPushMatrix()
- glLoadIdentity()
- for i in xrange(2, 15, 2):
- glStencilFunc(GL_EQUAL, i, 0xFF);
- glColor(float(i)/10, float(i)/10, float(i)/5)
- glBegin(GL_QUADS)
- glVertex3f(-1000,-1000,-1)
- glVertex3f( 1000,-1000,-1)
- glVertex3f( 1000, 1000,-1)
- glVertex3f(-1000, 1000,-1)
- glEnd()
- for i in xrange(1, 15, 2):
- glStencilFunc(GL_EQUAL, i, 0xFF);
- glColor(float(i)/10, 0, 0)
- glBegin(GL_QUADS)
- glVertex3f(-1000,-1000,-1)
- glVertex3f( 1000,-1000,-1)
- glVertex3f( 1000, 1000,-1)
- glVertex3f(-1000, 1000,-1)
- glEnd()
- glPopMatrix()
-
- glDisable(GL_STENCIL_TEST)
- glEnable(GL_DEPTH_TEST)
-
- #Fix the depth buffer
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
- self.drawModel(obj.displayList)
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
- elif self.viewMode == "Normal":
- glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
- glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))
- glEnable(GL_LIGHTING)
- self.drawModel(obj.displayList)
-
- if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
- glEnable(GL_DEPTH_TEST)
- glDisable(GL_LIGHTING)
- glColor3f(1,1,1)
- self.drawModel(obj.outlineDisplayList)
-
- if self.drawSteepOverhang:
- if obj.steepDirty:
- obj.steepDirty = False
- glNewList(obj.steepDisplayList, GL_COMPILE)
- opengl.DrawMeshSteep(obj.mesh, self.parent.matrix, 60)
- glEndList()
- glDisable(GL_LIGHTING)
- glColor3f(1,1,1)
- self.drawModel(obj.steepDisplayList)
-
- glPopMatrix()
- #if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
- # glDisable(GL_LIGHTING)
- # glDisable(GL_DEPTH_TEST)
- # glDisable(GL_BLEND)
- # glColor3f(1,0,0)
- # glBegin(GL_LINES)
- # for err in self.parent.errorList:
- # glVertex3f(err[0].x, err[0].y, err[0].z)
- # glVertex3f(err[1].x, err[1].y, err[1].z)
- # glEnd()
- # glEnable(GL_DEPTH_TEST)
-
- opengl.DrawMachine(machineSize)
-
- #Draw the current selected tool
- if self.parent.objectsMaxV is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
- glPushMatrix()
- pos = self.getObjectCenterPos()
- glTranslate(pos[0], pos[1], pos[2])
- self.parent.tool.OnDraw()
- glPopMatrix()
-
- def drawModel(self, displayList):
- vMin = self.parent.objectsMinV
- vMax = self.parent.objectsMaxV
- if vMin is None:
- return
- offset = - vMin - (vMax - vMin) / 2
-
- matrix = opengl.convert3x3MatrixTo4x4(self.parent.matrix)
-
- glPushMatrix()
- glTranslate(0, 0, self.parent.objectsSize[2]/2)
- if self.tempMatrix is not None:
- tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
- glMultMatrixf(tempMatrix)
- glTranslate(0, 0, -self.parent.objectsSize[2]/2)
- glTranslate(offset[0], offset[1], -vMin[2])
- glMultMatrixf(matrix)
- glCallList(displayList)
- glPopMatrix()
+++ /dev/null
-from __future__ import absolute_import
-
-import wx
-import os
-import threading
-import time
-import re
-import shutil
-import ConfigParser
-import numpy
-import math
-
-import OpenGL
-OpenGL.ERROR_CHECKING = False
-from OpenGL.GLU import *
-from OpenGL.GL import *
-
-from Cura.gui.util import opengl
-from Cura.gui.util import toolbarUtil
-from Cura.gui import configBase
-from Cura.gui import printWindow
-from Cura.gui.util import dropTarget
-from Cura.gui.util import taskbar
-from Cura.gui.util import previewTools
-from Cura.gui.util import openglGui
-from Cura.util import validators
-from Cura.util import profile
-from Cura.util import util3d
-from Cura.util import meshLoader
-from Cura.util.meshLoaders import stl
-from Cura.util import mesh
-from Cura.util import sliceRun
-from Cura.util import gcodeInterpreter
-from Cura.util import explorer
-
-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.matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
- self.profile = None
-
- self.modelDisplayList = None
- self.modelDirty = True
-
- self.centerX = self.getSize()[0]/2 + 5
- self.centerY = self.getSize()[1]/2 + 5
-
- self.updateMatrix()
-
- def isSameExceptForPosition(self, other):
- if self.filename != other.filename:
- return False
- if self.matrix != other.matrix:
- return False
- if self.profile != other.profile:
- return False
- return True
-
- def updateMatrix(self):
- self.mesh.matrix = self.matrix
- self.mesh.processMatrix()
-
- scaleX = numpy.linalg.norm(self.matrix[::,0].getA().flatten())
- scaleY = numpy.linalg.norm(self.matrix[::,1].getA().flatten())
- scaleZ = numpy.linalg.norm(self.matrix[::,2].getA().flatten())
- self.parent.scaleXctrl.setValue(round(scaleX, 2))
- self.parent.scaleYctrl.setValue(round(scaleY, 2))
- self.parent.scaleZctrl.setValue(round(scaleZ, 2))
- self.parent.scaleXmmctrl.setValue(round(self.getSize()[0], 2))
- self.parent.scaleYmmctrl.setValue(round(self.getSize()[1], 2))
- self.parent.scaleZmmctrl.setValue(round(self.getSize()[2], 2))
-
- def getMinimum(self):
- return self.mesh.getMinimum()
- def getMaximum(self):
- return self.mesh.getMaximum()
- def getSize(self):
- return self.mesh.getSize()
- def getBoundaryCircle(self):
- return self.mesh.boundaryCircleSize
-
- def clone(self):
- p = ProjectObject(self.parent, self.filename)
-
- p.centerX = self.centerX + 5
- p.centerY = self.centerY + 5
-
- p.filename = self.filename
- p.matrix = self.matrix.copy()
- p.profile = self.profile
-
- p.updateMatrix()
-
- return p
-
- def clampXY(self):
- size = self.getSize()
- if self.centerX < size[0] / 2:
- self.centerX = size[0] / 2
- if self.centerY < size[1] / 2:
- self.centerY = size[1] / 2
- if self.centerX > self.parent.machineSize[0] - size[0] / 2:
- self.centerX = self.parent.machineSize[0] - size[0] / 2
- if self.centerY > self.parent.machineSize[1] - size[1] / 2:
- self.centerY = self.parent.machineSize[1] - size[1] / 2
-
-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.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
-
- self.list = []
- self.selection = None
- self.printMode = 0
- self.alwaysAutoPlace = profile.getPreference('planner_always_autoplace') == '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()
-
- sizer = wx.GridBagSizer(2,2)
- self.panel.SetSizer(sizer)
- self.glCanvas = 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.glCanvas, (1,0), span=(5,1), flag=wx.EXPAND)
- sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
- sizer.Add(self.addButton, (2,1), span=(1,1))
- sizer.Add(self.remButton, (2,2), span=(1,1))
- sizer.Add(self.sliceButton, (3,1), span=(1,1))
- if not self.alwaysAutoPlace:
- sizer.Add(self.autoPlaceButton, (3,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)
-
- group = []
- self.rotateToolButton = openglGui.glRadioButton(self.glCanvas, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
- self.scaleToolButton = openglGui.glRadioButton(self.glCanvas, 9, 'Scale', (1,-1), group, self.OnToolSelect)
- self.mirrorToolButton = openglGui.glRadioButton(self.glCanvas, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
-
- self.resetRotationButton = openglGui.glButton(self.glCanvas, 12, 'Reset', (0,-2), self.OnRotateReset)
- self.layFlatButton = openglGui.glButton(self.glCanvas, 16, 'Lay flat', (0,-3), self.OnLayFlat)
-
- self.resetScaleButton = openglGui.glButton(self.glCanvas, 13, 'Reset', (1,-2), self.OnScaleReset)
-
- self.mirrorXButton = openglGui.glButton(self.glCanvas, 14, 'Mirror X', (2,-2), lambda : self.OnMirror(0))
- self.mirrorYButton = openglGui.glButton(self.glCanvas, 18, 'Mirror Y', (2,-3), lambda : self.OnMirror(1))
- self.mirrorZButton = openglGui.glButton(self.glCanvas, 22, 'Mirror Z', (2,-4), lambda : self.OnMirror(2))
-
- self.scaleForm = openglGui.glFrame(self.glCanvas, (2, -2))
- openglGui.glGuiLayoutGrid(self.scaleForm)
- openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
- self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
- openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
- self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
- openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
- self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
- openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
- self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
- openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
- self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
- openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
- self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
- openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
- self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
-
- self.SetSize((800,600))
-
- self.OnToolSelect()
-
- def OnToolSelect(self):
- if self.rotateToolButton.getSelected():
- self.tool = previewTools.toolRotate(self.glCanvas)
- elif self.scaleToolButton.getSelected():
- self.tool = previewTools.toolScale(self.glCanvas)
- elif self.mirrorToolButton.getSelected():
- self.tool = previewTools.toolNone(self.glCanvas)
- else:
- self.tool = previewTools.toolNone(self.glCanvas)
- self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
- self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
- self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
- self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
- self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
- self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
- self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
- self.glCanvas.Refresh()
-
- def OnRotateReset(self):
- if self.selection is None:
- return
- x = numpy.linalg.norm(self.selection.matrix[::,0].getA().flatten())
- y = numpy.linalg.norm(self.selection.matrix[::,1].getA().flatten())
- z = numpy.linalg.norm(self.selection.matrix[::,2].getA().flatten())
- self.selection.matrix = numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
- self.selection.updateMatrix()
-
- def OnLayFlat(self):
- if self.selection is None:
- return
- transformedVertexes = (numpy.matrix(self.selection.mesh.vertexes, copy = False) * self.selection.matrix).getA()
- minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
- dotMin = 1.0
- dotV = None
- for v in transformedVertexes:
- diff = v - minZvertex
- len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
- if len < 5:
- continue
- dot = (diff[2] / len)
- if dotMin > dot:
- dotMin = dot
- dotV = diff
- if dotV is None:
- return
- rad = -math.atan2(dotV[1], dotV[0])
- self.selection.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
- rad = -math.asin(dotMin)
- self.selection.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
-
-
- transformedVertexes = (numpy.matrix(self.selection.mesh.vertexes, copy = False) * self.selection.matrix).getA()
- minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
- dotMin = 1.0
- dotV = None
- for v in transformedVertexes:
- diff = v - minZvertex
- len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
- if len < 5:
- continue
- dot = (diff[2] / len)
- if dotMin > dot:
- dotMin = dot
- dotV = diff
- if dotV is None:
- return
- if dotV[1] < 0:
- rad = math.asin(dotMin)
- else:
- rad = -math.asin(dotMin)
- self.selection.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
-
- self.selection.updateMatrix()
-
- def OnScaleReset(self):
- if self.selection is None:
- return
- x = 1/numpy.linalg.norm(self.selection.matrix[::,0].getA().flatten())
- y = 1/numpy.linalg.norm(self.selection.matrix[::,1].getA().flatten())
- z = 1/numpy.linalg.norm(self.selection.matrix[::,2].getA().flatten())
- self.selection.matrix *= numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
- self.selection.updateMatrix()
-
- def OnMirror(self, axis):
- if self.selection is None:
- return
- matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
- matrix[axis][axis] = -1
- self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
-
- def OnScaleEntry(self, value, axis):
- if self.selection is None:
- return
- try:
- value = float(value)
- except:
- return
- scale = numpy.linalg.norm(self.selection.matrix[::,axis].getA().flatten())
- scale = value / scale
- if scale == 0:
- return
- if self.scaleUniform.getValue():
- matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
- else:
- matrix = [[1.0,0,0], [0, 1.0, 0], [0, 0, 1.0]]
- matrix[axis][axis] = scale
- self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
- self.selection.updateMatrix()
-
- def OnScaleEntryMM(self, value, axis):
- try:
- value = float(value)
- except:
- return
- scale = self.selection.getSize()[axis]
- scale = value / scale
- if scale == 0:
- return
- if self.scaleUniform.getValue():
- matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
- else:
- matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
- matrix[axis][axis] = scale
- self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
- self.selection.updateMatrix()
-
- 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.glCanvas.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.glCanvas.Refresh()
-
- def OnPrintTypeChange(self):
- self.printMode = 0
- if self.printAllAtOnce.GetValue():
- self.printMode = 1
- if self.alwaysAutoPlace:
- self.OnAutoPlace(None)
- self.glCanvas.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:
- vMin = item.getMinimum()
- vMax = item.getMaximum()
- offset = - vMin - (vMax - vMin) / 2
- offset += numpy.array([item.centerX, item.centerY, (vMax[2] - vMin[2]) / 2])
- vertexes = (item.mesh.vertexes * item.matrix).getA() + offset
- for v in vertexes:
- output.addVertex(v[0], v[1], v[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, 'matrix', ','.join(map(str, item.matrix.getA().flatten())))
- 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.matrix = numpy.matrix(numpy.array(map(float, cp.get(section, 'matrix').split(',')), numpy.float64).reshape((3,3,)))
- 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.updateMatrix()
- i += 1
-
- self.list.append(item)
-
- self.selected = self.list[0]
- self._updateListbox()
- self.OnListSelect(None)
- self.glCanvas.Refresh()
-
- dlg.Destroy()
-
- def On3DClick(self):
- self.glCanvas.yaw = 30
- self.glCanvas.pitch = 60
- self.glCanvas.zoom = 300
- self.glCanvas.view3D = True
- self.glCanvas.Refresh()
-
- def OnTopClick(self):
- self.glCanvas.view3D = False
- self.glCanvas.zoom = self.machineSize[0] / 2 + 10
- self.glCanvas.offsetX = 0
- self.glCanvas.offsetY = 0
- self.glCanvas.Refresh()
-
- def OnListSelect(self, e):
- if self.listbox.GetSelection() == -1:
- return
- self.selection = self.list[self.listbox.GetSelection()]
- self.glCanvas.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.glCanvas.Refresh()
- dlg.Destroy()
-
- def OnRemModel(self, e):
- if self.selection is None:
- return
- self.list.remove(self.selection)
- self._updateListbox()
- self.glCanvas.Refresh()
-
- def OnMoveUp(self, e):
- if self.selection is 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.glCanvas.Refresh()
-
- def OnMoveDown(self, e):
- if self.selection is 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.glCanvas.Refresh()
-
- def OnCopy(self, e):
- if self.selection is None:
- return
-
- item = self.selection.clone()
- self.list.insert(self.list.index(self.selection), item)
- self.selection = item
-
- self._updateListbox()
- self.glCanvas.Refresh()
-
- def OnSetCustomProfile(self, e):
- if self.selection is 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 is not 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)
- if not self.alwaysAutoPlace:
- for item in self.list:
- item.clampXY()
- self.glCanvas.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.getSize()[0] / 2 * dirX
- item.centerY = posY + item.getSize()[1] / 2 * 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.getSize()[0] / 2 * dirX
- item.centerY = posY + item.getSize()[1] / 2 * dirY
- posY += item.getSize()[1] * dirY + extraSizeMin[1] + 1
- minX = min(minX, item.centerX - item.getSize()[0] / 2)
- minY = min(minY, item.centerY - item.getSize()[1] / 2)
- maxX = max(maxX, item.centerX + item.getSize()[0] / 2)
- maxY = max(maxY, item.centerY + item.getSize()[1] / 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()
-
- if self.printMode == 0:
- fileList = []
- positionList = []
- for item in self.list:
- fileList.append(item.filename)
- if profile.getPreference('machine_center_is_zero') == 'True':
- pos = [item.centerX - self.machineSize[0] / 2, item.centerY - self.machineSize[1] / 2]
- else:
- pos = [item.centerX, item.centerY]
- positionList.append(pos + item.matrix.getA().flatten().tolist())
- print positionList
- sliceCommand = sliceRun.getSliceCommand(resultFilename, fileList, positionList)
- else:
- self._saveCombinedSTL(resultFilename + "_temp_.stl")
- sliceCommand = sliceRun.getSliceCommand(resultFilename, [resultFilename + "_temp_.stl"], [profile.getMachineCenterCoords()])
-
- pspw = ProjectSliceProgressWindow(sliceCommand, resultFilename, len(self.list))
- pspw.Centre()
- pspw.Show(True)
-
- 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(openglGui.glGuiPanel):
- def __init__(self, parent, projectPlannerWindow):
- super(PreviewGLCanvas, self).__init__(parent)
- self.parent = projectPlannerWindow
- 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.dragType = ''
- self.viewport = None
- self.allowDrag = False
- self.tempMatrix = None
-
- self.objColor = profile.getPreferenceColour('model_colour')
-
- def OnMouseLeftDown(self,e):
- self.allowDrag = True
- if not self.parent.alwaysAutoPlace and not self.view3D:
- p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
- p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
- p0 -= self.viewTarget
- p1 -= self.viewTarget
- p0 -= self.getObjectCenterPos() - self.viewTarget
- p1 -= self.getObjectCenterPos() - self.viewTarget
- cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
-
- for item in self.parent.list:
- iMin =-item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0])
- iMax = item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0])
- if iMin[0] <= cursorZ0[0] <= iMax[0] and iMin[1] <= cursorZ0[1] <= iMax[1]:
- self.parent.selection = item
- self.parent._updateListbox()
- self.parent.OnListSelect(None)
-
- def OnMouseMotion(self,e):
- if self.viewport is not None:
- p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
- p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
- p0 -= self.viewTarget
- p1 -= self.viewTarget
- p0 -= self.getObjectCenterPos() - self.viewTarget
- p1 -= self.getObjectCenterPos() - self.viewTarget
- if not e.Dragging() or self.dragType != 'tool':
- self.parent.tool.OnMouseMove(p0, p1)
- else:
- p0 = [0,0,0]
- p1 = [1,0,0]
-
- if self.allowDrag and e.Dragging() and e.LeftIsDown():
- if self.dragType == '':
- #Define the drag type depending on the cursor position.
- self.dragType = 'viewRotate'
- if self.parent.tool.OnDragStart(p0, p1):
- self.dragType = 'tool'
- if self.dragType == 'viewRotate':
- 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 is not 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()
- elif self.dragType == 'tool':
- self.parent.tool.OnDrag(p0, p1)
- else:
- if self.dragType != '':
- if self.tempMatrix is not None:
- self.parent.selection.matrix *= self.tempMatrix
- self.parent.selection.updateMatrix()
- self.tempMatrix = None
- self.parent.tool.OnDragEnd()
- self.dragType = ''
- 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):
- 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)
- self.viewTarget = self.parent.machineSize / 2
- self.viewTarget[2] = 0
- glTranslate(-self.viewTarget[0], -self.viewTarget[1], -self.viewTarget[2])
-
- self.viewport = glGetIntegerv(GL_VIEWPORT)
- self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
- self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
-
- self.OnDraw()
-
- 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.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin #- self.parent.extruderOffset[item.extruder]
- iMax1 = item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax #- self.parent.extruderOffset[item.extruder]
- if iMin1[0] < -self.parent.headSizeMin[0] or iMin1[1] < -self.parent.headSizeMin[1]:
- item.validPlacement = False
- if iMax1[0] > machineSize[0] + self.parent.headSizeMax[0] or iMax1[1] > machineSize[1] + self.parent.headSizeMax[1]:
- item.validPlacement = False
- for idx2 in xrange(0, idx1):
- item2 = self.parent.list[idx2]
- iMin2 =-item2.getSize() / 2 + numpy.array([item2.centerX, item2.centerY, 0])
- iMax2 = item2.getSize() / 2 + 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 is None:
- item.modelDisplayList = glGenLists(1);
- if item.modelDirty:
- item.modelDirty = False
- 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)
- vMin = item.getMinimum()
- vMax = item.getMaximum()
- offset = - vMin - (vMax - vMin) / 2
- matrix = opengl.convert3x3MatrixTo4x4(item.matrix)
- glPushMatrix()
- glTranslate(0, 0, item.getSize()[2]/2)
- if self.tempMatrix is not None and item == self.parent.selection:
- tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
- glMultMatrixf(tempMatrix)
- glTranslate(0, 0, -item.getSize()[2]/2)
- glTranslate(offset[0], offset[1], -vMin[2])
- glMultMatrixf(matrix)
- glCallList(item.modelDisplayList)
- glPopMatrix()
-
- vMin =-item.getSize() / 2
- vMax = item.getSize() / 2
- vMax[2] -= vMin[2]
- vMin[2] = 0
- vMinHead = vMin - extraSizeMin# - self.parent.extruderOffset[item.extruder]
- vMaxHead = vMax + extraSizeMax# - self.parent.extruderOffset[item.extruder]
-
- glDisable(GL_LIGHTING)
-
- if not self.parent.alwaysAutoPlace:
- glLineWidth(1)
- 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]))
-
- if self.parent.selection is not None:
- glPushMatrix()
- glTranslate(self.parent.selection.centerX, self.parent.selection.centerY, self.parent.selection.getSize()[2]/2)
- self.parent.tool.OnDraw()
- glPopMatrix()
-
- def getObjectSize(self):
- if self.parent.selection is not None:
- return self.parent.selection.getSize()
- return [0.0,0.0,0.0]
- def getObjectBoundaryCircle(self):
- if self.parent.selection is not None:
- return self.parent.selection.getBoundaryCircle()
- return 0.0
- def getObjectMatrix(self):
- return self.parent.selection.matrix
- def getObjectCenterPos(self):
- if self.parent.selection is None:
- return [0,0,0]
- return [self.parent.selection.centerX, self.parent.selection.centerY, self.getObjectSize()[2] / 2]
-
-class ProjectSliceProgressWindow(wx.Frame):
- def __init__(self, sliceCommand, resultFilename, fileCount):
- super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
- self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
-
- self.sliceCommand = sliceCommand
- self.resultFilename = resultFilename
- self.fileCount = fileCount
- 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(self.fileCount)
- self.progressGauge2.SetValue(-1)
- 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:
- if stepName == 'slice':
- self.progressGauge2.SetValue(self.progressGauge2.GetValue() + 1)
- self.totalDoneFactor = 0
- 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 * self.fileCount)
-
- def OnRun(self):
- self.progressLog = []
- p = sliceRun.startSliceCommandProcess(self.sliceCommand)
- line = p.stdout.readline()
- 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.")
- return
- line = p.stdout.readline()
- line = p.stderr.readline()
- while len(line) > 0:
- line = line.rstrip()
- self.progressLog.append(line)
- line = p.stderr.readline()
- self.returnCode = p.wait()
- self.progressGauge2.SetValue(self.fileCount)
-
- 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 is not None:
- 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 explorer.hasExplorer():
- 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):
- explorer.openExplorer(self.resultFilename)
-
- def OnPrint(self, e):
- printWindow.printFile(self.resultFilename)
-
- def OnShowLog(self, e):
- LogWindow('\n'.join(self.progressLog))
-
-class preferencesDialog(wx.Frame):
- def __init__(self, parent):
- super(preferencesDialog, self).__init__(None, title="Project Planner Preferences", style=wx.DEFAULT_DIALOG_STYLE)
-
- self.parent = parent
- wx.EVT_CLOSE(self, self.OnClose)
-
- self.panel = configBase.configPanelBase(self)
- extruderAmount = int(profile.getPreference('extruder_amount'))
-
- left, right, main = self.panel.CreateConfigPanel(self)
- configBase.TitleRow(left, 'User interface settings')
- c = configBase.SettingRow(left, 'Always auto place objects in planner', 'planner_always_autoplace', True, 'Disable this to allow manual placement in the project planner (requires restart).', type = 'preference')
- 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)