def ellipse(x, y, a, b, resolution, fillDensity=False): """Returns polygon for a filled ellipse with x, y, a, b, resolution (lines per mm) and fill density (lines per mm) as a Polygon object""" poly = toolpath.Polygon() if not resolution: resolution = self.circleResolution if fillDensity: if a > b: largerDimension = a else: largerDimension = b numFills = int(float(fillDensity) * float(largerDimension)) else: numFills = 1 startAngle, endAngle = 0, 360 # Not really cicumference but will do? circumference = float(2) * math.pi * float(float(a + b) / 2) angleDiv = (float(360) / float(circumference * resolution) ) # + ( 360 % int( circumference * resolution ) ) lastX, lastY = _calcEllipse(startAngle, a, b) # Compensate for arc going beyond 360 deg if startAngle > endAngle: endAngle += 360 for d in range(1, numFills + 1): ra = (float(d) / float(numFills)) * float(a) rb = (float(d) / float(numFills)) * float(b) for theta in _frange(startAngle, endAngle + angleDiv, angleDiv): newX, newY = _calcEllipse(theta, ra, rb) aLine = poly.addPoint(toolpath.Point(newX + x, newY + y)) if debug: print "aLine", aLine return poly
def line(line): """Returns polygon for line (x1, y2, x2, y2) as a Polygon object""" poly = toolpath.Polygon() x1, y1, x2, y2 = line poly.addPoint(toolpath.Point(x1, y1)) poly.addPoint(toolpath.Point(x2, y2)) return poly
def circleStroke(x1, y1, x2, y2, radius, resolution, fillDensity=False): """Returns polygon for a photoplotter moving stroke using a circular aperture with x, y, radius, resolution (lines per mm) and fill density (lines per mm) as a Polygon object""" poly = toolpath.Polygon() deltaY = y2 - y1 deltaX = x2 - x1 if x1 == x2 and y1 == y2: #print "this is not a move, this is why software like eagle that uses drm on your own files....is crap" poly.addPolygon( circle(x1, y1, radius, resolution=resolution, fillDensity=fillDensity)) else: if debug: print "PMWC, fill density", fillDensity #Plot central line poly.addPoint(toolpath.Point(x1, y1)) poly.addPoint(toolpath.Point(x2, y2)) # For each locus numFills = int(float(fillDensity) * float(radius)) for d in range(1, numFills + 1): r = (float(d) / float(numFills)) * float(radius) if debug: print "using r", r, "mm" theta = _angleFromDeltas(deltaX, deltaY) rsintheta = r * math.sin(theta) rcostheta = r * math.cos(theta) # Makes sure angle is in correct quadrant if deltaX > 0: startOffset = math.radians(90) endOffset = math.radians(-90) else: startOffset = math.radians(-90) endOffset = math.radians(90) if deltaY < 0: startOffset = -startOffset endOffset = -endOffset # Plot side lines and end arcs (simi-circles) of locus # reversing these point lets locus be drawn in one continual motion poly.addPoint(toolpath.Point(x2 - rsintheta, y2 + rcostheta)) poly.addPoint(toolpath.Point(x1 - rsintheta, y1 + rcostheta)) poly.addPolygon( arc(x1, y1, r, theta + startOffset, theta + endOffset, resolution, fillDensity=False)) poly.addPoint(toolpath.Point(x1 + rsintheta, y1 - rcostheta)) poly.addPoint(toolpath.Point(x2 + rsintheta, y2 - rcostheta)) poly.addPolygon( arc(x2, y2, r, theta - startOffset, theta - endOffset, resolution, fillDensity=False)) return poly
def raster(fileName, originalWidth, originalHeight, svg=False): """Returns polygon for a vectorised raster as a Polygon object. Uses external potrace program to convert raster file into polygon(s) """ poly = toolpath.Polygon() if svg: os.system("potrace --svg --output " + fileName[:-3] + "svg " + fileName) os.system( "potrace --alphamax 0 --turdsize 5 --backend gimppath --output " + fileName[:-3] + "gimppath " + fileName) os.system("rm " + fileName) f = open(fileName[:-3] + "gimppath") pathLines = f.readlines() f.close() os.system("rm " + fileName[:-3] + "gimppath") scale = 0.005 # temp - competely arbitary # 1 / 200, i.e 1 / (resolution = 20 * 100 for some reason) for l in pathLines: parts = l.split(' ') isPoint = False for i, p in enumerate(parts): if p == 'TYPE:': ptype = int(parts[i + 1]) isPoint = True elif p == 'X:': x = float(parts[i + 1]) * scale elif p == 'Y:': y = float(parts[i + 1]) * scale if isPoint: poly.addPoint(toolpath.Point(x, y)) #print "NEW POINT", x, y, ptype # This should not be assumed? poly.closed = True """ #this needs to be done on all paths at same time maxX, maxY = 0, 0 for p in points: x, y, t = p maxX = max(maxX, x) maxY = max(maxY, y) print "max", maxX, maxY #print "read", len(points), "points" scaleX = originalWidth / maxX scaleY = originalHeight / maxY print "scales", scaleX, scaleY for i in range(len(points)): x, y, y = points[i] x = x * scaleX y = y * scaleY points[i] = x, y, t """ #should make this return a list of all found polygons return poly
def circle(x, y, radius, resolution, fillDensity=False): """Returns polygon for a filled circle with x, y, radius, resolution (lines per mm) and fill density (lines per mm) as a Polygon object""" poly = toolpath.Polygon() if fillDensity: numFills = int(float(fillDensity) * float(radius)) else: numFills = 1 for d in range(1, numFills + 1): r = (float(d) / float(numFills)) * float(radius) if debug: print "using r", r, "mm" poly.addPolygon( arc(x, y, r, math.radians(0), math.radians(360), resolution)) return poly
def arc(x, y, radius, startAngle, endAngle, resolution): """Returns polygon an arc with x, y, radius, start angle (radians), end engle (radians) and resolution (lines per mm) a Polygon object""" # This function works in degrees but takes parameters in radians poly = toolpath.Polygon() if debug: print "Plotting arc at", x, y, "from", startAngle, "(", math.degrees( startAngle), ") to", endAngle, "(", math.degrees(endAngle), ")" startAngle, endAngle = math.degrees(startAngle), math.degrees(endAngle) circumference = float(2) * math.pi * float(radius) angleDiv = (float(360) / float(circumference * resolution) ) # + ( 360 % int( circumference * resolution ) ) lastX, lastY = _calcCircle(startAngle, radius) # compensate for arc going beyond 360 deg if startAngle > endAngle: endAngle += 360 # make detail proportional to radius to always give good resolution for theta in _frange(startAngle, endAngle + angleDiv, angleDiv): cx, cy = _calcCircle(theta, radius) poly.addPoint(toolpath.Point(x + cx, y + cy)) return poly
def rectangle(x, y, width, height, fillDensity=False): """Returns polygon for line x, y, width, height and fill density (lines per mm) as a Polygon object""" poly = toolpath.Polygon() numFillsY = int(float(fillDensity) * float(height)) cornerX, cornerY = x - (width / 2), y - (height / 2) invert = False for dy in range(0, numFillsY + 1): ry = (float(dy) / float(numFillsY)) * float(height) line1 = (cornerX, cornerY + ry, cornerX + width, cornerY + ry) line2 = (cornerX, cornerY + ry, cornerX + width, cornerY - ry) if invert: line1 = _reverseLine(line1) invert = not invert x1, y1, x2, y2 = line1 poly.addPoint(toolpath.Point(x1, y1)) poly.addPoint(toolpath.Point(x2, y2)) poly.addPoint(toolpath.Point(cornerX, cornerY)) poly.addPoint(toolpath.Point(cornerX, cornerY + height)) poly.addPoint(toolpath.Point(cornerX + width, cornerY)) poly.addPoint(toolpath.Point(cornerX + width, cornerY + height)) return poly
def point(point): """Returns polygon for point (x, y) as a Polygon Object""" poly = toolpath.Polygon() x, y = point poly.addPoint(toolpath.Point(x, y)) return poly