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 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
)
128 class graphxy(canvas
.canvas
):
130 def plot(self
, data
, styles
=None):
132 raise RuntimeError("layout setup was already performed")
145 styles
= d
.defaultstyles
146 elif styles
!= d
.defaultstyles
:
147 raise RuntimeError("defaultstyles differ")
150 plotitems
.append(plotitem(self
, d
, styles
))
151 self
.plotitems
.extend(plotitems
)
157 def pos_pt(self
, x
, y
, xaxis
=None, yaxis
=None):
159 xaxis
= self
.axes
["x"]
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):
166 xaxis
= self
.axes
["x"]
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"""
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
)
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
):
215 while key
[0] in string
.letters
:
221 def removedomethod(self
, method
):
225 self
.domethods
.remove(method
)
230 def axistrafo(self
, axis
, t
):
231 c
= canvas
.canvas([t
])
232 c
.insert(axis
.canvas
)
235 def axisatv(self
, axis
, v
):
236 if axis
.positioner
.fixtickdirection
[0]:
238 self
.axistrafo(axis
, trafo
.translate_pt(self
.xpos_pt
+ v
*self
.width_pt
- axis
.positioner
.x1_pt
, 0))
241 self
.axistrafo(axis
, trafo
.translate_pt(0, self
.ypos_pt
+ v
*self
.height_pt
- axis
.positioner
.y1_pt
))
244 if not self
.removedomethod(self
.dolayout
): return
246 # count the usage of styles and perform selects
248 def stylesid(styles
):
249 return ":".join([str(id(style
)) for style
in styles
])
250 for plotitem
in self
.plotitems
:
252 styletotal
[stylesid(plotitem
.styles
)] += 1
254 styletotal
[stylesid(plotitem
.styles
)] = 1
256 for plotitem
in self
.plotitems
:
258 styleindex
[stylesid(plotitem
.styles
)] += 1
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
)
272 keys
= list(self
.axes
.keys())
273 keys
.sort() #TODO: alphabetical sorting breaks for axis numbers bigger than 9
275 self
.axes
[key
].create(self
.texrunner
)
281 nextkey
= "%s%i" % (key
[0], (num
+2))
282 if self
.axes
.has_key(nextkey
):
283 sign
= 2*(num
% 2) - 1
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
)
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
))
303 def dobackground(self
):
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
)
312 if not self
.removedomethod(self
.doaxes
): return
313 for axis
in self
.axes
.values():
314 self
.insert(axis
.canvas
)
318 if not self
.removedomethod(self
.dodata
): return
319 for plotitem
in self
.plotitems
:
324 if not self
.removedomethod(self
.dokey
): return
325 if self
.key
is not None:
326 c
= self
.key
.paint(self
.plotitems
)
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
)
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
)])
341 while len(self
.domethods
):
344 def initwidthheight(self
, width
, height
, ratio
):
349 raise ValueError("specify width and/or height")
351 self
.width
= ratio
* self
.height
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
):
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
)
364 self
.axes
[key
] = aaxis
365 for key
, axisat
in [("x", self
.xaxisat
), ("y", self
.yaxisat
)]:
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
)
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")
418 self
.axesnames
[0].append(key
)
420 self
.axesnames
[1].append(key
)
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
)
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
)
437 self
.backgroundattrs
= backgroundattrs
438 self
.axesdist
= axesdist
439 self
.axesdist_pt
= unit
.topt(axesdist
)
441 self
.domethods
= [self
.dolayout
, self
.dobackground
, self
.doaxes
, self
.dodata
, self
.dokey
]
446 return canvas
.canvas
.bbox(self
)
448 def registerPS(self
, registry
):
450 canvas
.canvas
.registerPS(self
, registry
)
452 def registerPDF(self
, registry
):
454 canvas
.canvas
.registerPDF(self
, registry
)
456 def outputPS(self
, file, writer
, context
):
458 canvas
.canvas
.outputPS(self
, file, writer
, context
)
460 def outputPDF(self
, file, writer
, context
):
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):
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):
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):
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):
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)),
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)),
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)),
613 # def _addpos(self, x, y, dx, dy):
617 # def _connect(self, x1, y1, x2, y2):
619 # return path._lineto(x2, y2)
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):
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):
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):
651 # axis._vtickpoint = self._vztickpoint
652 # axis.vgridpath = self.vzgridpath
653 # axis.vbaseline = self.vzbaseline
654 # axis.vtickdirection = self.vztickdirection
656 # raise ValueError("Axis key '%s' not allowed" % key)
657 # if axis.painter is not None:
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)
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)
679 # self.height = height
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),
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
701 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
703 # self.defaultstyle = {}
707 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)