"""
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 skeinforge_tools.skeinforge_utilities.vector3 import Vector3
from skeinforge_tools.skeinforge_utilities import euclidean
import math
 
 
__author__ = "Enrique Perez (perez_enrique@yahoo.com)"
__date__ = "$Date: 2008/21/04 $"
__license__ = "GPL 3.0"
 
 
def addCircleIntersectionLoop( circleIntersectionPaths, circleIntersections ):
	"Add a circle intersection loop."
	firstCircleIntersection = circleIntersectionPaths[ 0 ]
	circleIntersectionAhead = firstCircleIntersection
	for circleIntersectionIndex in xrange( len( circleIntersections ) + 1 ):
		circleIntersectionAhead = circleIntersectionAhead.getCircleIntersectionAhead()
		if circleIntersectionAhead.index == firstCircleIntersection.index:
			firstCircleIntersection.steppedOn = True
			return
		if circleIntersectionAhead.steppedOn == True:
			print( 'circleIntersectionAhead.steppedOn == True in intercircle.' )
			print( circleIntersectionAhead )
		circleIntersectionAhead.addToList( circleIntersectionPaths )
	firstCircleIntersection.steppedOn = True
	print( "addCircleIntersectionLoop would have gone into an endless loop, this should never happen." )
	print( "circleIntersectionPaths" )
	for circleIntersection in circleIntersectionPaths:
		print( circleIntersection )
		print( circleIntersection.circleNodeAhead )
		print( circleIntersection.circleNodeBehind )
	print( "firstCircleIntersection" )
	print( firstCircleIntersection )
	print( "circleIntersections" )
	for circleIntersection in circleIntersections:
		print( circleIntersection )
 
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.getPolygonLength( 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( 'This should never happen, 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 getAroundsFromLoop( loop, radius, thresholdRatio = 0.9 ):
	"Get the arounds from the loop, later combine with get arounds."
	slightlyGreaterThanRadius = 1.01 * abs( radius )
	points = getPointsFromLoop( loop, slightlyGreaterThanRadius, thresholdRatio )
	return getAroundsFromPoints( points, radius )
 
def getAroundsFromLoops( loops, radius, thresholdRatio = 0.9 ):
	"Get the arounds from the loops."
	slightlyGreaterThanRadius = 1.01 * abs( radius )
	points = getPointsFromLoops( loops, slightlyGreaterThanRadius, thresholdRatio )
	return getAroundsFromPoints( points, radius )
 
def getAroundsFromPoints( points, radius ):
	"Get the arounds from the points."
	arounds = []
	radius = abs( radius )
	centers = getCentersFromPoints( points, 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.circle * radius )
	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 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.circle )
	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.circle )
		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 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 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."
	circleNodes = []
	oneOverRadius = 1.0 / radius
	points = euclidean.getAwayPoints( points, 0.001 * radius )
	for point in points:
		circleNodes.append( CircleNode( point * oneOverRadius, len( circleNodes ) ) )
	return circleNodes
 
def getInsetFromClockwiseTriple( aheadAbsolute, behindAbsolute, center, radius ):
	"Get loop inset from clockwise triple, out from widdershins loop."
	originalCenterMinusBehind = euclidean.getNormalized( center - behindAbsolute )
	reverseRoundZAngle = complex( originalCenterMinusBehind.real, - originalCenterMinusBehind.imag )
	rotatedAheadAbsolute = aheadAbsolute * reverseRoundZAngle
	rotatedBehindAbsolute = behindAbsolute * reverseRoundZAngle
	rotatedCenter = center * reverseRoundZAngle
	aheadIntersection = getIntersectionAtInset( rotatedAheadAbsolute, rotatedCenter, radius )
	behindIntersection = getIntersectionAtInset( rotatedCenter, rotatedBehindAbsolute, radius )
	centerMinusAhead = rotatedCenter - rotatedAheadAbsolute
	if abs( centerMinusAhead.imag ) < abs( 0.00001 * centerMinusAhead.real ):
		between = 0.5 * ( aheadIntersection + behindIntersection )
		return originalCenterMinusBehind * between
	yMinusAhead = behindIntersection.imag - aheadIntersection.imag
	x = aheadIntersection.real + yMinusAhead * centerMinusAhead.real / centerMinusAhead.imag
	insetFromClockwiseTriple = originalCenterMinusBehind * complex( x, behindIntersection.imag )
	insetMinusOriginal = insetFromClockwiseTriple - center
	distance = abs( insetMinusOriginal )
	maximumDistance = 2.0 * radius
	if distance < maximumDistance:
		return insetFromClockwiseTriple
	return center + maximumDistance / distance * insetMinusOriginal
 
def getInsetFromClockwiseLoop( loop, radius ):
	"Get loop inset from clockwise loop, out from widdershins loop."
	insetLoop = []
	for pointIndex in xrange( len( loop ) ):
		behindAbsolute = loop[ ( pointIndex + len( loop ) - 1 ) % len( loop ) ]
		center = loop[ pointIndex ]
		aheadAbsolute = loop[ ( pointIndex + 1 ) % len( loop ) ]
		insetLoop.append( getInsetFromClockwiseTriple( aheadAbsolute, behindAbsolute, center, radius ) )
	return insetLoop
 
def getInsetLoopsFromLoop( inset, loop, thresholdRatio = 0.9 ):
	"Get the inset loops, which might overlap."
	isInset = inset > 0
	insetLoops = []
	isLoopWiddershins = euclidean.isWiddershins( loop )
	arounds = getAroundsFromLoop( loop, inset, 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( inset, loops ):
	"Get the inset loops, which might overlap."
	insetLoops = []
	for loop in loops:
		insetLoops += getInsetLoopsFromLoop( inset, loop )
	return insetLoops
 
def getInsetSeparateLoopsFromLoops( inset, loops, thresholdRatio = 0.9 ):
	"Get the separate inset loops."
	isInset = inset > 0
	insetSeparateLoops = []
	radius = abs( inset )
	arounds = getAroundsFromLoops( loops, radius, thresholdRatio )
	for around in arounds:
		leftPoint = euclidean.getLeftPoint( around )
		if isInset == euclidean.isInFilledRegion( loops, leftPoint ):
			if isInset:
				around.reverse()
			insetSeparateLoops.append( around )
	return insetSeparateLoops
 
def getIntersectionAtInset( ahead, behind, inset ):
	"Get circle intersection loop at inset from segment."
	aheadMinusBehind = 0.5 * ( ahead - behind )
	rotatedClockwiseQuarter = complex( aheadMinusBehind.imag, - aheadMinusBehind.real )
	rotatedClockwiseQuarter *= inset / abs( rotatedClockwiseQuarter )
	return aheadMinusBehind + behind + rotatedClockwiseQuarter
 
def getLargestInsetLoopFromLoop( loop, radius ):
	"Get the largest inset loop from the loop."
	loops = getInsetLoopsFromLoop( radius, loop )
	return euclidean.getLargestLoop( loops )
 
def getLargestInsetLoopFromLoopNoMatterWhat( 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."
	largestInsetLoop = getLargestInsetLoopFromLoop( loop, radius )
	if largestInsetLoop != None:
		return largestInsetLoop
	largestInsetLoop = getLargestInsetLoopFromLoop( loop, 0.55 * radius )
	if largestInsetLoop != None:
		return largestInsetLoop
	largestInsetLoop = getLargestInsetLoopFromLoop( loop, 0.35 * radius )
	if largestInsetLoop != None:
		return largestInsetLoop
	largestInsetLoop = getLargestInsetLoopFromLoop( loop, 0.2 * radius )
	if largestInsetLoop != None:
		return largestInsetLoop
	print( 'This should never happen, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopNoMatterWhat in intercircle.' )
	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."
	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 getSimplifiedInsetFromClockwiseLoop( loop, radius ):
	"Get loop inset from clockwise loop, out from widdershins loop."
	return getWithoutIntersections( euclidean.getSimplifiedLoop( getInsetFromClockwiseLoop( loop, radius ), radius ) )
 
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 euclidean.getMaximumSpan( inset ) > 2.01 * abs( radius )
 
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."
	withoutIntersection = []
	for pointIndex in xrange( len( loop ) ):
		behind = loop[ ( pointIndex + len( loop ) - 1 ) % len( loop ) ]
		behindEnd = loop[ ( pointIndex + len( loop ) - 2 ) % len( loop ) ]
		behindMidpoint = 0.5 * ( behind + behindEnd )
		ahead = loop[ pointIndex ]
		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
			isYAboveFirst = y > aheadRotated.imag
			isYAboveSecond = y > aheadMidpointRotated.imag
			if isYAboveFirst != isYAboveSecond:
				xIntersection = euclidean.getXIntersection( aheadRotated, aheadMidpointRotated, y )
				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.getMaximumFromPoints( loop )
		self.minimum = euclidean.getMinimumFromPoints( 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 CircleIntersection:
	"An intersection of two complex circles."
	def __init__( self, circleNodeAhead, index, circleNodeBehind ):
		self.aheadMinusBehind = 0.5 * ( circleNodeAhead.circle - circleNodeBehind.circle )
		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( 'this should never happen, rotatedClockwiseQuarter in getDemichord in intercircle is 0' )
			print( circleNodeAhead.circle )
			print( circleNodeBehind.circle )
			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, %s' % ( self.index, self.getAbsolutePosition(), self.circleNodeBehind.index, self.circleNodeAhead.index, self.getCircleIntersectionAhead().index )
 
	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.circle
 
	def getCircleIntersectionAhead( self ):
		"Get the first circle intersection on the circle node ahead."
		circleIntersections = self.circleNodeAhead.circleIntersections
		circleIntersectionAhead = None
		largestDot = - 999999999.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( 'this should never happen, circleIntersectionAhead in intercircle is None' )
			print( self.circleNodeAhead.circle )
			for circleIntersection in circleIntersections:
				print( circleIntersection.circleNodeAhead.circle )
		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.circle - 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, circle, index ):
		self.circle = circle
		self.circleIntersections = []
		self.index = index
 
	def __repr__( self ):
		"Get the string representation of this CircleNode."
		return '%s, %s' % ( self.index, self.circle )
 
	def getWithinNodes( self, pixelTable ):
		"Get the nodes this circle node is within."
		withinNodes = []
		squareValues = euclidean.getSquareValuesFromPoint( pixelTable, 0.5 * self.circle )
		for squareValue in squareValues:
			if abs( self.circle - squareValue.circle ) < 2.0:
				withinNodes.append( squareValue )
		return withinNodes