2 This page is in the table of contents.
3 Hop is a script to raise the extruder when it is not extruding.
7 Note: In some cases where you have thin overhang this plugin can help solve the problem object being knocked off by the head
9 The hop manual page is at:
10 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Hop
13 The default 'Activate Hop' checkbox is off.
15 It is off because Vik and Nophead found better results without hopping. Numerous users reported better output without this plugin hence it is off by default.
17 When activated the extruder will hop when traveling. When it is off, nothing will be done.
20 ===Hop Over Layer Thickness===
23 Defines the ratio of the hop height over the layer height, this is the most important hop setting.
25 ===Minimum Hop Angle===
26 Default is 20 degrees.
28 Defines the minimum angle that the path of the extruder will be raised. An angle of ninety means that the extruder will go straight up as soon as it is not extruding and a low angle means the extruder path will gradually rise to the hop height.
31 The following examples hop the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and hop.py.
34 This brings up the hop dialog.
36 > python hop.py Screw Holder Bottom.stl
37 The hop tool is parsing the file:
38 Screw Holder Bottom.stl
40 The hop tool has created the file:
41 .. Screw Holder Bottom_hop.gcode
45 from __future__ import absolute_import
46 #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.
49 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
50 from fabmetheus_utilities import archive
51 from fabmetheus_utilities import euclidean
52 from fabmetheus_utilities import gcodec
53 from fabmetheus_utilities import settings
54 from skeinforge_application.skeinforge_utilities import skeinforge_craft
55 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
56 from skeinforge_application.skeinforge_utilities import skeinforge_profile
61 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
62 __date__ = '$Date: 2008/21/04 $'
63 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
66 def getCraftedText( fileName, text, hopRepository = None ):
67 "Hop a gcode linear move text."
68 return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), hopRepository )
70 def getCraftedTextFromText( gcodeText, hopRepository = None ):
71 "Hop a gcode linear move text."
72 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'hop'):
74 if hopRepository == None:
75 hopRepository = settings.getReadRepository( HopRepository() )
76 if not hopRepository.activateHop.value:
78 return HopSkein().getCraftedGcode( gcodeText, hopRepository )
80 def getNewRepository():
82 return HopRepository()
84 def writeOutput(fileName, shouldAnalyze=True):
85 "Hop a gcode linear move file. Chain hop the gcode if it is not already hopped."
86 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'hop', shouldAnalyze)
90 "A class to handle the hop settings."
92 "Set the default settings, execute title & settings fileName."
93 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.hop.html', self )
94 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Hop', self, '')
95 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Hop')
96 self.activateHop = settings.BooleanSetting().getFromValue('Activate Hop', self, False )
97 self.hopOverLayerThickness = settings.FloatSpin().getFromValue( 0.5, 'Hop Over Layer Thickness (ratio):', self, 1.5, 1.0 )
98 self.minimumHopAngle = settings.FloatSpin().getFromValue( 20.0, 'Minimum Hop Angle (degrees):', self, 60.0, 30.0 )
99 self.executeTitle = 'Hop'
102 "Hop button has been clicked."
103 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
104 for fileName in fileNames:
105 writeOutput(fileName)
109 "A class to hop a skein of extrusions."
112 self.distanceFeedRate = gcodec.DistanceFeedRate()
113 self.extruderActive = False
114 self.feedRateMinute = 961.0
116 self.hopDistance = self.hopHeight
117 self.justDeactivated = False
118 self.layerCount = settings.LayerCount()
121 self.oldLocation = None
123 def getCraftedGcode( self, gcodeText, hopRepository ):
124 "Parse gcode text and store the hop gcode."
125 self.lines = archive.getTextLines(gcodeText)
126 self.minimumSlope = math.tan( math.radians( hopRepository.minimumHopAngle.value ) )
127 self.parseInitialization( hopRepository )
128 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
129 line = self.lines[self.lineIndex]
131 return self.distanceFeedRate.output.getvalue()
133 def getHopLine(self, line):
134 "Get hopped gcode line."
135 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
136 self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine )
137 if self.extruderActive:
139 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
140 highestZ = location.z
141 if self.oldLocation != None:
142 highestZ = max( highestZ, self.oldLocation.z )
143 highestZHop = highestZ + self.hopHeight
144 locationComplex = location.dropAxis()
145 if self.justDeactivated:
146 oldLocationComplex = self.oldLocation.dropAxis()
147 distance = abs( locationComplex - oldLocationComplex )
148 if distance < self.minimumDistance:
149 if self.isNextTravel() or distance == 0.0:
150 return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop )
151 alongRatio = min( 0.41666666, self.hopDistance / distance )
152 oneMinusAlong = 1.0 - alongRatio
153 closeLocation = oldLocationComplex * oneMinusAlong + locationComplex * alongRatio
154 self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop ) )
155 if self.isNextTravel():
156 return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop )
157 farLocation = oldLocationComplex * alongRatio + locationComplex * oneMinusAlong
158 self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.feedRateMinute, farLocation, highestZHop )
160 if self.isNextTravel():
161 return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop )
164 def isNextTravel(self):
165 "Determine if there is another linear travel before the thread ends."
166 for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ):
167 line = self.lines[ afterIndex ]
168 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
169 firstWord = gcodec.getFirstWord(splitLine)
170 if firstWord == 'G1':
172 if firstWord == 'M101':
176 def parseInitialization( self, hopRepository ):
177 'Parse gcode initialization and store the parameters.'
178 for self.lineIndex in xrange(len(self.lines)):
179 line = self.lines[self.lineIndex]
180 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
181 firstWord = gcodec.getFirstWord(splitLine)
182 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
183 if firstWord == '(<layerHeight>':
184 layerHeight = float(splitLine[1])
185 self.hopHeight = hopRepository.hopOverLayerThickness.value * layerHeight
186 self.hopDistance = self.hopHeight / self.minimumSlope
187 self.minimumDistance = 0.5 * layerHeight
188 elif firstWord == '(</extruderInitialization>)':
189 self.distanceFeedRate.addTagBracketedProcedure('hop')
191 self.distanceFeedRate.addLine(line)
193 def parseLine(self, line):
194 "Parse a gcode line and add it to the bevel gcode."
195 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
196 if len(splitLine) < 1:
198 firstWord = splitLine[0]
199 if self.distanceFeedRate.getIsAlteration(line):
201 if firstWord == 'G1':
202 line = self.getHopLine(line)
203 self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
204 self.justDeactivated = False
205 elif firstWord == '(<layer>':
206 self.layerCount.printProgressIncrement('hop')
207 elif firstWord == 'M101':
208 self.extruderActive = True
209 elif firstWord == 'M103':
210 self.extruderActive = False
211 self.justDeactivated = True
212 self.distanceFeedRate.addLineCheckAlteration(line)
216 "Display the hop dialog."
217 if len(sys.argv) > 1:
218 writeOutput(' '.join(sys.argv[1 :]))
220 settings.startMainLoopFromConstructor(getNewRepository())
222 if __name__ == "__main__":