Updated build script to create win32/linux/macos versions. Fixed the defaults to they work with PLA. Fixed the temperature plugin default "ON" problem. Removed all profiles except for PLA.
2498 lines
94 KiB
Python
2498 lines
94 KiB
Python
"""
|
|
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
|
|
import cStringIO
|
|
import math
|
|
import random
|
|
|
|
|
|
__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
|
|
|
|
def concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ):
|
|
'Get connected paths from paths.'
|
|
bottomSegment = segments[ pathIndex ]
|
|
path = paths[ pathIndex ]
|
|
if bottomSegment == None:
|
|
connectedPaths.append(path)
|
|
return
|
|
endpoints = getEndpointsFromSegments( segments[ pathIndex + 1 : ] )
|
|
bottomSegmentEndpoint = bottomSegment[0]
|
|
nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width )
|
|
if nextEndpoint == None:
|
|
bottomSegmentEndpoint = bottomSegment[1]
|
|
nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width )
|
|
if nextEndpoint == None:
|
|
connectedPaths.append(path)
|
|
return
|
|
if len( bottomSegmentEndpoint.path ) > 0 and len( nextEndpoint.path ) > 0:
|
|
bottomEnd = bottomSegmentEndpoint.path[-1]
|
|
nextBegin = nextEndpoint.path[-1]
|
|
nextMinusBottomNormalized = getNormalized( nextBegin - bottomEnd )
|
|
if len( bottomSegmentEndpoint.path ) > 1:
|
|
bottomPenultimate = bottomSegmentEndpoint.path[-2]
|
|
if getDotProduct( getNormalized( bottomPenultimate - bottomEnd ), nextMinusBottomNormalized ) > 0.9:
|
|
connectedPaths.append(path)
|
|
return
|
|
if len( nextEndpoint.path ) > 1:
|
|
nextPenultimate = nextEndpoint.path[-2]
|
|
if getDotProduct( getNormalized( nextPenultimate - nextBegin ), - nextMinusBottomNormalized ) > 0.9:
|
|
connectedPaths.append(path)
|
|
return
|
|
nextEndpoint.path.reverse()
|
|
concatenatedPath = bottomSegmentEndpoint.path + nextEndpoint.path
|
|
paths[ nextEndpoint.pathIndex ] = concatenatedPath
|
|
segments[ nextEndpoint.pathIndex ] = getSegmentFromPath( concatenatedPath, nextEndpoint.pathIndex )
|
|
addValueSegmentToPixelTable( bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width )
|
|
|
|
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
|
|
|
|
def getConnectedPaths( paths, pixelDictionary, width ):
|
|
'Get connected paths from paths.'
|
|
if len(paths) < 2:
|
|
return paths
|
|
connectedPaths = []
|
|
segments = []
|
|
for pathIndex in xrange( len(paths) ):
|
|
path = paths[ pathIndex ]
|
|
segments.append( getSegmentFromPath( path, pathIndex ) )
|
|
for pathIndex in xrange( 0, len(paths) - 1 ):
|
|
concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width )
|
|
connectedPaths.append( paths[-1] )
|
|
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
|
|
channelRadius = radius * .01
|
|
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
|
|
channelRadius = radius * .01
|
|
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
|
|
|
|
def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, width):
|
|
'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:
|
|
nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width)
|
|
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)
|
|
nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width)
|
|
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 %ss' % ( 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
|
|
|
|
def getClosestMiss(self, endpoints, path, pixelDictionary, width):
|
|
'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
|
|
isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > 0.9
|
|
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
|
|
|
|
def getClosestMissCheckEndpointPath( self, endpoints, path, pixelDictionary, width ):
|
|
'Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.'
|
|
pathMaskTable = {}
|
|
smallestDistance = 987654321.0
|
|
penultimateMinusPoint = complex(0.0, 0.0)
|
|
if len(path) > 1:
|
|
penultimatePoint = path[-2]
|
|
addSegmentToPixelTable( penultimatePoint, self.point, pathMaskTable, 0, 0, width )
|
|
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
|
|
isOverlappingSelf = getDotProduct( penultimateMinusPoint, normalizedSegment ) > 0.9
|
|
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
|
|
endpointPath = endpoint.path
|
|
if len( endpointPath ) > 2:
|
|
segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
|
|
pointRotated = segmentYMirror * self.point
|
|
endpointPointRotated = segmentYMirror * endpoint.point
|
|
if isXSegmentIntersectingPath( endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ):
|
|
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
|
|
|
|
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 )
|