chiark / gitweb /
Add back the ultimaker platform, and made the platform mesh simpler.
[cura.git] / Cura / slice / cura_sf / skeinforge_application / skeinforge_plugins / craft_plugins / joris.py
1 """
2 This page is in the table of contents.
3 The Joris plugin makes the perimiter slowly increase in Z over the layer. This will make vases/cups without a z blob.
4
5 ==Operation==
6 The default 'Activate Joris' checkbox is off.  When it is on, the Joris plugin will do it's work.
7
8 ==Settings==
9 ===Layers From===
10 Default: 1
11
12 Defines which layer of the print the joris process starts from.
13
14 ==Tips==
15
16 ==Examples==
17
18 """
19
20 from __future__ import absolute_import
21
22 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
23 from fabmetheus_utilities import archive
24 from fabmetheus_utilities import gcodec
25 from fabmetheus_utilities import settings
26 from skeinforge_application.skeinforge_utilities import skeinforge_craft
27 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
28 from skeinforge_application.skeinforge_utilities import skeinforge_profile
29
30 __author__ = 'Daid (daid303@gmail.com'
31 __date__ = '$Date: 2012/24/01 $'
32 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
33
34
35 def getCraftedText(fileName, gcodeText, repository=None):
36         'Joris a gcode linear move text.'
37         return getCraftedTextFromText(archive.getTextIfEmpty(fileName, gcodeText), repository)
38
39 def getCraftedTextFromText(gcodeText, repository=None):
40         'Joris a gcode linear move text.'
41         if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'Joris'):
42                 return gcodeText
43         if repository == None:
44                 repository = settings.getReadRepository(JorisRepository())
45         if not repository.activateJoris.value:
46                 return gcodeText
47         return JorisSkein().getCraftedGcode(gcodeText, repository)
48
49 def getIsMinimumSides(loops, sides=3):
50         'Determine if all the loops have at least the given number of sides.'
51         for loop in loops:
52                 if len(loop) < sides:
53                         return False
54         return True
55
56 def getNewRepository():
57         'Get new repository.'
58         return JorisRepository()
59
60 def writeOutput(fileName, shouldAnalyze=True):
61         'Joris a gcode linear move file.  Chain Joris the gcode if it is not already Jorised.'
62         skeinforge_craft.writeChainTextWithNounMessage(fileName, 'joris', shouldAnalyze)
63
64
65 class JorisRepository(object):
66         'A class to handle the Joris settings.'
67         def __init__(self):
68                 'Set the default settings, execute title & settings fileName.'
69                 skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.joris.html', self )
70                 self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Joris', self, '')
71                 self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Joris')
72                 self.activateJoris = settings.BooleanSetting().getFromValue('Activate Joris', self, False)
73                 settings.LabelSeparator().getFromRepository(self)
74                 self.layersFrom = settings.IntSpin().getSingleIncrementFromValue(0, 'Layers From (index):', self, 912345678, 1)
75                 self.executeTitle = 'Joris'
76
77         def execute(self):
78                 'Joris button has been clicked.'
79                 fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
80                 for fileName in fileNames:
81                         writeOutput(fileName)
82
83
84 class JorisSkein(object):
85         'A class to Joris a skein of extrusions.'
86         def __init__(self):
87                 'Initialize.'
88                 self.distanceFeedRate = gcodec.DistanceFeedRate()
89                 self.lines = None
90                 self.layerIndex = -1
91                 self.feedRateMinute = 959.0
92                 self.travelFeedRateMinute = 957.0
93                 self.perimeter = None
94                 self.oldLocation = None
95                 self.doJoris = False
96                 self.firstLayer = True
97         
98         def getCraftedGcode( self, gcodeText, repository ):
99                 'Parse gcode text and store the joris gcode.'
100                 self.lines = archive.getTextLines(gcodeText)
101                 self.repository = repository
102                 self.layersFromBottom = repository.layersFrom.value
103                 self.parseInitialization()
104                 for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
105                         line = self.lines[self.lineIndex]
106                         self.parseLine(line)
107                 return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue())
108                 
109         def parseInitialization(self):
110                 'Parse gcode initialization and store the parameters.'
111                 for self.lineIndex in xrange(len(self.lines)):
112                         line = self.lines[self.lineIndex]
113                         splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
114                         firstWord = gcodec.getFirstWord(splitLine)
115                         self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
116                         if firstWord == '(<layerThickness>':
117                                 self.layerThickness = float(splitLine[1])
118                         elif firstWord == '(</extruderInitialization>)':
119                                 self.distanceFeedRate.addTagBracketedProcedure('joris')
120                                 return
121                         elif firstWord == '(<travelFeedRatePerSecond>':
122                                 self.travelFeedRateMinute = 60.0 * float(splitLine[1])
123                         self.distanceFeedRate.addLine(line)
124                         
125         def parseLine(self, line):
126                 'Parse a gcode line and add it to the joris skein.'
127                 splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
128                 if len(splitLine) < 1:
129                         return
130                 firstWord = splitLine[0]
131                 if firstWord == 'G1' and self.doJoris:
132                         self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine)
133                         location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
134                         self.oldLocation = location
135                         if self.perimeter != None:
136                                 self.perimeter.append(location.dropAxis())
137                                 return
138                 elif firstWord == '(<layer>':
139                         self.layerIndex += 1
140                         settings.printProgress(self.layerIndex, 'joris')
141                 elif firstWord == 'M108':
142                         self.oldFlowRate = gcodec.getDoubleAfterFirstLetter(splitLine[1])
143                 elif firstWord == '(<edge>':
144                         if self.layerIndex >= self.layersFromBottom:
145                                 self.doJoris = True
146                 elif firstWord == 'M101' and self.doJoris:
147                         self.perimeter = []
148                         return
149                 elif firstWord == 'M103' and self.doJoris:
150                         self.addJorisedPerimeter()
151                         return
152                 elif firstWord == '(</edge>)':
153                         self.doJoris = False
154                 self.distanceFeedRate.addLine(line)
155                 
156         def addJorisedPerimeter(self):
157                 'Add jorised perimeter.'
158                 if self.perimeter is None:
159                         return
160                 #Calculate the total length of the perimeter.
161                 p = self.oldLocation.dropAxis()
162                 perimeterLength = 0
163                 for point in self.perimeter:
164                         perimeterLength += abs( point - p )
165                         p = point
166                 
167                 #Build the perimeter with an increasing Z over the length.
168                 if self.firstLayer:
169                         #On the first layer, we need to create an extra jorised perimeter, else we create a gap at the end of the perimeter.
170                         p = self.oldLocation.dropAxis()
171                         length = 0;
172                         self.distanceFeedRate.addLine('M101') # Turn extruder on.
173                         for point in self.perimeter:
174                                 length += abs( point - p );
175                                 p = point
176                                 self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, point, self.oldLocation.z - self.layerThickness + self.layerThickness * length / perimeterLength)
177                         self.distanceFeedRate.addLine('M103') # Turn extruder off.
178                         self.firstLayer = False
179
180                 p = self.oldLocation.dropAxis()
181                 length = 0;
182                 self.distanceFeedRate.addLine('M101') # Turn extruder on.
183                 for point in self.perimeter:
184                         length += abs( point - p );
185                         p = point
186                         self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, point, self.oldLocation.z + self.layerThickness * length / perimeterLength)
187                 self.distanceFeedRate.addLine('M103') # Turn extruder off.
188                 self.perimeter = None
189