2496 lines
94 KiB
Python
2496 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
|
|
|
|
from fabmetheus_utilities.vector3 import Vector3
|
|
from fabmetheus_utilities import xml_simple_writer
|
|
|
|
import sys
|
|
import math
|
|
import random
|
|
|
|
if sys.version_info[0] < 3:
|
|
import cStringIO
|
|
else:
|
|
import io as cStringIO
|
|
|
|
__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, sharpestProduct, 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, sharpestProduct, width)
|
|
if nextEndpoint == None:
|
|
bottomSegmentEndpoint = bottomSegment[1]
|
|
nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath(endpoints, bottomSegmentEndpoint.path, pixelDictionary, sharpestProduct, width)
|
|
if nextEndpoint == None:
|
|
connectedPaths.append(path)
|
|
return
|
|
if len(bottomSegmentEndpoint.path) > 0 and len(nextEndpoint.path) > 0:
|
|
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.99:
|
|
connectedPaths.append(path)
|
|
return
|
|
if len( nextEndpoint.path ) > 1:
|
|
nextPenultimate = nextEndpoint.path[-2]
|
|
if getDotProduct(getNormalized(nextPenultimate - nextBegin), - nextMinusBottomNormalized) > 0.99:
|
|
connectedPaths.append(path)
|
|
return
|
|
nextEndpoint.path.reverse()
|
|
concatenatedPath = bottomSegmentEndpoint.path + nextEndpoint.path
|
|
paths[nextEndpoint.pathIndex] = concatenatedPath
|
|
segments[nextEndpoint.pathIndex] = getSegmentFromPath(concatenatedPath, nextEndpoint.pathIndex)
|
|
addValueSegmentToPixelTable(bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width)
|
|
|
|
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, sharpestProduct, width):
|
|
'Get connected paths from paths.'
|
|
if len(paths) < 2:
|
|
return paths
|
|
connectedPaths = []
|
|
segments = []
|
|
for pathIndex in xrange(len(paths)):
|
|
path = paths[pathIndex]
|
|
segments.append(getSegmentFromPath(path, pathIndex))
|
|
for pathIndex in xrange(0, len(paths) - 1):
|
|
concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, sharpestProduct, 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 = abs(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 = abs(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, sharpestProduct, 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, sharpestProduct, 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, sharpestProduct, 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(object):
|
|
'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(object):
|
|
'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, sharpestProduct, 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) > sharpestProduct
|
|
if not isOverlappingSelf:
|
|
if len(path) > 2:
|
|
segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
|
|
pointRotated = segmentYMirror * self.point
|
|
endpointPointRotated = segmentYMirror * endpoint.point
|
|
if isXSegmentIntersectingPath(path[max(0, len(path) - 21) : -1], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag):
|
|
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, sharpestProduct, width):
|
|
'Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.'
|
|
pathMaskTable = {}
|
|
smallestDistance = 987654321.0
|
|
penultimateMinusPoint = complex(0.0, 0.0)
|
|
if len(path) > 1:
|
|
penultimatePoint = path[-2]
|
|
addSegmentToPixelTable(penultimatePoint, self.point, pathMaskTable, 0, 0, width)
|
|
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) > sharpestProduct
|
|
if not isOverlappingSelf:
|
|
if len(path) > 2:
|
|
segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
|
|
pointRotated = segmentYMirror * self.point
|
|
endpointPointRotated = segmentYMirror * endpoint.point
|
|
if isXSegmentIntersectingPath(path[ max(0, len(path) - 21) : -1], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag):
|
|
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(object):
|
|
'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(object):
|
|
'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(object):
|
|
'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(object):
|
|
'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(object):
|
|
'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 )
|