showcircle
[PyX/mjg.git] / test / experimental / newbox.py
blob9b905f482cc50201d8f3aa388cbd712c20680c66
1 import sys; sys.path.insert(0, "../..")
3 import math
4 from pyx import *
6 c = canvas.canvas()
8 # I'm missing some basic functionality ...
9 def normpatheltrafo(normpathel, param):
10 tx, ty = normpathel.at_pt(param)
11 tdx, tdy = normpathel.tangentvector_pt(param)
12 return trafo.translate_pt(tx, ty)*trafo.rotate(math.degrees(math.atan2(tdy, tdx)))
14 def normpathelbegintrafo(normpathel):
15 if isinstance(normpathel, path.normcurve):
16 tx, ty = normpathel.x0_pt, normpathel.y0_pt
17 tdx, tdy = normpathel.x1_pt - normpathel.x0_pt, normpathel.y1_pt - normpathel.y0_pt
18 return trafo.translate_pt(tx, ty)*trafo.rotate(math.degrees(math.atan2(tdy, tdx)))
19 else:
20 tx, ty = normpathel.x0_pt, normpathel.y0_pt
21 tdx, tdy = normpathel.x1_pt - normpathel.x0_pt, normpathel.y1_pt - normpathel.y0_pt
22 return trafo.translate_pt(tx, ty)*trafo.rotate(math.degrees(math.atan2(tdy, tdx)))
24 def normpathelendtrafo(normpathel):
25 if isinstance(normpathel, path.normcurve):
26 tx, ty = normpathel.x3_pt, normpathel.y3_pt
27 tdx, tdy = normpathel.x3_pt - normpathel.x2_pt, normpathel.y3_pt - normpathel.y2_pt
28 return trafo.translate_pt(tx, ty)*trafo.rotate(math.degrees(math.atan2(tdy, tdx)))
29 else:
30 tx, ty = normpathel.x1_pt, normpathel.y1_pt
31 tdx, tdy = normpathel.x1_pt - normpathel.x0_pt, normpathel.y1_pt - normpathel.y0_pt
32 return trafo.translate_pt(tx, ty)*trafo.rotate(math.degrees(math.atan2(tdy, tdx)))
34 def connect(normpathel1, normpathel2, round):
35 "returns a list of normpathels connecting normpathel1 and normpathel2 either rounded or not"
36 # this is for corners, i.e. when there are jumps in the tangent vector
37 t1 = normpathelendtrafo(normpathel1)
38 t2 = normpathelbegintrafo(normpathel2)
39 xs, ys = t1._apply(0, 0) # TODO: _apply -> apply_pt
40 xe, ye = t2._apply(0, 0)
41 if (xs-xe)*(xs-xe) + (xs-xe)*(xs-xe) < 1e-5**2:
42 return []
43 if round:
44 # calculate crossing point of normal
45 try:
46 param = ((t2.matrix[0][1]*(t1.vector[1]-t2.vector[1]) -
47 t2.matrix[1][1]*(t1.vector[0]-t2.vector[0])) /
48 (t2.matrix[1][1]*t1.matrix[0][1] -
49 t2.matrix[0][1]*t1.matrix[1][1]))
50 except ArithmeticError:
51 return [path.normline(xs, ys, xe, ye)]
52 else:
53 # since t1 is a translation + rotation, param is the radius
54 # hence, param should be equal to enlargeby_pt
55 # print param
56 x, y = t1._apply(0, param)
57 # c.fill(path.circle_pt(x, y, 1))
58 angle1 = math.atan2(ys-y, xs-x)
59 angle2 = math.atan2(ye-y, xe-x)
60 while angle2 < angle1:
61 angle1 -= 2*math.pi
62 return [path._arctobcurve(x, y, param, angle1, angle2)]
63 else:
64 # calculate crossing point of tangents
65 try:
66 param = ((t2.matrix[0][0]*(t1.vector[1]-t2.vector[1]) -
67 t2.matrix[1][0]*(t1.vector[0]-t2.vector[0])) /
68 (t2.matrix[1][0]*t1.matrix[0][0] -
69 t2.matrix[0][0]*t1.matrix[1][0]))
70 except ArithmeticError:
71 return [path.normline(xs, ys, xe, ye)]
72 else:
73 x, y = t1._apply(param, 0)
74 return [path.normline(xs, ys, x, y), path.normline(x, y, xe, ye)]
77 def enlarged(normpath, enlargeby_pt, round):
78 newnormsubpaths = []
79 for subpath in normpath.subpaths:
80 splitnormpathels = subpath.normpathels[:] # do splitting on a copy
81 i = 0
82 while i < len(splitnormpathels):
83 if isinstance(splitnormpathels[i], path.normcurve) and splitnormpathels[i].arclen_pt() > 50:
84 splitnormpathels[i:i+1] = splitnormpathels[i].midpointsplit()
85 else:
86 i += 1
87 newnormpathels = []
88 for normpathel in splitnormpathels:
89 # get old and new start and end points
90 ts = normpathelbegintrafo(normpathel)
91 xs, ys = ts._apply(0, 0)
92 nxs, nys = ts._apply(0, -enlargeby_pt)
93 te = normpathelendtrafo(normpathel)
94 xe, ye = te._apply(0, 0)
95 nxe, nye = te._apply(0, -enlargeby_pt)
97 if isinstance(normpathel, path.normcurve):
98 # We should do not alter the sign. Could we do any better here?
99 try:
100 cs = 1/(normpathel.curvradius_pt(0) + enlargeby_pt)
101 except ArithmeticError:
102 cs = 0
103 try:
104 ce = 1/(normpathel.curvradius_pt(1) + enlargeby_pt)
105 except ArithmeticError:
106 ce = 0
108 # this should be a function (in path?), not a method in a class
109 # the parameter convention is quite different from other places ...
110 bezierparams = deco.smoothed.normal._onebezierbetweentwopathels((nxs, nys), (nxe, nye),
111 (ts.matrix[0][0], ts.matrix[1][0]),
112 (te.matrix[0][0], te.matrix[1][0]),
113 cs, ce, strict=1)
114 c.fill(path.circle_pt(bezierparams[0][0], bezierparams[0][1], 1), [color.rgb.blue])
115 c.fill(path.circle_pt(bezierparams[3][0], bezierparams[3][1], 1), [color.rgb.blue])
116 newnormpathel = path.normcurve(bezierparams[0][0], bezierparams[0][1],
117 bezierparams[1][0], bezierparams[1][1],
118 bezierparams[2][0], bezierparams[2][1],
119 bezierparams[3][0], bezierparams[3][1])
120 showtangent(newnormpathel)
121 showcircle(newnormpathel)
122 else:
123 # line
124 newnormpathel = path.normline(nxs, nys, nxe, nye)
126 if len(newnormpathels):
127 newnormpathels.extend(connect(newnormpathels[-1], newnormpathel, round=round))
128 newnormpathels.append(newnormpathel)
129 if subpath.closed:
130 newnormpathels.extend(connect(newnormpathels[-1], newnormpathels[0], round=round))
131 newnormsubpaths.append(path.normsubpath(newnormpathels, subpath.closed))
132 return path.normpath(newnormsubpaths)
135 def showtangent(normcurve):
136 dx = 2
137 dy = 1
139 cx = 3*normcurve.x1_pt - 3*normcurve.x0_pt
140 bx = 3*normcurve.x2_pt - 6*normcurve.x1_pt + 3*normcurve.x0_pt
141 ax = normcurve.x3_pt - 3*normcurve.x2_pt + 3*normcurve.x1_pt - normcurve.x0_pt
143 cy = 3*normcurve.y1_pt - 3*normcurve.y0_pt
144 by = 3*normcurve.y2_pt - 6*normcurve.y1_pt + 3*normcurve.y0_pt
145 ay = normcurve.y3_pt - 3*normcurve.y2_pt + 3*normcurve.y1_pt - normcurve.y0_pt
147 x = - (dy*bx-dx*by)/(3.0*dy*ax-3.0*dx*ay)
148 s = math.sqrt(x*x-(dy*cx-dx*cy)/(3.0*dy*ax-3.0*dx*ay))
149 t1 = x-s
150 t2 = x+s
151 ts = []
152 if 0 < t1 < 1:
153 ts.append(t1)
154 if 0 < t2 < 1:
155 ts.append(t2)
156 for t in ts:
157 trafo = normpatheltrafo(normcurve, t)
158 c.stroke(path.line_pt(*list(trafo._apply(-100, 0))+list(trafo._apply(100, 0))), [color.rgb.red])
160 def showcircle(normcurve):
161 x0 = 350
162 y0 = 200
163 dx = 1
164 dy = -2
165 cos = dx/math.sqrt(dx*dx+dy*dy)
166 sin = dy/math.sqrt(dx*dx+dy*dy)
168 r = 16
170 cx = 3*normcurve.x1_pt - 3*normcurve.x0_pt
171 bx = 3*normcurve.x2_pt - 6*normcurve.x1_pt + 3*normcurve.x0_pt
172 ax = normcurve.x3_pt - 3*normcurve.x2_pt + 3*normcurve.x1_pt - normcurve.x0_pt
174 cy = 3*normcurve.y1_pt - 3*normcurve.y0_pt
175 by = 3*normcurve.y2_pt - 6*normcurve.y1_pt + 3*normcurve.y0_pt
176 ay = normcurve.y3_pt - 3*normcurve.y2_pt + 3*normcurve.y1_pt - normcurve.y0_pt
178 def x(t):
179 return ax*t*t*t+bx*t*t+cx*t+normcurve.x0_pt
180 def y(t):
181 return ay*t*t*t+by*t*t+cy*t+normcurve.y0_pt
182 def xdot(t):
183 return 3*ax*t*t+2*bx*t+cx
184 def ydot(t):
185 return 3*ay*t*t+2*by*t+cy
186 def xddot(t):
187 return 6*ax*t+2*bx
188 def yddot(t):
189 return 6*ay*t+2*by
190 def l(t, sign):
191 return cos*(x(t)-x0)+sin*(y(t)-y0)+sign*math.sqrt(r*r-(sin*(x(t)-x0)-cos*(y(t)-y0))**2)
192 def ldot(t, sign):
193 return cos*xdot(t)+sin*ydot(t)-sign*((sin*(x(t)-x0)-cos*(y(t)-y0))*
194 (sin*xdot(t)-cos*ydot(t))/
195 math.sqrt(r*r-(sin*(x(t)-x0)-cos*(y(t)-y0))**2))
196 def z(t, sign):
197 return (x(t)-x0-l(t, sign)*cos)*xdot(t)+(y(t)-y0-l(t, sign)*sin)*ydot(t)
198 def zdot(t, sign):
199 return ((x(t)-x0-l(t, sign)*cos)*xddot(t)+(xdot(t)-ldot(t, sign)*cos)*xdot(t)+
200 (y(t)-y0-l(t, sign)*sin)*yddot(t)+(ydot(t)-ldot(t, sign)*sin)*ydot(t))
201 def start(t):
202 return sin*(x(t)-x0)-cos*(y(t)-y0)
203 def startdot(t):
204 return sin*xdot(t)-cos*ydot(t)
205 def find(sign):
206 x = - (sin*bx-cos*by)/(3.0*sin*ax-3.0*cos*ay)
207 s = math.sqrt(x*x-(sin*cx-cos*cy)/(3.0*sin*ax-3.0*cos*ay))
208 t1 = x-s
209 t2 = x+s
210 #print startdot(t1), startdot(t2), start(t1), start(t2)
211 startt1, startt2 = start(t1), start(t2)
212 #print startt1, startt2
213 if startt1*startt2 < 0:
214 while abs(startt1)+abs(startt2) > 1e-5:
215 tn = 0.5*(t1+t2)
216 starttn = start(tn)
217 if starttn*startt1 < 0:
218 t2, startt2 = tn, starttn
219 else:
220 t1, startt1 = tn, starttn
221 ist = 0.5*(t1+t2)
222 elif abs(startt1) < abs(startt2):
223 ist = t1
224 else:
225 ist = t2
226 if ist > 1:
227 ist = 1
228 if ist < 0:
229 ist = 0
231 isz=z(ist, sign)
232 while abs(isz) > 1e-5:
233 # print ist, isz
234 ist -= 0.05*isz / zdot(ist, sign) # overshoots lead to nan-results ...
235 isz = z(ist, sign)
236 if isz > -1: # nan?
237 # print ist, isz
238 return ist
240 for sign in [-1, 1]:
241 ist = find(sign)
242 if ist is not None and 0 < ist < 1:
243 isl = l(ist, sign)
244 c.fill(path.circle_pt(x(ist), y(ist), 1), [color.rgb.green])
245 c.stroke(path.circle_pt(x0+isl*cos, y0+isl*sin, r), [color.rgb.green])
248 # parameters for the enlargement:
249 enlargeby_pt = 10
250 round = 0 # a boolean to turn round corners on and off
252 # some examples:
253 for normpath in [path.normpath(path.rect(0, 0, 5, 5)),
254 path.normpath(path.circle(10, 3, 3)),
255 path.normpath(path.path(path.moveto(0, 8),
256 path.curveto(1, 8, 1, 9, 2, 11),
257 path.curveto(1, 12, 1, 12, 0, 11),
258 path.lineto(0, 8),
259 path.closepath())),
260 path.normpath(path.path(path.moveto(5, 8),
261 path.curveto(20, 8, 6, 9, 7, 11),
262 path.curveto(6, 12, 6, 12, 5, 11),
263 path.lineto(5, 8),
264 path.closepath())),
265 path.normpath(path.path(path.moveto(16, 0),
266 path.curveto(18, 0, 18, 4, 20, 4),
267 path.lineto(20, 12),
268 path.curveto(12, 12, 20, 8, 16, 8),
269 path.closepath()))]:
270 c.stroke(normpath)
271 c.stroke(enlarged(normpath, enlargeby_pt, round), [color.rgb.blue])
273 c.writeEPSfile("newbox")