2012-02-19 23:30:49 +00:00
"""
Euclidean is a collection of python utilities for complex numbers , paths , polygons & Vector3s .
To use euclidean , install python 2. x on your machine , which is avaliable from http : / / www . python . org / download /
Then in the folder which euclidean is in , type ' python ' in a shell to run the python interpreter . Finally type ' import euclidean ' to import these utilities and ' from vector3 import Vector3 ' to import the Vector3 class .
Below are examples of euclidean use .
>> > from euclidean import *
>> > origin = complex ( )
>> > right = complex ( 1.0 , 0.0 )
>> > back = complex ( 0.0 , 1.0 )
>> > getMaximum ( right , back )
1.0 , 1.0
>> > polygon = [ origin , right , back ]
>> > getLoopLength ( polygon )
3.4142135623730949
>> > getAreaLoop ( polygon )
0.5
"""
from __future__ import absolute_import
try :
import psyco
psyco . full ( )
except :
pass
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__
from fabmetheus_utilities . vector3 import Vector3
from fabmetheus_utilities import xml_simple_writer
2012-06-21 17:53:18 +00:00
import sys
2012-02-19 23:30:49 +00:00
import math
import random
2012-06-22 13:37:52 +00:00
if sys . version_info [ 0 ] < 3 :
2012-06-21 17:53:18 +00:00
import cStringIO
else :
import io as cStringIO
2012-02-19 23:30:49 +00:00
__author__ = ' Enrique Perez (perez_enrique@yahoo.com) '
__date__ = ' $Date: 2008/21/04 $ '
__license__ = ' GNU Affero General Public License http://www.gnu.org/licenses/agpl.html '
globalGoldenAngle = 3.8832220774509332 # (math.sqrt(5.0) - 1.0) * math.pi
globalGoldenRatio = 1.6180339887498948482045868 # math.sqrt(1.25) + 0.5
globalTau = math . pi + math . pi # http://tauday.com/
def addElementToListDictionary ( element , key , listDictionary ) :
' Add an element to the list table. '
if key in listDictionary :
listDictionary [ key ] . append ( element )
else :
listDictionary [ key ] = [ element ]
def addElementToListDictionaryIfNotThere ( element , key , listDictionary ) :
' Add the value to the lists. '
if key in listDictionary :
elements = listDictionary [ key ]
if element not in elements :
elements . append ( element )
else :
listDictionary [ key ] = [ element ]
def addElementToPixelList ( element , pixelDictionary , x , y ) :
' Add an element to the pixel list. '
addElementToListDictionary ( element , ( x , y ) , pixelDictionary )
def addElementToPixelListFromPoint ( element , pixelDictionary , point ) :
' Add an element to the pixel list. '
addElementToPixelList ( element , pixelDictionary , int ( round ( point . real ) ) , int ( round ( point . imag ) ) )
def addHorizontallyBoundedPoint ( begin , center , end , horizontalBegin , horizontalEnd , path ) :
' Add point if it is within the horizontal bounds. '
if center . real > = horizontalEnd and center . real < = horizontalBegin :
path . append ( center )
return
if end != None :
if center . real > horizontalBegin and end . real < = horizontalBegin :
centerMinusEnd = center - end
along = ( center . real - horizontalBegin ) / centerMinusEnd . real
path . append ( center - along * centerMinusEnd )
return
if begin != None :
if center . real < horizontalEnd and begin . real > = horizontalEnd :
centerMinusBegin = center - begin
along = ( center . real - horizontalEnd ) / centerMinusBegin . real
path . append ( center - along * centerMinusBegin )
def addListToListTable ( elementList , key , listDictionary ) :
' Add a list to the list table. '
if key in listDictionary :
listDictionary [ key ] + = elementList
else :
listDictionary [ key ] = elementList
def addLoopToPixelTable ( loop , pixelDictionary , width ) :
' Add loop to the pixel table. '
for pointIndex in xrange ( len ( loop ) ) :
pointBegin = loop [ pointIndex ]
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
addValueSegmentToPixelTable ( pointBegin , pointEnd , pixelDictionary , None , width )
def addNestedRingBeginning ( distanceFeedRate , loop , z ) :
' Add nested ring beginning to gcode output. '
distanceFeedRate . addLine ( ' (<nestedRing>) ' )
distanceFeedRate . addLine ( ' (<boundaryPerimeter>) ' )
for point in loop :
pointVector3 = Vector3 ( point . real , point . imag , z )
distanceFeedRate . addLine ( distanceFeedRate . getBoundaryLine ( pointVector3 ) )
def addPathToPixelTable ( path , pixelDictionary , value , width ) :
' Add path to the pixel table. '
for pointIndex in xrange ( len ( path ) - 1 ) :
pointBegin = path [ pointIndex ]
pointEnd = path [ pointIndex + 1 ]
addValueSegmentToPixelTable ( pointBegin , pointEnd , pixelDictionary , value , width )
def addPixelTableToPixelTable ( fromPixelTable , intoPixelTable ) :
' Add from pixel table to the into pixel table. '
for fromPixelTableKey in fromPixelTable . keys ( ) :
intoPixelTable [ fromPixelTableKey ] = fromPixelTable [ fromPixelTableKey ]
def addPixelToPixelTableWithSteepness ( isSteep , pixelDictionary , value , x , y ) :
' Add pixels to the pixel table with steepness. '
if isSteep :
pixelDictionary [ ( y , x ) ] = value
else :
pixelDictionary [ ( x , y ) ] = value
def addPointToPath ( path , pixelDictionary , point , value , width ) :
' Add a point to a path and the pixel table. '
path . append ( point )
if len ( path ) < 2 :
return
begin = path [ - 2 ]
addValueSegmentToPixelTable ( begin , point , pixelDictionary , value , width )
def addSegmentToPixelTable ( beginComplex , endComplex , pixelDictionary , shortenDistanceBegin , shortenDistanceEnd , width ) :
' Add line segment to the pixel table. '
if abs ( beginComplex - endComplex ) < = 0.0 :
return
beginComplex / = width
endComplex / = width
if shortenDistanceBegin > 0.0 :
endMinusBeginComplex = endComplex - beginComplex
endMinusBeginComplexLength = abs ( endMinusBeginComplex )
if endMinusBeginComplexLength < shortenDistanceBegin :
return
beginComplex = beginComplex + endMinusBeginComplex * shortenDistanceBegin / endMinusBeginComplexLength
if shortenDistanceEnd > 0.0 :
beginMinusEndComplex = beginComplex - endComplex
beginMinusEndComplexLength = abs ( beginMinusEndComplex )
if beginMinusEndComplexLength < 0.0 :
return
endComplex = endComplex + beginMinusEndComplex * shortenDistanceEnd / beginMinusEndComplexLength
deltaX = endComplex . real - beginComplex . real
deltaY = endComplex . imag - beginComplex . imag
isSteep = abs ( deltaY ) > abs ( deltaX )
if isSteep :
beginComplex = complex ( beginComplex . imag , beginComplex . real )
endComplex = complex ( endComplex . imag , endComplex . real )
if beginComplex . real > endComplex . real :
endComplex , beginComplex = beginComplex , endComplex
deltaX = endComplex . real - beginComplex . real
deltaY = endComplex . imag - beginComplex . imag
if deltaX > 0.0 :
gradient = deltaY / deltaX
else :
gradient = 0.0
print ( ' Warning, deltaX in addSegmentToPixelTable in euclidean is 0. ' )
print ( beginComplex )
print ( endComplex )
print ( shortenDistanceBegin )
print ( shortenDistanceEnd )
print ( width )
xBegin = int ( round ( beginComplex . real ) )
xEnd = int ( round ( endComplex . real ) )
yIntersection = beginComplex . imag - beginComplex . real * gradient
if isSteep :
pixelDictionary [ ( int ( round ( beginComplex . imag ) ) , xBegin ) ] = None
pixelDictionary [ ( int ( round ( endComplex . imag ) ) , xEnd ) ] = None
for x in xrange ( xBegin + 1 , xEnd ) :
y = int ( math . floor ( yIntersection + x * gradient ) )
pixelDictionary [ ( y , x ) ] = None
pixelDictionary [ ( y + 1 , x ) ] = None
else :
pixelDictionary [ ( xBegin , int ( round ( beginComplex . imag ) ) ) ] = None
pixelDictionary [ ( xEnd , int ( round ( endComplex . imag ) ) ) ] = None
for x in xrange ( xBegin + 1 , xEnd ) :
y = int ( math . floor ( yIntersection + x * gradient ) )
pixelDictionary [ ( x , y ) ] = None
pixelDictionary [ ( x , y + 1 ) ] = None
def addSquareTwoToPixelDictionary ( pixelDictionary , point , value , width ) :
' Add square with two pixels around the center to pixel dictionary. '
point / = width
x = int ( round ( point . real ) )
y = int ( round ( point . imag ) )
for xStep in xrange ( x - 2 , x + 3 ) :
for yStep in xrange ( y - 2 , y + 3 ) :
pixelDictionary [ ( xStep , yStep ) ] = value
def addToThreadsFromLoop ( extrusionHalfWidth , gcodeType , loop , oldOrderedLocation , skein ) :
' Add to threads from the last location from loop. '
loop = getLoopStartingClosest ( extrusionHalfWidth , oldOrderedLocation . dropAxis ( ) , loop )
oldOrderedLocation . x = loop [ 0 ] . real
oldOrderedLocation . y = loop [ 0 ] . imag
gcodeTypeStart = gcodeType
if isWiddershins ( loop ) :
skein . distanceFeedRate . addLine ( ' (< %s > outer ) ' % gcodeType )
else :
skein . distanceFeedRate . addLine ( ' (< %s > inner ) ' % gcodeType )
skein . addGcodeFromThreadZ ( loop + [ loop [ 0 ] ] , oldOrderedLocation . z )
skein . distanceFeedRate . addLine ( ' (</ %s >) ' % gcodeType )
def addToThreadsRemove ( extrusionHalfWidth , nestedRings , oldOrderedLocation , skein , threadSequence ) :
' Add to threads from the last location from nested rings. '
while len ( nestedRings ) > 0 :
getTransferClosestNestedRing ( extrusionHalfWidth , nestedRings , oldOrderedLocation , skein , threadSequence )
def addValueSegmentToPixelTable ( beginComplex , endComplex , pixelDictionary , value , width ) :
' Add line segment to the pixel table. '
if abs ( beginComplex - endComplex ) < = 0.0 :
return
beginComplex / = width
endComplex / = width
deltaX = endComplex . real - beginComplex . real
deltaY = endComplex . imag - beginComplex . imag
isSteep = abs ( deltaY ) > abs ( deltaX )
if isSteep :
beginComplex = complex ( beginComplex . imag , beginComplex . real )
endComplex = complex ( endComplex . imag , endComplex . real )
if beginComplex . real > endComplex . real :
endComplex , beginComplex = beginComplex , endComplex
deltaX = endComplex . real - beginComplex . real
deltaY = endComplex . imag - beginComplex . imag
if deltaX > 0.0 :
gradient = deltaY / deltaX
else :
gradient = 0.0
print ( ' Warning, deltaX in addValueSegmentToPixelTable in euclidean is 0. ' )
print ( beginComplex )
print ( value )
print ( endComplex )
print ( width )
xBegin = int ( round ( beginComplex . real ) )
xEnd = int ( round ( endComplex . real ) )
yIntersection = beginComplex . imag - beginComplex . real * gradient
if isSteep :
pixelDictionary [ ( int ( round ( beginComplex . imag ) ) , xBegin ) ] = value
pixelDictionary [ ( int ( round ( endComplex . imag ) ) , xEnd ) ] = value
for x in xrange ( xBegin + 1 , xEnd ) :
y = int ( math . floor ( yIntersection + x * gradient ) )
pixelDictionary [ ( y , x ) ] = value
pixelDictionary [ ( y + 1 , x ) ] = value
else :
pixelDictionary [ ( xBegin , int ( round ( beginComplex . imag ) ) ) ] = value
pixelDictionary [ ( xEnd , int ( round ( endComplex . imag ) ) ) ] = value
for x in xrange ( xBegin + 1 , xEnd ) :
y = int ( math . floor ( yIntersection + x * gradient ) )
pixelDictionary [ ( x , y ) ] = value
pixelDictionary [ ( x , y + 1 ) ] = value
def addValueToOutput ( depth , keyInput , output , value ) :
' Add value to the output. '
depthStart = ' ' * depth
output . write ( ' %s %s : ' % ( depthStart , keyInput ) )
if value . __class__ == dict :
output . write ( ' \n ' )
keys = value . keys ( )
keys . sort ( )
for key in keys :
addValueToOutput ( depth + 1 , key , output , value [ key ] )
return
if value . __class__ == list :
output . write ( ' \n ' )
for elementIndex , element in enumerate ( value ) :
addValueToOutput ( depth + 1 , elementIndex , output , element )
return
output . write ( ' %s \n ' % value )
def addXIntersectionIndexesFromLoopListsY ( loopLists , xIntersectionIndexList , y ) :
' Add the x intersection indexes for the loop lists. '
for loopListIndex in xrange ( len ( loopLists ) ) :
loopList = loopLists [ loopListIndex ]
addXIntersectionIndexesFromLoopsY ( loopList , loopListIndex , xIntersectionIndexList , y )
def addXIntersectionIndexesFromLoopsY ( loops , solidIndex , xIntersectionIndexList , y ) :
' Add the x intersection indexes for the loops. '
for loop in loops :
addXIntersectionIndexesFromLoopY ( loop , solidIndex , xIntersectionIndexList , y )
def addXIntersectionIndexesFromLoopY ( loop , solidIndex , xIntersectionIndexList , y ) :
' Add the x intersection indexes for a loop. '
for pointIndex in xrange ( len ( loop ) ) :
pointFirst = loop [ pointIndex ]
pointSecond = loop [ ( pointIndex + 1 ) % len ( loop ) ]
xIntersection = getXIntersectionIfExists ( pointFirst , pointSecond , y )
if xIntersection != None :
xIntersectionIndexList . append ( XIntersectionIndex ( solidIndex , xIntersection ) )
def addXIntersectionIndexesFromSegment ( index , segment , xIntersectionIndexList ) :
' Add the x intersection indexes from the segment. '
for endpoint in segment :
xIntersectionIndexList . append ( XIntersectionIndex ( index , endpoint . point . real ) )
def addXIntersectionIndexesFromSegments ( index , segments , xIntersectionIndexList ) :
' Add the x intersection indexes from the segments. '
for segment in segments :
addXIntersectionIndexesFromSegment ( index , segment , xIntersectionIndexList )
def addXIntersectionIndexesFromXIntersections ( index , xIntersectionIndexList , xIntersections ) :
' Add the x intersection indexes from the XIntersections. '
for xIntersection in xIntersections :
xIntersectionIndexList . append ( XIntersectionIndex ( index , xIntersection ) )
def addXIntersections ( loop , xIntersections , y ) :
' Add the x intersections for a loop. '
for pointIndex in xrange ( len ( loop ) ) :
pointFirst = loop [ pointIndex ]
pointSecond = loop [ ( pointIndex + 1 ) % len ( loop ) ]
xIntersection = getXIntersectionIfExists ( pointFirst , pointSecond , y )
if xIntersection != None :
xIntersections . append ( xIntersection )
def addXIntersectionsFromLoopForTable ( loop , xIntersectionsTable , width ) :
' Add the x intersections for a loop into a table. '
for pointIndex in xrange ( len ( loop ) ) :
pointBegin = loop [ pointIndex ]
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
if pointBegin . imag > pointEnd . imag :
pointOriginal = pointBegin
pointBegin = pointEnd
pointEnd = pointOriginal
fillBegin = int ( math . ceil ( pointBegin . imag / width ) )
fillEnd = int ( math . ceil ( pointEnd . imag / width ) )
if fillEnd > fillBegin :
secondMinusFirstComplex = pointEnd - pointBegin
secondMinusFirstImaginaryOverReal = secondMinusFirstComplex . real / secondMinusFirstComplex . imag
beginRealMinusImaginary = pointBegin . real - pointBegin . imag * secondMinusFirstImaginaryOverReal
for fillLine in xrange ( fillBegin , fillEnd ) :
y = fillLine * width
xIntersection = y * secondMinusFirstImaginaryOverReal + beginRealMinusImaginary
addElementToListDictionary ( xIntersection , fillLine , xIntersectionsTable )
def addXIntersectionsFromLoops ( loops , xIntersections , y ) :
' Add the x intersections for the loops. '
for loop in loops :
addXIntersections ( loop , xIntersections , y )
def addXIntersectionsFromLoopsForTable ( loops , xIntersectionsTable , width ) :
' Add the x intersections for a loop into a table. '
for loop in loops :
addXIntersectionsFromLoopForTable ( loop , xIntersectionsTable , width )
def compareSegmentLength ( endpoint , otherEndpoint ) :
' Get comparison in order to sort endpoints in ascending order of segment length. '
if endpoint . segmentLength > otherEndpoint . segmentLength :
return 1
if endpoint . segmentLength < otherEndpoint . segmentLength :
return - 1
return 0
2012-03-22 12:43:07 +00:00
def concatenateRemovePath ( connectedPaths , pathIndex , paths , pixelDictionary , segments , sharpestProduct , width ) :
2012-02-19 23:30:49 +00:00
' Get connected paths from paths. '
2012-03-22 12:43:07 +00:00
bottomSegment = segments [ pathIndex ]
path = paths [ pathIndex ]
2012-02-19 23:30:49 +00:00
if bottomSegment == None :
connectedPaths . append ( path )
return
2012-03-22 12:43:07 +00:00
endpoints = getEndpointsFromSegments ( segments [ pathIndex + 1 : ] )
2012-02-19 23:30:49 +00:00
bottomSegmentEndpoint = bottomSegment [ 0 ]
2012-03-22 12:43:07 +00:00
nextEndpoint = bottomSegmentEndpoint . getClosestMissCheckEndpointPath ( endpoints , bottomSegmentEndpoint . path , pixelDictionary , sharpestProduct , width )
2012-02-19 23:30:49 +00:00
if nextEndpoint == None :
bottomSegmentEndpoint = bottomSegment [ 1 ]
2012-03-22 12:43:07 +00:00
nextEndpoint = bottomSegmentEndpoint . getClosestMissCheckEndpointPath ( endpoints , bottomSegmentEndpoint . path , pixelDictionary , sharpestProduct , width )
2012-02-19 23:30:49 +00:00
if nextEndpoint == None :
connectedPaths . append ( path )
return
2012-03-22 12:43:07 +00:00
if len ( bottomSegmentEndpoint . path ) > 0 and len ( nextEndpoint . path ) > 0 :
2012-02-19 23:30:49 +00:00
bottomEnd = bottomSegmentEndpoint . path [ - 1 ]
nextBegin = nextEndpoint . path [ - 1 ]
2012-03-22 12:43:07 +00:00
nextMinusBottomNormalized = getNormalized ( nextBegin - bottomEnd )
2012-02-19 23:30:49 +00:00
if len ( bottomSegmentEndpoint . path ) > 1 :
bottomPenultimate = bottomSegmentEndpoint . path [ - 2 ]
2012-03-22 12:43:07 +00:00
if getDotProduct ( getNormalized ( bottomPenultimate - bottomEnd ) , nextMinusBottomNormalized ) > 0.99 :
2012-02-19 23:30:49 +00:00
connectedPaths . append ( path )
return
if len ( nextEndpoint . path ) > 1 :
nextPenultimate = nextEndpoint . path [ - 2 ]
2012-03-22 12:43:07 +00:00
if getDotProduct ( getNormalized ( nextPenultimate - nextBegin ) , - nextMinusBottomNormalized ) > 0.99 :
2012-02-19 23:30:49 +00:00
connectedPaths . append ( path )
return
nextEndpoint . path . reverse ( )
concatenatedPath = bottomSegmentEndpoint . path + nextEndpoint . path
2012-03-22 12:43:07 +00:00
paths [ nextEndpoint . pathIndex ] = concatenatedPath
segments [ nextEndpoint . pathIndex ] = getSegmentFromPath ( concatenatedPath , nextEndpoint . pathIndex )
addValueSegmentToPixelTable ( bottomSegmentEndpoint . point , nextEndpoint . point , pixelDictionary , None , width )
2012-02-19 23:30:49 +00:00
def getAngleAroundZAxisDifference ( subtractFromVec3 , subtractVec3 ) :
' Get the angle around the Z axis difference between a pair of Vector3s. '
subtractVectorMirror = complex ( subtractVec3 . x , - subtractVec3 . y )
differenceVector = getRoundZAxisByPlaneAngle ( subtractVectorMirror , subtractFromVec3 )
return math . atan2 ( differenceVector . y , differenceVector . x )
def getAngleDifferenceByComplex ( subtractFromComplex , subtractComplex ) :
' Get the angle between a pair of normalized complexes. '
subtractComplexMirror = complex ( subtractComplex . real , - subtractComplex . imag )
differenceComplex = subtractComplexMirror * subtractFromComplex
return math . atan2 ( differenceComplex . imag , differenceComplex . real )
def getAreaLoop ( loop ) :
' Get the area of a complex polygon. '
areaLoopDouble = 0.0
for pointIndex , point in enumerate ( loop ) :
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
areaLoopDouble + = point . real * pointEnd . imag - pointEnd . real * point . imag
return 0.5 * areaLoopDouble
def getAreaLoopAbsolute ( loop ) :
' Get the absolute area of a complex polygon. '
return abs ( getAreaLoop ( loop ) )
def getAreaLoops ( loops ) :
' Get the area of a list of complex polygons. '
areaLoops = 0.0
for loop in loops :
areaLoops + = getAreaLoop ( loop )
return areaLoops
def getAreaVector3LoopAbsolute ( loop ) :
' Get the absolute area of a vector3 polygon. '
return getAreaLoopAbsolute ( getComplexPath ( loop ) )
def getAroundLoop ( begin , end , loop ) :
' Get an arc around a loop. '
aroundLoop = [ ]
if end < = begin :
end + = len ( loop )
for pointIndex in xrange ( begin , end ) :
aroundLoop . append ( loop [ pointIndex % len ( loop ) ] )
return aroundLoop
def getAwayPath ( path , radius ) :
' Get a path with only the points that are far enough away from each other, except for the last point. '
if len ( path ) < 2 :
return path
lastPoint = path [ - 1 ]
awayPath = getAwayPoints ( path , radius )
if len ( awayPath ) == 0 :
return [ lastPoint ]
if abs ( lastPoint - awayPath [ - 1 ] ) > 0.001 * radius :
awayPath . append ( lastPoint )
return awayPath
def getAwayPoints ( points , radius ) :
' Get a path with only the points that are far enough away from each other. '
awayPoints = [ ]
oneOverOverlapDistance = 1000.0 / radius
pixelDictionary = { }
for point in points :
x = int ( point . real * oneOverOverlapDistance )
y = int ( point . imag * oneOverOverlapDistance )
if not getSquareIsOccupied ( pixelDictionary , x , y ) :
awayPoints . append ( point )
pixelDictionary [ ( x , y ) ] = None
return awayPoints
def getBooleanFromDictionary ( defaultBoolean , dictionary , key ) :
' Get boolean from the dictionary and key. '
if key not in dictionary :
return defaultBoolean
return getBooleanFromValue ( dictionary [ key ] )
def getBooleanFromValue ( value ) :
' Get boolean from the word. '
firstCharacter = str ( value ) . lower ( ) . lstrip ( ) [ : 1 ]
return firstCharacter == ' t ' or firstCharacter == ' 1 '
def getBottomByPath ( path ) :
' Get the bottom of the path. '
bottom = 987654321987654321.0
for point in path :
bottom = min ( bottom , point . z )
return bottom
def getBottomByPaths ( paths ) :
' Get the bottom of the paths. '
bottom = 987654321987654321.0
for path in paths :
for point in path :
bottom = min ( bottom , point . z )
return bottom
def getClippedAtEndLoopPath ( clip , loopPath ) :
' Get a clipped loop path. '
if clip < = 0.0 :
return loopPath
loopPathLength = getPathLength ( loopPath )
clip = min ( clip , 0.3 * loopPathLength )
lastLength = 0.0
pointIndex = 0
totalLength = 0.0
clippedLength = loopPathLength - clip
while totalLength < clippedLength and pointIndex < len ( loopPath ) - 1 :
firstPoint = loopPath [ pointIndex ]
secondPoint = loopPath [ pointIndex + 1 ]
pointIndex + = 1
lastLength = totalLength
totalLength + = abs ( firstPoint - secondPoint )
remainingLength = clippedLength - lastLength
clippedLoopPath = loopPath [ : pointIndex ]
ultimateClippedPoint = loopPath [ pointIndex ]
penultimateClippedPoint = clippedLoopPath [ - 1 ]
segment = ultimateClippedPoint - penultimateClippedPoint
segmentLength = abs ( segment )
if segmentLength < = 0.0 :
return clippedLoopPath
newUltimatePoint = penultimateClippedPoint + segment * remainingLength / segmentLength
return clippedLoopPath + [ newUltimatePoint ]
def getClippedLoopPath ( clip , loopPath ) :
' Get a clipped loop path. '
if clip < = 0.0 :
return loopPath
loopPathLength = getPathLength ( loopPath )
clip = min ( clip , 0.3 * loopPathLength )
lastLength = 0.0
pointIndex = 0
totalLength = 0.0
while totalLength < clip and pointIndex < len ( loopPath ) - 1 :
firstPoint = loopPath [ pointIndex ]
secondPoint = loopPath [ pointIndex + 1 ]
pointIndex + = 1
lastLength = totalLength
totalLength + = abs ( firstPoint - secondPoint )
remainingLength = clip - lastLength
clippedLoopPath = loopPath [ pointIndex : ]
ultimateClippedPoint = clippedLoopPath [ 0 ]
penultimateClippedPoint = loopPath [ pointIndex - 1 ]
segment = ultimateClippedPoint - penultimateClippedPoint
segmentLength = abs ( segment )
loopPath = clippedLoopPath
if segmentLength > 0.0 :
newUltimatePoint = penultimateClippedPoint + segment * remainingLength / segmentLength
loopPath = [ newUltimatePoint ] + loopPath
return getClippedAtEndLoopPath ( clip , loopPath )
def getClippedSimplifiedLoopPath ( clip , loopPath , radius ) :
' Get a clipped and simplified loop path. '
return getSimplifiedPath ( getClippedLoopPath ( clip , loopPath ) , radius )
def getClosestDistanceIndexToLine ( point , loop ) :
' Get the distance squared to the closest segment of the loop and index of that segment. '
smallestDistance = 987654321987654321.0
closestDistanceIndex = None
for pointIndex in xrange ( len ( loop ) ) :
segmentBegin = loop [ pointIndex ]
segmentEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
distance = getDistanceToPlaneSegment ( segmentBegin , segmentEnd , point )
if distance < smallestDistance :
smallestDistance = distance
closestDistanceIndex = DistanceIndex ( distance , pointIndex )
return closestDistanceIndex
def getClosestPointOnSegment ( segmentBegin , segmentEnd , point ) :
' Get the closest point on the segment. '
segmentDifference = segmentEnd - segmentBegin
if abs ( segmentDifference ) < = 0.0 :
return segmentBegin
pointMinusSegmentBegin = point - segmentBegin
beginPlaneDot = getDotProduct ( pointMinusSegmentBegin , segmentDifference )
differencePlaneDot = getDotProduct ( segmentDifference , segmentDifference )
intercept = beginPlaneDot / differencePlaneDot
intercept = max ( intercept , 0.0 )
intercept = min ( intercept , 1.0 )
return segmentBegin + segmentDifference * intercept
def getComplexByCommaString ( valueCommaString ) :
' Get the commaString as a complex. '
try :
splitLine = valueCommaString . replace ( ' , ' , ' ' ) . split ( )
return complex ( float ( splitLine [ 0 ] ) , float ( splitLine [ 1 ] ) )
except :
pass
return None
def getComplexByWords ( words , wordIndex = 0 ) :
' Get the complex by the first two words. '
try :
return complex ( float ( words [ wordIndex ] ) , float ( words [ wordIndex + 1 ] ) )
except :
pass
return None
def getComplexDefaultByDictionary ( defaultComplex , dictionary , key ) :
' Get the value as a complex. '
if key in dictionary :
return complex ( dictionary [ key ] . strip ( ) . replace ( ' ( ' , ' ' ) . replace ( ' ) ' , ' ' ) )
return defaultComplex
def getComplexDefaultByDictionaryKeys ( defaultComplex , dictionary , keyX , keyY ) :
' Get the value as a complex. '
x = getFloatDefaultByDictionary ( defaultComplex . real , dictionary , keyX )
y = getFloatDefaultByDictionary ( defaultComplex . real , dictionary , keyY )
return complex ( x , y )
def getComplexPath ( vector3Path ) :
' Get the complex path from the vector3 path. '
complexPath = [ ]
for point in vector3Path :
complexPath . append ( point . dropAxis ( ) )
return complexPath
def getComplexPathByMultiplier ( multiplier , path ) :
' Get the multiplied complex path. '
complexPath = [ ]
for point in path :
complexPath . append ( multiplier * point )
return complexPath
def getComplexPaths ( vector3Paths ) :
' Get the complex paths from the vector3 paths. '
complexPaths = [ ]
for vector3Path in vector3Paths :
complexPaths . append ( getComplexPath ( vector3Path ) )
return complexPaths
def getComplexPolygon ( center , radius , sides , startAngle = 0.0 ) :
' Get the complex polygon. '
complexPolygon = [ ]
sideAngle = 2.0 * math . pi / float ( sides )
for side in xrange ( abs ( sides ) ) :
unitPolar = getWiddershinsUnitPolar ( startAngle )
complexPolygon . append ( unitPolar * radius + center )
startAngle + = sideAngle
return complexPolygon
def getComplexPolygonByComplexRadius ( radius , sides , startAngle = 0.0 ) :
' Get the complex polygon. '
complexPolygon = [ ]
sideAngle = 2.0 * math . pi / float ( sides )
for side in xrange ( abs ( sides ) ) :
unitPolar = getWiddershinsUnitPolar ( startAngle )
complexPolygon . append ( complex ( unitPolar . real * radius . real , unitPolar . imag * radius . imag ) )
startAngle + = sideAngle
return complexPolygon
def getComplexPolygonByStartEnd ( endAngle , radius , sides , startAngle = 0.0 ) :
' Get the complex polygon by start and end angle. '
angleExtent = endAngle - startAngle
sideAngle = 2.0 * math . pi / float ( sides )
sides = int ( math . ceil ( abs ( angleExtent / sideAngle ) ) )
sideAngle = angleExtent / float ( sides )
complexPolygon = [ ]
for side in xrange ( abs ( sides ) + 1 ) :
unitPolar = getWiddershinsUnitPolar ( startAngle )
complexPolygon . append ( unitPolar * radius )
startAngle + = sideAngle
return getLoopWithoutCloseEnds ( 0.000001 * radius , complexPolygon )
def getConcatenatedList ( originalLists ) :
' Get the lists as one concatenated list. '
concatenatedList = [ ]
for originalList in originalLists :
concatenatedList + = originalList
return concatenatedList
2012-03-22 12:43:07 +00:00
def getConnectedPaths ( paths , pixelDictionary , sharpestProduct , width ) :
2012-02-19 23:30:49 +00:00
' Get connected paths from paths. '
if len ( paths ) < 2 :
return paths
connectedPaths = [ ]
segments = [ ]
2012-03-22 12:43:07 +00:00
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 ] )
2012-02-19 23:30:49 +00:00
return connectedPaths
def getCrossProduct ( firstComplex , secondComplex ) :
' Get z component cross product of a pair of complexes. '
return firstComplex . real * secondComplex . imag - firstComplex . imag * secondComplex . real
def getDecimalPlacesCarried ( extraDecimalPlaces , value ) :
' Get decimal places carried by the decimal places of the value plus the extraDecimalPlaces. '
return max ( 0 , 1 + int ( math . ceil ( extraDecimalPlaces - math . log10 ( value ) ) ) )
def getDiagonalFlippedLoop ( loop ) :
' Get loop flipped over the dialogonal, in other words with the x and y swapped. '
diagonalFlippedLoop = [ ]
for point in loop :
diagonalFlippedLoop . append ( complex ( point . imag , point . real ) )
return diagonalFlippedLoop
def getDiagonalFlippedLoops ( loops ) :
' Get loops flipped over the dialogonal, in other words with the x and y swapped. '
diagonalFlippedLoops = [ ]
for loop in loops :
diagonalFlippedLoops . append ( getDiagonalFlippedLoop ( loop ) )
return diagonalFlippedLoops
def getDictionaryString ( dictionary ) :
' Get the dictionary string. '
output = cStringIO . StringIO ( )
keys = dictionary . keys ( )
keys . sort ( )
for key in keys :
addValueToOutput ( 0 , key , output , dictionary [ key ] )
return output . getvalue ( )
def getDistanceToLine ( begin , end , point ) :
' Get the distance from a vector3 point to an infinite line. '
pointMinusBegin = point - begin
if begin == end :
return abs ( pointMinusBegin )
endMinusBegin = end - begin
return abs ( endMinusBegin . cross ( pointMinusBegin ) ) / abs ( endMinusBegin )
def getDistanceToLineByPath ( begin , end , path ) :
' Get the maximum distance from a path to an infinite line. '
distanceToLine = - 987654321.0
for point in path :
distanceToLine = max ( getDistanceToLine ( begin , end , point ) , distanceToLine )
return distanceToLine
def getDistanceToLineByPaths ( begin , end , paths ) :
' Get the maximum distance from paths to an infinite line. '
distanceToLine = - 987654321.0
for path in paths :
distanceToLine = max ( getDistanceToLineByPath ( begin , end , path ) , distanceToLine )
return distanceToLine
def getDistanceToPlaneSegment ( segmentBegin , segmentEnd , point ) :
' Get the distance squared from a point to the x & y components of a segment. '
segmentDifference = segmentEnd - segmentBegin
pointMinusSegmentBegin = point - segmentBegin
beginPlaneDot = getDotProduct ( pointMinusSegmentBegin , segmentDifference )
if beginPlaneDot < = 0.0 :
return abs ( point - segmentBegin ) * abs ( point - segmentBegin )
differencePlaneDot = getDotProduct ( segmentDifference , segmentDifference )
if differencePlaneDot < = beginPlaneDot :
return abs ( point - segmentEnd ) * abs ( point - segmentEnd )
intercept = beginPlaneDot / differencePlaneDot
interceptPerpendicular = segmentBegin + segmentDifference * intercept
return abs ( point - interceptPerpendicular ) * abs ( point - interceptPerpendicular )
def getDotProduct ( firstComplex , secondComplex ) :
' Get the dot product of a pair of complexes. '
return firstComplex . real * secondComplex . real + firstComplex . imag * secondComplex . imag
def getDotProductPlusOne ( firstComplex , secondComplex ) :
' Get the dot product plus one of the x and y components of a pair of Vector3s. '
return 1.0 + getDotProduct ( firstComplex , secondComplex )
def getDurationString ( seconds ) :
' Get the duration string. '
secondsRounded = int ( round ( seconds ) )
durationString = getPluralString ( secondsRounded % 60 , ' second ' )
if seconds < 60 :
return durationString
durationString = ' %s %s ' % ( getPluralString ( ( secondsRounded / 60 ) % 60 , ' minute ' ) , durationString )
if seconds < 3600 :
return durationString
return ' %s %s ' % ( getPluralString ( secondsRounded / 3600 , ' hour ' ) , durationString )
def getEndpointFromPath ( path , pathIndex ) :
' Get endpoint segment from a path. '
begin = path [ - 1 ]
end = path [ - 2 ]
endpointBegin = Endpoint ( )
endpointEnd = Endpoint ( ) . getFromOtherPoint ( endpointBegin , end )
endpointBegin . getFromOtherPoint ( endpointEnd , begin )
endpointBegin . path = path
endpointBegin . pathIndex = pathIndex
return endpointBegin
def getEndpointsFromSegments ( segments ) :
' Get endpoints from segments. '
endpoints = [ ]
for segment in segments :
for endpoint in segment :
endpoints . append ( endpoint )
return endpoints
def getEndpointsFromSegmentTable ( segmentTable ) :
' Get the endpoints from the segment table. '
endpoints = [ ]
segmentTableKeys = segmentTable . keys ( )
segmentTableKeys . sort ( )
for segmentTableKey in segmentTableKeys :
for segment in segmentTable [ segmentTableKey ] :
for endpoint in segment :
endpoints . append ( endpoint )
return endpoints
def getEnumeratorKeys ( enumerator , keys ) :
' Get enumerator keys. '
if len ( keys ) == 1 :
return keys [ 0 ]
return getEnumeratorKeysExceptForOneArgument ( enumerator , keys )
def getEnumeratorKeysAlwaysList ( enumerator , keys ) :
' Get enumerator keys. '
if keys . __class__ != list :
return [ keys ]
if len ( keys ) == 1 :
return keys
return getEnumeratorKeysExceptForOneArgument ( enumerator , keys )
def getEnumeratorKeysExceptForOneArgument ( enumerator , keys ) :
' Get enumerator keys, except when there is one argument. '
if len ( keys ) == 0 :
return range ( 0 , len ( enumerator ) )
beginIndex = keys [ 0 ]
endIndex = keys [ 1 ]
if len ( keys ) == 2 :
if beginIndex == None :
beginIndex = 0
if endIndex == None :
endIndex = len ( enumerator )
return range ( beginIndex , endIndex )
step = keys [ 2 ]
beginIndexDefault = 0
endIndexDefault = len ( enumerator )
if step < 0 :
beginIndexDefault = endIndexDefault - 1
endIndexDefault = - 1
if beginIndex == None :
beginIndex = beginIndexDefault
if endIndex == None :
endIndex = endIndexDefault
return range ( beginIndex , endIndex , step )
def getFillOfSurroundings ( nestedRings , penultimateFillLoops ) :
' Get extra fill loops of nested rings. '
fillOfSurroundings = [ ]
for nestedRing in nestedRings :
fillOfSurroundings + = nestedRing . getFillLoops ( penultimateFillLoops )
return fillOfSurroundings
def getFlattenedNestedRings ( nestedRings ) :
' Get flattened nested rings. '
flattenedNestedRings = [ ]
for nestedRing in nestedRings :
nestedRing . addFlattenedNestedRings ( flattenedNestedRings )
return flattenedNestedRings
def getFloatDefaultByDictionary ( defaultFloat , dictionary , key ) :
' Get the value as a float. '
evaluatedFloat = None
if key in dictionary :
evaluatedFloat = getFloatFromValue ( dictionary [ key ] )
if evaluatedFloat == None :
return defaultFloat
return evaluatedFloat
def getFloatFromValue ( value ) :
' Get the value as a float. '
try :
return float ( value )
except :
pass
return None
def getFourSignificantFigures ( number ) :
' Get number rounded to four significant figures as a string. '
if number == None :
return None
absoluteNumber = abs ( number )
if absoluteNumber > = 100.0 :
return getRoundedToPlacesString ( 2 , number )
if absoluteNumber < 0.000000001 :
return getRoundedToPlacesString ( 13 , number )
return getRoundedToPlacesString ( 3 - math . floor ( math . log10 ( absoluteNumber ) ) , number )
def getHalfSimplifiedLoop ( loop , radius , remainder ) :
' Get the loop with half of the points inside the channel removed. '
if len ( loop ) < 2 :
return loop
2012-04-21 22:46:34 +00:00
channelRadius = abs ( radius * .01 )
2012-02-19 23:30:49 +00:00
simplified = [ ]
addIndex = 0
if remainder == 1 :
addIndex = len ( loop ) - 1
for pointIndex in xrange ( len ( loop ) ) :
point = loop [ pointIndex ]
if pointIndex % 2 == remainder or pointIndex == addIndex :
simplified . append ( point )
elif not isWithinChannel ( channelRadius , pointIndex , loop ) :
simplified . append ( point )
return simplified
def getHalfSimplifiedPath ( path , radius , remainder ) :
' Get the path with half of the points inside the channel removed. '
if len ( path ) < 2 :
return path
2012-04-21 22:46:34 +00:00
channelRadius = abs ( radius * .01 )
2012-02-19 23:30:49 +00:00
simplified = [ path [ 0 ] ]
for pointIndex in xrange ( 1 , len ( path ) - 1 ) :
point = path [ pointIndex ]
if pointIndex % 2 == remainder :
simplified . append ( point )
elif not isWithinChannel ( channelRadius , pointIndex , path ) :
simplified . append ( point )
simplified . append ( path [ - 1 ] )
return simplified
def getHorizontallyBoundedPath ( horizontalBegin , horizontalEnd , path ) :
' Get horizontally bounded path. '
horizontallyBoundedPath = [ ]
for pointIndex , point in enumerate ( path ) :
begin = None
previousIndex = pointIndex - 1
if previousIndex > = 0 :
begin = path [ previousIndex ]
end = None
nextIndex = pointIndex + 1
if nextIndex < len ( path ) :
end = path [ nextIndex ]
addHorizontallyBoundedPoint ( begin , point , end , horizontalBegin , horizontalEnd , horizontallyBoundedPath )
return horizontallyBoundedPath
def getIncrementFromRank ( rank ) :
' Get the increment from the rank which is 0 at 1 and increases by three every power of ten. '
rankZone = int ( math . floor ( rank / 3 ) )
rankModulo = rank % 3
powerOfTen = pow ( 10 , rankZone )
moduloMultipliers = ( 1 , 2 , 5 )
return float ( powerOfTen * moduloMultipliers [ rankModulo ] )
def getInsidesAddToOutsides ( loops , outsides ) :
' Add loops to either the insides or outsides. '
insides = [ ]
for loopIndex in xrange ( len ( loops ) ) :
loop = loops [ loopIndex ]
if isInsideOtherLoops ( loopIndex , loops ) :
insides . append ( loop )
else :
outsides . append ( loop )
return insides
def getIntermediateLocation ( alongWay , begin , end ) :
' Get the intermediate location between begin and end. '
return begin * ( 1.0 - alongWay ) + end * alongWay
def getIntersectionOfXIntersectionIndexes ( totalSolidSurfaceThickness , xIntersectionIndexList ) :
' Get x intersections from surrounding layers. '
xIntersectionList = [ ]
solidTable = { }
solid = False
xIntersectionIndexList . sort ( )
for xIntersectionIndex in xIntersectionIndexList :
toggleHashtable ( solidTable , xIntersectionIndex . index , ' ' )
oldSolid = solid
solid = len ( solidTable ) > = totalSolidSurfaceThickness
if oldSolid != solid :
xIntersectionList . append ( xIntersectionIndex . x )
return xIntersectionList
def getIntersectionOfXIntersectionsTables ( xIntersectionsTables ) :
' Get the intersection of the XIntersections tables. '
if len ( xIntersectionsTables ) == 0 :
return { }
intersectionOfXIntersectionsTables = { }
firstIntersectionTable = xIntersectionsTables [ 0 ]
for firstIntersectionTableKey in firstIntersectionTable . keys ( ) :
xIntersectionIndexList = [ ]
for xIntersectionsTableIndex in xrange ( len ( xIntersectionsTables ) ) :
xIntersectionsTable = xIntersectionsTables [ xIntersectionsTableIndex ]
if firstIntersectionTableKey in xIntersectionsTable :
addXIntersectionIndexesFromXIntersections ( xIntersectionsTableIndex , xIntersectionIndexList , xIntersectionsTable [ firstIntersectionTableKey ] )
xIntersections = getIntersectionOfXIntersectionIndexes ( len ( xIntersectionsTables ) , xIntersectionIndexList )
if len ( xIntersections ) > 0 :
intersectionOfXIntersectionsTables [ firstIntersectionTableKey ] = xIntersections
return intersectionOfXIntersectionsTables
def getIntFromValue ( value ) :
' Get the value as an int. '
try :
return int ( value )
except :
pass
return None
def getIsInFilledRegion ( loops , point ) :
' Determine if the point is in the filled region of the loops. '
return getNumberOfIntersectionsToLeftOfLoops ( loops , point ) % 2 == 1
def getIsInFilledRegionByPaths ( loops , paths ) :
' Determine if the point of any path is in the filled region of the loops. '
for path in paths :
if len ( path ) > 0 :
if getIsInFilledRegion ( loops , path [ 0 ] ) :
return True
return False
def getIsRadianClose ( firstRadian , secondRadian ) :
' Determine if the firstRadian is close to the secondRadian. '
return abs ( math . pi - abs ( math . pi - ( ( firstRadian - secondRadian ) % ( math . pi + math . pi ) ) ) ) < 0.000001
def getIsWiddershinsByVector3 ( polygon ) :
' Determine if the polygon goes round in the widdershins direction. '
return isWiddershins ( getComplexPath ( polygon ) )
def getJoinOfXIntersectionIndexes ( xIntersectionIndexList ) :
' Get joined x intersections from surrounding layers. '
xIntersections = [ ]
solidTable = { }
solid = False
xIntersectionIndexList . sort ( )
for xIntersectionIndex in xIntersectionIndexList :
toggleHashtable ( solidTable , xIntersectionIndex . index , ' ' )
oldSolid = solid
solid = len ( solidTable ) > 0
if oldSolid != solid :
xIntersections . append ( xIntersectionIndex . x )
return xIntersections
def getLargestLoop ( loops ) :
' Get largest loop from loops. '
largestArea = - 987654321.0
largestLoop = [ ]
for loop in loops :
loopArea = abs ( getAreaLoopAbsolute ( loop ) )
if loopArea > largestArea :
largestArea = loopArea
largestLoop = loop
return largestLoop
def getLeftPoint ( points ) :
' Get the leftmost complex point in the points. '
leftmost = 987654321.0
leftPointComplex = None
for pointComplex in points :
if pointComplex . real < leftmost :
leftmost = pointComplex . real
leftPointComplex = pointComplex
return leftPointComplex
def getLeftPointIndex ( points ) :
' Get the index of the leftmost complex point in the points. '
if len ( points ) < 1 :
return None
leftPointIndex = 0
for pointIndex in xrange ( len ( points ) ) :
if points [ pointIndex ] . real < points [ leftPointIndex ] . real :
leftPointIndex = pointIndex
return leftPointIndex
def getListTableElements ( listDictionary ) :
' Get all the element in a list table. '
listDictionaryElements = [ ]
for listDictionaryValue in listDictionary . values ( ) :
listDictionaryElements + = listDictionaryValue
return listDictionaryElements
def getLoopCentroid ( polygonComplex ) :
' Get the area of a complex polygon using http://en.wikipedia.org/wiki/Centroid. '
polygonDoubleArea = 0.0
polygonTorque = 0.0
for pointIndex in xrange ( len ( polygonComplex ) ) :
pointBegin = polygonComplex [ pointIndex ]
pointEnd = polygonComplex [ ( pointIndex + 1 ) % len ( polygonComplex ) ]
doubleArea = pointBegin . real * pointEnd . imag - pointEnd . real * pointBegin . imag
doubleCenter = complex ( pointBegin . real + pointEnd . real , pointBegin . imag + pointEnd . imag )
polygonDoubleArea + = doubleArea
polygonTorque + = doubleArea * doubleCenter
torqueMultiplier = 0.333333333333333333333333 / polygonDoubleArea
return polygonTorque * torqueMultiplier
def getLoopConvex ( points ) :
' Get convex hull of points using gift wrap algorithm. '
loopConvex = [ ]
pointSet = set ( )
for point in points :
if point not in pointSet :
pointSet . add ( point )
loopConvex . append ( point )
if len ( loopConvex ) < 4 :
return loopConvex
leftPoint = getLeftPoint ( loopConvex )
lastPoint = leftPoint
pointSet . remove ( leftPoint )
loopConvex = [ leftPoint ]
lastSegment = complex ( 0.0 , 1.0 )
while True :
greatestDotProduct = - 9.9
greatestPoint = None
greatestSegment = None
if len ( loopConvex ) > 2 :
nextSegment = getNormalized ( leftPoint - lastPoint )
if abs ( nextSegment ) > 0.0 :
greatestDotProduct = getDotProduct ( nextSegment , lastSegment )
for point in pointSet :
nextSegment = getNormalized ( point - lastPoint )
if abs ( nextSegment ) > 0.0 :
dotProduct = getDotProduct ( nextSegment , lastSegment )
if dotProduct > greatestDotProduct :
greatestDotProduct = dotProduct
greatestPoint = point
greatestSegment = nextSegment
if greatestPoint == None :
return loopConvex
lastPoint = greatestPoint
loopConvex . append ( greatestPoint )
pointSet . remove ( greatestPoint )
lastSegment = greatestSegment
return loopConvex
def getLoopConvexCentroid ( polygonComplex ) :
' Get centroid of the convex hull of a complex polygon. '
return getLoopCentroid ( getLoopConvex ( polygonComplex ) )
def getLoopInsideContainingLoop ( containingLoop , loops ) :
' Get a loop that is inside the containing loop. '
for loop in loops :
if loop != containingLoop :
if isPathInsideLoop ( containingLoop , loop ) :
return loop
return None
def getLoopLength ( polygon ) :
' Get the length of a polygon perimeter. '
polygonLength = 0.0
for pointIndex in xrange ( len ( polygon ) ) :
point = polygon [ pointIndex ]
secondPoint = polygon [ ( pointIndex + 1 ) % len ( polygon ) ]
polygonLength + = abs ( point - secondPoint )
return polygonLength
def getLoopStartingClosest ( extrusionHalfWidth , location , loop ) :
' Add to threads from the last location from loop. '
closestIndex = getClosestDistanceIndexToLine ( location , loop ) . index
loop = getAroundLoop ( closestIndex , closestIndex , loop )
closestPoint = getClosestPointOnSegment ( loop [ 0 ] , loop [ 1 ] , location )
if abs ( closestPoint - loop [ 0 ] ) > extrusionHalfWidth and abs ( closestPoint - loop [ 1 ] ) > extrusionHalfWidth :
loop = [ closestPoint ] + loop [ 1 : ] + [ loop [ 0 ] ]
elif abs ( closestPoint - loop [ 0 ] ) > abs ( closestPoint - loop [ 1 ] ) :
loop = loop [ 1 : ] + [ loop [ 0 ] ]
return loop
def getLoopWithoutCloseEnds ( close , loop ) :
' Get loop without close ends. '
if len ( loop ) < 2 :
return loop
if abs ( loop [ 0 ] - loop [ - 1 ] ) > close :
return loop
return loop [ : - 1 ]
def getLoopWithoutCloseSequentialPoints ( close , loop ) :
' Get loop without close sequential points. '
if len ( loop ) < 2 :
return loop
lastPoint = loop [ - 1 ]
loopWithoutCloseSequentialPoints = [ ]
for point in loop :
if abs ( point - lastPoint ) > close :
loopWithoutCloseSequentialPoints . append ( point )
lastPoint = point
return loopWithoutCloseSequentialPoints
def getMaximum ( firstComplex , secondComplex ) :
' Get a complex with each component the maximum of the respective components of a pair of complexes. '
return complex ( max ( firstComplex . real , secondComplex . real ) , max ( firstComplex . imag , secondComplex . imag ) )
def getMaximumByComplexPath ( path ) :
' Get a complex with each component the maximum of the respective components of a complex path. '
maximum = complex ( - 987654321987654321.0 , - 987654321987654321.0 )
for point in path :
maximum = getMaximum ( maximum , point )
return maximum
def getMaximumByComplexPaths ( paths ) :
' Get a complex with each component the maximum of the respective components of complex paths. '
maximum = complex ( - 987654321987654321.0 , - 987654321987654321.0 )
for path in paths :
for point in path :
maximum = getMaximum ( maximum , point )
return maximum
def getMaximumByVector3Path ( path ) :
' Get a vector3 with each component the maximum of the respective components of a vector3 path. '
maximum = Vector3 ( - 987654321987654321.0 , - 987654321987654321.0 , - 987654321987654321.0 )
for point in path :
maximum . maximize ( point )
return maximum
def getMaximumByVector3Paths ( paths ) :
' Get a complex with each component the maximum of the respective components of a complex path. '
maximum = Vector3 ( - 987654321987654321.0 , - 987654231987654321.0 , - 987654321987654321.0 )
for path in paths :
for point in path :
maximum . maximize ( point )
return maximum
def getMaximumSpan ( loop ) :
' Get the maximum span of the loop. '
extent = getMaximumByComplexPath ( loop ) - getMinimumByComplexPath ( loop )
return max ( extent . real , extent . imag )
def getMinimum ( firstComplex , secondComplex ) :
' Get a complex with each component the minimum of the respective components of a pair of complexes. '
return complex ( min ( firstComplex . real , secondComplex . real ) , min ( firstComplex . imag , secondComplex . imag ) )
def getMinimumByComplexPath ( path ) :
' Get a complex with each component the minimum of the respective components of a complex path. '
minimum = complex ( 987654321987654321.0 , 987654321987654321.0 )
for point in path :
minimum = getMinimum ( minimum , point )
return minimum
def getMinimumByComplexPaths ( paths ) :
' Get a complex with each component the minimum of the respective components of complex paths. '
minimum = complex ( 987654321987654321.0 , 987654321987654321.0 )
for path in paths :
for point in path :
minimum = getMinimum ( minimum , point )
return minimum
def getMinimumByVector3Path ( path ) :
' Get a vector3 with each component the minimum of the respective components of a vector3 path. '
minimum = Vector3 ( 987654321987654321.0 , 987654321987654321.0 , 987654321987654321.0 )
for point in path :
minimum . minimize ( point )
return minimum
def getMinimumByVector3Paths ( paths ) :
' Get a complex with each component the minimum of the respective components of a complex path. '
minimum = Vector3 ( 987654321987654321.0 , 987654321987654321.0 , 987654321987654321.0 )
for path in paths :
for point in path :
minimum . minimize ( point )
return minimum
def getMirrorPath ( path ) :
" Get mirror path. "
close = 0.001 * getPathLength ( path )
for pointIndex in xrange ( len ( path ) - 1 , - 1 , - 1 ) :
point = path [ pointIndex ]
flipPoint = complex ( - point . real , point . imag )
if abs ( flipPoint - path [ - 1 ] ) > close :
path . append ( flipPoint )
return path
def getNormal ( begin , center , end ) :
' Get normal. '
centerMinusBegin = ( center - begin ) . getNormalized ( )
endMinusCenter = ( end - center ) . getNormalized ( )
return centerMinusBegin . cross ( endMinusCenter )
def getNormalByPath ( path ) :
' Get normal by path. '
totalNormal = Vector3 ( )
for pointIndex , point in enumerate ( path ) :
center = path [ ( pointIndex + 1 ) % len ( path ) ]
end = path [ ( pointIndex + 2 ) % len ( path ) ]
totalNormal + = getNormalWeighted ( point , center , end )
return totalNormal . getNormalized ( )
def getNormalized ( complexNumber ) :
' Get the normalized complex. '
complexNumberLength = abs ( complexNumber )
if complexNumberLength > 0.0 :
return complexNumber / complexNumberLength
return complexNumber
def getNormalWeighted ( begin , center , end ) :
' Get weighted normal. '
return ( center - begin ) . cross ( end - center )
def getNumberOfIntersectionsToLeft ( loop , point ) :
' Get the number of intersections through the loop for the line going left. '
numberOfIntersectionsToLeft = 0
for pointIndex in xrange ( len ( loop ) ) :
firstPointComplex = loop [ pointIndex ]
secondPointComplex = loop [ ( pointIndex + 1 ) % len ( loop ) ]
xIntersection = getXIntersectionIfExists ( firstPointComplex , secondPointComplex , point . imag )
if xIntersection != None :
if xIntersection < point . real :
numberOfIntersectionsToLeft + = 1
return numberOfIntersectionsToLeft
def getNumberOfIntersectionsToLeftOfLoops ( loops , point ) :
' Get the number of intersections through the loop for the line starting from the left point and going left. '
totalNumberOfIntersectionsToLeft = 0
for loop in loops :
totalNumberOfIntersectionsToLeft + = getNumberOfIntersectionsToLeft ( loop , point )
return totalNumberOfIntersectionsToLeft
def getOrderedNestedRings ( nestedRings ) :
' Get ordered nestedRings from nestedRings. '
insides = [ ]
orderedNestedRings = [ ]
for loopIndex in xrange ( len ( nestedRings ) ) :
nestedRing = nestedRings [ loopIndex ]
otherLoops = [ ]
for beforeIndex in xrange ( loopIndex ) :
otherLoops . append ( nestedRings [ beforeIndex ] . boundary )
for afterIndex in xrange ( loopIndex + 1 , len ( nestedRings ) ) :
otherLoops . append ( nestedRings [ afterIndex ] . boundary )
if isPathEntirelyInsideLoops ( otherLoops , nestedRing . boundary ) :
insides . append ( nestedRing )
else :
orderedNestedRings . append ( nestedRing )
for outside in orderedNestedRings :
outside . getFromInsideSurroundings ( insides )
return orderedNestedRings
def getPathCopy ( path ) :
' Get path copy. '
pathCopy = [ ]
for point in path :
pathCopy . append ( point . copy ( ) )
return pathCopy
def getPathLength ( path ) :
' Get the length of a path ( an open polyline ). '
pathLength = 0.0
for pointIndex in xrange ( len ( path ) - 1 ) :
firstPoint = path [ pointIndex ]
secondPoint = path [ pointIndex + 1 ]
pathLength + = abs ( firstPoint - secondPoint )
return pathLength
2012-03-22 12:43:07 +00:00
def getPathsFromEndpoints ( endpoints , maximumConnectionLength , pixelDictionary , sharpestProduct , width ) :
2012-02-19 23:30:49 +00:00
' Get paths from endpoints. '
if len ( endpoints ) < 2 :
return [ ]
endpoints = endpoints [ : ] # so that the first two endpoints aren't removed when used again
for beginningEndpoint in endpoints [ : : 2 ] :
beginningPoint = beginningEndpoint . point
addSegmentToPixelTable ( beginningPoint , beginningEndpoint . otherEndpoint . point , pixelDictionary , 0 , 0 , width )
endpointFirst = endpoints [ 0 ]
endpoints . remove ( endpointFirst )
otherEndpoint = endpointFirst . otherEndpoint
endpoints . remove ( otherEndpoint )
nextEndpoint = None
path = [ ]
paths = [ path ]
if len ( endpoints ) > 1 :
2012-03-22 12:43:07 +00:00
nextEndpoint = otherEndpoint . getClosestMiss ( endpoints , path , pixelDictionary , sharpestProduct , width )
2012-02-19 23:30:49 +00:00
if nextEndpoint != None :
if abs ( nextEndpoint . point - endpointFirst . point ) < abs ( nextEndpoint . point - otherEndpoint . point ) :
endpointFirst = endpointFirst . otherEndpoint
otherEndpoint = endpointFirst . otherEndpoint
addPointToPath ( path , pixelDictionary , endpointFirst . point , None , width )
addPointToPath ( path , pixelDictionary , otherEndpoint . point , len ( paths ) - 1 , width )
oneOverEndpointWidth = 1.0 / maximumConnectionLength
endpointTable = { }
for endpoint in endpoints :
addElementToPixelListFromPoint ( endpoint , endpointTable , endpoint . point * oneOverEndpointWidth )
while len ( endpointTable ) > 0 :
if len ( endpointTable ) == 1 :
if len ( endpointTable . values ( ) [ 0 ] ) < 2 :
return [ ]
endpoints = getSquareValuesFromPoint ( endpointTable , otherEndpoint . point * oneOverEndpointWidth )
2012-03-22 12:43:07 +00:00
nextEndpoint = otherEndpoint . getClosestMiss ( endpoints , path , pixelDictionary , sharpestProduct , width )
2012-02-19 23:30:49 +00:00
if nextEndpoint == None :
path = [ ]
paths . append ( path )
endpoints = getListTableElements ( endpointTable )
nextEndpoint = otherEndpoint . getClosestEndpoint ( endpoints )
# this commented code should be faster than the getListTableElements code, but it isn't, someday a spiral algorithim could be tried
# endpoints = getSquareValuesFromPoint( endpointTable, otherEndpoint.point * oneOverEndpointWidth )
# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints)
# if nextEndpoint == None:
# endpoints = []
# for endpointTableValue in endpointTable.values():
# endpoints.append( endpointTableValue[0] )
# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints)
# endpoints = getSquareValuesFromPoint( endpointTable, nextEndpoint.point * oneOverEndpointWidth )
# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints)
addPointToPath ( path , pixelDictionary , nextEndpoint . point , len ( paths ) - 1 , width )
removeElementFromPixelListFromPoint ( nextEndpoint , endpointTable , nextEndpoint . point * oneOverEndpointWidth )
otherEndpoint = nextEndpoint . otherEndpoint
addPointToPath ( path , pixelDictionary , otherEndpoint . point , len ( paths ) - 1 , width )
removeElementFromPixelListFromPoint ( otherEndpoint , endpointTable , otherEndpoint . point * oneOverEndpointWidth )
return paths
def getPlaneDot ( vec3First , vec3Second ) :
' Get the dot product of the x and y components of a pair of Vector3s. '
return vec3First . x * vec3Second . x + vec3First . y * vec3Second . y
def getPluralString ( number , suffix ) :
' Get the plural string. '
if number == 1 :
return ' 1 %s ' % suffix
return ' %s %s s ' % ( number , suffix )
def getPointPlusSegmentWithLength ( length , point , segment ) :
' Get point plus a segment scaled to a given length. '
return segment * length / abs ( segment ) + point
def getPointsByHorizontalDictionary ( width , xIntersectionsDictionary ) :
' Get points from the horizontalXIntersectionsDictionary. '
points = [ ]
xIntersectionsDictionaryKeys = xIntersectionsDictionary . keys ( )
xIntersectionsDictionaryKeys . sort ( )
for xIntersectionsDictionaryKey in xIntersectionsDictionaryKeys :
for xIntersection in xIntersectionsDictionary [ xIntersectionsDictionaryKey ] :
points . append ( complex ( xIntersection , xIntersectionsDictionaryKey * width ) )
return points
def getPointsByVerticalDictionary ( width , xIntersectionsDictionary ) :
' Get points from the verticalXIntersectionsDictionary. '
points = [ ]
xIntersectionsDictionaryKeys = xIntersectionsDictionary . keys ( )
xIntersectionsDictionaryKeys . sort ( )
for xIntersectionsDictionaryKey in xIntersectionsDictionaryKeys :
for xIntersection in xIntersectionsDictionary [ xIntersectionsDictionaryKey ] :
points . append ( complex ( xIntersectionsDictionaryKey * width , xIntersection ) )
return points
def getRadiusArealizedMultiplier ( sides ) :
' Get the radius multiplier for a polygon of equal area. '
return math . sqrt ( globalTau / sides / math . sin ( globalTau / sides ) )
def getRandomComplex ( begin , end ) :
' Get random complex. '
endMinusBegin = end - begin
return begin + complex ( random . random ( ) * endMinusBegin . real , random . random ( ) * endMinusBegin . imag )
def getRank ( width ) :
' Get the rank which is 0 at 1 and increases by three every power of ten. '
return int ( math . floor ( 3.0 * math . log10 ( width ) ) )
def getRotatedComplexes ( planeAngle , points ) :
' Get points rotated by the plane angle '
rotatedComplexes = [ ]
for point in points :
rotatedComplexes . append ( planeAngle * point )
return rotatedComplexes
def getRotatedComplexLists ( planeAngle , pointLists ) :
' Get point lists rotated by the plane angle '
rotatedComplexLists = [ ]
for pointList in pointLists :
rotatedComplexLists . append ( getRotatedComplexes ( planeAngle , pointList ) )
return rotatedComplexLists
def getRotatedWiddershinsQuarterAroundZAxis ( vector3 ) :
' Get Vector3 rotated a quarter widdershins turn around Z axis. '
return Vector3 ( - vector3 . y , vector3 . x , vector3 . z )
def getRoundedPoint ( point ) :
' Get point with each component rounded. '
return Vector3 ( round ( point . x ) , round ( point . y ) , round ( point . z ) )
def getRoundedToPlaces ( decimalPlaces , number ) :
' Get number rounded to a number of decimal places. '
decimalPlacesRounded = max ( 1 , int ( round ( decimalPlaces ) ) )
return round ( number , decimalPlacesRounded )
def getRoundedToPlacesString ( decimalPlaces , number ) :
' Get number rounded to a number of decimal places as a string, without exponential formatting. '
roundedToPlaces = getRoundedToPlaces ( decimalPlaces , number )
roundedToPlacesString = str ( roundedToPlaces )
if ' e ' in roundedToPlacesString :
return ( ' %.15f ' % roundedToPlaces ) . rstrip ( ' 0 ' )
return roundedToPlacesString
def getRoundedToThreePlaces ( number ) :
' Get number rounded to three places as a string. '
return str ( round ( number , 3 ) )
def getRoundZAxisByPlaneAngle ( planeAngle , vector3 ) :
' Get Vector3 rotated by a plane angle. '
return Vector3 ( vector3 . x * planeAngle . real - vector3 . y * planeAngle . imag , vector3 . x * planeAngle . imag + vector3 . y * planeAngle . real , vector3 . z )
def getSegmentFromPath ( path , pathIndex ) :
' Get endpoint segment from a path. '
if len ( path ) < 2 :
return None
begin = path [ - 1 ]
end = path [ - 2 ]
forwardEndpoint = getEndpointFromPath ( path , pathIndex )
reversePath = path [ : ]
reversePath . reverse ( )
reverseEndpoint = getEndpointFromPath ( reversePath , pathIndex )
return ( forwardEndpoint , reverseEndpoint )
def getSegmentFromPoints ( begin , end ) :
' Get endpoint segment from a pair of points. '
endpointFirst = Endpoint ( )
endpointSecond = Endpoint ( ) . getFromOtherPoint ( endpointFirst , end )
endpointFirst . getFromOtherPoint ( endpointSecond , begin )
return ( endpointFirst , endpointSecond )
def getSegmentsFromXIntersectionIndexes ( xIntersectionIndexList , y ) :
' Get endpoint segments from the x intersection indexes. '
xIntersections = getXIntersectionsFromIntersections ( xIntersectionIndexList )
return getSegmentsFromXIntersections ( xIntersections , y )
def getSegmentsFromXIntersections ( xIntersections , y ) :
' Get endpoint segments from the x intersections. '
segments = [ ]
end = len ( xIntersections )
if len ( xIntersections ) % 2 == 1 :
end - = 1
for xIntersectionIndex in xrange ( 0 , end , 2 ) :
firstX = xIntersections [ xIntersectionIndex ]
secondX = xIntersections [ xIntersectionIndex + 1 ]
if firstX != secondX :
segments . append ( getSegmentFromPoints ( complex ( firstX , y ) , complex ( secondX , y ) ) )
return segments
def getSimplifiedLoop ( loop , radius ) :
' Get loop with points inside the channel removed. '
if len ( loop ) < 2 :
return loop
simplificationMultiplication = 256
simplificationRadius = radius / float ( simplificationMultiplication )
maximumIndex = len ( loop ) * simplificationMultiplication
pointIndex = 1
while pointIndex < maximumIndex :
oldLoopLength = len ( loop )
loop = getHalfSimplifiedLoop ( loop , simplificationRadius , 0 )
loop = getHalfSimplifiedLoop ( loop , simplificationRadius , 1 )
simplificationRadius + = simplificationRadius
if oldLoopLength == len ( loop ) :
if simplificationRadius > radius :
return getAwayPoints ( loop , radius )
else :
simplificationRadius * = 1.5
simplificationRadius = min ( simplificationRadius , radius )
pointIndex + = pointIndex
return getAwayPoints ( loop , radius )
def getSimplifiedLoops ( loops , radius ) :
' Get the simplified loops. '
simplifiedLoops = [ ]
for loop in loops :
simplifiedLoops . append ( getSimplifiedLoop ( loop , radius ) )
return simplifiedLoops
def getSimplifiedPath ( path , radius ) :
' Get path with points inside the channel removed. '
if len ( path ) < 2 :
return path
simplificationMultiplication = 256
simplificationRadius = radius / float ( simplificationMultiplication )
maximumIndex = len ( path ) * simplificationMultiplication
pointIndex = 1
while pointIndex < maximumIndex :
oldPathLength = len ( path )
path = getHalfSimplifiedPath ( path , simplificationRadius , 0 )
path = getHalfSimplifiedPath ( path , simplificationRadius , 1 )
simplificationRadius + = simplificationRadius
if oldPathLength == len ( path ) :
if simplificationRadius > radius :
return getAwayPath ( path , radius )
else :
simplificationRadius * = 1.5
simplificationRadius = min ( simplificationRadius , radius )
pointIndex + = pointIndex
return getAwayPath ( path , radius )
def getSquareIsOccupied ( pixelDictionary , x , y ) :
' Determine if a square around the x and y pixel coordinates is occupied. '
squareValues = [ ]
for xStep in xrange ( x - 1 , x + 2 ) :
for yStep in xrange ( y - 1 , y + 2 ) :
if ( xStep , yStep ) in pixelDictionary :
return True
return False
def getSquareLoopWiddershins ( beginComplex , endComplex ) :
' Get a square loop from the beginning to the end and back. '
loop = [ beginComplex , complex ( endComplex . real , beginComplex . imag ) , endComplex ]
loop . append ( complex ( beginComplex . real , endComplex . imag ) )
return loop
def getSquareValues ( pixelDictionary , x , y ) :
' Get a list of the values in a square around the x and y pixel coordinates. '
squareValues = [ ]
for xStep in xrange ( x - 1 , x + 2 ) :
for yStep in xrange ( y - 1 , y + 2 ) :
stepKey = ( xStep , yStep )
if stepKey in pixelDictionary :
squareValues + = pixelDictionary [ stepKey ]
return squareValues
def getSquareValuesFromPoint ( pixelDictionary , point ) :
' Get a list of the values in a square around the point. '
return getSquareValues ( pixelDictionary , int ( round ( point . real ) ) , int ( round ( point . imag ) ) )
def getStepKeyFromPoint ( point ) :
' Get step key for the point. '
return ( int ( round ( point . real ) ) , int ( round ( point . imag ) ) )
def getThreeSignificantFigures ( number ) :
' Get number rounded to three significant figures as a string. '
absoluteNumber = abs ( number )
if absoluteNumber > = 10.0 :
return getRoundedToPlacesString ( 1 , number )
if absoluteNumber < 0.000000001 :
return getRoundedToPlacesString ( 12 , number )
return getRoundedToPlacesString ( 1 - math . floor ( math . log10 ( absoluteNumber ) ) , number )
def getTopPath ( path ) :
' Get the top of the path. '
top = - 987654321987654321.0
for point in path :
top = max ( top , point . z )
return top
def getTopPaths ( paths ) :
' Get the top of the paths. '
top = - 987654321987654321.0
for path in paths :
for point in path :
top = max ( top , point . z )
return top
def getTransferClosestNestedRing ( extrusionHalfWidth , nestedRings , oldOrderedLocation , skein , threadSequence ) :
' Get and transfer the closest remaining nested ring. '
if len ( nestedRings ) > 0 :
oldOrderedLocation . z = nestedRings [ 0 ] . z
closestDistance = 987654321987654321.0
closestNestedRing = None
for remainingNestedRing in nestedRings :
distance = getClosestDistanceIndexToLine ( oldOrderedLocation . dropAxis ( ) , remainingNestedRing . boundary ) . distance
if distance < closestDistance :
closestDistance = distance
closestNestedRing = remainingNestedRing
nestedRings . remove ( closestNestedRing )
closestNestedRing . addToThreads ( extrusionHalfWidth , oldOrderedLocation , skein , threadSequence )
return closestNestedRing
def getTransferredNestedRings ( insides , loop ) :
' Get transferred paths from inside nested rings. '
transferredSurroundings = [ ]
for insideIndex in xrange ( len ( insides ) - 1 , - 1 , - 1 ) :
insideSurrounding = insides [ insideIndex ]
if isPathInsideLoop ( loop , insideSurrounding . boundary ) :
transferredSurroundings . append ( insideSurrounding )
del insides [ insideIndex ]
return transferredSurroundings
def getTransferredPaths ( insides , loop ) :
' Get transferred paths from inside paths. '
transferredPaths = [ ]
for insideIndex in xrange ( len ( insides ) - 1 , - 1 , - 1 ) :
inside = insides [ insideIndex ]
if isPathInsideLoop ( loop , inside ) :
transferredPaths . append ( inside )
del insides [ insideIndex ]
return transferredPaths
def getTranslatedComplexPath ( path , translateComplex ) :
' Get the translated complex path. '
translatedComplexPath = [ ]
for point in path :
translatedComplexPath . append ( point + translateComplex )
return translatedComplexPath
def getVector3Path ( complexPath , z = 0.0 ) :
' Get the vector3 path from the complex path. '
vector3Path = [ ]
for complexPoint in complexPath :
vector3Path . append ( Vector3 ( complexPoint . real , complexPoint . imag , z ) )
return vector3Path
def getVector3Paths ( complexPaths , z = 0.0 ) :
' Get the vector3 paths from the complex paths. '
vector3Paths = [ ]
for complexPath in complexPaths :
vector3Paths . append ( getVector3Path ( complexPath , z ) )
return vector3Paths
def getWiddershinsUnitPolar ( angle ) :
' Get polar complex from counterclockwise angle from 1, 0. '
return complex ( math . cos ( angle ) , math . sin ( angle ) )
def getXIntersectionIfExists ( beginComplex , endComplex , y ) :
' Get the x intersection if it exists. '
if ( y > beginComplex . imag ) == ( y > endComplex . imag ) :
return None
endMinusBeginComplex = endComplex - beginComplex
return ( y - beginComplex . imag ) / endMinusBeginComplex . imag * endMinusBeginComplex . real + beginComplex . real
def getXIntersectionsFromIntersections ( xIntersectionIndexList ) :
' Get x intersections from the x intersection index list, in other words subtract non negative intersections from negatives. '
xIntersections = [ ]
fill = False
solid = False
solidTable = { }
xIntersectionIndexList . sort ( )
for solidX in xIntersectionIndexList :
if solidX . index > = 0 :
toggleHashtable ( solidTable , solidX . index , ' ' )
else :
fill = not fill
oldSolid = solid
solid = ( len ( solidTable ) == 0 and fill )
if oldSolid != solid :
xIntersections . append ( solidX . x )
return xIntersections
def getXYComplexFromVector3 ( vector3 ) :
' Get an xy complex from a vector3 if it exists, otherwise return None. '
if vector3 == None :
return None
return vector3 . dropAxis ( )
def getYIntersectionIfExists ( beginComplex , endComplex , x ) :
' Get the y intersection if it exists. '
if ( x > beginComplex . real ) == ( x > endComplex . real ) :
return None
endMinusBeginComplex = endComplex - beginComplex
return ( x - beginComplex . real ) / endMinusBeginComplex . real * endMinusBeginComplex . imag + beginComplex . imag
def getZComponentCrossProduct ( vec3First , vec3Second ) :
' Get z component cross product of a pair of Vector3s. '
return vec3First . x * vec3Second . y - vec3First . y * vec3Second . x
def isInsideOtherLoops ( loopIndex , loops ) :
' Determine if a loop in a list is inside another loop in that list. '
return isPathInsideLoops ( loops [ : loopIndex ] + loops [ loopIndex + 1 : ] , loops [ loopIndex ] )
def isLineIntersectingInsideXSegment ( beginComplex , endComplex , segmentFirstX , segmentSecondX , y ) :
' Determine if the line is crossing inside the x segment. '
xIntersection = getXIntersectionIfExists ( beginComplex , endComplex , y )
if xIntersection == None :
return False
if xIntersection < min ( segmentFirstX , segmentSecondX ) :
return False
return xIntersection < = max ( segmentFirstX , segmentSecondX )
def isLineIntersectingLoop ( loop , pointBegin , pointEnd ) :
' Determine if the line is intersecting loops. '
normalizedSegment = pointEnd - pointBegin
normalizedSegmentLength = abs ( normalizedSegment )
if normalizedSegmentLength > 0.0 :
normalizedSegment / = normalizedSegmentLength
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
pointBeginRotated = segmentYMirror * pointBegin
pointEndRotated = segmentYMirror * pointEnd
if isLoopIntersectingInsideXSegment ( loop , pointBeginRotated . real , pointEndRotated . real , segmentYMirror , pointBeginRotated . imag ) :
return True
return False
def isLineIntersectingLoops ( loops , pointBegin , pointEnd ) :
' Determine if the line is intersecting loops. '
normalizedSegment = pointEnd - pointBegin
normalizedSegmentLength = abs ( normalizedSegment )
if normalizedSegmentLength > 0.0 :
normalizedSegment / = normalizedSegmentLength
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
pointBeginRotated = segmentYMirror * pointBegin
pointEndRotated = segmentYMirror * pointEnd
if isLoopListIntersectingInsideXSegment ( loops , pointBeginRotated . real , pointEndRotated . real , segmentYMirror , pointBeginRotated . imag ) :
return True
return False
def isLoopIntersectingInsideXSegment ( loop , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
' Determine if the loop is intersecting inside the x segment. '
rotatedLoop = getRotatedComplexes ( segmentYMirror , loop )
for pointIndex in xrange ( len ( rotatedLoop ) ) :
pointFirst = rotatedLoop [ pointIndex ]
pointSecond = rotatedLoop [ ( pointIndex + 1 ) % len ( rotatedLoop ) ]
if isLineIntersectingInsideXSegment ( pointFirst , pointSecond , segmentFirstX , segmentSecondX , y ) :
return True
return False
def isLoopIntersectingLoop ( loop , otherLoop ) :
' Determine if the loop is intersecting the other loop. '
for pointIndex in xrange ( len ( loop ) ) :
pointBegin = loop [ pointIndex ]
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
if isLineIntersectingLoop ( otherLoop , pointBegin , pointEnd ) :
return True
return False
def isLoopIntersectingLoops ( loop , otherLoops ) :
' Determine if the loop is intersecting other loops. '
for pointIndex in xrange ( len ( loop ) ) :
pointBegin = loop [ pointIndex ]
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
if isLineIntersectingLoops ( otherLoops , pointBegin , pointEnd ) :
return True
return False
def isLoopListIntersecting ( loops ) :
' Determine if a loop in the list is intersecting the other loops. '
for loopIndex in xrange ( len ( loops ) - 1 ) :
loop = loops [ loopIndex ]
if isLoopIntersectingLoops ( loop , loops [ loopIndex + 1 : ] ) :
return True
return False
def isLoopListIntersectingInsideXSegment ( loopList , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
' Determine if the loop list is crossing inside the x segment. '
for alreadyFilledLoop in loopList :
if isLoopIntersectingInsideXSegment ( alreadyFilledLoop , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
return True
return False
def isPathEntirelyInsideLoop ( loop , path ) :
' Determine if a path is entirely inside another loop. '
for point in path :
if not isPointInsideLoop ( loop , point ) :
return False
return True
def isPathEntirelyInsideLoops ( loops , path ) :
' Determine if a path is entirely inside another loop in a list. '
for loop in loops :
if isPathEntirelyInsideLoop ( loop , path ) :
return True
return False
def isPathInsideLoop ( loop , path ) :
' Determine if a path is inside another loop. '
return isPointInsideLoop ( loop , getLeftPoint ( path ) )
def isPathInsideLoops ( loops , path ) :
' Determine if a path is inside another loop in a list. '
for loop in loops :
if isPathInsideLoop ( loop , path ) :
return True
return False
def isPixelTableIntersecting ( bigTable , littleTable , maskTable = { } ) :
' Add path to the pixel table. '
littleTableKeys = littleTable . keys ( )
for littleTableKey in littleTableKeys :
if littleTableKey not in maskTable :
if littleTableKey in bigTable :
return True
return False
def isPointInsideLoop ( loop , point ) :
' Determine if a point is inside another loop. '
return getNumberOfIntersectionsToLeft ( loop , point ) % 2 == 1
def isSegmentCompletelyInX ( segment , xFirst , xSecond ) :
' Determine if the segment overlaps within x. '
segmentFirstX = segment [ 0 ] . point . real
segmentSecondX = segment [ 1 ] . point . real
if max ( segmentFirstX , segmentSecondX ) > max ( xFirst , xSecond ) :
return False
return min ( segmentFirstX , segmentSecondX ) > = min ( xFirst , xSecond )
def isWiddershins ( polygonComplex ) :
' Determine if the complex polygon goes round in the widdershins direction. '
return getAreaLoop ( polygonComplex ) > 0.0
def isWithinChannel ( channelRadius , pointIndex , loop ) :
' Determine if the the point is within the channel between two adjacent points. '
point = loop [ pointIndex ]
behindSegmentComplex = loop [ ( pointIndex + len ( loop ) - 1 ) % len ( loop ) ] - point
behindSegmentComplexLength = abs ( behindSegmentComplex )
if behindSegmentComplexLength < channelRadius :
return True
aheadSegmentComplex = loop [ ( pointIndex + 1 ) % len ( loop ) ] - point
aheadSegmentComplexLength = abs ( aheadSegmentComplex )
if aheadSegmentComplexLength < channelRadius :
return True
behindSegmentComplex / = behindSegmentComplexLength
aheadSegmentComplex / = aheadSegmentComplexLength
absoluteZ = getDotProductPlusOne ( aheadSegmentComplex , behindSegmentComplex )
if behindSegmentComplexLength * absoluteZ < channelRadius :
return True
return aheadSegmentComplexLength * absoluteZ < channelRadius
def isXSegmentIntersectingPath ( path , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
' Determine if a path is crossing inside the x segment. '
rotatedPath = getRotatedComplexes ( segmentYMirror , path )
for pointIndex in xrange ( len ( rotatedPath ) - 1 ) :
pointFirst = rotatedPath [ pointIndex ]
pointSecond = rotatedPath [ pointIndex + 1 ]
if isLineIntersectingInsideXSegment ( pointFirst , pointSecond , segmentFirstX , segmentSecondX , y ) :
return True
return False
def isXSegmentIntersectingPaths ( paths , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
' Determine if a path list is crossing inside the x segment. '
for path in paths :
if isXSegmentIntersectingPath ( path , segmentFirstX , segmentSecondX , segmentYMirror , y ) :
return True
return False
def joinSegmentTables ( fromTable , intoTable ) :
' Join both segment tables and put the join into the intoTable. '
intoTableKeys = intoTable . keys ( )
fromTableKeys = fromTable . keys ( )
joinedKeyTable = { }
concatenatedTableKeys = intoTableKeys + fromTableKeys
for concatenatedTableKey in concatenatedTableKeys :
joinedKeyTable [ concatenatedTableKey ] = None
joinedKeys = joinedKeyTable . keys ( )
joinedKeys . sort ( )
for joinedKey in joinedKeys :
xIntersectionIndexList = [ ]
if joinedKey in intoTable :
addXIntersectionIndexesFromSegments ( 0 , intoTable [ joinedKey ] , xIntersectionIndexList )
if joinedKey in fromTable :
addXIntersectionIndexesFromSegments ( 1 , fromTable [ joinedKey ] , xIntersectionIndexList )
xIntersections = getJoinOfXIntersectionIndexes ( xIntersectionIndexList )
lineSegments = getSegmentsFromXIntersections ( xIntersections , joinedKey )
if len ( lineSegments ) > 0 :
intoTable [ joinedKey ] = lineSegments
else :
print ( ' This should never happen, there are no line segments in joinSegments in euclidean ' )
def joinXIntersectionsTables ( fromTable , intoTable ) :
' Join both XIntersections tables and put the join into the intoTable. '
joinedKeyTable = { }
concatenatedTableKeys = fromTable . keys ( ) + intoTable . keys ( )
for concatenatedTableKey in concatenatedTableKeys :
joinedKeyTable [ concatenatedTableKey ] = None
for joinedKey in joinedKeyTable . keys ( ) :
xIntersectionIndexList = [ ]
if joinedKey in intoTable :
addXIntersectionIndexesFromXIntersections ( 0 , xIntersectionIndexList , intoTable [ joinedKey ] )
if joinedKey in fromTable :
addXIntersectionIndexesFromXIntersections ( 1 , xIntersectionIndexList , fromTable [ joinedKey ] )
xIntersections = getJoinOfXIntersectionIndexes ( xIntersectionIndexList )
if len ( xIntersections ) > 0 :
intoTable [ joinedKey ] = xIntersections
else :
print ( ' This should never happen, there are no line segments in joinSegments in euclidean ' )
def overwriteDictionary ( fromDictionary , keys , toDictionary ) :
' Overwrite the dictionary. '
for key in keys :
if key in fromDictionary :
toDictionary [ key ] = fromDictionary [ key ]
def removeElementFromDictionary ( dictionary , key ) :
' Remove element from the dictionary. '
if key in dictionary :
del dictionary [ key ]
def removeElementFromListTable ( element , key , listDictionary ) :
' Remove an element from the list table. '
if key not in listDictionary :
return
elementList = listDictionary [ key ]
if len ( elementList ) < 2 :
del listDictionary [ key ]
return
if element in elementList :
elementList . remove ( element )
def removeElementFromPixelListFromPoint ( element , pixelDictionary , point ) :
' Remove an element from the pixel list. '
stepKey = getStepKeyFromPoint ( point )
removeElementFromListTable ( element , stepKey , pixelDictionary )
def removeElementsFromDictionary ( dictionary , keys ) :
' Remove list from the dictionary. '
for key in keys :
removeElementFromDictionary ( dictionary , key )
def removePixelTableFromPixelTable ( pixelDictionaryToBeRemoved , pixelDictionaryToBeRemovedFrom ) :
' Remove pixel from the pixel table. '
removeElementsFromDictionary ( pixelDictionaryToBeRemovedFrom , pixelDictionaryToBeRemoved . keys ( ) )
def removePrefixFromDictionary ( dictionary , prefix ) :
' Remove the attributes starting with the prefix from the dictionary. '
for key in dictionary . keys ( ) :
if key . startswith ( prefix ) :
del dictionary [ key ]
def removeTrueFromDictionary ( dictionary , key ) :
' Remove key from the dictionary in the value is true. '
if key in dictionary :
if getBooleanFromValue ( dictionary [ key ] ) :
del dictionary [ key ]
def removeTrueListFromDictionary ( dictionary , keys ) :
' Remove list from the dictionary in the value is true. '
for key in keys :
removeTrueFromDictionary ( dictionary , key )
def subtractXIntersectionsTable ( subtractFromTable , subtractTable ) :
' Subtract the subtractTable from the subtractFromTable. '
subtractFromTableKeys = subtractFromTable . keys ( )
subtractFromTableKeys . sort ( )
for subtractFromTableKey in subtractFromTableKeys :
xIntersectionIndexList = [ ]
addXIntersectionIndexesFromXIntersections ( - 1 , xIntersectionIndexList , subtractFromTable [ subtractFromTableKey ] )
if subtractFromTableKey in subtractTable :
addXIntersectionIndexesFromXIntersections ( 0 , xIntersectionIndexList , subtractTable [ subtractFromTableKey ] )
xIntersections = getXIntersectionsFromIntersections ( xIntersectionIndexList )
if len ( xIntersections ) > 0 :
subtractFromTable [ subtractFromTableKey ] = xIntersections
else :
del subtractFromTable [ subtractFromTableKey ]
def swapList ( elements , indexBegin , indexEnd ) :
' Swap the list elements. '
elements [ indexBegin ] , elements [ indexEnd ] = elements [ indexEnd ] , elements [ indexBegin ]
def toggleHashtable ( hashtable , key , value ) :
' Toggle a hashtable between having and not having a key. '
if key in hashtable :
del hashtable [ key ]
else :
hashtable [ key ] = value
def transferClosestFillLoop ( extrusionHalfWidth , oldOrderedLocation , remainingFillLoops , skein ) :
' Transfer the closest remaining fill loop. '
closestDistance = 987654321987654321.0
closestFillLoop = None
for remainingFillLoop in remainingFillLoops :
distance = getClosestDistanceIndexToLine ( oldOrderedLocation . dropAxis ( ) , remainingFillLoop ) . distance
if distance < closestDistance :
closestDistance = distance
closestFillLoop = remainingFillLoop
newClosestFillLoop = getLoopInsideContainingLoop ( closestFillLoop , remainingFillLoops )
while newClosestFillLoop != None :
closestFillLoop = newClosestFillLoop
newClosestFillLoop = getLoopInsideContainingLoop ( closestFillLoop , remainingFillLoops )
remainingFillLoops . remove ( closestFillLoop )
addToThreadsFromLoop ( extrusionHalfWidth , ' loop ' , closestFillLoop [ : ] , oldOrderedLocation , skein )
def transferClosestPath ( oldOrderedLocation , remainingPaths , skein ) :
' Transfer the closest remaining path. '
closestDistance = 987654321987654321.0
closestPath = None
oldOrderedLocationComplex = oldOrderedLocation . dropAxis ( )
for remainingPath in remainingPaths :
distance = min ( abs ( oldOrderedLocationComplex - remainingPath [ 0 ] ) , abs ( oldOrderedLocationComplex - remainingPath [ - 1 ] ) )
if distance < closestDistance :
closestDistance = distance
closestPath = remainingPath
remainingPaths . remove ( closestPath )
skein . addGcodeFromThreadZ ( closestPath , oldOrderedLocation . z )
oldOrderedLocation . x = closestPath [ - 1 ] . real
oldOrderedLocation . y = closestPath [ - 1 ] . imag
def transferClosestPaths ( oldOrderedLocation , remainingPaths , skein ) :
' Transfer the closest remaining paths. '
while len ( remainingPaths ) > 0 :
transferClosestPath ( oldOrderedLocation , remainingPaths , skein )
def transferPathsToNestedRings ( nestedRings , paths ) :
' Transfer paths to nested rings. '
for nestedRing in nestedRings :
nestedRing . transferPaths ( paths )
def translateVector3Path ( path , translateVector3 ) :
' Translate the vector3 path. '
for point in path :
point . setToVector3 ( point + translateVector3 )
def translateVector3Paths ( paths , translateVector3 ) :
' Translate the vector3 paths. '
for path in paths :
translateVector3Path ( path , translateVector3 )
def unbuckleBasis ( basis , maximumUnbuckling , normal ) :
' Unbuckle space. '
normalDot = basis . dot ( normal )
dotComplement = math . sqrt ( 1.0 - normalDot * normalDot )
unbuckling = maximumUnbuckling
if dotComplement > 0.0 :
unbuckling = min ( 1.0 / dotComplement , maximumUnbuckling )
basis . setToVector3 ( basis * unbuckling )
class DistanceIndex :
' A class to hold the distance and the index of the loop. '
def __init__ ( self , distance , index ) :
' Initialize. '
self . distance = distance
self . index = index
def __repr__ ( self ) :
' Get the string representation of this distance index. '
return ' %s , %s ' % ( self . distance , self . index )
class Endpoint :
' The endpoint of a segment. '
def __repr__ ( self ) :
' Get the string representation of this Endpoint. '
return ' Endpoint %s , %s ' % ( self . point , self . otherEndpoint . point )
def getClosestEndpoint ( self , endpoints ) :
' Get closest endpoint. '
smallestDistance = 987654321987654321.0
closestEndpoint = None
for endpoint in endpoints :
distance = abs ( self . point - endpoint . point )
if distance < smallestDistance :
smallestDistance = distance
closestEndpoint = endpoint
return closestEndpoint
2012-03-22 12:43:07 +00:00
def getClosestMiss ( self , endpoints , path , pixelDictionary , sharpestProduct , width ) :
2012-02-19 23:30:49 +00:00
' Get the closest endpoint which the segment to that endpoint misses the other extrusions. '
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 )
penultimateMinusPoint = penultimatePoint - self . point
if abs ( penultimateMinusPoint ) > 0.0 :
penultimateMinusPoint / = abs ( penultimateMinusPoint )
for endpoint in endpoints :
endpoint . segment = endpoint . point - self . point
endpoint . segmentLength = abs ( endpoint . segment )
if endpoint . segmentLength < = 0.0 :
return 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
2012-03-22 12:43:07 +00:00
isOverlappingSelf = getDotProduct ( penultimateMinusPoint , normalizedSegment ) > sharpestProduct
2012-02-19 23:30:49 +00:00
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 ) :
isOverlappingSelf = True
if not isOverlappingSelf :
totalMaskTable = pathMaskTable . copy ( )
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 ) :
return endpoint
return None
2012-03-22 12:43:07 +00:00
def getClosestMissCheckEndpointPath ( self , endpoints , path , pixelDictionary , sharpestProduct , width ) :
2012-02-19 23:30:49 +00:00
' 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 ]
2012-03-22 12:43:07 +00:00
addSegmentToPixelTable ( penultimatePoint , self . point , pathMaskTable , 0 , 0 , width )
2012-02-19 23:30:49 +00:00
penultimateMinusPoint = penultimatePoint - self . point
if abs ( penultimateMinusPoint ) > 0.0 :
penultimateMinusPoint / = abs ( penultimateMinusPoint )
for endpoint in endpoints :
endpoint . segment = endpoint . point - self . point
endpoint . segmentLength = abs ( endpoint . segment )
if endpoint . segmentLength < = 0.0 :
return 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
2012-03-22 12:43:07 +00:00
isOverlappingSelf = getDotProduct ( penultimateMinusPoint , normalizedSegment ) > sharpestProduct
2012-02-19 23:30:49 +00:00
if not isOverlappingSelf :
if len ( path ) > 2 :
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
pointRotated = segmentYMirror * self . point
endpointPointRotated = segmentYMirror * endpoint . point
2012-03-22 12:43:07 +00:00
if isXSegmentIntersectingPath ( path [ max ( 0 , len ( path ) - 21 ) : - 1 ] , pointRotated . real , endpointPointRotated . real , segmentYMirror , pointRotated . imag ) :
2012-02-19 23:30:49 +00:00
isOverlappingSelf = True
endpointPath = endpoint . path
2012-03-22 12:43:07 +00:00
if len ( endpointPath ) > 2 :
2012-02-19 23:30:49 +00:00
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
pointRotated = segmentYMirror * self . point
endpointPointRotated = segmentYMirror * endpoint . point
2012-03-22 12:43:07 +00:00
if isXSegmentIntersectingPath ( endpointPath , pointRotated . real , endpointPointRotated . real , segmentYMirror , pointRotated . imag ) :
2012-02-19 23:30:49 +00:00
isOverlappingSelf = True
if not isOverlappingSelf :
totalMaskTable = pathMaskTable . copy ( )
2012-03-22 12:43:07 +00:00
addSegmentToPixelTable ( endpoint . point , endpoint . otherEndpoint . point , totalMaskTable , 0 , 0 , width )
2012-02-19 23:30:49 +00:00
segmentTable = { }
2012-03-22 12:43:07 +00:00
addSegmentToPixelTable ( self . point , endpoint . point , segmentTable , 0 , 0 , width )
if not isPixelTableIntersecting ( pixelDictionary , segmentTable , totalMaskTable ) :
2012-02-19 23:30:49 +00:00
return endpoint
return None
def getFromOtherPoint ( self , otherEndpoint , point ) :
' Initialize from other endpoint. '
self . otherEndpoint = otherEndpoint
self . point = point
return self
class LoopLayer :
' Loops with a z. '
def __init__ ( self , z ) :
' Initialize. '
self . loops = [ ]
self . z = z
def __repr__ ( self ) :
' Get the string representation of this loop layer. '
return ' %s , %s ' % ( self . z , self . loops )
class NestedRing :
' A nested ring. '
def __init__ ( self ) :
' Initialize. '
self . boundary = [ ]
self . innerNestedRings = None
def __repr__ ( self ) :
' Get the string representation of this nested ring. '
return str ( self . __dict__ )
def addFlattenedNestedRings ( self , flattenedNestedRings ) :
' Add flattened nested rings. '
flattenedNestedRings . append ( self )
for innerNestedRing in self . innerNestedRings :
flattenedNestedRings + = getFlattenedNestedRings ( innerNestedRing . innerNestedRings )
def getFromInsideSurroundings ( self , inputSurroundingInsides ) :
' Initialize from inside nested rings. '
transferredSurroundings = getTransferredNestedRings ( inputSurroundingInsides , self . boundary )
self . innerNestedRings = getOrderedNestedRings ( transferredSurroundings )
return self
class NestedBand ( NestedRing ) :
' A loop that surrounds paths. '
def __init__ ( self ) :
' Initialize. '
NestedRing . __init__ ( self )
self . edgePaths = [ ]
self . extraLoops = [ ]
self . infillBoundaries = [ ]
self . infillPaths = [ ]
# self.lastExistingFillLoops = None
self . lastFillLoops = None
self . loop = None
self . penultimateFillLoops = [ ]
self . z = None
def __repr__ ( self ) :
' Get the string representation of this nested ring. '
stringRepresentation = ' boundary \n %s \n ' % self . boundary
stringRepresentation + = ' loop \n %s \n ' % self . loop
stringRepresentation + = ' inner nested rings \n %s \n ' % self . innerNestedRings
stringRepresentation + = ' infillPaths \n '
for infillPath in self . infillPaths :
stringRepresentation + = ' infillPath \n %s \n ' % infillPath
stringRepresentation + = ' edgePaths \n '
for edgePath in self . edgePaths :
stringRepresentation + = ' edgePath \n %s \n ' % edgePath
return stringRepresentation + ' \n '
def addPerimeterInner ( self , extrusionHalfWidth , oldOrderedLocation , skein , threadSequence ) :
' Add to the edge and the inner island. '
if self . loop == None :
skein . distanceFeedRate . addLine ( ' (<edgePath>) ' )
transferClosestPaths ( oldOrderedLocation , self . edgePaths [ : ] , skein )
skein . distanceFeedRate . addLine ( ' (</edgePath>) ' )
else :
addToThreadsFromLoop ( extrusionHalfWidth , ' edge ' , self . loop [ : ] , oldOrderedLocation , skein )
skein . distanceFeedRate . addLine ( ' (</boundaryPerimeter>) ' )
addToThreadsRemove ( extrusionHalfWidth , self . innerNestedRings [ : ] , oldOrderedLocation , skein , threadSequence )
def addToBoundary ( self , vector3 ) :
' Add vector3 to boundary. '
self . boundary . append ( vector3 . dropAxis ( ) )
self . z = vector3 . z
def addToLoop ( self , vector3 ) :
' Add vector3 to loop. '
if self . loop == None :
self . loop = [ ]
self . loop . append ( vector3 . dropAxis ( ) )
self . z = vector3 . z
def addToThreads ( self , extrusionHalfWidth , oldOrderedLocation , skein , threadSequence ) :
' Add to paths from the last location. '
addNestedRingBeginning ( skein . distanceFeedRate , self . boundary , self . z )
threadFunctionDictionary = {
' infill ' : self . transferInfillPaths , ' loops ' : self . transferClosestFillLoops , ' edge ' : self . addPerimeterInner }
for threadType in threadSequence :
threadFunctionDictionary [ threadType ] ( extrusionHalfWidth , oldOrderedLocation , skein , threadSequence )
skein . distanceFeedRate . addLine ( ' (</nestedRing>) ' )
def getFillLoops ( self , penultimateFillLoops ) :
' Get last fill loops from the outside loop and the loops inside the inside loops. '
fillLoops = self . getLoopsToBeFilled ( ) [ : ]
surroundingBoundaries = self . getSurroundingBoundaries ( )
withinLoops = [ ]
if penultimateFillLoops == None :
penultimateFillLoops = self . penultimateFillLoops
if penultimateFillLoops == None :
print ( ' Warning, penultimateFillLoops == None in getFillLoops in NestedBand in euclidean. ' )
return fillLoops
for penultimateFillLoop in penultimateFillLoops :
if len ( penultimateFillLoop ) > 2 :
if getIsInFilledRegion ( surroundingBoundaries , penultimateFillLoop [ 0 ] ) :
withinLoops . append ( penultimateFillLoop )
if not getIsInFilledRegionByPaths ( self . penultimateFillLoops , fillLoops ) :
fillLoops + = self . penultimateFillLoops
for nestedRing in self . innerNestedRings :
fillLoops + = getFillOfSurroundings ( nestedRing . innerNestedRings , penultimateFillLoops )
return fillLoops
#
# def getLastExistingFillLoops(self):
# 'Get last existing fill loops.'
# lastExistingFillLoops = self.lastExistingFillLoops[:]
# for nestedRing in self.innerNestedRings:
# lastExistingFillLoops += nestedRing.getLastExistingFillLoops()
# return lastExistingFillLoops
def getLoopsToBeFilled ( self ) :
' Get last fill loops from the outside loop and the loops inside the inside loops. '
if self . lastFillLoops == None :
return self . getSurroundingBoundaries ( )
return self . lastFillLoops
def getSurroundingBoundaries ( self ) :
' Get the boundary of the surronding loop plus any boundaries of the innerNestedRings. '
surroundingBoundaries = [ self . boundary ]
for nestedRing in self . innerNestedRings :
surroundingBoundaries . append ( nestedRing . boundary )
return surroundingBoundaries
def transferClosestFillLoops ( self , extrusionHalfWidth , oldOrderedLocation , skein , threadSequence ) :
' Transfer closest fill loops. '
if len ( self . extraLoops ) < 1 :
return
remainingFillLoops = self . extraLoops [ : ]
while len ( remainingFillLoops ) > 0 :
transferClosestFillLoop ( extrusionHalfWidth , oldOrderedLocation , remainingFillLoops , skein )
def transferInfillPaths ( self , extrusionHalfWidth , oldOrderedLocation , skein , threadSequence ) :
' Transfer the infill paths. '
if len ( self . infillBoundaries ) == 0 and len ( self . infillPaths ) == 0 :
return
skein . distanceFeedRate . addLine ( ' (<infill>) ' )
for infillBoundary in self . infillBoundaries :
skein . distanceFeedRate . addLine ( ' (<infillBoundary>) ' )
for infillPoint in infillBoundary :
infillPointVector3 = Vector3 ( infillPoint . real , infillPoint . imag , self . z )
skein . distanceFeedRate . addLine ( skein . distanceFeedRate . getInfillBoundaryLine ( infillPointVector3 ) )
skein . distanceFeedRate . addLine ( ' (</infillBoundary>) ' )
transferClosestPaths ( oldOrderedLocation , self . infillPaths [ : ] , skein )
skein . distanceFeedRate . addLine ( ' (</infill>) ' )
def transferPaths ( self , paths ) :
' Transfer paths. '
for nestedRing in self . innerNestedRings :
transferPathsToNestedRings ( nestedRing . innerNestedRings , paths )
self . infillPaths = getTransferredPaths ( paths , self . boundary )
class PathZ :
' Complex path with a z. '
def __init__ ( self , z ) :
self . path = [ ]
self . z = z
def __repr__ ( self ) :
' Get the string representation of this path z. '
return ' %s , %s ' % ( self . z , self . path )
class ProjectiveSpace :
' Class to define a projective space. '
def __init__ ( self , basisX = Vector3 ( 1.0 , 0.0 , 0.0 ) , basisY = Vector3 ( 0.0 , 1.0 , 0.0 ) , basisZ = Vector3 ( 0.0 , 0.0 , 1.0 ) ) :
' Initialize the basis vectors. '
self . basisX = basisX
self . basisY = basisY
self . basisZ = basisZ
def __repr__ ( self ) :
' Get the string representation of this ProjectivePlane. '
return ' %s , %s , %s ' % ( self . basisX , self . basisY , self . basisZ )
def getByBasisXZ ( self , basisX , basisZ ) :
' Get by x basis x and y basis. '
self . basisX = basisX
self . basisZ = basisZ
self . basisX . normalize ( )
self . basisY = basisZ . cross ( self . basisX )
self . basisY . normalize ( )
return self
def getByBasisZFirst ( self , basisZ , firstVector3 ) :
' Get by basisZ and first. '
self . basisZ = basisZ
self . basisY = basisZ . cross ( firstVector3 )
self . basisY . normalize ( )
self . basisX = self . basisY . cross ( self . basisZ )
self . basisX . normalize ( )
return self
def getByBasisZTop ( self , basisZ , top ) :
' Get by basisZ and top. '
return self . getByBasisXZ ( top . cross ( basisZ ) , basisZ )
def getByLatitudeLongitude ( self , viewpointLatitude , viewpointLongitude ) :
' Get by latitude and longitude. '
longitudeComplex = getWiddershinsUnitPolar ( math . radians ( 90.0 - viewpointLongitude ) )
viewpointLatitudeRatio = getWiddershinsUnitPolar ( math . radians ( viewpointLatitude ) )
basisZ = Vector3 ( viewpointLatitudeRatio . imag * longitudeComplex . real , viewpointLatitudeRatio . imag * longitudeComplex . imag , viewpointLatitudeRatio . real )
return self . getByBasisXZ ( Vector3 ( - longitudeComplex . imag , longitudeComplex . real , 0.0 ) , basisZ )
def getByTilt ( self , tilt ) :
' Get by latitude and longitude. '
xPlaneAngle = getWiddershinsUnitPolar ( tilt . real )
self . basisX = Vector3 ( xPlaneAngle . real , 0.0 , xPlaneAngle . imag )
yPlaneAngle = getWiddershinsUnitPolar ( tilt . imag )
self . basisY = Vector3 ( 0.0 , yPlaneAngle . real , yPlaneAngle . imag )
self . basisZ = self . basisX . cross ( self . basisY )
return self
def getComplexByComplex ( self , pointComplex ) :
' Get complex by complex point. '
return self . basisX . dropAxis ( ) * pointComplex . real + self . basisY . dropAxis ( ) * pointComplex . imag
def getCopy ( self ) :
' Get copy. '
return ProjectiveSpace ( self . basisX , self . basisY , self . basisZ )
def getDotComplex ( self , point ) :
' Get the dot complex. '
return complex ( point . dot ( self . basisX ) , point . dot ( self . basisY ) )
def getDotVector3 ( self , point ) :
' Get the dot vector3. '
return Vector3 ( point . dot ( self . basisX ) , point . dot ( self . basisY ) , point . dot ( self . basisZ ) )
def getNextSpace ( self , nextNormal ) :
' Get next space by next normal. '
nextSpace = self . getCopy ( )
nextSpace . normalize ( )
dotNext = nextSpace . basisZ . dot ( nextNormal )
if dotNext > 0.999999 :
return nextSpace
if dotNext < - 0.999999 :
nextSpace . basisX = - nextSpace . basisX
return nextSpace
crossNext = nextSpace . basisZ . cross ( nextNormal )
oldBasis = ProjectiveSpace ( ) . getByBasisZTop ( nextSpace . basisZ , crossNext )
newBasis = ProjectiveSpace ( ) . getByBasisZTop ( nextNormal , crossNext )
nextSpace . basisX = newBasis . getVector3ByPoint ( oldBasis . getDotVector3 ( nextSpace . basisX ) )
nextSpace . basisY = newBasis . getVector3ByPoint ( oldBasis . getDotVector3 ( nextSpace . basisY ) )
nextSpace . basisZ = newBasis . getVector3ByPoint ( oldBasis . getDotVector3 ( nextSpace . basisZ ) )
nextSpace . normalize ( )
return nextSpace
def getSpaceByXYScaleAngle ( self , angle , scale ) :
' Get space by angle and scale. '
spaceByXYScaleRotation = ProjectiveSpace ( )
planeAngle = getWiddershinsUnitPolar ( angle )
spaceByXYScaleRotation . basisX = self . basisX * scale . real * planeAngle . real + self . basisY * scale . imag * planeAngle . imag
spaceByXYScaleRotation . basisY = - self . basisX * scale . real * planeAngle . imag + self . basisY * scale . imag * planeAngle . real
spaceByXYScaleRotation . basisZ = self . basisZ
return spaceByXYScaleRotation
def getVector3ByPoint ( self , point ) :
' Get vector3 by point. '
return self . basisX * point . x + self . basisY * point . y + self . basisZ * point . z
def normalize ( self ) :
' Normalize. '
self . basisX . normalize ( )
self . basisY . normalize ( )
self . basisZ . normalize ( )
def unbuckle ( self , maximumUnbuckling , normal ) :
' Unbuckle space. '
unbuckleBasis ( self . basisX , maximumUnbuckling , normal )
unbuckleBasis ( self . basisY , maximumUnbuckling , normal )
class XIntersectionIndex :
' A class to hold the x intersection position and the index of the loop which intersected. '
def __init__ ( self , index , x ) :
' Initialize. '
self . index = index
self . x = x
def __cmp__ ( self , other ) :
' Get comparison in order to sort x intersections in ascending order of x. '
if self . x < other . x :
return - 1
return int ( self . x > other . x )
def __eq__ ( self , other ) :
' Determine whether this XIntersectionIndex is identical to other one. '
if other == None :
return False
if other . __class__ != self . __class__ :
return False
return self . index == other . index and self . x == other . x
def __ne__ ( self , other ) :
' Determine whether this XIntersectionIndex is not identical to other one. '
return not self . __eq__ ( other )
def __repr__ ( self ) :
' Get the string representation of this x intersection. '
return ' XIntersectionIndex index %s ; x %s ' % ( self . index , self . x )