From: daid Date: Wed, 5 Feb 2014 12:15:27 +0000 (+0100) Subject: Add some more documentation. And the zhop feature. X-Git-Tag: 14.02-RC1~36 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=ad7641e059048c7dcb25da1f47c0a7e95e7f4f7c;p=cura.git Add some more documentation. And the zhop feature. --- diff --git a/Cura/avr_isp/ispBase.py b/Cura/avr_isp/ispBase.py index adc5ca96..ffee7285 100644 --- a/Cura/avr_isp/ispBase.py +++ b/Cura/avr_isp/ispBase.py @@ -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 diff --git a/Cura/avr_isp/stk500v2.py b/Cura/avr_isp/stk500v2.py index 8bacdab9..975dd30b 100644 --- a/Cura/avr_isp/stk500v2.py +++ b/Cura/avr_isp/stk500v2.py @@ -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 diff --git a/Cura/doctest.py b/Cura/doctest.py index 3640367e..e74d76d6 100644 --- a/Cura/doctest.py +++ b/Cura/doctest.py @@ -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': diff --git a/Cura/gui/printWindow2.py b/Cura/gui/printWindow2.py index de51e3fd..57c62284 100644 --- a/Cura/gui/printWindow2.py +++ b/Cura/gui/printWindow2.py @@ -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(): diff --git a/Cura/gui/tools/imageToMesh.py b/Cura/gui/tools/imageToMesh.py index 9825d394..f26f90c5 100644 --- a/Cura/gui/tools/imageToMesh.py +++ b/Cura/gui/tools/imageToMesh.py @@ -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) diff --git a/Cura/gui/tools/minecraftImport.py b/Cura/gui/tools/minecraftImport.py index 16b86aa4..e5079996 100644 --- a/Cura/gui/tools/minecraftImport.py +++ b/Cura/gui/tools/minecraftImport.py @@ -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): diff --git a/Cura/util/gcodeGenerator.py b/Cura/util/gcodeGenerator.py index 2d32f31e..cb6ed709 100644 --- a/Cura/util/gcodeGenerator.py +++ b/Cura/util/gcodeGenerator.py @@ -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 diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index 50b2be34..4bf13a7a 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -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 diff --git a/Cura/util/machineCom.py b/Cura/util/machineCom.py index 3be02e68..ec372df7 100644 --- a/Cura/util/machineCom.py +++ b/Cura/util/machineCom.py @@ -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 diff --git a/Cura/util/meshLoader.py b/Cura/util/meshLoader.py index b37c66e8..cbe10067 100644 --- a/Cura/util/meshLoader.py +++ b/Cura/util/meshLoader.py @@ -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) diff --git a/Cura/util/meshLoaders/amf.py b/Cura/util/meshLoaders/amf.py index 848f8bd0..69d999cf 100644 --- a/Cura/util/meshLoaders/amf.py +++ b/Cura/util/meshLoaders/amf.py @@ -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'): diff --git a/Cura/util/meshLoaders/dae.py b/Cura/util/meshLoaders/dae.py index f173ddf1..9dd0e91b 100644 --- a/Cura/util/meshLoaders/dae.py +++ b/Cura/util/meshLoaders/dae.py @@ -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() diff --git a/Cura/util/meshLoaders/obj.py b/Cura/util/meshLoaders/obj.py index 8c1fc083..c347b6cc 100644 --- a/Cura/util/meshLoaders/obj.py +++ b/Cura/util/meshLoaders/obj.py @@ -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 = [] diff --git a/Cura/util/meshLoaders/stl.py b/Cura/util/meshLoaders/stl.py index dc7c56ed..63aaf861 100644 --- a/Cura/util/meshLoaders/stl.py +++ b/Cura/util/meshLoaders/stl.py @@ -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") diff --git a/Cura/util/objectScene.py b/Cura/util/objectScene.py index 757f063d..01b535ee 100644 --- a/Cura/util/objectScene.py +++ b/Cura/util/objectScene.py @@ -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 diff --git a/Cura/util/mesh.py b/Cura/util/printableObject.py similarity index 98% rename from Cura/util/mesh.py rename to Cura/util/printableObject.py index 3532c778..ff980aae 100644 --- a/Cura/util/mesh.py +++ b/Cura/util/printableObject.py @@ -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 diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 1e3e4416..3b99f09a 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -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.")) diff --git a/Cura/util/sliceEngine.py b/Cura/util/sliceEngine.py index 51e8a59e..b800ebb7 100644 --- a/Cura/util/sliceEngine.py +++ b/Cura/util/sliceEngine.py @@ -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),