2 This page is in the table of contents.
3 Drill is a script to drill down small holes.
6 The default 'Activate Drill' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called.
10 The drill script will move the tool from the top of the hole plus the 'Drilling Margin on Top', to the bottom of the hole minus the 'Drilling Margin on Bottom'.
12 ===Drilling Margin on Top===
13 Default is three millimeters.
15 ===Drilling Margin on Bottom===
16 Default is one millimeter.
19 The following examples drill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and drill.py.
22 This brings up the drill dialog.
24 > python drill.py Screw Holder Bottom.stl
25 The drill tool is parsing the file:
26 Screw Holder Bottom.stl
28 The drill tool has created the file:
29 .. Screw Holder Bottom_drill.gcode
33 from __future__ import absolute_import
34 #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.
37 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
38 from fabmetheus_utilities import archive
39 from fabmetheus_utilities import euclidean
40 from fabmetheus_utilities import gcodec
41 from fabmetheus_utilities import intercircle
42 from fabmetheus_utilities import settings
43 from skeinforge_application.skeinforge_utilities import skeinforge_craft
44 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
45 from skeinforge_application.skeinforge_utilities import skeinforge_profile
49 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
50 __date__ = '$Date: 2008/21/04 $'
51 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
53 def getCraftedText( fileName, text, repository=None):
54 "Drill a gcode linear move file or text."
55 return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
57 def getCraftedTextFromText(gcodeText, repository=None):
58 "Drill a gcode linear move text."
59 if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'drill'):
61 if repository == None:
62 repository = settings.getReadRepository( DrillRepository() )
63 if not repository.activateDrill.value:
65 return DrillSkein().getCraftedGcode(gcodeText, repository)
67 def getNewRepository():
69 return DrillRepository()
71 def getPolygonCenter( polygon ):
72 "Get the centroid of a polygon."
75 for pointIndex in xrange( len( polygon ) ):
76 pointBegin = polygon[pointIndex]
77 pointEnd = polygon[ (pointIndex + 1) % len( polygon ) ]
78 area = pointBegin.real * pointEnd.imag - pointBegin.imag * pointEnd.real
80 pointSum += complex( pointBegin.real + pointEnd.real, pointBegin.imag + pointEnd.imag ) * area
81 return pointSum / 3.0 / areaSum
83 def writeOutput(fileName, shouldAnalyze=True):
84 "Drill a gcode linear move file."
85 skeinforge_craft.writeChainTextWithNounMessage(fileName, 'drill', shouldAnalyze)
89 "A layer of loops and paths."
90 def __init__( self, z ):
91 "Thread layer constructor."
96 "Get the string representation of this thread layer."
97 return '%s, %s' % ( self.z, self.points )
100 class DrillRepository:
101 "A class to handle the drill settings."
103 "Set the default settings, execute title & settings fileName."
104 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.drill.html', self )
105 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Drill', self, '')
106 self.activateDrill = settings.BooleanSetting().getFromValue('Activate Drill', self, True )
107 self.drillingMarginOnBottom = settings.FloatSpin().getFromValue( 0.0, 'Drilling Margin on Bottom (millimeters):', self, 5.0, 1.0 )
108 self.drillingMarginOnTop = settings.FloatSpin().getFromValue( 0.0, 'Drilling Margin on Top (millimeters):', self, 20.0, 3.0 )
109 self.executeTitle = 'Drill'
112 "Drill button has been clicked."
113 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
114 for fileName in fileNames:
115 writeOutput(fileName)
119 "A class to drill a skein of extrusions."
122 self.distanceFeedRate = gcodec.DistanceFeedRate()
123 self.extruderActive = False
124 self.halfLayerThickness = 0.4
125 self.isDrilled = False
128 self.maximumDistance = 0.06
129 self.oldLocation = None
130 self.threadLayer = None
131 self.threadLayers = []
133 def addDrillHoles(self):
134 "Parse a gcode line."
135 self.isDrilled = True
136 if len( self.threadLayers ) < 1:
138 topThreadLayer = self.threadLayers[0]
139 drillPoints = topThreadLayer.points
140 for drillPoint in drillPoints:
141 zTop = topThreadLayer.z + self.halfLayerThickness + self.repository.drillingMarginOnTop.value
142 drillingCenterDepth = self.getDrillingCenterDepth( topThreadLayer.z, drillPoint )
143 zBottom = drillingCenterDepth - self.halfLayerThickness - self.repository.drillingMarginOnBottom.value
144 self.addGcodeFromVerticalThread( drillPoint, zTop, zBottom )
146 def addGcodeFromVerticalThread( self, point, zBegin, zEnd ):
147 "Add a thread to the output."
148 self.distanceFeedRate.addGcodeMovementZ( point, zBegin )
149 self.distanceFeedRate.addLine('M101') # Turn extruder on.
150 self.distanceFeedRate.addGcodeMovementZ( point, zEnd )
151 self.distanceFeedRate.addLine('M103') # Turn extruder off.
153 def addThreadLayerIfNone(self):
154 "Add a thread layer if it is none."
155 if self.threadLayer != None:
157 self.threadLayer = ThreadLayer( self.layerZ )
158 self.threadLayers.append( self.threadLayer )
160 def getCraftedGcode(self, gcodeText, repository):
161 "Parse gcode text and store the drill gcode."
162 self.lines = archive.getTextLines(gcodeText)
163 self.repository = repository
164 self.parseInitialization()
165 for line in self.lines[self.lineIndex :]:
166 self.parseNestedRing(line)
167 for line in self.lines[self.lineIndex :]:
169 return self.distanceFeedRate.output.getvalue()
171 def getDrillingCenterDepth( self, drillingCenterDepth, drillPoint ):
172 "Get the drilling center depth."
173 for threadLayer in self.threadLayers[1 :]:
174 if self.isPointClose( drillPoint, threadLayer.points ):
175 drillingCenterDepth = threadLayer.z
177 return drillingCenterDepth
178 return drillingCenterDepth
180 def isPointClose( self, drillPoint, points ):
181 "Determine if a point on the thread layer is close."
183 if abs( point - drillPoint ) < self.maximumDistance:
187 def linearMove( self, splitLine ):
188 "Add a linear move to the loop."
189 self.addThreadLayerIfNone()
190 location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
191 if self.extruderActive:
193 self.oldLocation = location
195 def parseInitialization(self):
196 'Parse gcode initialization and store the parameters.'
197 for self.lineIndex in xrange(len(self.lines)):
198 line = self.lines[self.lineIndex]
199 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
200 firstWord = gcodec.getFirstWord(splitLine)
201 self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
202 if firstWord == '(</extruderInitialization>)':
203 self.distanceFeedRate.addTagBracketedProcedure('drill')
205 elif firstWord == '(<layerHeight>':
206 self.halfLayerThickness = 0.5 * float(splitLine[1])
207 elif firstWord == '(<edgeWidth>':
208 self.maximumDistance = 0.1 * float(splitLine[1])
209 self.distanceFeedRate.addLine(line)
211 def parseLine(self, line):
212 "Parse a gcode line."
213 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
214 if len(splitLine) < 1:
216 firstWord = splitLine[0]
217 self.distanceFeedRate.addLine(line)
218 if firstWord == '(<layer>':
219 if not self.isDrilled:
222 def parseNestedRing(self, line):
223 "Parse a nested ring."
224 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
225 if len(splitLine) < 1:
227 firstWord = splitLine[0]
228 if firstWord == 'G1':
229 self.linearMove(splitLine)
230 if firstWord == 'M101':
231 self.extruderActive = True
232 elif firstWord == 'M103':
233 self.extruderActive = False
234 elif firstWord == '(<boundaryPoint>':
235 location = gcodec.getLocationFromSplitLine(None, splitLine)
236 if self.boundary == None:
238 self.boundary.append(location.dropAxis())
239 elif firstWord == '(<layer>':
240 self.layerZ = float(splitLine[1])
241 self.threadLayer = None
242 elif firstWord == '(<boundaryPerimeter>)':
243 self.addThreadLayerIfNone()
244 elif firstWord == '(</boundaryPerimeter>)':
245 if self.boundary != None:
246 self.threadLayer.points.append( getPolygonCenter( self.boundary ) )
251 "Display the drill dialog."
252 if len(sys.argv) > 1:
253 writeOutput(' '.join(sys.argv[1 :]))
255 settings.startMainLoopFromConstructor(getNewRepository())
257 if __name__ == "__main__":