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)
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."""
48 def __init__(self
, graph
, data
, styles
):
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
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
)
94 for columnname
in self
.usedcolumnnames
:
96 useitems
.append((columnname
, self
.dynamiccolumns
[columnname
]))
98 useitems
.append((columnname
, self
.data
.columns
[columnname
]))
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:
120 elif len(stylesdata
) == 1:
122 raise AttributeError("access to styledata attribute '%s' failed" % attr
)
125 class graph(canvas
.canvas
):
128 canvas
.canvas
.__init
__(self
)
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
):
141 self
._calls
[method
].append((args
, kwargs
))
146 return canvas
.canvas
.bbox(self
)
148 def registerPS(self
, registry
):
150 canvas
.canvas
.registerPS(self
, registry
)
152 def registerPDF(self
, registry
):
154 canvas
.canvas
.registerPDF(self
, registry
)
156 def processPS(self
, file, writer
, context
, registry
, bbox
):
158 canvas
.canvas
.processPS(self
, file, writer
, context
, registry
, bbox
)
160 def processPDF(self
, file, writer
, context
, registry
, bbox
):
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")
168 raise RuntimeError("can't plot further data after dodata() has been executed")
181 styles
= d
.defaultstyles
182 elif styles
!= d
.defaultstyles
:
183 raise RuntimeError("defaultstyles differ")
186 plotitems
.append(plotitem(self
, d
, styles
))
187 self
.plotitems
.extend(plotitems
)
189 for aplotitem
in plotitems
:
190 aplotitem
.makedynamicdata(self
)
197 if self
.did(self
.doranges
):
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
)
208 raise NotImplementedError
210 def dobackground(self
):
211 raise NotImplementedError
214 raise NotImplementedError
217 if self
.did(self
.dodata
):
222 # count the usage of styles and perform selects
224 def stylesid(styles
):
225 return ":".join([str(id(style
)) for style
in styles
])
226 for plotitem
in self
.plotitems
:
228 styletotal
[stylesid(plotitem
.styles
)] += 1
230 styletotal
[stylesid(plotitem
.styles
)] = 1
232 for plotitem
in self
.plotitems
:
234 styleindex
[stylesid(plotitem
.styles
)] += 1
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
:
246 raise NotImplementedError
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
):
264 self
.xpos_pt
= unit
.topt(self
.xpos
)
265 self
.ypos_pt
= unit
.topt(self
.ypos
)
266 self
.xaxisat
= xaxisat
267 self
.yaxisat
= yaxisat
269 self
.backgroundattrs
= backgroundattrs
270 self
.axesdist_pt
= unit
.topt(axesdist
)
276 raise ValueError("specify width and/or height")
278 self
.width
= ratio
* self
.height
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
)
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
)
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
)
331 self
.axesnames
[1].append(axisname
)
332 aaxis
.setcreatecall(self
.doaxiscreate
, axisname
)
335 def pos_pt(self
, x
, y
, xaxis
=None, yaxis
=None):
337 xaxis
= self
.axes
["x"]
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):
345 xaxis
= self
.axes
["x"]
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"""
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
)
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
)
400 def axisatv(self
, axis
, v
):
401 if axis
.positioner
.fixtickdirection
[0]:
403 self
.axistrafo(axis
, trafo
.translate_pt(self
.xpos_pt
+ v
*self
.width_pt
- axis
.positioner
.x1_pt
, 0))
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
):
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
))
429 if axisname
[1:] == "3":
430 dependsonaxisname
= axisname
[0]
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
))
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
):
449 self
.doaxispositioner(axisname
)
450 self
.axes
[axisname
].create()
453 if self
.did(self
.dolayout
):
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
):
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
)
470 if self
.did(self
.doaxes
):
474 for axis
in self
.axes
.values():
475 self
.insert(axis
.canvas
)
478 if self
.did(self
.dokey
):
482 if self
.key
is not None:
483 c
= self
.key
.paint(self
.plotitems
)
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
)
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):
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):
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):
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):
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)),
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)),
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)),
646 # def _addpos(self, x, y, dx, dy):
650 # def _connect(self, x1, y1, x2, y2):
652 # return path._lineto(x2, y2)
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):
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):
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):
684 # axis._vtickpoint = self._vztickpoint
685 # axis.vgridpath = self.vzgridpath
686 # axis.vbaseline = self.vzbaseline
687 # axis.vtickdirection = self.vztickdirection
689 # raise ValueError("Axis key '%s' not allowed" % key)
690 # if axis.painter is not None:
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)
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)
712 # self.height = height
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),
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
734 # self.domethods = [self.dolayout, self.dobackground, self.doaxes, self.dodata]
736 # self.defaultstyle = {}
740 # return bbox._bbox(self._xpos - 200, self._ypos - 200, self._xpos + 200, self._ypos + 200)