diff --git a/Cura/cura_sf/fabmetheus_utilities/settings.py b/Cura/cura_sf/fabmetheus_utilities/settings.py index fa79a29c..f5d76296 100644 --- a/Cura/cura_sf/fabmetheus_utilities/settings.py +++ b/Cura/cura_sf/fabmetheus_utilities/settings.py @@ -138,7 +138,7 @@ def getProfileInformation(): 'Widen_Width_over_Edge_Width_ratio': DEFSET, },'inset': { 'Add_Custom_Code_for_Temperature_Reading': "False", - 'Infill_in_Direction_of_Bridge': "True", + 'Infill_in_Direction_of_Bridge': ifSettingAboveZero('fill_density'), 'Infill_Width': storedSettingFloat("nozzle_size"), 'Loop_Order_Choice': DEFSET, 'Overlap_Removal_Width_over_Perimeter_Width_ratio': DEFSET, diff --git a/Cura/gui/alterationPanel.py b/Cura/gui/alterationPanel.py index 5f5098d6..144f3d94 100644 --- a/Cura/gui/alterationPanel.py +++ b/Cura/gui/alterationPanel.py @@ -9,6 +9,8 @@ class alterationPanel(wx.Panel): wx.Panel.__init__(self, parent,-1) self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'nextobject.gcode', 'replace.csv'] + if int(profile.getPreference('extruder_amount')) > 1: + self.alterationFileList.append('switchExtruder.gcode') self.currentFile = None #self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB) diff --git a/Cura/gui/configBase.py b/Cura/gui/configBase.py index 40026d1e..79a7d248 100644 --- a/Cura/gui/configBase.py +++ b/Cura/gui/configBase.py @@ -64,7 +64,7 @@ class configWindowBase(wx.Frame): self.popup.text.SetLabel(setting.helpText) self.popup.text.Wrap(350) self.popup.Fit() - if os.name == 'darwin': + if sys.platform == 'darwin': x, y = self.ClientToScreenXY(0, 0) sx, sy = self.GetClientSizeTuple() else: diff --git a/Cura/gui/configWizard.py b/Cura/gui/configWizard.py index 8edaf410..812b073f 100644 --- a/Cura/gui/configWizard.py +++ b/Cura/gui/configWizard.py @@ -180,11 +180,13 @@ class UltimakerCheckupPage(InfoPage): wx.CallAfter(self.AddProgressText, "Error: Missing start message.") self.comm.close() return - + + #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. + time.sleep(3) + wx.CallAfter(self.AddProgressText, "Disabling step motors...") if self.DoCommCommandWithTimeout('M84') == False: wx.CallAfter(self.AddProgressText, "Error: Missing reply to Deactivate steppers (M84).") - wx.CallAfter(self.AddProgressText, "Possible cause: Temperature MIN/MAX.\nCheck temperature sensor connections.") self.comm.close() return @@ -289,6 +291,7 @@ class UltimakerCheckupPage(InfoPage): if line == '': self.comm.close() return False + print line if line.startswith(replyStart): break t.cancel() @@ -372,6 +375,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): return if line.startswith('start'): break + #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. + time.sleep(3) + self.sendGCommand('M302') #Disable cold extrusion protection self.sendGCommand("M92 E%f" % (currentEValue)); self.sendGCommand("G92 E0"); @@ -392,6 +398,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): return if line.startswith('start'): break + #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. + time.sleep(3) + self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out. wx.MessageBox('Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)', 'Machine heatup', wx.OK | wx.ICON_INFORMATION) self.sendGCommand('M104 S0') diff --git a/Cura/gui/gcodeTextArea.py b/Cura/gui/gcodeTextArea.py index 68d05249..5487241f 100644 --- a/Cura/gui/gcodeTextArea.py +++ b/Cura/gui/gcodeTextArea.py @@ -3,101 +3,109 @@ import sys,math,os from util import profile -class GcodeTextArea(wx.stc.StyledTextCtrl): - def __init__(self, parent): - super(GcodeTextArea, self).__init__(parent) - - self.SetLexer(wx.stc.STC_LEX_CONTAINER) - self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyle) - - fontSize = wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize() - fontName = wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL).GetFaceName() - self.SetStyleBits(5) - self.StyleSetSpec(0, "face:%s,size:%d" % (fontName, fontSize)) - self.StyleSetSpec(1, "fore:#006000,face:%s,size:%d" % (fontName, fontSize)) - self.IndicatorSetStyle(0, wx.stc.STC_INDIC_TT) - self.IndicatorSetForeground(0, "#0000FF") - self.IndicatorSetStyle(1, wx.stc.STC_INDIC_SQUIGGLE) - self.IndicatorSetForeground(1, "#FF0000") - self.SetWrapMode(wx.stc.STC_WRAP_NONE) - self.SetScrollWidth(1000) - - #GCodes and MCodes as supported by Marlin - #GCode 21 is not really supported by Marlin, but we still do not report it as error as it's often used. - self.supportedGCodes = [0,1,2,3,4,21,28,90,91,92] - self.supportedMCodes = [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,42,80,81,82,83,84,85,92,104,105,106,107,109,114,115,117,119,140,190,201,202,203,204,205,206,220,221,240,301,302,303,400,500,501,502,503,999] - - def OnStyle(self, e): - lineNr = self.LineFromPosition(self.GetEndStyled()) - while self.PositionFromLine(lineNr) > -1: - line = self.GetLine(lineNr) - start = self.PositionFromLine(lineNr) - length = self.LineLength(lineNr) - self.StartStyling(start, 255) - self.SetStyling(length, 0) - if ';' in line: - pos = line.index(';') - self.StartStyling(start + pos, 31) - self.SetStyling(length - pos, 1) - length = pos +if sys.platform == 'darwin': + class GcodeTextArea(wx.TextCtrl): + def __init__(self, parent): + super(GcodeTextArea, self).__init__(parent, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB) - pos = 0 - while pos < length: - if line[pos] in " \t\n\r": - while pos < length and line[pos] in " \t\n\r": - pos += 1 - else: - end = pos - while end < length and not line[end] in " \t\n\r": - end += 1 - if self.checkGCodePart(line[pos:end], start + pos): - self.StartStyling(start + pos, 0x20) - self.SetStyling(end - pos, 0x20) - pos = end - lineNr += 1 + self.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) - def checkGCodePart(self, part, pos): - if len(part) < 2: - self.StartStyling(pos, 0x40) - self.SetStyling(1, 0x40) - return True - if not part[0] in "GMXYZFESTBPIDCJ": - self.StartStyling(pos, 0x40) - self.SetStyling(1, 0x40) - return True - if part[1] == '{': - if part[-1] != '}': - return True - tag = part[2:-1] - if not profile.isProfileSetting(tag) and not profile.isPreference(tag): - self.StartStyling(pos + 2, 0x40) - self.SetStyling(len(tag), 0x40) - return True - elif part[0] in "GM": - try: - code = int(part[1:]) - except (ValueError): - self.StartStyling(pos + 1, 0x40) - self.SetStyling(len(part) - 1, 0x40) - return True - if part[0] == 'G': - if not code in self.supportedGCodes: - return True - if part[0] == 'M': - if not code in self.supportedMCodes: - return True - else: - try: - float(part[1:]) - except (ValueError): - self.StartStyling(pos + 1, 0x40) - self.SetStyling(len(part) - 1, 0x40) - return True - return False +else: + class GcodeTextArea(wx.stc.StyledTextCtrl): + def __init__(self, parent): + super(GcodeTextArea, self).__init__(parent) - def GetValue(self): - return self.GetText() + self.SetLexer(wx.stc.STC_LEX_CONTAINER) + self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyle) + + fontSize = wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize() + fontName = wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL).GetFaceName() + self.SetStyleBits(5) + self.StyleSetSpec(0, "face:%s,size:%d" % (fontName, fontSize)) + self.StyleSetSpec(1, "fore:#006000,face:%s,size:%d" % (fontName, fontSize)) + self.IndicatorSetStyle(0, wx.stc.STC_INDIC_TT) + self.IndicatorSetForeground(0, "#0000FF") + self.IndicatorSetStyle(1, wx.stc.STC_INDIC_SQUIGGLE) + self.IndicatorSetForeground(1, "#FF0000") + self.SetWrapMode(wx.stc.STC_WRAP_NONE) + self.SetScrollWidth(1000) + + #GCodes and MCodes as supported by Marlin + #GCode 21 is not really supported by Marlin, but we still do not report it as error as it's often used. + self.supportedGCodes = [0,1,2,3,4,21,28,90,91,92] + self.supportedMCodes = [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,42,80,81,82,83,84,85,92,104,105,106,107,109,114,115,117,119,140,190,201,202,203,204,205,206,220,221,240,301,302,303,400,500,501,502,503,999] + + def OnStyle(self, e): + lineNr = self.LineFromPosition(self.GetEndStyled()) + while self.PositionFromLine(lineNr) > -1: + line = self.GetLine(lineNr) + start = self.PositionFromLine(lineNr) + length = self.LineLength(lineNr) + self.StartStyling(start, 255) + self.SetStyling(length, 0) + if ';' in line: + pos = line.index(';') + self.StartStyling(start + pos, 31) + self.SetStyling(length - pos, 1) + length = pos + + pos = 0 + while pos < length: + if line[pos] in " \t\n\r": + while pos < length and line[pos] in " \t\n\r": + pos += 1 + else: + end = pos + while end < length and not line[end] in " \t\n\r": + end += 1 + if self.checkGCodePart(line[pos:end], start + pos): + self.StartStyling(start + pos, 0x20) + self.SetStyling(end - pos, 0x20) + pos = end + lineNr += 1 + + def checkGCodePart(self, part, pos): + if len(part) < 2: + self.StartStyling(pos, 0x40) + self.SetStyling(1, 0x40) + return True + if not part[0] in "GMXYZFESTBPIDCJ": + self.StartStyling(pos, 0x40) + self.SetStyling(1, 0x40) + return True + if part[1] == '{': + if part[-1] != '}': + return True + tag = part[2:-1] + if not profile.isProfileSetting(tag) and not profile.isPreference(tag): + self.StartStyling(pos + 2, 0x40) + self.SetStyling(len(tag), 0x40) + return True + elif part[0] in "GM": + try: + code = int(part[1:]) + except (ValueError): + self.StartStyling(pos + 1, 0x40) + self.SetStyling(len(part) - 1, 0x40) + return True + if part[0] == 'G': + if not code in self.supportedGCodes: + return True + if part[0] == 'M': + if not code in self.supportedMCodes: + return True + else: + try: + float(part[1:]) + except (ValueError): + self.StartStyling(pos + 1, 0x40) + self.SetStyling(len(part) - 1, 0x40) + return True + return False + + def GetValue(self): + return self.GetText() - def SetValue(self, s): - self.SetText(s) + def SetValue(self, s): + self.SetText(s) diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index 34d2f965..d4b8f55f 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -41,7 +41,7 @@ class mainWindow(configBase.configWindowBase): menubar = wx.MenuBar() fileMenu = wx.Menu() i = fileMenu.Append(-1, 'Load model file...') - self.Bind(wx.EVT_MENU, self.OnLoadModel, i) + self.Bind(wx.EVT_MENU, lambda e: self._showModelLoadDialog(1), i) fileMenu.AppendSeparator() i = fileMenu.Append(-1, 'Open Profile...') self.Bind(wx.EVT_MENU, self.OnLoadProfile, i) @@ -189,23 +189,23 @@ class mainWindow(configBase.configWindowBase): loadButton = wx.Button(self, -1, 'Load Model') sliceButton = wx.Button(self, -1, 'Slice to GCode') printButton = wx.Button(self, -1, 'Print GCode') - self.Bind(wx.EVT_BUTTON, self.OnLoadModel, loadButton) + self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(1), loadButton) self.Bind(wx.EVT_BUTTON, self.OnSlice, sliceButton) self.Bind(wx.EVT_BUTTON, self.OnPrint, printButton) extruderCount = int(profile.getPreference('extruder_amount')) if extruderCount > 1: loadButton2 = wx.Button(self, -1, 'Load Dual') - self.Bind(wx.EVT_BUTTON, self.OnLoadModel2, loadButton2) + self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(2), loadButton2) if extruderCount > 2: loadButton3 = wx.Button(self, -1, 'Load Tripple') - self.Bind(wx.EVT_BUTTON, self.OnLoadModel3, loadButton3) + self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(3), loadButton3) if extruderCount > 2: loadButton4 = wx.Button(self, -1, 'Load Quad') - self.Bind(wx.EVT_BUTTON, self.OnLoadModel4, loadButton4) + self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(4), loadButton4) #Also bind double clicking the 3D preview to load an STL file. - self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, self.OnLoadModel, self.preview3d.glCanvas) + self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, lambda e: self._showModelLoadDialog(1), self.preview3d.glCanvas) #Main sizer, to position the preview window, buttons and tab control sizer = wx.GridBagSizer() @@ -236,21 +236,19 @@ class mainWindow(configBase.configWindowBase): self.Show(True) def OnLoadProfile(self, e): - dlg=wx.FileDialog(self, "Select profile file to load", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("ini files (*.ini)|*.ini") if dlg.ShowModal() == wx.ID_OK: profileFile = dlg.GetPath() - self.lastPath = os.path.split(profileFile)[0] profile.loadGlobalProfile(profileFile) self.updateProfileToControls() dlg.Destroy() def OnSaveProfile(self, e): - dlg=wx.FileDialog(self, "Select profile file to save", self.lastPath, style=wx.FD_SAVE) + dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE) dlg.SetWildcard("ini files (*.ini)|*.ini") if dlg.ShowModal() == wx.ID_OK: profileFile = dlg.GetPath() - self.lastPath = os.path.split(profileFile)[0] profile.saveGlobalProfile(profileFile) dlg.Destroy() @@ -268,7 +266,7 @@ class mainWindow(configBase.configWindowBase): machineCom.InstallFirmware(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../firmware/default.hex")) def OnCustomFirmware(self, e): - dlg=wx.FileDialog(self, "Open firmware to upload", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX") if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetPath() diff --git a/Cura/gui/opengl.py b/Cura/gui/opengl.py index 92ffcdea..e5204cff 100644 --- a/Cura/gui/opengl.py +++ b/Cura/gui/opengl.py @@ -1,3 +1,7 @@ +import math + +from util import util3d +from util import profile try: import OpenGL @@ -215,3 +219,84 @@ def DrawSTL(mesh): glVertex3f(v3.x, v3.y, v3.z) glVertex3f(v2.x, v2.y, v2.z) glEnd() + +def DrawGCodeLayer(layer): + filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 + filamentArea = math.pi * filamentRadius * filamentRadius + lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10 + + fillCycle = 0 + fillColorCycle = [[0.5,0.5,0.0],[0.0,0.5,0.5],[0.5,0.0,0.5]] + + glDisable(GL_CULL_FACE) + for path in layer: + if path.type == 'move': + glColor3f(0,0,1) + if path.type == 'extrude': + if path.pathType == 'FILL': + glColor3fv(fillColorCycle[fillCycle]) + fillCycle = (fillCycle + 1) % len(fillColorCycle) + elif path.pathType == 'WALL-INNER': + glColor3fv([0,1,0]) + elif path.pathType == 'SUPPORT': + glColor3fv([0,1,1]) + elif path.pathType == 'SKIRT': + glColor3fv([0,0.5,0.5]) + else: + glColor3fv([1,0,0]) + if path.type == 'retract': + glColor3fv([0,1,1]) + if path.type == 'extrude': + drawLength = 0.0 + prevNormal = None + for i in xrange(0, len(path.list)-1): + v0 = path.list[i] + v1 = path.list[i+1] + + # Calculate line width from ePerDistance (needs layer thickness and filament diameter) + dist = (v0 - v1).vsize() + if dist > 0 and path.layerThickness > 0: + extrusionMMperDist = (v1.e - v0.e) / dist + lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 + + drawLength += (v0 - v1).vsize() + normal = (v0 - v1).cross(util3d.Vector3(0,0,1)) + normal.normalize() + + vv2 = v0 + normal * lineWidth + vv3 = v1 + normal * lineWidth + vv0 = v0 - normal * lineWidth + vv1 = v1 - normal * lineWidth + + glBegin(GL_QUADS) + glVertex3f(vv0.x, vv0.y, vv0.z - 0.01) + glVertex3f(vv1.x, vv1.y, vv1.z - 0.01) + glVertex3f(vv3.x, vv3.y, vv3.z - 0.01) + glVertex3f(vv2.x, vv2.y, vv2.z - 0.01) + glEnd() + if prevNormal != None: + n = (normal + prevNormal) + n.normalize() + vv4 = v0 + n * lineWidth + vv5 = v0 - n * lineWidth + glBegin(GL_QUADS) + glVertex3f(vv2.x, vv2.y, vv2.z) + glVertex3f(vv4.x, vv4.y, vv4.z) + glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z) + glVertex3f(v0.x, v0.y, v0.z) + + glVertex3f(vv0.x, vv0.y, vv0.z) + glVertex3f(vv5.x, vv5.y, vv5.z) + glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z) + glVertex3f(v0.x, v0.y, v0.z) + glEnd() + + prevNormal = normal + prevVv1 = vv1 + prevVv3 = vv3 + else: + glBegin(GL_LINE_STRIP) + for v in path.list: + glVertex3f(v.x, v.y, v.z) + glEnd() + glEnable(GL_CULL_FACE) diff --git a/Cura/gui/preview3d.py b/Cura/gui/preview3d.py index 52665936..372ec4e3 100644 --- a/Cura/gui/preview3d.py +++ b/Cura/gui/preview3d.py @@ -372,6 +372,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.offsetY = 0 self.view3D = True self.gcodeDisplayList = None + self.gcodeDisplayListMade = None self.gcodeDisplayListCount = 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]] @@ -446,93 +447,23 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList)); self.gcodeDisplayListCount = len(self.parent.gcode.layerList) self.parent.gcodeDirty = False - prevLayerZ = 0.0 - curLayerZ = 0.0 - - layerThickness = 0.0 - filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 - filamentArea = math.pi * filamentRadius * filamentRadius - lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10 - - curLayerNum = 0 - for layer in self.parent.gcode.layerList: - glNewList(self.gcodeDisplayList + curLayerNum, GL_COMPILE) - glDisable(GL_CULL_FACE) - curLayerZ = layer[0].list[1].z - layerThickness = curLayerZ - prevLayerZ - prevLayerZ = layer[-1].list[-1].z - for path in layer: - if path.type == 'move': - glColor3f(0,0,1) - if path.type == 'extrude': - if path.pathType == 'FILL': - glColor3f(0.5,0.5,0) - elif path.pathType == 'WALL-INNER': - glColor3f(0,1,0) - elif path.pathType == 'SUPPORT': - glColor3f(0,1,1) - elif path.pathType == 'SKIRT': - glColor3f(0,0.5,0.5) - else: - glColor3f(1,0,0) - if path.type == 'retract': - glColor3f(0,1,1) - if path.type == 'extrude': - for i in xrange(0, len(path.list)-1): - v0 = path.list[i] - v1 = path.list[i+1] - - # Calculate line width from ePerDistance (needs layer thickness and filament diameter) - dist = (v0 - v1).vsize() - if dist > 0 and layerThickness > 0: - extrusionMMperDist = (v1.e - v0.e) / dist - lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2 - - normal = (v0 - v1).cross(util3d.Vector3(0,0,1)) - normal.normalize() - v2 = v0 + normal * lineWidth - v3 = v1 + normal * lineWidth - v0 = v0 - normal * lineWidth - v1 = v1 - normal * lineWidth - - glBegin(GL_QUADS) - if path.pathType == 'FILL': #Remove depth buffer fighting on infill/wall overlap - glVertex3f(v0.x, v0.y, v0.z - 0.02) - glVertex3f(v1.x, v1.y, v1.z - 0.02) - glVertex3f(v3.x, v3.y, v3.z - 0.02) - glVertex3f(v2.x, v2.y, v2.z - 0.02) - else: - glVertex3f(v0.x, v0.y, v0.z - 0.01) - glVertex3f(v1.x, v1.y, v1.z - 0.01) - glVertex3f(v3.x, v3.y, v3.z - 0.01) - glVertex3f(v2.x, v2.y, v2.z - 0.01) - glEnd() - - #for v in path['list']: - # glBegin(GL_TRIANGLE_FAN) - # glVertex3f(v.x, v.y, v.z - 0.001) - # for i in xrange(0, 16+1): - # if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap - # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02) - # else: - # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01) - # glEnd() - else: - glBegin(GL_LINE_STRIP) - for v in path.list: - glVertex3f(v.x, v.y, v.z) - glEnd() - curLayerNum += 1 - glEnable(GL_CULL_FACE) - glEndList() + self.gcodeDisplayListMade = 0 + + if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList): + glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE) + opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade]) + glEndList() + self.gcodeDisplayListMade += 1 + self.Refresh() if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"): glEnable(GL_COLOR_MATERIAL) glEnable(GL_LIGHTING) - for i in xrange(0, self.parent.layerSpin.GetValue() + 1): + drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1) + for i in xrange(0, drawUpToLayer): c = 1.0 if i < self.parent.layerSpin.GetValue(): - c = 0.9 - (self.parent.layerSpin.GetValue() - i) * 0.1 + c = 0.9 - (drawUpToLayer - i) * 0.1 if c < 0.4: c = (0.4 + c) / 2 if c < 0.1: diff --git a/Cura/gui/projectPlanner.py b/Cura/gui/projectPlanner.py index b24721e5..4b5b1ec0 100644 --- a/Cura/gui/projectPlanner.py +++ b/Cura/gui/projectPlanner.py @@ -19,6 +19,8 @@ except: from gui import opengl from gui import toolbarUtil from gui import icon +from gui import configBase +from gui import validators from util import profile from util import util3d from util import stl @@ -29,11 +31,12 @@ class Action(object): pass class ProjectObject(stl.stlModel): - def __init__(self, filename): + def __init__(self, parent, filename): super(ProjectObject, self).__init__() self.load(filename) + self.parent = parent self.filename = filename self.scale = 1.0 self.rotate = 0.0 @@ -108,7 +111,7 @@ class ProjectObject(stl.stlModel): self.modelDirty = True def clone(self): - p = ProjectObject(self.filename) + p = ProjectObject(self.parent, self.filename) p.centerX = self.centerX + 5 p.centerY = self.centerY + 5 @@ -126,6 +129,16 @@ class ProjectObject(stl.stlModel): p.updateModelTransform() return p + + def clampXY(self): + if self.centerX < -self.getMinimum().x * self.scale + self.parent.extruderOffset[self.extruder].x: + self.centerX = -self.getMinimum().x * self.scale + self.parent.extruderOffset[self.extruder].x + if self.centerY < -self.getMinimum().y * self.scale + self.parent.extruderOffset[self.extruder].y: + self.centerY = -self.getMinimum().y * self.scale + self.parent.extruderOffset[self.extruder].y + if self.centerX > self.parent.machineSize.x + self.parent.extruderOffset[self.extruder].x - self.getMaximum().x * self.scale: + self.centerX = self.parent.machineSize.x + self.parent.extruderOffset[self.extruder].x - self.getMaximum().x * self.scale + if self.centerY > self.parent.machineSize.y + self.parent.extruderOffset[self.extruder].y - self.getMaximum().y * self.scale: + self.centerY = self.parent.machineSize.y + self.parent.extruderOffset[self.extruder].y - self.getMaximum().y * self.scale class projectPlanner(wx.Frame): "Main user interface window" @@ -159,6 +172,8 @@ class projectPlanner(wx.Frame): toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True) self.toolbar.AddSeparator() toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner') + self.toolbar.AddSeparator() + toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences') self.toolbar.Realize() @@ -228,6 +243,11 @@ class projectPlanner(wx.Frame): def OnQuit(self, e): self.Close() + def OnPreferences(self, e): + prefDialog = preferencesDialog(self) + prefDialog.Centre() + prefDialog.Show(True) + 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") @@ -264,7 +284,7 @@ class projectPlanner(wx.Frame): while cp.has_section('model_%d' % (i)): section = 'model_%d' % (i) - item = ProjectObject(unicode(cp.get(section, 'filename'), "utf-8")) + item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8")) item.centerX = float(cp.get(section, 'centerX')) item.centerY = float(cp.get(section, 'centerY')) item.scale = float(cp.get(section, 'scale')) @@ -317,7 +337,7 @@ class projectPlanner(wx.Frame): dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL") if dlg.ShowModal() == wx.ID_OK: for filename in dlg.GetPaths(): - item = ProjectObject(filename) + item = ProjectObject(self, filename) profile.putPreference('lastFile', item.filename) self.list.append(item) self.selection = item @@ -387,6 +407,8 @@ class projectPlanner(wx.Frame): bestAllowedSize = i bestArea = area self._doAutoPlace(bestAllowedSize) + for item in self.list: + item.clampXY() self.preview.Refresh() def _doAutoPlace(self, allowedSizeY): @@ -537,14 +559,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas): if item != None: item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2 - if item.centerX < -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x: - item.centerX = -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x - if item.centerY < -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y: - item.centerY = -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y - if item.centerX > self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale: - item.centerX = self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale - if item.centerY > self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale: - item.centerY = self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale + item.clampXY() self.Refresh() else: self.allowDrag = False @@ -821,6 +836,41 @@ class ProjectSliceProgressWindow(wx.Frame): wx.CallAfter(self.abortButton.SetLabel, 'Close') +class preferencesDialog(configBase.configWindowBase): + def __init__(self, parent): + super(preferencesDialog, self).__init__(title="Project Planner Preferences") + + self.parent = parent + wx.EVT_CLOSE(self, self.OnClose) + + extruderAmount = int(profile.getPreference('extruder_amount')) + + left, right, main = self.CreateConfigPanel(self) + configBase.TitleRow(left, 'Machine head size') + c = configBase.SettingRow(left, 'Head size - X towards home (mm)', 'extruder_head_size_min_x', '0', 'Size of your printer head in the X direction, on the Ultimaker your fan is in this direction.', type = 'preference') + validators.validFloat(c, 0.1) + c = configBase.SettingRow(left, 'Head size - X towards end (mm)', 'extruder_head_size_max_x', '0', 'Size of your printer head in the X direction.', type = 'preference') + validators.validFloat(c, 0.1) + c = configBase.SettingRow(left, 'Head size - Y towards home (mm)', 'extruder_head_size_min_y', '0', 'Size of your printer head in the Y direction.', type = 'preference') + validators.validFloat(c, 0.1) + c = configBase.SettingRow(left, 'Head size - Y towards end (mm)', 'extruder_head_size_max_y', '0', 'Size of your printer head in the Y direction.', type = 'preference') + validators.validFloat(c, 0.1) + + 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 = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0) + self.parent.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0) + + self.MakeModal(False) + self.Destroy() + def main(): app = wx.App(False) projectPlanner().Show(True) diff --git a/Cura/gui/simpleMode.py b/Cura/gui/simpleMode.py index 4f38ca95..63928fd3 100644 --- a/Cura/gui/simpleMode.py +++ b/Cura/gui/simpleMode.py @@ -51,8 +51,11 @@ class simpleModeWindow(configBase.configWindowBase): menubar.Append(helpMenu, 'Help') self.SetMenuBar(menubar) - self.lastPath = "" - self.filename = profile.getPreference('lastFile') + if profile.getPreference('lastFile') != '': + self.filelist = profile.getPreference('lastFile').split(';') + self.SetTitle(self.filelist[-1] + ' - Cura - ' + version.getVersion()) + else: + self.filelist = [] self.progressPanelList = [] #Preview window @@ -116,9 +119,8 @@ class simpleModeWindow(configBase.configWindowBase): sizer.Add(printButton, (1,3), flag=wx.RIGHT, border=5) self.sizer = sizer - if self.filename != "None": - self.preview3d.loadModelFiles([self.filename]) - self.lastPath = os.path.split(self.filename)[0] + if len(self.filelist) > 0: + self.preview3d.loadModelFiles(self.filelist) self.updateProfileToControls() @@ -136,7 +138,7 @@ class simpleModeWindow(configBase.configWindowBase): machineCom.InstallFirmware(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../firmware/default.hex")) def OnCustomFirmware(self, e): - dlg=wx.FileDialog(self, "Open firmware to upload", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX") if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetPath() @@ -150,20 +152,17 @@ class simpleModeWindow(configBase.configWindowBase): self.updateProfileToControls() def OnLoadModel(self, e): - dlg=wx.FileDialog(self, "Open file to print", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL") if dlg.ShowModal() == wx.ID_OK: - self.filename=dlg.GetPath() - profile.putPreference('lastFile', self.filename) - if not(os.path.exists(self.filename)): - return - self.lastPath = os.path.split(self.filename)[0] - self.preview3d.loadModelFiles([self.filename]) + self.filelist = [dlg.GetPath()] + profile.putPreference('lastFile', ';'.join(self.filelist)) + self.preview3d.loadModelFiles(self.filelist) self.preview3d.setViewMode("Normal") dlg.Destroy() def OnSlice(self, e): - if self.filename == None: + if len(self.filelist) < 1: wx.MessageBox('You need to load a file before you can slice it.', 'Print error', wx.OK | wx.ICON_INFORMATION) return #save the current profile so we can put it back latter @@ -262,7 +261,7 @@ class simpleModeWindow(configBase.configWindowBase): put('bottom_thickness', '0.0') #Create a progress panel and add it to the window. The progress panel will start the Skein operation. - spp = sliceProgessPanel.sliceProgessPanel(self, self, self.filename) + spp = sliceProgessPanel.sliceProgessPanel(self, self, self.filelist) self.sizer.Add(spp, (len(self.progressPanelList)+2,0), span=(1,4), flag=wx.EXPAND) self.sizer.Layout() newSize = self.GetSize(); @@ -274,13 +273,13 @@ class simpleModeWindow(configBase.configWindowBase): profile.loadGlobalProfileFromString(oldProfile) def OnPrint(self, e): - if self.filename == None: + if len(self.filelist) < 1: wx.MessageBox('You need to load a file and slice it before you can print it.', 'Print error', wx.OK | wx.ICON_INFORMATION) return - if not os.path.exists(self.filename[: self.filename.rfind('.')] + "_export.gcode"): + if not os.path.exists(self.filelist[0][: self.filelist[0].rfind('.')] + "_export.gcode"): wx.MessageBox('You need to slice the file to GCode before you can print it.', 'Print error', wx.OK | wx.ICON_INFORMATION) return - printWindow.printFile(self.filename[: self.filename.rfind('.')] + "_export.gcode") + printWindow.printFile(self.filelist[0][: self.filelist[0].rfind('.')] + "_export.gcode") def OnNormalSwitch(self, e): from gui import mainWindow diff --git a/Cura/gui/sliceProgessPanel.py b/Cura/gui/sliceProgessPanel.py index 9e49e5b3..d2240a56 100644 --- a/Cura/gui/sliceProgessPanel.py +++ b/Cura/gui/sliceProgessPanel.py @@ -190,11 +190,10 @@ class WorkerThread(threading.Thread): resultFile.write(';LAYER:%d\n' % (layerNr)) resultFile.write(';EXTRUDER:%d\n' % (nextExtruder)) if nextExtruder != currentExtruder: - resultFile.write("G1 E-5 F5000\n") - resultFile.write("G92 E0\n") - resultFile.write("T%d\n" % (nextExtruder)) - resultFile.write("G1 E5 F5000\n") - resultFile.write("G92 E0\n") + resultFile.write(';TYPE:CUSTOM\n') + profile.setTempOverride('extruder', nextExtruder) + resultFile.write(profile.getAlterationFileContents('switchExtruder.gcode')) + profile.resetTempOverride() currentExtruder = nextExtruder layerHasLine = True resultFile.write(line) diff --git a/Cura/gui/toolbarUtil.py b/Cura/gui/toolbarUtil.py index 1ec088ef..a609aa6e 100644 --- a/Cura/gui/toolbarUtil.py +++ b/Cura/gui/toolbarUtil.py @@ -1,6 +1,6 @@ from __future__ import division -import os +import os, sys import wx from wx.lib import buttons @@ -41,7 +41,7 @@ class Toolbar(wx.ToolBar): popup.text.SetLabel(control.helpText) popup.text.Wrap(350) popup.Fit(); - if os.name == 'darwin': + if sys.platform == 'darwin': x, y = self.GetParent().ClientToScreenXY(0, 0) sx, sy = self.GetParent().GetClientSizeTuple() else: diff --git a/Cura/images/preferences.png b/Cura/images/preferences.png new file mode 100644 index 00000000..dab6b519 Binary files /dev/null and b/Cura/images/preferences.png differ diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index 5714972c..08586178 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -10,9 +10,10 @@ from util import util3d from util import profile class gcodePath(object): - def __init__(self, newType, pathType, startPoint): + def __init__(self, newType, pathType, layerThickness, startPoint): self.type = newType self.pathType = pathType + self.layerThickness = layerThickness self.list = [startPoint] class gcode(object): @@ -61,10 +62,11 @@ class gcode(object): scale = 1.0 posAbs = True feedRate = 3600 + layerThickness = 0.1 pathType = 'CUSTOM'; startCodeDone = False currentLayer = [] - currentPath = gcodePath('move', pathType, pos.copy()) + currentPath = gcodePath('move', pathType, layerThickness, pos.copy()) currentPath.list[0].e = totalExtrusion currentLayer.append(currentPath) for line in gcodeFile: @@ -88,6 +90,9 @@ class gcode(object): pathType = 'WALL-INNER' elif comment == 'skirt': pathType = 'SKIRT' + if comment.startswith('LAYER:'): + self.layerList.append(currentLayer) + currentLayer = [] if pathType != "CUSTOM": startCodeDone = True line = line[0:line.find(';')] @@ -126,9 +131,8 @@ class gcode(object): else: pos.z += z * scale #Check if we have a new layer. - if oldPos.z < pos.z and startCodeDone and len(currentLayer) > 0: - self.layerList.append(currentLayer) - currentLayer = [] + if oldPos.z != pos.z: + layerThickness = abs(oldPos.z - pos.z) if f is not None: feedRate = f if x is not None or y is not None or z is not None: @@ -152,7 +156,7 @@ class gcode(object): if totalExtrusion > maxExtrusion: maxExtrusion = totalExtrusion if currentPath.type != moveType or currentPath.pathType != pathType: - currentPath = gcodePath(moveType, pathType, currentPath.list[-1]) + currentPath = gcodePath(moveType, pathType, layerThickness, currentPath.list[-1]) currentLayer.append(currentPath) newPos = pos.copy() newPos.e = totalExtrusion diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 5e6f67ef..22d30fb0 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -122,6 +122,13 @@ G92 E0 G1 Z0 F{max_z_speed} """, ####################################################################################### + 'switchExtruder.gcode': """;Switch between the current extruder and the next extruder, when printing with multiple extruders. +G1 E-5 F5000 +G92 E0 +T{extruder} +G1 E5 F5000 +G92 E0 +""", } preferencesDefaultSettings = { 'wizardDone': 'False',