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

Source Code for Module plugins.support

  1  """ 
  2  Support adds layers for supporting overhangs. 
  3  Extracted from the Skeinforge raft plugin.  
  4   
  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  from fabmetheus_utilities.geometry.solids import triangle_mesh 
 17  from fabmetheus_utilities.vector3 import Vector3 
 18  from entities import SupportPath 
 19  import logging 
 20  import math 
 21  import os 
 22   
 23  name = __name__ 
 24  logger = logging.getLogger(name) 
 25   
26 -def performAction(slicedModel):
27 "Add support layers." 28 if not config.getboolean(name, 'active'): 29 logger.info("%s plugin is not active", name.capitalize()) 30 return 31 SupportSkein(slicedModel).support()
32
33 -class SupportSkein:
34 'A class to support a skein of extrusions.'
35 - def __init__(self, slicedModel):
36 self.slicedModel = slicedModel 37 self.boundaryLayers = [] 38 self.supportLayers = [] 39 40 self.debug = config.getboolean(name, 'debug') 41 self.supportLocation = config.get(name, 'location') 42 self.supportMinimumAngle = config.getfloat(name, 'min.angle') 43 self.minimumSupportRatio = math.tan(math.radians(self.supportMinimumAngle)) 44 self.supportCrossHatch = config.getboolean(name, 'crosshatch') 45 self.supportFeedRate = config.getfloat('speed', 'feed.rate.support') 46 self.supportFlowRateRatio = config.getfloat('speed', 'flow.rate.support.ratio') 47 48 self.raftAdditionalMarginOverLengthPercent = config.getfloat(name, 'extension.percent') 49 self.raftMargin = config.getfloat(name, 'extension.distance') 50 self.infillOverhangOverExtrusionWidth = config.getfloat(name, 'infill.overhang.ratio') 51 52 self.supportStartFile = config.get(name, 'support.start.file') 53 self.supportEndFile = config.get(name, 'support.end.file') 54 self.absoluteSupportStartFilePath = os.path.join('alterations', self.supportStartFile) 55 self.absoluteSupportEndFilePath = os.path.join('alterations', self.supportEndFile) 56 self.supportStartLines = archive.getTextLines(archive.getFileText(self.absoluteSupportStartFilePath, printWarning=False)) 57 self.supportEndLines = archive.getTextLines(archive.getFileText(self.absoluteSupportEndFilePath, printWarning=False)) 58 59 self.extrusionWidth = config.getfloat('carve', 'extrusion.width') 60 self.supportGapOverPerimeterExtrusionWidth = config.getfloat(name, 'gap.over.perimeter.extrusion.width.ratio') 61 self.supportOutset = self.extrusionWidth * self.supportGapOverPerimeterExtrusionWidth 62 63 self.interfaceInfillDensity = config.getfloat(name, 'interface.infill.density') 64 self.interfaceLayerThicknessRatio = config.getfloat(name, 'interface.layer.thickness.ratio') 65 interfaceExtrusionWidth = self.extrusionWidth * self.interfaceLayerThicknessRatio 66 self.interfaceStep = interfaceExtrusionWidth / self.interfaceInfillDensity 67 68 self.cornerMinimum = self.slicedModel.carvingCornerMinimum 69 self.cornerMaximum = self.slicedModel.carvingCornerMaximum 70 self.cornerMinimumComplex = self.cornerMinimum.dropAxis() 71 self.cornerMaximumComplex = self.cornerMaximum.dropAxis()
72
73 - def support(self):
74 'Add support layers to sliced model' 75 76 for layer in self.slicedModel.layers.values(): 77 perimeters = [] 78 layer.getPerimeterPaths(perimeters) 79 boundaryLayer = euclidean.LoopLayer(layer.z) # TODO refactor out 80 for perimeter in perimeters: 81 boundaryLoop = [] 82 boundaryLayer.loops.append(boundaryLoop) 83 for boundaryPoint in perimeter.boundaryPoints: 84 boundaryLoop.append(boundaryPoint.dropAxis()) 85 self.boundaryLayers.append(boundaryLayer) 86 87 if len(self.boundaryLayers) < 0: 88 logger.error('This should never happen, there are no boundary layers in support') 89 return 90 91 originalExtent = self.cornerMaximumComplex - self.cornerMinimumComplex 92 self.raftOutsetRadius = self.raftMargin + (self.raftAdditionalMarginOverLengthPercent * 0.01) * max(originalExtent.real, originalExtent.imag)#todo ACT +0.1 93 self.setBoundaryLayers() 94 95 for layer in self.slicedModel.layers.values(): 96 endpoints = self.getSupportEndpoints(layer.index) 97 if len(endpoints) > 0: 98 self.addSupportLayer(endpoints, layer)
99
100 - def getSupportEndpoints(self, layerIndex):
101 'Get the support layer segments.' 102 if len(self.supportLayers) <= layerIndex: 103 return [] 104 supportSegmentTable = self.supportLayers[layerIndex].supportSegmentTable 105 if layerIndex % 2 == 1 and self.supportCrossHatch: 106 return getVerticalEndpoints(supportSegmentTable, self.interfaceStep, 0.1 * self.extrusionWidth, self.interfaceStep) 107 return euclidean.getEndpointsFromSegmentTable(supportSegmentTable)
108
109 - def setBoundaryLayers(self):
110 'Set the boundary layers.' 111 112 if len(self.boundaryLayers) < 2: 113 return 114 115 if self.supportLocation == 'EmptyLayersOnly': 116 supportLayer = SupportLayer([]) 117 self.supportLayers.append(supportLayer) 118 for boundaryLayerIndex in xrange(1, len(self.boundaryLayers) - 1): 119 self.addEmptyLayerSupport(boundaryLayerIndex) 120 self.truncateSupportSegmentTables() 121 self.addSegmentTablesToSupportLayers() 122 return 123 124 for boundaryLayer in self.boundaryLayers: 125 # thresholdRadius of 0.8 is needed to avoid the ripple inset bug http://hydraraptor.blogspot.com/2010/12/crackers.html 126 supportLoops = intercircle.getInsetSeparateLoopsFromLoops(-self.supportOutset, boundaryLayer.loops, 0.8) 127 supportLayer = SupportLayer(supportLoops) 128 self.supportLayers.append(supportLayer) 129 130 131 for supportLayerIndex in xrange(len(self.supportLayers) -1 ): 132 self.addSupportSegmentTable(supportLayerIndex) 133 134 self.truncateSupportSegmentTables() 135 136 for supportLayerIndex in xrange(len(self.supportLayers) - 1): 137 boundaryLoops = self.boundaryLayers[supportLayerIndex].loops 138 self.extendXIntersections(boundaryLoops, self.supportOutset, self.supportLayers[supportLayerIndex].xIntersectionsTable) 139 140 for supportLayer in self.supportLayers: 141 self.addToFillXIntersectionIndexTables(supportLayer) 142 143 if self.supportLocation == 'ExteriorOnly': 144 for supportLayerIndex in xrange(1, len(self.supportLayers)): 145 self.subtractJoinedFill(supportLayerIndex) 146 147 for supportLayer in self.supportLayers: 148 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable) 149 150 for supportLayerIndex in xrange(len(self.supportLayers) - 2, -1, -1): 151 xIntersectionsTable = self.supportLayers[supportLayerIndex].xIntersectionsTable 152 aboveXIntersectionsTable = self.supportLayers[supportLayerIndex + 1].xIntersectionsTable 153 euclidean.joinXIntersectionsTables(aboveXIntersectionsTable, xIntersectionsTable) 154 155 for supportLayerIndex in xrange(len(self.supportLayers)): 156 supportLayer = self.supportLayers[supportLayerIndex] 157 self.extendXIntersections(supportLayer.supportLoops, self.raftOutsetRadius, supportLayer.xIntersectionsTable) 158 159 for supportLayer in self.supportLayers: 160 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable) 161 162 self.addSegmentTablesToSupportLayers()
163
164 - def addSupportLayer(self, endpoints, layer):
165 'Add support layer before the object layer.' 166 167 # TODO refactor to parent object - avoid duplication 168 for line in self.supportStartLines: 169 layer.preSupportGcodeCommands.append(line) 170 171 aroundPixelTable = {} 172 aroundWidth = 0.25 * self.interfaceStep 173 boundaryLoops = self.boundaryLayers[layer.index].loops 174 halfSupportOutset = 0.5 * self.supportOutset 175 aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset) 176 for aroundBoundaryLoop in aroundBoundaryLoops: 177 euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth) 178 paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, aroundWidth) 179 feedRateMinuteMultiplied = self.supportFeedRate * 60 180 supportFlowRateMultiplied = self.supportFlowRateRatio * self.supportFeedRate 181 182 for path in paths: 183 supportPath = SupportPath(layer.z, self.slicedModel.runtimeParameters) 184 supportPath.addPath(path) 185 layer.supportPaths.append(supportPath) 186 187 layer.postSupportGcodeCommands.extend(self.supportEndLines)
188
189 - def addSupportSegmentTable(self, layerIndex):
190 'Add support segments from the boundary layers.' 191 aboveLayer = self.boundaryLayers[ layerIndex + 1 ] 192 aboveLoops = aboveLayer.loops 193 supportLayer = self.supportLayers[layerIndex] 194 195 if len(aboveLoops) < 1: 196 return 197 198 boundaryLayer = self.boundaryLayers[layerIndex] 199 rise = aboveLayer.z - boundaryLayer.z 200 201 outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops(-self.minimumSupportRatio * rise, boundaryLayer.loops) 202 numberOfSubSteps = 4 203 subStepSize = self.interfaceStep / float(numberOfSubSteps) 204 aboveIntersectionsTable = {} 205 euclidean.addXIntersectionsFromLoopsForTable(aboveLoops, aboveIntersectionsTable, subStepSize) 206 outsetIntersectionsTable = {} 207 euclidean.addXIntersectionsFromLoopsForTable(outsetSupportLoops, outsetIntersectionsTable, subStepSize) 208 euclidean.subtractXIntersectionsTable(aboveIntersectionsTable, outsetIntersectionsTable) 209 for aboveIntersectionsTableKey in aboveIntersectionsTable.keys(): 210 supportIntersectionsTableKey = int(round(float(aboveIntersectionsTableKey) / numberOfSubSteps)) 211 xIntersectionIndexList = [] 212 if supportIntersectionsTableKey in supportLayer.xIntersectionsTable: 213 euclidean.addXIntersectionIndexesFromXIntersections(0, xIntersectionIndexList, supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ]) 214 euclidean.addXIntersectionIndexesFromXIntersections(1, xIntersectionIndexList, aboveIntersectionsTable[ aboveIntersectionsTableKey ]) 215 supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] = euclidean.getJoinOfXIntersectionIndexes(xIntersectionIndexList)
216
218 'Add segment tables to the support layers.' 219 for supportLayer in self.supportLayers: 220 supportLayer.supportSegmentTable = {} 221 xIntersectionsTable = supportLayer.xIntersectionsTable 222 for xIntersectionsTableKey in xIntersectionsTable: 223 y = xIntersectionsTableKey * self.interfaceStep 224 supportLayer.supportSegmentTable[ xIntersectionsTableKey ] = euclidean.getSegmentsFromXIntersections(xIntersectionsTable[ xIntersectionsTableKey ], y)
225
226 - def addEmptyLayerSupport(self, boundaryLayerIndex):
227 'Add support material to a layer if it is empty.' 228 supportLayer = SupportLayer([]) 229 self.supportLayers.append(supportLayer) 230 if len(self.boundaryLayers[ boundaryLayerIndex ].loops) > 0: 231 return 232 aboveXIntersectionsTable = {} 233 euclidean.addXIntersectionsFromLoopsForTable(self.getInsetLoopsAbove(boundaryLayerIndex), aboveXIntersectionsTable, self.interfaceStep) 234 belowXIntersectionsTable = {} 235 euclidean.addXIntersectionsFromLoopsForTable(self.getInsetLoopsBelow(boundaryLayerIndex), belowXIntersectionsTable, self.interfaceStep) 236 supportLayer.xIntersectionsTable = euclidean.getIntersectionOfXIntersectionsTables([ aboveXIntersectionsTable, belowXIntersectionsTable ])
237
238 - def subtractJoinedFill(self, supportLayerIndex):
239 'Join the fill then subtract it from the support layer table.' 240 supportLayer = self.supportLayers[supportLayerIndex] 241 fillXIntersectionsTable = supportLayer.fillXIntersectionsTable 242 belowFillXIntersectionsTable = self.supportLayers[ supportLayerIndex - 1 ].fillXIntersectionsTable 243 euclidean.joinXIntersectionsTables(belowFillXIntersectionsTable, supportLayer.fillXIntersectionsTable) 244 euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable)
245
247 'Truncate the support segments after the last support segment which contains elements.' 248 for supportLayerIndex in xrange(len(self.supportLayers) - 1, -1, -1): 249 if len(self.supportLayers[supportLayerIndex].xIntersectionsTable) > 0: 250 self.supportLayers = self.supportLayers[ : supportLayerIndex + 1 ] 251 return 252 self.supportLayers = []
253
254 - def addToFillXIntersectionIndexTables(self, supportLayer):
255 'Add fill segments from the boundary layers.' 256 supportLoops = supportLayer.supportLoops 257 supportLayer.fillXIntersectionsTable = {} 258 if len(supportLoops) < 1: 259 return 260 euclidean.addXIntersectionsFromLoopsForTable(supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep)
261
262 - def extendXIntersections(self, loops, radius, xIntersectionsTable):
263 'Extend the support segments.' 264 xIntersectionsTableKeys = xIntersectionsTable.keys() 265 for xIntersectionsTableKey in xIntersectionsTableKeys: 266 lineSegments = euclidean.getSegmentsFromXIntersections(xIntersectionsTable[ xIntersectionsTableKey ], xIntersectionsTableKey) 267 xIntersectionIndexList = [] 268 loopXIntersections = [] 269 euclidean.addXIntersectionsFromLoops(loops, loopXIntersections, xIntersectionsTableKey) 270 for lineSegmentIndex in xrange(len(lineSegments)): 271 lineSegment = lineSegments[ lineSegmentIndex ] 272 extendedLineSegment = getExtendedLineSegment(radius, lineSegment, loopXIntersections) 273 if extendedLineSegment != None: 274 euclidean.addXIntersectionIndexesFromSegment(lineSegmentIndex, extendedLineSegment, xIntersectionIndexList) 275 xIntersections = euclidean.getJoinOfXIntersectionIndexes(xIntersectionIndexList) 276 if len(xIntersections) > 0: 277 xIntersectionsTable[ xIntersectionsTableKey ] = xIntersections 278 else: 279 del xIntersectionsTable[ xIntersectionsTableKey ]
280
281 -def getCrossHatchPointLine(crossHatchPointLineTable, y):
282 'Get the cross hatch point line.' 283 if not crossHatchPointLineTable.has_key(y): 284 crossHatchPointLineTable[ y ] = {} 285 return crossHatchPointLineTable[ y ]
286
287 -def getEndpointsFromYIntersections(x, yIntersections):
288 'Get endpoints from the y intersections.' 289 endpoints = [] 290 for yIntersectionIndex in xrange(0, len(yIntersections), 2): 291 firstY = yIntersections[ yIntersectionIndex ] 292 secondY = yIntersections[ yIntersectionIndex + 1 ] 293 if firstY != secondY: 294 firstComplex = complex(x, firstY) 295 secondComplex = complex(x, secondY) 296 endpointFirst = euclidean.Endpoint() 297 endpointSecond = euclidean.Endpoint().getFromOtherPoint(endpointFirst, secondComplex) 298 endpointFirst.getFromOtherPoint(endpointSecond, firstComplex) 299 endpoints.append(endpointFirst) 300 endpoints.append(endpointSecond) 301 return endpoints
302
303 -def getExtendedLineSegment(extensionDistance, lineSegment, loopXIntersections):
304 'Get extended line segment.' 305 pointBegin = lineSegment[0].point 306 pointEnd = lineSegment[1].point 307 segment = pointEnd - pointBegin 308 segmentLength = abs(segment) 309 if segmentLength <= 0.0: 310 logger.error('This should never happen in getExtendedLineSegment in raft, the segment should have a length greater than zero. %s', lineSegment) 311 return None 312 segmentExtend = segment * extensionDistance / segmentLength 313 lineSegment[0].point -= segmentExtend 314 lineSegment[1].point += segmentExtend 315 for loopXIntersection in loopXIntersections: 316 setExtendedPoint(lineSegment[0], pointBegin, loopXIntersection) 317 setExtendedPoint(lineSegment[1], pointEnd, loopXIntersection) 318 return lineSegment
319
320 -def getLoopsBySegmentsDictionary(segmentsDictionary, width):
321 'Get loops from a horizontal segments dictionary.' 322 points = [] 323 for endpoint in getVerticalEndpoints(segmentsDictionary, width, 0.1 * width, width): 324 points.append(endpoint.point) 325 for endpoint in euclidean.getEndpointsFromSegmentTable(segmentsDictionary): 326 points.append(endpoint.point) 327 return triangle_mesh.getDescendingAreaOrientedLoops(points, points, width + width)
328
329 -def getVerticalEndpoints(horizontalSegmentsTable, horizontalStep, verticalOverhang, verticalStep):
330 'Get vertical endpoints.' 331 interfaceSegmentsTableKeys = horizontalSegmentsTable.keys() 332 interfaceSegmentsTableKeys.sort() 333 verticalTableTable = {} 334 for interfaceSegmentsTableKey in interfaceSegmentsTableKeys: 335 interfaceSegments = horizontalSegmentsTable[interfaceSegmentsTableKey] 336 for interfaceSegment in interfaceSegments: 337 begin = int(round(interfaceSegment[0].point.real / verticalStep)) 338 end = int(round(interfaceSegment[1].point.real / verticalStep)) 339 for stepIndex in xrange(begin, end + 1): 340 if stepIndex not in verticalTableTable: 341 verticalTableTable[stepIndex] = {} 342 verticalTableTable[stepIndex][interfaceSegmentsTableKey] = None 343 verticalTableTableKeys = verticalTableTable.keys() 344 verticalTableTableKeys.sort() 345 verticalEndpoints = [] 346 for verticalTableTableKey in verticalTableTableKeys: 347 verticalTable = verticalTableTable[verticalTableTableKey] 348 verticalTableKeys = verticalTable.keys() 349 verticalTableKeys.sort() 350 xIntersections = [] 351 for verticalTableKey in verticalTableKeys: 352 y = verticalTableKey * horizontalStep 353 if verticalTableKey - 1 not in verticalTableKeys: 354 xIntersections.append(y - verticalOverhang) 355 if verticalTableKey + 1 not in verticalTableKeys: 356 xIntersections.append(y + verticalOverhang) 357 for segment in euclidean.getSegmentsFromXIntersections(xIntersections, verticalTableTableKey * verticalStep): 358 for endpoint in segment: 359 endpoint.point = complex(endpoint.point.imag, endpoint.point.real) 360 verticalEndpoints.append(endpoint) 361 return verticalEndpoints
362
363 -def setExtendedPoint(lineSegmentEnd, pointOriginal, x):
364 'Set the point in the extended line segment.' 365 if x > min(lineSegmentEnd.point.real, pointOriginal.real) and x < max(lineSegmentEnd.point.real, pointOriginal.real): 366 lineSegmentEnd.point = complex(x, pointOriginal.imag)
367
368 -class SupportLayer:
369 'Support loops with segment tables.'
370 - def __init__(self, supportLoops):
371 self.supportLoops = supportLoops 372 self.supportSegmentTable = {} 373 self.xIntersectionsTable = {}
374
375 - def __repr__(self):
376 'Get the string representation of this loop layer.' 377 return '%s' % (self.supportLoops)
378