chiark / gitweb /
Add some more documentation. And the zhop feature.
authordaid <daid303@gmail.com>
Wed, 5 Feb 2014 12:15:27 +0000 (13:15 +0100)
committerdaid <daid303@gmail.com>
Wed, 5 Feb 2014 12:15:27 +0000 (13:15 +0100)
18 files changed:
Cura/avr_isp/ispBase.py
Cura/avr_isp/stk500v2.py
Cura/doctest.py
Cura/gui/printWindow2.py
Cura/gui/tools/imageToMesh.py
Cura/gui/tools/minecraftImport.py
Cura/util/gcodeGenerator.py
Cura/util/gcodeInterpreter.py
Cura/util/machineCom.py
Cura/util/meshLoader.py
Cura/util/meshLoaders/amf.py
Cura/util/meshLoaders/dae.py
Cura/util/meshLoaders/obj.py
Cura/util/meshLoaders/stl.py
Cura/util/objectScene.py
Cura/util/printableObject.py [moved from Cura/util/mesh.py with 98% similarity]
Cura/util/profile.py
Cura/util/sliceEngine.py

index adc5ca9666f42c97654dfb1894d656706cd3b206..ffee7285ad1d9eefd2f88d106129b0747dad98de 100644 (file)
@@ -1,15 +1,24 @@
-__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
-import os, struct, sys, time
+"""
+General interface for Isp based AVR programmers.
+The ISP AVR programmer can load firmware into AVR chips. Which are commonly used on 3D printers.
 
-from serial import Serial
+ Needs to be subclassed to support different programmers.
+ Currently only the stk500v2 subclass exists.
+"""
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import chipDB
 
 class IspBase():
+       """
+       Base class for ISP based AVR programmers.
+       Functions in this class raise an IspError when something goes wrong.
+       """
        def programChip(self, flashData):
+               """ Program a chip with the given flash data. """
                self.curExtAddr = -1
                self.chip = chipDB.getChipFromDB(self.getSignature())
-               if self.chip == False:
+               if not self.chip:
                        raise IspError("Chip with signature: " + str(self.getSignature()) + "not found")
                self.chipErase()
                
@@ -18,8 +27,11 @@ class IspBase():
                print("Verifying %i bytes" % len(flashData))
                self.verifyFlash(flashData)
 
-       #low level ISP commands
        def getSignature(self):
+               """
+               Get the AVR signature from the chip. This is a 3 byte array which describes which chip we are connected to.
+               This is important to verify that we are programming the correct type of chip and that we use proper flash block sizes.
+               """
                sig = []
                sig.append(self.sendISP([0x30, 0x00, 0x00, 0x00])[3])
                sig.append(self.sendISP([0x30, 0x00, 0x01, 0x00])[3])
@@ -27,8 +39,23 @@ class IspBase():
                return sig
        
        def chipErase(self):
+               """
+               Do a full chip erase, clears all data, and lockbits.
+               """
                self.sendISP([0xAC, 0x80, 0x00, 0x00])
 
+       def writeFlash(self, flashData):
+               """
+               Write the flash data, needs to be implemented in a subclass.
+               """
+               raise IspError("Called undefined writeFlash")
+
+       def verifyFlash(self, flashData):
+               """
+               Verify the flash data, needs to be implemented in a subclass.
+               """
+               raise IspError("Called undefined verifyFlash")
+
 class IspError():
        def __init__(self, value):
                self.value = value
index 8bacdab9f1d812a8ece2f73229af1b7b7b8eb068..975dd30b933d8d89630bdd9dbe8ac078dd39afff 100644 (file)
@@ -1,3 +1,7 @@
+"""
+STK500v2 protocol implementation for programming AVR chips.
+The STK500v2 protocol is used by the ArduinoMega2560 and a few other Arduino platforms to load firmware.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 import os, struct, sys, time
 
index 3640367e675612158da504f1feecba44d53448d1..e74d76d6fb4f855b70de18a61cd3388b89182a86 100644 (file)
@@ -13,6 +13,8 @@ import random
 def treeWalk(moduleList, dirname, fnames):
        """ Callback from the os.path.walk function, see if the given path is a module and import it to put it in the moduleList """
        dirname = dirname.replace("\\", ".").replace("/", ".")
+       if dirname.startswith('Cura.gui'):
+               return
        if dirname == 'Cura.util.pymclevel':
                return
        if dirname == 'Cura.util.Power':
index de51e3fdc250043466339d6ea164d4debd7cb22e..57c622846323e498035674673fca443a4cfcad70 100644 (file)
@@ -23,7 +23,7 @@ class printWindow(wx.Frame):
                self.sizer = wx.GridBagSizer(2, 2)
                self.panel.SetSizer(self.sizer)
 
-               sb = wx.StaticBox(self.panel, label=_("Statistics"))
+               sb = wx.StaticBox(self.panel, label=_("Info"))
                boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
 
                self.powerWarningText = wx.StaticText(parent=self.panel,
@@ -176,6 +176,9 @@ class printWindow(wx.Frame):
 
                self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
 
+               if self._printerConnection.hasActiveConnection() and not self._printerConnection.isActiveConnectionOpen():
+                       self._printerConnection.openActiveConnection()
+
        def OnPowerWarningChange(self, e):
                type = self.powerManagement.get_providing_power_source_type()
                if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
index 9825d3944ecca833769af7c34c982792c1063007..f26f90c5b270b18537c84e7086d17b7c53ca980f 100644 (file)
@@ -3,7 +3,7 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AG
 import wx
 import numpy
 
-from Cura.util import mesh
+from Cura.util import printableObject
 
 def supportedExtensions():
        return ['.bmp', '.jpg', '.jpeg', '.png']
@@ -128,7 +128,7 @@ def convertImage(filename, height=20.0, width=100.0, blur=0, invert=False, baseH
        v3 = v0[1:,0:-1,:]
        v4 = v0[1:,1:,:]
 
-       obj = mesh.printableObject(filename)
+       obj = printableObject.printableObject(filename)
        m = obj._addMesh()
        m._prepareFaceCount((w-1) * (h-1) * 2 + 2 + (w-1)*4 + (h-1)*4)
        m.vertexes = numpy.array(numpy.concatenate((v1,v3,v2,v2,v3,v4), 2).reshape(((w-1) * (h-1) * 6, 3)), numpy.float32, copy=False)
index 16b86aa45d2b05406e3f47d40484f1fc1296c30a..e50799960886dee16de6a3b4c6eb00bd7684b7c8 100644 (file)
@@ -9,7 +9,7 @@ import glob
 import os
 import numpy
 
-from Cura.util import mesh
+from Cura.util import printableObject
 from Cura.util.meshLoaders import stl
 from Cura.util.pymclevel import mclevel
 
@@ -216,7 +216,7 @@ class minecraftImportWindow(wx.Frame):
                                                if y == sy - 1 or not self.isSolid[blocks[x, y + 1, z]]:
                                                        faceCount += 1
 
-               obj = mesh.printableObject(None)
+               obj = printableObject.printableObject(None)
                m = obj._addMesh()
                m._prepareFaceCount(faceCount * 2)
                for x in xrange(0, sx):
index 2d32f31e13d5fd8515ede90d19bd4ce3712fd1fd..cb6ed709761abd2a18333a431fed14a6b77a3d2e 100644 (file)
@@ -1,3 +1,7 @@
+"""
+A simple generator for GCode. To assist in creation of simple GCode instructions.
+This is not intended for advanced use or complex paths. The CuraEngine generates the real GCode instructions.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import math
index 50b2be34596acb34cf57478b738f5bdcdfaec587..4bf13a7a281427b7238661fda3b1706cfc79ab76 100644 (file)
@@ -1,3 +1,7 @@
+"""
+The GCodeInterpreter module generates layer information from GCode.
+It does this by parsing the whole GCode file. On large files this can take a while and should be used from a thread.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import sys
@@ -85,16 +89,18 @@ class gcode(object):
                                pathType = line[6:].strip()
 
                        if ';' in line:
-                               #Slic3r GCode comment parser
                                comment = line[line.find(';')+1:].strip()
+                               #Slic3r GCode comment parser
                                if comment == 'fill':
                                        pathType = 'FILL'
                                elif comment == 'perimeter':
                                        pathType = 'WALL-INNER'
                                elif comment == 'skirt':
                                        pathType = 'SKIRT'
+                               #Cura layer comments.
                                if comment.startswith('LAYER:'):
                                        currentPath = gcodePath(moveType, pathType, layerThickness, currentPath['points'][-1])
+                                       layerThickness = 0.0
                                        currentPath['extruder'] = currentExtruder
                                        for path in currentLayer:
                                                path['points'] = numpy.array(path['points'], numpy.float32)
@@ -155,7 +161,8 @@ class gcode(object):
                                        if moveType == 'move' and oldPos[2] != pos[2]:
                                                if oldPos[2] > pos[2] and abs(oldPos[2] - pos[2]) > 5.0 and pos[2] < 1.0:
                                                        oldPos[2] = 0.0
-                                               layerThickness = abs(oldPos[2] - pos[2])
+                                               if layerThickness == 0.0:
+                                                       layerThickness = abs(oldPos[2] - pos[2])
                                        if currentPath['type'] != moveType or currentPath['pathType'] != pathType:
                                                currentPath = gcodePath(moveType, pathType, layerThickness, currentPath['points'][-1])
                                                currentPath['extruder'] = currentExtruder
index 3be02e684c743e55fe6f7b170b6920ce3245a70d..ec372df7e440bcebe905a2b08b99c496ff23fa03 100644 (file)
@@ -1,3 +1,7 @@
+"""
+MachineCom handles communication with GCode based printers trough serial ports.
+For actual printing of objects this module is used from Cura.serialCommunication and ran in a seperate process.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import os
index b37c66e88c44825fbc1c4cf4330922e15491d3e2..cbe100672af05689127ee8e448878d6bccfcce3f 100644 (file)
@@ -1,3 +1,7 @@
+"""
+The meshLoader module contains a universal interface for loading 3D files.
+Depending on the file extension the proper meshLoader is called to load the file.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import os
@@ -8,9 +12,11 @@ from Cura.util.meshLoaders import dae
 from Cura.util.meshLoaders import amf
 
 def loadSupportedExtensions():
+       """ return a list of supported file extensions for loading. """
        return ['.stl', '.obj', '.dae', '.amf']
 
 def saveSupportedExtensions():
+       """ return a list of supported file extensions for saving. """
        return ['.amf', '.stl']
 
 def loadWildcardFilter():
@@ -21,13 +27,14 @@ def saveWildcardFilter():
        wildcardList = ';'.join(map(lambda s: '*' + s, saveSupportedExtensions()))
        return "Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
 
-#loadMeshes loads 1 or more printableObjects from a file.
-# STL files are a single printableObject with a single mesh, these are most common.
-# OBJ files usually contain a single mesh, but they can contain multiple meshes
-# AMF can contain whole scenes of objects with each object having multiple meshes.
-# DAE files are a mess, but they can contain scenes of objects as well as grouped meshes
-
 def loadMeshes(filename):
+       """
+       loadMeshes loads 1 or more printableObjects from a file.
+       STL files are a single printableObject with a single mesh, these are most common.
+       OBJ files usually contain a single mesh, but they can contain multiple meshes
+       AMF can contain whole scenes of objects with each object having multiple meshes.
+       DAE files are a mess, but they can contain scenes of objects as well as grouped meshes
+       """
        ext = os.path.splitext(filename)[1].lower()
        if ext == '.stl':
                return stl.loadScene(filename)
@@ -41,6 +48,9 @@ def loadMeshes(filename):
        return []
 
 def saveMeshes(filename, objects):
+       """
+       Save a list of objects into the file given by the filename. Use the filename extension to find out the file format.
+       """
        ext = os.path.splitext(filename)[1].lower()
        if ext == '.stl':
                stl.saveScene(filename, objects)
index 848f8bd03a8bd23fe0a4433bbe49e8d709535839..69d999cf642852a1aa6c0d3be87110e972c19cb5 100644 (file)
@@ -15,7 +15,7 @@ try:
 except:
        from xml.etree import ElementTree
 
-from Cura.util import mesh
+from Cura.util import printableObject
 from Cura.util import profile
 
 def loadScene(filename):
@@ -48,7 +48,7 @@ def loadScene(filename):
 
        ret = []
        for amfObj in amf.iter('object'):
-               obj = mesh.printableObject(filename)
+               obj = printableObject.printableObject(filename)
                for amfMesh in amfObj.iter('mesh'):
                        vertexList = []
                        for vertices in amfMesh.iter('vertices'):
index f173ddf10f768ca113737e49670c25c57eb60421..9dd0e91b8a431af5c29ea863b334c588059607be 100644 (file)
@@ -10,7 +10,7 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AG
 from  xml.parsers.expat import ParserCreate
 import os
 
-from Cura.util import mesh
+from Cura.util import printableObject
 
 def loadScene(filename):
        loader = daeLoader(filename)
@@ -18,7 +18,7 @@ def loadScene(filename):
 
 class daeLoader(object):
        def __init__(self, filename):
-               self.obj = mesh.printableObject(filename)
+               self.obj = printableObject.printableObject(filename)
                self.mesh = self.obj._addMesh()
 
                r = ParserCreate()
index 8c1fc083335b9e3b270e476624e9613c17ab0a9b..c347b6cc5094e959e1f60f69e798539cd93af7d0 100644 (file)
@@ -8,10 +8,10 @@ http://en.wikipedia.org/wiki/Wavefront_.obj_file
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import os
-from Cura.util import mesh
+from Cura.util import printableObject
 
 def loadScene(filename):
-       obj = mesh.printableObject(filename)
+       obj = printableObject.printableObject(filename)
        m = obj._addMesh()
 
        vertexList = []
index dc7c56ed1bf354e012253e904e1202f62a4974dd..63aaf861c9433717733c7d9c5b82bdc9c5977ea9 100644 (file)
@@ -17,7 +17,7 @@ import os
 import struct
 import time
 
-from Cura.util import mesh
+from Cura.util import printableObject
 
 def _loadAscii(m, f):
        cnt = 0
@@ -48,7 +48,7 @@ def _loadBinary(m, f):
                m._addFace(data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11])
 
 def loadScene(filename):
-       obj = mesh.printableObject(filename)
+       obj = printableObject.printableObject(filename)
        m = obj._addMesh()
 
        f = open(filename, "rb")
index 757f063dbd374fbe57094cc714ee111e94a9373e..01b535eed0a3a8e7b765e4f2d1d4255690f0f82a 100644 (file)
@@ -1,3 +1,9 @@
+"""
+The objectScene module contain a objectScene class,
+this class contains a group of printableObjects that are located on the build platform.
+
+The objectScene handles the printing order of these objects, and if they collide.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 import random
 import numpy
similarity index 98%
rename from Cura/util/mesh.py
rename to Cura/util/printableObject.py
index 3532c7781274fcba3a140a9b5b198b4e2b6fa789..ff980aae6e2b4a0e82c51b9f50ac3b90bc4f941f 100644 (file)
@@ -1,3 +1,8 @@
+"""
+The printableObject module contains a printableObject class,
+which is used to represent a single object that can be printed.
+A single object can have 1 or more meshes which represent different sections for multi-material extrusion.
+"""
 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import time
index 1e3e4416cf5420c088324e554766cfe10804cc94..3b99f09abd628ece1db2f291dbbbd3364953a2b6 100644 (file)
@@ -182,7 +182,8 @@ setting('retraction_amount',         4.5, float, 'advanced', _('Retraction')).se
 setting('retraction_dual_amount',   16.5, float, 'advanced', _('Retraction')).setRange(0).setLabel(_("Dual extrusion switch amount (mm)"), _("Amount of retraction when switching nozzle with dual-extrusion, set at 0 for no retraction at all. A value of 16.0mm seems to generate good results."))
 setting('retraction_min_travel',     1.5, float, 'expert',   _('Retraction')).setRange(0).setLabel(_("Minimum travel (mm)"), _("Minimum amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area."))
 setting('retraction_combing',       True, bool,  'expert',   _('Retraction')).setLabel(_("Enable combing"), _("Combing is the act of avoiding holes in the print for the head to travel over. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract."))
-setting('retraction_minimal_extrusion',0.02, float,'expert',  _('Retraction')).setRange(0).setLabel(_("Minimal extrusion before retracting (mm)"), _("The minimal amount of extrusion that needs to be done before retracting again if a retraction needs to happen before this minimal is reached the retraction is ignored.\nThis avoids retraction a lot on the same piece of filament which flattens the filament and causes grinding issues."))
+setting('retraction_minimal_extrusion',0.02, float,'expert', _('Retraction')).setRange(0).setLabel(_("Minimal extrusion before retracting (mm)"), _("The minimal amount of extrusion that needs to be done before retracting again if a retraction needs to happen before this minimal is reached the retraction is ignored.\nThis avoids retraction a lot on the same piece of filament which flattens the filament and causes grinding issues."))
+setting('retraction_hop',            0.0, float, 'expert',   _('Retraction')).setRange(0).setLabel(_("Z hop when retracting (mm)"), _("When a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works good. This feature has a lot of positive effect on delta towers."))
 setting('bottom_thickness',          0.3, float, 'advanced', _('Quality')).setRange(0).setLabel(_("Initial layer thickness (mm)"), _("Layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier. Set to 0.0 to have the bottom layer thickness the same as the other layers."))
 setting('object_sink',               0.0, float, 'advanced', _('Quality')).setLabel(_("Cut off object bottom (mm)"), _("Sinks the object into the platform, this can be used for objects that do not have a flat bottom and thus create a too small first layer."))
 #setting('enable_skin',             False, bool,  'advanced', _('Quality')).setLabel(_("Duplicate outlines"), _("Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality."))
index 51e8a59ec83ea4d63b48e624e860a6f74365c0b4..b800ebb76414398f24fbf140c1ab03059e9d051d 100644 (file)
@@ -441,6 +441,7 @@ class Engine(object):
                        'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
                        'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
                        'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
+                       'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
                        'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
                        'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
                        'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),