X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Fgui%2FsceneView.py;h=5974fd8b674ec483fd4e22d382d8f52fe01d7fd3;hb=103989f26ff3dfa1b4b3308bd4bd02d0627351f7;hp=7b30c5394d43f39e6237a1ae49cd576c12490816;hpb=68c6f2784d44cebdc241e40eded0e25b1f3d9137;p=cura.git diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index 7b30c539..5974fd8b 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -8,6 +8,7 @@ import os import traceback import threading import math +import platform import OpenGL OpenGL.ERROR_CHECKING = False @@ -26,6 +27,8 @@ from Cura.util import gcodeInterpreter from Cura.gui.util import previewTools from Cura.gui.util import opengl from Cura.gui.util import openglGui +from Cura.gui.tools import youmagineGui +from Cura.gui.tools import imageToMesh class SceneView(openglGui.glGuiPanel): def __init__(self, parent): @@ -37,6 +40,7 @@ class SceneView(openglGui.glGuiPanel): self._scene = objectScene.Scene() self._gcode = None self._gcodeVBOs = [] + self._gcodeFilename = None self._gcodeLoadThread = None self._objectShader = None self._objectLoadShader = None @@ -49,33 +53,33 @@ class SceneView(openglGui.glGuiPanel): self._viewTarget = numpy.array([0,0,0], numpy.float32) self._animView = None self._animZoom = None - self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0] - self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32) + self._platformMesh = {} self._isSimpleMode = True + self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh()) self._viewport = None self._modelMatrix = None self._projMatrix = None self.tempMatrix = None - self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel) - self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow) + self.openFileButton = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel) + self.printButton = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton) self.printButton.setDisabled(True) group = [] - self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect) - self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect) - self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect) + self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect) + self.scaleToolButton = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect) + self.mirrorToolButton = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect) - self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset) - self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat) + self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset) + self.layFlatButton = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat) - self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset) - self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax) + self.resetScaleButton = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset) + self.scaleMaxButton = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax) - self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0)) - self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1)) - self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2)) + self.mirrorXButton = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0)) + self.mirrorYButton = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1)) + self.mirrorZButton = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2)) self.rotateToolButton.setExpandArrow(True) self.scaleToolButton.setExpandArrow(True) @@ -83,23 +87,26 @@ class SceneView(openglGui.glGuiPanel): self.scaleForm = openglGui.glFrame(self, (2, -2)) openglGui.glGuiLayoutGrid(self.scaleForm) - openglGui.glLabel(self.scaleForm, 'Scale X', (0,0)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8)) self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None) - self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange) - self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh()) + self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange) + self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh()) + + self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene)) + self.youMagineButton.setDisabled(True) self.notification = openglGui.glNotification(self, (0, 0)) @@ -114,10 +121,83 @@ class SceneView(openglGui.glGuiPanel): self.updateToolButtons() self.updateProfileToControls() + def loadGCodeFile(self, filename): + self.OnDeleteAll(None) + if self._gcode is not None: + self._gcode = None + for layerVBOlist in self._gcodeVBOs: + for vbo in layerVBOlist: + self.glReleaseList.append(vbo) + self._gcodeVBOs = [] + self._gcode = gcodeInterpreter.gcode() + self._gcodeFilename = filename + self.printButton.setBottomText('') + self.viewSelection.setValue(4) + self.printButton.setDisabled(False) + self.youMagineButton.setDisabled(True) + self.OnViewChange() + + def loadSceneFiles(self, filenames): + self.youMagineButton.setDisabled(False) + #if self.viewSelection.getValue() == 4: + # self.viewSelection.setValue(0) + # self.OnViewChange() + self.loadScene(filenames) + + def loadFiles(self, filenames): + mainWindow = self.GetParent().GetParent().GetParent() + # only one GCODE file can be active + # so if single gcode file, process this + # otherwise ignore all gcode files + gcodeFilename = None + if len(filenames) == 1: + filename = filenames[0] + ext = os.path.splitext(filename)[1].lower() + if ext == '.g' or ext == '.gcode': + gcodeFilename = filename + mainWindow.addToModelMRU(filename) + if gcodeFilename is not None: + self.loadGCodeFile(gcodeFilename) + else: + # process directories and special file types + # and keep scene files for later processing + scene_filenames = [] + ignored_types = dict() + # use file list as queue + # pop first entry for processing and append new files at end + while filenames: + filename = filenames.pop(0) + if os.path.isdir(filename): + # directory: queue all included files and directories + filenames.extend(os.path.join(filename, f) for f in os.listdir(filename)) + else: + ext = os.path.splitext(filename)[1].lower() + if ext == '.ini': + profile.loadProfile(filename) + mainWindow.addToProfileMRU(filename) + elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions(): + scene_filenames.append(filename) + mainWindow.addToModelMRU(filename) + else: + ignored_types[ext] = 1 + if ignored_types: + ignored_types = ignored_types.keys() + ignored_types.sort() + self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types)) + mainWindow.updateProfileToAllControls() + # now process all the scene files + if scene_filenames: + self.loadSceneFiles(scene_filenames) + self._selectObject(None) + self.sceneUpdated() + newZoom = numpy.max(self._machineSize) + self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5) + self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5) + def showLoadModel(self, button = 1): if button == 1: - dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE) - dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE") + dlg=wx.FileDialog(self, _("Open 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE) + dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE") if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return @@ -126,35 +206,12 @@ class SceneView(openglGui.glGuiPanel): if len(filenames) < 1: return False profile.putPreference('lastFile', filenames[0]) - gcodeFilename = None - for filename in filenames: - self.GetParent().GetParent().GetParent().addToModelMRU(filename) - ext = filename[filename.rfind('.')+1:].upper() - if ext == 'G' or ext == 'GCODE': - gcodeFilename = filename - if gcodeFilename is not None: - if self._gcode is not None: - self._gcode = None - for layerVBOlist in self._gcodeVBOs: - for vbo in layerVBOlist: - self.glReleaseList.append(vbo) - self._gcodeVBOs = [] - self._gcode = gcodeInterpreter.gcode() - self._gcodeFilename = gcodeFilename - self.printButton.setBottomText('') - self.viewSelection.setValue(4) - self.printButton.setDisabled(False) - self.OnViewChange() - else: - if self.viewSelection.getValue() == 4: - self.viewSelection.setValue(0) - self.OnViewChange() - self.loadScene(filenames) + self.loadFiles(filenames) def showSaveModel(self): if len(self._scene.objects()) < 1: return - dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) + dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) dlg.SetWildcard(meshLoader.saveWildcardFilter()) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() @@ -163,12 +220,10 @@ class SceneView(openglGui.glGuiPanel): dlg.Destroy() meshLoader.saveMeshes(filename, self._scene.objects()) - def showPrintWindow(self, button): + def OnPrintButton(self, button): if button == 1: if machineCom.machineIsConnected(): - printWindow.printFile(self._gcodeFilename) - if self._gcodeFilename == self._slicer.getGCodeFilename(): - self._slicer.submitSliceInfoOnline() + self.showPrintWindow() elif len(removableStorage.getPossibleSDcardDrives()) > 0: drives = removableStorage.getPossibleSDcardDrives() if len(drives) > 1: @@ -186,17 +241,25 @@ class SceneView(openglGui.glGuiPanel): self.showSaveGCode() if button == 3: menu = wx.Menu() - self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB')) - self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...')) - self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...')) + self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB"))) + self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode..."))) + self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log..."))) self.PopupMenu(menu) menu.Destroy() + def showPrintWindow(self): + if self._gcodeFilename is None: + return + self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID()) + if self._gcodeFilename == self._slicer.getGCodeFilename(): + self._slicer.submitSliceInfoOnline() + def showSaveGCode(self): - defPath = profile.getPreference('lastFile') - defPath = defPath[0:defPath.rfind('.')] + '.gcode' - dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE) - dlg.SetFilename(self._scene._objectList[0].getName()) + if len(self._scene._objectList) < 1: + return + dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE) + filename = self._scene._objectList[0].getName() + '.gcode' + dlg.SetFilename(filename) dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g') if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() @@ -232,7 +295,7 @@ class SceneView(openglGui.glGuiPanel): self._slicer.submitSliceInfoOnline() def _showSliceLog(self): - dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE) + dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE) dlg.ShowModal() dlg.Destroy() @@ -273,7 +336,6 @@ class SceneView(openglGui.glGuiPanel): self.viewMode = 'gcode' if self._gcode is not None and self._gcode.layerList is not None: self.layerSelect.setRange(1, len(self._gcode.layerList) - 1) - self.layerSelect.setValue(len(self._gcode.layerList) - 1) self._selectObject(None) elif self.viewSelection.getValue() == 1: self.viewMode = 'overhang' @@ -360,7 +422,7 @@ class SceneView(openglGui.glGuiPanel): if self._focusObj is None: return obj = self._focusObj - dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100) + dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return @@ -393,11 +455,23 @@ class SceneView(openglGui.glGuiPanel): self._selectObject(None) self.sceneUpdated() + def OnCenter(self, e): + if self._focusObj is None: + return + self._focusObj.setPosition(numpy.array([0.0, 0.0])) + self._scene.pushFree() + newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2]) + self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5) + self.sceneUpdated() + def _splitCallback(self, progress): print progress def OnMergeObjects(self, e): if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj: + if len(self._scene.objects()) == 2: + self._scene.merge(self._scene.objects()[0], self._scene.objects()[1]) + self.sceneUpdated() return self._scene.merge(self._selectedObj, self._focusObj) self.sceneUpdated() @@ -432,11 +506,16 @@ class SceneView(openglGui.glGuiPanel): self._gcodeVBOs = [] if ready: self.printButton.setProgressBar(None) - cost = self._slicer.getFilamentCost() - if cost is not None: - self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost)) - else: - self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount())) + text = '%s' % (self._slicer.getPrintTime()) + for e in xrange(0, int(profile.getMachineSetting('extruder_amount'))): + amount = self._slicer.getFilamentAmount(e) + if amount is None: + continue + text += '\n%s' % (amount) + cost = self._slicer.getFilamentCost(e) + if cost is not None: + text += '\n%s' % (cost) + self.printButton.setBottomText(text) self._gcode = gcodeInterpreter.gcode() self._gcodeFilename = self._slicer.getGCodeFilename() else: @@ -448,17 +527,13 @@ class SceneView(openglGui.glGuiPanel): self._gcode.load(self._gcodeFilename) def _gcodeLoadCallback(self, progress): - if self._gcode is None: + if not self or self._gcode is None: return True if len(self._gcode.layerList) % 15 == 0: time.sleep(0.1) 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) + self.layerSelect.setRange(1, len(self._gcode.layerList) - 1) if self.viewMode == 'gcode': self._queueRefresh() return False @@ -466,7 +541,12 @@ class SceneView(openglGui.glGuiPanel): def loadScene(self, fileList): for filename in fileList: try: - objList = meshLoader.loadMeshes(filename) + ext = os.path.splitext(filename)[1].lower() + if ext in imageToMesh.supportedExtensions(): + imageToMesh.convertImageDialog(self, filename).Show() + objList = [] + else: + objList = meshLoader.loadMeshes(filename) except: traceback.print_exc() else: @@ -478,6 +558,8 @@ class SceneView(openglGui.glGuiPanel): self._scene.add(obj) self._scene.centerAll() self._selectObject(obj) + if obj.getScale()[0] < 1.0: + self.notification.message("Warning: Object scaled down.") self.sceneUpdated() def _deleteObject(self, obj): @@ -499,7 +581,7 @@ class SceneView(openglGui.glGuiPanel): self.updateProfileToControls() self.updateToolButtons() if zoom and obj is not None: - newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2]) + newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2]) self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5) newZoom = obj.getBoundaryCircle() * 6 if newZoom > numpy.max(self._machineSize) * 3: @@ -509,17 +591,17 @@ class SceneView(openglGui.glGuiPanel): def updateProfileToControls(self): oldSimpleMode = self._isSimpleMode self._isSimpleMode = profile.getPreference('startMode') == 'Simple' - if self._isSimpleMode and not oldSimpleMode: + if self._isSimpleMode != oldSimpleMode: self._scene.arrangeAll() self.sceneUpdated() - self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]) + self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]) self._objColors[0] = profile.getPreferenceColour('model_colour') self._objColors[1] = profile.getPreferenceColour('model_colour2') self._objColors[2] = profile.getPreferenceColour('model_colour3') self._objColors[3] = profile.getPreferenceColour('model_colour4') self._scene.setMachineSize(self._machineSize) self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)) - self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height')) + self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height')) if self._selectedObj is not None: scale = self._selectedObj.getScale() @@ -532,7 +614,7 @@ class SceneView(openglGui.glGuiPanel): self.scaleZmmctrl.setValue(round(size[2], 2)) def OnKeyChar(self, keyCode): - if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE: + if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"): if self._selectedObj is not None: self._deleteObject(self._selectedObj) self.QueueRefresh() @@ -605,13 +687,14 @@ class SceneView(openglGui.glGuiPanel): if e.GetButton() == 3: menu = wx.Menu() if self._focusObj is not None: - self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete')) - self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply')) - self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split')) - if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1: - self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge')) + self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object"))) + self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform"))) + self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object"))) + self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts"))) + if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1: + self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge"))) if len(self._scene.objects()) > 0: - self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all')) + self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects"))) if menu.MenuItemCount > 0: self.PopupMenu(menu) menu.Destroy() @@ -689,7 +772,8 @@ class SceneView(openglGui.glGuiPanel): self.Refresh() def OnMouseLeave(self, e): - self._mouseX = -1 + #self._mouseX = -1 + pass def getMouseRay(self, x, y): if self._viewport is None: @@ -732,13 +816,13 @@ class SceneView(openglGui.glGuiPanel): def OnPaint(self,e): if machineCom.machineIsConnected(): self.printButton._imageID = 6 - self.printButton._tooltip = 'Print' + self.printButton._tooltip = _("Print") elif len(removableStorage.getPossibleSDcardDrives()) > 0: self.printButton._imageID = 2 - self.printButton._tooltip = 'Toolpath to SD' + self.printButton._tooltip = _("Toolpath to SD") else: self.printButton._imageID = 3 - self.printButton._tooltip = 'Save toolpath' + self.printButton._tooltip = _("Save toolpath") if self._animView is not None: self._viewTarget = self._animView.getPosition() @@ -829,7 +913,7 @@ void main(void) gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity); } """) - else: + if self._objectShader is None or not self._objectShader.isValid(): self._objectShader = opengl.GLFakeShader() self._objectOverhangShader = opengl.GLFakeShader() self._objectLoadShader = None @@ -877,7 +961,8 @@ void main(void) self._gcodeLoadThread.start() if self._gcode is not None and self._gcode.layerList is not None: glPushMatrix() - glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0) + if profile.getMachineSetting('machine_center_is_zero') != 'True': + glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0) t = time.time() drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1) for n in xrange(0, drawUpTill): @@ -1025,6 +1110,18 @@ void main(void) self._drawMachine() + if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID(): + glEnable(GL_BLEND) + z = self._usbPrintMonitor.getZ() + size = self._machineSize + glColor4ub(255,255,0,128) + glBegin(GL_QUADS) + glVertex3f(-size[0]/2,-size[1]/2, z) + glVertex3f( size[0]/2,-size[1]/2, z) + glVertex3f( size[0]/2, size[1]/2, z) + glVertex3f(-size[0]/2, size[1]/2, z) + glEnd() + if self.viewMode == 'gcode': if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive(): glDisable(GL_DEPTH_TEST) @@ -1032,7 +1129,7 @@ void main(void) glLoadIdentity() glTranslate(0,-4,-10) glColor4ub(60,60,60,255) - opengl.glDrawStringCenter('Loading toolpath for visualization...') + opengl.glDrawStringCenter(_("Loading toolpath for visualization...")) glPopMatrix() else: #Draw the object box-shadow, so you can see where it will collide with other objects. @@ -1083,7 +1180,7 @@ void main(void) glLoadIdentity() glTranslate(0,-4,-10) glColor4ub(60,60,60,255) - opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.') + opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support.")) glPopMatrix() def _renderObject(self, obj, brightness = False, addSink = True): @@ -1117,13 +1214,75 @@ void main(void) glEnable(GL_CULL_FACE) glEnable(GL_BLEND) - if profile.getPreference('machine_type') == 'ultimaker': + size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')] + + machine = profile.getMachineSetting('machine_type') + if profile.getMachineSetting('machine_type').startswith('ultimaker'): + if machine not in self._platformMesh: + meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl')) + if len(meshes) > 0: + self._platformMesh[machine] = meshes[0] + else: + self._platformMesh[machine] = None + if machine == 'ultimaker2': + self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32) + else: + self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32) glColor4f(1,1,1,0.5) self._objectShader.bind() - self._renderObject(self._platformMesh, False, False) + self._renderObject(self._platformMesh[machine], False, False) self._objectShader.unbind() - size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')] + #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text. + if machine == 'ultimaker2': + if not hasattr(self._platformMesh[machine], 'texture'): + self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png') + glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture) + glEnable(GL_TEXTURE_2D) + glPushMatrix() + glColor4f(1,1,1,1) + + glTranslate(0,150,-5) + h = 50 + d = 8 + w = 100 + glEnable(GL_BLEND) + glBlendFunc(GL_DST_COLOR, GL_ZERO) + glBegin(GL_QUADS) + glTexCoord2f(1, 0) + glVertex3f( w, 0, h) + glTexCoord2f(0, 0) + glVertex3f(-w, 0, h) + glTexCoord2f(0, 1) + glVertex3f(-w, 0, 0) + glTexCoord2f(1, 1) + glVertex3f( w, 0, 0) + + glTexCoord2f(1, 0) + glVertex3f(-w, d, h) + glTexCoord2f(0, 0) + glVertex3f( w, d, h) + glTexCoord2f(0, 1) + glVertex3f( w, d, 0) + glTexCoord2f(1, 1) + glVertex3f(-w, d, 0) + glEnd() + glDisable(GL_TEXTURE_2D) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glPopMatrix() + else: + glColor4f(0,0,0,1) + glLineWidth(3) + glBegin(GL_LINES) + glVertex3f(-size[0] / 2, -size[1] / 2, 0) + glVertex3f(-size[0] / 2, -size[1] / 2, 10) + glVertex3f(-size[0] / 2, -size[1] / 2, 0) + glVertex3f(-size[0] / 2+10, -size[1] / 2, 0) + glVertex3f(-size[0] / 2, -size[1] / 2, 0) + glVertex3f(-size[0] / 2, -size[1] / 2+10, 0) + glEnd() + + #Cornerpoints for big blue square v0 = [ size[0] / 2, size[1] / 2, size[2]] v1 = [ size[0] / 2,-size[1] / 2, size[2]] v2 = [-size[0] / 2, size[1] / 2, size[2]] @@ -1143,7 +1302,9 @@ void main(void) glDrawArrays(GL_QUADS, 4, 8) glColor4ub(5, 171, 231, 128) glDrawArrays(GL_QUADS, 12, 8) + glDisableClientState(GL_VERTEX_ARRAY) + #Draw checkerboard sx = self._machineSize[0] sy = self._machineSize[1] for x in xrange(-int(sx/20)-1, int(sx / 20) + 1): @@ -1156,6 +1317,7 @@ void main(void) y1 = max(min(y1, sy/2), -sy/2) x2 = max(min(x2, sx/2), -sx/2) y2 = max(min(y2, sy/2), -sy/2) + #Black or "white" checker if (x & 1) == (y & 1): glColor4ub(5, 171, 231, 127) else: @@ -1166,8 +1328,7 @@ void main(void) glVertex3f(x2, y2, -0.02) glVertex3f(x1, y2, -0.02) glEnd() - - glDisableClientState(GL_VERTEX_ARRAY) + #if UM2, draw bat-area zone for head. THe head can't stop there, because its bat-area. glDisable(GL_BLEND) glDisable(GL_CULL_FACE) @@ -1192,6 +1353,7 @@ void main(void) def _generateGCodeVBOs2(self, layer): filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 filamentArea = math.pi * filamentRadius * filamentRadius + useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode' ret = [] for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']: @@ -1213,7 +1375,10 @@ void main(void) normal[:,2] /= lens ePerDist = path['extrusion'][1:] / lens - lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2) + if useFilamentArea: + lineWidth = ePerDist / path['layerThickness'] / 2.0 + else: + lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2) normal[:,0] *= lineWidth normal[:,1] *= lineWidth @@ -1223,8 +1388,6 @@ void main(void) b = numpy.concatenate((b, a[1:] - normal), 1) b = numpy.concatenate((b, a[:-1] - normal), 1) b = numpy.concatenate((b, a[:-1] + normal), 1) - #b = numpy.concatenate((b, a[:-1]), 1) - #b = numpy.concatenate((b, a[:-1]), 1) b = b.reshape((len(b) * 4, 3)) if len(a) > 2: