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)
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."""
49 def __init__(self
, graph
, data
, styles
):
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
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
)
96 for columnname
in self
.usedcolumnnames
:
98 useitems
.append((columnname
, self
.data
.columns
[columnname
]))
100 useitems
.append((columnname
, self
.dynamiccolumns
[columnname
]))
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:
122 elif len(stylesdata
) == 1:
124 raise AttributeError("access to styledata attribute '%s' failed" % attr
)
127 class graph(canvas
.canvas
):
130 canvas
.canvas
.__init
__(self
)
137 def did(self
, method
, *args
, **kwargs
):
138 if not self
._calls
.has_key(method
):
139 self
._calls
[method
] = []
140 for callargs
in self
._calls
[method
]:
141 if callargs
== (args
, kwargs
):
143 self
._calls
[method
].append((args
, kwargs
))
148 return canvas
.canvas
.bbox(self
)
150 def registerPS(self
, registry
):
152 canvas
.canvas
.registerPS(self
, registry
)
154 def registerPDF(self
, registry
):
156 canvas
.canvas
.registerPDF(self
, registry
)
158 def outputPS(self
, file, writer
, context
):
160 canvas
.canvas
.outputPS(self
, file, writer
, context
)
162 def outputPDF(self
, file, writer
, context
):
164 canvas
.canvas
.outputPDF(self
, file, writer
, context
)
166 def plot(self
, data
, styles
=None, rangewarning
=1):
167 if self
.didranges
and rangewarnings
:
168 raise warnings
.warn("axes ranges have already been analysed; no further adjustments will be performed")
170 raise RuntimeError("can't add further data while data has already been processed")
183 styles
= d
.defaultstyles
184 elif styles
!= d
.defaultstyles
:
185 raise RuntimeError("defaultstyles differ")
188 plotitems
.append(plotitem(self
, d
, styles
))
189 self
.plotitems
.extend(plotitems
)
196 if self
.did(self
.doranges
):
198 for plotitem
in self
.plotitems
:
199 plotitem
.adjustaxesstatic(self
)
200 for plotitem
in self
.plotitems
:
201 plotitem
.makedynamicdata(self
)
202 for plotitem
in self
.plotitems
:
203 plotitem
.adjustaxesdynamic(self
)
207 if self
.did(self
.dodata
):
212 # count the usage of styles and perform selects
214 def stylesid(styles
):
215 return ":".join([str(id(style
)) for style
in styles
])
216 for plotitem
in self
.plotitems
:
218 styletotal
[stylesid(plotitem
.styles
)] += 1
220 styletotal
[stylesid(plotitem
.styles
)] = 1
222 for plotitem
in self
.plotitems
:
224 styleindex
[stylesid(plotitem
.styles
)] += 1
226 styleindex
[stylesid(plotitem
.styles
)] = 0
227 plotitem
.selectstyles(self
, styleindex
[stylesid(plotitem
.styles
)],
228 styletotal
[stylesid(plotitem
.styles
)])
230 for plotitem
in self
.plotitems
:
236 class graphxy(graph
):
238 def pos_pt(self
, x
, y
, xaxis
=None, yaxis
=None):
240 xaxis
= self
.axes
["x"]
242 yaxis
= self
.axes
["y"]
243 return self
.xpos_pt
+ xaxis
.convert(x
)*self
.width_pt
, self
.ypos_pt
+ yaxis
.convert(y
)*self
.height_pt
245 def pos(self
, x
, y
, xaxis
=None, yaxis
=None):
247 xaxis
= self
.axes
["x"]
249 yaxis
= self
.axes
["y"]
250 return self
.xpos
+ xaxis
.convert(x
)*self
.width
, self
.ypos
+ yaxis
.convert(y
)*self
.height
252 def vpos_pt(self
, vx
, vy
):
253 return self
.xpos_pt
+ vx
*self
.width_pt
, self
.ypos_pt
+ vy
*self
.height_pt
255 def vpos(self
, vx
, vy
):
256 return self
.xpos
+ vx
*self
.width
, self
.ypos
+ vy
*self
.height
258 def vgeodesic(self
, vx1
, vy1
, vx2
, vy2
):
259 """returns a geodesic path between two points in graph coordinates"""
260 return path
.line_pt(self
.xpos_pt
+ vx1
*self
.width_pt
,
261 self
.ypos_pt
+ vy1
*self
.height_pt
,
262 self
.xpos_pt
+ vx2
*self
.width_pt
,
263 self
.ypos_pt
+ vy2
*self
.height_pt
)
265 def vgeodesic_el(self
, vx1
, vy1
, vx2
, vy2
):
266 """returns a geodesic path element between two points in graph coordinates"""
267 return path
.lineto_pt(self
.xpos_pt
+ vx2
*self
.width_pt
,
268 self
.ypos_pt
+ vy2
*self
.height_pt
)
270 def vcap_pt(self
, coordinate
, length_pt
, vx
, vy
):
271 """returns an error cap path for a given coordinate, lengths and
272 point in graph coordinates"""
274 return path
.line_pt(self
.xpos_pt
+ vx
*self
.width_pt
- 0.5*length_pt
,
275 self
.ypos_pt
+ vy
*self
.height_pt
,
276 self
.xpos_pt
+ vx
*self
.width_pt
+ 0.5*length_pt
,
277 self
.ypos_pt
+ vy
*self
.height_pt
)
278 elif coordinate
== 1:
279 return path
.line_pt(self
.xpos_pt
+ vx
*self
.width_pt
,
280 self
.ypos_pt
+ vy
*self
.height_pt
- 0.5*length_pt
,
281 self
.xpos_pt
+ vx
*self
.width_pt
,
282 self
.ypos_pt
+ vy
*self
.height_pt
+ 0.5*length_pt
)
284 raise ValueError("direction invalid")
286 def xvgridpath(self
, vx
):
287 return path
.line_pt(self
.xpos_pt
+ vx
*self
.width_pt
, self
.ypos_pt
,
288 self
.xpos_pt
+ vx
*self
.width_pt
, self
.ypos_pt
+ self
.height_pt
)
290 def yvgridpath(self
, vy
):
291 return path
.line_pt(self
.xpos_pt
, self
.ypos_pt
+ vy
*self
.height_pt
,
292 self
.xpos_pt
+ self
.width_pt
, self
.ypos_pt
+ vy
*self
.height_pt
)
294 def axistrafo(self
, axis
, t
):
295 c
= canvas
.canvas([t
])
296 c
.insert(axis
.canvas
)
299 def axisatv(self
, axis
, v
):
300 if axis
.positioner
.fixtickdirection
[0]:
302 self
.axistrafo(axis
, trafo
.translate_pt(self
.xpos_pt
+ v
*self
.width_pt
- axis
.positioner
.x1_pt
, 0))
305 self
.axistrafo(axis
, trafo
.translate_pt(0, self
.ypos_pt
+ v
*self
.height_pt
- axis
.positioner
.y1_pt
))
307 def doaxispositioner(self
, axisname
):
308 if self
.did(self
.doaxispositioner
, axisname
):
312 self
.axes
["x"].setpositioner(positioner
.lineaxispos_pt(self
.xpos_pt
, self
.ypos_pt
,
313 self
.xpos_pt
+ self
.width_pt
, self
.ypos_pt
,
314 (0, 1), self
.xvgridpath
))
315 elif axisname
== "x2":
316 self
.axes
["x2"].setpositioner(positioner
.lineaxispos_pt(self
.xpos_pt
, self
.ypos_pt
+ self
.height_pt
,
317 self
.xpos_pt
+ self
.width_pt
, self
.ypos_pt
+ self
.height_pt
,
318 (0, -1), self
.xvgridpath
))
319 elif axisname
== "y":
320 self
.axes
["y"].setpositioner(positioner
.lineaxispos_pt(self
.xpos_pt
, self
.ypos_pt
,
321 self
.xpos_pt
, self
.ypos_pt
+ self
.height_pt
,
322 (1, 0), self
.yvgridpath
))
323 elif axisname
== "y2":
324 self
.axes
["y2"].setpositioner(positioner
.lineaxispos_pt(self
.xpos_pt
+ self
.width_pt
, self
.ypos_pt
,
325 self
.xpos_pt
+ self
.width_pt
, self
.ypos_pt
+ self
.height_pt
,
326 (-1, 0), self
.yvgridpath
))
328 if axisname
[1:] == "3":
329 dependsonaxisname
= axisname
[0]
331 dependsonaxisname
= "%s%d" % (axisname
[0], int(axisname
[1:]) - 2)
332 self
.doaxiscreate(dependsonaxisname
)
333 sign
= 2*(int(axisname
[1:]) % 2) - 1
334 if axisname
[0] == "x":
335 y_pt
= self
.axes
[dependsonaxisname
].positioner
.y1_pt
- sign
* (self
.axes
[dependsonaxisname
].canvas
.extent_pt
+ self
.axesdist_pt
)
336 self
.axes
[axisname
].setpositioner(positioner
.lineaxispos_pt(self
.xpos_pt
, y_pt
,
337 self
.xpos_pt
+ self
.width_pt
, y_pt
,
338 (0, sign
), self
.xvgridpath
))
340 x_pt
= self
.axes
[dependsonaxisname
].positioner
.x1_pt
- sign
* (self
.axes
[dependsonaxisname
].canvas
.extent_pt
+ self
.axesdist_pt
)
341 self
.axes
[axisname
].setpositioner(positioner
.lineaxispos_pt(x_pt
, self
.ypos_pt
,
342 x_pt
, self
.ypos_pt
+ self
.height_pt
,
343 (sign
, 0), self
.yvgridpath
))
345 def doaxiscreate(self
, axisname
):
346 if self
.did(self
.doaxiscreate
, axisname
):
348 self
.doaxispositioner(axisname
)
349 self
.axes
[axisname
].create(self
.texrunner
)
352 if self
.did(self
.dolayout
):
354 for axisname
in self
.axes
.keys():
355 self
.doaxiscreate(axisname
)
356 if self
.xaxisat
is not None:
357 self
.axisatv(self
.axes
["x"], self
.axes
["y"].convert(self
.xaxisat
))
358 if self
.yaxisat
is not None:
359 self
.axisatv(self
.axes
["y"], self
.axes
["x"].convert(self
.yaxisat
))
361 def dobackground(self
):
362 if self
.did(self
.dobackground
):
364 if self
.backgroundattrs
is not None:
365 self
.draw(path
.rect_pt(self
.xpos_pt
, self
.ypos_pt
, self
.width_pt
, self
.height_pt
),
366 self
.backgroundattrs
)
369 if self
.did(self
.doaxes
):
373 for axis
in self
.axes
.values():
374 self
.insert(axis
.canvas
)
377 if self
.did(self
.dokey
):
381 if self
.key
is not None:
382 c
= self
.key
.paint(self
.plotitems
)
384 def parentchildalign(pmin
, pmax
, cmin
, cmax
, pos
, dist
, inside
):
385 ppos
= pmin
+0.5*(cmax
-cmin
)+dist
+pos
*(pmax
-pmin
-cmax
+cmin
-2*dist
)
386 cpos
= 0.5*(cmin
+cmax
)+(1-inside
)*(1-2*pos
)*(cmax
-cmin
+2*dist
)
388 x
= parentchildalign(self
.xpos_pt
, self
.xpos_pt
+self
.width_pt
,
389 bbox
.llx_pt
, bbox
.urx_pt
,
390 self
.key
.hpos
, unit
.topt(self
.key
.hdist
), self
.key
.hinside
)
391 y
= parentchildalign(self
.ypos_pt
, self
.ypos_pt
+self
.height_pt
,
392 bbox
.lly_pt
, bbox
.ury_pt
,
393 self
.key
.vpos
, unit
.topt(self
.key
.vdist
), self
.key
.vinside
)
394 self
.insert(c
, [trafo
.translate_pt(x
, y
)])
401 def __init__(self
, xpos
=0, ypos
=0, width
=None, height
=None, ratio
=goldenmean
,
402 key
=None, backgroundattrs
=None, axesdist
=0.8*unit
.v_cm
,
403 xaxisat
=None, yaxisat
=None, **axes
):
408 self
.xpos_pt
= unit
.topt(self
.xpos
)
409 self
.ypos_pt
= unit
.topt(self
.ypos
)
410 self
.xaxisat
= xaxisat
411 self
.yaxisat
= yaxisat
413 self
.backgroundattrs
= backgroundattrs
414 self
.axesdist_pt
= unit
.topt(axesdist
)
420 raise ValueError("specify width and/or height")
422 self
.width
= ratio
* self
.height
424 self
.height
= (1.0/ratio
) * self
.width
425 self
.width_pt
= unit
.topt(self
.width
)
426 self
.height_pt
= unit
.topt(self
.height
)
428 for axisname
, aaxis
in axes
.items():
429 if aaxis
is not None:
430 if not isinstance(aaxis
, axis
.linkedaxis
):
431 self
.axes
[axisname
] = axis
.anchoredaxis(aaxis
, axisname
)
433 self
.axes
[axisname
] = aaxis
434 for axisname
, axisat
in [("x", xaxisat
), ("y", yaxisat
)]:
435 okey
= axisname
+ "2"
436 if not axes
.has_key(axisname
):
437 if not axes
.has_key(okey
):
438 self
.axes
[axisname
] = axis
.anchoredaxis(axis
.linear(), axisname
)
439 self
.axes
[okey
] = axis
.linkedaxis(self
.axes
[axisname
], okey
)
441 self
.axes
[axisname
] = axis
.linkedaxis(self
.axes
[okey
], axisname
)
442 elif not axes
.has_key(okey
) and axisat
is None:
443 self
.axes
[okey
] = axis
.linkedaxis(self
.axes
[axisname
], okey
)
445 if self
.axes
.has_key("x"):
446 self
.xbasepath
= self
.axes
["x"].basepath
447 self
.xvbasepath
= self
.axes
["x"].vbasepath
448 self
.xgridpath
= self
.axes
["x"].gridpath
449 self
.xtickpoint_pt
= self
.axes
["x"].tickpoint_pt
450 self
.xtickpoint
= self
.axes
["x"].tickpoint
451 self
.xvtickpoint_pt
= self
.axes
["x"].vtickpoint_pt
452 self
.xvtickpoint
= self
.axes
["x"].tickpoint
453 self
.xtickdirection
= self
.axes
["x"].tickdirection
454 self
.xvtickdirection
= self
.axes
["x"].vtickdirection
456 if self
.axes
.has_key("y"):
457 self
.ybasepath
= self
.axes
["y"].basepath
458 self
.yvbasepath
= self
.axes
["y"].vbasepath
459 self
.ygridpath
= self
.axes
["y"].gridpath
460 self
.ytickpoint_pt
= self
.axes
["y"].tickpoint_pt
461 self
.ytickpoint
= self
.axes
["y"].tickpoint
462 self
.yvtickpoint_pt
= self
.axes
["y"].vtickpoint_pt
463 self
.yvtickpoint
= self
.axes
["y"].tickpoint
464 self
.ytickdirection
= self
.axes
["y"].tickdirection
465 self
.yvtickdirection
= self
.axes
["y"].vtickdirection
467 self
.axesnames
= ([], [])
468 for axisname
, aaxis
in self
.axes
.items():
469 if axisname
[0] not in "xy" or (len(axisname
) != 1 and (not axisname
[1:].isdigit() or
470 axisname
[1:] == "1")):
471 raise ValueError("invalid axis name")
472 if axisname
[0] == "x":
473 self
.axesnames
[0].append(axisname
)
475 self
.axesnames
[1].append(axisname
)
476 aaxis
.setcreatecall(self
.doaxiscreate
, axisname
)
479 # some thoughts, but deferred right now
481 # class graphxyz(graphxy):
483 # axisnames = "x", "y", "z"
485 # def _vxtickpoint(self, axis, v):
486 # return self._vpos(v, axis.vypos, axis.vzpos)
488 # def _vytickpoint(self, axis, v):
489 # return self._vpos(axis.vxpos, v, axis.vzpos)
491 # def _vztickpoint(self, axis, v):
492 # return self._vpos(axis.vxpos, axis.vypos, v)
494 # def vxtickdirection(self, axis, v):
495 # x1, y1 = self._vpos(v, axis.vypos, axis.vzpos)
496 # x2, y2 = self._vpos(v, 0.5, 0)
497 # dx, dy = x1 - x2, y1 - y2
498 # norm = math.hypot(dx, dy)
499 # return dx/norm, dy/norm
501 # def vytickdirection(self, axis, v):
502 # x1, y1 = self._vpos(axis.vxpos, v, axis.vzpos)
503 # x2, y2 = self._vpos(0.5, v, 0)
504 # dx, dy = x1 - x2, y1 - y2
505 # norm = math.hypot(dx, dy)
506 # return dx/norm, dy/norm
508 # def vztickdirection(self, axis, v):
510 # x1, y1 = self._vpos(axis.vxpos, axis.vypos, v)
511 # x2, y2 = self._vpos(0.5, 0.5, v)
512 # dx, dy = x1 - x2, y1 - y2
513 # norm = math.hypot(dx, dy)
514 # return dx/norm, dy/norm
516 # def _pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
517 # if xaxis is None: xaxis = self.axes["x"]
518 # if yaxis is None: yaxis = self.axes["y"]
519 # if zaxis is None: zaxis = self.axes["z"]
520 # return self._vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
522 # def pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None):
523 # if xaxis is None: xaxis = self.axes["x"]
524 # if yaxis is None: yaxis = self.axes["y"]
525 # if zaxis is None: zaxis = self.axes["z"]
526 # return self.vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(z))
528 # def _vpos(self, vx, vy, vz):
529 # x, y, z = (vx - 0.5)*self._depth, (vy - 0.5)*self._width, (vz - 0.5)*self._height
530 # d0 = float(self.a[0]*self.b[1]*(z-self.eye[2])
531 # + self.a[2]*self.b[0]*(y-self.eye[1])
532 # + self.a[1]*self.b[2]*(x-self.eye[0])
533 # - self.a[2]*self.b[1]*(x-self.eye[0])
534 # - self.a[0]*self.b[2]*(y-self.eye[1])
535 # - self.a[1]*self.b[0]*(z-self.eye[2]))
536 # da = (self.eye[0]*self.b[1]*(z-self.eye[2])
537 # + self.eye[2]*self.b[0]*(y-self.eye[1])
538 # + self.eye[1]*self.b[2]*(x-self.eye[0])
539 # - self.eye[2]*self.b[1]*(x-self.eye[0])
540 # - self.eye[0]*self.b[2]*(y-self.eye[1])
541 # - self.eye[1]*self.b[0]*(z-self.eye[2]))
542 # db = (self.a[0]*self.eye[1]*(z-self.eye[2])
543 # + self.a[2]*self.eye[0]*(y-self.eye[1])
544 # + self.a[1]*self.eye[2]*(x-self.eye[0])
545 # - self.a[2]*self.eye[1]*(x-self.eye[0])
546 # - self.a[0]*self.eye[2]*(y-self.eye[1])
547 # - self.a[1]*self.eye[0]*(z-self.eye[2]))
548 # return da/d0 + self._xpos, db/d0 + self._ypos
550 # def vpos(self, vx, vy, vz):
551 # tx, ty = self._vpos(vx, vy, vz)
552 # return unit.t_pt(tx), unit.t_pt(ty)
554 # def xbaseline(self, axis, x1, x2, xaxis=None):
555 # if xaxis is None: xaxis = self.axes["x"]
556 # return self.vxbaseline(axis, xaxis.convert(x1), xaxis.convert(x2))
558 # def ybaseline(self, axis, y1, y2, yaxis=None):
559 # if yaxis is None: yaxis = self.axes["y"]
560 # return self.vybaseline(axis, yaxis.convert(y1), yaxis.convert(y2))
562 # def zbaseline(self, axis, z1, z2, zaxis=None):
563 # if zaxis is None: zaxis = self.axes["z"]
564 # return self.vzbaseline(axis, zaxis.convert(z1), zaxis.convert(z2))
566 # def vxbaseline(self, axis, v1, v2):
567 # return (path._line(*(self._vpos(v1, 0, 0) + self._vpos(v2, 0, 0))) +
568 # path._line(*(self._vpos(v1, 0, 1) + self._vpos(v2, 0, 1))) +
569 # path._line(*(self._vpos(v1, 1, 1) + self._vpos(v2, 1, 1))) +
570 # path._line(*(self._vpos(v1, 1, 0) + self._vpos(v2, 1, 0))))
572 # def vybaseline(self, axis, v1, v2):
573 # return (path._line(*(self._vpos(0, v1, 0) + self._vpos(0, v2, 0))) +
574 # path._line(*(self._vpos(0, v1, 1) + self._vpos(0, v2, 1))) +
575 # path._line(*(self._vpos(1, v1, 1) + self._vpos(1, v2, 1))) +
576 # path._line(*(self._vpos(1, v1, 0) + self._vpos(1, v2, 0))))
578 # def vzbaseline(self, axis, v1, v2):
579 # return (path._line(*(self._vpos(0, 0, v1) + self._vpos(0, 0, v2))) +
580 # path._line(*(self._vpos(0, 1, v1) + self._vpos(0, 1, v2))) +
581 # path._line(*(self._vpos(1, 1, v1) + self._vpos(1, 1, v2))) +
582 # path._line(*(self._vpos(1, 0, v1) + self._vpos(1, 0, v2))))
584 # def xgridpath(self, x, xaxis=None):
586 # if xaxis is None: xaxis = self.axes["x"]
587 # v = xaxis.convert(x)
588 # return path._line(self._xpos+v*self._width, self._ypos,
589 # self._xpos+v*self._width, self._ypos+self._height)
591 # def ygridpath(self, y, yaxis=None):
593 # if yaxis is None: yaxis = self.axes["y"]
594 # v = yaxis.convert(y)
595 # return path._line(self._xpos, self._ypos+v*self._height,
596 # self._xpos+self._width, self._ypos+v*self._height)
598 # def zgridpath(self, z, zaxis=None):
600 # if zaxis is None: zaxis = self.axes["z"]
601 # v = zaxis.convert(z)
602 # return path._line(self._xpos, self._zpos+v*self._height,
603 # self._xpos+self._width, self._zpos+v*self._height)
605 # def vxgridpath(self, v):
606 # return path.path(path._moveto(*self._vpos(v, 0, 0)),
607 # path._lineto(*self._vpos(v, 0, 1)),
608 # path._lineto(*self._vpos(v, 1, 1)),
609 # path._lineto(*self._vpos(v, 1, 0)),
612 # def vygridpath(self, v):
613 # return path.path(path._moveto(*self._vpos(0, v, 0)),
614 # path._lineto(*self._vpos(0, v, 1)),
615 # path._lineto(*self._vpos(1, v, 1)),
616 # path._lineto(*self._vpos(1, v, 0)),
619 # def vzgridpath(self, v):
620 # return path.path(path._moveto(*self._vpos(0, 0, v)),
621 # path._lineto(*self._vpos(0, 1, v)),
622 # path._lineto(*self._vpos(1, 1, v)),
623 # path._lineto(*self._vpos(1, 0, v)),
626 # def _addpos(self, x, y, dx, dy):
630 # def _connect(self, x1, y1, x2, y2):
632 # return path._lineto(x2, y2)
636 # if not self.removedomethod(self.doaxes): return
637 # axesdist_pt = unit.topt(self.axesdist)
638 # XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0])
639 # YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1])
640 # ZPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[2])
641 # items = list(self.axes.items())
642 # items.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
643 # for key, axis in items:
644 # num = self.keynum(key)
645 # num2 = 1 - num % 2 # x1 -> 0, x2 -> 1, x3 -> 0, x4 -> 1, ...
646 # num3 = 1 - 2 * (num % 2) # x1 -> -1, x2 -> 1, x3 -> -1, x4 -> 1, ...
647 # if XPattern.match(key):
650 # axis._vtickpoint = self._vxtickpoint
651 # axis.vgridpath = self.vxgridpath
652 # axis.vbaseline = self.vxbaseline
653 # axis.vtickdirection = self.vxtickdirection
654 # elif YPattern.match(key):
657 # axis._vtickpoint = self._vytickpoint
658 # axis.vgridpath = self.vygridpath
659 # axis.vbaseline = self.vybaseline
660 # axis.vtickdirection = self.vytickdirection
661 # elif ZPattern.match(key):
664 # axis._vtickpoint = self._vztickpoint
665 # axis.vgridpath = self.vzgridpath
666 # axis.vbaseline = self.vzbaseline
667 # axis.vtickdirection = self.vztickdirection
669 # raise ValueError("Axis key '%s' not allowed" % key)
670 # if axis.painter is not None:
672 # # if XPattern.match(key):
673 # # self._xaxisextents[num2] += axis._extent
674 # # needxaxisdist[num2] = 1
675 # # if YPattern.match(key):
676 # # self._yaxisextents[num2] += axis._extent
677 # # needyaxisdist[num2] = 1
679 # def __init__(self, tex, xpos=0, ypos=0, width=None, height=None, depth=None,
680 # phi=30, theta=30, distance=1,
681 # backgroundattrs=None, axesdist=0.8*unit.v_cm, **axes):
682 # canvas.canvas.__init__(self)
686 # self._xpos = unit.topt(xpos)
687 # self._ypos = unit.topt(ypos)
688 # self._width = unit.topt(width)
689 # self._height = unit.topt(height)
690 # self._depth = unit.topt(depth)
692 # self.height = height
694 # if self._width <= 0: raise ValueError("width < 0")
695 # if self._height <= 0: raise ValueError("height < 0")
696 # if self._depth <= 0: raise ValueError("height < 0")
697 # self._distance = distance*math.sqrt(self._width*self._width+
698 # self._height*self._height+
699 # self._depth*self._depth)
700 # phi *= -math.pi/180
701 # theta *= math.pi/180
702 # self.a = (-math.sin(phi), math.cos(phi), 0)
703 # self.b = (-math.cos(phi)*math.sin(theta),
704 # -math.sin(phi)*math.sin(theta),
706 # self.eye = (self._distance*math.cos(phi)*math.cos(theta),
707 # self._distance*math.sin(phi)*math.cos(theta),
708 # self._distance*math.sin(theta))
709 # self.initaxes(axes)
710 # self.axesdist = axesdist
711 # self.backgroundattrs = backgroundattrs
714 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
716 # self.defaultstyle = {}
720 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)