chiark / gitweb /
Merge branch 'master' of github.com:daid/Cura
authorDaid <daid303@gmail.com>
Fri, 4 May 2012 22:10:37 +0000 (00:10 +0200)
committerDaid <daid303@gmail.com>
Fri, 4 May 2012 22:10:37 +0000 (00:10 +0200)
Conflicts:
Cura/gui/sliceProgessPanel.py

27 files changed:
Cura/cura_sf/fabmetheus_utilities/geometry/solids/triangle_mesh.py
Cura/cura_sf/fabmetheus_utilities/settings.py
Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py
Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py
Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py
Cura/gui/alterationPanel.py
Cura/gui/gcodeTextArea.py [new file with mode: 0644]
Cura/gui/mainWindow.py
Cura/gui/opengl.py
Cura/gui/preferencesDialog.py
Cura/gui/preview3d.py
Cura/gui/printWindow.py
Cura/gui/projectPlanner.py
Cura/gui/simpleMode.py
Cura/gui/sliceProgessPanel.py
Cura/gui/toolbarUtil.py
Cura/gui/validators.py
Cura/images/copy.png [new file with mode: 0644]
Cura/images/move-down.png [new file with mode: 0644]
Cura/images/move-up.png [new file with mode: 0644]
Cura/images/object-add.png [new file with mode: 0644]
Cura/images/object-remove.png [new file with mode: 0644]
Cura/util/gcodeInterpreter.py
Cura/util/profile.py
Cura/util/sliceRun.py
Cura/util/stl.py
Cura/util/util3d.py

index 3239830577c9f7a30304a8e70a74b030070295cd..550f400689a94c47afe9e5cdca1b9fa1401f76e8 100644 (file)
@@ -824,7 +824,7 @@ class TriangleMesh( group.Group ):
                halfHeight = 0.5 * self.layerHeight
                self.zoneArrangement = ZoneArrangement(self.layerHeight, self.getTransformedVertexes())
                layerTop = self.cornerMaximum.z - halfHeight * 0.5
-               z = self.cornerMinimum.z + halfHeight
+               z = halfHeight
                layerCount = int((layerTop - z) / self.layerHeight) + 1
                while z < layerTop:
                        getLoopLayerAppend(self.loopLayers, layerCount, z).loops = self.getLoopsFromMesh(self.zoneArrangement.getEmptyZ(z))
index 08ad268d7691e7f16bb2159f724f4f58879a5752..fa79a29c5fa00b78acb55e74506a517fca3f2980 100644 (file)
@@ -113,6 +113,9 @@ def getProfileInformation():
                        'SwapYZ': storedSetting("swap_yz"),
                        'Scale': storedSettingFloat("model_scale"),
                        'Rotate': storedSettingFloat("model_rotate_base"),
+                       'CenterX': storedSettingFloat("machine_center_x"),
+                       'CenterY': storedSettingFloat("machine_center_y"),
+                       'AlternativeCenterFile': storedSetting("alternative_center"),
                },'scale': {
                        'Activate_Scale': "False",
                        'XY_Plane_Scale_ratio': DEFSET,
@@ -171,7 +174,7 @@ def getProfileInformation():
                        'Surrounding_Angle_degrees': DEFSET,
                        'Thread_Sequence_Choice': storedSetting('sequence'),
                },'multiply': {
-                       'Activate_Multiply': "True",
+                       'Activate_Multiply': "False",
                        'Center_X_mm': storedSettingFloat("machine_center_x"),
                        'Center_Y_mm': storedSettingFloat("machine_center_y"),
                        'Number_of_Columns_integer': storedSetting('model_multiply_x'),
index ae949b3daacc020e76f2c9dc102b5927e79bf73d..b3ad845e30dead5232f62f30929227e106008507 100644 (file)
@@ -185,8 +185,11 @@ class CarveRepository:
                self.flipZ = settings.BooleanSetting().getFromValue('FlipZ', self, False)
                self.swapXZ = settings.BooleanSetting().getFromValue('SwapXZ', self, False)
                self.swapYZ = settings.BooleanSetting().getFromValue('SwapYZ', self, False)
+               self.centerX = settings.FloatSpin().getFromValue(0.0, 'CenterX', self, 1000.0, 0.0)
+               self.centerY = settings.FloatSpin().getFromValue(0.0, 'CenterY', self, 1000.0, 0.0)
                self.scale = settings.FloatSpin().getFromValue( 0.1, 'Scale', self, 10.0, 1.0 )
                self.rotate = settings.FloatSpin().getFromValue( -180.0, 'Rotate', self, 180.0, 0.0 )
+               self.alternativeCenter = settings.StringSetting().getFromValue('AlternativeCenterFile', self, '')
 
 
        def execute(self):
@@ -219,16 +222,6 @@ class CarveSkein:
                mat10 = math.sin(rotate) * scaleX
                mat11 = math.cos(rotate) * scaleY
 
-               minZ = carving.getMinimumZ()
-               minSize = carving.getCarveCornerMinimum()
-               maxSize = carving.getCarveCornerMaximum()
-               for v in carving.vertexes:
-                       v.z -= minZ
-                       v.x -= minSize.x + (maxSize.x - minSize.x) / 2
-                       v.y -= minSize.y + (maxSize.y - minSize.y) / 2
-                       #v.x += self.machineCenter.x
-                       #v.y += self.machineCenter.y
-               
                for i in xrange(0, len(carving.vertexes)):
                        x = carving.vertexes[i].x
                        y = carving.vertexes[i].y
@@ -242,6 +235,34 @@ class CarveSkein:
                                x * mat10 + y * mat11,
                                z * scaleZ)
 
+               if repository.alternativeCenter.value != '':
+                       carving2 = svg_writer.getCarving(repository.alternativeCenter.value)
+                       for i in xrange(0, len(carving2.vertexes)):
+                               x = carving2.vertexes[i].x
+                               y = carving2.vertexes[i].y
+                               z = carving2.vertexes[i].z
+                               if swapXZ:
+                                       x, z = z, x
+                               if swapYZ:
+                                       y, z = z, y
+                               carving2.vertexes[i] = Vector3(
+                                       x * mat00 + y * mat01,
+                                       x * mat10 + y * mat11,
+                                       z * scaleZ)
+                       minZ = carving2.getMinimumZ()
+                       minSize = carving2.getCarveCornerMinimum()
+                       maxSize = carving2.getCarveCornerMaximum()
+               else:
+                       minZ = carving.getMinimumZ()
+                       minSize = carving.getCarveCornerMinimum()
+                       maxSize = carving.getCarveCornerMaximum()
+               for v in carving.vertexes:
+                       v.z -= minZ
+                       v.x -= minSize.x + (maxSize.x - minSize.x) / 2
+                       v.y -= minSize.y + (maxSize.y - minSize.y) / 2
+                       v.x += repository.centerX.value
+                       v.y += repository.centerY.value
+
                layerHeight = repository.layerHeight.value
                edgeWidth = repository.edgeWidth.value
                carving.setCarveLayerHeight(layerHeight)
index 5a4f54916e4f3ee4f565cea450c6b103752c6d01..eb363a8cddd94e06fd872a9180b6954ff266faf5 100644 (file)
@@ -223,6 +223,8 @@ class CoolSkein:
 
        def addCoolTemperature(self, remainingOrbitTime):
                'Parse a gcode line and add it to the cool skein.'
+               if self.repository.minimumLayerTime.value < 0.0001:
+                       return
                layerCool = self.repository.maximumCool.value * remainingOrbitTime / self.repository.minimumLayerTime.value
                if self.isBridgeLayer:
                        layerCool = max(self.repository.bridgeCool.value, layerCool)
@@ -404,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.'
index 4a87d47134363009846d61b992ee7d624e7ab123..51f3549c5bc73eb9cfc1b54e8e2c2f343eb83801 100644 (file)
@@ -171,6 +171,8 @@ class SkirtSkein:
 
        def addSkirt(self, z):
                'At skirt at z to gcode output.'
+               if len(self.outsetLoops) < 1 or len(self.outsetLoops[0]) < 1:
+                       return
                self.setSkirtFeedFlowTemperature()
                self.distanceFeedRate.addLine('(<skirt>)')
                oldTemperature = self.oldTemperatureInput
index c102f19b1f502ca2797f362935c9f82410f36acf..5f5098d63bf57134355dd0833850e4b937640bae 100644 (file)
@@ -1,6 +1,7 @@
 import wx\r
 import sys,math,threading,os\r
 \r
+from gui import gcodeTextArea\r
 from util import profile\r
 \r
 class alterationPanel(wx.Panel):\r
@@ -10,8 +11,9 @@ class alterationPanel(wx.Panel):
                self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'nextobject.gcode', 'replace.csv']\r
                self.currentFile = None\r
 \r
-               self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB)\r
-               self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))\r
+               #self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB)\r
+               #self.textArea.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))\r
+               self.textArea = gcodeTextArea.GcodeTextArea(self)\r
                self.list = wx.ListBox(self, choices=self.alterationFileList, style=wx.LB_SINGLE)\r
                self.list.SetSelection(0)\r
                self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.list)\r
diff --git a/Cura/gui/gcodeTextArea.py b/Cura/gui/gcodeTextArea.py
new file mode 100644 (file)
index 0000000..68d0524
--- /dev/null
@@ -0,0 +1,103 @@
+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:#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)
+
index 3cd22ab273cb93c1cfd52a3f08bb11e789d230a2..34d2f9656fc2e4c4fdb2897b21f2ecf048ebb713 100644 (file)
@@ -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)
index f7331aadebe7f4014fc6ae8c9d4aaef60ed71b08..92ffcdea219f78d93dda69c959b14dcca3117c4b 100644 (file)
@@ -17,7 +17,9 @@ def InitGL(window, view3D, zoom):
        glViewport(0,0, size.GetWidth(), size.GetHeight())\r
        \r
        glLightfv(GL_LIGHT0, GL_POSITION, [1.0, 1.0, 1.0, 0.0])\r
+       glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])\r
 \r
+       glEnable(GL_NORMALIZE)\r
        glEnable(GL_LIGHTING)\r
        glEnable(GL_LIGHT0)\r
        glEnable(GL_DEPTH_TEST)\r
@@ -198,6 +200,7 @@ def DrawBox(vMin, vMax):
        glEnd()\r
 \r
 def DrawSTL(mesh):\r
+       glEnable(GL_CULL_FACE)\r
        for face in mesh.faces:\r
                glBegin(GL_TRIANGLES)\r
                v1 = face.v[0]\r
index ec3159792ee13b5f36129e125f95b7a79ea6b760..41bded92aa5a96de8e41a28ac4e5f13f841710bf 100644 (file)
@@ -7,6 +7,7 @@ import ConfigParser
 from gui import configBase\r
 from gui import validators\r
 from gui import machineCom\r
+from util import profile\r
 \r
 class preferencesDialog(configBase.configWindowBase):\r
        def __init__(self, parent):\r
@@ -14,6 +15,8 @@ class preferencesDialog(configBase.configWindowBase):
                \r
                wx.EVT_CLOSE(self, self.OnClose)\r
                \r
+               self.oldExtruderAmount = int(profile.getPreference('extruder_amount'))\r
+               \r
                left, right, main = self.CreateConfigPanel(self)\r
                configBase.TitleRow(left, 'Machine settings')\r
                c = configBase.SettingRow(left, 'Steps per E', 'steps_per_e', '0', 'Amount of steps per mm filament extrusion', type = 'preference')\r
@@ -25,6 +28,13 @@ class preferencesDialog(configBase.configWindowBase):
                c = configBase.SettingRow(left, 'Machine height (mm)', 'machine_height', '200', 'Size of the machine in mm', type = 'preference')\r
                validators.validFloat(c, 10.0)\r
                c = configBase.SettingRow(left, 'Extruder count', 'extruder_amount', ['1', '2', '3', '4'], 'Amount of extruders in your machine.', type = 'preference')\r
+               \r
+               for i in xrange(1, self.oldExtruderAmount):\r
+                       configBase.TitleRow(left, 'Extruder %d' % (i+1))\r
+                       c = configBase.SettingRow(left, 'Offset X', 'extruder_offset_x%d' % (i), '0.0', 'The offset of your secondary extruder compared to the primary.', type = 'preference')\r
+                       validators.validFloat(c)\r
+                       c = configBase.SettingRow(left, 'Offset Y', 'extruder_offset_y%d' % (i), '0.0', 'The offset of your secondary extruder compared to the primary.', type = 'preference')\r
+                       validators.validFloat(c)\r
 \r
                configBase.TitleRow(left, 'Filament settings')\r
                c = configBase.SettingRow(left, 'Filament density (kg/m3)', 'filament_density', '1300', 'Weight of the filament per m3. Around 1300 for PLA. And around 1040 for ABS. This value is used to estimate the weight if the filament used for the print.', type = 'preference')\r
@@ -42,10 +52,16 @@ class preferencesDialog(configBase.configWindowBase):
                #c = configBase.SettingRow(left, 'Slicer selection', 'slicer', ['Cura (Skeinforge based)', 'Slic3r'], 'Which slicer to use to slice objects. Usually the Cura engine produces the best results. But Slic3r is developing fast and is faster with slicing.', type = 'preference')\r
                c = configBase.SettingRow(left, 'Save profile on slice', 'save_profile', False, 'When slicing save the profile as [stl_file]_profile.ini next to the model.', type = 'preference')\r
                \r
+               self.okButton = wx.Button(left, -1, 'Ok')\r
+               left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))\r
+               self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)\r
+               \r
                self.MakeModal(True)\r
                main.Fit()\r
                self.Fit()\r
 \r
        def OnClose(self, e):\r
+               if self.oldExtruderAmount != int(profile.getPreference('extruder_amount')):\r
+                       wx.MessageBox('After changing the amount of extruders you need to restart Cura for full effect.', 'Extruder amount warning.', wx.OK | wx.ICON_INFORMATION)\r
                self.MakeModal(False)\r
                self.Destroy()\r
index ac8eff9728a4891bca1670e0a9f5086b08c6ac1b..526659366883f0614569d9b1c0ded54692da0762 100644 (file)
@@ -1,11 +1,6 @@
 from __future__ import division\r
 \r
-import sys\r
-import math\r
-import threading\r
-import re\r
-import time\r
-import os\r
+import sys, math, threading, re, time, os\r
 \r
 from wx import glcanvas\r
 import wx\r
@@ -43,11 +38,12 @@ class previewPanel(wx.Panel):
                \r
                self.glCanvas = PreviewGLCanvas(self)\r
                self.objectList = []\r
+               self.errorList = []\r
                self.gcode = None\r
                self.objectsMinV = None\r
                self.objectsMaxV = None\r
                self.loadThread = None\r
-               self.machineSize = util3d.Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height')))\r
+               self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))\r
                self.machineCenter = util3d.Vector3(float(profile.getProfileSetting('machine_center_x')), float(profile.getProfileSetting('machine_center_y')), 0)\r
                \r
                self.toolbar = toolbarUtil.Toolbar(self)\r
@@ -63,7 +59,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)\r
                self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)\r
                self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)\r
-               self.OnViewChange()\r
                self.toolbar.AddSeparator()\r
 \r
                self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)\r
@@ -93,22 +88,21 @@ class previewPanel(wx.Panel):
                self.toolbar2.AddSeparator()\r
 \r
                # Multiply\r
-               self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')\r
-               self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')\r
-               self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')\r
-               self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')\r
-\r
-               self.toolbar2.AddSeparator()\r
+               #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')\r
+               #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')\r
+               #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')\r
+               #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')\r
+               #self.toolbar2.AddSeparator()\r
 \r
                # Rotate\r
                self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')\r
                self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS)\r
                self.rotate.SetRange(0, 360)\r
-               self.Bind(wx.EVT_TEXT, self.OnRotate)\r
+               self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)\r
                self.toolbar2.AddControl(self.rotate)\r
 \r
                self.toolbar2.Realize()\r
-               self.updateToolbar()\r
+               self.OnViewChange()\r
                \r
                sizer = wx.BoxSizer(wx.VERTICAL)\r
                sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)\r
@@ -181,7 +175,6 @@ class previewPanel(wx.Panel):
                self.glCanvas.Refresh()\r
 \r
        def OnLayerNrChange(self, e):\r
-               self.gcodeDirty = True\r
                self.glCanvas.Refresh()\r
 \r
        def updateCenterX(self):\r
@@ -203,8 +196,9 @@ class previewPanel(wx.Panel):
        def loadModelFiles(self, filelist):\r
                while len(filelist) > len(self.objectList):\r
                        self.objectList.append(previewObject())\r
-               for idx in xrange(len(self.objectList), len(filelist)):\r
+               for idx in xrange(len(filelist), len(self.objectList)):\r
                        self.objectList[idx].mesh = None\r
+                       self.objectList[idx].filename = None\r
                for idx in xrange(0, len(filelist)):\r
                        obj = self.objectList[idx]\r
                        if obj.filename != filelist[idx]:\r
@@ -232,12 +226,11 @@ class previewPanel(wx.Panel):
        \r
        def doFileLoadThread(self):\r
                for obj in self.objectList:\r
-                       if os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:\r
+                       if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:\r
                                obj.ileTime = os.stat(obj.filename).st_mtime\r
                                mesh = stl.stlModel()\r
                                mesh.load(obj.filename)\r
                                obj.dirty = False\r
-                               obj.errorList = []\r
                                obj.mesh = mesh\r
                                self.updateModelTransform()\r
                                wx.CallAfter(self.updateToolbar)\r
@@ -272,9 +265,11 @@ class previewPanel(wx.Panel):
                pass\r
        \r
        def updateToolbar(self):\r
-               self.layerSpin.Show(self.gcode != None)\r
+               self.gcodeViewButton.Show(self.gcode != None)\r
+               self.mixedViewButton.Show(self.gcode != None)\r
+               self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")\r
                if self.gcode != None:\r
-                       self.layerSpin.SetRange(1, len(self.gcode.layerList))\r
+                       self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)\r
                self.toolbar.Realize()\r
        \r
        def OnViewChange(self):\r
@@ -288,6 +283,7 @@ class previewPanel(wx.Panel):
                        self.glCanvas.viewMode = "GCode"\r
                elif self.mixedViewButton.GetValue():\r
                        self.glCanvas.viewMode = "Mixed"\r
+               self.updateToolbar()\r
                self.glCanvas.Refresh()\r
        \r
        def updateModelTransform(self, f=0):\r
@@ -376,6 +372,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                self.offsetY = 0\r
                self.view3D = True\r
                self.gcodeDisplayList = None\r
+               self.gcodeDisplayListCount = 0\r
                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]]\r
        \r
        def OnMouseMotion(self,e):\r
@@ -424,8 +421,12 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                        glTranslate(0,0,-self.zoom)\r
                        glRotate(-self.pitch, 1,0,0)\r
                        glRotate(self.yaw, 0,0,1)\r
-                       if self.parent.objectsMaxV != None:\r
-                               glTranslate(0,0,-self.parent.objectsMaxV.z * profile.getProfileSettingFloat('model_scale') / 2)\r
+                       if self.viewMode == "GCode" or self.viewMode == "Mixed":\r
+                               if self.parent.gcode != None:\r
+                                       glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)\r
+                       else:\r
+                               if self.parent.objectsMaxV != None:\r
+                                       glTranslate(0,0,-self.parent.objectsMaxV.z * profile.getProfileSettingFloat('model_scale') / 2)\r
                else:\r
                        glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)\r
                        glTranslate(self.offsetX, self.offsetY, 0.0)\r
@@ -438,99 +439,113 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                machineSize = self.parent.machineSize\r
                opengl.DrawMachine(machineSize)\r
 \r
-               if self.parent.gcode != None:\r
-                       if self.gcodeDisplayList == None:\r
-                               self.gcodeDisplayList = glGenLists(1);\r
-                       if self.parent.gcodeDirty:\r
-                               self.parent.gcodeDirty = False\r
-                               glNewList(self.gcodeDisplayList, GL_COMPILE)\r
-                               prevLayerZ = 0.0\r
-                               curLayerZ = 0.0\r
-                               \r
-                               layerThickness = 0.0\r
-                               filamentRadius = float(profile.getProfileSetting('filament_diameter')) / 2\r
-                               filamentArea = math.pi * filamentRadius * filamentRadius\r
-                               lineWidth = float(profile.getProfileSetting('nozzle_size')) / 2 / 10\r
-                               \r
-                               curLayerNum = 0\r
-                               for layer in self.parent.gcode.layerList:\r
-                                       curLayerZ = layer[0].list[1].z\r
-                                       layerThickness = curLayerZ - prevLayerZ\r
-                                       prevLayerZ = layer[-1].list[-1].z\r
-                                       for path in layer:\r
-                                               c = 1.0\r
-                                               if curLayerNum != self.parent.layerSpin.GetValue():\r
-                                                       if curLayerNum < self.parent.layerSpin.GetValue():\r
-                                                               c = 0.9 - (self.parent.layerSpin.GetValue() - curLayerNum) * 0.1\r
-                                                               if c < 0.4:\r
-                                                                       c = 0.4\r
-                                                       else:\r
-                                                               break\r
-                                               if path.type == 'move':\r
-                                                       glColor3f(0,0,c)\r
-                                               if path.type == 'extrude':\r
-                                                       if path.pathType == 'FILL':\r
-                                                               glColor3f(c/2,c/2,0)\r
-                                                       elif path.pathType == 'WALL-INNER':\r
-                                                               glColor3f(0,c,0)\r
-                                                       elif path.pathType == 'SUPPORT':\r
-                                                               glColor3f(0,c,c)\r
-                                                       elif path.pathType == 'SKIRT':\r
-                                                               glColor3f(0,c/2,c/2)\r
-                                                       else:\r
-                                                               glColor3f(c,0,0)\r
-                                               if path.type == 'retract':\r
-                                                       glColor3f(0,c,c)\r
-                                               if c > 0.4 and path.type == 'extrude':\r
-                                                       for i in xrange(0, len(path.list)-1):\r
-                                                               v0 = path.list[i]\r
-                                                               v1 = path.list[i+1]\r
-\r
-                                                               # Calculate line width from ePerDistance (needs layer thickness and filament diameter)\r
-                                                               dist = (v0 - v1).vsize()\r
-                                                               if dist > 0 and layerThickness > 0:\r
-                                                                       extrusionMMperDist = (v1.e - v0.e) / dist\r
-                                                                       lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2\r
-\r
-                                                               normal = (v0 - v1).cross(util3d.Vector3(0,0,1))\r
-                                                               normal.normalize()\r
-                                                               v2 = v0 + normal * lineWidth\r
-                                                               v3 = v1 + normal * lineWidth\r
-                                                               v0 = v0 - normal * lineWidth\r
-                                                               v1 = v1 - normal * lineWidth\r
-\r
-                                                               glBegin(GL_QUADS)\r
-                                                               if path.pathType == 'FILL':     #Remove depth buffer fighting on infill/wall overlap\r
-                                                                       glVertex3f(v0.x, v0.y, v0.z - 0.02)\r
-                                                                       glVertex3f(v1.x, v1.y, v1.z - 0.02)\r
-                                                                       glVertex3f(v3.x, v3.y, v3.z - 0.02)\r
-                                                                       glVertex3f(v2.x, v2.y, v2.z - 0.02)\r
-                                                               else:\r
-                                                                       glVertex3f(v0.x, v0.y, v0.z - 0.01)\r
-                                                                       glVertex3f(v1.x, v1.y, v1.z - 0.01)\r
-                                                                       glVertex3f(v3.x, v3.y, v3.z - 0.01)\r
-                                                                       glVertex3f(v2.x, v2.y, v2.z - 0.01)\r
-                                                               glEnd()\r
-                                               \r
-                                                       #for v in path['list']:\r
-                                                       #       glBegin(GL_TRIANGLE_FAN)\r
-                                                       #       glVertex3f(v.x, v.y, v.z - 0.001)\r
-                                                       #       for i in xrange(0, 16+1):\r
-                                                       #               if path['pathType'] == 'FILL':  #Remove depth buffer fighting on infill/wall overlap\r
-                                                       #                       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)\r
-                                                       #               else:\r
-                                                       #                       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)\r
-                                                       #       glEnd()\r
+               if self.parent.gcode != None and self.parent.gcodeDirty:\r
+                       if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:\r
+                               if self.gcodeDisplayList != None:\r
+                                       glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)\r
+                               self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));\r
+                               self.gcodeDisplayListCount = len(self.parent.gcode.layerList)\r
+                       self.parent.gcodeDirty = False\r
+                       prevLayerZ = 0.0\r
+                       curLayerZ = 0.0\r
+                       \r
+                       layerThickness = 0.0\r
+                       filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2\r
+                       filamentArea = math.pi * filamentRadius * filamentRadius\r
+                       lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10\r
+                       \r
+                       curLayerNum = 0\r
+                       for layer in self.parent.gcode.layerList:\r
+                               glNewList(self.gcodeDisplayList + curLayerNum, GL_COMPILE)\r
+                               glDisable(GL_CULL_FACE)\r
+                               curLayerZ = layer[0].list[1].z\r
+                               layerThickness = curLayerZ - prevLayerZ\r
+                               prevLayerZ = layer[-1].list[-1].z\r
+                               for path in layer:\r
+                                       if path.type == 'move':\r
+                                               glColor3f(0,0,1)\r
+                                       if path.type == 'extrude':\r
+                                               if path.pathType == 'FILL':\r
+                                                       glColor3f(0.5,0.5,0)\r
+                                               elif path.pathType == 'WALL-INNER':\r
+                                                       glColor3f(0,1,0)\r
+                                               elif path.pathType == 'SUPPORT':\r
+                                                       glColor3f(0,1,1)\r
+                                               elif path.pathType == 'SKIRT':\r
+                                                       glColor3f(0,0.5,0.5)\r
                                                else:\r
-                                                       glBegin(GL_LINE_STRIP)\r
-                                                       for v in path.list:\r
-                                                               glVertex3f(v.x, v.y, v.z)\r
+                                                       glColor3f(1,0,0)\r
+                                       if path.type == 'retract':\r
+                                               glColor3f(0,1,1)\r
+                                       if path.type == 'extrude':\r
+                                               for i in xrange(0, len(path.list)-1):\r
+                                                       v0 = path.list[i]\r
+                                                       v1 = path.list[i+1]\r
+\r
+                                                       # Calculate line width from ePerDistance (needs layer thickness and filament diameter)\r
+                                                       dist = (v0 - v1).vsize()\r
+                                                       if dist > 0 and layerThickness > 0:\r
+                                                               extrusionMMperDist = (v1.e - v0.e) / dist\r
+                                                               lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2\r
+\r
+                                                       normal = (v0 - v1).cross(util3d.Vector3(0,0,1))\r
+                                                       normal.normalize()\r
+                                                       v2 = v0 + normal * lineWidth\r
+                                                       v3 = v1 + normal * lineWidth\r
+                                                       v0 = v0 - normal * lineWidth\r
+                                                       v1 = v1 - normal * lineWidth\r
+\r
+                                                       glBegin(GL_QUADS)\r
+                                                       if path.pathType == 'FILL':     #Remove depth buffer fighting on infill/wall overlap\r
+                                                               glVertex3f(v0.x, v0.y, v0.z - 0.02)\r
+                                                               glVertex3f(v1.x, v1.y, v1.z - 0.02)\r
+                                                               glVertex3f(v3.x, v3.y, v3.z - 0.02)\r
+                                                               glVertex3f(v2.x, v2.y, v2.z - 0.02)\r
+                                                       else:\r
+                                                               glVertex3f(v0.x, v0.y, v0.z - 0.01)\r
+                                                               glVertex3f(v1.x, v1.y, v1.z - 0.01)\r
+                                                               glVertex3f(v3.x, v3.y, v3.z - 0.01)\r
+                                                               glVertex3f(v2.x, v2.y, v2.z - 0.01)\r
                                                        glEnd()\r
-                                       curLayerNum += 1\r
+                                       \r
+                                               #for v in path['list']:\r
+                                               #       glBegin(GL_TRIANGLE_FAN)\r
+                                               #       glVertex3f(v.x, v.y, v.z - 0.001)\r
+                                               #       for i in xrange(0, 16+1):\r
+                                               #               if path['pathType'] == 'FILL':  #Remove depth buffer fighting on infill/wall overlap\r
+                                               #                       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)\r
+                                               #               else:\r
+                                               #                       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)\r
+                                               #       glEnd()\r
+                                       else:\r
+                                               glBegin(GL_LINE_STRIP)\r
+                                               for v in path.list:\r
+                                                       glVertex3f(v.x, v.y, v.z)\r
+                                               glEnd()\r
+                               curLayerNum += 1\r
+                               glEnable(GL_CULL_FACE)\r
                                glEndList()\r
-                       if self.viewMode == "GCode" or self.viewMode == "Mixed":\r
-                               glCallList(self.gcodeDisplayList)\r
                \r
+               if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):\r
+                       glEnable(GL_COLOR_MATERIAL)\r
+                       glEnable(GL_LIGHTING)\r
+                       for i in xrange(0, self.parent.layerSpin.GetValue() + 1):\r
+                               c = 1.0\r
+                               if i < self.parent.layerSpin.GetValue():\r
+                                       c = 0.9 - (self.parent.layerSpin.GetValue() - i) * 0.1\r
+                                       if c < 0.4:\r
+                                               c = (0.4 + c) / 2\r
+                                       if c < 0.1:\r
+                                               c = 0.1\r
+                               glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])\r
+                               glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])\r
+                               glCallList(self.gcodeDisplayList + i)\r
+                       glDisable(GL_LIGHTING)\r
+                       glDisable(GL_COLOR_MATERIAL)\r
+                       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);\r
+                       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);\r
+\r
+               glColor3f(1.0,1.0,1.0)\r
                glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)\r
                for obj in self.parent.objectList:\r
                        if obj.mesh == None:\r
@@ -543,17 +558,15 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                                opengl.DrawSTL(obj.mesh)\r
                                glEndList()\r
                        \r
-                       glEnable(GL_NORMALIZE)\r
                        if self.viewMode == "Transparent" or self.viewMode == "Mixed":\r
                                glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))\r
                                glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))\r
                                #If we want transparent, then first render a solid black model to remove the printer size lines.\r
                                if self.viewMode != "Mixed":\r
                                        glDisable(GL_BLEND)\r
-                                       glDisable(GL_LIGHTING)\r
-                                       glColor3f(0,0,0)\r
+                                       glColor3f(0.0,0.0,0.0)\r
                                        self.drawModel(obj)\r
-                                       glColor3f(1,1,1)\r
+                                       glColor3f(1.0,1.0,1.0)\r
                                #After the black model is rendered, render the model again but now with lighting and no depth testing.\r
                                glDisable(GL_DEPTH_TEST)\r
                                glEnable(GL_LIGHTING)\r
@@ -608,22 +621,22 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                                glEnable(GL_LIGHTING)\r
                                self.drawModel(obj)\r
                        \r
-                       if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":\r
-                               glDisable(GL_LIGHTING)\r
-                               glDisable(GL_DEPTH_TEST)\r
-                               glDisable(GL_BLEND)\r
-                               glColor3f(1,0,0)\r
-                               #glBegin(GL_LINES)\r
-                               #for err in self.parent.errorList:\r
-                               #       glVertex3f(err[0].x, err[0].y, err[0].z)\r
-                               #       glVertex3f(err[1].x, err[1].y, err[1].z)\r
-                               #glEnd()\r
-                               glEnable(GL_DEPTH_TEST)\r
+               if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":\r
+                       glDisable(GL_LIGHTING)\r
+                       glDisable(GL_DEPTH_TEST)\r
+                       glDisable(GL_BLEND)\r
+                       glColor3f(1,0,0)\r
+                       glBegin(GL_LINES)\r
+                       for err in self.parent.errorList:\r
+                               glVertex3f(err[0].x, err[0].y, err[0].z)\r
+                               glVertex3f(err[1].x, err[1].y, err[1].z)\r
+                       glEnd()\r
+                       glEnable(GL_DEPTH_TEST)\r
                glFlush()\r
        \r
        def drawModel(self, obj):\r
-               multiX = int(profile.getProfileSetting('model_multiply_x'))\r
-               multiY = int(profile.getProfileSetting('model_multiply_y'))\r
+               multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))\r
+               multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))\r
                modelScale = profile.getProfileSettingFloat('model_scale')\r
                modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale\r
                glPushMatrix()\r
index 195b5942575e56d999bcd3602fe0a4944974225a..5f68556d778726ebae66f77f57094ae12c7d7954 100644 (file)
@@ -176,14 +176,9 @@ class printWindow(wx.Frame):
                status = ""\r
                if self.gcode != None:\r
                        status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)\r
-                       cost_kg = float(profile.getPreference('filament_cost_kg'))\r
-                       cost_meter = float(profile.getPreference('filament_cost_meter'))\r
-                       if cost_kg > 0.0 and cost_meter > 0.0:\r
-                               status += "Filament cost: %.2f / %.2f\n" % (self.gcode.calculateWeight() * cost_kg, self.gcode.extrusionAmount / 1000 * cost_meter)\r
-                       elif cost_kg > 0.0:\r
-                               status += "Filament cost: %.2f\n" % (self.gcode.calculateWeight() * cost_kg)\r
-                       elif cost_meter > 0.0:\r
-                               status += "Filament cost: %.2f\n" % (self.gcode.extrusionAmount / 1000 * cost_meter)\r
+                       cost = self.gcode.calculateCost()\r
+                       if cost != False:\r
+                               status += "Filament cost: %s\n" % (cost)\r
                        status += "Print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))\r
                if self.printIdx == None:\r
                        self.progress.SetValue(0)\r
index 31162255926dff29b138a141d4ef40b4045c4d04..b24721e525eac793e1d62bb14bf781e3f1f36acf 100644 (file)
@@ -23,10 +23,110 @@ from util import profile
 from util import util3d\r
 from util import stl\r
 from util import sliceRun\r
+from util import gcodeInterpreter\r
 \r
-class Action():\r
+class Action(object):\r
        pass\r
 \r
+class ProjectObject(stl.stlModel):\r
+       def __init__(self, filename):\r
+               super(ProjectObject, self).__init__()\r
+\r
+               self.load(filename)\r
+\r
+               self.filename = filename\r
+               self.scale = 1.0\r
+               self.rotate = 0.0\r
+               self.flipX = False\r
+               self.flipY = False\r
+               self.flipZ = False\r
+               self.swapXZ = False\r
+               self.swapYZ = False\r
+               self.extruder = 0\r
+               \r
+               self.modelDisplayList = None\r
+               self.modelDirty = False\r
+\r
+               self.origonalVertexes = list(self.vertexes)\r
+               for i in xrange(0, len(self.origonalVertexes)):\r
+                       self.origonalVertexes[i] = self.origonalVertexes[i].copy()\r
+               self.getMinimumZ()\r
+               \r
+               self.centerX = -self.getMinimum().x + 5\r
+               self.centerY = -self.getMinimum().y + 5\r
+               \r
+               self.updateModelTransform()\r
+\r
+               self.centerX = -self.getMinimum().x + 5\r
+               self.centerY = -self.getMinimum().y + 5\r
+\r
+       def updateModelTransform(self):\r
+               rotate = self.rotate / 180.0 * math.pi\r
+               scaleX = 1.0\r
+               scaleY = 1.0\r
+               scaleZ = 1.0\r
+               if self.flipX:\r
+                       scaleX = -scaleX\r
+               if self.flipY:\r
+                       scaleY = -scaleY\r
+               if self.flipZ:\r
+                       scaleZ = -scaleZ\r
+               swapXZ = self.swapXZ\r
+               swapYZ = self.swapYZ\r
+               mat00 = math.cos(rotate) * scaleX\r
+               mat01 =-math.sin(rotate) * scaleY\r
+               mat10 = math.sin(rotate) * scaleX\r
+               mat11 = math.cos(rotate) * scaleY\r
+               \r
+               for i in xrange(0, len(self.origonalVertexes)):\r
+                       x = self.origonalVertexes[i].x\r
+                       y = self.origonalVertexes[i].y\r
+                       z = self.origonalVertexes[i].z\r
+                       if swapXZ:\r
+                               x, z = z, x\r
+                       if swapYZ:\r
+                               y, z = z, y\r
+                       self.vertexes[i].x = x * mat00 + y * mat01\r
+                       self.vertexes[i].y = x * mat10 + y * mat11\r
+                       self.vertexes[i].z = z * scaleZ\r
+\r
+               for face in self.faces:\r
+                       v1 = face.v[0]\r
+                       v2 = face.v[1]\r
+                       v3 = face.v[2]\r
+                       face.normal = (v2 - v1).cross(v3 - v1)\r
+                       face.normal.normalize()\r
+\r
+               minZ = self.getMinimumZ()\r
+               minV = self.getMinimum()\r
+               maxV = self.getMaximum()\r
+               for v in self.vertexes:\r
+                       v.z -= minZ\r
+                       v.x -= minV.x + (maxV.x - minV.x) / 2\r
+                       v.y -= minV.y + (maxV.y - minV.y) / 2\r
+               self.getMinimumZ()\r
+               self.modelDirty = True\r
+       \r
+       def clone(self):\r
+               p = ProjectObject(self.filename)\r
+\r
+               p.centerX = self.centerX + 5\r
+               p.centerY = self.centerY + 5\r
+               \r
+               p.filename = self.filename\r
+               p.scale = self.scale\r
+               p.rotate = self.rotate\r
+               p.flipX = self.flipX\r
+               p.flipY = self.flipY\r
+               p.flipZ = self.flipZ\r
+               p.swapXZ = self.swapXZ\r
+               p.swapYZ = self.swapYZ\r
+               p.extruder = self.extruder\r
+               \r
+               p.updateModelTransform()\r
+               \r
+               return p\r
+\r
 class projectPlanner(wx.Frame):\r
        "Main user interface window"\r
        def __init__(self):\r
@@ -39,9 +139,15 @@ class projectPlanner(wx.Frame):
                self.list = []\r
                self.selection = None\r
 \r
-               self.machineSize = util3d.Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height')))\r
-               self.headSizeMin = util3d.Vector3(70,16,0)\r
-               self.headSizeMax = util3d.Vector3(16,35,0)\r
+               self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))\r
+               self.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)\r
+               self.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)\r
+\r
+               self.extruderOffset = [\r
+                       util3d.Vector3(0,0,0),\r
+                       util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0),\r
+                       util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0),\r
+                       util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0)]\r
 \r
                self.toolbar = toolbarUtil.Toolbar(self)\r
 \r
@@ -55,6 +161,14 @@ class projectPlanner(wx.Frame):
                toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')\r
                \r
                self.toolbar.Realize()\r
+\r
+               self.toolbar2 = toolbarUtil.Toolbar(self)\r
+               toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')\r
+               toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')\r
+               toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')\r
+               toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')\r
+               toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')\r
+               self.toolbar2.Realize()\r
                \r
                sizer = wx.GridBagSizer(2,2)\r
                self.SetSizer(sizer)\r
@@ -65,7 +179,8 @@ class projectPlanner(wx.Frame):
                self.sliceButton = wx.Button(self, -1, "Slice")\r
                self.autoPlaceButton = wx.Button(self, -1, "Auto Place")\r
                \r
-               sizer.Add(self.toolbar, (0,0), span=(1,3), flag=wx.EXPAND)\r
+               sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND)\r
+               sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND)\r
                sizer.Add(self.preview, (1,0), span=(4,1), flag=wx.EXPAND)\r
                sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)\r
                sizer.Add(self.addButton, (2,1), span=(1,1))\r
@@ -95,6 +210,12 @@ class projectPlanner(wx.Frame):
                sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
                sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)\r
                sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
+               \r
+               if int(profile.getPreference('extruder_amount')) > 1:\r
+                       self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)\r
+                       sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)\r
+                       sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)\r
+                       self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)\r
 \r
                self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)\r
                self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)\r
@@ -126,6 +247,7 @@ class projectPlanner(wx.Frame):
                                cp.set(section, 'flipZ', str(item.flipZ))\r
                                cp.set(section, 'swapXZ', str(item.swapXZ))\r
                                cp.set(section, 'swapYZ', str(item.swapYZ))\r
+                               cp.set(section, 'extruder', str(item.extruder+1))\r
                                i += 1\r
                        cp.write(open(dlg.GetPath(), "w"))\r
                dlg.Destroy()\r
@@ -142,18 +264,19 @@ class projectPlanner(wx.Frame):
                        while cp.has_section('model_%d' % (i)):\r
                                section = 'model_%d' % (i)\r
                                \r
-                               item = stl.stlModel()\r
-                               item.filename = unicode(cp.get(section, 'filename'), "utf-8")\r
-                               self.loadModelFile(item)\r
+                               item = ProjectObject(unicode(cp.get(section, 'filename'), "utf-8"))\r
                                item.centerX = float(cp.get(section, 'centerX'))\r
                                item.centerY = float(cp.get(section, 'centerY'))\r
                                item.scale = float(cp.get(section, 'scale'))\r
                                item.rotate = float(cp.get(section, 'rotate'))\r
-                               cp.get(section, 'flipX')\r
-                               cp.get(section, 'flipY')\r
-                               cp.get(section, 'flipZ')\r
-                               cp.get(section, 'swapXZ')\r
-                               cp.get(section, 'swapYZ')\r
+                               item.flipX = cp.get(section, 'flipX') == 'True'\r
+                               item.flipY = cp.get(section, 'flipY') == 'True'\r
+                               item.flipZ = cp.get(section, 'flipZ') == 'True'\r
+                               item.swapXZ = cp.get(section, 'swapXZ') == 'True'\r
+                               item.swapYZ = cp.get(section, 'swapYZ') == 'True'\r
+                               if cp.has_option(section, 'extruder'):\r
+                                       item.extuder = int(cp.get(section, 'extruder'))-1\r
+                               item.updateModelTransform()\r
                                i += 1\r
                                \r
                                self.list.append(item)\r
@@ -161,6 +284,7 @@ class projectPlanner(wx.Frame):
                        \r
                        self.listbox.SetSelection(len(self.list)-1)\r
                        self.OnListSelect(None)\r
+                       self.preview.Refresh()\r
 \r
                dlg.Destroy()\r
 \r
@@ -184,39 +308,76 @@ class projectPlanner(wx.Frame):
                self.selection = self.list[self.listbox.GetSelection()]\r
                self.scaleCtrl.SetValue(str(self.selection.scale))\r
                self.rotateCtrl.SetValue(int(self.selection.rotate))\r
+               if int(profile.getPreference('extruder_amount')) > 1:\r
+                       self.extruderCtrl.SetValue(str(self.selection.extruder+1))\r
                self.preview.Refresh()\r
-\r
+       \r
        def OnAddModel(self, e):\r
                dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)\r
                dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")\r
                if dlg.ShowModal() == wx.ID_OK:\r
                        for filename in dlg.GetPaths():\r
-                               item = stl.stlModel()\r
-                               item.filename=filename\r
+                               item = ProjectObject(filename)\r
                                profile.putPreference('lastFile', item.filename)\r
-                               if not(os.path.exists(item.filename)):\r
-                                       return\r
-                               self.loadModelFile(item)\r
                                self.list.append(item)\r
-                               self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])\r
-                               self.listbox.SetSelection(len(self.list)-1)\r
-                               self.OnListSelect(None)\r
+                               self.selection = item\r
+                               self._updateListbox()\r
+               self.preview.Refresh()\r
                dlg.Destroy()\r
        \r
        def OnRemModel(self, e):\r
                if self.selection == None:\r
                        return\r
                self.list.remove(self.selection)\r
+               self._updateListbox()\r
+               self.preview.Refresh()\r
+       \r
+       def OnMoveUp(self, e):\r
+               if self.selection == None:\r
+                       return\r
                i = self.listbox.GetSelection()\r
-               self.listbox.Delete(i)\r
-               if len(self.list) > i:\r
-                       self.listbox.SetSelection(i)\r
-               elif len(self.list) > 0:\r
-                       self.listbox.SetSelection(len(self.list) - 1)\r
-               self.selection = None\r
-               self.OnListSelect(None)\r
+               if i == 0:\r
+                       return\r
+               self.list.remove(self.selection)\r
+               self.list.insert(i-1, self.selection)\r
+               self._updateListbox()\r
+               self.preview.Refresh()\r
+\r
+       def OnMoveDown(self, e):\r
+               if self.selection == None:\r
+                       return\r
+               i = self.listbox.GetSelection()\r
+               if i == len(self.list) - 1:\r
+                       return\r
+               self.list.remove(self.selection)\r
+               self.list.insert(i+1, self.selection)\r
+               self._updateListbox()\r
+               self.preview.Refresh()\r
+       \r
+       def OnCopy(self, e):\r
+               if self.selection == None:\r
+                       return\r
+               \r
+               item = self.selection.clone()\r
+               self.list.append(item)\r
+               self.selection = item\r
+               \r
+               self._updateListbox()\r
                self.preview.Refresh()\r
        \r
+       def _updateListbox(self):\r
+               self.listbox.Clear()\r
+               for item in self.list:\r
+                       self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])\r
+               if self.selection in self.list:\r
+                       self.listbox.SetSelection(self.list.index(self.selection))\r
+               elif len(self.list) > 0:\r
+                       self.selection = self.list[0]\r
+                       self.listbox.SetSelection(0)\r
+               else:\r
+                       self.selection = None\r
+                       self.listbox.SetSelection(-1)\r
+\r
        def OnAutoPlace(self, e):\r
                bestAllowedSize = int(self.machineSize.y)\r
                bestArea = self._doAutoPlace(bestAllowedSize)\r
@@ -233,7 +394,7 @@ class projectPlanner(wx.Frame):
                extraSizeMax = self.headSizeMax\r
                if profile.getProfileSettingFloat('skirt_line_count') > 0:\r
                        skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')\r
-                       extraSizeMin = extraSizeMin - util3d.Vector3(skirtSize, skirtSize, 0)\r
+                       extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)\r
                        extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)\r
 \r
                posX = self.machineSize.x\r
@@ -268,9 +429,7 @@ class projectPlanner(wx.Frame):
                return (maxX - minX) + (maxY - minY)\r
 \r
        def OnSlice(self, e):\r
-               oldProfile = profile.getGlobalProfileString()\r
-               \r
-               put = profile.putProfileSetting\r
+               put = profile.setTempOverride\r
 \r
                put('model_multiply_x', '1')\r
                put('model_multiply_y', '1')\r
@@ -281,8 +440,8 @@ class projectPlanner(wx.Frame):
                clearZ = 0\r
                actionList = []\r
                for item in self.list:\r
-                       put('machine_center_x', item.centerX)\r
-                       put('machine_center_y', item.centerY)\r
+                       put('machine_center_x', item.centerX - self.extruderOffset[item.extruder].x)\r
+                       put('machine_center_y', item.centerY - self.extruderOffset[item.extruder].y)\r
                        put('model_scale', item.scale)\r
                        put('flip_x', item.flipX)\r
                        put('flip_y', item.flipY)\r
@@ -295,13 +454,14 @@ class projectPlanner(wx.Frame):
                        action.sliceCmd = sliceRun.getSliceCommand(item.filename)\r
                        action.centerX = item.centerX\r
                        action.centerY = item.centerY\r
+                       action.extruder = item.extruder\r
                        action.filename = item.filename\r
                        clearZ = max(clearZ, item.getMaximum().z * item.scale)\r
                        action.clearZ = clearZ\r
                        actionList.append(action)\r
                \r
                #Restore the old profile.\r
-               profile.loadGlobalProfileFromString(oldProfile)\r
+               profile.resetTempOverride()\r
                \r
                dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)\r
                dlg.SetWildcard("GCode file (*.gcode)|*.gcode")\r
@@ -312,34 +472,10 @@ class projectPlanner(wx.Frame):
                dlg.Destroy()\r
                \r
                pspw = ProjectSliceProgressWindow(actionList, resultFilename)\r
+               pspw.extruderOffset = self.extruderOffset\r
                pspw.Centre()\r
                pspw.Show(True)\r
        \r
-       def loadModelFile(self, item):\r
-               item.load(item.filename)\r
-               item.origonalVertexes = list(item.vertexes)\r
-               for i in xrange(0, len(item.origonalVertexes)):\r
-                       item.origonalVertexes[i] = item.origonalVertexes[i].copy()\r
-               item.getMinimumZ()\r
-               \r
-               item.centerX = -item.getMinimum().x + 5\r
-               item.centerY = -item.getMinimum().y + 5\r
-               item.scale = 1.0\r
-               item.rotate = 0.0\r
-               item.flipX = False\r
-               item.flipY = False\r
-               item.flipZ = False\r
-               item.swapXZ = False\r
-               item.swapYZ = False\r
-               \r
-               item.modelDisplayList = None\r
-               item.modelDirty = False\r
-               \r
-               self.updateModelTransform(item)\r
-\r
-               item.centerX = -item.getMinimum().x + 5\r
-               item.centerY = -item.getMinimum().y + 5\r
-\r
        def OnScaleChange(self, e):\r
                if self.selection == None:\r
                        return\r
@@ -353,57 +489,13 @@ class projectPlanner(wx.Frame):
                if self.selection == None:\r
                        return\r
                self.selection.rotate = float(self.rotateCtrl.GetValue())\r
-               self.updateModelTransform(self.selection)\r
-\r
-       def updateModelTransform(self, item):\r
-               rotate = item.rotate / 180.0 * math.pi\r
-               scaleX = 1.0\r
-               scaleY = 1.0\r
-               scaleZ = 1.0\r
-               if item.flipX:\r
-                       scaleX = -scaleX\r
-               if item.flipY:\r
-                       scaleY = -scaleY\r
-               if item.flipZ:\r
-                       scaleZ = -scaleZ\r
-               swapXZ = item.swapXZ\r
-               swapYZ = item.swapYZ\r
-               mat00 = math.cos(rotate) * scaleX\r
-               mat01 =-math.sin(rotate) * scaleY\r
-               mat10 = math.sin(rotate) * scaleX\r
-               mat11 = math.cos(rotate) * scaleY\r
-               \r
-               for i in xrange(0, len(item.origonalVertexes)):\r
-                       x = item.origonalVertexes[i].x\r
-                       y = item.origonalVertexes[i].y\r
-                       z = item.origonalVertexes[i].z\r
-                       if swapXZ:\r
-                               x, z = z, x\r
-                       if swapYZ:\r
-                               y, z = z, y\r
-                       item.vertexes[i].x = x * mat00 + y * mat01\r
-                       item.vertexes[i].y = x * mat10 + y * mat11\r
-                       item.vertexes[i].z = z * scaleZ\r
-\r
-               for face in item.faces:\r
-                       v1 = face.v[0]\r
-                       v2 = face.v[1]\r
-                       v3 = face.v[2]\r
-                       face.normal = (v2 - v1).cross(v3 - v1)\r
-                       face.normal.normalize()\r
+               self.selection.updateModelTransform()\r
+               self.preview.Refresh()\r
 \r
-               self.moveModel(item)\r
-       \r
-       def moveModel(self, item):\r
-               minZ = item.getMinimumZ()\r
-               min = item.getMinimum()\r
-               max = item.getMaximum()\r
-               for v in item.vertexes:\r
-                       v.z -= minZ\r
-                       v.x -= min.x + (max.x - min.x) / 2\r
-                       v.y -= min.y + (max.y - min.y) / 2\r
-               item.getMinimumZ()\r
-               item.modelDirty = True\r
+       def OnExtruderChange(self, e):\r
+               if self.selection == None:\r
+                       return\r
+               self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1\r
                self.preview.Refresh()\r
 \r
 class PreviewGLCanvas(glcanvas.GLCanvas):\r
@@ -445,14 +537,14 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                                if item != None:\r
                                        item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2\r
                                        item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2\r
-                                       if item.centerX < -item.getMinimum().x * item.scale:\r
-                                               item.centerX = -item.getMinimum().x * item.scale\r
-                                       if item.centerY < -item.getMinimum().y * item.scale:\r
-                                               item.centerY = -item.getMinimum().y * item.scale\r
-                                       if item.centerX > self.parent.machineSize.x - item.getMaximum().x * item.scale:\r
-                                               item.centerX = self.parent.machineSize.x - item.getMaximum().x * item.scale\r
-                                       if item.centerY > self.parent.machineSize.y - item.getMaximum().y * item.scale:\r
-                                               item.centerY = self.parent.machineSize.y - item.getMaximum().y * item.scale\r
+                                       if item.centerX < -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x:\r
+                                               item.centerX = -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x\r
+                                       if item.centerY < -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y:\r
+                                               item.centerY = -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y\r
+                                       if item.centerX > self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale:\r
+                                               item.centerX = self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale\r
+                                       if item.centerY > self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale:\r
+                                               item.centerY = self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale\r
                        self.Refresh()\r
                else:\r
                        self.allowDrag = False\r
@@ -508,7 +600,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                extraSizeMax = self.parent.headSizeMax\r
                if profile.getProfileSettingFloat('skirt_line_count') > 0:\r
                        skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')\r
-                       extraSizeMin = extraSizeMin - util3d.Vector3(skirtSize, skirtSize, 0)\r
+                       extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)\r
                        extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)\r
 \r
                for item in self.parent.list:\r
@@ -517,8 +609,8 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                \r
                for idx1 in xrange(0, len(self.parent.list)):\r
                        item = self.parent.list[idx1]\r
-                       iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin\r
-                       iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax\r
+                       iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin - self.parent.extruderOffset[item.extruder]\r
+                       iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax - self.parent.extruderOffset[item.extruder]\r
                        for idx2 in xrange(0, idx1):\r
                                item2 = self.parent.list[idx2]\r
                                iMin2 = item2.getMinimum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)\r
@@ -566,8 +658,8 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                        \r
                        vMin = item.getMinimum() * item.scale\r
                        vMax = item.getMaximum() * item.scale\r
-                       vMinHead = vMin - extraSizeMin\r
-                       vMaxHead = vMax + extraSizeMax\r
+                       vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]\r
+                       vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]\r
 \r
                        glDisable(GL_LIGHTING)\r
 \r
@@ -612,28 +704,6 @@ class ProjectSliceProgressWindow(wx.Frame):
                self.startTime = time.time()\r
                self.sliceStartTime = time.time()\r
                \r
-               #How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.\r
-               # TODO: Duplicate with sliceProgressPanel, move to sliceRun.\r
-               self.sliceStepTimeFactor = {\r
-                       'start': 3.3713991642,\r
-                       'slice': 15.4984838963,\r
-                       'preface': 5.17178297043,\r
-                       'inset': 116.362634182,\r
-                       'fill': 215.702672005,\r
-                       'multiply': 21.9536788464,\r
-                       'speed': 12.759510994,\r
-                       'raft': 31.4580039978,\r
-                       'skirt': 19.3436040878,\r
-                       'skin': 1.0,\r
-                       'joris': 1.0,\r
-                       'comb': 23.7805759907,\r
-                       'cool': 27.148763895,\r
-                       'dimension': 90.4914340973\r
-               }\r
-               self.totalRunTimeFactor = 0\r
-               for v in self.sliceStepTimeFactor.itervalues():\r
-                       self.totalRunTimeFactor += v\r
-               \r
                self.sizer = wx.GridBagSizer(2, 2) \r
                self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))\r
                self.progressGauge = wx.Gauge(self, -1)\r
@@ -644,6 +714,7 @@ class ProjectSliceProgressWindow(wx.Frame):
                self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER)\r
                self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND)\r
                self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND)\r
+\r
                self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER)\r
                self.sizer.AddGrowableCol(0)\r
                self.sizer.AddGrowableRow(0)\r
@@ -664,19 +735,19 @@ class ProjectSliceProgressWindow(wx.Frame):
 \r
        def SetProgress(self, stepName, layer, maxLayer):\r
                if self.prevStep != stepName:\r
-                       self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]\r
+                       self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]\r
                        newTime = time.time()\r
                        #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName\r
                        self.startTime = newTime\r
                        self.prevStep = stepName\r
                \r
-               progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000\r
+               progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000\r
                self.progressGauge.SetValue(int(progresValue))\r
                self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")\r
        \r
        def OnRun(self):\r
                resultFile = open(self.resultFilename, "w")\r
-               put = profile.putProfileSetting\r
+               put = profile.setTempOverride\r
                for action in self.actionList:\r
                        p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\r
                        line = p.stdout.readline()\r
@@ -697,24 +768,27 @@ class ProjectSliceProgressWindow(wx.Frame):
                                if self.abort:\r
                                        p.terminate()\r
                                        wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")\r
+                                       resultFile.close()\r
                                        return\r
                                line = p.stdout.readline()\r
                        self.returnCode = p.wait()\r
                        \r
-                       oldProfile = profile.getGlobalProfileString()\r
-                       put('machine_center_x', action.centerX)\r
-                       put('machine_center_y', action.centerY)\r
+                       put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x)\r
+                       put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y)\r
                        put('clear_z', action.clearZ)\r
+                       put('extruder', action.extruder)\r
                        \r
                        if action == self.actionList[0]:\r
                                resultFile.write(';TYPE:CUSTOM\n')\r
+                               resultFile.write('T%d\n' % (action.extruder))\r
+                               currentExtruder = action.extruder\r
                                resultFile.write(profile.getAlterationFileContents('start.gcode'))\r
                        else:\r
                                #reset the extrusion length, and move to the next object center.\r
                                resultFile.write(';TYPE:CUSTOM\n')\r
                                resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))\r
                        resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))\r
-                       profile.loadGlobalProfileFromString(oldProfile)\r
+                       profile.resetTempOverride()\r
                        \r
                        f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r")\r
                        data = f.read(4096)\r
@@ -731,9 +805,20 @@ class ProjectSliceProgressWindow(wx.Frame):
                resultFile.write(';TYPE:CUSTOM\n')\r
                resultFile.write(profile.getAlterationFileContents('end.gcode'))\r
                resultFile.close()\r
+               \r
+               gcode = gcodeInterpreter.gcode()\r
+               gcode.load(self.resultFilename)\r
+               \r
                self.abort = True\r
                sliceTime = time.time() - self.sliceStartTime\r
-               wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %d:%d' % (sliceTime / 60, sliceTime % 60))\r
+               status = "Slicing took: %02d:%02d\n" % (sliceTime / 60, sliceTime % 60)\r
+               status = "Filament: %.2fm %.2fg\n" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)\r
+               status += "Print time: %02d:%02d\n" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))\r
+               cost = gcode.calculateCost()\r
+               if cost != False:\r
+                       status += "Cost: %s\n" % (cost)\r
+               wx.CallAfter(self.statusText.SetLabel, status)\r
+               \r
                wx.CallAfter(self.abortButton.SetLabel, 'Close')\r
 \r
 def main():\r
index f49f1e0a41fc8278b666f3aeb544ee512db7d499..4f38ca952368e3af304bff08fc45e8467603f8cb 100644 (file)
@@ -117,7 +117,7 @@ class simpleModeWindow(configBase.configWindowBase):
                self.sizer = sizer
 
                if self.filename != "None":
-                       self.preview3d.loadModelFile(self.filename)
+                       self.preview3d.loadModelFiles([self.filename])
                        self.lastPath = os.path.split(self.filename)[0]
 
                self.updateProfileToControls()
@@ -158,7 +158,7 @@ class simpleModeWindow(configBase.configWindowBase):
                        if not(os.path.exists(self.filename)):
                                return
                        self.lastPath = os.path.split(self.filename)[0]
-                       self.preview3d.loadModelFile(self.filename)
+                       self.preview3d.loadModelFiles([self.filename])
                        self.preview3d.setViewMode("Normal")
                dlg.Destroy()
        
index 0dba1fbc80c4eb95485ad08a9c4340c745b95c66..9e49e5b37bfda043cd51367fea15755aa99de720 100644 (file)
@@ -1,11 +1,12 @@
 from __future__ import absolute_import
 import __init__
 
-import wx, sys, os, math, threading, subprocess, time
+import wx, sys, os, math, threading, subprocess, time, re
 
 from util import profile
 from util import sliceRun
 from util import exporer
+from util import gcodeInterpreter
 
 class sliceProgessPanel(wx.Panel):
        def __init__(self, mainWindow, parent, filelist):
@@ -14,27 +15,6 @@ class sliceProgessPanel(wx.Panel):
                self.filelist = filelist
                self.abort = False
                
-               #How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
-               self.sliceStepTimeFactor = {
-                       'start': 3.3713991642,
-                       'slice': 15.4984838963,
-                       'preface': 5.17178297043,
-                       'inset': 116.362634182,
-                       'fill': 215.702672005,
-                       'multiply': 21.9536788464,
-                       'speed': 12.759510994,
-                       'raft': 31.4580039978,
-                       'skirt': 19.3436040878,
-                       'skin': 1.0,
-                       'joris': 1.0,
-                       'comb': 23.7805759907,
-                       'cool': 27.148763895,
-                       'dimension': 90.4914340973
-               }
-               self.totalRunTimeFactor = 0
-               for v in self.sliceStepTimeFactor.itervalues():
-                       self.totalRunTimeFactor += v
-
                box = wx.StaticBox(self, -1, filelist[0])
                self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
 
@@ -58,18 +38,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:
-                       print filename, self.filelist.index(filename)
-                       if self.filelist.index(filename) > 0:
-                               profile.putProfileSetting('fan_enabled', 'False')
-                               profile.putProfileSetting('skirt_line_count', '0')
-                               profile.putProfileSetting('machine_center_x', profile.getProfileSettingFloat('machine_center_x') + 22)
+                       idx = self.filelist.index(filename)
+                       print filename, idx
+                       if idx > 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):
@@ -98,7 +80,12 @@ class sliceProgessPanel(wx.Panel):
                self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
                self.sizer.Add(self.logButton, 0)
                if result.returnCode == 0:
-                       self.statusText.SetLabel("Ready.")
+                       status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
+                       status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
+                       cost = result.gcode.calculateCost()
+                       if cost != False:
+                               status += " Cost: %s" % (cost)
+                       self.statusText.SetLabel(status)
                        if exporer.hasExporer():
                                self.openFileLocationButton = wx.Button(self, -1, "Open file location")
                                self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
@@ -117,13 +104,13 @@ class sliceProgessPanel(wx.Panel):
        
        def SetProgress(self, stepName, layer, maxLayer):
                if self.prevStep != stepName:
-                       self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
+                       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 + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
+               progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
                self.progressGauge.SetValue(int(progresValue))
                self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
 
@@ -167,6 +154,8 @@ class WorkerThread(threading.Thread):
                if self.fileIdx == len(self.cmdList):
                        if len(self.filelist) > 1:
                                self._stitchMultiExtruder()
+                       self.gcode = gcodeInterpreter.gcode()
+                       self.gcode.load(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode')
                        wx.CallAfter(self.notifyWindow.OnSliceDone, self)
                else:
                        self.run()
@@ -177,10 +166,13 @@ 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))
+               resultFile.write('T%d\n' % (currentExtruder))
                layerNr = -1
                hasLine = True
                while hasLine:
@@ -191,14 +183,17 @@ class WorkerThread(threading.Thread):
                                        hasLine = True
                                        if line.startswith(';LAYER:'):
                                                break
+                                       if 'Z' in line:
+                                               lastZ = float(re.search('Z([^\s]+)', line).group(1))
                                        if not layerHasLine:
                                                nextExtruder = files.index(f)
                                                resultFile.write(';LAYER:%d\n' % (layerNr))
                                                resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
                                                if nextExtruder != currentExtruder:
-                                                       resultFile.write("G1 E-2 F3000\n")
+                                                       resultFile.write("G1 E-5 F5000\n")
+                                                       resultFile.write("G92 E0\n")
                                                        resultFile.write("T%d\n" % (nextExtruder))
-                                                       resultFile.write("G1 E2 F3000\n")
+                                                       resultFile.write("G1 E5 F5000\n")
                                                        resultFile.write("G92 E0\n")
                                                        currentExtruder = nextExtruder
                                                layerHasLine = True
index eba0a0b0aa064277ee102bc047d29e01c53c79cc..1ec088efae8920e0d061cac672e59a4524fc2700 100644 (file)
@@ -180,6 +180,7 @@ class NormalButton(buttons.GenBitmapButton):
                super(NormalButton, self).__init__(parent, id, self.bitmap, size=size)
 
                self.helpText = helpText
+               self.callback = callback
 
                self.SetBezelWidth(1)
                self.SetUseFocusIndicator(False)
@@ -187,10 +188,14 @@ class NormalButton(buttons.GenBitmapButton):
                self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
                self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
                
-               self.Bind(wx.EVT_BUTTON, callback)
+               self.Bind(wx.EVT_BUTTON, self.OnButton)
                
                parent.AddControl(self)
 
+       def OnButton(self, event):
+               self.GetParent().OnPopupHide(event)
+               self.callback(event)
+
        def OnMouseEnter(self, event):
                self.GetParent().OnPopupDisplay(event)
                event.Skip()
index b1da7783a363945f8383ecb8c0bb5bd6e9ebdb5a..f29e8d1eb9cfb8e065415dc5374577f5b60a2340 100644 (file)
@@ -11,7 +11,7 @@ SUCCESS = 0
 WARNING = 1
 ERROR   = 2
 
-class validFloat():
+class validFloat(object):
        def __init__(self, setting, minValue = None, maxValue = None):
                self.setting = setting
                self.setting.validators.append(self)
@@ -29,7 +29,7 @@ class validFloat():
                except (ValueError, SyntaxError):
                        return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid number or expression'
 
-class validInt():
+class validInt(object):
        def __init__(self, setting, minValue = None, maxValue = None):
                self.setting = setting
                self.setting.validators.append(self)
@@ -47,7 +47,7 @@ class validInt():
                except (ValueError, SyntaxError):
                        return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid whole number or expression'
 
-class warningAbove():
+class warningAbove(object):
        def __init__(self, setting, minValueForWarning, warningMessage):
                self.setting = setting
                self.setting.validators.append(self)
@@ -68,7 +68,7 @@ class warningAbove():
                        #We already have an error by the int/float validator in this case.
                        return SUCCESS, ''
 
-class wallThicknessValidator():
+class wallThicknessValidator(object):
        def __init__(self, setting):
                self.setting = setting
                self.setting.validators.append(self)
@@ -94,7 +94,7 @@ class wallThicknessValidator():
                        #We already have an error by the int/float validator in this case.
                        return SUCCESS, ''
 
-class printSpeedValidator():
+class printSpeedValidator(object):
        def __init__(self, setting):
                self.setting = setting
                self.setting.validators.append(self)
diff --git a/Cura/images/copy.png b/Cura/images/copy.png
new file mode 100644 (file)
index 0000000..5cdeb5f
Binary files /dev/null and b/Cura/images/copy.png differ
diff --git a/Cura/images/move-down.png b/Cura/images/move-down.png
new file mode 100644 (file)
index 0000000..5891219
Binary files /dev/null and b/Cura/images/move-down.png differ
diff --git a/Cura/images/move-up.png b/Cura/images/move-up.png
new file mode 100644 (file)
index 0000000..b3d9cef
Binary files /dev/null and b/Cura/images/move-up.png differ
diff --git a/Cura/images/object-add.png b/Cura/images/object-add.png
new file mode 100644 (file)
index 0000000..1b362da
Binary files /dev/null and b/Cura/images/object-add.png differ
diff --git a/Cura/images/object-remove.png b/Cura/images/object-remove.png
new file mode 100644 (file)
index 0000000..064e523
Binary files /dev/null and b/Cura/images/object-remove.png differ
index 0518be27316f8bf0fefa97c3a49593877a173c10..5714972cae3ff0472d63d7739e71e2f722baa842 100644 (file)
@@ -9,13 +9,13 @@ import os
 from util import util3d
 from util import profile
 
-class gcodePath():
+class gcodePath(object):
        def __init__(self, newType, pathType, startPoint):
                self.type = newType
                self.pathType = pathType
                self.list = [startPoint]
 
-class gcode():
+class gcode(object):
        def __init__(self):
                self.regMatch = {}
                self.layerList = []
@@ -36,7 +36,18 @@ class gcode():
                #Calculates the weight of the filament in kg
                radius = float(profile.getProfileSetting('filament_diameter')) / 2
                volumeM3 = (self.extrusionAmount * (math.pi * radius * radius)) / (1000*1000*1000)
-               return volumeM3 * float(profile.getPreference('filament_density'))
+               return volumeM3 * profile.getPreferenceFloat('filament_density')
+       
+       def calculateCost(self):
+               cost_kg = profile.getPreferenceFloat('filament_cost_kg')
+               cost_meter = profile.getPreferenceFloat('filament_cost_meter')
+               if cost_kg > 0.0 and cost_meter > 0.0:
+                       return "%.2f / %.2f" % (self.calculateWeight() * cost_kg, self.extrusionAmount / 1000 * cost_meter)
+               elif cost_kg > 0.0:
+                       return "%.2f" % (self.calculateWeight() * cost_kg)
+               elif cost_meter > 0.0:
+                       return "%.2f" % (self.extrusionAmount / 1000 * cost_meter)
+               return False
        
        def _load(self, gcodeFile):
                filePos = 0
@@ -45,6 +56,7 @@ class gcode():
                currentE = 0.0
                totalExtrusion = 0.0
                maxExtrusion = 0.0
+               currentExtruder = 0
                totalMoveTimeMinute = 0.0
                scale = 1.0
                posAbs = True
@@ -79,6 +91,15 @@ class gcode():
                                if pathType != "CUSTOM":
                                        startCodeDone = True
                                line = line[0:line.find(';')]
+                       T = self.getCodeInt(line, 'T')
+                       if T is not None:
+                               if currentExtruder > 0:
+                                       posOffset.x -= profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
+                                       posOffset.y -= profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
+                               currentExtruder = T
+                               if currentExtruder > 0:
+                                       posOffset.x += profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
+                                       posOffset.y += profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
                        
                        G = self.getCodeInt(line, 'G')
                        if G is not None:
@@ -91,17 +112,17 @@ class gcode():
                                        oldPos = pos.copy()
                                        if x is not None:
                                                if posAbs:
-                                                       pos.x = x * scale
+                                                       pos.x = x * scale + posOffset.x
                                                else:
                                                        pos.x += x * scale
                                        if y is not None:
                                                if posAbs:
-                                                       pos.y = y * scale
+                                                       pos.y = y * scale + posOffset.y
                                                else:
                                                        pos.y += y * scale
                                        if z is not None:
                                                if posAbs:
-                                                       pos.z = z * scale
+                                                       pos.z = z * scale + posOffset.z
                                                else:
                                                        pos.z += z * scale
                                                #Check if we have a new layer.
@@ -200,8 +221,8 @@ class gcode():
                self.layerList.append(currentLayer)
                self.extrusionAmount = maxExtrusion
                self.totalMoveTimeMinute = totalMoveTimeMinute
-               print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
-               print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
+               #print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
+               #print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
 
        def getCodeInt(self, line, code):
                if code not in self.regMatch:
index b6fb3ac75d6b49f9c217af85b6a142f74d91c2a9..5e6f67ef7b8d2b8705b9fb850b7dd3f399c434a0 100644 (file)
@@ -34,7 +34,7 @@ profileDefaultSettings = {
        'bottom_layer_speed': '20',\r
        'cool_min_layer_time': '10',\r
        'fan_enabled': 'True',\r
-       'fan_layer': '0',\r
+       'fan_layer': '1',\r
        'fan_speed': '100',\r
        'model_scale': '1.0',\r
        'flip_x': 'False',\r
@@ -67,6 +67,9 @@ profileDefaultSettings = {
        \r
        'add_start_end_gcode': 'True',\r
        'gcode_extension': 'gcode',\r
+       'alternative_center': '',\r
+       'clear_z': '0.0',\r
+       'extruder': '0',\r
 }\r
 alterationDefault = {\r
 #######################################################################################\r
@@ -128,6 +131,12 @@ preferencesDefaultSettings = {
        'machine_depth': '205',\r
        'machine_height': '200',\r
        'extruder_amount': '1',\r
+       'extruder_offset_x1': '-22.0',\r
+       'extruder_offset_y1': '0.0',\r
+       'extruder_offset_x2': '0.0',\r
+       'extruder_offset_y2': '0.0',\r
+       'extruder_offset_x3': '0.0',\r
+       'extruder_offset_y3': '0.0',\r
        'filament_density': '1300',\r
        'steps_per_e': '0',\r
        'serial_port': 'AUTO',\r
@@ -136,12 +145,18 @@ preferencesDefaultSettings = {
        'save_profile': 'False',\r
        'filament_cost_kg': '0',\r
        'filament_cost_meter': '0',\r
+       \r
+       'extruder_head_size_min_x': '70.0',\r
+       'extruder_head_size_min_y': '18.0',\r
+       'extruder_head_size_max_x': '18.0',\r
+       'extruder_head_size_max_y': '35.0',\r
 }\r
 \r
 #########################################################\r
 ## Profile and preferences functions\r
 #########################################################\r
 \r
+## Profile functions\r
 def getDefaultProfilePath():\r
        return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini"))\r
 \r
@@ -177,15 +192,31 @@ def getGlobalProfileString():
        \r
        p = []\r
        alt = []\r
-       for key in globalProfileParser.options('profile'):\r
-               p.append(key + "=" + globalProfileParser.get('profile', key))\r
-       for key in globalProfileParser.options('alterations'):\r
-               alt.append(key + "=" + globalProfileParser.get('alterations', key))\r
+       tempDone = []\r
+       if globalProfileParser.has_section('profile'):\r
+               for key in globalProfileParser.options('profile'):\r
+                       if key in tempOverride:\r
+                               p.append(key + "=" + unicode(tempOverride[key]))\r
+                               tempDone.append(key)\r
+                       else:\r
+                               p.append(key + "=" + globalProfileParser.get('profile', key))\r
+       if globalProfileParser.has_section('alterations'):\r
+               for key in globalProfileParser.options('alterations'):\r
+                       if key in tempOverride:\r
+                               p.append(key + "=" + tempOverride[key])\r
+                               tempDone.append(key)\r
+                       else:\r
+                               alt.append(key + "=" + globalProfileParser.get('alterations', key))\r
+       for key in tempOverride:\r
+               if key not in tempDone:\r
+                       p.append(key + "=" + unicode(tempOverride[key]))\r
        ret = '\b'.join(p) + '\f' + '\b'.join(alt)\r
        ret = base64.b64encode(zlib.compress(ret, 9))\r
        return ret\r
 \r
 def getProfileSetting(name):\r
+       if name in tempOverride:\r
+               return unicode(tempOverride[name])\r
        #Check if we have a configuration file loaded, else load the default.\r
        if not globals().has_key('globalProfileParser'):\r
                loadGlobalProfile(getDefaultProfilePath())\r
@@ -217,13 +248,27 @@ def putProfileSetting(name, value):
                globalProfileParser.add_section('profile')\r
        globalProfileParser.set('profile', name, str(value))\r
 \r
+def isProfileSetting(name):\r
+       if name in profileDefaultSettings:\r
+               return True\r
+       return False\r
+\r
+## Preferences functions\r
 global globalPreferenceParser\r
 globalPreferenceParser = None\r
 \r
 def getPreferencePath():\r
        return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini"))\r
 \r
+def getPreferenceFloat(name):\r
+       try:\r
+               return float(eval(getPreference(name), {}, {}))\r
+       except (ValueError, SyntaxError):\r
+               return 0.0\r
+\r
 def getPreference(name):\r
+       if name in tempOverride:\r
+               return unicode(tempOverride[name])\r
        global globalPreferenceParser\r
        if globalPreferenceParser == None:\r
                globalPreferenceParser = ConfigParser.ConfigParser()\r
@@ -253,6 +298,18 @@ def putPreference(name, value):
        globalPreferenceParser.set('preference', name, unicode(value).encode("utf-8"))\r
        globalPreferenceParser.write(open(getPreferencePath(), 'w'))\r
 \r
+def isPreference(name):\r
+       if name in preferencesDefaultSettings:\r
+               return True\r
+       return False\r
+\r
+## Temp overrides for multi-extruder slicing and the project planner.\r
+tempOverride = {}\r
+def setTempOverride(name, value):\r
+       tempOverride[name] = value\r
+def resetTempOverride():\r
+       tempOverride.clear()\r
+\r
 #########################################################\r
 ## Utility functions to calculate common profile values\r
 #########################################################\r
@@ -296,7 +353,11 @@ def replaceTagMatch(m):
        tag = m.group(0)[1:-1]\r
        if tag in ['print_speed', 'retraction_speed', 'travel_speed', 'max_z_speed', 'bottom_layer_speed', 'cool_min_feedrate']:\r
                return str(getProfileSettingFloat(tag) * 60)\r
-       return str(getProfileSettingFloat(tag))\r
+       if isProfileSetting(tag):\r
+               return str(getProfileSettingFloat(tag))\r
+       if isPreference(tag):\r
+               return str(getProfileSettingFloat(tag))\r
+       return tag\r
 \r
 ### Get aleration raw contents. (Used internally in Cura)\r
 def getAlterationFile(filename):\r
@@ -333,7 +394,7 @@ def getAlterationFileContents(filename):
        if filename == 'start.gcode':\r
                #For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion.\r
                #We also set our steps per E here, if configured.\r
-               eSteps = float(getPreference('steps_per_e'))\r
+               eSteps = getPreferenceFloat('steps_per_e')\r
                if eSteps > 0:\r
                        prefix += 'M92 E%f\n' % (eSteps)\r
                temp = getProfileSettingFloat('print_temperature')\r
index edc304d317c553c0dbbfe58f86fb53ef84319982..4bc302a04f443f9fd2f8686f76af50c59d38853d 100644 (file)
@@ -5,6 +5,28 @@ import platform, os, subprocess, sys
 from cura_sf.skeinforge_application.skeinforge_utilities import skeinforge_craft
 from util import profile
 
+#How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
+sliceStepTimeFactor = {
+       'start': 3.3713991642,
+       'slice': 15.4984838963,
+       'preface': 5.17178297043,
+       'inset': 116.362634182,
+       'fill': 215.702672005,
+       'multiply': 21.9536788464,
+       'speed': 12.759510994,
+       'raft': 31.4580039978,
+       'skirt': 19.3436040878,
+       'skin': 1.0,
+       'joris': 1.0,
+       'comb': 23.7805759907,
+       'cool': 27.148763895,
+       'dimension': 90.4914340973
+}
+
+totalRunTimeFactor = 0
+for v in sliceStepTimeFactor.itervalues():
+       totalRunTimeFactor += v
+
 def getPyPyExe():
        "Return the path to the pypy executable if we can find it. Else return False"
        if platform.system() == "Windows":
index 9b1323a294237880f6e61ca31d1f95b4d2ba76ea..bf7558b4974d0945ba81abffb59f20e3124279a1 100644 (file)
@@ -9,11 +9,11 @@ import struct
 
 from util import util3d
 
-class stlFace():
+class stlFace(object):
        def __init__(self, v0, v1, v2):
                self.v = [v0, v1, v2]
 
-class stlModel():
+class stlModel(object):
        def __init__(self):
                self.faces = []
                self.vertexes = []
index 30b25159d6221d1bfd3770d25c69855cc948179a..d5411fc9c493e623ef82689242e591b422f96953 100644 (file)
@@ -1,7 +1,7 @@
 
 import math
 
-class Vector3():
+class Vector3(object):
        def __init__(self, x=0.0, y=0.0, z=0.0):
                self.x = x
                self.y = y