def rect(_ctx, x, y, width, height, roundness=0.0, draw=True, **kwargs): """ Roundness is either a relative float between 0.0 and 1.0, or the absolute radius of the corners. """ BezierPath.checkKwargs(kwargs) p = _ctx.BezierPath(**kwargs) r = max(0, roundness) if r == 0.0: p.rect(x, y, width, height) else: if r <= 1.0 and isinstance(r, float): r = min(r * width / 2, r * height / 2) else: r = min(r, width / 2, height / 2) p.moveto(x, y + r) a, b, c, d = (x, y), (x + width, y), (x, y + height), (x + width, y + height) p._nsBezierPath.appendBezierPathWithArcFromPoint_toPoint_radius_( a, b, r) p._nsBezierPath.appendBezierPathWithArcFromPoint_toPoint_radius_( b, d, r) p._nsBezierPath.appendBezierPathWithArcFromPoint_toPoint_radius_( d, c, r) p._nsBezierPath.appendBezierPathWithArcFromPoint_toPoint_radius_( c, a, r) p.closepath() p.inheritFromContext(kwargs.keys()) if draw: p.draw() return p
def __init__(self, start_x, start_y, end_x, end_y, decay, length, draw=True, **kwargs): from math import sin, cos, pi, e, sqrt, atan2, pow BezierPath.__init__(self, _ctx, **kwargs) if -1e-2 < decay < 1e-2: # very decay of 0 indicates a line # but very small decays are practically line ans too small will cause e**1/decay to big = python error self.moveto(start_x, start_y ) self.lineto(end_x, end_y ) self.inheritFromContext(kwargs.keys()) if draw: self.draw() return b = 1/float(decay) dt = pi/4 m = 8 if abs(b) > 1: # when the curve gets very flat we need to take a few more samples # a better approach might be to simply fit a single cubic curve to the end points dt /= abs(b) if b < 0: #curve reverses dt *= -1 D = sqrt(pow(start_x-end_x,2) + pow(start_y-end_y,2)) t = atan2(start_y-end_y, start_x-end_x) a = e**(-b*t)*D self.moveto(a*e**(b*t) * cos(t) + end_x, a*e**(b*t) * sin(t) + end_y ) for i in range(int(length*m)): t2 = t - dt Dt = t2 -t # logarithmic spiral equations x0 = a*e**(b*t) * cos(t) + end_x y0 = a*e**(b*t) * sin(t) + end_y x3 = a*e**(b*t2) * cos(t2) + end_x y3 = a*e**(b*t2) * sin(t2) + end_y # Derivatives of spiral equation dx1 = a*b*e**(b*t) * cos(t) - a*e**(b*t) * sin(t) dy1 = a*e**(b*t) * cos(t) + a*b*e**(b*t) * sin(t) dx2 = a*b*e**(b*t2) * cos(t2) - a*e**(b*t2) * sin(t2) dy2 = a*e**(b*t2) * cos(t2) + a*b*e**(b*t2) * sin(t2) # calculate control points x1 = x0 + ((Dt/3.0) * dx1) y1 = y0 + ((Dt/3.0) * dy1) x2 = x3 - ((Dt/3.0) * dx2) y2 = y3 - ((Dt/3.0) * dy2) t -= dt self.curveto(x1, y1, x2, y2, x3, y3) self.inheritFromContext(kwargs.keys()) if draw: self.draw()
def contours(path): """Returns a list of contours in the path. A contour is a sequence of lines and curves separated from the next contour by a MOVETO. For example, the glyph "o" has two contours: the inner circle and the outer circle. >>> path = BezierPath(None) >>> path.moveto(0, 0) >>> path.lineto(100, 100) >>> len(contours(path)) 1 A new contour is defined as something that starts with a moveto: >>> path.moveto(50, 50) >>> path.curveto(150, 150, 50, 250, 80, 95) >>> len(contours(path)) 2 Empty moveto's don't do anything: >>> path.moveto(50, 50) >>> path.moveto(50, 50) >>> len(contours(path)) 2 It doesn't matter if the path is closed or open: >>> path.closepath() >>> len(contours(path)) 2 """ contours = [] current_contour = None empty = True for i, el in enumerate(path): if el.cmd == MOVETO: if not empty: contours.append(current_contour) current_contour = BezierPath(path._ctx) current_contour.moveto(el.x, el.y) empty = True elif el.cmd == LINETO: empty = False current_contour.lineto(el.x, el.y) elif el.cmd == CURVETO: empty = False current_contour.curveto(el.ctrl1.x, el.ctrl1.y, el.ctrl2.x, el.ctrl2.y, el.x, el.y) elif el.cmd == CLOSE: current_contour.closepath() if not empty: contours.append(current_contour) return contours
def render(args): from nodebox.graphics import BezierPath, strokewidth, fill, directed, push, translate, rotate, pop, drawpath geom2d = getPropValue(args, 'inputs', 'geometry') # Do the drawing here!! path = BezierPath() strokewidth(width=1) fill(0) ps = geom2d.points for b_path in geom2d.bezier_paths: if len(b_path) > 4: path.moveto(*geom2d.points[b_path[0]]) for p_num in xrange((len(b_path) - 1) // 3): # debugPrint b_path,p_num path.curveto(ps[b_path[p_num * 3 + 1]][0], ps[b_path[p_num * 3 + 1]][1], ps[b_path[p_num * 3 + 2]][0], ps[b_path[p_num * 3 + 2]][1] , ps[b_path[p_num * 3 + 3]][0], ps[b_path[p_num * 3 + 3]][1] ) # points = path.points(amount=len(glyphs), start=0.05, end=0.95) points = path.points(amount=0, start=0.05, end=0.95) for angle, pt in directed(points): push() translate(pt.x, pt.y) rotate(angle) pop() drawpath(path, fill=None, stroke=(0, 0, 0, 1))
def insert_point(path, t): """Returns a path copy with an extra point at t. >>> path = BezierPath(None) >>> path.moveto(0, 0) >>> insert_point(path, 0.1) Traceback (most recent call last): ... NodeBoxError: The given path is empty >>> path.moveto(0, 0) >>> insert_point(path, 0.2) Traceback (most recent call last): ... NodeBoxError: The given path is empty >>> path.lineto(100, 50) >>> len(path) 2 >>> path = insert_point(path, 0.5) >>> len(path) 3 >>> path[1] PathElement(LINETO, ((50.0, 25.0),)) >>> path = BezierPath(None) >>> path.moveto(0, 100) >>> path.curveto(0, 50, 100, 50, 100, 100) >>> path = insert_point(path, 0.5) >>> path[1] PathElement(LINETO, ((25.0, 62.5), (0.0, 75.0), (50.0, 62.5)) """ i, t, closeto = _locate(path, t) x0 = path[i].x y0 = path[i].y p1 = path[i + 1] p1cmd, x3, y3, x1, y1, x2, y2 = p1.cmd, p1.x, p1.y, p1.ctrl1.x, p1.ctrl1.y, p1.ctrl2.x, p1.ctrl2.y if p1cmd == CLOSE: pt_cmd = LINETO pt_x, pt_y = linepoint(t, x0, y0, closeto.x, closeto.y) elif p1cmd == LINETO: pt_cmd = LINETO pt_x, pt_y = linepoint(t, x0, y0, x3, y3) elif p1cmd == CURVETO: pt_cmd = CURVETO pt_x, pt_y, pt_c1x, pt_c1y, pt_c2x, pt_c2y, pt_h1x, pt_h1y, pt_h2x, pt_h2y = \ curvepoint(t, x0, y0, x1, y1, x2, y2, x3, y3, True) else: raise NodeBoxError, "Locate should not return a MOVETO" new_path = BezierPath(None) new_path.moveto(path[0].x, path[0].y) for j in range(1, len(path)): if j == i + 1: if pt_cmd == CURVETO: new_path.curveto(pt_h1x, pt_h1y, pt_c1x, pt_c1y, pt_x, pt_y) new_path.curveto(pt_c2x, pt_c2y, pt_h2x, pt_h2y, path[j].x, path[j].y) elif pt_cmd == LINETO: new_path.lineto(pt_x, pt_y) if path[j].cmd != CLOSE: new_path.lineto(path[j].x, path[j].y) else: new_path.closepath() else: raise NodeBoxError, "Didn't expect pt_cmd %s here" % pt_cmd else: if path[j].cmd == MOVETO: new_path.moveto(path[j].x, path[j].y) if path[j].cmd == LINETO: new_path.lineto(path[j].x, path[j].y) if path[j].cmd == CURVETO: new_path.curveto(path[j].ctrl1.x, path[j].ctrl1.y, path[j].ctrl2.x, path[j].ctrl2.y, path[j].x, path[j].y) if path[j].cmd == CLOSE: new_path.closepath() return new_path
def findpath(points, curvature=1.0): """Constructs a path between the given list of points. Interpolates the list of points and determines a smooth bezier path betweem them. The curvature parameter offers some control on how separate segments are stitched together: from straight angles to smooth curves. Curvature is only useful if the path has more than three points. """ # The list of points consists of Point objects, # but it shouldn't crash on something straightforward # as someone supplying a list of (x,y)-tuples. from types import TupleType for i, pt in enumerate(points): if type(pt) == TupleType: points[i] = Point(pt[0], pt[1]) if len(points) == 0: return None if len(points) == 1: path = BezierPath(None) path.moveto(points[0].x, points[0].y) return path if len(points) == 2: path = BezierPath(None) path.moveto(points[0].x, points[0].y) path.lineto(points[1].x, points[1].y) return path # Zero curvature means straight lines. curvature = max(0, min(1, curvature)) if curvature == 0: path = BezierPath(None) path.moveto(points[0].x, points[0].y) for i in range(len(points)): path.lineto(points[i].x, points[i].y) return path curvature = 4 + (1.0 - curvature) * 40 dx = {0: 0, len(points) - 1: 0} dy = {0: 0, len(points) - 1: 0} bi = {1: -0.25} ax = {1: (points[2].x - points[0].x - dx[0]) / 4} ay = {1: (points[2].y - points[0].y - dy[0]) / 4} for i in range(2, len(points) - 1): bi[i] = -1 / (curvature + bi[i - 1]) ax[i] = -(points[i + 1].x - points[i - 1].x - ax[i - 1]) * bi[i] ay[i] = -(points[i + 1].y - points[i - 1].y - ay[i - 1]) * bi[i] r = range(1, len(points) - 1) r.reverse() for i in r: dx[i] = ax[i] + dx[i + 1] * bi[i] dy[i] = ay[i] + dy[i + 1] * bi[i] path = BezierPath(None) path.moveto(points[0].x, points[0].y) for i in range(len(points) - 1): path.curveto(points[i].x + dx[i], points[i].y + dy[i], points[i + 1].x - dx[i + 1], points[i + 1].y - dy[i + 1], points[i + 1].x, points[i + 1].y) return path
def insert_point(path, t): """Returns a path copy with an extra point at t. >>> path = BezierPath(None) >>> path.moveto(0, 0) >>> insert_point(path, 0.1) Traceback (most recent call last): ... NodeBoxError: The given path is empty >>> path.moveto(0, 0) >>> insert_point(path, 0.2) Traceback (most recent call last): ... NodeBoxError: The given path is empty >>> path.lineto(100, 50) >>> len(path) 2 >>> path = insert_point(path, 0.5) >>> len(path) 3 >>> path[1] PathElement(LINETO, ((50.0, 25.0),)) >>> path = BezierPath(None) >>> path.moveto(0, 100) >>> path.curveto(0, 50, 100, 50, 100, 100) >>> path = insert_point(path, 0.5) >>> path[1] PathElement(LINETO, ((25.0, 62.5), (0.0, 75.0), (50.0, 62.5)) """ i, t, closeto = _locate(path, t) x0 = path[i].x y0 = path[i].y p1 = path[i+1] p1cmd, x3, y3, x1, y1, x2, y2 = p1.cmd, p1.x, p1.y, p1.ctrl1.x, p1.ctrl1.y, p1.ctrl2.x, p1.ctrl2.y if p1cmd == CLOSE: pt_cmd = LINETO pt_x, pt_y = linepoint(t, x0, y0, closeto.x, closeto.y) elif p1cmd == LINETO: pt_cmd = LINETO pt_x, pt_y = linepoint(t, x0, y0, x3, y3) elif p1cmd == CURVETO: pt_cmd = CURVETO pt_x, pt_y, pt_c1x, pt_c1y, pt_c2x, pt_c2y, pt_h1x, pt_h1y, pt_h2x, pt_h2y = \ curvepoint(t, x0, y0, x1, y1, x2, y2, x3, y3, True) else: raise NodeBoxError, "Locate should not return a MOVETO" new_path = BezierPath(None) new_path.moveto(path[0].x, path[0].y) for j in range(1, len(path)): if j == i+1: if pt_cmd == CURVETO: new_path.curveto(pt_h1x, pt_h1y, pt_c1x, pt_c1y, pt_x, pt_y) new_path.curveto(pt_c2x, pt_c2y, pt_h2x, pt_h2y, path[j].x, path[j].y) elif pt_cmd == LINETO: new_path.lineto(pt_x, pt_y) if path[j].cmd != CLOSE: new_path.lineto(path[j].x, path[j].y) else: new_path.closepath() else: raise NodeBoxError, "Didn't expect pt_cmd %s here" % pt_cmd else: if path[j].cmd == MOVETO: new_path.moveto(path[j].x, path[j].y) if path[j].cmd == LINETO: new_path.lineto(path[j].x, path[j].y) if path[j].cmd == CURVETO: new_path.curveto(path[j].ctrl1.x, path[j].ctrl1.y, path[j].ctrl2.x, path[j].ctrl2.y, path[j].x, path[j].y) if path[j].cmd == CLOSE: new_path.closepath() return new_path
def findpath(points, curvature=1.0): """Constructs a path between the given list of points. Interpolates the list of points and determines a smooth bezier path betweem them. The curvature parameter offers some control on how separate segments are stitched together: from straight angles to smooth curves. Curvature is only useful if the path has more than three points. """ # The list of points consists of Point objects, # but it shouldn't crash on something straightforward # as someone supplying a list of (x,y)-tuples. from types import TupleType for i, pt in enumerate(points): if type(pt) == TupleType: points[i] = Point(pt[0], pt[1]) if len(points) == 0: return None if len(points) == 1: path = BezierPath(None) path.moveto(points[0].x, points[0].y) return path if len(points) == 2: path = BezierPath(None) path.moveto(points[0].x, points[0].y) path.lineto(points[1].x, points[1].y) return path # Zero curvature means straight lines. curvature = max(0, min(1, curvature)) if curvature == 0: path = BezierPath(None) path.moveto(points[0].x, points[0].y) for i in range(len(points)): path.lineto(points[i].x, points[i].y) return path curvature = 4 + (1.0-curvature)*40 dx = {0: 0, len(points)-1: 0} dy = {0: 0, len(points)-1: 0} bi = {1: -0.25} ax = {1: (points[2].x-points[0].x-dx[0]) / 4} ay = {1: (points[2].y-points[0].y-dy[0]) / 4} for i in range(2, len(points)-1): bi[i] = -1 / (curvature + bi[i-1]) ax[i] = -(points[i+1].x-points[i-1].x-ax[i-1]) * bi[i] ay[i] = -(points[i+1].y-points[i-1].y-ay[i-1]) * bi[i] r = range(1, len(points)-1) r.reverse() for i in r: dx[i] = ax[i] + dx[i+1] * bi[i] dy[i] = ay[i] + dy[i+1] * bi[i] path = BezierPath(None) path.moveto(points[0].x, points[0].y) for i in range(len(points)-1): path.curveto(points[i].x + dx[i], points[i].y + dy[i], points[i+1].x - dx[i+1], points[i+1].y - dy[i+1], points[i+1].x, points[i+1].y) return path
# Add the upper directory (where the nodebox module is) to the search path. import os, sys;sys.path.insert(0,os.path.join("..","..")) from nodebox.graphics import canvas, BezierPath, drawpath, triangle, ellipse from nodebox.graphics import drawpath, color, fill, stroke, nostroke, background, strokewidth from nodebox.graphics import translate, scale, rotate from random import random import squirtle import amethyst import amethsvg_MOD01 as svgRead # couleur dans la librairy 'amethyst' White= color(amethyst.triplet("white")) # standard nodeboxGL graphics command path1 = BezierPath() path1.moveto(0,0) path1.curveto(0,16.585,28.321,28.335,28.321,28.335) # squirtle svg parser filename = "svg/curve.svg" path2 = squirtle.SVG(filename) # svg lib from Nodebox # The parse() command will return # a list of the shapes in the SVG file. paths3 = svgRead.parse(open("svg/curve.svg").read()) def draw(canvas): #background(color(amethyst.triplet("red")))
# Add the upper directory (where the nodebox module is) to the search path. import os, sys sys.path.insert(0, os.path.join("..", "..")) from nodebox.graphics import canvas, BezierPath, drawpath, triangle, ellipse from nodebox.graphics import drawpath, color, fill, stroke, nostroke, background, strokewidth from nodebox.graphics import translate, scale, rotate from random import random import squirtle import amethyst import amethsvg_MOD01 as svgRead # couleur dans la librairy 'amethyst' White = color(amethyst.triplet("white")) # standard nodeboxGL graphics command path1 = BezierPath() path1.moveto(0, 0) path1.curveto(0, 16.585, 28.321, 28.335, 28.321, 28.335) # squirtle svg parser filename = "svg/curve.svg" path2 = squirtle.SVG(filename) # svg lib from Nodebox # The parse() command will return # a list of the shapes in the SVG file. paths3 = svgRead.parse(open("svg/curve.svg").read()) def draw(canvas): #background(color(amethyst.triplet("red")))