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

Source Code for Module plugins.inset

  1  """ 
  2  Inset will inset the outside outlines by half the perimeter width, and outset the inside outlines by the same amount. 
  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, config 
 14  from fabmetheus_utilities import archive, euclidean, intercircle 
 15  from fabmetheus_utilities.geometry.solids import triangle_mesh 
 16  from entities import NestedRing, Layer, GcodeCommand,  BoundaryPerimeter 
 17  from multiprocessing import Pool, Manager, Process 
 18  from utilities import class_pickler 
 19  import copy_reg 
 20  import logging 
 21  import math 
 22  import multiprocessing 
 23  import os 
 24  import sys 
 25  import types 
 26  from utilities import memory_tracker 
 27   
 28  logger = logging.getLogger(__name__) 
 29  name = __name__ 
 30   
31 -def performAction(slicedModel):
32 "Inset the slicedModel." 33 34 i = InsetSkein(slicedModel) 35 if slicedModel.runtimeParameters.profileMemory: 36 memory_tracker.track_object(i) 37 i.inset() 38 39 if slicedModel.runtimeParameters.profileMemory: 40 memory_tracker.create_snapshot("After inset")
41
42 -class InsetSkein:
43 44 "A class to inset a skein of extrusions."
45 - def __init__(self, slicedModel):
46 self.slicedModel = slicedModel 47 self.overlapRemovalWidthOverPerimeterWidth = config.getfloat(name, 'overlap.removal.scaler') 48 self.nozzleDiameter = config.getfloat(name, 'nozzle.diameter') 49 self.bridgeWidthMultiplier = config.getfloat(name, 'bridge.width.multiplier.ratio') 50 self.loopOrderAscendingArea = config.getboolean(name, 'loop.order.preferloops') 51 self.layerThickness = self.slicedModel.runtimeParameters.layerThickness 52 self.perimeterWidth = self.slicedModel.runtimeParameters.perimeterWidth 53 self.halfPerimeterWidth = 0.5 * self.perimeterWidth 54 self.overlapRemovalWidth = self.perimeterWidth * (0.7853) * self.overlapRemovalWidthOverPerimeterWidth 55 self.multiprocess = config.getboolean(name, 'multiprocess')
56
57 - def inset(self):
58 "Inset the layers" 59 60 if self.multiprocess: 61 manager = Manager() 62 sharedLayers = manager.list(self.slicedModel.layers.values()) 63 64 p = Pool() 65 resultLayers = p.map(self.addInsetForLayer, sharedLayers) 66 p.close() 67 p.join() 68 69 for resultLayer in resultLayers: 70 self.slicedModel.layers[resultLayer.z] = resultLayer 71 72 else: 73 74 for x in self.slicedModel.layers.values(): 75 self.addInsetForLayer(x)
76
77 - def addInsetForLayer(self, layer):
78 halfWidth = self.halfPerimeterWidth * 0.7853 79 if layer.bridgeRotation != None: 80 halfWidth = self.bridgeWidthMultiplier * ((2 * self.nozzleDiameter - self.layerThickness) / 2) * 0.7853 81 82 alreadyFilledArounds = [] 83 84 for nestedRing in layer.nestedRings: 85 self.addInset(nestedRing, halfWidth, alreadyFilledArounds) 86 return layer
87
88 - def addInset(self, nestedRing, halfWidth, alreadyFilledArounds):
89 "Add inset to the layer." 90 91 # Note: inner nested rings have to come first so the intersecting check below does not give a false positive 92 for innerNestedRing in nestedRing.innerNestedRings: 93 self.addInset(innerNestedRing, halfWidth, alreadyFilledArounds) 94 95 boundary = [nestedRing.getXYBoundaries()] 96 insetBoundaryPerimeter = intercircle.getInsetLoopsFromLoops(halfWidth, boundary) 97 98 triangle_mesh.sortLoopsInOrderOfArea(not self.loopOrderAscendingArea, insetBoundaryPerimeter) 99 100 for loop in insetBoundaryPerimeter: 101 centerOutset = intercircle.getLargestCenterOutsetLoopFromLoopRegardless(loop, halfWidth) 102 103 "Add the perimeter block remainder of the loop which does not overlap the alreadyFilledArounds loops." 104 if self.overlapRemovalWidthOverPerimeterWidth < 0.1: 105 nestedRing.perimeter.addPath(centerOutset.center + [centerOutset.center[0]]) 106 break 107 isIntersectingSelf = isIntersectingItself(centerOutset.center, self.overlapRemovalWidth) 108 109 if isIntersectingWithinLists(centerOutset.center, alreadyFilledArounds) or isIntersectingSelf: 110 self.addGcodeFromPerimeterPaths(nestedRing, isIntersectingSelf, centerOutset.center, alreadyFilledArounds, halfWidth, boundary) 111 else: 112 nestedRing.perimeter.addPath(centerOutset.center + [centerOutset.center[0]]) 113 addAlreadyFilledArounds(alreadyFilledArounds, centerOutset.center, self.overlapRemovalWidth)
114 115
116 - def addGcodeFromPerimeterPaths(self, nestedRing, isIntersectingSelf, loop, alreadyFilledArounds, halfWidth, boundary):
117 "Add the perimeter paths to the output." 118 segments = [] 119 outlines = [] 120 thickOutlines = [] 121 allLoopLists = alreadyFilledArounds[:] + [thickOutlines] 122 aroundLists = alreadyFilledArounds 123 for pointIndex in xrange(len(loop)): 124 pointBegin = loop[pointIndex] 125 pointEnd = loop[(pointIndex + 1) % len(loop)] 126 if isIntersectingSelf: 127 if euclidean.isLineIntersectingLoops(outlines, pointBegin, pointEnd): 128 segments += getSegmentsFromLoopListsPoints(allLoopLists, pointBegin, pointEnd) 129 else: 130 segments += getSegmentsFromLoopListsPoints(alreadyFilledArounds, pointBegin, pointEnd) 131 addSegmentOutline(False, outlines, pointBegin, pointEnd, self.overlapRemovalWidth) 132 addSegmentOutline(True, thickOutlines, pointBegin, pointEnd, self.overlapRemovalWidth) 133 else: 134 segments += getSegmentsFromLoopListsPoints(alreadyFilledArounds, pointBegin, pointEnd) 135 perimeterPaths = [] 136 path = [] 137 muchSmallerThanRadius = 0.1 * halfWidth 138 segments = getInteriorSegments(boundary, segments) 139 for segment in segments: 140 pointBegin = segment[0].point 141 if not isCloseToLast(perimeterPaths, pointBegin, muchSmallerThanRadius): 142 path = [pointBegin] 143 perimeterPaths.append(path) 144 path.append(segment[1].point) 145 if len(perimeterPaths) > 1: 146 firstPath = perimeterPaths[0] 147 lastPath = perimeterPaths[-1] 148 if abs(lastPath[-1] - firstPath[0]) < 0.1 * muchSmallerThanRadius: 149 connectedBeginning = lastPath[:-1] + firstPath 150 perimeterPaths[0] = connectedBeginning 151 perimeterPaths.remove(lastPath) 152 muchGreaterThanRadius = 6.0 * halfWidth 153 for perimeterPath in perimeterPaths: 154 if euclidean.getPathLength(perimeterPath) > muchGreaterThanRadius: 155 nestedRing.perimeter.addPath(perimeterPath)
156
157 -def addAlreadyFilledArounds(alreadyFilledArounds, loop, radius):
158 "Add already filled loops around loop to alreadyFilledArounds." 159 radius = abs(radius) 160 alreadyFilledLoop = [] 161 slightlyGreaterThanRadius = 1.01 * radius 162 muchGreaterThanRadius = 2.5 * radius 163 centers = intercircle.getCentersFromLoop(loop, slightlyGreaterThanRadius) 164 for center in centers: 165 alreadyFilledInset = intercircle.getSimplifiedInsetFromClockwiseLoop(center, radius) 166 if intercircle.isLargeSameDirection(alreadyFilledInset, center, radius): 167 alreadyFilledLoop.append(alreadyFilledInset) 168 if len(alreadyFilledLoop) > 0: 169 alreadyFilledArounds.append(alreadyFilledLoop)
170
171 -def addSegmentOutline(isThick, outlines, pointBegin, pointEnd, width):
172 "Add a diamond or hexagonal outline for a line segment." 173 width = abs(width) 174 exclusionWidth = 0.6 * width 175 slope = 0.2 176 if isThick: 177 slope = 3.0 178 exclusionWidth = 0.8 * width 179 segment = pointEnd - pointBegin 180 segmentLength = abs(segment) 181 if segmentLength == 0.0: 182 return 183 normalizedSegment = segment / segmentLength 184 outline = [] 185 segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) 186 pointBeginRotated = segmentYMirror * pointBegin 187 pointEndRotated = segmentYMirror * pointEnd 188 along = 0.05 189 alongLength = along * segmentLength 190 if alongLength > 0.1 * exclusionWidth: 191 along *= 0.1 * exclusionWidth / alongLength 192 alongEnd = 1.0 - along 193 remainingToHalf = 0.5 - along 194 alongToWidth = exclusionWidth / slope / segmentLength 195 pointBeginIntermediate = euclidean.getIntermediateLocation(along, pointBeginRotated, pointEndRotated) 196 pointEndIntermediate = euclidean.getIntermediateLocation(alongEnd, pointBeginRotated, pointEndRotated) 197 outline.append(pointBeginIntermediate) 198 verticalWidth = complex(0.0, exclusionWidth) 199 if alongToWidth > 0.9 * remainingToHalf: 200 verticalWidth = complex(0.0, slope * remainingToHalf * segmentLength) 201 middle = (pointBeginIntermediate + pointEndIntermediate) * 0.5 202 middleDown = middle - verticalWidth 203 middleUp = middle + verticalWidth 204 outline.append(middleUp) 205 outline.append(pointEndIntermediate) 206 outline.append(middleDown) 207 else: 208 alongOutsideBegin = along + alongToWidth 209 alongOutsideEnd = alongEnd - alongToWidth 210 outsideBeginCenter = euclidean.getIntermediateLocation(alongOutsideBegin, pointBeginRotated, pointEndRotated) 211 outsideBeginCenterDown = outsideBeginCenter - verticalWidth 212 outsideBeginCenterUp = outsideBeginCenter + verticalWidth 213 outsideEndCenter = euclidean.getIntermediateLocation(alongOutsideEnd, pointBeginRotated, pointEndRotated) 214 outsideEndCenterDown = outsideEndCenter - verticalWidth 215 outsideEndCenterUp = outsideEndCenter + verticalWidth 216 outline.append(outsideBeginCenterUp) 217 outline.append(outsideEndCenterUp) 218 outline.append(pointEndIntermediate) 219 outline.append(outsideEndCenterDown) 220 outline.append(outsideBeginCenterDown) 221 outlines.append(euclidean.getPointsRoundZAxis(normalizedSegment, outline))
222
223 -def getInteriorSegments(loops, segments):
224 'Get segments inside the loops.' 225 interiorSegments = [] 226 for segment in segments: 227 center = 0.5 * (segment[0].point + segment[1].point) 228 if euclidean.getIsInFilledRegion(loops, center): 229 interiorSegments.append(segment) 230 return interiorSegments
231
232 -def getIsIntersectingWithinList(loop, loopList):
233 "Determine if the loop is intersecting or is within the loop list." 234 leftPoint = euclidean.getLeftPoint(loop) 235 for otherLoop in loopList: 236 if euclidean.getNumberOfIntersectionsToLeft(otherLoop, leftPoint) % 2 == 1: 237 return True 238 return euclidean.isLoopIntersectingLoops(loop, loopList)
239 240
241 -def getSegmentsFromLoopListsPoints(loopLists, pointBegin, pointEnd):
242 "Get endpoint segments from the beginning and end of a line segment." 243 normalizedSegment = pointEnd - pointBegin 244 normalizedSegmentLength = abs(normalizedSegment) 245 if normalizedSegmentLength == 0.0: 246 return [] 247 normalizedSegment /= normalizedSegmentLength 248 segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) 249 pointBeginRotated = segmentYMirror * pointBegin 250 pointEndRotated = segmentYMirror * pointEnd 251 rotatedLoopLists = [] 252 for loopList in loopLists: 253 rotatedLoopList = [] 254 rotatedLoopLists.append(rotatedLoopList) 255 for loop in loopList: 256 rotatedLoop = euclidean.getPointsRoundZAxis(segmentYMirror, loop) 257 rotatedLoopList.append(rotatedLoop) 258 xIntersectionIndexList = [] 259 xIntersectionIndexList.append(euclidean.XIntersectionIndex(-1, pointBeginRotated.real)) 260 xIntersectionIndexList.append(euclidean.XIntersectionIndex(-1, pointEndRotated.real)) 261 euclidean.addXIntersectionIndexesFromLoopListsY(rotatedLoopLists, xIntersectionIndexList, pointBeginRotated.imag) 262 segments = euclidean.getSegmentsFromXIntersectionIndexes(xIntersectionIndexList, pointBeginRotated.imag) 263 for segment in segments: 264 for endpoint in segment: 265 endpoint.point *= normalizedSegment 266 return segments
267
268 -def isCloseToLast(paths, point, radius):
269 "Determine if the point is close to the last point of the last path." 270 if len(paths) < 1: 271 return False 272 lastPath = paths[-1] 273 return abs(lastPath[-1] - point) < radius
274
275 -def isIntersectingItself(loop, width):
276 "Determine if the loop is intersecting itself." 277 outlines = [] 278 for pointIndex in xrange(len(loop)): 279 pointBegin = loop[pointIndex] 280 pointEnd = loop[(pointIndex + 1) % len(loop)] 281 if euclidean.isLineIntersectingLoops(outlines, pointBegin, pointEnd): 282 return True 283 addSegmentOutline(False, outlines, pointBegin, pointEnd, width) 284 return False
285
286 -def isIntersectingWithinLists(loop, loopLists):
287 "Determine if the loop is intersecting or is within the loop lists." 288 for loopList in loopLists: 289 if getIsIntersectingWithinList(loop, loopList): 290 return True 291 return False
292 293 copy_reg.pickle(types.MethodType, class_pickler._pickle_method, class_pickler._unpickle_method) 294