further adjust some copyright dates
[PyX.git] / pyx / graph / graph.py
blob6c5636079dd86b8973568383e8ac726def21451e
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-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 import math, re, string
27 from pyx import canvas, path, trafo, unit
28 from pyx.graph import style
29 from pyx.graph.axis import axis, positioner
32 goldenmean = 0.5 * (math.sqrt(5) + 1)
35 class styledata:
36 """style data storage class
38 Instances of this class are used to store data from the styles
39 and to pass point data to the styles by instances named privatedata
40 and sharedata. sharedata is shared between all the style(s) in use
41 by a data instance, while privatedata is private to each style and
42 used as a storage place instead of self to prevent side effects when
43 using a style several times."""
44 pass
47 class plotitem:
49 def __init__(self, graph, data, styles):
50 self.data = data
51 self.title = data.title
53 # add styles to ensure all needs of the given styles
54 provided = [] # already provided sharedata variables
55 addstyles = [] # a list of style instances to be added in front
56 for s in styles:
57 for n in s.needsdata:
58 if n not in provided:
59 defaultprovider = style.getdefaultprovider(n)
60 addstyles.append(defaultprovider)
61 provided.extend(defaultprovider.providesdata)
62 provided.extend(s.providesdata)
64 self.styles = addstyles + styles
65 self.sharedata = styledata()
66 self.privatedatalist = [styledata() for s in self.styles]
68 # perform setcolumns to all styles
69 columnnames = self.data.columnnames(graph)
70 self.usedcolumnnames = []
71 for privatedata, s in zip(self.privatedatalist, self.styles):
72 self.usedcolumnnames.extend(s.columnnames(privatedata, self.sharedata, graph, columnnames))
74 def selectstyles(self, graph, selectindex, selecttotal):
75 for privatedata, style in zip(self.privatedatalist, self.styles):
76 style.selectstyle(privatedata, self.sharedata, graph, selectindex, selecttotal)
78 def adjustaxesstatic(self, graph):
79 for columnname, data in self.data.columns.items():
80 for privatedata, style in zip(self.privatedatalist, self.styles):
81 style.adjustaxis(privatedata, self.sharedata, graph, columnname, data)
83 def makedynamicdata(self, graph):
84 self.dynamiccolumns = self.data.dynamiccolumns(graph)
86 def adjustaxesdynamic(self, graph):
87 for columnname, data in self.dynamiccolumns.items():
88 for privatedata, style in zip(self.privatedatalist, self.styles):
89 style.adjustaxis(privatedata, self.sharedata, graph, columnname, data)
91 def draw(self, graph):
92 for privatedata, style in zip(self.privatedatalist, self.styles):
93 style.initdrawpoints(privatedata, self.sharedata, graph)
94 point = {}
95 useitems = []
96 for columnname in self.usedcolumnnames:
97 try:
98 useitems.append((columnname, self.data.columns[columnname]))
99 except:
100 useitems.append((columnname, self.dynamiccolumns[columnname]))
101 if not useitems:
102 raise ValueError("cannot draw empty data")
103 for i in xrange(len(useitems[0][1])):
104 for columnname, data in useitems:
105 point[columnname] = data[i]
106 for privatedata, style in zip(self.privatedatalist, self.styles):
107 style.drawpoint(privatedata, self.sharedata, graph, point)
108 for privatedata, style in zip(self.privatedatalist, self.styles):
109 style.donedrawpoints(privatedata, self.sharedata, graph)
111 def key_pt(self, graph, x_pt, y_pt, width_pt, height_pt):
112 for privatedata, style in zip(self.privatedatalist, self.styles):
113 style.key_pt(privatedata, self.sharedata, graph, x_pt, y_pt, width_pt, height_pt)
115 def __getattr__(self, attr):
116 # read only access to the styles privatedata
117 stylesdata = [getattr(styledata, attr)
118 for styledata in self.privatedatalist
119 if hasattr(styledata, attr)]
120 if len(stylesdata) > 1:
121 return stylesdata
122 elif len(stylesdata) == 1:
123 return stylesdata[0]
124 raise AttributeError("access to styledata attribute '%s' failed" % attr)
128 class graphxy(canvas.canvas):
130 def plot(self, data, styles=None):
131 if self.haslayout:
132 raise RuntimeError("layout setup was already performed")
133 singledata = 0
134 try:
135 for d in data:
136 pass
137 except:
138 usedata = [data]
139 singledata = 1
140 else:
141 usedata = data
142 if styles is None:
143 for d in usedata:
144 if styles is None:
145 styles = d.defaultstyles
146 elif styles != d.defaultstyles:
147 raise RuntimeError("defaultstyles differ")
148 plotitems = []
149 for d in usedata:
150 plotitems.append(plotitem(self, d, styles))
151 self.plotitems.extend(plotitems)
152 if singledata:
153 return plotitems[0]
154 else:
155 return plotitems
157 def pos_pt(self, x, y, xaxis=None, yaxis=None):
158 if xaxis is None:
159 xaxis = self.axes["x"]
160 if yaxis is None:
161 yaxis = self.axes["y"]
162 return self.xpos_pt + xaxis.convert(x)*self.width_pt, self.ypos_pt + yaxis.convert(y)*self.height_pt
164 def pos(self, x, y, xaxis=None, yaxis=None):
165 if xaxis is None:
166 xaxis = self.axes["x"]
167 if yaxis is None:
168 yaxis = self.axes["y"]
169 return self.xpos + xaxis.convert(x)*self.width, self.ypos + yaxis.convert(y)*self.height
171 def vpos_pt(self, vx, vy):
172 return self.xpos_pt + vx*self.width_pt, self.ypos_pt + vy*self.height_pt
174 def vpos(self, vx, vy):
175 return self.xpos + vx*self.width, self.ypos + vy*self.height
177 def vgeodesic(self, vx1, vy1, vx2, vy2):
178 """returns a geodesic path between two points in graph coordinates"""
179 return path.line_pt(self.xpos_pt + vx1*self.width_pt,
180 self.ypos_pt + vy1*self.height_pt,
181 self.xpos_pt + vx2*self.width_pt,
182 self.ypos_pt + vy2*self.height_pt)
184 def vgeodesic_el(self, vx1, vy1, vx2, vy2):
185 """returns a geodesic path element between two points in graph coordinates"""
186 return path.lineto_pt(self.xpos_pt + vx2*self.width_pt,
187 self.ypos_pt + vy2*self.height_pt)
189 def vcap_pt(self, coordinate, length_pt, vx, vy):
190 """returns an error cap path for a given coordinate, lengths and
191 point in graph coordinates"""
192 if coordinate == 0:
193 return path.line_pt(self.xpos_pt + vx*self.width_pt - 0.5*length_pt,
194 self.ypos_pt + vy*self.height_pt,
195 self.xpos_pt + vx*self.width_pt + 0.5*length_pt,
196 self.ypos_pt + vy*self.height_pt)
197 elif coordinate == 1:
198 return path.line_pt(self.xpos_pt + vx*self.width_pt,
199 self.ypos_pt + vy*self.height_pt - 0.5*length_pt,
200 self.xpos_pt + vx*self.width_pt,
201 self.ypos_pt + vy*self.height_pt + 0.5*length_pt)
202 else:
203 raise ValueError("direction invalid")
205 def xvgridpath(self, vx):
206 return path.line_pt(self.xpos_pt + vx*self.width_pt, self.ypos_pt,
207 self.xpos_pt + vx*self.width_pt, self.ypos_pt + self.height_pt)
209 def yvgridpath(self, vy):
210 return path.line_pt(self.xpos_pt, self.ypos_pt + vy*self.height_pt,
211 self.xpos_pt + self.width_pt, self.ypos_pt + vy*self.height_pt)
213 def keynum(self, key):
214 try:
215 while key[0] in string.letters:
216 key = key[1:]
217 return int(key)
218 except IndexError:
219 return 1
221 def removedomethod(self, method):
222 hadmethod = 0
223 while 1:
224 try:
225 self.domethods.remove(method)
226 hadmethod = 1
227 except ValueError:
228 return hadmethod
230 def axistrafo(self, axis, t):
231 c = canvas.canvas([t])
232 c.insert(axis.canvas)
233 axis.canvas = c
235 def axisatv(self, axis, v):
236 if axis.positioner.fixtickdirection[0]:
237 # it is a y-axis
238 self.axistrafo(axis, trafo.translate_pt(self.xpos_pt + v*self.width_pt - axis.positioner.x1_pt, 0))
239 else:
240 # it is an x-axis
241 self.axistrafo(axis, trafo.translate_pt(0, self.ypos_pt + v*self.height_pt - axis.positioner.y1_pt))
243 def dolayout(self):
244 if not self.removedomethod(self.dolayout): return
246 # count the usage of styles and perform selects
247 styletotal = {}
248 def stylesid(styles):
249 return ":".join([str(id(style)) for style in styles])
250 for plotitem in self.plotitems:
251 try:
252 styletotal[stylesid(plotitem.styles)] += 1
253 except:
254 styletotal[stylesid(plotitem.styles)] = 1
255 styleindex = {}
256 for plotitem in self.plotitems:
257 try:
258 styleindex[stylesid(plotitem.styles)] += 1
259 except:
260 styleindex[stylesid(plotitem.styles)] = 0
261 plotitem.selectstyles(self, styleindex[stylesid(plotitem.styles)], styletotal[stylesid(plotitem.styles)])
263 # adjust the axes ranges
264 for plotitem in self.plotitems:
265 plotitem.adjustaxesstatic(self)
266 for plotitem in self.plotitems:
267 plotitem.makedynamicdata(self)
268 for plotitem in self.plotitems:
269 plotitem.adjustaxesdynamic(self)
271 # finish all axes
272 keys = list(self.axes.keys())
273 keys.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
274 for key in keys:
275 self.axes[key].create(self.texrunner)
276 if key[1:]:
277 num = int(key[1:])
278 else:
279 num = 1
280 if num:
281 nextkey = "%s%i" % (key[0], (num+2))
282 if self.axes.has_key(nextkey):
283 sign = 2*(num % 2) - 1
284 if key[0] == "x":
285 y_pt = self.axes[key].positioner.y1_pt - sign * (self.axes[key].canvas.extent_pt + self.axesdist_pt)
286 apositioner = positioner.lineaxispos_pt(self.xpos_pt, y_pt,
287 self.xpos_pt + self.width_pt, y_pt,
288 (0, sign), self.xvgridpath)
289 else:
290 x_pt = self.axes[key].positioner.x1_pt - sign * (self.axes[key].canvas.extent_pt + self.axesdist_pt)
291 apositioner = positioner.lineaxispos_pt(x_pt, self.ypos_pt,
292 x_pt, self.ypos_pt + self.height_pt,
293 (sign, 0), self.yvgridpath)
294 self.axes[nextkey].setpositioner(apositioner)
296 if self.xaxisat is not None:
297 self.axisatv(self.axes["x"], self.axes["y"].convert(self.xaxisat))
298 if self.yaxisat is not None:
299 self.axisatv(self.axes["y"], self.axes["x"].convert(self.yaxisat))
301 self.haslayout = 1
303 def dobackground(self):
304 self.dolayout()
305 if not self.removedomethod(self.dobackground): return
306 if self.backgroundattrs is not None:
307 self.draw(path.rect_pt(self.xpos_pt, self.ypos_pt, self.width_pt, self.height_pt),
308 self.backgroundattrs)
310 def doaxes(self):
311 self.dolayout()
312 if not self.removedomethod(self.doaxes): return
313 for axis in self.axes.values():
314 self.insert(axis.canvas)
316 def dodata(self):
317 self.dolayout()
318 if not self.removedomethod(self.dodata): return
319 for plotitem in self.plotitems:
320 plotitem.draw(self)
322 def dokey(self):
323 self.dolayout()
324 if not self.removedomethod(self.dokey): return
325 if self.key is not None:
326 c = self.key.paint(self.plotitems)
327 bbox = c.bbox()
328 def parentchildalign(pmin, pmax, cmin, cmax, pos, dist, inside):
329 ppos = pmin+0.5*(cmax-cmin)+dist+pos*(pmax-pmin-cmax+cmin-2*dist)
330 cpos = 0.5*(cmin+cmax)+(1-inside)*(1-2*pos)*(cmax-cmin+2*dist)
331 return ppos-cpos
332 x = parentchildalign(self.xpos_pt, self.xpos_pt+self.width_pt,
333 bbox.llx_pt, bbox.urx_pt,
334 self.key.hpos, unit.topt(self.key.hdist), self.key.hinside)
335 y = parentchildalign(self.ypos_pt, self.ypos_pt+self.height_pt,
336 bbox.lly_pt, bbox.ury_pt,
337 self.key.vpos, unit.topt(self.key.vdist), self.key.vinside)
338 self.insert(c, [trafo.translate_pt(x, y)])
340 def finish(self):
341 while len(self.domethods):
342 self.domethods[0]()
344 def initwidthheight(self, width, height, ratio):
345 self.width = width
346 self.height = height
347 if width is None:
348 if height is None:
349 raise ValueError("specify width and/or height")
350 else:
351 self.width = ratio * self.height
352 elif height is None:
353 self.height = (1.0/ratio) * self.width
354 self.width_pt = unit.topt(self.width)
355 self.height_pt = unit.topt(self.height)
357 def initaxes(self, axes):
358 self.axes = {}
359 for key, aaxis in axes.items():
360 if aaxis is not None:
361 if not isinstance(aaxis, axis.linkedaxis):
362 self.axes[key] = axis.anchoredaxis(aaxis, key)
363 else:
364 self.axes[key] = aaxis
365 for key, axisat in [("x", self.xaxisat), ("y", self.yaxisat)]:
366 okey = key + "2"
367 if not axes.has_key(key):
368 if not axes.has_key(okey):
369 self.axes[key] = axis.anchoredaxis(axis.linear(), key)
370 self.axes[okey] = axis.linkedaxis(self.axes[key], okey)
371 else:
372 self.axes[key] = axis.linkedaxis(self.axes[okey], key)
373 elif not axes.has_key(okey) and axisat is None:
374 self.axes[okey] = axis.linkedaxis(self.axes[key], okey)
376 if self.axes.has_key("x"):
377 self.axes["x"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt,
378 self.xpos_pt + self.width_pt, self.ypos_pt,
379 (0, 1), self.xvgridpath))
380 self.xbasepath = self.axes["x"].basepath
381 self.xvbasepath = self.axes["x"].vbasepath
382 self.xgridpath = self.axes["x"].gridpath
383 self.xtickpoint_pt = self.axes["x"].tickpoint_pt
384 self.xtickpoint = self.axes["x"].tickpoint
385 self.xvtickpoint_pt = self.axes["x"].vtickpoint_pt
386 self.xvtickpoint = self.axes["x"].tickpoint
387 self.xtickdirection = self.axes["x"].tickdirection
388 self.xvtickdirection = self.axes["x"].vtickdirection
390 if self.axes.has_key("x2"):
391 self.axes["x2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt + self.height_pt,
392 self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt,
393 (0, -1), self.xvgridpath))
394 if self.axes.has_key("y"):
395 self.axes["y"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt,
396 self.xpos_pt, self.ypos_pt + self.height_pt,
397 (1, 0), self.yvgridpath))
398 self.ybasepath = self.axes["y"].basepath
399 self.yvbasepath = self.axes["y"].vbasepath
400 self.ygridpath = self.axes["y"].gridpath
401 self.ytickpoint_pt = self.axes["y"].tickpoint_pt
402 self.ytickpoint = self.axes["y"].tickpoint
403 self.yvtickpoint_pt = self.axes["y"].vtickpoint_pt
404 self.yvtickpoint = self.axes["y"].tickpoint
405 self.ytickdirection = self.axes["y"].tickdirection
406 self.yvtickdirection = self.axes["y"].vtickdirection
408 if self.axes.has_key("y2"):
409 self.axes["y2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt + self.width_pt, self.ypos_pt,
410 self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt,
411 (-1, 0), self.yvgridpath))
413 self.axesnames = ([], [])
414 for key in self.axes.keys():
415 if len(key) != 1 and (not key[1:].isdigit() or key[1:] == "1"):
416 raise ValueError("invalid axis count")
417 if key[0] == "x":
418 self.axesnames[0].append(key)
419 elif key[0] == "y":
420 self.axesnames[1].append(key)
421 else:
422 raise ValueError("invalid axis name")
424 def __init__(self, xpos=0, ypos=0, width=None, height=None, ratio=goldenmean,
425 key=None, backgroundattrs=None, axesdist=0.8*unit.v_cm,
426 xaxisat=None, yaxisat=None, **axes):
427 canvas.canvas.__init__(self)
428 self.xpos = xpos
429 self.ypos = ypos
430 self.xpos_pt = unit.topt(self.xpos)
431 self.ypos_pt = unit.topt(self.ypos)
432 self.xaxisat = xaxisat
433 self.yaxisat = yaxisat
434 self.initwidthheight(width, height, ratio)
435 self.initaxes(axes)
436 self.key = key
437 self.backgroundattrs = backgroundattrs
438 self.axesdist = axesdist
439 self.axesdist_pt = unit.topt(axesdist)
440 self.plotitems = []
441 self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata, self.dokey]
442 self.haslayout = 0
444 def bbox(self):
445 self.finish()
446 return canvas.canvas.bbox(self)
448 def registerPS(self, registry):
449 self.finish()
450 canvas.canvas.registerPS(self, registry)
452 def registerPDF(self, registry):
453 self.finish()
454 canvas.canvas.registerPDF(self, registry)
456 def outputPS(self, file, writer, context):
457 self.finish()
458 canvas.canvas.outputPS(self, file, writer, context)
460 def outputPDF(self, file, writer, context):
461 self.finish()
462 canvas.canvas.outputPDF(self, file, writer, context)
466 # some thoughts, but deferred right now
468 # class graphxyz(graphxy):
470 # axisnames = "x", "y", "z"
472 # def _vxtickpoint(self, axis, v):
473 # return self._vpos(v, axis.vypos, axis.vzpos)
475 # def _vytickpoint(self, axis, v):
476 # return self._vpos(axis.vxpos, v, axis.vzpos)
478 # def _vztickpoint(self, axis, v):
479 # return self._vpos(axis.vxpos, axis.vypos, v)
481 # def vxtickdirection(self, axis, v):
482 # x1, y1 = self._vpos(v, axis.vypos, axis.vzpos)
483 # x2, y2 = self._vpos(v, 0.5, 0)
484 # dx, dy = x1 - x2, y1 - y2
485 # norm = math.hypot(dx, dy)
486 # return dx/norm, dy/norm
488 # def vytickdirection(self, axis, v):
489 # x1, y1 = self._vpos(axis.vxpos, v, axis.vzpos)
490 # x2, y2 = self._vpos(0.5, v, 0)
491 # dx, dy = x1 - x2, y1 - y2
492 # norm = math.hypot(dx, dy)
493 # return dx/norm, dy/norm
495 # def vztickdirection(self, axis, v):
496 # return -1, 0
497 # x1, y1 = self._vpos(axis.vxpos, axis.vypos, v)
498 # x2, y2 = self._vpos(0.5, 0.5, v)
499 # dx, dy = x1 - x2, y1 - y2
500 # norm = math.hypot(dx, dy)
501 # return dx/norm, dy/norm
503 # def _pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
504 # if xaxis is None: xaxis = self.axes["x"]
505 # if yaxis is None: yaxis = self.axes["y"]
506 # if zaxis is None: zaxis = self.axes["z"]
507 # return self._vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
509 # def pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
510 # if xaxis is None: xaxis = self.axes["x"]
511 # if yaxis is None: yaxis = self.axes["y"]
512 # if zaxis is None: zaxis = self.axes["z"]
513 # return self.vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
515 # def _vpos(self, vx, vy, vz):
516 # x, y, z = (vx - 0.5)*self._depth, (vy - 0.5)*self._width, (vz - 0.5)*self._height
517 # d0 = float(self.a[0]*self.b[1]*(z-self.eye[2])
518 # + self.a[2]*self.b[0]*(y-self.eye[1])
519 # + self.a[1]*self.b[2]*(x-self.eye[0])
520 # - self.a[2]*self.b[1]*(x-self.eye[0])
521 # - self.a[0]*self.b[2]*(y-self.eye[1])
522 # - self.a[1]*self.b[0]*(z-self.eye[2]))
523 # da = (self.eye[0]*self.b[1]*(z-self.eye[2])
524 # + self.eye[2]*self.b[0]*(y-self.eye[1])
525 # + self.eye[1]*self.b[2]*(x-self.eye[0])
526 # - self.eye[2]*self.b[1]*(x-self.eye[0])
527 # - self.eye[0]*self.b[2]*(y-self.eye[1])
528 # - self.eye[1]*self.b[0]*(z-self.eye[2]))
529 # db = (self.a[0]*self.eye[1]*(z-self.eye[2])
530 # + self.a[2]*self.eye[0]*(y-self.eye[1])
531 # + self.a[1]*self.eye[2]*(x-self.eye[0])
532 # - self.a[2]*self.eye[1]*(x-self.eye[0])
533 # - self.a[0]*self.eye[2]*(y-self.eye[1])
534 # - self.a[1]*self.eye[0]*(z-self.eye[2]))
535 # return da/d0 + self._xpos, db/d0 + self._ypos
537 # def vpos(self, vx, vy, vz):
538 # tx, ty = self._vpos(vx, vy, vz)
539 # return unit.t_pt(tx), unit.t_pt(ty)
541 # def xbaseline(self, axis, x1, x2, xaxis=None):
542 # if xaxis is None: xaxis = self.axes["x"]
543 # return self.vxbaseline(axis, xaxis.convert(x1), xaxis.convert(x2))
545 # def ybaseline(self, axis, y1, y2, yaxis=None):
546 # if yaxis is None: yaxis = self.axes["y"]
547 # return self.vybaseline(axis, yaxis.convert(y1), yaxis.convert(y2))
549 # def zbaseline(self, axis, z1, z2, zaxis=None):
550 # if zaxis is None: zaxis = self.axes["z"]
551 # return self.vzbaseline(axis, zaxis.convert(z1), zaxis.convert(z2))
553 # def vxbaseline(self, axis, v1, v2):
554 # return (path._line(*(self._vpos(v1, 0, 0) + self._vpos(v2, 0, 0))) +
555 # path._line(*(self._vpos(v1, 0, 1) + self._vpos(v2, 0, 1))) +
556 # path._line(*(self._vpos(v1, 1, 1) + self._vpos(v2, 1, 1))) +
557 # path._line(*(self._vpos(v1, 1, 0) + self._vpos(v2, 1, 0))))
559 # def vybaseline(self, axis, v1, v2):
560 # return (path._line(*(self._vpos(0, v1, 0) + self._vpos(0, v2, 0))) +
561 # path._line(*(self._vpos(0, v1, 1) + self._vpos(0, v2, 1))) +
562 # path._line(*(self._vpos(1, v1, 1) + self._vpos(1, v2, 1))) +
563 # path._line(*(self._vpos(1, v1, 0) + self._vpos(1, v2, 0))))
565 # def vzbaseline(self, axis, v1, v2):
566 # return (path._line(*(self._vpos(0, 0, v1) + self._vpos(0, 0, v2))) +
567 # path._line(*(self._vpos(0, 1, v1) + self._vpos(0, 1, v2))) +
568 # path._line(*(self._vpos(1, 1, v1) + self._vpos(1, 1, v2))) +
569 # path._line(*(self._vpos(1, 0, v1) + self._vpos(1, 0, v2))))
571 # def xgridpath(self, x, xaxis=None):
572 # assert 0
573 # if xaxis is None: xaxis = self.axes["x"]
574 # v = xaxis.convert(x)
575 # return path._line(self._xpos+v*self._width, self._ypos,
576 # self._xpos+v*self._width, self._ypos+self._height)
578 # def ygridpath(self, y, yaxis=None):
579 # assert 0
580 # if yaxis is None: yaxis = self.axes["y"]
581 # v = yaxis.convert(y)
582 # return path._line(self._xpos, self._ypos+v*self._height,
583 # self._xpos+self._width, self._ypos+v*self._height)
585 # def zgridpath(self, z, zaxis=None):
586 # assert 0
587 # if zaxis is None: zaxis = self.axes["z"]
588 # v = zaxis.convert(z)
589 # return path._line(self._xpos, self._zpos+v*self._height,
590 # self._xpos+self._width, self._zpos+v*self._height)
592 # def vxgridpath(self, v):
593 # return path.path(path._moveto(*self._vpos(v, 0, 0)),
594 # path._lineto(*self._vpos(v, 0, 1)),
595 # path._lineto(*self._vpos(v, 1, 1)),
596 # path._lineto(*self._vpos(v, 1, 0)),
597 # path.closepath())
599 # def vygridpath(self, v):
600 # return path.path(path._moveto(*self._vpos(0, v, 0)),
601 # path._lineto(*self._vpos(0, v, 1)),
602 # path._lineto(*self._vpos(1, v, 1)),
603 # path._lineto(*self._vpos(1, v, 0)),
604 # path.closepath())
606 # def vzgridpath(self, v):
607 # return path.path(path._moveto(*self._vpos(0, 0, v)),
608 # path._lineto(*self._vpos(0, 1, v)),
609 # path._lineto(*self._vpos(1, 1, v)),
610 # path._lineto(*self._vpos(1, 0, v)),
611 # path.closepath())
613 # def _addpos(self, x, y, dx, dy):
614 # assert 0
615 # return x+dx, y+dy
617 # def _connect(self, x1, y1, x2, y2):
618 # assert 0
619 # return path._lineto(x2, y2)
621 # def doaxes(self):
622 # self.dolayout()
623 # if not self.removedomethod(self.doaxes): return
624 # axesdist_pt = unit.topt(self.axesdist)
625 # XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0])
626 # YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1])
627 # ZPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[2])
628 # items = list(self.axes.items())
629 # items.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
630 # for key, axis in items:
631 # num = self.keynum(key)
632 # num2 = 1 - num % 2 # x1 -> 0, x2 -> 1, x3 -> 0, x4 -> 1, ...
633 # num3 = 1 - 2 * (num % 2) # x1 -> -1, x2 -> 1, x3 -> -1, x4 -> 1, ...
634 # if XPattern.match(key):
635 # axis.vypos = 0
636 # axis.vzpos = 0
637 # axis._vtickpoint = self._vxtickpoint
638 # axis.vgridpath = self.vxgridpath
639 # axis.vbaseline = self.vxbaseline
640 # axis.vtickdirection = self.vxtickdirection
641 # elif YPattern.match(key):
642 # axis.vxpos = 0
643 # axis.vzpos = 0
644 # axis._vtickpoint = self._vytickpoint
645 # axis.vgridpath = self.vygridpath
646 # axis.vbaseline = self.vybaseline
647 # axis.vtickdirection = self.vytickdirection
648 # elif ZPattern.match(key):
649 # axis.vxpos = 0
650 # axis.vypos = 0
651 # axis._vtickpoint = self._vztickpoint
652 # axis.vgridpath = self.vzgridpath
653 # axis.vbaseline = self.vzbaseline
654 # axis.vtickdirection = self.vztickdirection
655 # else:
656 # raise ValueError("Axis key '%s' not allowed" % key)
657 # if axis.painter is not None:
658 # axis.dopaint(self)
659 # # if XPattern.match(key):
660 # # self._xaxisextents[num2] += axis._extent
661 # # needxaxisdist[num2] = 1
662 # # if YPattern.match(key):
663 # # self._yaxisextents[num2] += axis._extent
664 # # needyaxisdist[num2] = 1
666 # def __init__(self, tex, xpos=0, ypos=0, width=None, height=None, depth=None,
667 # phi=30, theta=30, distance=1,
668 # backgroundattrs=None, axesdist=0.8*unit.v_cm, **axes):
669 # canvas.canvas.__init__(self)
670 # self.tex = tex
671 # self.xpos = xpos
672 # self.ypos = ypos
673 # self._xpos = unit.topt(xpos)
674 # self._ypos = unit.topt(ypos)
675 # self._width = unit.topt(width)
676 # self._height = unit.topt(height)
677 # self._depth = unit.topt(depth)
678 # self.width = width
679 # self.height = height
680 # self.depth = depth
681 # if self._width <= 0: raise ValueError("width < 0")
682 # if self._height <= 0: raise ValueError("height < 0")
683 # if self._depth <= 0: raise ValueError("height < 0")
684 # self._distance = distance*math.sqrt(self._width*self._width+
685 # self._height*self._height+
686 # self._depth*self._depth)
687 # phi *= -math.pi/180
688 # theta *= math.pi/180
689 # self.a = (-math.sin(phi), math.cos(phi), 0)
690 # self.b = (-math.cos(phi)*math.sin(theta),
691 # -math.sin(phi)*math.sin(theta),
692 # math.cos(theta))
693 # self.eye = (self._distance*math.cos(phi)*math.cos(theta),
694 # self._distance*math.sin(phi)*math.cos(theta),
695 # self._distance*math.sin(theta))
696 # self.initaxes(axes)
697 # self.axesdist = axesdist
698 # self.backgroundattrs = backgroundattrs
700 # self.data = []
701 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
702 # self.haslayout = 0
703 # self.defaultstyle = {}
705 # def bbox(self):
706 # self.finish()
707 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)