I had a problem with Adobe Illustrator's spiral tool producing ugly, square spirals. I decided I would see if I could get nodebox to make a better bezier spiral. It turned out to be non-trivial (to me) but not really hard either. Since it worked very well for my needs I though I would share it in case anyone else mind find it useful.
It should be pretty self-explanatory with the example. Enjoy--and feel free to post any suggestions.
from nodebox.graphicsimportBezierPathclass spiral(BezierPath):
'''A logarithmic spiral beginning at start_x, start_y and spiraling toward the point end_x, end_y.
Decay: determines how tightly the spiral turns. Negative values for decay result in the spiral turning
opposite direction. A value of zero results in a line
Length: is how many times around the spiril turns.
'''def__init__(self, start_x, start_y, end_x, end_y, decay, length, draw=True, **kwargs):
frommathimport sin, cos, pi, e, sqrt, atan2, powBezierPath.__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 errorself.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 = 8ifabs(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 inrange(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()#### End Spiral Classsize(820, 820)autoclosepath(close=False)nofill()strokewidth(.5)var("begin_x", NUMBER, 30, 0, WIDTH)var("begin_y", NUMBER, 200, 0, WIDTH)var("end_x", NUMBER, 300, 0, WIDTH)var("end_y", NUMBER, 200, 0, WIDTH)var("decay", NUMBER, 5, -10, 10)var("rotations", NUMBER, 2, .25, 5)
p = spiral(begin_x, begin_y, end_x, end_y, decay , rotations, stroke=(.7, .2, .2))
Spiral
Posted by Mark M on Feb 26, 2008I had a problem with Adobe Illustrator's spiral tool producing ugly, square spirals. I decided I would see if I could get nodebox to make a better bezier spiral. It turned out to be non-trivial (to me) but not really hard either. Since it worked very well for my needs I though I would share it in case anyone else mind find it useful.
It should be pretty self-explanatory with the example. Enjoy--and feel free to post any suggestions.