diff --git a/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py b/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py index f018e0c6..eb363a8c 100644 --- a/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py +++ b/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py @@ -406,9 +406,10 @@ class CoolSkein: def setMultiplier(self, remainingOrbitTime): 'Set the feed and flow rate multiplier.' layerTimeActive = self.getLayerTimeActive() - self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive)) - - + if remainingOrbitTime + layerTimeActive > 0.00001: + self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive)) + else: + self.multiplier = 1.0 def main(): 'Display the cool dialog.' diff --git a/Cura/gui/alterationPanel.py b/Cura/gui/alterationPanel.py index c102f19b..5f5098d6 100644 --- a/Cura/gui/alterationPanel.py +++ b/Cura/gui/alterationPanel.py @@ -1,6 +1,7 @@ import wx import sys,math,threading,os +from gui import gcodeTextArea from util import profile class alterationPanel(wx.Panel): @@ -10,8 +11,9 @@ class alterationPanel(wx.Panel): self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'nextobject.gcode', 'replace.csv'] self.currentFile = None - self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB) - self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + #self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB) + #self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + self.textArea = gcodeTextArea.GcodeTextArea(self) self.list = wx.ListBox(self, choices=self.alterationFileList, style=wx.LB_SINGLE) self.list.SetSelection(0) self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.list) diff --git a/Cura/gui/gcodeTextArea.py b/Cura/gui/gcodeTextArea.py new file mode 100644 index 00000000..6cb108a1 --- /dev/null +++ b/Cura/gui/gcodeTextArea.py @@ -0,0 +1,101 @@ +import wx, wx.stc +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:#008000,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") + + #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) + diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index 3cd22ab2..34d2f965 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -86,6 +86,7 @@ class mainWindow(configBase.configWindowBase): if profile.getPreference('lastFile') != '': self.filelist = profile.getPreference('lastFile').split(';') + self.SetTitle(self.filelist[-1] + ' - Cura - ' + version.getVersion()) else: self.filelist = [] self.progressPanelList = [] @@ -299,6 +300,7 @@ class mainWindow(configBase.configWindowBase): filelist.append(self._showOpenDialog("Open file to print")) if filelist[-1] == False: return + self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion()) self.filelist = filelist profile.putPreference('lastFile', ';'.join(self.filelist)) self.preview3d.loadModelFiles(self.filelist) diff --git a/Cura/gui/preview3d.py b/Cura/gui/preview3d.py index ec525096..a2949c91 100644 --- a/Cura/gui/preview3d.py +++ b/Cura/gui/preview3d.py @@ -1,11 +1,6 @@ from __future__ import division -import sys -import math -import threading -import re -import time -import os +import sys, math, threading, re, time, os from wx import glcanvas import wx @@ -63,7 +58,6 @@ class previewPanel(wx.Panel): self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange) self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange) self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange) - self.OnViewChange() self.toolbar.AddSeparator() self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) @@ -103,11 +97,11 @@ class previewPanel(wx.Panel): self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation') self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS) self.rotate.SetRange(0, 360) - self.Bind(wx.EVT_TEXT, self.OnRotate) + self.rotate.Bind(wx.EVT_TEXT, self.OnRotate) self.toolbar2.AddControl(self.rotate) self.toolbar2.Realize() - self.updateToolbar() + self.OnViewChange() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1) @@ -180,7 +174,6 @@ class previewPanel(wx.Panel): self.glCanvas.Refresh() def OnLayerNrChange(self, e): - self.gcodeDirty = True self.glCanvas.Refresh() def updateCenterX(self): @@ -272,7 +265,9 @@ class previewPanel(wx.Panel): pass def updateToolbar(self): - self.layerSpin.Show(self.gcode != None) + self.gcodeViewButton.Show(self.gcode != None) + self.mixedViewButton.Show(self.gcode != None) + self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed") if self.gcode != None: self.layerSpin.SetRange(1, len(self.gcode.layerList)) self.toolbar.Realize() @@ -288,6 +283,7 @@ class previewPanel(wx.Panel): self.glCanvas.viewMode = "GCode" elif self.mixedViewButton.GetValue(): self.glCanvas.viewMode = "Mixed" + self.updateToolbar() self.glCanvas.Refresh() def updateModelTransform(self, f=0): @@ -376,6 +372,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.offsetY = 0 self.view3D = True self.gcodeDisplayList = 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]] def OnMouseMotion(self,e): @@ -439,33 +436,37 @@ class PreviewGLCanvas(glcanvas.GLCanvas): opengl.DrawMachine(machineSize) if self.parent.gcode != None: - if self.gcodeDisplayList == None: - self.gcodeDisplayList = glGenLists(1); if self.parent.gcodeDirty: + if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None: + if self.gcodeDisplayList != None: + glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount) + self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList)); + self.gcodeDisplayListCount = len(self.parent.gcode.layerList) self.parent.gcodeDirty = False - glNewList(self.gcodeDisplayList, GL_COMPILE) prevLayerZ = 0.0 curLayerZ = 0.0 layerThickness = 0.0 - filamentRadius = float(profile.getProfileSetting('filament_diameter')) / 2 + filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 filamentArea = math.pi * filamentRadius * filamentRadius - lineWidth = float(profile.getProfileSetting('nozzle_size')) / 2 / 10 + 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: c = 1.0 - if curLayerNum != self.parent.layerSpin.GetValue(): - if curLayerNum < self.parent.layerSpin.GetValue(): - c = 0.9 - (self.parent.layerSpin.GetValue() - curLayerNum) * 0.1 - if c < 0.4: - c = 0.4 - else: - break + #if curLayerNum != self.parent.layerSpin.GetValue(): + # if curLayerNum < self.parent.layerSpin.GetValue(): + # c = 0.9 - (self.parent.layerSpin.GetValue() - curLayerNum) * 0.1 + # if c < 0.4: + # c = 0.4 + # else: + # break if path.type == 'move': glColor3f(0,0,c) if path.type == 'extrude': @@ -527,9 +528,24 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glVertex3f(v.x, v.y, v.z) glEnd() curLayerNum += 1 - glEndList() + glEnable(GL_CULL_FACE) + glEndList() if self.viewMode == "GCode" or self.viewMode == "Mixed": - glCallList(self.gcodeDisplayList) + glEnable(GL_COLOR_MATERIAL) + glEnable(GL_LIGHTING) + glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0]) + for i in xrange(0, self.parent.layerSpin.GetValue() + 1): + c = 1.0 + if i < self.parent.layerSpin.GetValue(): + c = 0.9 - (self.parent.layerSpin.GetValue() - i) * 0.1 + if c < 0.4: + c = (0.4 + c) / 2 + if c < 0.1: + c = 0.1 + glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c]) + glCallList(self.gcodeDisplayList + i) + glDisable(GL_COLOR_MATERIAL) + glDisable(GL_LIGHTING) glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0) for obj in self.parent.objectList: diff --git a/Cura/gui/projectPlanner.py b/Cura/gui/projectPlanner.py index 9b664ce5..a198a76d 100644 --- a/Cura/gui/projectPlanner.py +++ b/Cura/gui/projectPlanner.py @@ -285,9 +285,7 @@ class projectPlanner(wx.Frame): return (maxX - minX) + (maxY - minY) def OnSlice(self, e): - oldProfile = profile.getGlobalProfileString() - - put = profile.putProfileSetting + put = profile.setTempOverride put('model_multiply_x', '1') put('model_multiply_y', '1') @@ -319,7 +317,7 @@ class projectPlanner(wx.Frame): actionList.append(action) #Restore the old profile. - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() 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") @@ -670,6 +668,7 @@ class ProjectSliceProgressWindow(wx.Frame): self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER) self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND) self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND) + self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER) self.sizer.AddGrowableCol(0) self.sizer.AddGrowableRow(0) @@ -702,7 +701,7 @@ class ProjectSliceProgressWindow(wx.Frame): def OnRun(self): resultFile = open(self.resultFilename, "w") - put = profile.putProfileSetting + put = profile.setTempOverride for action in self.actionList: p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) line = p.stdout.readline() @@ -727,7 +726,6 @@ class ProjectSliceProgressWindow(wx.Frame): line = p.stdout.readline() self.returnCode = p.wait() - oldProfile = profile.getGlobalProfileString() put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x) put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y) put('clear_z', action.clearZ) @@ -743,7 +741,7 @@ class ProjectSliceProgressWindow(wx.Frame): resultFile.write(';TYPE:CUSTOM\n') resultFile.write(profile.getAlterationFileContents('nextobject.gcode')) resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action)) - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r") data = f.read(4096) @@ -762,7 +760,7 @@ class ProjectSliceProgressWindow(wx.Frame): resultFile.close() self.abort = True sliceTime = time.time() - self.sliceStartTime - wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %d:%d' % (sliceTime / 60, sliceTime % 60)) + wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %02d:%02d' % (sliceTime / 60, sliceTime % 60)) wx.CallAfter(self.abortButton.SetLabel, 'Close') def main(): diff --git a/Cura/gui/sliceProgessPanel.py b/Cura/gui/sliceProgessPanel.py index 0fce912b..4eed4712 100644 --- a/Cura/gui/sliceProgessPanel.py +++ b/Cura/gui/sliceProgessPanel.py @@ -59,21 +59,20 @@ class sliceProgessPanel(wx.Panel): if profile.getPreference('save_profile') == 'True': profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini") cmdList = [] - oldProfile = profile.getGlobalProfileString() for filename in self.filelist: idx = self.filelist.index(filename) print filename, idx if idx > 0: - profile.putProfileSetting('fan_enabled', 'False') - profile.putProfileSetting('skirt_line_count', '0') - profile.putProfileSetting('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx))) - profile.putProfileSetting('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx))) - profile.putProfileSetting('alternative_center', self.filelist[0]) + profile.setTempOverride('fan_enabled', 'False') + profile.setTempOverride('skirt_line_count', '0') + profile.setTempOverride('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx))) + profile.setTempOverride('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx))) + profile.setTempOverride('alternative_center', self.filelist[0]) if len(self.filelist) > 1: - profile.putProfileSetting('add_start_end_gcode', 'False') - profile.putProfileSetting('gcode_extension', 'multi_extrude_tmp') + profile.setTempOverride('add_start_end_gcode', 'False') + profile.setTempOverride('gcode_extension', 'multi_extrude_tmp') cmdList.append(sliceRun.getSliceCommand(filename)) - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() self.thread = WorkerThread(self, filelist, cmdList) def OnAbort(self, e): @@ -188,7 +187,10 @@ class WorkerThread(threading.Thread): resultFile.write(';TYPE:CUSTOM\n') resultFile.write(profile.getAlterationFileContents('start.gcode')) for filename in self.filelist: - files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r")) + if os.path.isfile(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp'): + files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r")) + else: + return currentExtruder = 0 resultFile.write('T%d\n' % (currentExtruder)) diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 96e1eb35..686a8d8b 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -67,6 +67,9 @@ profileDefaultSettings = { 'add_start_end_gcode': 'True', 'gcode_extension': 'gcode', + 'alternative_center': '', + 'clear_z': '0.0', + 'extruder': '0', } alterationDefault = { ####################################################################################### @@ -153,6 +156,7 @@ preferencesDefaultSettings = { ## Profile and preferences functions ######################################################### +## Profile functions def getDefaultProfilePath(): return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini")) @@ -188,17 +192,31 @@ def getGlobalProfileString(): p = [] alt = [] + tempDone = [] if globalProfileParser.has_section('profile'): for key in globalProfileParser.options('profile'): - p.append(key + "=" + globalProfileParser.get('profile', key)) + if key in tempOverride: + p.append(key + "=" + unicode(tempOverride[key])) + tempDone.append(key) + else: + p.append(key + "=" + globalProfileParser.get('profile', key)) if globalProfileParser.has_section('alterations'): for key in globalProfileParser.options('alterations'): - alt.append(key + "=" + globalProfileParser.get('alterations', key)) + if key in tempOverride: + p.append(key + "=" + tempOverride[key]) + tempDone.append(key) + else: + alt.append(key + "=" + globalProfileParser.get('alterations', key)) + for key in tempOverride: + if key not in tempDone: + p.append(key + "=" + unicode(tempOverride[key])) ret = '\b'.join(p) + '\f' + '\b'.join(alt) ret = base64.b64encode(zlib.compress(ret, 9)) return ret def getProfileSetting(name): + if name in tempOverride: + return unicode(tempOverride[name]) #Check if we have a configuration file loaded, else load the default. if not globals().has_key('globalProfileParser'): loadGlobalProfile(getDefaultProfilePath()) @@ -230,6 +248,12 @@ def putProfileSetting(name, value): globalProfileParser.add_section('profile') globalProfileParser.set('profile', name, str(value)) +def isProfileSetting(name): + if name in profileDefaultSettings: + return True + return False + +## Preferences functions global globalPreferenceParser globalPreferenceParser = None @@ -239,11 +263,12 @@ def getPreferencePath(): def getPreferenceFloat(name): try: return float(eval(getPreference(name), {}, {})) - except (ValueError, SyntaxError): return 0.0 def getPreference(name): + if name in tempOverride: + return unicode(tempOverride[name]) global globalPreferenceParser if globalPreferenceParser == None: globalPreferenceParser = ConfigParser.ConfigParser() @@ -273,6 +298,18 @@ def putPreference(name, value): globalPreferenceParser.set('preference', name, unicode(value).encode("utf-8")) globalPreferenceParser.write(open(getPreferencePath(), 'w')) +def isPreference(name): + if name in preferencesDefaultSettings: + return True + return False + +## Temp overrides for multi-extruder slicing and the project planner. +tempOverride = {} +def setTempOverride(name, value): + tempOverride[name] = value +def resetTempOverride(): + tempOverride = {} + ######################################################### ## Utility functions to calculate common profile values ######################################################### @@ -316,7 +353,11 @@ def replaceTagMatch(m): tag = m.group(0)[1:-1] if tag in ['print_speed', 'retraction_speed', 'travel_speed', 'max_z_speed', 'bottom_layer_speed', 'cool_min_feedrate']: return str(getProfileSettingFloat(tag) * 60) - return str(getProfileSettingFloat(tag)) + if isProfileSetting(tag): + return str(getProfileSettingFloat(tag)) + if isPreference(tag): + return str(getProfileSettingFloat(tag)) + return tag ### Get aleration raw contents. (Used internally in Cura) def getAlterationFile(filename):