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