Module LineFillStrategy
[hide private]
[frames] | no frames]

Source Code for Module LineFillStrategy

  1  """ 
  2  Fills a layer using a line pattern. 
  3   
  4  Credits: 
  5      Original Author: Enrique Perez (http://skeinforge.com) 
  6      Contributors: Please see the documentation in Skeinforge  
  7      Modifed as SFACT: Ahmet Cem Turan (github.com/ahmetcemturan/SFACT)     
  8   
  9  License:  
 10      GNU Affero General Public License http://www.gnu.org/licenses/agpl.html 
 11  """ 
 12   
 13  from config import config 
 14  from fabmetheus_utilities import archive, euclidean, intercircle 
 15  from fabmetheus_utilities.vector3 import Vector3 
 16  import logging 
 17  import math 
 18  import sys 
 19  from utilities import memory_tracker 
 20   
21 -def getStrategy(slicedModel):
22 '''Returns an instance of the strategy''' 23 return LineFillStrategy(slicedModel)
24
25 -class LineFillStrategy:
26 - def __init__(self, slicedModel):
27 # TODO - remove or reduce dependency on slicedModel 28 self.slicedModel = slicedModel 29 30 self.infillSolidity = config.getfloat('fill', 'infill.solidity.ratio') 31 self.infillWidthOverThickness = config.getfloat('fill', 'extrusion.lines.extra.spacer.scaler') 32 self.infillPerimeterOverlap = config.getfloat('fill', 'infill.overlap.over.perimeter.scaler') 33 self.extraShellsAlternatingSolidLayer = config.getint('fill', 'shells.alternating.solid') 34 self.extraShellsBase = config.getint('fill', 'shells.base') 35 self.extraShellsSparseLayer = config.getint('fill', 'shells.sparse') 36 self.solidSurfaceThickness = config.getint('fill', 'fully.filled.layers') 37 self.doubleSolidSurfaceThickness = self.solidSurfaceThickness + self.solidSurfaceThickness 38 self.startFromChoice = config.get('fill', 'extrusion.sequence.start.layer') 39 self.threadSequenceChoice = config.get('fill', 'extrusion.sequence.print.order') 40 self.threadSequence = self.threadSequenceChoice.split(",") 41 self.diaphragmPeriod = config.getint('fill', 'diaphragm.every.n.layers') 42 self.diaphragmThickness = config.getint('fill', 'diaphragm.thickness') 43 self.infillBeginRotation = math.radians(config.getfloat('fill', 'infill.rotation.begin')) 44 self.infillBeginRotationRepeat = config.getint('fill', 'infill.rotation.repeat') 45 self.infillOddLayerExtraRotation = math.radians(config.getfloat('fill', 'infill.rotation.odd.layer')) 46 self.bridgeWidthMultiplier = config.getfloat('inset', 'bridge.width.multiplier.ratio') 47 self.extrusionWidth = config.getfloat('carve', 'extrusion.width') 48 self.infillWidth = self.extrusionWidth * self.infillWidthOverThickness * (0.7853) 49 self.betweenWidth = self.extrusionWidth * self.infillWidthOverThickness * (0.7853) 50 self.previousExtraShells = -1 51 self.oldOrderedLocation = None
52
53 - def fill(self, layer):
54 'Add fill to the carve layer.' 55 layerIndex = layer.index 56 alreadyFilledArounds = [] 57 pixelTable = {} 58 arounds = [] 59 betweenWidth = self.extrusionWidth / 1.7594801994 # this really sucks I cant find hwe#(self.repository.infillWidthOverThickness.value * self.extrusionWidth *(0.7853))/1.5 #- 0.0866#todo todo TODO *0.5 is the distance between the outer loops.. 60 self.layerExtrusionWidth = self.infillWidth # spacing between fill lines 61 layerFillInset = self.infillWidth # the distance between perimeter incl loops and the fill pattern 62 63 layerRotation = self.getLayerRotation(layerIndex, layer) 64 reverseRotation = complex(layerRotation.real, -layerRotation.imag) 65 surroundingCarves = [] 66 layerRemainder = layerIndex % self.diaphragmPeriod 67 extraShells = self.extraShellsSparseLayer 68 69 if layerRemainder >= self.diaphragmThickness and layer.bridgeRotation == None: 70 for surroundingIndex in xrange(1, self.solidSurfaceThickness + 1): 71 self.addRotatedCarve(layerIndex, -surroundingIndex, reverseRotation, surroundingCarves) 72 self.addRotatedCarve(layerIndex, surroundingIndex, reverseRotation, surroundingCarves) 73 74 if len(surroundingCarves) < self.doubleSolidSurfaceThickness: 75 extraShells = self.extraShellsAlternatingSolidLayer 76 if self.previousExtraShells != self.extraShellsBase: 77 extraShells = self.extraShellsBase 78 79 if layer.bridgeRotation != None: 80 extraShells = 0 81 betweenWidth *= self.bridgeWidthMultiplier#/0.7853 #todo check what is better with or without the normalizer 82 self.layerExtrusionWidth *= self.bridgeWidthMultiplier 83 layerFillInset *= self.bridgeWidthMultiplier 84 85 aroundInset = 0.25 * self.layerExtrusionWidth 86 aroundWidth = 0.25 * self.layerExtrusionWidth 87 self.previousExtraShells = extraShells 88 gridPointInsetX = 0.5 * layerFillInset 89 doubleExtrusionWidth = 2.0 * self.layerExtrusionWidth 90 endpoints = [] 91 infillPaths = [] 92 layerInfillSolidity = self.infillSolidity 93 94 self.isDoubleJunction = True 95 self.isJunctionWide = True 96 rotatedLoops = [] 97 98 nestedRings = layer.nestedRings 99 100 createFillForSurroundings(nestedRings, betweenWidth, False) 101 102 for extraShellIndex in xrange(extraShells): 103 createFillForSurroundings(nestedRings, self.layerExtrusionWidth, True) 104 105 fillLoops = euclidean.getFillOfSurroundings(nestedRings, None) 106 107 slightlyGreaterThanFill = 1.001 * layerFillInset #todo was 1.01 ACT 0.95 How much the parallel fill is filled 108 109 for loop in fillLoops: 110 alreadyFilledLoop = [] 111 alreadyFilledArounds.append(alreadyFilledLoop) 112 planeRotatedPerimeter = euclidean.getPointsRoundZAxis(reverseRotation, loop) 113 rotatedLoops.append(planeRotatedPerimeter) 114 centers = intercircle.getCentersFromLoop(planeRotatedPerimeter, slightlyGreaterThanFill) 115 euclidean.addLoopToPixelTable(planeRotatedPerimeter, pixelTable, aroundWidth) 116 for center in centers: 117 alreadyFilledInset = intercircle.getSimplifiedInsetFromClockwiseLoop(center, layerFillInset) 118 if intercircle.isLargeSameDirection(alreadyFilledInset, center, layerFillInset): 119 alreadyFilledLoop.append(alreadyFilledInset) 120 around = intercircle.getSimplifiedInsetFromClockwiseLoop(center, aroundInset) 121 if euclidean.isPathInsideLoop(planeRotatedPerimeter, around) == euclidean.isWiddershins(planeRotatedPerimeter): 122 around.reverse() 123 arounds.append(around) 124 euclidean.addLoopToPixelTable(around, pixelTable, aroundWidth) 125 126 if len(arounds) < 1: 127 self.addThreadsBridgeLayer(layerIndex, nestedRings, layer) 128 return 129 130 back = euclidean.getBackOfLoops(arounds) 131 front = euclidean.getFrontOfLoops(arounds) 132 front = math.ceil(front / self.layerExtrusionWidth) * self.layerExtrusionWidth 133 fillWidth = back - front 134 numberOfLines = int(math.ceil(fillWidth / self.layerExtrusionWidth)) 135 self.frontOverWidth = 0.0 136 self.horizontalSegmentLists = euclidean.getHorizontalSegmentListsFromLoopLists(alreadyFilledArounds, front, numberOfLines, rotatedLoops, self.layerExtrusionWidth) 137 self.surroundingXIntersectionLists = [] 138 self.yList = [] 139 removedEndpoints = [] 140 141 if len(surroundingCarves) >= self.doubleSolidSurfaceThickness: 142 xIntersectionIndexLists = [] 143 self.frontOverWidth = euclidean.getFrontOverWidthAddXListYList(front, surroundingCarves, numberOfLines, xIntersectionIndexLists, self.layerExtrusionWidth, self.yList) 144 for fillLine in xrange(len(self.horizontalSegmentLists)): 145 xIntersectionIndexList = xIntersectionIndexLists[fillLine] 146 surroundingXIntersections = euclidean.getIntersectionOfXIntersectionIndexes(self.doubleSolidSurfaceThickness, xIntersectionIndexList) 147 self.surroundingXIntersectionLists.append(surroundingXIntersections) 148 addSparseEndpoints(doubleExtrusionWidth, endpoints, fillLine, self.horizontalSegmentLists, layerInfillSolidity, removedEndpoints, self.solidSurfaceThickness, surroundingXIntersections) 149 else: 150 for fillLine in xrange(len(self.horizontalSegmentLists)): 151 addSparseEndpoints(doubleExtrusionWidth, endpoints, fillLine, self.horizontalSegmentLists, layerInfillSolidity, removedEndpoints, self.solidSurfaceThickness, None) 152 153 paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.layerExtrusionWidth, pixelTable, aroundWidth) 154 155 oldRemovedEndpointLength = len(removedEndpoints) + 1 156 157 while oldRemovedEndpointLength - len(removedEndpoints) > 0: 158 oldRemovedEndpointLength = len(removedEndpoints) 159 removeEndpoints(pixelTable, self.layerExtrusionWidth, paths, removedEndpoints, aroundWidth) 160 161 paths = euclidean.getConnectedPaths(paths, pixelTable, aroundWidth) 162 163 for path in paths: 164 addPathToInfillPaths(self.layerExtrusionWidth, infillPaths, path, layerRotation) 165 166 for nestedRing in nestedRings: 167 nestedRing.transferPaths(infillPaths) 168 169 self.addThreadsBridgeLayer(layerIndex, nestedRings, layer)
170
171 - def addRotatedCarve(self, currentLayer, layerDelta, reverseRotation, surroundingCarves):
172 'Add a rotated carve to the surrounding carves.' 173 layerIndex = currentLayer + layerDelta 174 if layerIndex < 0 or layerIndex >= len(self.slicedModel.layers): 175 return 176 177 layer = self.slicedModel.layers.values()[layerIndex] 178 179 nestedRings = layer.nestedRings 180 rotatedCarve = [] 181 for nestedRing in nestedRings: 182 planeRotatedLoop = euclidean.getPointsRoundZAxis(reverseRotation, nestedRing.getXYBoundaries()) 183 rotatedCarve.append(planeRotatedLoop) 184 outsetRadius = float(abs(layerDelta)) * self.extrusionWidth #todo investigate was float(abs(layerDelta)) * self.layerThickness 185 rotatedCarve = intercircle.getInsetSeparateLoopsFromLoops(-outsetRadius, rotatedCarve) 186 surroundingCarves.append(rotatedCarve)
187
188 - def addThreadsBridgeLayer(self, layerIndex, nestedRings, rotatedLayer):
189 'Add the threads, add the bridge end & the layer end tag.' 190 if self.oldOrderedLocation == None or self.startFromChoice == "LowerLeft": 191 self.oldOrderedLocation = getLowerLeftCorner(nestedRings) 192 extrusionHalfWidth = 0.5 * self.layerExtrusionWidth 193 threadSequence = self.threadSequence 194 if layerIndex < 1: 195 threadSequence = ['perimeter', 'loops', 'infill'] 196 197 for nestedRing in nestedRings: 198 nestedRing.addToThreads(extrusionHalfWidth, self.oldOrderedLocation, threadSequence)
199
200 - def getLayerRotation(self, layerIndex, rotatedLayer):
201 'Get the layer rotation.' 202 rotation = rotatedLayer.bridgeRotation 203 if rotation != None: 204 return rotation 205 infillOddLayerRotationMultiplier = float(layerIndex % (self.infillBeginRotationRepeat + 1) == self.infillBeginRotationRepeat) 206 layerAngle = self.infillBeginRotation + infillOddLayerRotationMultiplier * self.infillOddLayerExtraRotation 207 return euclidean.getWiddershinsUnitPolar(layerAngle)
208
209 -def addPathToInfillPaths(infillWidth, infillPaths, path, rotationPlaneAngle):
210 'Add simplified path to fill.' 211 simplifiedPath = euclidean.getSimplifiedPath(path, infillWidth) 212 if len(simplifiedPath) < 2: 213 return 214 planeRotated = euclidean.getPointsRoundZAxis(rotationPlaneAngle, simplifiedPath) 215 infillPaths.append(planeRotated)
216
217 -def addPointOnPath(path, pathIndex, pixelTable, point, pointIndex, width):
218 'Add a point to a path and the pixel table.' 219 pointIndexMinusOne = pointIndex - 1 220 if pointIndex < len(path) and pointIndexMinusOne >= 0: 221 segmentTable = {} 222 begin = path[ pointIndexMinusOne ] 223 end = path[pointIndex] 224 euclidean.addValueSegmentToPixelTable(begin, end, segmentTable, pathIndex, width) 225 euclidean.removePixelTableFromPixelTable(segmentTable, pixelTable) 226 if pointIndexMinusOne >= 0: 227 begin = path[ pointIndexMinusOne ] 228 euclidean.addValueSegmentToPixelTable(begin, point, pixelTable, pathIndex, width) 229 if pointIndex < len(path): 230 end = path[pointIndex] 231 euclidean.addValueSegmentToPixelTable(point, end, pixelTable, pathIndex, width) 232 path.insert(pointIndex, point)
233
234 -def addPointOnPathIfFree(path, pathIndex, pixelTable, point, pointIndex, width):
235 'Add the closest point to a path, if the point added to a path is free.' 236 if isAddedPointOnPathFree(path, pixelTable, point, pointIndex, width): 237 addPointOnPath(path, pathIndex, pixelTable, point, pointIndex, width)
238
239 -def addSparseEndpoints(doubleExtrusionWidth, endpoints, fillLine, horizontalSegmentLists, infillSolidity, removedEndpoints, solidSurfaceThickness, surroundingXIntersections):
240 'Add sparse endpoints.' 241 horizontalEndpoints = horizontalSegmentLists[fillLine] 242 for segment in horizontalEndpoints: 243 addSparseEndpointsFromSegment(doubleExtrusionWidth, endpoints, fillLine, horizontalSegmentLists, infillSolidity, removedEndpoints, segment, solidSurfaceThickness, surroundingXIntersections)
244
245 -def addSparseEndpointsFromSegment(doubleExtrusionWidth, endpoints, fillLine, horizontalSegmentLists, infillSolidity, removedEndpoints, segment, solidSurfaceThickness, surroundingXIntersections):
246 'Add sparse endpoints from a segment.' 247 endpointFirstPoint = segment[0].point 248 endpointSecondPoint = segment[1].point 249 if surroundingXIntersections == None: 250 endpoints += segment 251 return 252 if infillSolidity > 0.0: 253 if fillLine < 1 or fillLine >= len(horizontalSegmentLists) - 1: 254 endpoints += segment 255 return 256 if int(round(round(fillLine * infillSolidity) / infillSolidity)) == fillLine: 257 endpoints += segment 258 return 259 if abs(endpointFirstPoint - endpointSecondPoint) < doubleExtrusionWidth: 260 endpoints += segment 261 return 262 if not isSegmentAround(horizontalSegmentLists[ fillLine - 1 ], segment): 263 endpoints += segment 264 return 265 if not isSegmentAround(horizontalSegmentLists[ fillLine + 1 ], segment): 266 endpoints += segment 267 return 268 if solidSurfaceThickness == 0: 269 removedEndpoints += segment 270 return 271 if isSegmentCompletelyInAnIntersection(segment, surroundingXIntersections): 272 removedEndpoints += segment 273 return 274 endpoints += segment
275
276 -def createExtraFillLoops(nestedRing, radius, shouldExtraLoopsBeAdded):
277 'Create extra fill loops.' 278 for innerNestedRing in nestedRing.innerNestedRings: 279 createFillForSurroundings(innerNestedRing.innerNestedRings, radius, shouldExtraLoopsBeAdded) 280 281 loopsToBeFilled = nestedRing.getLoopsToBeFilled() 282 allFillLoops = getExtraFillLoops(loopsToBeFilled , radius) 283 284 if len(allFillLoops) < 1: 285 return 286 if shouldExtraLoopsBeAdded: 287 nestedRing.extraLoops += allFillLoops 288 nestedRing.penultimateFillLoops = nestedRing.lastFillLoops 289 nestedRing.lastFillLoops = allFillLoops
290
291 -def createFillForSurroundings(nestedRings, radius, shouldExtraLoopsBeAdded):
292 'Create extra fill loops for surrounding loops.' 293 for nestedRing in nestedRings: 294 createExtraFillLoops(nestedRing, radius, shouldExtraLoopsBeAdded)
295
296 -def getAdditionalLength(path, point, pointIndex):
297 'Get the additional length added by inserting a point into a path.' 298 if pointIndex == 0: 299 return abs(point - path[0]) 300 if pointIndex == len(path): 301 return abs(point - path[-1]) 302 return abs(point - path[pointIndex - 1]) + abs(point - path[pointIndex]) - abs(path[pointIndex] - path[pointIndex - 1])
303
304 -def getExtraFillLoops(loops, radius):
305 'Get extra loops between inside and outside loops. Extra perimeters' 306 greaterThanRadius = radius / 0.7853 #todo was *1.4 ACT (radius /0.7853) how much the tight spots are covered by the extra loops 307 extraFillLoops = [] 308 centers = intercircle.getCentersFromPoints(intercircle.getPointsFromLoops(loops, greaterThanRadius), greaterThanRadius) 309 for center in centers: 310 inset = intercircle.getSimplifiedInsetFromClockwiseLoop(center, radius) 311 if intercircle.isLargeSameDirection(inset, center, radius): 312 if euclidean.getIsInFilledRegion(loops, euclidean.getLeftPoint(inset)): 313 inset.reverse() 314 extraFillLoops.append(inset) 315 return extraFillLoops
316
317 -def getLowerLeftCorner(nestedRings):
318 'Get the lower left corner from the nestedRings.' 319 lowerLeftCorner = Vector3() 320 lowestRealPlusImaginary = 987654321.0 321 for nestedRing in nestedRings: 322 for point in nestedRing.getXYBoundaries(): 323 realPlusImaginary = point.real + point.imag 324 if realPlusImaginary < lowestRealPlusImaginary: 325 lowestRealPlusImaginary = realPlusImaginary 326 lowerLeftCorner.setToXYZ(point.real, point.imag, nestedRing.z) 327 return lowerLeftCorner
328
329 -def getWithLeastLength(path, point):
330 'Insert a point into a path, at the index at which the path would be shortest.' 331 if len(path) < 1: 332 return 0 333 shortestPointIndex = None 334 shortestAdditionalLength = 999999999987654321.0 335 for pointIndex in xrange(len(path) + 1): 336 additionalLength = getAdditionalLength(path, point, pointIndex) 337 if additionalLength < shortestAdditionalLength: 338 shortestAdditionalLength = additionalLength 339 shortestPointIndex = pointIndex 340 return shortestPointIndex
341
342 -def isAddedPointOnPathFree(path, pixelTable, point, pointIndex, width):
343 'Determine if the point added to a path is intersecting the pixel table or the path.' 344 if pointIndex > 0 and pointIndex < len(path): 345 if isSharpCorner((path[pointIndex - 1]), point, (path[pointIndex])): 346 return False 347 pointIndexMinusOne = pointIndex - 1 348 if pointIndexMinusOne >= 0: 349 maskTable = {} 350 begin = path[ pointIndexMinusOne ] 351 if pointIndex < len(path): 352 end = path[pointIndex] 353 euclidean.addValueSegmentToPixelTable(begin, end, maskTable, None, width) 354 segmentTable = {} 355 euclidean.addSegmentToPixelTable(point, begin, segmentTable, 0.0, 2.0, width) 356 if euclidean.isPixelTableIntersecting(pixelTable, segmentTable, maskTable): 357 return False 358 if isAddedPointOnPathIntersectingPath(begin, path, point, pointIndexMinusOne): 359 return False 360 if pointIndex < len(path): 361 maskTable = {} 362 begin = path[pointIndex] 363 if pointIndexMinusOne >= 0: 364 end = path[ pointIndexMinusOne ] 365 euclidean.addValueSegmentToPixelTable(begin, end, maskTable, None, width) 366 segmentTable = {} 367 euclidean.addSegmentToPixelTable(point, begin, segmentTable, 0.0, 2.0, width) 368 if euclidean.isPixelTableIntersecting(pixelTable, segmentTable, maskTable): 369 return False 370 if isAddedPointOnPathIntersectingPath(begin, path, point, pointIndex): 371 return False 372 return True
373
374 -def isAddedPointOnPathIntersectingPath(begin, path, point, pointIndex):
375 'Determine if the point added to a path is intersecting the path by checking line intersection.' 376 segment = point - begin 377 segmentLength = abs(segment) 378 if segmentLength <= 0.0: 379 return False 380 normalizedSegment = segment / segmentLength 381 segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) 382 pointRotated = segmentYMirror * point 383 beginRotated = segmentYMirror * begin 384 if euclidean.isXSegmentIntersectingPath(path[ max(0, pointIndex - 20) : pointIndex ], pointRotated.real, beginRotated.real, segmentYMirror, pointRotated.imag): 385 return True 386 return euclidean.isXSegmentIntersectingPath(path[ pointIndex + 1 : pointIndex + 21 ], pointRotated.real, beginRotated.real, segmentYMirror, pointRotated.imag)
387
388 -def isPointAddedAroundClosest(pixelTable, layerExtrusionWidth, paths, removedEndpointPoint, width):
389 'Add the closest removed endpoint to the path, with minimal twisting.' 390 closestDistanceSquared = 999999999987654321.0 391 closestPathIndex = None 392 for pathIndex in xrange(len(paths)): 393 path = paths[ pathIndex ] 394 for pointIndex in xrange(len(path)): 395 point = path[pointIndex] 396 distanceSquared = abs(point - removedEndpointPoint) 397 if distanceSquared < closestDistanceSquared: 398 closestDistanceSquared = distanceSquared 399 closestPathIndex = pathIndex 400 if closestPathIndex == None: 401 return 402 if closestDistanceSquared < 0.8 * layerExtrusionWidth ** 2 : #todo was 0.8 * layerExtrusionWidth ** 2 maybe 0.88617 the behaviour of fill ends originally 0.8 * layerExtrusionWidth * layerExtrusionWidth: 403 return 404 closestPath = paths[ closestPathIndex ] 405 closestPointIndex = getWithLeastLength(closestPath, removedEndpointPoint) 406 if isAddedPointOnPathFree(closestPath, pixelTable, removedEndpointPoint, closestPointIndex, width): 407 addPointOnPath(closestPath, closestPathIndex, pixelTable, removedEndpointPoint, closestPointIndex, width) 408 return True 409 return isSidePointAdded(pixelTable, closestPath, closestPathIndex, closestPointIndex, layerExtrusionWidth, removedEndpointPoint, width)
410
411 -def isSegmentAround(aroundSegments, segment):
412 'Determine if there is another segment around.' 413 for aroundSegment in aroundSegments: 414 endpoint = aroundSegment[0] 415 if isSegmentInX(segment, endpoint.point.real, endpoint.otherEndpoint.point.real): 416 return True 417 return False
418
419 -def isSegmentCompletelyInAnIntersection(segment, xIntersections):
420 'Add sparse endpoints from a segment.' 421 for xIntersectionIndex in xrange(0, len(xIntersections), 2): 422 surroundingXFirst = xIntersections[ xIntersectionIndex ] 423 surroundingXSecond = xIntersections[ xIntersectionIndex + 1 ] 424 if euclidean.isSegmentCompletelyInX(segment, surroundingXFirst, surroundingXSecond): 425 return True 426 return False
427
428 -def isSegmentInX(segment, xFirst, xSecond):
429 'Determine if the segment overlaps within x.' 430 segmentFirstX = segment[0].point.real 431 segmentSecondX = segment[1].point.real 432 if min(segmentFirstX, segmentSecondX) > max(xFirst, xSecond): 433 return False 434 return max(segmentFirstX, segmentSecondX) > min(xFirst, xSecond)
435
436 -def isSharpCorner(beginComplex, centerComplex, endComplex):
437 'Determine if the three complex points form a sharp corner.' 438 centerBeginComplex = beginComplex - centerComplex 439 centerEndComplex = endComplex - centerComplex 440 centerBeginLength = abs(centerBeginComplex) 441 centerEndLength = abs(centerEndComplex) 442 if centerBeginLength <= 0.0 or centerEndLength <= 0.0: 443 return False 444 centerBeginComplex /= centerBeginLength 445 centerEndComplex /= centerEndLength 446 return euclidean.getDotProduct(centerBeginComplex, centerEndComplex) > 0.9
447
448 -def isSidePointAdded(pixelTable, closestPath, closestPathIndex, closestPointIndex, layerExtrusionWidth, removedEndpointPoint, width):
449 'Add side point along with the closest removed endpoint to the path, with minimal twisting.' 450 if closestPointIndex <= 0 or closestPointIndex >= len(closestPath): 451 return False 452 pointBegin = closestPath[ closestPointIndex - 1 ] 453 pointEnd = closestPath[ closestPointIndex ] 454 removedEndpointPoint = removedEndpointPoint 455 closest = pointBegin 456 farthest = pointEnd 457 removedMinusClosest = removedEndpointPoint - pointBegin 458 removedMinusClosestLength = abs(removedMinusClosest) 459 if removedMinusClosestLength <= 0.0: 460 return False 461 removedMinusOther = removedEndpointPoint - pointEnd 462 removedMinusOtherLength = abs(removedMinusOther) 463 if removedMinusOtherLength <= 0.0: 464 return False 465 insertPointAfter = None 466 insertPointBefore = None 467 if removedMinusOtherLength < removedMinusClosestLength: 468 closest = pointEnd 469 farthest = pointBegin 470 removedMinusClosest = removedMinusOther 471 removedMinusClosestLength = removedMinusOtherLength 472 insertPointBefore = removedEndpointPoint 473 else: 474 insertPointAfter = removedEndpointPoint 475 removedMinusClosestNormalized = removedMinusClosest / removedMinusClosestLength 476 perpendicular = removedMinusClosestNormalized * complex(0.0, layerExtrusionWidth) 477 sidePoint = removedEndpointPoint + perpendicular 478 #extra check in case the line to the side point somehow slips by the line to the perpendicular 479 sidePointOther = removedEndpointPoint - perpendicular 480 if abs(sidePoint - farthest) > abs(sidePointOther - farthest): 481 perpendicular = -perpendicular 482 sidePoint = sidePointOther 483 maskTable = {} 484 closestSegmentTable = {} 485 toPerpendicularTable = {} 486 euclidean.addValueSegmentToPixelTable(pointBegin, pointEnd, maskTable, None, width) 487 euclidean.addValueSegmentToPixelTable(closest, removedEndpointPoint, closestSegmentTable, None, width) 488 euclidean.addValueSegmentToPixelTable(sidePoint, farthest, toPerpendicularTable, None, width) 489 if euclidean.isPixelTableIntersecting(pixelTable, toPerpendicularTable, maskTable) or euclidean.isPixelTableIntersecting(closestSegmentTable, toPerpendicularTable, maskTable): 490 sidePoint = removedEndpointPoint - perpendicular 491 toPerpendicularTable = {} 492 euclidean.addValueSegmentToPixelTable(sidePoint, farthest, toPerpendicularTable, None, width) 493 if euclidean.isPixelTableIntersecting(pixelTable, toPerpendicularTable, maskTable) or euclidean.isPixelTableIntersecting(closestSegmentTable, toPerpendicularTable, maskTable): 494 return False 495 if insertPointBefore != None: 496 addPointOnPathIfFree(closestPath, closestPathIndex, pixelTable, insertPointBefore, closestPointIndex, width) 497 addPointOnPathIfFree(closestPath, closestPathIndex, pixelTable, sidePoint, closestPointIndex, width) 498 if insertPointAfter != None: 499 addPointOnPathIfFree(closestPath, closestPathIndex, pixelTable, insertPointAfter, closestPointIndex, width) 500 return True
501
502 -def removeEndpoints(pixelTable, layerExtrusionWidth, paths, removedEndpoints, aroundWidth):
503 'Remove endpoints which are added to the path.' 504 for removedEndpointIndex in xrange(len(removedEndpoints) - 1, -1, -1): 505 removedEndpoint = removedEndpoints[ removedEndpointIndex ] 506 removedEndpointPoint = removedEndpoint.point 507 if isPointAddedAroundClosest(pixelTable, layerExtrusionWidth, paths, removedEndpointPoint, aroundWidth): 508 removedEndpoints.remove(removedEndpoint)
509