remove shebang -- see comment 3 on https://bugzilla.redhat.com/bugzilla/show_bug...
[PyX/mjg.git] / pyx / graph / graph.py
blob2d1079df5e4b9d0e6b096591b30d3f179be2f1c3
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2005 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 import math, re, string, warnings
26 from pyx import canvas, path, trafo, unit
27 from pyx.graph import style
28 from pyx.graph.axis import axis, positioner
31 goldenmean = 0.5 * (math.sqrt(5) + 1)
34 class styledata:
35 """style data storage class
37 Instances of this class are used to store data from the styles
38 and to pass point data to the styles by instances named privatedata
39 and sharedata. sharedata is shared between all the style(s) in use
40 by a data instance, while privatedata is private to each style and
41 used as a storage place instead of self to prevent side effects when
42 using a style several times."""
43 pass
46 class plotitem:
48 def __init__(self, graph, data, styles):
49 self.data = data
50 self.title = data.title
52 # add styles to ensure all needs of the given styles
53 provided = [] # already provided sharedata variables
54 addstyles = [] # a list of style instances to be added in front
55 for s in styles:
56 for n in s.needsdata:
57 if n not in provided:
58 defaultprovider = style.getdefaultprovider(n)
59 addstyles.append(defaultprovider)
60 provided.extend(defaultprovider.providesdata)
61 provided.extend(s.providesdata)
63 self.styles = addstyles + styles
64 self.sharedata = styledata()
65 self.privatedatalist = [styledata() for s in self.styles]
67 # perform setcolumns to all styles
68 self.usedcolumnnames = []
69 for privatedata, s in zip(self.privatedatalist, self.styles):
70 self.usedcolumnnames.extend(s.columnnames(privatedata, self.sharedata, graph, self.data.columnnames))
72 def selectstyles(self, graph, selectindex, selecttotal):
73 for privatedata, style in zip(self.privatedatalist, self.styles):
74 style.selectstyle(privatedata, self.sharedata, graph, selectindex, selecttotal)
76 def adjustaxesstatic(self, graph):
77 for columnname, data in self.data.columns.items():
78 for privatedata, style in zip(self.privatedatalist, self.styles):
79 style.adjustaxis(privatedata, self.sharedata, graph, columnname, data)
81 def makedynamicdata(self, graph):
82 self.dynamiccolumns = self.data.dynamiccolumns(graph)
84 def adjustaxesdynamic(self, graph):
85 for columnname, data in self.dynamiccolumns.items():
86 for privatedata, style in zip(self.privatedatalist, self.styles):
87 style.adjustaxis(privatedata, self.sharedata, graph, columnname, data)
89 def draw(self, graph):
90 for privatedata, style in zip(self.privatedatalist, self.styles):
91 style.initdrawpoints(privatedata, self.sharedata, graph)
92 point = {}
93 useitems = []
94 for columnname in self.usedcolumnnames:
95 try:
96 useitems.append((columnname, self.dynamiccolumns[columnname]))
97 except KeyError:
98 useitems.append((columnname, self.data.columns[columnname]))
99 if not useitems:
100 raise ValueError("cannot draw empty data")
101 for i in xrange(len(useitems[0][1])):
102 for columnname, data in useitems:
103 point[columnname] = data[i]
104 for privatedata, style in zip(self.privatedatalist, self.styles):
105 style.drawpoint(privatedata, self.sharedata, graph, point)
106 for privatedata, style in zip(self.privatedatalist, self.styles):
107 style.donedrawpoints(privatedata, self.sharedata, graph)
109 def key_pt(self, graph, x_pt, y_pt, width_pt, height_pt):
110 for privatedata, style in zip(self.privatedatalist, self.styles):
111 style.key_pt(privatedata, self.sharedata, graph, x_pt, y_pt, width_pt, height_pt)
113 def __getattr__(self, attr):
114 # read only access to the styles privatedata
115 stylesdata = [getattr(styledata, attr)
116 for styledata in self.privatedatalist
117 if hasattr(styledata, attr)]
118 if len(stylesdata) > 1:
119 return stylesdata
120 elif len(stylesdata) == 1:
121 return stylesdata[0]
122 raise AttributeError("access to styledata attribute '%s' failed" % attr)
125 class graph(canvas.canvas):
127 def __init__(self):
128 canvas.canvas.__init__(self)
129 self.axes = {}
130 self.plotitems = []
131 self._calls = {}
132 self.didranges = 0
133 self.diddata = 0
135 def did(self, method, *args, **kwargs):
136 if not self._calls.has_key(method):
137 self._calls[method] = []
138 for callargs in self._calls[method]:
139 if callargs == (args, kwargs):
140 return 1
141 self._calls[method].append((args, kwargs))
142 return 0
144 def bbox(self):
145 self.finish()
146 return canvas.canvas.bbox(self)
148 def registerPS(self, registry):
149 self.finish()
150 canvas.canvas.registerPS(self, registry)
152 def registerPDF(self, registry):
153 self.finish()
154 canvas.canvas.registerPDF(self, registry)
156 def processPS(self, file, writer, context, registry, bbox):
157 self.finish()
158 canvas.canvas.processPS(self, file, writer, context, registry, bbox)
160 def processPDF(self, file, writer, context, registry, bbox):
161 self.finish()
162 canvas.canvas.processPDF(self, file, writer, context, registry, bbox)
164 def plot(self, data, styles=None, rangewarning=1):
165 if self.didranges and rangewarning:
166 warnings.warn("axes ranges have already been analysed; no further adjustments will be performed")
167 if self.diddata:
168 raise RuntimeError("can't plot further data after dodata() has been executed")
169 singledata = 0
170 try:
171 for d in data:
172 pass
173 except:
174 usedata = [data]
175 singledata = 1
176 else:
177 usedata = data
178 if styles is None:
179 for d in usedata:
180 if styles is None:
181 styles = d.defaultstyles
182 elif styles != d.defaultstyles:
183 raise RuntimeError("defaultstyles differ")
184 plotitems = []
185 for d in usedata:
186 plotitems.append(plotitem(self, d, styles))
187 self.plotitems.extend(plotitems)
188 if self.didranges:
189 for aplotitem in plotitems:
190 aplotitem.makedynamicdata(self)
191 if singledata:
192 return plotitems[0]
193 else:
194 return plotitems
196 def doranges(self):
197 if self.did(self.doranges):
198 return
199 for plotitem in self.plotitems:
200 plotitem.adjustaxesstatic(self)
201 for plotitem in self.plotitems:
202 plotitem.makedynamicdata(self)
203 for plotitem in self.plotitems:
204 plotitem.adjustaxesdynamic(self)
205 self.didranges = 1
207 def dolayout(self):
208 raise NotImplementedError
210 def dobackground(self):
211 raise NotImplementedError
213 def doaxes(self):
214 raise NotImplementedError
216 def dodata(self):
217 if self.did(self.dodata):
218 return
219 self.dolayout()
220 self.dobackground()
222 # count the usage of styles and perform selects
223 styletotal = {}
224 def stylesid(styles):
225 return ":".join([str(id(style)) for style in styles])
226 for plotitem in self.plotitems:
227 try:
228 styletotal[stylesid(plotitem.styles)] += 1
229 except:
230 styletotal[stylesid(plotitem.styles)] = 1
231 styleindex = {}
232 for plotitem in self.plotitems:
233 try:
234 styleindex[stylesid(plotitem.styles)] += 1
235 except:
236 styleindex[stylesid(plotitem.styles)] = 0
237 plotitem.selectstyles(self, styleindex[stylesid(plotitem.styles)],
238 styletotal[stylesid(plotitem.styles)])
240 for plotitem in self.plotitems:
241 plotitem.draw(self)
243 self.diddata = 1
245 def dokey(self):
246 raise NotImplementedError
248 def finish(self):
249 self.dobackground()
250 self.doaxes()
251 self.dodata()
252 self.dokey()
255 class graphxy(graph):
257 def __init__(self, xpos=0, ypos=0, width=None, height=None, ratio=goldenmean,
258 key=None, backgroundattrs=None, axesdist=0.8*unit.v_cm,
259 xaxisat=None, yaxisat=None, **axes):
260 graph.__init__(self)
262 self.xpos = xpos
263 self.ypos = ypos
264 self.xpos_pt = unit.topt(self.xpos)
265 self.ypos_pt = unit.topt(self.ypos)
266 self.xaxisat = xaxisat
267 self.yaxisat = yaxisat
268 self.key = key
269 self.backgroundattrs = backgroundattrs
270 self.axesdist_pt = unit.topt(axesdist)
272 self.width = width
273 self.height = height
274 if width is None:
275 if height is None:
276 raise ValueError("specify width and/or height")
277 else:
278 self.width = ratio * self.height
279 elif height is None:
280 self.height = (1.0/ratio) * self.width
281 self.width_pt = unit.topt(self.width)
282 self.height_pt = unit.topt(self.height)
284 for axisname, aaxis in axes.items():
285 if aaxis is not None:
286 if not isinstance(aaxis, axis.linkedaxis):
287 self.axes[axisname] = axis.anchoredaxis(aaxis, self.texrunner, axisname)
288 else:
289 self.axes[axisname] = aaxis
290 for axisname, axisat in [("x", xaxisat), ("y", yaxisat)]:
291 okey = axisname + "2"
292 if not axes.has_key(axisname):
293 if not axes.has_key(okey):
294 self.axes[axisname] = axis.anchoredaxis(axis.linear(), self.texrunner, axisname)
295 self.axes[okey] = axis.linkedaxis(self.axes[axisname], okey)
296 else:
297 self.axes[axisname] = axis.linkedaxis(self.axes[okey], axisname)
298 elif not axes.has_key(okey) and axisat is None:
299 self.axes[okey] = axis.linkedaxis(self.axes[axisname], okey)
301 if self.axes.has_key("x"):
302 self.xbasepath = self.axes["x"].basepath
303 self.xvbasepath = self.axes["x"].vbasepath
304 self.xgridpath = self.axes["x"].gridpath
305 self.xtickpoint_pt = self.axes["x"].tickpoint_pt
306 self.xtickpoint = self.axes["x"].tickpoint
307 self.xvtickpoint_pt = self.axes["x"].vtickpoint_pt
308 self.xvtickpoint = self.axes["x"].tickpoint
309 self.xtickdirection = self.axes["x"].tickdirection
310 self.xvtickdirection = self.axes["x"].vtickdirection
312 if self.axes.has_key("y"):
313 self.ybasepath = self.axes["y"].basepath
314 self.yvbasepath = self.axes["y"].vbasepath
315 self.ygridpath = self.axes["y"].gridpath
316 self.ytickpoint_pt = self.axes["y"].tickpoint_pt
317 self.ytickpoint = self.axes["y"].tickpoint
318 self.yvtickpoint_pt = self.axes["y"].vtickpoint_pt
319 self.yvtickpoint = self.axes["y"].tickpoint
320 self.ytickdirection = self.axes["y"].tickdirection
321 self.yvtickdirection = self.axes["y"].vtickdirection
323 self.axesnames = ([], [])
324 for axisname, aaxis in self.axes.items():
325 if axisname[0] not in "xy" or (len(axisname) != 1 and (not axisname[1:].isdigit() or
326 axisname[1:] == "1")):
327 raise ValueError("invalid axis name")
328 if axisname[0] == "x":
329 self.axesnames[0].append(axisname)
330 else:
331 self.axesnames[1].append(axisname)
332 aaxis.setcreatecall(self.doaxiscreate, axisname)
335 def pos_pt(self, x, y, xaxis=None, yaxis=None):
336 if xaxis is None:
337 xaxis = self.axes["x"]
338 if yaxis is None:
339 yaxis = self.axes["y"]
340 return (self.xpos_pt + xaxis.convert(x)*self.width_pt,
341 self.ypos_pt + yaxis.convert(y)*self.height_pt)
343 def pos(self, x, y, xaxis=None, yaxis=None):
344 if xaxis is None:
345 xaxis = self.axes["x"]
346 if yaxis is None:
347 yaxis = self.axes["y"]
348 return (self.xpos + xaxis.convert(x)*self.width,
349 self.ypos + yaxis.convert(y)*self.height)
351 def vpos_pt(self, vx, vy):
352 return (self.xpos_pt + vx*self.width_pt,
353 self.ypos_pt + vy*self.height_pt)
355 def vpos(self, vx, vy):
356 return (self.xpos + vx*self.width,
357 self.ypos + vy*self.height)
359 def vgeodesic(self, vx1, vy1, vx2, vy2):
360 """returns a geodesic path between two points in graph coordinates"""
361 return path.line_pt(self.xpos_pt + vx1*self.width_pt,
362 self.ypos_pt + vy1*self.height_pt,
363 self.xpos_pt + vx2*self.width_pt,
364 self.ypos_pt + vy2*self.height_pt)
366 def vgeodesic_el(self, vx1, vy1, vx2, vy2):
367 """returns a geodesic path element between two points in graph coordinates"""
368 return path.lineto_pt(self.xpos_pt + vx2*self.width_pt,
369 self.ypos_pt + vy2*self.height_pt)
371 def vcap_pt(self, coordinate, length_pt, vx, vy):
372 """returns an error cap path for a given coordinate, lengths and
373 point in graph coordinates"""
374 if coordinate == 0:
375 return path.line_pt(self.xpos_pt + vx*self.width_pt - 0.5*length_pt,
376 self.ypos_pt + vy*self.height_pt,
377 self.xpos_pt + vx*self.width_pt + 0.5*length_pt,
378 self.ypos_pt + vy*self.height_pt)
379 elif coordinate == 1:
380 return path.line_pt(self.xpos_pt + vx*self.width_pt,
381 self.ypos_pt + vy*self.height_pt - 0.5*length_pt,
382 self.xpos_pt + vx*self.width_pt,
383 self.ypos_pt + vy*self.height_pt + 0.5*length_pt)
384 else:
385 raise ValueError("direction invalid")
387 def xvgridpath(self, vx):
388 return path.line_pt(self.xpos_pt + vx*self.width_pt, self.ypos_pt,
389 self.xpos_pt + vx*self.width_pt, self.ypos_pt + self.height_pt)
391 def yvgridpath(self, vy):
392 return path.line_pt(self.xpos_pt, self.ypos_pt + vy*self.height_pt,
393 self.xpos_pt + self.width_pt, self.ypos_pt + vy*self.height_pt)
395 def axistrafo(self, axis, t):
396 c = canvas.canvas([t])
397 c.insert(axis.canvas)
398 axis.canvas = c
400 def axisatv(self, axis, v):
401 if axis.positioner.fixtickdirection[0]:
402 # it is a y-axis
403 self.axistrafo(axis, trafo.translate_pt(self.xpos_pt + v*self.width_pt - axis.positioner.x1_pt, 0))
404 else:
405 # it is an x-axis
406 self.axistrafo(axis, trafo.translate_pt(0, self.ypos_pt + v*self.height_pt - axis.positioner.y1_pt))
408 def doaxispositioner(self, axisname):
409 if self.did(self.doaxispositioner, axisname):
410 return
411 self.doranges()
412 if axisname == "x":
413 self.axes["x"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt,
414 self.xpos_pt + self.width_pt, self.ypos_pt,
415 (0, 1), self.xvgridpath))
416 elif axisname == "x2":
417 self.axes["x2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt + self.height_pt,
418 self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt,
419 (0, -1), self.xvgridpath))
420 elif axisname == "y":
421 self.axes["y"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt,
422 self.xpos_pt, self.ypos_pt + self.height_pt,
423 (1, 0), self.yvgridpath))
424 elif axisname == "y2":
425 self.axes["y2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt + self.width_pt, self.ypos_pt,
426 self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt,
427 (-1, 0), self.yvgridpath))
428 else:
429 if axisname[1:] == "3":
430 dependsonaxisname = axisname[0]
431 else:
432 dependsonaxisname = "%s%d" % (axisname[0], int(axisname[1:]) - 2)
433 self.doaxiscreate(dependsonaxisname)
434 sign = 2*(int(axisname[1:]) % 2) - 1
435 if axisname[0] == "x":
436 y_pt = self.axes[dependsonaxisname].positioner.y1_pt - sign * (self.axes[dependsonaxisname].canvas.extent_pt + self.axesdist_pt)
437 self.axes[axisname].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, y_pt,
438 self.xpos_pt + self.width_pt, y_pt,
439 (0, sign), self.xvgridpath))
440 else:
441 x_pt = self.axes[dependsonaxisname].positioner.x1_pt - sign * (self.axes[dependsonaxisname].canvas.extent_pt + self.axesdist_pt)
442 self.axes[axisname].setpositioner(positioner.lineaxispos_pt(x_pt, self.ypos_pt,
443 x_pt, self.ypos_pt + self.height_pt,
444 (sign, 0), self.yvgridpath))
446 def doaxiscreate(self, axisname):
447 if self.did(self.doaxiscreate, axisname):
448 return
449 self.doaxispositioner(axisname)
450 self.axes[axisname].create()
452 def dolayout(self):
453 if self.did(self.dolayout):
454 return
455 for axisname in self.axes.keys():
456 self.doaxiscreate(axisname)
457 if self.xaxisat is not None:
458 self.axisatv(self.axes["x"], self.axes["y"].convert(self.xaxisat))
459 if self.yaxisat is not None:
460 self.axisatv(self.axes["y"], self.axes["x"].convert(self.yaxisat))
462 def dobackground(self):
463 if self.did(self.dobackground):
464 return
465 if self.backgroundattrs is not None:
466 self.draw(path.rect_pt(self.xpos_pt, self.ypos_pt, self.width_pt, self.height_pt),
467 self.backgroundattrs)
469 def doaxes(self):
470 if self.did(self.doaxes):
471 return
472 self.dolayout()
473 self.dobackground()
474 for axis in self.axes.values():
475 self.insert(axis.canvas)
477 def dokey(self):
478 if self.did(self.dokey):
479 return
480 self.dobackground()
481 self.dodata()
482 if self.key is not None:
483 c = self.key.paint(self.plotitems)
484 bbox = c.bbox()
485 def parentchildalign(pmin, pmax, cmin, cmax, pos, dist, inside):
486 ppos = pmin+0.5*(cmax-cmin)+dist+pos*(pmax-pmin-cmax+cmin-2*dist)
487 cpos = 0.5*(cmin+cmax)+(1-inside)*(1-2*pos)*(cmax-cmin+2*dist)
488 return ppos-cpos
489 if bbox:
490 x = parentchildalign(self.xpos_pt, self.xpos_pt+self.width_pt,
491 bbox.llx_pt, bbox.urx_pt,
492 self.key.hpos, unit.topt(self.key.hdist), self.key.hinside)
493 y = parentchildalign(self.ypos_pt, self.ypos_pt+self.height_pt,
494 bbox.lly_pt, bbox.ury_pt,
495 self.key.vpos, unit.topt(self.key.vdist), self.key.vinside)
496 self.insert(c, [trafo.translate_pt(x, y)])
499 # some thoughts, but deferred right now
501 # class graphxyz(graphxy):
503 # axisnames = "x", "y", "z"
505 # def _vxtickpoint(self, axis, v):
506 # return self._vpos(v, axis.vypos, axis.vzpos)
508 # def _vytickpoint(self, axis, v):
509 # return self._vpos(axis.vxpos, v, axis.vzpos)
511 # def _vztickpoint(self, axis, v):
512 # return self._vpos(axis.vxpos, axis.vypos, v)
514 # def vxtickdirection(self, axis, v):
515 # x1, y1 = self._vpos(v, axis.vypos, axis.vzpos)
516 # x2, y2 = self._vpos(v, 0.5, 0)
517 # dx, dy = x1 - x2, y1 - y2
518 # norm = math.hypot(dx, dy)
519 # return dx/norm, dy/norm
521 # def vytickdirection(self, axis, v):
522 # x1, y1 = self._vpos(axis.vxpos, v, axis.vzpos)
523 # x2, y2 = self._vpos(0.5, v, 0)
524 # dx, dy = x1 - x2, y1 - y2
525 # norm = math.hypot(dx, dy)
526 # return dx/norm, dy/norm
528 # def vztickdirection(self, axis, v):
529 # return -1, 0
530 # x1, y1 = self._vpos(axis.vxpos, axis.vypos, v)
531 # x2, y2 = self._vpos(0.5, 0.5, v)
532 # dx, dy = x1 - x2, y1 - y2
533 # norm = math.hypot(dx, dy)
534 # return dx/norm, dy/norm
536 # def _pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
537 # if xaxis is None: xaxis = self.axes["x"]
538 # if yaxis is None: yaxis = self.axes["y"]
539 # if zaxis is None: zaxis = self.axes["z"]
540 # return self._vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
542 # def pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
543 # if xaxis is None: xaxis = self.axes["x"]
544 # if yaxis is None: yaxis = self.axes["y"]
545 # if zaxis is None: zaxis = self.axes["z"]
546 # return self.vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
548 # def _vpos(self, vx, vy, vz):
549 # x, y, z = (vx - 0.5)*self._depth, (vy - 0.5)*self._width, (vz - 0.5)*self._height
550 # d0 = float(self.a[0]*self.b[1]*(z-self.eye[2])
551 # + self.a[2]*self.b[0]*(y-self.eye[1])
552 # + self.a[1]*self.b[2]*(x-self.eye[0])
553 # - self.a[2]*self.b[1]*(x-self.eye[0])
554 # - self.a[0]*self.b[2]*(y-self.eye[1])
555 # - self.a[1]*self.b[0]*(z-self.eye[2]))
556 # da = (self.eye[0]*self.b[1]*(z-self.eye[2])
557 # + self.eye[2]*self.b[0]*(y-self.eye[1])
558 # + self.eye[1]*self.b[2]*(x-self.eye[0])
559 # - self.eye[2]*self.b[1]*(x-self.eye[0])
560 # - self.eye[0]*self.b[2]*(y-self.eye[1])
561 # - self.eye[1]*self.b[0]*(z-self.eye[2]))
562 # db = (self.a[0]*self.eye[1]*(z-self.eye[2])
563 # + self.a[2]*self.eye[0]*(y-self.eye[1])
564 # + self.a[1]*self.eye[2]*(x-self.eye[0])
565 # - self.a[2]*self.eye[1]*(x-self.eye[0])
566 # - self.a[0]*self.eye[2]*(y-self.eye[1])
567 # - self.a[1]*self.eye[0]*(z-self.eye[2]))
568 # return da/d0 + self._xpos, db/d0 + self._ypos
570 # def vpos(self, vx, vy, vz):
571 # tx, ty = self._vpos(vx, vy, vz)
572 # return unit.t_pt(tx), unit.t_pt(ty)
574 # def xbaseline(self, axis, x1, x2, xaxis=None):
575 # if xaxis is None: xaxis = self.axes["x"]
576 # return self.vxbaseline(axis, xaxis.convert(x1), xaxis.convert(x2))
578 # def ybaseline(self, axis, y1, y2, yaxis=None):
579 # if yaxis is None: yaxis = self.axes["y"]
580 # return self.vybaseline(axis, yaxis.convert(y1), yaxis.convert(y2))
582 # def zbaseline(self, axis, z1, z2, zaxis=None):
583 # if zaxis is None: zaxis = self.axes["z"]
584 # return self.vzbaseline(axis, zaxis.convert(z1), zaxis.convert(z2))
586 # def vxbaseline(self, axis, v1, v2):
587 # return (path._line(*(self._vpos(v1, 0, 0) + self._vpos(v2, 0, 0))) +
588 # path._line(*(self._vpos(v1, 0, 1) + self._vpos(v2, 0, 1))) +
589 # path._line(*(self._vpos(v1, 1, 1) + self._vpos(v2, 1, 1))) +
590 # path._line(*(self._vpos(v1, 1, 0) + self._vpos(v2, 1, 0))))
592 # def vybaseline(self, axis, v1, v2):
593 # return (path._line(*(self._vpos(0, v1, 0) + self._vpos(0, v2, 0))) +
594 # path._line(*(self._vpos(0, v1, 1) + self._vpos(0, v2, 1))) +
595 # path._line(*(self._vpos(1, v1, 1) + self._vpos(1, v2, 1))) +
596 # path._line(*(self._vpos(1, v1, 0) + self._vpos(1, v2, 0))))
598 # def vzbaseline(self, axis, v1, v2):
599 # return (path._line(*(self._vpos(0, 0, v1) + self._vpos(0, 0, v2))) +
600 # path._line(*(self._vpos(0, 1, v1) + self._vpos(0, 1, v2))) +
601 # path._line(*(self._vpos(1, 1, v1) + self._vpos(1, 1, v2))) +
602 # path._line(*(self._vpos(1, 0, v1) + self._vpos(1, 0, v2))))
604 # def xgridpath(self, x, xaxis=None):
605 # assert 0
606 # if xaxis is None: xaxis = self.axes["x"]
607 # v = xaxis.convert(x)
608 # return path._line(self._xpos+v*self._width, self._ypos,
609 # self._xpos+v*self._width, self._ypos+self._height)
611 # def ygridpath(self, y, yaxis=None):
612 # assert 0
613 # if yaxis is None: yaxis = self.axes["y"]
614 # v = yaxis.convert(y)
615 # return path._line(self._xpos, self._ypos+v*self._height,
616 # self._xpos+self._width, self._ypos+v*self._height)
618 # def zgridpath(self, z, zaxis=None):
619 # assert 0
620 # if zaxis is None: zaxis = self.axes["z"]
621 # v = zaxis.convert(z)
622 # return path._line(self._xpos, self._zpos+v*self._height,
623 # self._xpos+self._width, self._zpos+v*self._height)
625 # def vxgridpath(self, v):
626 # return path.path(path._moveto(*self._vpos(v, 0, 0)),
627 # path._lineto(*self._vpos(v, 0, 1)),
628 # path._lineto(*self._vpos(v, 1, 1)),
629 # path._lineto(*self._vpos(v, 1, 0)),
630 # path.closepath())
632 # def vygridpath(self, v):
633 # return path.path(path._moveto(*self._vpos(0, v, 0)),
634 # path._lineto(*self._vpos(0, v, 1)),
635 # path._lineto(*self._vpos(1, v, 1)),
636 # path._lineto(*self._vpos(1, v, 0)),
637 # path.closepath())
639 # def vzgridpath(self, v):
640 # return path.path(path._moveto(*self._vpos(0, 0, v)),
641 # path._lineto(*self._vpos(0, 1, v)),
642 # path._lineto(*self._vpos(1, 1, v)),
643 # path._lineto(*self._vpos(1, 0, v)),
644 # path.closepath())
646 # def _addpos(self, x, y, dx, dy):
647 # assert 0
648 # return x+dx, y+dy
650 # def _connect(self, x1, y1, x2, y2):
651 # assert 0
652 # return path._lineto(x2, y2)
654 # def doaxes(self):
655 # self.dolayout()
656 # if not self.removedomethod(self.doaxes): return
657 # axesdist_pt = unit.topt(self.axesdist)
658 # XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0])
659 # YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1])
660 # ZPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[2])
661 # items = list(self.axes.items())
662 # items.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
663 # for key, axis in items:
664 # num = self.keynum(key)
665 # num2 = 1 - num % 2 # x1 -> 0, x2 -> 1, x3 -> 0, x4 -> 1, ...
666 # num3 = 1 - 2 * (num % 2) # x1 -> -1, x2 -> 1, x3 -> -1, x4 -> 1, ...
667 # if XPattern.match(key):
668 # axis.vypos = 0
669 # axis.vzpos = 0
670 # axis._vtickpoint = self._vxtickpoint
671 # axis.vgridpath = self.vxgridpath
672 # axis.vbaseline = self.vxbaseline
673 # axis.vtickdirection = self.vxtickdirection
674 # elif YPattern.match(key):
675 # axis.vxpos = 0
676 # axis.vzpos = 0
677 # axis._vtickpoint = self._vytickpoint
678 # axis.vgridpath = self.vygridpath
679 # axis.vbaseline = self.vybaseline
680 # axis.vtickdirection = self.vytickdirection
681 # elif ZPattern.match(key):
682 # axis.vxpos = 0
683 # axis.vypos = 0
684 # axis._vtickpoint = self._vztickpoint
685 # axis.vgridpath = self.vzgridpath
686 # axis.vbaseline = self.vzbaseline
687 # axis.vtickdirection = self.vztickdirection
688 # else:
689 # raise ValueError("Axis key '%s' not allowed" % key)
690 # if axis.painter is not None:
691 # axis.dopaint(self)
692 # # if XPattern.match(key):
693 # # self._xaxisextents[num2] += axis._extent
694 # # needxaxisdist[num2] = 1
695 # # if YPattern.match(key):
696 # # self._yaxisextents[num2] += axis._extent
697 # # needyaxisdist[num2] = 1
699 # def __init__(self, tex, xpos=0, ypos=0, width=None, height=None, depth=None,
700 # phi=30, theta=30, distance=1,
701 # backgroundattrs=None, axesdist=0.8*unit.v_cm, **axes):
702 # canvas.canvas.__init__(self)
703 # self.tex = tex
704 # self.xpos = xpos
705 # self.ypos = ypos
706 # self._xpos = unit.topt(xpos)
707 # self._ypos = unit.topt(ypos)
708 # self._width = unit.topt(width)
709 # self._height = unit.topt(height)
710 # self._depth = unit.topt(depth)
711 # self.width = width
712 # self.height = height
713 # self.depth = depth
714 # if self._width <= 0: raise ValueError("width < 0")
715 # if self._height <= 0: raise ValueError("height < 0")
716 # if self._depth <= 0: raise ValueError("height < 0")
717 # self._distance = distance*math.sqrt(self._width*self._width+
718 # self._height*self._height+
719 # self._depth*self._depth)
720 # phi *= -math.pi/180
721 # theta *= math.pi/180
722 # self.a = (-math.sin(phi), math.cos(phi), 0)
723 # self.b = (-math.cos(phi)*math.sin(theta),
724 # -math.sin(phi)*math.sin(theta),
725 # math.cos(theta))
726 # self.eye = (self._distance*math.cos(phi)*math.cos(theta),
727 # self._distance*math.sin(phi)*math.cos(theta),
728 # self._distance*math.sin(theta))
729 # self.initaxes(axes)
730 # self.axesdist = axesdist
731 # self.backgroundattrs = backgroundattrs
733 # self.data = []
734 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
735 # self.haslayout = 0
736 # self.defaultstyle = {}
738 # def bbox(self):
739 # self.finish()
740 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)