def __init__(self, trimOutline, pathWidth, angleDegrees, shiftX=0, shiftY=0, design=None, designType=c.PARTIAL_ROW): LineGroup.__init__(self, None) self.shiftX = shiftX self.shiftY = shiftY self.designType = designType self.trimOutline = Outline(trimOutline) self.angleRad = (angleDegrees/360.0*2*np.pi) self.pathWidth = pathWidth lowerLeft = Point(self.trimOutline.minX, self.trimOutline.minY) upperRight = Point(self.trimOutline.maxX, self.trimOutline.maxY) self.trimDiagonal = (lowerLeft - upperRight)*1.1 self.operations = (self.extendDesign, self.createField, self.centerAndRotateField, self.trimField) try: self.design = design(space = self.pathWidth, length = self.trimDiagonal, height = self.trimDiagonal, ) self.designType = c.FULL_FIELD except Exception: self.design = LineGroup(design) # print('\nInfill times:') for i in range(self.designType, c.TRIMMED_FIELD): startTime = time.time() self.operations[i]();
def extendDesign(self): tempDesign = LineGroup(self.design.lines) designWidth = self.design.maxX - self.design.minX while(designWidth <= self.trimDiagonal): shiftX = self.design[-1].end.x - tempDesign[0].start.x shiftY = self.design[-1].end.y - tempDesign[0].start.y self.design.addLineGroup(tempDesign.translate(shiftX, shiftY)) designWidth = self.design.maxX - self.design.minX
def __init__(self, start, end, direction, center, numPoints=c.ARC_NUMPOINTS): LG.__init__(self, None) self.start = start self.end = end self.direction = direction self.center = center self.numPoints = numPoints self.arcToLines()
def extendDesign(self): tempDesign = LineGroup(self.design.lines) designWidth = self.design.maxX - self.design.minX while (designWidth <= self.trimDiagonal): shiftX = self.design[-1].end.x - tempDesign[0].start.x shiftY = self.design[-1].end.y - tempDesign[0].start.y self.design.addLineGroup(tempDesign.translate(shiftX, shiftY)) designWidth = self.design.maxX - self.design.minX
def __init__(self, trimOutline, pathWidth, angleDegrees, shiftX=0, shiftY=0, design=None, designType=c.PARTIAL_ROW): LineGroup.__init__(self, None) self.shiftX = shiftX self.shiftY = shiftY self.designType = designType self.trimOutline = Outline(trimOutline) self.angleRad = (angleDegrees / 360.0 * 2 * np.pi) self.pathWidth = pathWidth lowerLeft = Point(self.trimOutline.minX, self.trimOutline.minY) upperRight = Point(self.trimOutline.maxX, self.trimOutline.maxY) self.trimDiagonal = (lowerLeft - upperRight) * 1.1 self.operations = (self.extendDesign, self.createField, self.centerAndRotateField, self.trimField) try: self.design = design( space=self.pathWidth, length=self.trimDiagonal, height=self.trimDiagonal, ) self.designType = c.FULL_FIELD except Exception: self.design = LineGroup(design) # print('\nInfill times:') for i in range(self.designType, c.TRIMMED_FIELD): startTime = time.time() self.operations[i]()
def _straightLines(*, space: float = 0, length: float = 0, height: float = 0) -> LineGroup: lines = [] currHeight = 0 while currHeight < height: lines.append(Line(Point(0, currHeight), Point(length, currHeight))) currHeight += space group = LineGroup() group.lines = lines group.minX = 0 group.minY = 0 group.maxX = length group.maxY = currHeight - space return group
def _straightLines(*, space: float=0, length: float=0, height: float=0) -> LineGroup: lines = [] currHeight = 0 while currHeight < height: lines.append(Line(Point(0,currHeight), Point(length,currHeight))) currHeight += space group = LineGroup() group.lines = lines group.minX = 0 group.minY = 0 group.maxX = length group.maxY = currHeight-space return group
class Infill(LineGroup): def __init__(self, trimOutline, pathWidth, angleDegrees, shiftX=0, shiftY=0, design=None, designType=c.PARTIAL_ROW): LineGroup.__init__(self, None) self.shiftX = shiftX self.shiftY = shiftY self.designType = designType self.trimOutline = Outline(trimOutline) self.angleRad = (angleDegrees/360.0*2*np.pi) self.pathWidth = pathWidth lowerLeft = Point(self.trimOutline.minX, self.trimOutline.minY) upperRight = Point(self.trimOutline.maxX, self.trimOutline.maxY) self.trimDiagonal = (lowerLeft - upperRight)*1.1 self.operations = (self.extendDesign, self.createField, self.centerAndRotateField, self.trimField) try: self.design = design(space = self.pathWidth, length = self.trimDiagonal, height = self.trimDiagonal, ) self.designType = c.FULL_FIELD except Exception: self.design = LineGroup(design) # print('\nInfill times:') for i in range(self.designType, c.TRIMMED_FIELD): startTime = time.time() self.operations[i](); # print((self.operations[i].__name__ + # ''.ljust(maxLength - len(self.operations[i].__name__)) + # '%.2f sec' %(time.time()-startTime))) def extendDesign(self): tempDesign = LineGroup(self.design.lines) designWidth = self.design.maxX - self.design.minX while(designWidth <= self.trimDiagonal): shiftX = self.design[-1].end.x - tempDesign[0].start.x shiftY = self.design[-1].end.y - tempDesign[0].start.y self.design.addLineGroup(tempDesign.translate(shiftX, shiftY)) designWidth = self.design.maxX - self.design.minX def createField(self): tempDesign = self.design.translate(0, self.pathWidth) designHeight = 0 # abs(self.design.maxY - self.design.minY) while(designHeight < self.trimDiagonal): self.design.addLineGroup(tempDesign) tempDesign = tempDesign.translate(0, self.pathWidth) designHeight += self.pathWidth def centerAndRotateField(self): designCP = self.design.getMidPoint() trimOutlineCP = self.trimOutline.getMidPoint() transX = trimOutlineCP.x - designCP.x transY = trimOutlineCP.y - designCP.y self.design = self.design.transform(mt.combineTransformations( [mt.translateMatrix(transX+self.shiftX, transY+self.shiftY), mt.rotateMatrix(self.angleRad, trimOutlineCP)])) def trimField(self): trimStarts = self.trimOutline.starts trimVectors = self.trimOutline.vectors fieldStarts = self.design.starts fieldVectors = self.design.vectors trimLen = len(self.trimOutline) fieldLen = len(self.design) Q_Less_P = fieldStarts - trimStarts.reshape(trimLen,1,2) denom = np.cross(trimVectors, fieldVectors.reshape(fieldLen,1,2)) all_t = np.cross(Q_Less_P, trimVectors.reshape(trimLen,1,2)).transpose()/denom all_u = np.cross(Q_Less_P, fieldVectors).transpose()/denom for t, u, line in zip(all_t, all_u, self.design): if not self.trimOutline.lineOutsideBoundingBox(line): pointSet = set([line.start]) t = t[(0 <= u) & (u <= 1) & (0 <= t) & (t <= 1)] pointSet |= set(Point(line.start.x + line.vector[c.X]*value, line.start.y+line.vector[c.Y]*value) for value in t) pointSet.add(line.end) pointList = sorted(list(pointSet)) pointVectors = np.array([point.normalVector for point in pointList]) """ Calculation for midPoints from here: http://stackoverflow.com/questions/23855976/middle-point-of-each-pair-of-an-numpy-array """ midPoints = (pointVectors[1:] + pointVectors[:-1])/2.0 for i in range(len(midPoints)): if self.trimOutline.isInside(midPoints[i]): self.append(Line(pointList[i], pointList[i+1]))
def organizedLayer(inOutlines, randStart): """ Takes in a list of LineGroup objects and returns them as an organized layer. A dictonary was used to hold the coroutines from linegroup since we will want to delete keys, value pairs while iterating throught the dict. The coroutines yield in a boolean and a Point and then yield back out the line which is 'closest' to the point. 'Closest' being in quotes because we could use any number of parameters to decide which line is closest from Euclidean distance of end points to time since its neighbor was printed (for cooling purposes). The yielded in boolean is whether or not the previous line was used. Currently if the LineGroup with the closest line is a outline then the entire outline is printed before checking any other LineGroups again. Parameters ---------- inOutlines - a list of LineGroups that make up the layer Return ------ A single organized LineGroup """ layer = LineGroup() lineCoros = { i: inOutlines[i].nearestLine_Coro(i) for i in range(len(inOutlines)) } for coro in lineCoros.values(): next(coro) """ Find the lower left most point of the boudnding box which encloses the layer and use that as the starting point for the sort. """ minX = min(i.minX for i in inOutlines) minY = min(i.minY for i in inOutlines) if randStart: farthestLeftOutline = min(inOutlines, key=lambda x: x.minX) startPoint = random.choice(farthestLeftOutline.lines).start # maxX = min(i.maxX for i in inOutlines) # maxY = min(i.maxY for i in inOutlines) # # line = Line(Point(minX, minY), Point(maxX, maxY)) # # angle = 2*math.pi*randStart # startX = line.getMidPoint().x + line.length/2*math.cos(angle) # startY = line.getMidPoint().y + line.length/2*math.sin(angle) # # startPoint = Point(startX, startY) else: startPoint = Point(minX, minY) lastPoint = startPoint # The starting point for the sort indexOfClosest = -1 # A default value for the inital run while True: results = [] for key in list(lineCoros.keys()): try: results.append(lineCoros[key].send( (c.USED if key == indexOfClosest else c.NOT_USED, lastPoint))) except StopIteration: """ If we get a StopIteration exception from the coroutine that means the LineGroup has no more Lines and we can remove it from the dictionary. """ del lineCoros[key] if len(results) == 0: break # No more items in the dictionary result = min(results, key=lambda x: x.distance) line = result.line indexOfClosest = result.name lastPoint = line.end layer.append(line) if isinstance(inOutlines[indexOfClosest], Outline): """ If the closest line was from an Outline then go around the whole outline without checking any other LineGroup. Outlines are required to be continuous contours (except if the have internal holes) so there is no need to check any other LineGroup for a closer line. Plus if the outline was being used as a brim to help start a print we would not want to stop partially through the brim. """ while True: try: line = lineCoros[indexOfClosest].send( (c.USED, lastPoint)).line except StopIteration: del lineCoros[indexOfClosest] break else: """ A reminder than an else is run if there is no exception. """ lastPoint = line.end layer.append(line) return layer
def __init__(self, outline=None): LineGroup.__init__(self, outline) self.outlineFinished = False
def noInfill() -> LineGroup: return LineGroup()
def _hexagons(*, space: float = 0, length: float = 0, height: float = 0) -> LineGroup: baseLine = LineGroup(None) baseLine.addLinesFromCoordinateList( [[0, 0], [sideLength, 0], [ sideLength + math.cos(math.pi / 4) * sideLength, math.cos(math.pi / 4) * sideLength ], [ sideLength * 2 + math.cos(math.pi / 4) * sideLength, math.cos(math.pi / 4) * sideLength ], [2 * (sideLength + math.cos(math.pi / 4) * sideLength), 0]]) fullLine = LineGroup(baseLine) while fullLine.maxX - fullLine.minX < length: baseLine = baseLine.translate(baseLine.maxX - baseLine.minX, 0) fullLine.addLineGroup(baseLine) mirrorLine = LineGroup(fullLine) mirrorLine = mirrorLine.mirror(c.X) mirrorLine = mirrorLine.translate(0, -space) fullLine.addLineGroup(mirrorLine) field = LineGroup(fullLine) while field.maxY - field.minY < height: fullLine = fullLine.translate( 0, fullLine.maxY - fullLine.minY + space) field.addLineGroup(fullLine) return field
class Infill(LineGroup): def __init__(self, trimOutline, pathWidth, angleDegrees, shiftX=0, shiftY=0, design=None, designType=c.PARTIAL_ROW): LineGroup.__init__(self, None) self.shiftX = shiftX self.shiftY = shiftY self.designType = designType self.trimOutline = Outline(trimOutline) self.angleRad = (angleDegrees / 360.0 * 2 * np.pi) self.pathWidth = pathWidth lowerLeft = Point(self.trimOutline.minX, self.trimOutline.minY) upperRight = Point(self.trimOutline.maxX, self.trimOutline.maxY) self.trimDiagonal = (lowerLeft - upperRight) * 1.1 self.operations = (self.extendDesign, self.createField, self.centerAndRotateField, self.trimField) try: self.design = design( space=self.pathWidth, length=self.trimDiagonal, height=self.trimDiagonal, ) self.designType = c.FULL_FIELD except Exception: self.design = LineGroup(design) # print('\nInfill times:') for i in range(self.designType, c.TRIMMED_FIELD): startTime = time.time() self.operations[i]() # print((self.operations[i].__name__ + # ''.ljust(maxLength - len(self.operations[i].__name__)) + # '%.2f sec' %(time.time()-startTime))) def extendDesign(self): tempDesign = LineGroup(self.design.lines) designWidth = self.design.maxX - self.design.minX while (designWidth <= self.trimDiagonal): shiftX = self.design[-1].end.x - tempDesign[0].start.x shiftY = self.design[-1].end.y - tempDesign[0].start.y self.design.addLineGroup(tempDesign.translate(shiftX, shiftY)) designWidth = self.design.maxX - self.design.minX def createField(self): tempDesign = self.design.translate(0, self.pathWidth) designHeight = 0 # abs(self.design.maxY - self.design.minY) while (designHeight < self.trimDiagonal): self.design.addLineGroup(tempDesign) tempDesign = tempDesign.translate(0, self.pathWidth) designHeight += self.pathWidth def centerAndRotateField(self): designCP = self.design.getMidPoint() trimOutlineCP = self.trimOutline.getMidPoint() transX = trimOutlineCP.x - designCP.x transY = trimOutlineCP.y - designCP.y self.design = self.design.transform( mt.combineTransformations([ mt.translateMatrix(transX + self.shiftX, transY + self.shiftY), mt.rotateMatrix(self.angleRad, trimOutlineCP) ])) def trimField(self): trimStarts = self.trimOutline.starts trimVectors = self.trimOutline.vectors fieldStarts = self.design.starts fieldVectors = self.design.vectors trimLen = len(self.trimOutline) fieldLen = len(self.design) Q_Less_P = fieldStarts - trimStarts.reshape(trimLen, 1, 2) denom = np.cross(trimVectors, fieldVectors.reshape(fieldLen, 1, 2)) all_t = np.cross(Q_Less_P, trimVectors.reshape(trimLen, 1, 2)).transpose() / denom all_u = np.cross(Q_Less_P, fieldVectors).transpose() / denom for t, u, line in zip(all_t, all_u, self.design): if not self.trimOutline.lineOutsideBoundingBox(line): pointSet = set([line.start]) t = t[(0 <= u) & (u <= 1) & (0 <= t) & (t <= 1)] pointSet |= set( Point(line.start.x + line.vector[c.X] * value, line.start.y + line.vector[c.Y] * value) for value in t) pointSet.add(line.end) pointList = sorted(list(pointSet)) pointVectors = np.array( [point.normalVector for point in pointList]) """ Calculation for midPoints from here: http://stackoverflow.com/questions/23855976/middle-point-of-each-pair-of-an-numpy-array """ midPoints = (pointVectors[1:] + pointVectors[:-1]) / 2.0 for i in range(len(midPoints)): if self.trimOutline.isInside(midPoints[i]): self.append(Line(pointList[i], pointList[i + 1]))
def _hexagons(*, space: float=0, length: float=0, height: float=0) -> LineGroup: baseLine = LineGroup(None) baseLine.addLinesFromCoordinateList([[0,0], [sideLength, 0], [sideLength+math.cos(math.pi/4)*sideLength, math.cos(math.pi/4)*sideLength], [sideLength*2+math.cos(math.pi/4)*sideLength, math.cos(math.pi/4)*sideLength], [2*(sideLength+math.cos(math.pi/4)*sideLength), 0]]) fullLine = LineGroup(baseLine) while fullLine.maxX - fullLine.minX < length: baseLine = baseLine.translate(baseLine.maxX - baseLine.minX, 0) fullLine.addLineGroup(baseLine) mirrorLine = LineGroup(fullLine) mirrorLine = mirrorLine.mirror(c.X) mirrorLine = mirrorLine.translate(0, -space) fullLine.addLineGroup(mirrorLine) field = LineGroup(fullLine) while field.maxY - field.minY < height: fullLine = fullLine.translate(0, fullLine.maxY-fullLine.minY+space) field.addLineGroup(fullLine) return field