some code reorganisation/renaming: separate graph directory, write(-Postscript) metho...
[PyX/mjg.git] / pyx / graph / type.py
blob8c9e1acfc7784290f3125802899bccd62750a5d8
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
9 # This file is part of PyX (http://pyx.sourceforge.net/).
11 # PyX is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # PyX is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with PyX; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 import math, re, string
27 from pyx import canvas, path, trafo, unit
28 from pyx.graph import painter, axis
31 goldenmean = 0.5 * (math.sqrt(5) + 1)
34 class lineaxispos:
35 """an axispos linear along a line with a fix direction for the ticks"""
37 def __init__(self, convert, x1, y1, x2, y2, fixtickdirection):
38 """initializes the instance
39 - only the convert method is needed from the axis
40 - x1, y1, x2, y2 are PyX lengths (start and end position of the line)
41 - fixtickdirection is a tuple tick direction (fixed along the line)"""
42 self.convert = convert
43 self.x1 = x1
44 self.y1 = y1
45 self.x2 = x2
46 self.y2 = y2
47 self.x1_pt = unit.topt(x1)
48 self.y1_pt = unit.topt(y1)
49 self.x2_pt = unit.topt(x2)
50 self.y2_pt = unit.topt(y2)
51 self.fixtickdirection = fixtickdirection
53 def vbasepath(self, v1=None, v2=None):
54 if v1 is None:
55 v1 = 0
56 if v2 is None:
57 v2 = 1
58 return path.line_pt((1-v1)*self.x1_pt+v1*self.x2_pt,
59 (1-v1)*self.y1_pt+v1*self.y2_pt,
60 (1-v2)*self.x1_pt+v2*self.x2_pt,
61 (1-v2)*self.y1_pt+v2*self.y2_pt)
63 def basepath(self, x1=None, x2=None):
64 if x1 is None:
65 v1 = 0
66 else:
67 v1 = self.convert(x1)
68 if x2 is None:
69 v2 = 1
70 else:
71 v2 = self.convert(x2)
72 return path.line_pt((1-v1)*self.x1_pt+v1*self.x2_pt,
73 (1-v1)*self.y1_pt+v1*self.y2_pt,
74 (1-v2)*self.x1_pt+v2*self.x2_pt,
75 (1-v2)*self.y1_pt+v2*self.y2_pt)
77 def gridpath(self, x):
78 raise RuntimeError("gridpath not available")
80 def vgridpath(self, v):
81 raise RuntimeError("gridpath not available")
83 def vtickpoint_pt(self, v):
84 return (1-v)*self.x1_pt+v*self.x2_pt, (1-v)*self.y1_pt+v*self.y2_pt
86 def vtickpoint(self, v):
87 return (1-v)*self.x1+v*self.x2, (1-v)*self.y1+v*self.y2
89 def tickpoint_pt(self, x):
90 v = self.convert(x)
91 return (1-v)*self.x1_pt+v*self.x2_pt, (1-v)*self.y1_pt+v*self.y2_pt
93 def tickpoint(self, x):
94 v = self.convert(x)
95 return (1-v)*self.x1+v*self.x2, (1-v)*self.y1+v*self.y2
97 def tickdirection(self, x):
98 return self.fixtickdirection
100 def vtickdirection(self, v):
101 return self.fixtickdirection
104 class lineaxisposlinegrid(lineaxispos):
105 """an axispos linear along a line with a fix direction for the ticks
106 with support for grid lines for a rectangular graphs"""
108 __implements__ = painter._Iaxispos
110 def __init__(self, convert, x1, y1, x2, y2, fixtickdirection, startgridlength, endgridlength):
111 """initializes the instance
112 - only the convert method is needed from the axis
113 - x1, y1, x2, y2 are PyX lengths (start and end position of the line)
114 - fixtickdirection is a tuple tick direction (fixed along the line)
115 - startgridlength and endgridlength are PyX lengths for the starting
116 and end point of the grid, respectively; the gridpath is a line along
117 the fixtickdirection"""
118 lineaxispos.__init__(self, convert, x1, y1, x2, y2, fixtickdirection)
119 self.startgridlength = startgridlength
120 self.endgridlength = endgridlength
121 self.startgridlength_pt = unit.topt(self.startgridlength)
122 self.endgridlength_pt = unit.topt(self.endgridlength)
124 def gridpath(self, x):
125 v = self.convert(x)
126 return path.line_pt((1-v)*self.x1_pt+v*self.x2_pt+self.fixtickdirection[0]*self.startgridlength_pt,
127 (1-v)*self.y1_pt+v*self.y2_pt+self.fixtickdirection[1]*self.startgridlength_pt,
128 (1-v)*self.x1_pt+v*self.x2_pt+self.fixtickdirection[0]*self.endgridlength_pt,
129 (1-v)*self.y1_pt+v*self.y2_pt+self.fixtickdirection[1]*self.endgridlength_pt)
131 def vgridpath(self, v):
132 return path.line_pt((1-v)*self.x1_pt+v*self.x2_pt+self.fixtickdirection[0]*self.startgridlength_pt,
133 (1-v)*self.y1_pt+v*self.y2_pt+self.fixtickdirection[1]*self.startgridlength_pt,
134 (1-v)*self.x1_pt+v*self.x2_pt+self.fixtickdirection[0]*self.endgridlength_pt,
135 (1-v)*self.y1_pt+v*self.y2_pt+self.fixtickdirection[1]*self.endgridlength_pt)
138 class graphxy(canvas.canvas):
140 axisnames = "x", "y"
142 class axisposdata:
144 def __init__(self, type, axispos, tickdirection):
146 - type == 0: x-axis; type == 1: y-axis
147 - axispos_pt is the y or x position of the x-axis or y-axis
148 in postscript points, respectively
149 - axispos is analogous to axispos, but as a PyX length
150 - dx and dy is the tick direction
152 self.type = type
153 self.axispos = axispos
154 self.axispos_pt = unit.topt(axispos)
155 self.tickdirection = tickdirection
157 def plot(self, data, style=None):
158 if self.haslayout:
159 raise RuntimeError("layout setup was already performed")
160 try:
161 for d in data:
162 pass
163 except:
164 usedata = [data]
165 else:
166 usedata = data
167 if style is None:
168 for d in usedata:
169 if style is None:
170 style = d.defaultstyle
171 elif style != d.defaultstyle:
172 raise RuntimeError("defaultstyles differ")
173 for d in usedata:
174 d.setstyle(self, style)
175 self.plotdata.append(d)
176 return data
178 def pos_pt(self, x, y, xaxis=None, yaxis=None):
179 if xaxis is None:
180 xaxis = self.axes["x"]
181 if yaxis is None:
182 yaxis = self.axes["y"]
183 return self.xpos_pt + xaxis.convert(x)*self.width_pt, self.ypos_pt + yaxis.convert(y)*self.height_pt
185 def pos(self, x, y, xaxis=None, yaxis=None):
186 if xaxis is None:
187 xaxis = self.axes["x"]
188 if yaxis is None:
189 yaxis = self.axes["y"]
190 return self.xpos + xaxis.convert(x)*self.width, self.ypos + yaxis.convert(y)*self.height
192 def vpos_pt(self, vx, vy):
193 return self.xpos_pt + vx*self.width_pt, self.ypos_pt + vy*self.height_pt
195 def vpos(self, vx, vy):
196 return self.xpos + vx*self.width, self.ypos + vy*self.height
198 def vgeodesic(self, vx1, vy1, vx2, vy2):
199 """returns a geodesic path between two points in graph coordinates"""
200 return path.line_pt(self.xpos_pt + vx1*self.width_pt,
201 self.ypos_pt + vy1*self.height_pt,
202 self.xpos_pt + vx2*self.width_pt,
203 self.ypos_pt + vy2*self.height_pt)
205 def vgeodesic_el(self, vx1, vy1, vx2, vy2):
206 """returns a geodesic path element between two points in graph coordinates"""
207 return path.lineto_pt(self.xpos_pt + vx2*self.width_pt,
208 self.ypos_pt + vy2*self.height_pt)
210 def vcap_pt(self, direction, length_pt, vx, vy):
211 """returns an error cap path for a given direction, lengths and
212 point in graph coordinates"""
213 if direction == "x":
214 return path.line_pt(self.xpos_pt + vx*self.width_pt - 0.5*length_pt,
215 self.ypos_pt + vy*self.height_pt,
216 self.xpos_pt + vx*self.width_pt + 0.5*length_pt,
217 self.ypos_pt + vy*self.height_pt)
218 elif direction == "y":
219 return path.line_pt(self.xpos_pt + vx*self.width_pt,
220 self.ypos_pt + vy*self.height_pt - 0.5*length_pt,
221 self.xpos_pt + vx*self.width_pt,
222 self.ypos_pt + vy*self.height_pt + 0.5*length_pt)
223 else:
224 raise ValueError("direction invalid")
226 def keynum(self, key):
227 try:
228 while key[0] in string.letters:
229 key = key[1:]
230 return int(key)
231 except IndexError:
232 return 1
234 def removedomethod(self, method):
235 hadmethod = 0
236 while 1:
237 try:
238 self.domethods.remove(method)
239 hadmethod = 1
240 except ValueError:
241 return hadmethod
243 def dolayout(self):
244 if not self.removedomethod(self.dolayout): return
246 # count the usage of styles and perform selects
247 styletotal = {}
248 for data in self.plotdata:
249 try:
250 styletotal[id(data.style)] += 1
251 except:
252 styletotal[id(data.style)] = 1
253 styleindex = {}
254 for data in self.plotdata:
255 try:
256 styleindex[id(data.style)] += 1
257 except:
258 styleindex[id(data.style)] = 0
259 data.selectstyle(self, styleindex[id(data.style)], styletotal[id(data.style)])
261 # adjust the axes ranges
262 for step in range(3):
263 for data in self.plotdata:
264 data.adjustaxes(self, step)
266 # finish all axes
267 axesdist = unit.length(self.axesdist_str, default_type="v")
268 XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0])
269 YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1])
270 xaxisextents = [0, 0]
271 yaxisextents = [0, 0]
272 needxaxisdist = [0, 0]
273 needyaxisdist = [0, 0]
274 items = list(self.axes.items())
275 items.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
276 for key, axis in items:
277 num = self.keynum(key)
278 num2 = 1 - num % 2 # x1 -> 0, x2 -> 1, x3 -> 0, x4 -> 1, ...
279 num3 = 2 * (num % 2) - 1 # x1 -> 1, x2 -> -1, x3 -> 1, x4 -> -1, ...
280 if XPattern.match(key):
281 if needxaxisdist[num2]:
282 xaxisextents[num2] += axesdist
283 self.axespos[key] = lineaxisposlinegrid(self.axes[key].convert,
284 self.xpos,
285 self.ypos + num2*self.height - num3*xaxisextents[num2],
286 self.xpos + self.width,
287 self.ypos + num2*self.height - num3*xaxisextents[num2],
288 (0, num3),
289 xaxisextents[num2], xaxisextents[num2] + self.height)
290 if num == 1:
291 self.xbasepath = self.axespos[key].basepath
292 self.xvbasepath = self.axespos[key].vbasepath
293 self.xgridpath = self.axespos[key].gridpath
294 self.xvgridpath = self.axespos[key].vgridpath
295 self.xtickpoint_pt = self.axespos[key].tickpoint_pt
296 self.xtickpoint = self.axespos[key].tickpoint
297 self.xvtickpoint_pt = self.axespos[key].vtickpoint_pt
298 self.xvtickpoint = self.axespos[key].tickpoint
299 self.xtickdirection = self.axespos[key].tickdirection
300 self.xvtickdirection = self.axespos[key].vtickdirection
301 elif YPattern.match(key):
302 if needyaxisdist[num2]:
303 yaxisextents[num2] += axesdist
304 self.axespos[key] = lineaxisposlinegrid(self.axes[key].convert,
305 self.xpos + num2*self.width - num3*yaxisextents[num2],
306 self.ypos,
307 self.xpos + num2*self.width - num3*yaxisextents[num2],
308 self.ypos + self.height,
309 (num3, 0),
310 yaxisextents[num2], yaxisextents[num2] + self.width)
311 if num == 1:
312 self.ybasepath = self.axespos[key].basepath
313 self.yvbasepath = self.axespos[key].vbasepath
314 self.ygridpath = self.axespos[key].gridpath
315 self.yvgridpath = self.axespos[key].vgridpath
316 self.ytickpoint_pt = self.axespos[key].tickpoint_pt
317 self.ytickpoint = self.axespos[key].tickpoint
318 self.yvtickpoint_pt = self.axespos[key].vtickpoint_pt
319 self.yvtickpoint = self.axespos[key].tickpoint
320 self.ytickdirection = self.axespos[key].tickdirection
321 self.yvtickdirection = self.axespos[key].vtickdirection
322 else:
323 raise ValueError("Axis key '%s' not allowed" % key)
324 axis.finish(self.axespos[key])
325 if XPattern.match(key):
326 xaxisextents[num2] += axis.axiscanvas.extent
327 needxaxisdist[num2] = 1
328 if YPattern.match(key):
329 yaxisextents[num2] += axis.axiscanvas.extent
330 needyaxisdist[num2] = 1
331 self.haslayout = 1
333 def dobackground(self):
334 self.dolayout()
335 if not self.removedomethod(self.dobackground): return
336 if self.backgroundattrs is not None:
337 self.draw(path.rect_pt(self.xpos_pt, self.ypos_pt, self.width_pt, self.height_pt),
338 helper.ensurelist(self.backgroundattrs))
340 def doaxes(self):
341 self.dolayout()
342 if not self.removedomethod(self.doaxes): return
343 for axis in self.axes.values():
344 self.insert(axis.axiscanvas)
346 def dodata(self):
347 self.dolayout()
348 if not self.removedomethod(self.dodata): return
349 for data in self.plotdata:
350 data.draw(self)
352 def dokey(self):
353 self.dolayout()
354 if not self.removedomethod(self.dokey): return
355 if self.key is not None:
356 c = self.key.paint(self.plotdata)
357 bbox = c.bbox()
358 if self.key.right:
359 if self.key.hinside:
360 x = self.xpos_pt + self.width_pt - bbox.urx - self.key.hdist_pt
361 else:
362 x = self.xpos_pt + self.width_pt - bbox.llx + self.key.hdist_pt
363 else:
364 if self.key.hinside:
365 x = self.xpos_pt - bbox.llx + self.key.hdist_pt
366 else:
367 x = self.xpos_pt - bbox.urx - self.key.hdist_pt
368 if self.key.top:
369 if self.key.vinside:
370 y = self.ypos_pt + self.height_pt - bbox.ury - self.key.vdist_pt
371 else:
372 y = self.ypos_pt + self.height_pt - bbox.lly + self.key.vdist_pt
373 else:
374 if self.key.vinside:
375 y = self.ypos_pt - bbox.lly + self.key.vdist_pt
376 else:
377 y = self.ypos_pt - bbox.ury - self.key.vdist_pt
378 self.insert(c, [trafo.translate_pt(x, y)])
380 def finish(self):
381 while len(self.domethods):
382 self.domethods[0]()
384 def initwidthheight(self, width, height, ratio):
385 if (width is not None) and (height is None):
386 self.width = unit.length(width)
387 self.height = (1.0/ratio) * self.width
388 elif (height is not None) and (width is None):
389 self.height = unit.length(height)
390 self.width = ratio * self.height
391 else:
392 self.width = unit.length(width)
393 self.height = unit.length(height)
394 self.width_pt = unit.topt(self.width)
395 self.height_pt = unit.topt(self.height)
396 if self.width_pt <= 0: raise ValueError("width <= 0")
397 if self.height_pt <= 0: raise ValueError("height <= 0")
399 def initaxes(self, axes, addlinkaxes=0):
400 for key in self.axisnames:
401 if not axes.has_key(key):
402 axes[key] = axis.linaxis()
403 elif axes[key] is None:
404 del axes[key]
405 if addlinkaxes:
406 if not axes.has_key(key + "2") and axes.has_key(key):
407 axes[key + "2"] = axes[key].createlinkaxis()
408 elif axes[key + "2"] is None:
409 del axes[key + "2"]
410 self.axes = axes
412 def __init__(self, xpos=0, ypos=0, width=None, height=None, ratio=goldenmean,
413 key=None, backgroundattrs=None, axesdist="0.8 cm", **axes):
414 canvas.canvas.__init__(self)
415 self.xpos = unit.length(xpos)
416 self.ypos = unit.length(ypos)
417 self.xpos_pt = unit.topt(self.xpos)
418 self.ypos_pt = unit.topt(self.ypos)
419 self.initwidthheight(width, height, ratio)
420 self.initaxes(axes, 1)
421 self.axescanvas = {}
422 self.axespos = {}
423 self.key = key
424 self.backgroundattrs = backgroundattrs
425 self.axesdist_str = axesdist
426 self.plotdata = []
427 self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata, self.dokey]
428 self.haslayout = 0
429 self.addkeys = []
431 def bbox(self):
432 self.finish()
433 return canvas.canvas.bbox(self)
435 def outputPS(self, file):
436 self.finish()
437 canvas.canvas.outputPS(self, file)
441 # some thoughts, but deferred right now
443 # class graphxyz(graphxy):
445 # axisnames = "x", "y", "z"
447 # def _vxtickpoint(self, axis, v):
448 # return self._vpos(v, axis.vypos, axis.vzpos)
450 # def _vytickpoint(self, axis, v):
451 # return self._vpos(axis.vxpos, v, axis.vzpos)
453 # def _vztickpoint(self, axis, v):
454 # return self._vpos(axis.vxpos, axis.vypos, v)
456 # def vxtickdirection(self, axis, v):
457 # x1, y1 = self._vpos(v, axis.vypos, axis.vzpos)
458 # x2, y2 = self._vpos(v, 0.5, 0)
459 # dx, dy = x1 - x2, y1 - y2
460 # norm = math.sqrt(dx*dx + dy*dy)
461 # return dx/norm, dy/norm
463 # def vytickdirection(self, axis, v):
464 # x1, y1 = self._vpos(axis.vxpos, v, axis.vzpos)
465 # x2, y2 = self._vpos(0.5, v, 0)
466 # dx, dy = x1 - x2, y1 - y2
467 # norm = math.sqrt(dx*dx + dy*dy)
468 # return dx/norm, dy/norm
470 # def vztickdirection(self, axis, v):
471 # return -1, 0
472 # x1, y1 = self._vpos(axis.vxpos, axis.vypos, v)
473 # x2, y2 = self._vpos(0.5, 0.5, v)
474 # dx, dy = x1 - x2, y1 - y2
475 # norm = math.sqrt(dx*dx + dy*dy)
476 # return dx/norm, dy/norm
478 # def _pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
479 # if xaxis is None: xaxis = self.axes["x"]
480 # if yaxis is None: yaxis = self.axes["y"]
481 # if zaxis is None: zaxis = self.axes["z"]
482 # return self._vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
484 # def pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
485 # if xaxis is None: xaxis = self.axes["x"]
486 # if yaxis is None: yaxis = self.axes["y"]
487 # if zaxis is None: zaxis = self.axes["z"]
488 # return self.vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
490 # def _vpos(self, vx, vy, vz):
491 # x, y, z = (vx - 0.5)*self._depth, (vy - 0.5)*self._width, (vz - 0.5)*self._height
492 # d0 = float(self.a[0]*self.b[1]*(z-self.eye[2])
493 # + self.a[2]*self.b[0]*(y-self.eye[1])
494 # + self.a[1]*self.b[2]*(x-self.eye[0])
495 # - self.a[2]*self.b[1]*(x-self.eye[0])
496 # - self.a[0]*self.b[2]*(y-self.eye[1])
497 # - self.a[1]*self.b[0]*(z-self.eye[2]))
498 # da = (self.eye[0]*self.b[1]*(z-self.eye[2])
499 # + self.eye[2]*self.b[0]*(y-self.eye[1])
500 # + self.eye[1]*self.b[2]*(x-self.eye[0])
501 # - self.eye[2]*self.b[1]*(x-self.eye[0])
502 # - self.eye[0]*self.b[2]*(y-self.eye[1])
503 # - self.eye[1]*self.b[0]*(z-self.eye[2]))
504 # db = (self.a[0]*self.eye[1]*(z-self.eye[2])
505 # + self.a[2]*self.eye[0]*(y-self.eye[1])
506 # + self.a[1]*self.eye[2]*(x-self.eye[0])
507 # - self.a[2]*self.eye[1]*(x-self.eye[0])
508 # - self.a[0]*self.eye[2]*(y-self.eye[1])
509 # - self.a[1]*self.eye[0]*(z-self.eye[2]))
510 # return da/d0 + self._xpos, db/d0 + self._ypos
512 # def vpos(self, vx, vy, vz):
513 # tx, ty = self._vpos(vx, vy, vz)
514 # return unit.t_pt(tx), unit.t_pt(ty)
516 # def xbaseline(self, axis, x1, x2, xaxis=None):
517 # if xaxis is None: xaxis = self.axes["x"]
518 # return self.vxbaseline(axis, xaxis.convert(x1), xaxis.convert(x2))
520 # def ybaseline(self, axis, y1, y2, yaxis=None):
521 # if yaxis is None: yaxis = self.axes["y"]
522 # return self.vybaseline(axis, yaxis.convert(y1), yaxis.convert(y2))
524 # def zbaseline(self, axis, z1, z2, zaxis=None):
525 # if zaxis is None: zaxis = self.axes["z"]
526 # return self.vzbaseline(axis, zaxis.convert(z1), zaxis.convert(z2))
528 # def vxbaseline(self, axis, v1, v2):
529 # return (path._line(*(self._vpos(v1, 0, 0) + self._vpos(v2, 0, 0))) +
530 # path._line(*(self._vpos(v1, 0, 1) + self._vpos(v2, 0, 1))) +
531 # path._line(*(self._vpos(v1, 1, 1) + self._vpos(v2, 1, 1))) +
532 # path._line(*(self._vpos(v1, 1, 0) + self._vpos(v2, 1, 0))))
534 # def vybaseline(self, axis, v1, v2):
535 # return (path._line(*(self._vpos(0, v1, 0) + self._vpos(0, v2, 0))) +
536 # path._line(*(self._vpos(0, v1, 1) + self._vpos(0, v2, 1))) +
537 # path._line(*(self._vpos(1, v1, 1) + self._vpos(1, v2, 1))) +
538 # path._line(*(self._vpos(1, v1, 0) + self._vpos(1, v2, 0))))
540 # def vzbaseline(self, axis, v1, v2):
541 # return (path._line(*(self._vpos(0, 0, v1) + self._vpos(0, 0, v2))) +
542 # path._line(*(self._vpos(0, 1, v1) + self._vpos(0, 1, v2))) +
543 # path._line(*(self._vpos(1, 1, v1) + self._vpos(1, 1, v2))) +
544 # path._line(*(self._vpos(1, 0, v1) + self._vpos(1, 0, v2))))
546 # def xgridpath(self, x, xaxis=None):
547 # assert 0
548 # if xaxis is None: xaxis = self.axes["x"]
549 # v = xaxis.convert(x)
550 # return path._line(self._xpos+v*self._width, self._ypos,
551 # self._xpos+v*self._width, self._ypos+self._height)
553 # def ygridpath(self, y, yaxis=None):
554 # assert 0
555 # if yaxis is None: yaxis = self.axes["y"]
556 # v = yaxis.convert(y)
557 # return path._line(self._xpos, self._ypos+v*self._height,
558 # self._xpos+self._width, self._ypos+v*self._height)
560 # def zgridpath(self, z, zaxis=None):
561 # assert 0
562 # if zaxis is None: zaxis = self.axes["z"]
563 # v = zaxis.convert(z)
564 # return path._line(self._xpos, self._zpos+v*self._height,
565 # self._xpos+self._width, self._zpos+v*self._height)
567 # def vxgridpath(self, v):
568 # return path.path(path._moveto(*self._vpos(v, 0, 0)),
569 # path._lineto(*self._vpos(v, 0, 1)),
570 # path._lineto(*self._vpos(v, 1, 1)),
571 # path._lineto(*self._vpos(v, 1, 0)),
572 # path.closepath())
574 # def vygridpath(self, v):
575 # return path.path(path._moveto(*self._vpos(0, v, 0)),
576 # path._lineto(*self._vpos(0, v, 1)),
577 # path._lineto(*self._vpos(1, v, 1)),
578 # path._lineto(*self._vpos(1, v, 0)),
579 # path.closepath())
581 # def vzgridpath(self, v):
582 # return path.path(path._moveto(*self._vpos(0, 0, v)),
583 # path._lineto(*self._vpos(0, 1, v)),
584 # path._lineto(*self._vpos(1, 1, v)),
585 # path._lineto(*self._vpos(1, 0, v)),
586 # path.closepath())
588 # def _addpos(self, x, y, dx, dy):
589 # assert 0
590 # return x+dx, y+dy
592 # def _connect(self, x1, y1, x2, y2):
593 # assert 0
594 # return path._lineto(x2, y2)
596 # def doaxes(self):
597 # self.dolayout()
598 # if not self.removedomethod(self.doaxes): return
599 # axesdist = unit.topt(unit.length(self.axesdist_str, default_type="v"))
600 # XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0])
601 # YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1])
602 # ZPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[2])
603 # items = list(self.axes.items())
604 # items.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
605 # for key, axis in items:
606 # num = self.keynum(key)
607 # num2 = 1 - num % 2 # x1 -> 0, x2 -> 1, x3 -> 0, x4 -> 1, ...
608 # num3 = 1 - 2 * (num % 2) # x1 -> -1, x2 -> 1, x3 -> -1, x4 -> 1, ...
609 # if XPattern.match(key):
610 # axis.vypos = 0
611 # axis.vzpos = 0
612 # axis._vtickpoint = self._vxtickpoint
613 # axis.vgridpath = self.vxgridpath
614 # axis.vbaseline = self.vxbaseline
615 # axis.vtickdirection = self.vxtickdirection
616 # elif YPattern.match(key):
617 # axis.vxpos = 0
618 # axis.vzpos = 0
619 # axis._vtickpoint = self._vytickpoint
620 # axis.vgridpath = self.vygridpath
621 # axis.vbaseline = self.vybaseline
622 # axis.vtickdirection = self.vytickdirection
623 # elif ZPattern.match(key):
624 # axis.vxpos = 0
625 # axis.vypos = 0
626 # axis._vtickpoint = self._vztickpoint
627 # axis.vgridpath = self.vzgridpath
628 # axis.vbaseline = self.vzbaseline
629 # axis.vtickdirection = self.vztickdirection
630 # else:
631 # raise ValueError("Axis key '%s' not allowed" % key)
632 # if axis.painter is not None:
633 # axis.dopaint(self)
634 # # if XPattern.match(key):
635 # # self._xaxisextents[num2] += axis._extent
636 # # needxaxisdist[num2] = 1
637 # # if YPattern.match(key):
638 # # self._yaxisextents[num2] += axis._extent
639 # # needyaxisdist[num2] = 1
641 # def __init__(self, tex, xpos=0, ypos=0, width=None, height=None, depth=None,
642 # phi=30, theta=30, distance=1,
643 # backgroundattrs=None, axesdist="0.8 cm", **axes):
644 # canvas.canvas.__init__(self)
645 # self.tex = tex
646 # self.xpos = xpos
647 # self.ypos = ypos
648 # self._xpos = unit.topt(xpos)
649 # self._ypos = unit.topt(ypos)
650 # self._width = unit.topt(width)
651 # self._height = unit.topt(height)
652 # self._depth = unit.topt(depth)
653 # self.width = width
654 # self.height = height
655 # self.depth = depth
656 # if self._width <= 0: raise ValueError("width < 0")
657 # if self._height <= 0: raise ValueError("height < 0")
658 # if self._depth <= 0: raise ValueError("height < 0")
659 # self._distance = distance*math.sqrt(self._width*self._width+
660 # self._height*self._height+
661 # self._depth*self._depth)
662 # phi *= -math.pi/180
663 # theta *= math.pi/180
664 # self.a = (-math.sin(phi), math.cos(phi), 0)
665 # self.b = (-math.cos(phi)*math.sin(theta),
666 # -math.sin(phi)*math.sin(theta),
667 # math.cos(theta))
668 # self.eye = (self._distance*math.cos(phi)*math.cos(theta),
669 # self._distance*math.sin(phi)*math.cos(theta),
670 # self._distance*math.sin(theta))
671 # self.initaxes(axes)
672 # self.axesdist_str = axesdist
673 # self.backgroundattrs = backgroundattrs
675 # self.data = []
676 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
677 # self.haslayout = 0
678 # self.defaultstyle = {}
680 # def bbox(self):
681 # self.finish()
682 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)