2012-02-19 23:30:49 +00:00
"""
Intercircle is a collection of utilities for intersecting circles , used to get smooth loops around a collection of points and inset & outset loops .
"""
from __future__ import absolute_import
try :
import psyco
psyco . full ( )
except :
pass
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__
from fabmetheus_utilities . vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__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 '
globalDecreasingRadiusMultipliers = [ 1.0 , 0.55 , 0.35 , 0.2 ]
globalIntercircleMultiplier = 1.04 # 1.02 is enough to stop known intersection
def addCircleIntersectionLoop ( circleIntersectionLoop , circleIntersections ) :
' Add a circle intersection loop. '
firstCircleIntersection = circleIntersectionLoop [ 0 ]
circleIntersectionAhead = firstCircleIntersection
for circleIntersectionIndex in xrange ( len ( circleIntersections ) + 1 ) :
circleIntersectionAhead = circleIntersectionAhead . getCircleIntersectionAhead ( )
if circleIntersectionAhead == firstCircleIntersection or circleIntersectionAhead == None :
firstCircleIntersection . steppedOn = True
return
circleIntersectionAhead . addToList ( circleIntersectionLoop )
firstCircleIntersection . steppedOn = True
print ( ' Warning, addCircleIntersectionLoop would have gone into an endless loop. ' )
print ( ' circleIntersectionLoop ' )
for circleIntersection in circleIntersectionLoop :
print ( circleIntersection )
print ( circleIntersection . circleNodeAhead )
print ( circleIntersection . circleNodeBehind )
print ( ' firstCircleIntersection ' )
print ( firstCircleIntersection )
print ( ' circleIntersections ' )
for circleIntersection in circleIntersections :
print ( circleIntersection )
def addEndCap ( begin , end , points , radius ) :
' Get circular end cap. '
beginMinusEnd = begin - end
beginMinusEndLength = abs ( beginMinusEnd )
if beginMinusEndLength < = 0.0 :
points . append ( begin )
return
beginMinusEnd * = radius / beginMinusEndLength
perpendicular = complex ( - beginMinusEnd . imag , beginMinusEnd . real )
numberOfSides = 20 # to end up with close to unit length corners, 5 * 4
numberOfPositiveSides = numberOfSides / 2
totalAngle = 0.0
angle = euclidean . globalTau / float ( numberOfSides )
# dotProductMultiplier to compensate for the corner outset in addInsetPointFromClockwiseTriple
dotProductMultiplier = 2.0 - 1.0 / math . cos ( 0.5 * angle )
for sideIndex in xrange ( numberOfPositiveSides + 1 ) :
circumferentialPoint = math . sin ( totalAngle ) * beginMinusEnd + math . cos ( totalAngle ) * perpendicular
points . append ( begin + circumferentialPoint * dotProductMultiplier )
totalAngle + = angle
def addHalfPath ( path , points , radius , thresholdRatio = 0.9 ) :
' Add the points from every point on a half path and between points. '
lessThanRadius = 0.75 * radius
for pointIndex in xrange ( len ( path ) - 1 ) :
begin = path [ pointIndex ]
center = path [ pointIndex + 1 ]
centerBegin = getWiddershinsByLength ( begin , center , radius )
if centerBegin != None :
addPointsFromSegment ( begin + centerBegin , center + centerBegin , points , lessThanRadius , thresholdRatio )
endIndex = pointIndex + 2
if endIndex < len ( path ) :
end = path [ endIndex ]
centerEnd = getWiddershinsByLength ( center , end , radius )
if centerBegin != None and centerEnd != None :
centerPerpendicular = 0.5 * ( centerBegin + centerEnd )
points . append ( center + centerPerpendicular )
if euclidean . getCrossProduct ( centerBegin , centerEnd ) < 0.0 :
points . append ( center + centerBegin )
points . append ( center + centerEnd )
else :
points . append ( center )
addEndCap ( path [ 0 ] , path [ 1 ] , points , radius )
def addInsetPointFromClockwiseTriple ( begin , center , end , loop , radius ) :
' Get inset point with possible intersection from clockwise triple, out from widdershins loop. '
centerMinusBegin = center - begin
centerMinusBeginLength = abs ( centerMinusBegin )
centerMinusBeginClockwise = None
if centerMinusBeginLength > 0.0 :
centerMinusBeginClockwise = complex ( centerMinusBegin . imag , - centerMinusBegin . real ) / centerMinusBeginLength
endMinusCenter = end - center
endMinusCenterLength = abs ( endMinusCenter )
endMinusCenterClockwise = None
if endMinusCenterLength > 0.0 :
endMinusCenterClockwise = complex ( endMinusCenter . imag , - endMinusCenter . real ) / endMinusCenterLength
if centerMinusBeginClockwise == None and endMinusCenterClockwise == None :
return
if centerMinusBeginClockwise == None :
loop . append ( center + endMinusCenterClockwise * radius )
return
if endMinusCenterClockwise == None :
loop . append ( center + centerMinusBeginClockwise * radius )
return
centerClockwise = 0.5 * ( centerMinusBeginClockwise + endMinusCenterClockwise )
dotProduct = euclidean . getDotProduct ( centerMinusBeginClockwise , centerClockwise )
loop . append ( center + centerClockwise * radius / max ( 0.4 , abs ( dotProduct ) ) ) # 0.4 to avoid pointy corners
def addOrbits ( distanceFeedRate , loop , orbitalFeedRatePerSecond , temperatureChangeTime , z ) :
' Add orbits with the extruder off. '
timeInOrbit = 0.0
while timeInOrbit < temperatureChangeTime :
for point in loop :
distanceFeedRate . addGcodeMovementZWithFeedRate ( 60.0 * orbitalFeedRatePerSecond , point , z )
timeInOrbit + = euclidean . getLoopLength ( loop ) / orbitalFeedRatePerSecond
def addOrbitsIfLarge ( distanceFeedRate , loop , orbitalFeedRatePerSecond , temperatureChangeTime , z ) :
' Add orbits with the extruder off if the orbits are large enough. '
if orbitsAreLarge ( loop , temperatureChangeTime ) :
addOrbits ( distanceFeedRate , loop , orbitalFeedRatePerSecond , temperatureChangeTime , z )
def addPointsFromSegment ( pointBegin , pointEnd , points , radius , thresholdRatio = 0.9 ) :
' Add point complexes between the endpoints of a segment. '
if radius < = 0.0 :
print ( ' This should never happen, radius should never be zero or less in addPointsFromSegment in intercircle. ' )
thresholdRadius = radius * thresholdRatio # a higher number would be faster but would leave bigger dangling loops and extra dangling loops.
thresholdDiameter = thresholdRadius + thresholdRadius
segment = pointEnd - pointBegin
segmentLength = abs ( segment )
extraCircles = int ( math . floor ( segmentLength / thresholdDiameter ) )
if extraCircles < 1 :
return
if segmentLength == 0.0 :
print ( ' Warning, segmentLength = 0.0 in intercircle. ' )
print ( ' pointBegin ' )
print ( pointBegin )
print ( pointEnd )
return
if extraCircles < 2 :
lengthIncrement = segmentLength / ( float ( extraCircles ) + 1.0 )
segment * = lengthIncrement / segmentLength
pointBegin + = segment
else :
pointBegin + = segment * thresholdDiameter / segmentLength
remainingLength = segmentLength - thresholdDiameter - thresholdDiameter
lengthIncrement = remainingLength / ( float ( extraCircles ) - 1.0 )
segment * = lengthIncrement / segmentLength
for circleIndex in xrange ( extraCircles ) :
points . append ( pointBegin )
pointBegin + = segment
def directLoop ( isWiddershins , loop ) :
' Direct the loop. '
if euclidean . isWiddershins ( loop ) != isWiddershins :
loop . reverse ( )
def directLoopLists ( isWiddershins , loopLists ) :
' Direct the loop lists. '
for loopList in loopLists :
directLoops ( isWiddershins , loopList )
def directLoops ( isWiddershins , loops ) :
' Direct the loops. '
for loop in loops :
directLoop ( isWiddershins , loop )
def getAroundsFromLoop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the arounds from the loop. '
return getAroundsFromPoints ( getPointsFromLoop ( loop , radius , thresholdRatio ) , radius )
def getAroundsFromLoops ( loops , radius , thresholdRatio = 0.9 ) :
' Get the arounds from the loops. '
return getAroundsFromPoints ( getPointsFromLoops ( loops , radius , thresholdRatio ) , radius )
def getAroundsFromPath ( path , radius , thresholdRatio = 0.9 ) :
' Get the arounds from the path. '
radius = abs ( radius )
points = getPointsFromPath ( path , radius , thresholdRatio )
return getAroundsFromPathPoints ( points , radius , thresholdRatio = 0.9 )
def getAroundsFromPathPoints ( points , radius , thresholdRatio = 0.9 ) :
' Get the arounds from the path. '
centers = getCentersFromPoints ( points , 0.8 * radius )
arounds = [ ]
for center in centers :
if euclidean . isWiddershins ( center ) :
arounds . append ( euclidean . getSimplifiedPath ( center , radius ) )
return arounds
def getAroundsFromPaths ( paths , radius , thresholdRatio = 0.9 ) :
' Get the arounds from the path. '
radius = abs ( radius )
points = [ ]
for path in paths :
points + = getPointsFromPath ( path , radius , thresholdRatio )
return getAroundsFromPathPoints ( points , radius , thresholdRatio = 0.9 )
def getAroundsFromPoints ( points , radius ) :
' Get the arounds from the points. '
arounds = [ ]
radius = abs ( radius )
centers = getCentersFromPoints ( points , globalIntercircleMultiplier * radius )
for center in centers :
inset = getSimplifiedInsetFromClockwiseLoop ( center , radius )
if isLargeSameDirection ( inset , center , radius ) :
arounds . append ( inset )
return arounds
def getCentersFromCircleNodes ( circleNodes , radius ) :
' Get the complex centers of the circle intersection loops from circle nodes. '
if len ( circleNodes ) < 2 :
return [ ]
circleIntersections = getCircleIntersectionsFromCircleNodes ( circleNodes )
circleIntersectionLoops = getCircleIntersectionLoops ( circleIntersections )
return getCentersFromIntersectionLoops ( circleIntersectionLoops , radius )
def getCentersFromIntersectionLoop ( circleIntersectionLoop , radius ) :
' Get the centers from the intersection loop. '
loop = [ ]
for circleIntersection in circleIntersectionLoop :
loop . append ( circleIntersection . circleNodeAhead . actualPoint )
return loop
def getCentersFromIntersectionLoops ( circleIntersectionLoops , radius ) :
' Get the centers from the intersection loops. '
centers = [ ]
for circleIntersectionLoop in circleIntersectionLoops :
centers . append ( getCentersFromIntersectionLoop ( circleIntersectionLoop , radius ) )
return centers
def getCentersFromLoop ( loop , radius ) :
' Get the centers of the loop. '
circleNodes = getCircleNodesFromLoop ( loop , radius )
return getCentersFromCircleNodes ( circleNodes , radius )
def getCentersFromLoopDirection ( isWiddershins , loop , radius ) :
' Get the centers of the loop which go around in the given direction. '
centers = getCentersFromLoop ( loop , radius )
return getLoopsFromLoopsDirection ( isWiddershins , centers )
def getCentersFromPoints ( points , radius ) :
' Get the centers from the points. '
circleNodes = getCircleNodesFromPoints ( points , abs ( radius ) )
return getCentersFromCircleNodes ( circleNodes , abs ( radius ) )
def getCircleIntersectionLoops ( circleIntersections ) :
' Get all the loops going through the circle intersections. '
circleIntersectionLoops = [ ]
for circleIntersection in circleIntersections :
if not circleIntersection . steppedOn :
circleIntersectionLoop = [ circleIntersection ]
circleIntersectionLoops . append ( circleIntersectionLoop )
addCircleIntersectionLoop ( circleIntersectionLoop , circleIntersections )
return circleIntersectionLoops
def getCircleIntersectionsFromCircleNodes ( circleNodes ) :
' Get all the circle intersections which exist between all the circle nodes. '
if len ( circleNodes ) < 1 :
return [ ]
circleIntersections = [ ]
index = 0
pixelTable = { }
for circleNode in circleNodes :
euclidean . addElementToPixelListFromPoint ( circleNode , pixelTable , circleNode . dividedPoint )
accumulatedCircleNodeTable = { }
for circleNodeIndex in xrange ( len ( circleNodes ) ) :
circleNodeBehind = circleNodes [ circleNodeIndex ]
circleNodeIndexMinusOne = circleNodeIndex - 1
if circleNodeIndexMinusOne > = 0 :
circleNodeAdditional = circleNodes [ circleNodeIndexMinusOne ]
euclidean . addElementToPixelListFromPoint ( circleNodeAdditional , accumulatedCircleNodeTable , 0.5 * circleNodeAdditional . dividedPoint )
withinNodes = circleNodeBehind . getWithinNodes ( accumulatedCircleNodeTable )
for circleNodeAhead in withinNodes :
circleIntersectionForward = CircleIntersection ( circleNodeAhead , index , circleNodeBehind )
if not circleIntersectionForward . isWithinCircles ( pixelTable ) :
circleIntersections . append ( circleIntersectionForward )
circleNodeBehind . circleIntersections . append ( circleIntersectionForward )
index + = 1
circleIntersectionBackward = CircleIntersection ( circleNodeBehind , index , circleNodeAhead )
if not circleIntersectionBackward . isWithinCircles ( pixelTable ) :
circleIntersections . append ( circleIntersectionBackward )
circleNodeAhead . circleIntersections . append ( circleIntersectionBackward )
index + = 1
return circleIntersections
def getCircleNodesFromLoop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the circle nodes from every point on a loop and between points. '
radius = abs ( radius )
points = getPointsFromLoop ( loop , radius , thresholdRatio )
return getCircleNodesFromPoints ( points , radius )
def getCircleNodesFromPoints ( points , radius ) :
' Get the circle nodes from a path. '
if radius == 0.0 :
print ( ' Warning, radius is 0 in getCircleNodesFromPoints in intercircle. ' )
print ( points )
return [ ]
circleNodes = [ ]
oneOverRadius = 1.000001 / radius # to avoid problem of accidentally integral radius
points = euclidean . getAwayPoints ( points , radius )
for point in points :
circleNodes . append ( CircleNode ( oneOverRadius , point ) )
return circleNodes
def getInsetLoopsFromLoop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the inset loops, which might overlap. '
if radius == 0.0 :
return [ loop ]
isInset = radius > 0
insetLoops = [ ]
isLoopWiddershins = euclidean . isWiddershins ( loop )
arounds = getAroundsFromLoop ( loop , radius , thresholdRatio )
for around in arounds :
leftPoint = euclidean . getLeftPoint ( around )
shouldBeWithin = ( isInset == isLoopWiddershins )
if euclidean . isPointInsideLoop ( loop , leftPoint ) == shouldBeWithin :
if isLoopWiddershins != euclidean . isWiddershins ( around ) :
around . reverse ( )
insetLoops . append ( around )
return insetLoops
def getInsetLoopsFromLoops ( loops , radius ) :
' Get the inset loops, which might overlap. '
insetLoops = [ ]
for loop in loops :
insetLoops + = getInsetLoopsFromLoop ( loop , radius )
return insetLoops
def getInsetLoopsFromVector3Loop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the inset loops from vector3 loop, which might overlap. '
if len ( loop ) < 2 :
return [ loop ]
loopComplex = euclidean . getComplexPath ( loop )
loopComplexes = getInsetLoopsFromLoop ( loopComplex , radius )
return euclidean . getVector3Paths ( loopComplexes , loop [ 0 ] . z )
def getInsetSeparateLoopsFromLoops ( loops , radius , thresholdRatio = 0.9 ) :
' Get the separate inset loops. '
if radius == 0.0 :
return loops
isInset = radius > 0
insetSeparateLoops = [ ]
arounds = getAroundsFromLoops ( loops , abs ( radius ) , thresholdRatio )
for around in arounds :
if isInset == euclidean . getIsInFilledRegion ( loops , around [ 0 ] ) :
if isInset :
around . reverse ( )
insetSeparateLoops . append ( around )
return insetSeparateLoops
def getInsetSeparateLoopsFromAroundLoops ( loops , radius , radiusAround , thresholdRatio = 0.9 ) :
' Get the separate inset loops. '
if radius == 0.0 :
return loops
isInset = radius > 0
insetSeparateLoops = [ ]
radius = abs ( radius )
radiusAround = max ( abs ( radiusAround ) , radius )
points = getPointsFromLoops ( loops , radiusAround , thresholdRatio )
centers = getCentersFromPoints ( points , globalIntercircleMultiplier * radiusAround )
for center in centers :
inset = getSimplifiedInsetFromClockwiseLoop ( center , radius )
if isLargeSameDirection ( inset , center , radius ) :
if isInset == euclidean . getIsInFilledRegion ( loops , inset [ 0 ] ) :
if isInset :
inset . reverse ( )
insetSeparateLoops . append ( inset )
return insetSeparateLoops
def getIsLarge ( loop , radius ) :
' Determine if the loop is large enough. '
return euclidean . getMaximumSpan ( loop ) > 2.01 * abs ( radius )
def getLargestCenterOutsetLoopFromLoop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the largest circle outset loop from the loop. '
if radius == 0.0 :
return loop
radius = abs ( radius )
points = getPointsFromLoop ( loop , radius , thresholdRatio )
centers = getCentersFromPoints ( points , globalIntercircleMultiplier * radius )
largestCenterOutset = None
largestOutsetArea = - 987654321.0
for center in centers :
outset = getSimplifiedInsetFromClockwiseLoop ( center , radius )
if isLargeSameDirection ( outset , center , radius ) :
if euclidean . isPathInsideLoop ( loop , outset ) != euclidean . isWiddershins ( loop ) :
centerOutset = CenterOutset ( center , outset )
outsetArea = abs ( euclidean . getAreaLoop ( outset ) )
if outsetArea > largestOutsetArea :
largestOutsetArea = outsetArea
largestCenterOutset = centerOutset
if largestCenterOutset == None :
return None
largestCenterOutset . center = euclidean . getSimplifiedLoop ( largestCenterOutset . center , radius )
return largestCenterOutset
def getLargestCenterOutsetLoopFromLoopRegardless ( loop , radius ) :
' Get the largest circle outset loop from the loop, even if the radius has to be shrunk and even if there is still no outset loop. '
global globalDecreasingRadiusMultipliers
for decreasingRadiusMultiplier in globalDecreasingRadiusMultipliers :
decreasingRadius = radius * decreasingRadiusMultiplier
largestCenterOutsetLoop = getLargestCenterOutsetLoopFromLoop ( loop , decreasingRadius )
if largestCenterOutsetLoop != None :
return largestCenterOutsetLoop
return CenterOutset ( loop , loop )
def getLargestInsetLoopFromLoop ( loop , radius ) :
' Get the largest inset loop from the loop. '
loops = getInsetLoopsFromLoop ( loop , radius )
return euclidean . getLargestLoop ( loops )
def getLargestInsetLoopFromLoopRegardless ( loop , radius ) :
' Get the largest inset loop from the loop, even if the radius has to be shrunk and even if there is still no inset loop. '
global globalDecreasingRadiusMultipliers
for decreasingRadiusMultiplier in globalDecreasingRadiusMultipliers :
decreasingRadius = radius * decreasingRadiusMultiplier
largestInsetLoop = getLargestInsetLoopFromLoop ( loop , decreasingRadius )
if len ( largestInsetLoop ) > 0 :
return largestInsetLoop
2012-03-22 12:43:07 +00:00
print ( ' Warning, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle. ' )
2012-02-19 23:30:49 +00:00
print ( loop )
return loop
def getLoopsFromLoopsDirection ( isWiddershins , loops ) :
' Get the loops going round in a given direction. '
directionalLoops = [ ]
for loop in loops :
if euclidean . isWiddershins ( loop ) == isWiddershins :
directionalLoops . append ( loop )
return directionalLoops
def getPointsFromLoop ( loop , radius , thresholdRatio = 0.9 ) :
' Get the points from every point on a loop and between points. '
if radius == 0.0 :
print ( ' Warning, radius is 0 in getPointsFromLoop in intercircle. ' )
print ( loop )
return loop
radius = abs ( radius )
points = [ ]
for pointIndex in xrange ( len ( loop ) ) :
pointBegin = loop [ pointIndex ]
pointEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
points . append ( pointBegin )
addPointsFromSegment ( pointBegin , pointEnd , points , radius , thresholdRatio )
return points
def getPointsFromLoops ( loops , radius , thresholdRatio = 0.9 ) :
' Get the points from every point on a loop and between points. '
points = [ ]
for loop in loops :
points + = getPointsFromLoop ( loop , radius , thresholdRatio )
return points
def getPointsFromPath ( path , radius , thresholdRatio = 0.9 ) :
' Get the points from every point on a path and between points. '
if len ( path ) < 1 :
return [ ]
if len ( path ) < 2 :
return path
radius = abs ( radius )
points = [ ]
addHalfPath ( path , points , radius , thresholdRatio )
addHalfPath ( path [ : : - 1 ] , points , radius , thresholdRatio )
return points
def getSimplifiedInsetFromClockwiseLoop ( loop , radius ) :
' Get loop inset from clockwise loop, out from widdershins loop. '
inset = [ ]
for pointIndex , begin in enumerate ( loop ) :
center = loop [ ( pointIndex + 1 ) % len ( loop ) ]
end = loop [ ( pointIndex + 2 ) % len ( loop ) ]
addInsetPointFromClockwiseTriple ( begin , center , end , inset , radius )
return getWithoutIntersections ( euclidean . getSimplifiedLoop ( inset , radius ) )
def getWiddershinsByLength ( begin , end , length ) :
' Get the widdershins by length. '
endMinusBegin = end - begin
endMinusBeginLength = abs ( endMinusBegin )
if endMinusBeginLength < = 0.0 :
return None
endMinusBegin * = length / endMinusBeginLength
return complex ( - endMinusBegin . imag , endMinusBegin . real )
def getWithoutIntersections ( loop ) :
' Get loop without intersections. '
lastLoopLength = len ( loop )
while lastLoopLength > 3 :
removeIntersection ( loop )
if len ( loop ) == lastLoopLength :
return loop
lastLoopLength = len ( loop )
return loop
def isLargeSameDirection ( inset , loop , radius ) :
' Determine if the inset is in the same direction as the loop and it is large enough. '
if euclidean . isWiddershins ( inset ) != euclidean . isWiddershins ( loop ) :
return False
return getIsLarge ( inset , radius ) and len ( inset ) > 2
def isLoopIntersectingLoop ( anotherLoop , loop ) :
' Determine if the a loop is intersecting another loop. '
for pointIndex in xrange ( len ( loop ) ) :
pointFirst = loop [ pointIndex ]
pointSecond = loop [ ( pointIndex + 1 ) % len ( loop ) ]
segment = pointFirst - pointSecond
normalizedSegment = euclidean . getNormalized ( segment )
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
segmentFirstPoint = segmentYMirror * pointFirst
segmentSecondPoint = segmentYMirror * pointSecond
if euclidean . isLoopIntersectingInsideXSegment ( anotherLoop , segmentFirstPoint . real , segmentSecondPoint . real , segmentYMirror , segmentFirstPoint . imag ) :
return True
return False
def orbitsAreLarge ( loop , temperatureChangeTime ) :
' Determine if the orbits are large enough. '
if len ( loop ) < 1 :
print ( ' Zero length loop which was skipped over, this should never happen. ' )
return False
return temperatureChangeTime > 1.5
def removeIntersection ( loop ) :
' Get loop without the first intersection. '
for pointIndex , ahead in enumerate ( loop ) :
behind = loop [ ( pointIndex + len ( loop ) - 1 ) % len ( loop ) ]
behindEnd = loop [ ( pointIndex + len ( loop ) - 2 ) % len ( loop ) ]
behindMidpoint = 0.5 * ( behind + behindEnd )
aheadEnd = loop [ ( pointIndex + 1 ) % len ( loop ) ]
aheadMidpoint = 0.5 * ( ahead + aheadEnd )
normalizedSegment = behind - behindMidpoint
normalizedSegmentLength = abs ( normalizedSegment )
if normalizedSegmentLength > 0.0 :
normalizedSegment / = normalizedSegmentLength
segmentYMirror = complex ( normalizedSegment . real , - normalizedSegment . imag )
behindRotated = segmentYMirror * behind
behindMidpointRotated = segmentYMirror * behindMidpoint
aheadRotated = segmentYMirror * ahead
aheadMidpointRotated = segmentYMirror * aheadMidpoint
y = behindRotated . imag
xIntersection = euclidean . getXIntersectionIfExists ( aheadRotated , aheadMidpointRotated , y )
if xIntersection != None :
if xIntersection > min ( behindMidpointRotated . real , behindRotated . real ) and xIntersection < max ( behindMidpointRotated . real , behindRotated . real ) :
intersectionPoint = normalizedSegment * complex ( xIntersection , y )
loop [ ( pointIndex + len ( loop ) - 1 ) % len ( loop ) ] = intersectionPoint
del loop [ pointIndex ]
return
class BoundingLoop :
' A class to hold a bounding loop composed of a minimum complex, a maximum complex and an outset loop. '
def __eq__ ( self , other ) :
' Determine whether this bounding loop is identical to other one. '
if other == None :
return False
return self . minimum == other . minimum and self . maximum == other . maximum and self . loop == other . loop
def __repr__ ( self ) :
' Get the string representation of this bounding loop. '
return ' %s , %s , %s ' % ( self . minimum , self . maximum , self . loop )
def getFromLoop ( self , loop ) :
' Get the bounding loop from a path. '
self . loop = loop
self . maximum = euclidean . getMaximumByComplexPath ( loop )
self . minimum = euclidean . getMinimumByComplexPath ( loop )
return self
def getOutsetBoundingLoop ( self , outsetDistance ) :
' Outset the bounding rectangle and loop by a distance. '
outsetBoundingLoop = BoundingLoop ( )
outsetBoundingLoop . maximum = self . maximum + complex ( outsetDistance , outsetDistance )
outsetBoundingLoop . minimum = self . minimum - complex ( outsetDistance , outsetDistance )
greaterThanOutsetDistance = 1.1 * outsetDistance
centers = getCentersFromLoopDirection ( True , self . loop , greaterThanOutsetDistance )
outsetBoundingLoop . loop = getSimplifiedInsetFromClockwiseLoop ( centers [ 0 ] , outsetDistance )
return outsetBoundingLoop
def isEntirelyInsideAnother ( self , anotherBoundingLoop ) :
' Determine if this bounding loop is entirely inside another bounding loop. '
if self . minimum . imag < anotherBoundingLoop . minimum . imag or self . minimum . real < anotherBoundingLoop . minimum . real :
return False
if self . maximum . imag > anotherBoundingLoop . maximum . imag or self . maximum . real > anotherBoundingLoop . maximum . real :
return False
for point in self . loop :
if euclidean . getNumberOfIntersectionsToLeft ( anotherBoundingLoop . loop , point ) % 2 == 0 :
return False
return not isLoopIntersectingLoop ( anotherBoundingLoop . loop , self . loop ) #later check for intersection on only acute angles
def isOverlappingAnother ( self , anotherBoundingLoop ) :
' Determine if this bounding loop is intersecting another bounding loop. '
if self . isRectangleMissingAnother ( anotherBoundingLoop ) :
return False
for point in self . loop :
if euclidean . getNumberOfIntersectionsToLeft ( anotherBoundingLoop . loop , point ) % 2 == 1 :
return True
for point in anotherBoundingLoop . loop :
if euclidean . getNumberOfIntersectionsToLeft ( self . loop , point ) % 2 == 1 :
return True
return isLoopIntersectingLoop ( anotherBoundingLoop . loop , self . loop ) #later check for intersection on only acute angles
def isOverlappingAnotherInList ( self , boundingLoops ) :
' Determine if this bounding loop is intersecting another bounding loop in a list. '
for boundingLoop in boundingLoops :
if self . isOverlappingAnother ( boundingLoop ) :
return True
return False
def isRectangleMissingAnother ( self , anotherBoundingLoop ) :
' Determine if the rectangle of this bounding loop is missing the rectangle of another bounding loop. '
if self . maximum . imag < anotherBoundingLoop . minimum . imag or self . maximum . real < anotherBoundingLoop . minimum . real :
return True
return self . minimum . imag > anotherBoundingLoop . maximum . imag or self . minimum . real > anotherBoundingLoop . maximum . real
class CenterOutset :
' A class to hold a center and an outset. '
def __init__ ( self , center , outset ) :
' Set the center and outset. '
self . center = center
self . outset = outset
def __repr__ ( self ) :
' Get the string representation of this CenterOutset. '
return ' %s \n %s ' % ( self . center , self . outset )
class CircleIntersection :
' An intersection of two complex circles. '
def __init__ ( self , circleNodeAhead , index , circleNodeBehind ) :
self . aheadMinusBehind = 0.5 * ( circleNodeAhead . dividedPoint - circleNodeBehind . dividedPoint )
self . circleNodeAhead = circleNodeAhead
self . circleNodeBehind = circleNodeBehind
self . index = index
self . steppedOn = False
demichordWidth = math . sqrt ( 1.0 - self . aheadMinusBehind . real * self . aheadMinusBehind . real - self . aheadMinusBehind . imag * self . aheadMinusBehind . imag )
rotatedClockwiseQuarter = complex ( self . aheadMinusBehind . imag , - self . aheadMinusBehind . real )
rotatedClockwiseQuarterLength = abs ( rotatedClockwiseQuarter )
if rotatedClockwiseQuarterLength == 0 :
print ( ' Warning, rotatedClockwiseQuarter in getDemichord in intercircle is 0 ' )
print ( circleNodeAhead . dividedPoint )
print ( circleNodeBehind . dividedPoint )
self . demichord = 0.0
else :
self . demichord = rotatedClockwiseQuarter * demichordWidth / rotatedClockwiseQuarterLength
self . positionRelativeToBehind = self . aheadMinusBehind + self . demichord
def __repr__ ( self ) :
' Get the string representation of this CircleIntersection. '
return ' %s , %s , %s , %s ' % ( self . index , self . getAbsolutePosition ( ) , self . circleNodeBehind , self . circleNodeAhead )
def addToList ( self , circleIntersectionPath ) :
' Add this to the circle intersection path, setting stepped on to be true. '
self . steppedOn = True
circleIntersectionPath . append ( self )
def getAbsolutePosition ( self ) :
' Get the absolute position. '
return self . positionRelativeToBehind + self . circleNodeBehind . dividedPoint
def getCircleIntersectionAhead ( self ) :
' Get the first circle intersection on the circle node ahead. '
circleIntersections = self . circleNodeAhead . circleIntersections
circleIntersectionAhead = None
largestDot = - 912345678.0
for circleIntersection in circleIntersections :
if not circleIntersection . steppedOn :
circleIntersectionRelativeToMidpoint = euclidean . getNormalized ( circleIntersection . positionRelativeToBehind + self . aheadMinusBehind )
dot = euclidean . getDotProduct ( self . demichord , circleIntersectionRelativeToMidpoint )
if dot > largestDot :
largestDot = dot
circleIntersectionAhead = circleIntersection
if circleIntersectionAhead == None :
print ( ' Warning, circleIntersectionAhead in getCircleIntersectionAhead in intercircle is None for: ' )
print ( self . circleNodeAhead . dividedPoint )
print ( ' circleIntersectionsAhead ' )
for circleIntersection in circleIntersections :
print ( circleIntersection . circleNodeAhead . dividedPoint )
print ( ' circleIntersectionsBehind ' )
for circleIntersection in self . circleNodeBehind . circleIntersections :
print ( circleIntersection . circleNodeAhead . dividedPoint )
print ( ' This may lead to a loop not being sliced. ' )
print ( ' If this is a problem, you may as well send a bug report, even though I probably can not fix this particular problem. ' )
return circleIntersectionAhead
def isWithinCircles ( self , pixelTable ) :
' Determine if this circle intersection is within the circle node circles. '
absolutePosition = self . getAbsolutePosition ( )
squareValues = euclidean . getSquareValuesFromPoint ( pixelTable , absolutePosition )
for squareValue in squareValues :
if abs ( squareValue . dividedPoint - absolutePosition ) < 1.0 :
if squareValue != self . circleNodeAhead and squareValue != self . circleNodeBehind :
return True
return False
class CircleNode :
' A complex node of complex circle intersections. '
def __init__ ( self , oneOverRadius , point ) :
self . actualPoint = point
self . circleIntersections = [ ]
self . dividedPoint = point * oneOverRadius
# self.index = index # when debugging bring back index
def __repr__ ( self ) :
' Get the string representation of this CircleNode. '
# return '%s, %s, %s' % (self.index, self.dividedPoint, len(self.circleIntersections)) # when debugging bring back index
return ' %s , %s ' % ( self . dividedPoint , len ( self . circleIntersections ) )
def getWithinNodes ( self , pixelTable ) :
' Get the nodes this circle node is within. '
withinNodes = [ ]
squareValues = euclidean . getSquareValuesFromPoint ( pixelTable , 0.5 * self . dividedPoint )
for squareValue in squareValues :
if abs ( self . dividedPoint - squareValue . dividedPoint ) < 2.0 :
withinNodes . append ( squareValue )
return withinNodes