3 This page is in the table of contents.
4 This plugin limits the feed rate of the tool head, so that the stepper motors are not driven too fast and skip steps.
6 The limit manual page is at:
7 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit
9 The maximum z feed rate is defined in speed.
12 The default 'Activate Limit' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
15 ===Maximum Initial Feed Rate===
16 Default is one millimeter per second.
18 Defines the maximum speed of the inital tool head move.
21 The following examples limit the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and limit.py.
24 This brings up the limit dialog.
26 > python limit.py Screw Holder Bottom.stl
27 The limit tool is parsing the file:
28 Screw Holder Bottom.stl
30 The limit tool has created the file:
31 .. Screw Holder Bottom_limit.gcode
35 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
38 from datetime import date
39 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
40 from fabmetheus_utilities.vector3 import Vector3
41 from fabmetheus_utilities import archive
42 from fabmetheus_utilities import euclidean
43 from fabmetheus_utilities import gcodec
44 from fabmetheus_utilities import intercircle
45 from fabmetheus_utilities import settings
46 from skeinforge_application.skeinforge_utilities import skeinforge_craft
47 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
48 from skeinforge_application.skeinforge_utilities import skeinforge_profile
54 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
55 __date__ = '$Date: 2008/28/04 $'
56 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
59 def getCraftedText(fileName, gcodeText='', repository=None):
60 'Limit a gcode file or text.'
61 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository )
63 def getCraftedTextFromText(gcodeText, repository=None):
65 if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'limit'):
67 if repository == None:
68 repository = settings.getReadRepository(LimitRepository())
69 if not repository.activateLimit.value:
71 return LimitSkein().getCraftedGcode(gcodeText, repository)
73 def getNewRepository():
75 return LimitRepository()
77 def writeOutput(fileName, shouldAnalyze=True):
79 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'limit', shouldAnalyze)
82 class LimitRepository:
83 'A class to handle the limit settings.'
85 'Set the default settings, execute title & settings fileName.'
86 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self )
87 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '')
88 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit')
89 self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False)
90 self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0)
91 self.executeTitle = 'Limit'
94 'Limit button has been clicked.'
95 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
96 for fileName in fileNames:
101 'A class to limit a skein of extrusions.'
103 self.distanceFeedRate = gcodec.DistanceFeedRate()
104 self.feedRateMinute = None
106 self.maximumZDrillFeedRatePerSecond = 987654321.0
107 self.oldLocation = None
109 def getCraftedGcode(self, gcodeText, repository):
110 'Parse gcode text and store the limit gcode.'
111 self.repository = repository
112 self.lines = archive.getTextLines(gcodeText)
113 self.parseInitialization()
114 self.maximumZDrillFeedRatePerSecond = min(self.maximumZDrillFeedRatePerSecond, self.maximumZFeedRatePerSecond)
115 self.maximumZCurrentFeedRatePerSecond = self.maximumZFeedRatePerSecond
116 for lineIndex in xrange(self.lineIndex, len(self.lines)):
117 self.parseLine( lineIndex )
118 return self.distanceFeedRate.output.getvalue()
120 def getLimitedInitialMovement(self, line, splitLine):
121 'Get a limited linear movement.'
122 if self.oldLocation == None:
123 line = self.distanceFeedRate.getLineWithFeedRate(60.0 * self.repository.maximumInitialFeedRate.value, line, splitLine)
126 def getZLimitedLine(self, deltaZ, distance, line, splitLine):
127 'Get a replaced z limited gcode movement line.'
128 zFeedRateSecond = self.feedRateMinute * deltaZ / distance / 60.0
129 if zFeedRateSecond <= self.maximumZCurrentFeedRatePerSecond:
131 limitedFeedRateMinute = self.feedRateMinute * self.maximumZCurrentFeedRatePerSecond / zFeedRateSecond
132 return self.distanceFeedRate.getLineWithFeedRate(limitedFeedRateMinute, line, splitLine)
134 def getZLimitedLineArc(self, line, splitLine):
135 'Get a replaced z limited gcode arc movement line.'
136 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
137 if self.feedRateMinute == None or self.oldLocation == None:
139 relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
140 self.oldLocation += relativeLocation
141 deltaZ = abs(relativeLocation.z)
142 distance = gcodec.getArcDistance(relativeLocation, splitLine)
143 return self.getZLimitedLine(deltaZ, distance, line, splitLine)
145 def getZLimitedLineLinear(self, line, location, splitLine):
146 'Get a replaced z limited gcode linear movement line.'
147 self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
148 if location == self.oldLocation:
150 if self.feedRateMinute == None or self.oldLocation == None:
152 deltaZ = abs(location.z - self.oldLocation.z)
153 distance = abs(location - self.oldLocation)
154 return self.getZLimitedLine(deltaZ, distance, line, splitLine)
156 def parseInitialization(self):
157 'Parse gcode initialization and store the parameters.'
158 for self.lineIndex in xrange(len(self.lines)):
159 line = self.lines[self.lineIndex]
160 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
161 firstWord = gcodec.getFirstWord(splitLine)
162 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
163 if firstWord == '(</extruderInitialization>)':
164 self.distanceFeedRate.addTagBracketedProcedure('limit')
166 elif firstWord == '(<maximumZDrillFeedRatePerSecond>':
167 self.maximumZDrillFeedRatePerSecond = float(splitLine[1])
168 elif firstWord == '(<maximumZFeedRatePerSecond>':
169 self.maximumZFeedRatePerSecond = float(splitLine[1])
170 self.distanceFeedRate.addLine(line)
172 def parseLine( self, lineIndex ):
173 'Parse a gcode line and add it to the limit skein.'
174 line = self.lines[lineIndex].lstrip()
175 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
176 if len(splitLine) < 1:
178 firstWord = gcodec.getFirstWord(splitLine)
179 if firstWord == 'G1':
180 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
181 line = self.getLimitedInitialMovement(line, splitLine)
182 line = self.getZLimitedLineLinear(line, location, splitLine)
183 self.oldLocation = location
184 elif firstWord == 'G2' or firstWord == 'G3':
185 line = self.getZLimitedLineArc(line, splitLine)
186 elif firstWord == 'M101':
187 self.maximumZCurrentFeedRatePerSecond = self.maximumZDrillFeedRatePerSecond
188 elif firstWord == 'M103':
189 self.maximumZCurrentFeedRatePerSecond = self.maximumZFeedRatePerSecond
190 self.distanceFeedRate.addLine(line)
194 'Display the limit dialog.'
195 if len(sys.argv) > 1:
196 writeOutput(' '.join(sys.argv[1 :]))
198 settings.startMainLoopFromConstructor(getNewRepository())
200 if __name__ == '__main__':