chiark / gitweb /
Patch from SF49 to SF50. Only notiable change is the addition of the "Sharpest Angle...
authordaid <daid303@gmail.com>
Thu, 22 Mar 2012 12:43:07 +0000 (13:43 +0100)
committerdaid <daid303@gmail.com>
Thu, 22 Mar 2012 12:43:07 +0000 (13:43 +0100)
17 files changed:
SkeinPyPy/SkeinforgeVersion
SkeinPyPy/fabmetheus_utilities/euclidean.py
SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py
SkeinPyPy/fabmetheus_utilities/intercircle.py
SkeinPyPy/fabmetheus_utilities/svg_writer.py
SkeinPyPy/fabmetheus_utilities/version.txt
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dwindle.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py
SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py
SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py

index 4f56f122ec501b305ee530fb17d3a84b1aa0d094..fb1ff68037c1ad8e36fde878ec8f1213bc788fef 100644 (file)
@@ -1,2 +1,2 @@
-This SkeinPyPy version is based in Skeinforge: 49
+This SkeinPyPy version is based in Skeinforge: 50
 
index 072a5fa5dc2cb4162339cb18936db5331e00bb84..24708e0258c4f5e09041bc7ea56059a90733d063 100644 (file)
@@ -363,41 +363,41 @@ def compareSegmentLength( endpoint, otherEndpoint ):
                return - 1
        return 0
 
-def concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ):
+def concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, sharpestProduct, width):
        'Get connected paths from paths.'
-       bottomSegment = segments[ pathIndex ]
-       path = paths[ pathIndex ]
+       bottomSegment = segments[pathIndex]
+       path = paths[pathIndex]
        if bottomSegment == None:
                connectedPaths.append(path)
                return
-       endpoints = getEndpointsFromSegments( segments[ pathIndex + 1 : ] )
+       endpoints = getEndpointsFromSegments(segments[pathIndex + 1 :])
        bottomSegmentEndpoint = bottomSegment[0]
-       nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width )
+       nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath(endpoints, bottomSegmentEndpoint.path, pixelDictionary, sharpestProduct, width)
        if nextEndpoint == None:
                bottomSegmentEndpoint = bottomSegment[1]
-               nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width )
+               nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath(endpoints, bottomSegmentEndpoint.path, pixelDictionary, sharpestProduct, width)
        if nextEndpoint == None:
                connectedPaths.append(path)
                return
-       if len( bottomSegmentEndpoint.path ) > 0 and len( nextEndpoint.path ) > 0:
+       if len(bottomSegmentEndpoint.path) > 0 and len(nextEndpoint.path) > 0:
                bottomEnd = bottomSegmentEndpoint.path[-1]
                nextBegin = nextEndpoint.path[-1]
-               nextMinusBottomNormalized = getNormalized( nextBegin - bottomEnd )
+               nextMinusBottomNormalized = getNormalized(nextBegin - bottomEnd)
                if len( bottomSegmentEndpoint.path ) > 1:
                        bottomPenultimate = bottomSegmentEndpoint.path[-2]
-                       if getDotProduct( getNormalized( bottomPenultimate - bottomEnd ), nextMinusBottomNormalized ) > 0.9:
+                       if getDotProduct(getNormalized(bottomPenultimate - bottomEnd), nextMinusBottomNormalized) > 0.99:
                                connectedPaths.append(path)
                                return
                if len( nextEndpoint.path ) > 1:
                        nextPenultimate = nextEndpoint.path[-2]
-                       if getDotProduct( getNormalized( nextPenultimate - nextBegin ), - nextMinusBottomNormalized ) > 0.9:
+                       if getDotProduct(getNormalized(nextPenultimate - nextBegin), - nextMinusBottomNormalized) > 0.99:
                                connectedPaths.append(path)
                                return
        nextEndpoint.path.reverse()
        concatenatedPath = bottomSegmentEndpoint.path + nextEndpoint.path
-       paths[ nextEndpoint.pathIndex ] = concatenatedPath
-       segments[ nextEndpoint.pathIndex ] = getSegmentFromPath( concatenatedPath, nextEndpoint.pathIndex )
-       addValueSegmentToPixelTable( bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width )
+       paths[nextEndpoint.pathIndex] = concatenatedPath
+       segments[nextEndpoint.pathIndex] = getSegmentFromPath(concatenatedPath, nextEndpoint.pathIndex)
+       addValueSegmentToPixelTable(bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width)
 
 def getAngleAroundZAxisDifference( subtractFromVec3, subtractVec3 ):
        'Get the angle around the Z axis difference between a pair of Vector3s.'
@@ -668,18 +668,18 @@ def getConcatenatedList(originalLists):
                concatenatedList += originalList
        return concatenatedList
 
-def getConnectedPaths( paths, pixelDictionary, width ):
+def getConnectedPaths(paths, pixelDictionary, sharpestProduct, width):
        'Get connected paths from paths.'
        if len(paths) < 2:
                return paths
        connectedPaths = []
        segments = []
-       for pathIndex in xrange( len(paths) ):
-               path = paths[ pathIndex ]
-               segments.append( getSegmentFromPath( path, pathIndex ) )
-       for pathIndex in xrange( 0, len(paths) - 1 ):
-               concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width )
-       connectedPaths.append( paths[-1] )
+       for pathIndex in xrange(len(paths)):
+               path = paths[pathIndex]
+               segments.append(getSegmentFromPath(path, pathIndex))
+       for pathIndex in xrange(0, len(paths) - 1):
+               concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, sharpestProduct, width)
+       connectedPaths.append(paths[-1])
        return connectedPaths
 
 def getCrossProduct(firstComplex, secondComplex):
@@ -1327,7 +1327,7 @@ def getPathLength(path):
                pathLength += abs(firstPoint - secondPoint)
        return pathLength
 
-def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, width):
+def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, sharpestProduct, width):
        'Get paths from endpoints.'
        if len(endpoints) < 2:
                return []
@@ -1343,7 +1343,7 @@ def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, w
        path = []
        paths = [path]
        if len(endpoints) > 1:
-               nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width)
+               nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, sharpestProduct, width)
                if nextEndpoint != None:
                        if abs(nextEndpoint.point - endpointFirst.point) < abs(nextEndpoint.point - otherEndpoint.point):
                                endpointFirst = endpointFirst.otherEndpoint
@@ -1359,7 +1359,7 @@ def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, w
                        if len(endpointTable.values()[0]) < 2:
                                return []
                endpoints = getSquareValuesFromPoint(endpointTable, otherEndpoint.point * oneOverEndpointWidth)
-               nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width)
+               nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, sharpestProduct, width)
                if nextEndpoint == None:
                        path = []
                        paths.append(path)
@@ -2096,7 +2096,7 @@ class Endpoint:
                                closestEndpoint = endpoint
                return closestEndpoint
 
-       def getClosestMiss(self, endpoints, path, pixelDictionary, width):
+       def getClosestMiss(self, endpoints, path, pixelDictionary, sharpestProduct, width):
                'Get the closest endpoint which the segment to that endpoint misses the other extrusions.'
                pathMaskTable = {}
                smallestDistance = 987654321.0
@@ -2115,7 +2115,7 @@ class Endpoint:
                endpoints.sort(compareSegmentLength)
                for endpoint in endpoints[: 15]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds
                        normalizedSegment = endpoint.segment / endpoint.segmentLength
-                       isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > 0.9
+                       isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > sharpestProduct
                        if not isOverlappingSelf:
                                if len(path) > 2:
                                        segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
@@ -2132,14 +2132,14 @@ class Endpoint:
                                        return endpoint
                return None
 
-       def getClosestMissCheckEndpointPath( self, endpoints, path, pixelDictionary, width ):
+       def getClosestMissCheckEndpointPath(self, endpoints, path, pixelDictionary, sharpestProduct, width):
                'Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.'
                pathMaskTable = {}
                smallestDistance = 987654321.0
                penultimateMinusPoint = complex(0.0, 0.0)
                if len(path) > 1:
                        penultimatePoint = path[-2]
-                       addSegmentToPixelTable( penultimatePoint, self.point, pathMaskTable, 0, 0, width )
+                       addSegmentToPixelTable(penultimatePoint, self.point, pathMaskTable, 0, 0, width)
                        penultimateMinusPoint = penultimatePoint - self.point
                        if abs(penultimateMinusPoint) > 0.0:
                                penultimateMinusPoint /= abs(penultimateMinusPoint)
@@ -2151,27 +2151,27 @@ class Endpoint:
                endpoints.sort( compareSegmentLength )
                for endpoint in endpoints[ : 15 ]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds
                        normalizedSegment = endpoint.segment / endpoint.segmentLength
-                       isOverlappingSelf = getDotProduct( penultimateMinusPoint, normalizedSegment ) > 0.9
+                       isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > sharpestProduct
                        if not isOverlappingSelf:
                                if len(path) > 2:
                                        segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
                                        pointRotated = segmentYMirror * self.point
                                        endpointPointRotated = segmentYMirror * endpoint.point
-                                       if isXSegmentIntersectingPath( path[ max( 0, len(path) - 21 ) : - 1 ], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ):
+                                       if isXSegmentIntersectingPath(path[ max(0, len(path) - 21) : -1], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag):
                                                isOverlappingSelf = True
                                endpointPath = endpoint.path
-                               if len( endpointPath ) > 2:
+                               if len(endpointPath) > 2:
                                        segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
                                        pointRotated = segmentYMirror * self.point
                                        endpointPointRotated = segmentYMirror * endpoint.point
-                                       if isXSegmentIntersectingPath( endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ):
+                                       if isXSegmentIntersectingPath(endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag):
                                                isOverlappingSelf = True
                        if not isOverlappingSelf:
                                totalMaskTable = pathMaskTable.copy()
-                               addSegmentToPixelTable( endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width )
+                               addSegmentToPixelTable(endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width)
                                segmentTable = {}
-                               addSegmentToPixelTable( self.point, endpoint.point, segmentTable, 0, 0, width )
-                               if not isPixelTableIntersecting( pixelDictionary, segmentTable, totalMaskTable ):
+                               addSegmentToPixelTable(self.point, endpoint.point, segmentTable, 0, 0, width)
+                               if not isPixelTableIntersecting(pixelDictionary, segmentTable, totalMaskTable):
                                        return endpoint
                return None
 
index 9bf210d457f1e28eb13362ae4d7f4442e994996e..0cfffdcc51bbbd0961d6240b1ad676dc3b5d598a 100644 (file)
@@ -311,7 +311,7 @@ def getDescendingAreaLoops(allPoints, corners, importRadius):
        sortLoopsInOrderOfArea(True, loops)
        pointDictionary = {}
        for loop in loops:
-               if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.3:
+               if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.3 and intercircle.getIsLarge(loop, importRadius):
                        intercircle.directLoop(not euclidean.getIsInFilledRegion(descendingAreaLoops, loop[0]), loop)
                        descendingAreaLoops.append(loop)
                        addLoopToPointTable(loop, pointDictionary)
index 3c4e602648ebfc70ed30705e36fffc2e601b0306..ea448d06d46666c4e15295cc5afa9103c3370a46 100644 (file)
@@ -427,7 +427,7 @@ def getLargestInsetLoopFromLoopRegardless( loop, radius ):
                largestInsetLoop = getLargestInsetLoopFromLoop( loop, decreasingRadius )
                if len( largestInsetLoop ) > 0:
                        return largestInsetLoop
-       print('This should never happen, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle.')
+       print('Warning, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle.')
        print(loop)
        return loop
 
index ae836dfe2319c7e641c10249f7814df5e2da471f..15855d2644d64fc98e34051fab83cb1400821970 100644 (file)
@@ -218,9 +218,7 @@ class SVGWriter:
                self.setTexts('volume', 'Volume: %s cm3' % self.getRounded(volume))
                if not self.addLayerTemplateToSVG:
                        self.svgElement.getFirstChildByLocalName('script').removeFromIDNameParent()
-                       self.svgElement.getElementNodeByID('isoControlBox').removeFromIDNameParent()
-                       self.svgElement.getElementNodeByID('layerControlBox').removeFromIDNameParent()
-                       self.svgElement.getElementNodeByID('scrollControlBox').removeFromIDNameParent()
+                       self.svgElement.getElementNodeByID('controls').removeFromIDNameParent()
                self.graphicsElementNode.removeFromIDNameParent()
                self.addOriginalAsComment(elementNode)
                return documentNode.__repr__()
index 0d73a0c7fa6eba205e4e6fd460eebdf8ec81d0e0..2dc4ea1573805b1527eb26dde0395c20a1620ee0 100644 (file)
@@ -1 +1 @@
-12.02.10
\ No newline at end of file
+12.03.14
\ No newline at end of file
index ce54b1134e97cf2c5733b1afff664c8731db5823..181614ba5a10e937557f890b08c577c86462f080 100644 (file)
@@ -17,6 +17,9 @@ The default 'Activate Chamber' checkbox is on.  When it is on, the functions des
 ===Bed===
 The initial bed temperature is defined by 'Bed Temperature'.  If the 'Bed Temperature End Change Height' is greater or equal to the 'Bed Temperature Begin Change Height' and the 'Bed Temperature Begin Change Height' is greater or equal to zero, then the temperature will be ramped toward the 'Bed Temperature End'.  The ramp will start once the extruder reaches the 'Bed Temperature Begin Change Height', then the bed temperature will approach the 'Bed Temperature End' as the extruder reaches the 'Bed Temperature End Change Height', finally the bed temperature will stay at the 'Bed Temperature End' for the remainder of the build.
 
+The idea is described at:
+http://www.makerbot.com/blog/2011/03/17/if-you-cant-stand-the-heat/
+
 ====Bed Temperature====
 Default: 60C
 
index a817f52b265e15ba5c304b48adfb419b2369bb5d..665f7bfc7af04d1543c9aa6a4b1ff47e3d04b783 100644 (file)
@@ -174,7 +174,6 @@ class CombSkein:
        "A class to comb a skein of extrusions."
        def __init__(self):
                'Initialize'
-#              self.betweenTable = {}
                self.boundaryLoop = None
                self.distanceFeedRate = gcodec.DistanceFeedRate()
                self.extruderActive = False
@@ -240,7 +239,6 @@ class CombSkein:
        def getAroundBetweenPath(self, begin, end):
                'Get the path around the loops in the way of the original line segment.'
                aroundBetweenPath = []
-#              betweens = self.getBetweens()
                boundaries = self.getBoundaries()
                boundarySegments = self.getBoundarySegments(begin, boundaries, end)
                for boundarySegmentIndex, boundarySegment in enumerate(boundarySegments):
@@ -264,14 +262,6 @@ class CombSkein:
                                del aroundBetweenPath[pointIndex]
                return aroundBetweenPath
 
-#      def getBetweens(self):
-#              'Get betweens for the layer.'
-#              if not self.layerZ in self.betweenTable:
-#                      self.betweenTable[self.layerZ] = []
-#                      for boundary in self.getBoundaries():
-#                              self.betweenTable[self.layerZ] += intercircle.getInsetLoopsFromLoop(boundary, self.betweenInset)
-#              return self.betweenTable[self.layerZ]
-#
        def getBoundaries(self):
                "Get boundaries for the layer."
                if self.layerZ in self.layerTable:
@@ -439,7 +429,6 @@ class CombSkein:
                                return
                        elif firstWord == '(<edgeWidth>':
                                self.edgeWidth = float(splitLine[1])
-#                              self.betweenInset = 0.7 * self.edgeWidth
                                self.doubleEdgeWidth = self.edgeWidth + self.edgeWidth
                                self.halfEdgeWidth = 0.5 * self.edgeWidth
                                self.quadrupleEdgeWidth = self.doubleEdgeWidth + self.doubleEdgeWidth
index 419eab9f8576c52986b8025874f5f76e53885d5f..d78f194c06dfc563719e5a0e695fadfc492af712 100644 (file)
@@ -230,7 +230,8 @@ class CoolSkein:
 
        def addFlowRate(self, flowRate):
                'Add a multipled line of flow rate if different.'
-               self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
+               if flowRate != None:
+                       self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
 
        def addGcodeFromFeedRateMovementZ(self, feedRateMinute, point, z):
                'Add a movement to the output.'
index 05c1b599f7af671d2d781c535ca456f91f773654..8f175ea4c6207331cf8a7ad1d8b82efb25d9915e 100644 (file)
@@ -1,8 +1,6 @@
 """
 This page is in the table of contents.
-Dwindle is a plugin to smooth the surface dwindle of an object by replacing the edge surface with a surface printed at a fraction of the carve
-height.  This gives the impression that the object was carved at a much thinner height giving a high-quality finish, but still prints 
-in a relatively short time.  The latest process has some similarities with a description at:
+Dwindle is a plugin to reduce the feed rate and flow rate at the end of the thread, in order to reduce the ooze when traveling. It reduces the flow rate by a bit more than the feed rate, in order to use up the pent up plastic in the thread so that there is less remaining in the ooze.
 
 The dwindle manual page is at:
 http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle
@@ -11,10 +9,25 @@ http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle
 The default 'Activate Dwindle' checkbox is off.  When it is on, the functions described below will work, when it is off, nothing will be done.
 
 ==Settings==
-====Vertical Divisions====
-Default: 2
+===End Rate Multiplier===
+Default: 0.5
 
-Defines the number of times the dwindle infill and edges are divided vertically.
+Defines the ratio of the feed and flow rate at the end over the feed and flow rate of the rest of the thread. With reasonable values for the 'Pent Up Volume' and 'Slowdown Volume', the amount of ooze should be roughly proportional to the square of the 'End Rate Multiplier'. If the 'End Rate Multiplier' is too low, the printing will be very slow because the feed rate will be lower. If the 'End Rate Multiplier' is too high, there will still be a lot of ooze.
+
+===Pent Up Volume===
+Default: 0.4 mm3
+
+When the filament is stopped, there is a pent up volume of plastic that comes out afterwards. For best results, the 'Pent Up Volume' in dwindle should be set to that amount. If the 'Pent Up Volume' is too small, there will still be a lot of ooze. If the 'Pent Up Volume' is too large, the end of the thread will be thinner than the rest of the thread.
+
+===Slowdown Steps===
+Default: 3
+
+Dwindle reduces the feed rate and flow rate in steps so the thread will remain at roughly the same thickness until the end.  The "Slowdown Steps" setting is the number of steps, the more steps the smaller the variation in the thread thickness, but the larger the size of the resulting gcode file and the more time spent pausing between segments.
+
+===Slowdown Volume===
+Default: 5 mm3
+
+The 'Slowdown Volume' is the volume of the end of the thread where the feed and flow rates will be decreased. If the 'Slowdown Volume' is too small, there won't be enough time to get rid of the pent up plastic, so there will still be a lot of ooze. If the 'Slowdown Volume' is too large, a bit of time will be wasted because for a large portion of the thread, the feed rate will be slow. Overall, it is best to err on being too large, because too large would only waste machine time in production, rather than the more important string removal labor time.
 
 ==Examples==
 The following examples dwindle the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and dwindle.py.
@@ -80,15 +93,15 @@ class DwindleRepository:
        'A class to handle the dwindle settings.'
        def __init__(self):
                'Set the default settings, execute title & settings fileName.'
-               skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dwindle.html', self )
-               self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dwindle', self, '')
+               skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dwindle.html', self)
+               self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dwindle', self, '')
                self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle')
                self.activateDwindle = settings.BooleanSetting().getFromValue('Activate Dwindle', self, False)
                settings.LabelSeparator().getFromRepository(self)
                self.endRateMultiplier = settings.FloatSpin().getFromValue(0.4, 'End Rate Multiplier (ratio):', self, 0.8, 0.5)
                self.pentUpVolume = settings.FloatSpin().getFromValue(0.1, 'Pent Up Volume (cubic millimeters):', self, 1.0, 0.4)
                self.slowdownSteps = settings.IntSpin().getFromValue(2, 'Slowdown Steps (positive integer):', self, 10, 3)
-               self.slowdownVolume = settings.FloatSpin().getFromValue(0.4, 'Slowdown Volume (cubic millimeters):', self, 4.0, 2.0)
+               self.slowdownVolume = settings.FloatSpin().getFromValue(1.0, 'Slowdown Volume (cubic millimeters):', self, 10.0, 5.0)
                self.executeTitle = 'Dwindle'
 
        def execute(self):
@@ -110,6 +123,7 @@ class DwindleSkein:
                self.lines = None
                self.oldFlowRate = None
                self.oldLocation = None
+               self.operatingFlowRate = None
                self.threadSections = []
 
        def addThread(self):
@@ -138,7 +152,10 @@ class DwindleSkein:
                self.lines = archive.getTextLines(gcodeText)
                self.repository = repository
                self.parseInitialization()
-               self.area = self.infillWidth * self.layerHeight
+               if self.operatingFlowRate == None:
+                       print('Warning, there is no operatingFlowRate so dwindle will do nothing.')
+                       return gcodeText
+               self.area = self.infillWidth * self.layerHeight * self.volumeFraction
                self.oneOverSteps = 1.0 / float(repository.slowdownSteps.value)
                self.halfOverSteps = 0.5 * self.oneOverSteps
                for self.lineIndex in xrange(self.lineIndex, len(self.lines)):
@@ -165,6 +182,8 @@ class DwindleSkein:
                        elif firstWord == '(<operatingFlowRate>':
                                self.operatingFlowRate = float(splitLine[1])
                                self.oldFlowRate = self.operatingFlowRate
+                       elif firstWord == '(<volumeFraction>':
+                               self.volumeFraction = float(splitLine[1])
                        self.distanceFeedRate.addLine(line)
 
        def parseLine(self, line):
index 7cf5bcce346aeb099deb69b9f09d2ae0eeca5d7e..28e79b7e1134b5d0e323394150032de275c82590 100644 (file)
@@ -118,6 +118,13 @@ Default is 1.5.
 
 Defines the ratio of the infill width over the layer height.  The higher the value the wider apart the infill will be and therefore the sparser the infill will be.
 
+===Sharpest Angle===
+Default: 60 degrees
+
+Defines the sharpest angle that a thread is allowed to make before it is separated into two threads. If 'Sharpest Angle' is too low, the extruder will stop and start often, slowing printing and putting more wear and tear on the extruder. If 'Sharpest Angle' is too high, then threads will almost double back on themselves, leading to bumps in the fill, and sometimes filament being dragged by the nozzle.
+
+This parameter is used in fill, raft and skin.
+
 ===Solid Surface Thickness===
 Default is three.
 
@@ -791,7 +798,7 @@ class FillRepository:
                settings.LabelDisplay().getFromName('- Infill -', self )
                self.infillBeginRotation = settings.FloatSpin().getFromValue( 0.0, 'Infill Begin Rotation (degrees):', self, 90.0, 45.0 )
                self.infillBeginRotationRepeat = settings.IntSpin().getFromValue( 0, 'Infill Begin Rotation Repeat (layers):', self, 3, 1 )
-               self.infillOddLayerExtraRotation = settings.FloatSpin().getFromValue( 30.0, 'Infill Odd Layer Extra Rotation (degrees):', self, 90.0, 90.0 )
+               self.infillOddLayerExtraRotation = settings.FloatSpin().getFromValue(30.0, 'Infill Odd Layer Extra Rotation (degrees):', self, 90.0, 90.0)
                self.infillPatternLabel = settings.LabelDisplay().getFromName('Infill Pattern:', self )
                infillLatentStringVar = settings.LatentStringVar()
                self.infillPatternGridCircular = settings.Radio().getFromRadio( infillLatentStringVar, 'Grid Circular', self, False )
@@ -800,8 +807,8 @@ class FillRepository:
                self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True )
                self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 )
                self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 )
-               self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 )
                settings.LabelSeparator().getFromRepository(self)
+               self.sharpestAngle = settings.FloatSpin().getFromValue(50.0, 'Sharpest Angle (degrees):', self, 70.0, 60.0)
                self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3)
                self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self)
                self.startFromLowerLeft = settings.MenuRadio().getFromMenuButtonDisplay(self.startFromChoice, 'Lower Left', self, True)
@@ -878,7 +885,8 @@ class FillSkein:
                        extraShells = 0
                        self.distanceFeedRate.addLine('(<bridgeRotation> %s )' % layerRotation)
                self.distanceFeedRate.addLine('(<rotation> %s )' % layerRotation)
-               aroundWidth = 0.34321 * self.infillWidth
+#              aroundWidth = 0.34321 * self.infillWidth
+               aroundWidth = 0.24321 * self.infillWidth
                doubleInfillWidth = 2.0 * self.infillWidth
                gridPointInsetX = 0.5 * self.fillInset
                self.lastExtraShells = extraShells
@@ -929,7 +937,7 @@ class FillSkein:
                        for segments in self.horizontalSegmentsDictionary.values():
                                for segment in segments:
                                        endpoints += segment
-               paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.infillWidth, pixelTable, aroundWidth)
+               paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.infillWidth, pixelTable, self.sharpestProduct, aroundWidth)
                if gridCircular:
                        startAngle = euclidean.globalGoldenAngle * float(layerIndex)
                        for gridPoint in self.getGridPoints(fillLoops, reverseRotation):
@@ -942,7 +950,7 @@ class FillSkein:
                        while oldRemovedEndpointLength - len(removedEndpoints) > 0:
                                oldRemovedEndpointLength = len(removedEndpoints)
                                removeEndpoints(self.infillWidth, paths, pixelTable, removedEndpoints, aroundWidth)
-                       paths = euclidean.getConnectedPaths(paths, pixelTable, aroundWidth)
+                       paths = euclidean.getConnectedPaths(paths, pixelTable, self.sharpestProduct, aroundWidth)
                for path in paths:
                        addPath(self.infillWidth, infillPaths, path, layerRotation)
                euclidean.transferPathsToNestedRings(nestedRings, infillPaths)
@@ -1118,6 +1126,7 @@ class FillSkein:
                'Parse gcode text and store the bevel gcode.'
                self.repository = repository
                self.lines = archive.getTextLines(gcodeText)
+               self.sharpestProduct = math.sin(math.radians(repository.sharpestAngle.value))
                self.threadSequence = None
                if repository.threadSequenceInfillLoops.value:
                        self.threadSequence = ['infill', 'loops', 'edge']
@@ -1251,12 +1260,13 @@ class FillSkein:
                        if firstWord == '(<crafting>)':
                                self.distanceFeedRate.addLine(line)
                                return
+                       elif firstWord == '(<infillWidth>':
+                               self.infillWidth = float(splitLine[1])
                        elif firstWord == '(<layerHeight>':
                                self.layerHeight = float(splitLine[1])
-                               self.infillWidth = self.repository.infillWidth.value
                                self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0)))
                                self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value)
-                               self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth)
+                               self.distanceFeedRate.addTagRoundedLine('sharpestProduct', self.sharpestProduct)
                        elif firstWord == '(<edgeWidth>':
                                self.edgeWidth = float(splitLine[1])
                                threadSequenceString = ' '.join( self.threadSequence )
index 74cc85c0583133cfc2f66453817f364dcec14f16..bb68db20e4339ef2d9d206fb09b7644212a1639c 100644 (file)
@@ -38,6 +38,11 @@ Default is on.
 
 When selected, the M104 S0 gcode line will be added to the end of the file to turn the extruder heater off by setting the extruder heater temperature to 0.
 
+===Volume Fraction===
+Default: 0.82
+
+The 'Volume Fraction' is the estimated volume of the thread compared to the box defined by the layer height and infill width. This is used in dwindle, splodge, and statistic. It is in inset because inset is a required extrusion tool, earlier in the chain than dwindle and splodge. In dwindle and splodge it is used to determine the filament volume, in statistic it is used to determine the extrusion diameter.
+
 ==Examples==
 The following examples inset the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and inset.py.
 
index 90248e5672e0ad0114e22246fa07f91dbb15acd2..867e3a99a1250932566d3193655a08458b06de6c 100644 (file)
@@ -200,7 +200,7 @@ class MillSkein:
                endpoints = euclidean.getEndpointsFromSegmentTable( boundaryLayer.segmentTable )
                if len(endpoints) < 1:
                        return
-               paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.millWidth, self.aroundPixelTable, self.aroundWidth)
+               paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.millWidth, self.aroundPixelTable, 1.0, self.aroundWidth)
                averageZ = self.average.getAverage()
                if self.repository.addInnerLoops.value:
                        self.addGcodeFromLoops( boundaryLayer.innerLoops, averageZ )
index cdb47ce4bad46747c3e97dbc361485a9c9c41dcf..1fc1c868f18ba0476838d2e67d157c57d281f032 100644 (file)
@@ -443,6 +443,7 @@ class RaftSkein:
                self.operatingLayerEndLine = '(<operatingLayerEnd> </operatingLayerEnd>)'
                self.operatingJump = None
                self.orbitalFeedRatePerSecond = 2.01
+               self.sharpestProduct = 0.94
                self.supportFlowRate = None
                self.supportLayers = []
                self.supportLayersTemperature = None
@@ -547,7 +548,7 @@ class RaftSkein:
                        return
                aroundPixelTable = {}
                aroundWidth = 0.34321 * step
-               paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, aroundWidth)
+               paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, self.sharpestProduct, aroundWidth)
                self.addLayerLine(z)
                if self.operatingFlowRate != None:
                        self.addFlowRate(flowRateMultiplier * self.operatingFlowRate)
@@ -704,7 +705,7 @@ class RaftSkein:
                aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset)
                for aroundBoundaryLoop in aroundBoundaryLoops:
                        euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth)
-               paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, aroundWidth)
+               paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, self.sharpestProduct, aroundWidth)
                feedRateMinuteMultiplied = self.operatingFeedRateMinute
                supportFlowRateMultiplied = self.supportFlowRate
                if self.layerIndex == 0:
@@ -894,6 +895,11 @@ class RaftSkein:
                                self.baseTemperature = float(splitLine[1])
                        elif firstWord == '(<coolingRate>':
                                self.coolingRate = float(splitLine[1])
+                       elif firstWord == '(<edgeWidth>':
+                               self.edgeWidth = float(splitLine[1])
+                               self.halfEdgeWidth = 0.5 * self.edgeWidth
+                               self.quarterEdgeWidth = 0.25 * self.edgeWidth
+                               self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value
                        elif firstWord == '(</extruderInitialization>)':
                                self.distanceFeedRate.addTagBracketedProcedure('raft')
                        elif firstWord == '(<heatingRate>':
@@ -925,11 +931,8 @@ class RaftSkein:
                                self.operatingFlowRate = float(splitLine[1])
                                self.oldFlowRate = self.operatingFlowRate
                                self.supportFlowRate = self.operatingFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value
-                       elif firstWord == '(<edgeWidth>':
-                               self.edgeWidth = float(splitLine[1])
-                               self.halfEdgeWidth = 0.5 * self.edgeWidth
-                               self.quarterEdgeWidth = 0.25 * self.edgeWidth
-                               self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value
+                       elif firstWord == '(<sharpestProduct>':
+                               self.sharpestProduct = float(splitLine[1])
                        elif firstWord == '(<supportLayersTemperature>':
                                self.supportLayersTemperature = float(splitLine[1])
                        elif firstWord == '(<supportedLayersTemperature>':
index 37e0841d7501673e2f96099d5925397c3bee9ea4..d3ea4ea96d5896e3a6596036db183a4aad11eadc 100644 (file)
@@ -154,11 +154,13 @@ class SkinSkein:
                self.maximumZFeedRateMinute = 60.0
                self.oldFlowRate = None
                self.oldLocation = None
+               self.sharpestProduct = 0.94
                self.travelFeedRateMinute = 957.0
 
        def addFlowRateLine(self, flowRate):
                'Add a flow rate line.'
-               self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
+               if flowRate != None:
+                       self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate))
 
        def addPerimeterLoop(self, thread, z):
                'Add the edge loop to the gcode.'
@@ -170,7 +172,8 @@ class SkinSkein:
                        return
                bottomZ = self.oldLocation.z + self.layerHeight / self.verticalDivisionsFloat - self.layerHeight
                offsetY = 0.5 * self.skinInfillWidth
-               self.addFlowRateLine(self.oldFlowRate / self.verticalDivisionsFloat / self.horizontalInfillDivisionsFloat)
+               if self.oldFlowRate != None:
+                       self.addFlowRateLine(self.oldFlowRate / self.verticalDivisionsFloat / self.horizontalInfillDivisionsFloat)
                for verticalDivisionIndex in xrange(self.verticalDivisions):
                        z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex)
                        self.addSkinnedInfillBoundary(self.infillBoundaries, offsetY * (verticalDivisionIndex % 2 == 0), self.oldLocation.z, z)
@@ -199,7 +202,7 @@ class SkinSkein:
                                for endpoint in segment:
                                        endpoint.point = complex(endpoint.point.real, endpoint.point.imag + offsetY)
                                        endpoints.append(endpoint)
-               infillPaths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.skinInfillWidth, pixelTable, aroundWidth)
+               infillPaths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.skinInfillWidth, pixelTable, self.sharpestProduct, aroundWidth)
                for infillPath in infillPaths:
                        addPointBeforeThread = True
                        infillRotated = euclidean.getRotatedComplexes(self.rotation, infillPath)
@@ -239,9 +242,12 @@ class SkinSkein:
                for division in xrange(self.repository.horizontalPerimeterDivisions.value):
                        edges.append(self.getClippedSimplifiedLoopPathByLoop(intercircle.getLargestInsetLoopFromLoop(edgeThread, radius)))
                        radius += radiusAddition
-               skinnedPerimeterFlowRate = self.oldFlowRate / self.verticalDivisionsFloat
+               skinnedPerimeterFlowRate = None
+               if self.oldFlowRate != None:
+                       skinnedPerimeterFlowRate = self.oldFlowRate / self.verticalDivisionsFloat
                if getIsMinimumSides(edges):
-                       self.addFlowRateLine(skinnedPerimeterFlowRate / self.horizontalPerimeterDivisionsFloat)
+                       if self.oldFlowRate != None:
+                               self.addFlowRateLine(skinnedPerimeterFlowRate / self.horizontalPerimeterDivisionsFloat)
                        for verticalDivisionIndex in xrange(self.verticalDivisions):
                                z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex)
                                for edge in edges:
@@ -314,6 +320,9 @@ class SkinSkein:
                        self.distanceFeedRate.parseSplitLine(firstWord, splitLine)
                        if firstWord == '(<clipOverEdgeWidth>':
                                self.clipOverEdgeWidth = float(splitLine[1])
+                       elif firstWord == '(<edgeWidth>':
+                               self.edgeWidth = float(splitLine[1])
+                               self.halfEdgeWidth = 0.5 * self.edgeWidth
                        elif firstWord == '(</extruderInitialization>)':
                                self.distanceFeedRate.addTagBracketedProcedure('skin')
                                return
@@ -328,9 +337,8 @@ class SkinSkein:
                                self.maximumZFeedRateMinute = 60.0 * float(splitLine[1])
                        elif firstWord == '(<operatingFlowRate>':
                                self.oldFlowRate = float(splitLine[1])
-                       elif firstWord == '(<edgeWidth>':
-                               self.edgeWidth = float(splitLine[1])
-                               self.halfEdgeWidth = 0.5 * self.edgeWidth
+                       elif firstWord == '(<sharpestProduct>':
+                               self.sharpestProduct = float(splitLine[1])
                        elif firstWord == '(<travelFeedRatePerSecond>':
                                self.travelFeedRateMinute = 60.0 * float(splitLine[1])
                        self.distanceFeedRate.addLine(line)
index 432b34cdf00913b8f77eaf519d5ca6093b5f5382..cc6123768a67aa8b332271f50298981ae473b6d0 100644 (file)
@@ -96,15 +96,15 @@ def getNewRepository():
        'Get new repository.'
        return WidenRepository()
 
-def getWidenedLoop(loop, loopList, outsetLoop, radius):
+def getWidenedLoops(loop, loopList, outsetLoop, radius):
        'Get the widened loop.'
        intersectingWithinLoops = getIntersectingWithinLoops(loop, loopList, outsetLoop)
        if len(intersectingWithinLoops) < 1:
-               return loop
+               return [loop]
        loopsUnified = boolean_solid.getLoopsUnion(radius, [[loop], intersectingWithinLoops])
        if len(loopsUnified) < 1:
-               return loop
-       return euclidean.getLargestLoop(loopsUnified)
+               return [loop]
+       return loopsUnified
 
 def writeOutput(fileName, shouldAnalyze=True):
        'Widen the carving of a gcode file.'
@@ -121,6 +121,7 @@ class WidenRepository:
                self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute(
                        'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen')
                self.activateWiden = settings.BooleanSetting().getFromValue('Activate Widen', self, False)
+               self.widenWidthOverEdgeWidth = settings.IntSpin().getFromValue(2, 'Widen Width over Edge Width (ratio):', self, 4, 2)
                self.executeTitle = 'Widen'
 
        def execute(self):
@@ -155,15 +156,15 @@ class WidenSkein:
                                else:
                                        widdershinsLoops.append(loop)
                        else:
-#                              clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.doubleEdgeWidth)
+#                              clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.widenEdgeWidth)
 #                              clockwiseInsetLoop.reverse()
 #                              clockwiseInsetLoops.append(clockwiseInsetLoop)
-                               clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.doubleEdgeWidth)
+                               clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.widenEdgeWidth)
                                self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z)
                for widdershinsLoop in widdershinsLoops:
-                       outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.doubleEdgeWidth)
-                       widenedLoop = getWidenedLoop(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.edgeWidth)
-                       self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z)
+                       outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.widenEdgeWidth)
+                       for widenedLoop in getWidenedLoops(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.lessThanHalfEdgeWidth):
+                               self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z)
 
        def getCraftedGcode(self, gcodeText, repository):
                'Parse gcode text and store the widen gcode.'
@@ -188,7 +189,8 @@ class WidenSkein:
                                return
                        elif firstWord == '(<edgeWidth>':
                                self.edgeWidth = float(splitLine[1])
-                               self.doubleEdgeWidth = 2.0 * self.edgeWidth
+                               self.widenEdgeWidth = float(self.repository.widenWidthOverEdgeWidth.value) * self.edgeWidth
+                               self.lessThanHalfEdgeWidth = 0.49 * self.edgeWidth
                        self.distanceFeedRate.addLine(line)
 
        def parseLine(self, line):
index d44137327cbe3695333fc8da173faf26f6e85ef9..bbd30242a2fa839bf30f03627fa7540cd4dc1f82 100644 (file)
@@ -87,28 +87,23 @@ def getPluginsDirectoryPath():
        "Get the plugins directory path."
        return archive.getCraftPluginsDirectoryPath()
 
-def getProcedures( procedure, text ):
-       "Get the procedures up to and including the given procedure."
+def getProcedures(procedure, text):
+       'Get the procedures up to and including the given procedure.'
        craftSequence = getReadCraftSequence()
        sequenceIndexFromProcedure = 0
        if procedure in craftSequence:
                sequenceIndexFromProcedure = craftSequence.index(procedure)
-       sequenceIndexPlusOneFromText = getSequenceIndexPlusOneFromText(text)
-       return craftSequence[ sequenceIndexPlusOneFromText : sequenceIndexFromProcedure + 1 ]
+       craftSequence = craftSequence[: sequenceIndexFromProcedure + 1]
+       for craftSequenceIndex in xrange(len(craftSequence) - 1, -1, -1):
+               procedure = craftSequence[craftSequenceIndex]
+               if gcodec.isProcedureDone(text, procedure):
+                       return craftSequence[craftSequenceIndex + 1 :]
+       return craftSequence
 
 def getReadCraftSequence():
        "Get profile sequence."
        return skeinforge_profile.getCraftTypePluginModule().getCraftSequence()
 
-def getSequenceIndexPlusOneFromText(fileText):
-       "Get the profile sequence index of the file plus one.  Return zero if the procedure is not in the file"
-       craftSequence = getReadCraftSequence()
-       for craftSequenceIndex in xrange( len( craftSequence ) - 1, - 1, - 1 ):
-               procedure = craftSequence[ craftSequenceIndex ]
-               if gcodec.isProcedureDone( fileText, procedure ):
-                       return craftSequenceIndex + 1
-       return 0
-
 def writeChainTextWithNounMessage(fileName, procedure, shouldAnalyze=True):
        'Get and write a crafted shape file.'
        print('')