chiark / gitweb /
Move SF into its own directory, to seperate SF and Cura. Rename newui to gui.
[cura.git] / Cura / cura_sf / skeinforge_application / skeinforge_plugins / craft_plugins / widen.py
1 #! /usr/bin/env python
2 """
3 This page is in the table of contents.
4 Widen will widen the outside edges away from the inside edges, so that the outsides will be at least two edge widths away from the insides and therefore the outside filaments will not overlap the inside filaments.
5
6 For example, if a mug has a very thin wall, widen would widen the outside of the mug so that the wall of the mug would be two edge widths wide, and the outside wall filament would not overlap the inside filament.
7
8 For another example, if the outside of the object runs right next to a hole, widen would widen the wall around the hole so that the wall would bulge out around the hole, and the outside filament would not overlap the hole filament.
9
10 The widen manual page is at:
11 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen
12
13 ==Operation==
14 The default 'Activate Widen' checkbox is off.  When it is on, widen will work, when it is off, nothing will be done.
15
16 ==Examples==
17 The following examples widen the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and widen.py.
18
19 > python widen.py
20 This brings up the widen dialog.
21
22 > python widen.py Screw Holder Bottom.stl
23 The widen tool is parsing the file:
24 Screw Holder Bottom.stl
25 ..
26 The widen tool has created the file:
27 .. Screw Holder Bottom_widen.gcode
28
29 """
30
31 from __future__ import absolute_import
32 try:
33         import psyco
34         psyco.full()
35 except:
36         pass
37 #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 import __init__
39
40 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
41 from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
42 from fabmetheus_utilities.geometry.solids import triangle_mesh
43 from fabmetheus_utilities import archive
44 from fabmetheus_utilities import euclidean
45 from fabmetheus_utilities import gcodec
46 from fabmetheus_utilities import intercircle
47 from fabmetheus_utilities import settings
48 from skeinforge_application.skeinforge_utilities import skeinforge_craft
49 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
50 from skeinforge_application.skeinforge_utilities import skeinforge_profile
51 import os
52 import sys
53
54
55 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
56 __date__ = '$Date: 2008/28/04 $'
57 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
58
59
60 def getCraftedText(fileName, text='', repository=None):
61         'Widen the preface file or text.'
62         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)
63
64 def getCraftedTextFromText(gcodeText, repository=None):
65         'Widen the preface gcode text.'
66         if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'widen'):
67                 return gcodeText
68         if repository == None:
69                 repository = settings.getReadRepository( WidenRepository() )
70         if not repository.activateWiden.value:
71                 return gcodeText
72         return WidenSkein().getCraftedGcode(gcodeText, repository)
73
74 def getIntersectingWithinLoops(loop, loopList, outsetLoop):
75         'Get the loops which are intersecting or which it is within.'
76         intersectingWithinLoops = []
77         for otherLoop in loopList:
78                 if getIsIntersectingWithinLoop(loop, otherLoop, outsetLoop):
79                         intersectingWithinLoops.append(otherLoop)
80         return intersectingWithinLoops
81
82 def getIsIntersectingWithinLoop(loop, otherLoop, outsetLoop):
83         'Determine if the loop is intersecting or is within the other loop.'
84         if euclidean.isLoopIntersectingLoop(loop, otherLoop):
85                 return True
86         return euclidean.isPathInsideLoop(otherLoop, loop) != euclidean.isPathInsideLoop(otherLoop, outsetLoop)
87
88 def getIsPointInsideALoop(loops, point):
89         'Determine if a point is inside a loop of a loop list.'
90         for loop in loops:
91                 if euclidean.isPointInsideLoop(loop, point):
92                         return True
93         return False
94
95 def getNewRepository():
96         'Get new repository.'
97         return WidenRepository()
98
99 def getWidenedLoops(loop, loopList, outsetLoop, radius):
100         'Get the widened loop.'
101         intersectingWithinLoops = getIntersectingWithinLoops(loop, loopList, outsetLoop)
102         if len(intersectingWithinLoops) < 1:
103                 return [loop]
104         loopsUnified = boolean_solid.getLoopsUnion(radius, [[loop], intersectingWithinLoops])
105         if len(loopsUnified) < 1:
106                 return [loop]
107         return loopsUnified
108
109 def writeOutput(fileName, shouldAnalyze=True):
110         'Widen the carving of a gcode file.'
111         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'widen', shouldAnalyze)
112
113
114 class WidenRepository:
115         'A class to handle the widen settings.'
116         def __init__(self):
117                 'Set the default settings, execute title & settings fileName.'
118                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.widen.html', self)
119                 self.fileNameInput = settings.FileNameInput().getFromFileName(
120                         fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Widen', self, '')
121                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute(
122                         'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen')
123                 self.activateWiden = settings.BooleanSetting().getFromValue('Activate Widen', self, False)
124                 self.widenWidthOverEdgeWidth = settings.IntSpin().getFromValue(2, 'Widen Width over Edge Width (ratio):', self, 4, 2)
125                 self.executeTitle = 'Widen'
126
127         def execute(self):
128                 'Widen button has been clicked.'
129                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(
130                         self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
131                 for fileName in fileNames:
132                         writeOutput(fileName)
133
134
135 class WidenSkein:
136         'A class to widen a skein of extrusions.'
137         def __init__(self):
138                 self.boundary = None
139                 self.distanceFeedRate = gcodec.DistanceFeedRate()
140                 self.layerCount = settings.LayerCount()
141                 self.lineIndex = 0
142                 self.loopLayer = None
143
144         def addWiden(self, loopLayer):
145                 'Add widen to the layer.'
146                 triangle_mesh.sortLoopsInOrderOfArea(False, loopLayer.loops)
147                 widdershinsLoops = []
148                 clockwiseInsetLoops = []
149                 for loopIndex in xrange(len(loopLayer.loops)):
150                         loop = loopLayer.loops[loopIndex]
151                         if euclidean.isWiddershins(loop):
152                                 otherLoops = loopLayer.loops[: loopIndex] + loopLayer.loops[loopIndex + 1 :]
153                                 leftPoint = euclidean.getLeftPoint(loop)
154                                 if getIsPointInsideALoop(otherLoops, leftPoint):
155                                         self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z)
156                                 else:
157                                         widdershinsLoops.append(loop)
158                         else:
159 #                               clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.widenEdgeWidth)
160 #                               clockwiseInsetLoop.reverse()
161 #                               clockwiseInsetLoops.append(clockwiseInsetLoop)
162                                 clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.widenEdgeWidth)
163                                 self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z)
164                 for widdershinsLoop in widdershinsLoops:
165                         outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.widenEdgeWidth)
166                         for widenedLoop in getWidenedLoops(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.lessThanHalfEdgeWidth):
167                                 self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z)
168
169         def getCraftedGcode(self, gcodeText, repository):
170                 'Parse gcode text and store the widen gcode.'
171                 self.repository = repository
172                 self.lines = archive.getTextLines(gcodeText)
173                 self.parseInitialization()
174                 for line in self.lines[self.lineIndex :]:
175                         self.parseLine(line)
176                 return self.distanceFeedRate.output.getvalue()
177
178         def parseInitialization(self):
179                 'Parse gcode initialization and store the parameters.'
180                 for self.lineIndex in xrange(len(self.lines)):
181                         line = self.lines[self.lineIndex]
182                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
183                         firstWord = gcodec.getFirstWord(splitLine)
184                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
185                         if firstWord == '(</extruderInitialization>)':
186                                 self.distanceFeedRate.addTagBracketedProcedure('widen')
187                         elif firstWord == '(<crafting>)':
188                                 self.distanceFeedRate.addLine(line)
189                                 return
190                         elif firstWord == '(<edgeWidth>':
191                                 self.edgeWidth = float(splitLine[1])
192                                 self.widenEdgeWidth = float(self.repository.widenWidthOverEdgeWidth.value) * self.edgeWidth
193                                 self.lessThanHalfEdgeWidth = 0.49 * self.edgeWidth
194                         self.distanceFeedRate.addLine(line)
195
196         def parseLine(self, line):
197                 'Parse a gcode line and add it to the widen skein.'
198                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
199                 if len(splitLine) < 1:
200                         return
201                 firstWord = splitLine[0]
202                 if firstWord == '(<boundaryPoint>':
203                         location = gcodec.getLocationFromSplitLine(None, splitLine)
204                         self.boundary.append(location.dropAxis())
205                 elif firstWord == '(<layer>':
206                         self.layerCount.printProgressIncrement('widen')
207                         self.loopLayer = euclidean.LoopLayer(float(splitLine[1]))
208                         self.distanceFeedRate.addLine(line)
209                 elif firstWord == '(</layer>)':
210                         self.addWiden( self.loopLayer )
211                         self.loopLayer = None
212                 elif firstWord == '(<nestedRing>)':
213                         self.boundary = []
214                         self.loopLayer.loops.append( self.boundary )
215                 if self.loopLayer == None:
216                         self.distanceFeedRate.addLine(line)
217
218
219 def main():
220         'Display the widen dialog.'
221         if len(sys.argv) > 1:
222                 writeOutput(' '.join(sys.argv[1 :]))
223         else:
224                 settings.startMainLoopFromConstructor(getNewRepository())
225
226 if __name__ == '__main__':
227         main()