Package plugins :: Module comb
[hide private]
[frames] | no frames]

Source Code for Module plugins.comb

  1  """ 
  2  Comb the extrusion hair of a gcode file.  Modifies the travel paths so the nozzle does not go over empty spaces, thus reducing the strings that may build up. 
  3   
  4  Note: comb is called during gcode generation, not through the usual plugin channel. This is because the travel calculations are made at the last minute. 
  5  Credits: 
  6          Original Author: Enrique Perez (http://skeinforge.com) 
  7          Contributors: Please see the documentation in Skeinforge  
  8          Modifed as SFACT: Ahmet Cem Turan (github.com/ahmetcemturan/SFACT)       
  9   
 10  License:  
 11          GNU Affero General Public License http://www.gnu.org/licenses/agpl.html 
 12  """ 
 13   
 14  from config import config 
 15  from fabmetheus_utilities import archive, euclidean, intercircle 
 16  import logging 
 17  import math 
 18   
 19  name = __name__ 
 20  logger = logging.getLogger(name) 
 21   
22 -class CombSkein:
23 "A class to comb a skein of extrusions."
24 - def __init__(self, layer):
25 'Initialize' 26 self.betweenTable = {} 27 self.z = layer.z 28 29 self.perimeterWidth = layer.runtimeParameters.perimeterWidth 30 self.combInset = 0.7 * self.perimeterWidth 31 self.betweenInset = 0.4 * self.perimeterWidth 32 self.uTurnWidth = 0.5 * self.betweenInset 33 self.travelFeedRateMinute = layer.runtimeParameters.travelFeedRateMinute 34 35 self.boundaries = [] 36 perimeters = [] 37 layer.getPerimeterPaths(perimeters) 38 for perimeter in perimeters: 39 x = [] 40 for boundaryPoint in perimeter.boundaryPoints: 41 x.append(boundaryPoint.dropAxis()) 42 self.boundaries.append(x)
43
44 - def getBetweens(self):
45 "Set betweens for the layer." 46 if self.z in self.betweenTable: 47 return self.betweenTable[ self.z ] 48 if len(self.boundaries) == 0: 49 return [] 50 self.betweenTable[ self.z ] = [] 51 for boundaryLoop in self.boundaries: 52 self.betweenTable[ self.z ] += intercircle.getInsetLoopsFromLoop(boundaryLoop, self.betweenInset) 53 return self.betweenTable[ self.z ]
54
55 - def getIsAsFarAndNotIntersecting(self, begin, end):
56 "Determine if the point on the line is at least as far from the loop as the center point." 57 if begin == end: 58 print('this should never happen but it does not really matter, begin == end in getIsAsFarAndNotIntersecting in comb.') 59 print(begin) 60 return True 61 return not euclidean.isLineIntersectingLoops(self.getBetweens(), begin, end)
62
63 - def getIsRunningJumpPathAdded(self, betweens, end, lastPoint, nearestEndMinusLastSegment, pathAround, penultimatePoint, runningJumpSpace):
64 "Add a running jump path if possible, and return if it was added." 65 jumpStartPoint = lastPoint - nearestEndMinusLastSegment * runningJumpSpace 66 if euclidean.isLineIntersectingLoops(betweens, penultimatePoint, jumpStartPoint): 67 return False 68 pathAround[-1] = jumpStartPoint 69 return True
70
71 - def getPathsByIntersectedLoop(self, begin, end, loop):
72 "Get both paths along the loop from the point nearest to the begin to the point nearest to the end." 73 nearestBeginDistanceIndex = euclidean.getNearestDistanceIndex(begin, loop) 74 nearestEndDistanceIndex = euclidean.getNearestDistanceIndex(end, loop) 75 beginIndex = (nearestBeginDistanceIndex.index + 1) % len(loop) 76 endIndex = (nearestEndDistanceIndex.index + 1) % len(loop) 77 nearestBegin = euclidean.getNearestPointOnSegment(loop[ nearestBeginDistanceIndex.index ], loop[ beginIndex ], begin) 78 nearestEnd = euclidean.getNearestPointOnSegment(loop[ nearestEndDistanceIndex.index ], loop[ endIndex ], end) 79 clockwisePath = [ nearestBegin ] 80 widdershinsPath = [ nearestBegin ] 81 if nearestBeginDistanceIndex.index != nearestEndDistanceIndex.index: 82 widdershinsPath += euclidean.getAroundLoop(beginIndex, endIndex, loop) 83 clockwisePath += euclidean.getAroundLoop(endIndex, beginIndex, loop)[: :-1] 84 clockwisePath.append(nearestEnd) 85 widdershinsPath.append(nearestEnd) 86 return [ clockwisePath, widdershinsPath ]
87
88 - def getPathBetween(self, loop, points):
89 "Add a path between the perimeter and the fill." 90 paths = self.getPathsByIntersectedLoop(points[1], points[2], loop) 91 shortestPath = paths[int(euclidean.getPathLength(paths[1]) < euclidean.getPathLength(paths[0]))] 92 if len(shortestPath) < 2: 93 return shortestPath 94 if abs(points[1] - shortestPath[0]) > abs(points[1] - shortestPath[-1]): 95 shortestPath.reverse() 96 loopWiddershins = euclidean.isWiddershins(loop) 97 pathBetween = [] 98 for pointIndex in xrange(len(shortestPath)): 99 center = shortestPath[pointIndex] 100 centerPerpendicular = None 101 beginIndex = pointIndex - 1 102 if beginIndex >= 0: 103 begin = shortestPath[beginIndex] 104 centerPerpendicular = intercircle.getWiddershinsByLength(center, begin, self.combInset) 105 centerEnd = None 106 endIndex = pointIndex + 1 107 if endIndex < len(shortestPath): 108 end = shortestPath[endIndex] 109 centerEnd = intercircle.getWiddershinsByLength(end, center, self.combInset) 110 if centerPerpendicular == None: 111 centerPerpendicular = centerEnd 112 elif centerEnd != None: 113 centerPerpendicular = 0.5 * (centerPerpendicular + centerEnd) 114 between = None 115 if centerPerpendicular == None: 116 between = center 117 if between == None: 118 centerSideWiddershins = center + centerPerpendicular 119 if euclidean.isPointInsideLoop(loop, centerSideWiddershins) == loopWiddershins: 120 between = centerSideWiddershins 121 if between == None: 122 centerSideClockwise = center - centerPerpendicular 123 if euclidean.isPointInsideLoop(loop, centerSideClockwise) == loopWiddershins: 124 between = centerSideClockwise 125 if between == None: 126 between = center 127 pathBetween.append(between) 128 return pathBetween
129
130 - def getPathsBetween(self, z, begin, end):
131 "Insert paths between the perimeter and the fill." 132 self.z = z 133 aroundBetweenPath = [] 134 points = [begin] 135 lineX = [] 136 switchX = [] 137 segment = euclidean.getNormalized(end - begin) 138 segmentYMirror = complex(segment.real, -segment.imag) 139 beginRotated = segmentYMirror * begin 140 endRotated = segmentYMirror * end 141 y = beginRotated.imag 142 143 for boundaryIndex in xrange(len(self.boundaries)): 144 boundary = self.boundaries[ boundaryIndex ] 145 boundaryRotated = euclidean.getPointsRoundZAxis(segmentYMirror, boundary) 146 euclidean.addXIntersectionIndexesFromLoopY(boundaryRotated, boundaryIndex, switchX, y) 147 switchX.sort() 148 maximumX = max(beginRotated.real, endRotated.real) 149 minimumX = min(beginRotated.real, endRotated.real) 150 for xIntersection in switchX: 151 if xIntersection.x > minimumX and xIntersection.x < maximumX: 152 point = segment * complex(xIntersection.x, y) 153 points.append(point) 154 lineX.append(xIntersection) 155 points.append(end) 156 lineXIndex = 0 157 while lineXIndex < len(lineX) - 1: 158 lineXFirst = lineX[lineXIndex] 159 lineXSecond = lineX[lineXIndex + 1] 160 loopFirst = self.boundaries[lineXFirst.index] 161 if lineXSecond.index == lineXFirst.index: 162 pathBetween = self.getPathBetween(loopFirst, points[lineXIndex : lineXIndex + 4]) 163 pathBetween = self.getSimplifiedAroundPath(points[lineXIndex], points[lineXIndex + 3], loopFirst, pathBetween) 164 aroundBetweenPath += pathBetween 165 lineXIndex += 2 166 else: 167 lineXIndex += 1 168 return aroundBetweenPath
169
170 - def getSimplifiedAroundPath(self, begin, end, loop, pathAround):
171 "Get the simplified path between the perimeter and the fill." 172 pathAround = self.getSimplifiedBeginPath(begin, loop, pathAround) 173 return self.getSimplifiedEndPath(end, loop, pathAround)
174
175 - def getSimplifiedBeginPath(self, begin, loop, pathAround):
176 "Get the simplified begin path between the perimeter and the fill." 177 if len(pathAround) < 2: 178 return pathAround 179 pathIndex = 0 180 while pathIndex < len(pathAround) - 1: 181 if not self.getIsAsFarAndNotIntersecting(begin, pathAround[pathIndex + 1]): 182 return pathAround[pathIndex :] 183 pathIndex += 1 184 return pathAround[-1 :]
185
186 - def getSimplifiedEndPath(self, end, loop, pathAround):
187 "Get the simplified end path between the perimeter and the fill." 188 if len(pathAround) < 2: 189 return pathAround 190 pathIndex = len(pathAround) - 1 191 while pathIndex > 0: 192 if not self.getIsAsFarAndNotIntersecting(end, pathAround[pathIndex - 1]): 193 return pathAround[: pathIndex + 1] 194 pathIndex -= 1 195 return pathAround[: 1]
196