def getInsetPoint( loop, tinyRadius ): "Get the inset vertex." pointIndex = getWideAnglePointIndex( loop ) point = loop[ pointIndex % len( loop ) ] afterPoint = loop[ ( pointIndex + 1 ) % len( loop ) ] beforePoint = loop[ ( pointIndex - 1 ) % len( loop ) ] afterSegmentNormalized = euclidean.getNormalized( afterPoint - point ) beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point ) afterClockwise = complex( afterSegmentNormalized.imag, - afterSegmentNormalized.real ) beforeWiddershins = complex( - beforeSegmentNormalized.imag, beforeSegmentNormalized.real ) midpoint = afterClockwise + beforeWiddershins midpointNormalized = midpoint / abs( midpoint ) return point + midpointNormalized * tinyRadius
def insertPathsBetween( self, aroundBetweenPath, nextBeginning, pathEnd ): "Insert paths between the perimeter and the fill." betweenX = [] switchX = [] segment = euclidean.getNormalized( nextBeginning - pathEnd ) segmentYMirror = complex( segment.real, - segment.imag ) pathEndRotated = segmentYMirror * pathEnd nextBeginningRotated = segmentYMirror * nextBeginning y = pathEndRotated.imag for betweenIndex in xrange( len( self.getBetweens() ) ): between = self.getBetweens()[ betweenIndex ] betweenRotated = euclidean.getPointsRoundZAxis( segmentYMirror, between ) euclidean.addXIntersectionIndexes( betweenRotated, betweenIndex, switchX, y ) switchX.sort() maximumX = max( pathEndRotated.real, nextBeginningRotated.real ) minimumX = min( pathEndRotated.real, nextBeginningRotated.real ) for xIntersection in switchX: if xIntersection.x > minimumX and xIntersection.x < maximumX: betweenX.append( xIntersection ) betweenXIndex = self.getStartIndex( betweenX ) while betweenXIndex < len( betweenX ) - 1: betweenXFirst = betweenX[ betweenXIndex ] betweenXSecond = betweenX[ betweenXIndex + 1 ] loopFirst = self.getBetweens()[ betweenXFirst.index ] betweenFirst = segment * complex( betweenXFirst.x, y ) betweenSecond = segment * complex( betweenXSecond.x, y ) isLeavingPerimeter = False if betweenXSecond.index != betweenXFirst.index: isLeavingPerimeter = True self.addPathBetween( aroundBetweenPath, betweenFirst, betweenSecond, isLeavingPerimeter, loopFirst ) betweenXIndex += 2
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 insertPathsBetween(self, aroundBetweenPath, nextBeginning, pathEnd): "Insert paths between the perimeter and the fill." betweenX = [] switchX = [] segment = euclidean.getNormalized(nextBeginning - pathEnd) segmentYMirror = complex(segment.real, -segment.imag) pathEndRotated = segmentYMirror * pathEnd nextBeginningRotated = segmentYMirror * nextBeginning y = pathEndRotated.imag for betweenIndex in xrange(len(self.getBetweens())): between = self.getBetweens()[betweenIndex] betweenRotated = euclidean.getPointsRoundZAxis( segmentYMirror, between) euclidean.addXIntersectionIndexes(betweenRotated, betweenIndex, switchX, y) switchX.sort() maximumX = max(pathEndRotated.real, nextBeginningRotated.real) minimumX = min(pathEndRotated.real, nextBeginningRotated.real) for xIntersection in switchX: if xIntersection.x > minimumX and xIntersection.x < maximumX: betweenX.append(xIntersection) betweenXIndex = self.getStartIndex(betweenX) while betweenXIndex < len(betweenX) - 1: betweenXFirst = betweenX[betweenXIndex] betweenXSecond = betweenX[betweenXIndex + 1] loopFirst = self.getBetweens()[betweenXFirst.index] betweenFirst = segment * complex(betweenXFirst.x, y) betweenSecond = segment * complex(betweenXSecond.x, y) isLeavingPerimeter = False if betweenXSecond.index != betweenXFirst.index: isLeavingPerimeter = True self.addPathBetween(aroundBetweenPath, betweenFirst, betweenSecond, isLeavingPerimeter, loopFirst) betweenXIndex += 2
def getWideAnglePointIndex( loop ): "Get a point index which has a wide enough angle, most point indexes have a wide enough angle, this is just to make sure." dotProductMinimum = 9999999.9 widestPointIndex = 0 for pointIndex in xrange( len( loop ) ): point = loop[ pointIndex % len( loop ) ] afterPoint = loop[ ( pointIndex + 1 ) % len( loop ) ] beforePoint = loop[ ( pointIndex - 1 ) % len( loop ) ] afterSegmentNormalized = euclidean.getNormalized( afterPoint - point ) beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point ) dotProduct = euclidean.getDotProduct( afterSegmentNormalized, beforeSegmentNormalized ) if dotProduct < .99: return pointIndex if dotProduct < dotProductMinimum: dotProductMinimum = dotProduct widestPointIndex = pointIndex return widestPointIndex
def getPathsBetween(self, begin, end): "Insert paths between the perimeter and the fill." aroundBetweenPath = [] points = [begin] lineX = [] switchX = [] segment = euclidean.getNormalized(end - begin) segmentYMirror = complex(segment.real, -segment.imag) beginRotated = segmentYMirror * begin endRotated = segmentYMirror * end y = beginRotated.imag boundaries = self.getBoundaries() for boundaryIndex in xrange(len(boundaries)): boundary = boundaries[boundaryIndex] boundaryRotated = euclidean.getPointsRoundZAxis( segmentYMirror, boundary) euclidean.addXIntersectionIndexesFromLoopY(boundaryRotated, boundaryIndex, switchX, y) switchX.sort() maximumX = max(beginRotated.real, endRotated.real) minimumX = min(beginRotated.real, endRotated.real) for xIntersection in switchX: if xIntersection.x > minimumX and xIntersection.x < maximumX: point = segment * complex(xIntersection.x, y) points.append(point) lineX.append(xIntersection) points.append(end) lineXIndex = 0 pathBetweenAdded = False while lineXIndex < len(lineX) - 1: lineXFirst = lineX[lineXIndex] lineXSecond = lineX[lineXIndex + 1] loopFirst = boundaries[lineXFirst.index] isLeavingPerimeter = False if lineXSecond.index != lineXFirst.index: isLeavingPerimeter = True pathBetween = self.getPathBetween(points[lineXIndex + 1], points[lineXIndex + 2], isLeavingPerimeter, loopFirst) if isLeavingPerimeter: if not pathBetweenAdded: self.addRunningJumpPath(points[lineXIndex + 3], boundaries[lineXSecond.index], pathBetween) pathBetweenAdded = True else: pathBetween = self.getSimplifiedAroundPath( points[lineXIndex], points[lineXIndex + 3], loopFirst, pathBetween) pathBetweenAdded = True aroundBetweenPath += pathBetween lineXIndex += 2 return aroundBetweenPath
def getCircleIntersectionAhead( self ): circleIntersections = self.circleNodeAhead.circleIntersections circleIntersectionAhead = None smallestWiddershinsDot = 999999999.0 positionRelativeToAhead = self.getAbsolutePosition() - self.circleNodeAhead.circle positionRelativeToAhead = euclidean.getNormalized( positionRelativeToAhead ) for circleIntersection in circleIntersections: if not circleIntersection.steppedOn: circleIntersectionRelative = circleIntersection.getPositionRelativeToBehind() circleIntersectionRelative = euclidean.getNormalized( circleIntersectionRelative ) widdershinsDot = euclidean.getWiddershinsDotGiven( positionRelativeToAhead, circleIntersectionRelative ) if widdershinsDot < smallestWiddershinsDot: smallestWiddershinsDot = widdershinsDot 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 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 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 getInsetFromClockwiseTriple( aheadAbsoluteComplex, behindAbsoluteComplex, centerComplex, radius ): "Get loop inset from clockwise triple, out from widdershins loop." originalCenterMinusBehindComplex = euclidean.getNormalized( centerComplex - behindAbsoluteComplex ) reverseRoundZAngle = complex( originalCenterMinusBehindComplex.real, - originalCenterMinusBehindComplex.imag ) aheadAbsoluteComplex *= reverseRoundZAngle behindAbsoluteComplex *= reverseRoundZAngle centerComplex *= reverseRoundZAngle aheadIntersectionComplex = getIntersectionAtInset( aheadAbsoluteComplex, centerComplex, radius ) behindIntersectionComplex = getIntersectionAtInset( centerComplex, behindAbsoluteComplex, radius ) centerComplexMinusAhead = centerComplex - aheadAbsoluteComplex if abs( centerComplexMinusAhead.imag ) < abs( 0.000001 * centerComplexMinusAhead.real ): between = 0.5 * ( aheadIntersectionComplex + behindIntersectionComplex ) return originalCenterMinusBehindComplex * between yMinusAhead = behindIntersectionComplex.imag - aheadIntersectionComplex.imag x = aheadIntersectionComplex.real + yMinusAhead * centerComplexMinusAhead.real / centerComplexMinusAhead.imag return originalCenterMinusBehindComplex * complex( x, behindIntersectionComplex.imag )
def getInsetFromClockwiseTriple( aheadAbsoluteComplex, behindAbsoluteComplex, centerComplex, radius ): "Get loop inset from clockwise triple, out from widdershins loop." originalCenterMinusBehindComplex = euclidean.getNormalized( centerComplex - behindAbsoluteComplex ) reverseRoundZAngle = complex( originalCenterMinusBehindComplex.real, - originalCenterMinusBehindComplex.imag ) aheadAbsoluteComplex *= reverseRoundZAngle behindAbsoluteComplex *= reverseRoundZAngle centerComplex *= reverseRoundZAngle aheadIntersectionComplex = getIntersectionAtInset( aheadAbsoluteComplex, centerComplex, radius ) behindIntersectionComplex = getIntersectionAtInset( centerComplex, behindAbsoluteComplex, radius ) centerComplexMinusAhead = centerComplex - aheadAbsoluteComplex if abs( centerComplexMinusAhead.imag ) < abs( 0.000001 * centerComplexMinusAhead.real ): between = 0.5 * ( aheadIntersectionComplex + behindIntersectionComplex ) return originalCenterMinusBehindComplex * between yMinusAhead = behindIntersectionComplex.imag - aheadIntersectionComplex.imag x = aheadIntersectionComplex.real + yMinusAhead * centerComplexMinusAhead.real / centerComplexMinusAhead.imag return originalCenterMinusBehindComplex * complex( x, behindIntersectionComplex.imag )
def getPathsBetween(self, begin, end): "Insert paths between the perimeter and the fill." aroundBetweenPath = [] points = [begin] lineX = [] switchX = [] segment = euclidean.getNormalized(end - begin) segmentYMirror = complex(segment.real, -segment.imag) beginRotated = segmentYMirror * begin endRotated = segmentYMirror * end y = beginRotated.imag boundaries = self.getBoundaries() for boundaryIndex in xrange(len(boundaries)): boundary = boundaries[boundaryIndex] boundaryRotated = euclidean.getPointsRoundZAxis(segmentYMirror, boundary) euclidean.addXIntersectionIndexesFromLoopY(boundaryRotated, boundaryIndex, switchX, y) switchX.sort() maximumX = max(beginRotated.real, endRotated.real) minimumX = min(beginRotated.real, endRotated.real) for xIntersection in switchX: if xIntersection.x > minimumX and xIntersection.x < maximumX: point = segment * complex(xIntersection.x, y) points.append(point) lineX.append(xIntersection) points.append(end) lineXIndex = 0 while lineXIndex < len(lineX) - 1: lineXFirst = lineX[lineXIndex] lineXSecond = lineX[lineXIndex + 1] loopFirst = boundaries[lineXFirst.index] isLeavingPerimeter = False if lineXSecond.index != lineXFirst.index: isLeavingPerimeter = True pathBetween = self.getPathBetween( points[lineXIndex + 1], points[lineXIndex + 2], isLeavingPerimeter, loopFirst ) if isLeavingPerimeter: pathBetween = self.getRunningJumpPath( points[lineXIndex + 3], boundaries[lineXSecond.index], pathBetween ) else: pathBetween = self.getSimplifiedAroundPath( points[lineXIndex], points[lineXIndex + 3], loopFirst, pathBetween ) aroundBetweenPath += pathBetween lineXIndex += 2 return aroundBetweenPath
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 getIsAsFarAndNotIntersecting(self, begin, center, end, loop): "Determine if the point on the line is at least as far from the loop as the center point." if begin == end: print( "this should never happen but it does not really matter, begin == end in getIsAsFarAndNotIntersecting in comb." ) print(begin) return True centerMinusEnd = center - end centerMinusEndLength = abs(centerMinusEnd) if centerMinusEndLength <= 0.0: return True segment = euclidean.getNormalized(begin - end) segmentCenterMinusEnd = segment * centerMinusEndLength + end nearestCenterDistanceIndex = euclidean.getNearestDistanceIndex(center, loop) nearestCenterMinusEndDistanceIndex = euclidean.getNearestDistanceIndex(segmentCenterMinusEnd, loop) if nearestCenterMinusEndDistanceIndex.distance < nearestCenterDistanceIndex.distance: return False return not euclidean.isLineIntersectingLoops(self.getBetweens(), begin, end)
def getOverhangDirection( belowOutsetLoops, segmentBegin, segmentEnd ): "Add to span direction from the endpoint segments which overhang the layer below." segment = segmentEnd - segmentBegin normalizedSegment = euclidean.getNormalized( complex( segment.real, segment.imag ) ) segmentYMirror = complex( normalizedSegment.real, - normalizedSegment.imag ) segmentBegin = segmentYMirror * segmentBegin segmentEnd = segmentYMirror * segmentEnd solidXIntersectionList = [] y = segmentBegin.imag solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentBegin.real ) ) solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentEnd.real ) ) for belowLoopIndex in xrange( len( belowOutsetLoops ) ): belowLoop = belowOutsetLoops[ belowLoopIndex ] rotatedOutset = euclidean.getPointsRoundZAxis( segmentYMirror, belowLoop ) euclidean.addXIntersectionIndexes( rotatedOutset, belowLoopIndex, solidXIntersectionList, y ) overhangingSegments = euclidean.getSegmentsFromXIntersectionIndexes( solidXIntersectionList, y ) overhangDirection = complex() for overhangingSegment in overhangingSegments: overhangDirection += getDoubledRoundZ( overhangingSegment, normalizedSegment ) return overhangDirection
def getTruncatedJumpSpace(self, begin, segment): "Get the jump space truncated to an intersection." truncatedJumpSpace = begin + segment * self.runningJumpSpace switchX = [] segment = euclidean.getNormalized(truncatedJumpSpace - begin) segmentYMirror = complex(segment.real, -segment.imag) beginRotated = segmentYMirror * begin truncatedJumpSpaceRotated = segmentYMirror * truncatedJumpSpace y = beginRotated.imag betweens = self.getBetweens() for betweenIndex in xrange(len(betweens)): between = betweens[betweenIndex] betweenRotated = euclidean.getPointsRoundZAxis(segmentYMirror, between) euclidean.addXIntersectionIndexesFromLoopY(betweenRotated, betweenIndex, switchX, y) switchX.sort() maximumX = max(beginRotated.real, truncatedJumpSpaceRotated.real) minimumX = min(beginRotated.real, truncatedJumpSpaceRotated.real) for xIntersection in switchX: if xIntersection.x > minimumX and xIntersection.x < maximumX: return segment * complex(xIntersection.x, y) return truncatedJumpSpace
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 getCircleIntersectionAhead( self ): "Get the first circle intersection on the circle node ahead." aheadMinusBehind = 0.5 * ( self.circleNodeAhead.circle - self.circleNodeBehind.circle ) circleIntersections = self.circleNodeAhead.circleIntersections circleIntersectionAhead = None circleNodesMidpoint = 0.5 * ( self.circleNodeAhead.circle + self.circleNodeBehind.circle ) largestDot = - 999999999.0 positionRelativeToMidpoint = self.getDemichord( aheadMinusBehind ) circleNodeAheadCircleMinusMidpoint = self.circleNodeAhead.circle - circleNodesMidpoint for circleIntersection in circleIntersections: if not circleIntersection.steppedOn: circleIntersectionRelativeToMidpoint = circleIntersection.getPositionRelativeToBehind() + circleNodeAheadCircleMinusMidpoint circleIntersectionRelativeToMidpoint = euclidean.getNormalized( circleIntersectionRelativeToMidpoint ) dot = euclidean.getDotProduct( positionRelativeToMidpoint, 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 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