3
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
def rootWrapper(a,b,c,d):
24
#TODO: find a new cubic solver and put it here
25
#return solveCubicMonic(b/a,c/a,d/a)
30
return (-c+cmath.sqrt(det))/(2.0*b),(-c-cmath.sqrt(det))/(2.0*b)
37
def bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
48
return ax,ay,bx,by,cx,cy,x0,y0
49
#ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
51
def linebezierintersect(((lx1,ly1),(lx2,ly2)),((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
65
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
66
#cubic intersection coefficients
70
d=coef1*(y0-bb)-coef2*(x0-dd)
72
roots = rootWrapper(a,b,c,d)
75
if type(i) is complex and i.imag==0:
77
if type(i) is not complex and 0<=i<=1:
81
def bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
82
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
83
x=ax*(t**3)+bx*(t**2)+cx*t+x0
84
y=ay*(t**3)+by*(t**2)+cy*t+y0
87
def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
88
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
89
dx=3*ax*(t**2)+2*bx*t+cx
90
dy=3*ay*(t**2)+2*by*t+cy
93
def beziertatslope(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),(dy,dx)):
94
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
95
#quadratic coefficents of slope formula
109
roots = rootWrapper(0,a,b,c)
112
if type(i) is complex and i.imag==0:
114
if type(i) is not complex and 0<=i<=1:
118
def tpoint((x1,y1),(x2,y2),t):
119
return x1+t*(x2-x1),y1+t*(y2-y1)
120
def beziersplitatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
121
m1=tpoint((bx0,by0),(bx1,by1),t)
122
m2=tpoint((bx1,by1),(bx2,by2),t)
123
m3=tpoint((bx2,by2),(bx3,by3),t)
128
return ((bx0,by0),m1,m4,m),(m,m5,m3,(bx3,by3))
131
Approximating the arc length of a bezier curve
132
according to <http://www.cit.gu.edu.au/~anthony/info/graphics/bezier.curves>
135
L1 = |P0 P1| +|P1 P2| +|P2 P3|
140
ERR approaches 0 as the number of subdivisions (m) increases
144
Jens Gravesen <gravesen@mat.dth.dk>
145
"Adaptive subdivision and the length of Bezier curves"
146
mat-report no. 1992-10, Mathematical Institute, The Technical
147
University of Denmark.
149
def pointdistance((x1,y1),(x2,y2)):
150
return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
151
def Gravesen_addifclose(b, len, error = 0.001):
154
box += pointdistance(b[i-1], b[i])
155
chord = pointdistance(b[0], b[3])
156
if (box - chord) > error:
157
first, second = beziersplitatt(b, 0.5)
158
Gravesen_addifclose(first, len, error)
159
Gravesen_addifclose(second, len, error)
161
len[0] += (box / 2.0) + (chord / 2.0)
162
def bezierlengthGravesen(b, error = 0.001):
164
Gravesen_addifclose(b, len, error)
167
# balf = Bezier Arc Length Function
168
balfax,balfbx,balfcx,balfay,balfby,balfcy = 0,0,0,0,0,0
170
retval = (balfax*(t**2) + balfbx*t + balfcx)**2 + (balfay*(t**2) + balfby*t + balfcy)**2
171
return math.sqrt(retval)
173
def Simpson(f, a, b, n_limit, tolerance):
175
multiplier = (b - a)/6.0
177
interval = (b - a)/2.0
179
bsum = f(a + interval)
180
est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
182
#print multiplier, endsum, interval, asum, bsum, est1, est0
183
while n < n_limit and abs(est1 - est0) > tolerance:
190
for i in xrange(1, n, 2):
191
bsum += f(a + (i * interval))
192
est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
193
#print multiplier, endsum, interval, asum, bsum, est1, est0
196
def bezierlengthSimpson(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), tolerance = 0.001):
197
global balfax,balfbx,balfcx,balfay,balfby,balfcy
198
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
199
balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
200
return Simpson(balf, 0.0, 1.0, 4096, tolerance)
202
def beziertatlength(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), l = 0.5, tolerance = 0.001):
203
global balfax,balfbx,balfcx,balfay,balfby,balfcy
204
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
205
balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
208
curlen = Simpson(balf, 0.0, t, 4096, tolerance)
209
targetlen = l * curlen
210
diff = curlen - targetlen
211
while abs(diff) > tolerance:
217
curlen = Simpson(balf, 0.0, t, 4096, tolerance)
218
diff = curlen - targetlen
221
#default bezier length method
222
bezierlength = bezierlengthSimpson
224
if __name__ == '__main__':
226
#print linebezierintersect(((,),(,)),((,),(,),(,),(,)))
227
#print linebezierintersect(((0,1),(0,-1)),((-1,0),(-.5,0),(.5,0),(1,0)))
229
curves = [((0,0),(1,5),(4,5),(5,5)),
230
((0,0),(0,0),(5,0),(10,0)),
231
((0,0),(0,0),(5,1),(10,0)),
232
((-10,0),(0,0),(10,0),(10,10)),
233
((15,10),(0,0),(10,0),(-5,10))]
237
g = bezierlengthGravesen(curve,tol)
242
s = bezierlengthSimpson(curve,tol)
250
print beziertatlength(curve,0.5)