19 import re, codecs, os, platform, copy, itertools, math, cmath, random, sys, copy
23 if re.search(
"windows", platform.system(), re.I):
26 _default_directory = _winreg.QueryValueEx(_winreg.OpenKey(_winreg.HKEY_CURRENT_USER, \
27 r"Software\Microsoft\Windows\Current Version\Explorer\Shell Folders"),
"Desktop")[0]
32 _default_directory = os.path.expanduser(
"~") + os.sep +
"Desktop"
34 _default_fileName =
"tmp.svg"
37 _hacks[
"inkscape-text-vertical-shift"] =
False
39 def rgb(r, g, b, maximum=1.):
40 """Create an SVG color string "#xxyyzz" from r, g, and b.
42 r,g,b = 0 is black and r,g,b = maximum is white.
44 return "#%02x%02x%02x" % (
max(0,
min(r*255./maximum, 255)),
max(0,
min(g*255./maximum, 255)),
max(0,
min(b*255./maximum, 255)))
47 for name
in attr.keys():
48 name_colon = re.sub(
"__",
":", name)
49 if name_colon != name:
50 attr[name_colon] = attr[name]
54 name_dash = re.sub(
"_",
"-", name)
56 attr[name_dash] = attr[name]
63 """A tree representation of an SVG image or image fragment.
65 SVG(t, sub, sub, sub..., attribute=value)
67 t required SVG type name
68 sub optional list nested SVG elements or text/Unicode
69 attribute=value pairs optional keywords SVG attributes
71 In attribute names, "__" becomes ":" and "_" becomes "-".
75 <g id="mygroup" fill="blue">
76 <rect x="1" y="1" width="2" height="2" />
77 <rect x="3" y="3" width="2" height="2" />
82 >>> svg = SVG("g", SVG("rect", x=1, y=1, width=2, height=2), \
83 ... SVG("rect", x=3, y=3, width=2, height=2), \
84 ... id="mygroup", fill="blue")
86 Sub-elements and attributes may be accessed through tree-indexing:
88 >>> svg = SVG("text", SVG("tspan", "hello there"), stroke="none", fill="black")
96 Iteration is depth-first:
98 >>> svg = SVG("g", SVG("g", SVG("line", x1=0, y1=0, x2=1, y2=1)), \
99 ... SVG("text", SVG("tspan", "hello again")))
101 >>> for ti, s in svg:
102 ... print ti, repr(s)
105 (0, 0) <line x2=1 y1=0 x1=0 y2=1 />
110 (1,) <text (1 sub) />
111 (1, 0) <tspan (1 sub) />
112 (1, 0, 0) 'hello again'
114 Use "print" to navigate:
119 [0, 0] <line x2=1 y1=0 x1=0 y2=1 />
121 [1, 0] <tspan (1 sub) />
124 if len(t_sub) == 0:
raise TypeError(
"SVG element must have a t (SVG type)")
136 """Index is a list that descends tree, returning a sub-element if
137 it ends with a number and an attribute if it ends with a string."""
139 if isinstance(ti, (list, tuple)):
140 for i
in ti[:-1]: obj = obj[i]
143 if isinstance(ti, (int, long, slice)):
return obj.sub[ti]
144 else:
return obj.attr[ti]
147 """Index is a list that descends tree, returning a sub-element if
148 it ends with a number and an attribute if it ends with a string."""
150 if isinstance(ti, (list, tuple)):
151 for i
in ti[:-1]: obj = obj[i]
154 if isinstance(ti, (int, long, slice)): obj.sub[ti] = value
155 else: obj.attr[ti] = value
158 """Index is a list that descends tree, returning a sub-element if
159 it ends with a number and an attribute if it ends with a string."""
161 if isinstance(ti, (list, tuple)):
162 for i
in ti[:-1]: obj = obj[i]
165 if isinstance(ti, (int, long, slice)): del obj.sub[ti]
166 else: del obj.attr[ti]
169 """x in svg == True iff x is an attribute in svg."""
170 return value
in self.
attr
173 """x == y iff x represents the same SVG as y."""
174 if id(self) == id(other):
return True
175 return isinstance(other, SVG)
and self.
t == other.t
and self.
sub == other.sub
and self.
attr == other.attr
178 """x != y iff x does not represent the same SVG as y."""
179 return not (self == other)
182 """Appends x to the list of sub-elements (drawn last, overlaps
183 other primatives)."""
187 """Prepends x to the list of sub-elements (drawn first may be
188 overlapped by other primatives)."""
192 """Extends list of sub-elements by a list x."""
196 """Deep copy of SVG tree. Set shallow=True for a shallow copy."""
198 return copy.copy(self)
200 return copy.deepcopy(self)
204 """Manages SVG iteration."""
218 return self.
ti, self.
svg
220 if not isinstance(self.
svg, SVG):
raise StopIteration
223 if "iterators" not in self.__dict__:
225 for i, s
in enumerate(self.svg.sub):
226 self.iterators.append(self.__class__(s, self.
ti + (i,), self.
depth_limit))
227 for k, s
in self.svg.attr.items():
228 self.iterators.append(self.__class__(s, self.
ti + (k,), self.
depth_limit))
235 """Returns a depth-first generator over the SVG. If depth_limit
236 is a number, stop recursion at that depth."""
240 """Not implemented yet. Any ideas on how to do it?
242 Returns a breadth-first generator over the SVG. If depth_limit
243 is a number, stop recursion at that depth."""
244 raise NotImplementedError(
"Got an algorithm for breadth-first searching a tree without effectively copying the tree?")
248 def items(self, sub=True, attr=True, text=True):
249 """Get a recursively-generated list of tree-index, sub-element/attribute pairs.
251 If sub == False, do not show sub-elements.
252 If attr == False, do not show attributes.
253 If text == False, do not show text/Unicode sub-elements.
258 if isinstance(ti[-1], (int, long)):
259 if isinstance(s, basestring): show = text
263 if show: output.append((ti, s))
266 def keys(self, sub=True, attr=True, text=True):
267 """Get a recursively-generated list of tree-indexes.
269 If sub == False, do not show sub-elements.
270 If attr == False, do not show attributes.
271 If text == False, do not show text/Unicode sub-elements.
273 return [ti
for ti, s
in self.
items(sub, attr, text)]
275 def values(self, sub=True, attr=True, text=True):
276 """Get a recursively-generated list of sub-elements and attributes.
278 If sub == False, do not show sub-elements.
279 If attr == False, do not show attributes.
280 If text == False, do not show text/Unicode sub-elements.
282 return [s
for ti, s
in self.
items(sub, attr, text)]
287 """Print (actually, return a string of) the tree in a form useful for browsing."""
288 return self.
tree(sub=
True, attr=
False, text=
False)
290 def tree(self, depth_limit=None, sub=True, attr=True, text=True, tree_width=20, obj_width=80):
291 """Print (actually, return a string of) the tree in a form useful for browsing.
293 If depth_limit == a number, stop recursion at that depth.
294 If sub == False, do not show sub-elements.
295 If attr == False, do not show attributes.
296 If text == False, do not show text/Unicode sub-elements.
297 tree_width is the number of characters reserved for printing tree indexes.
298 obj_width is the number of characters reserved for printing sub-elements/attributes.
303 line =
"%s %s" % ((
"%%-%ds" % tree_width) % repr(
None), (
"%%-%ds" % obj_width) % (repr(self))[0:obj_width])
308 if isinstance(ti[-1], (int, long)):
309 if isinstance(s, basestring): show = text
314 line =
"%s %s" % ((
"%%-%ds" % tree_width) % repr(
list(ti)), (
"%%-%ds" % obj_width) % (
" "*len(ti) + repr(s))[0:obj_width])
317 return "\n".
join(output)
319 def xml(self, indent=" ", newl="\n", depth_limit=None, depth=0):
320 """Get an XML representation of the SVG.
322 indent string used for indenting
323 newl string used for newlines
324 If depth_limit == a number, stop recursion at that depth.
325 depth starting depth (not useful for users)
331 for n, v
in self.attr.items():
332 if isinstance(v, dict):
333 v =
"; ".
join([
"%s:%s" % (ni, vi)
for ni, vi
in v.items()])
334 elif isinstance(v, (list, tuple)):
336 attrstr.append(
" %s=%s" % (n, repr(v)))
337 attrstr =
"".
join(attrstr)
339 if len(self.
sub) == 0:
return "%s<%s%s />" % (indent * depth, self.
t, attrstr)
341 if depth_limit ==
None or depth_limit > depth:
344 if isinstance(s, SVG):
345 substr.append(s.xml(indent, newl, depth_limit, depth + 1) + newl)
346 elif isinstance(s, str):
347 substr.append(
"%s%s%s" % (indent * (depth + 1), s, newl))
349 substr.append(
"%s%s%s" % (indent * (depth + 1), repr(s), newl))
350 substr =
"".
join(substr)
352 return "%s<%s%s>%s%s%s</%s>" % (indent * depth, self.
t, attrstr, newl, substr, indent * depth, self.
t)
355 return "%s<%s (%d sub)%s />" % (indent * depth, self.
t, len(self.
sub), attrstr)
358 """Get an XML representation of the SVG that can be saved/rendered.
360 indent string used for indenting
361 newl string used for newlines
364 if self.
t ==
"svg": top = self
367 <?xml version="1.0" standalone="no"?>
368 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
370 """ + (
"".
join(top.__standalone_xml(indent, newl)))
373 output = [
u"<%s" % self.
t]
375 for n, v
in self.attr.items():
376 if isinstance(v, dict):
377 v =
"; ".
join([
"%s:%s" % (ni, vi)
for ni, vi
in v.items()])
378 elif isinstance(v, (list, tuple)):
380 output.append(
u" %s=\"%s\"" % (n, v))
382 if len(self.
sub) == 0:
383 output.append(
u" />%s%s" % (newl, newl))
386 elif self.
t ==
"text" or self.
t ==
"tspan" or self.
t ==
"style":
390 output.append(
u">%s%s" % (newl, newl))
393 if isinstance(s, SVG): output.extend(s.__standalone_xml(indent, newl))
394 else: output.append(unicode(s))
396 if self.
t ==
"tspan": output.append(
u"</%s>" % self.
t)
397 else: output.append(
u"</%s>%s%s" % (self.
t, newl, newl))
403 fileName = _default_fileName
404 if re.search(
"windows", platform.system(), re.I)
and not os.path.isabs(fileName):
405 fileName = _default_directory + os.sep + fileName
408 def save(self, fileName=None, encoding="utf-8", compresslevel=None):
409 """Save to a file for viewing. Note that svg.save() overwrites the file named _default_fileName.
411 fileName default=None note that _default_fileName will be overwritten if
412 no fileName is specified. If the extension
413 is ".svgz" or ".gz", the output will be gzipped
414 encoding default="utf-8" file encoding (default is Unicode)
415 compresslevel default=None if a number, the output will be gzipped with that
416 compression level (1-9, 1 being fastest and 9 most
421 if compresslevel !=
None or re.search(
"\.svgz$", fileName, re.I)
or re.search(
"\.gz$", fileName, re.I):
423 if compresslevel ==
None:
424 f = gzip.GzipFile(fileName,
"w")
426 f = gzip.GzipFile(fileName,
"w", compresslevel)
428 f = codecs.EncodedFile(f,
"utf-8", encoding)
433 f = codecs.open(fileName,
"w", encoding=encoding)
437 def inkview(self, fileName=None, encoding="utf-8"):
438 """View in "inkview", assuming that program is available on your system.
440 fileName default=None note that any file named _default_fileName will be
441 overwritten if no fileName is specified. If the extension
442 is ".svgz" or ".gz", the output will be gzipped
443 encoding default="utf-8" file encoding (default is Unicode)
446 self.
save(fileName, encoding)
447 os.spawnvp(os.P_NOWAIT,
"inkview", (
"inkview", fileName))
449 def inkscape(self, fileName=None, encoding="utf-8"):
450 """View in "inkscape", assuming that program is available on your system.
452 fileName default=None note that any file named _default_fileName will be
453 overwritten if no fileName is specified. If the extension
454 is ".svgz" or ".gz", the output will be gzipped
455 encoding default="utf-8" file encoding (default is Unicode)
458 self.
save(fileName, encoding)
459 os.spawnvp(os.P_NOWAIT,
"inkscape", (
"inkscape", fileName))
461 def firefox(self, fileName=None, encoding="utf-8"):
462 """View in "firefox", assuming that program is available on your system.
464 fileName default=None note that any file named _default_fileName will be
465 overwritten if no fileName is specified. If the extension
466 is ".svgz" or ".gz", the output will be gzipped
467 encoding default="utf-8" file encoding (default is Unicode)
470 self.
save(fileName, encoding)
471 os.spawnvp(os.P_NOWAIT,
"firefox", (
"firefox", fileName))
475 _canvas_defaults = {
"width":
"400px",
"height":
"400px",
"viewBox":
"0 0 100 100", \
476 "xmlns":
"http://www.w3.org/2000/svg",
"xmlns:xlink":
"http://www.w3.org/1999/xlink",
"version":
"1.1", \
477 "style": {
"stroke":
"black",
"fill":
"none",
"stroke-width":
"0.5pt",
"stroke-linejoin":
"round",
"text-anchor":
"middle"}, \
478 "font-family": [
"Helvetica",
"Arial",
"FreeSans",
"Sans",
"sans",
"sans-serif"], \
482 """Creates a top-level SVG object, allowing the user to control the
483 image size and aspect ratio.
485 canvas(sub, sub, sub..., attribute=value)
487 sub optional list nested SVG elements or text/Unicode
488 attribute=value pairs optional keywords SVG attributes
490 Default attribute values:
494 viewBox "0 0 100 100"
495 xmlns "http://www.w3.org/2000/svg"
496 xmlns:xlink "http://www.w3.org/1999/xlink"
498 style "stroke:black; fill:none; stroke-width:0.5pt; stroke-linejoin:round; text-anchor:middle"
499 font-family "Helvetica,Arial,FreeSans?,Sans,sans,sans-serif"
501 attributes =
dict(_canvas_defaults)
502 attributes.update(attr)
504 if sub ==
None or sub == ():
505 return SVG(
"svg", **attributes)
507 return SVG(
"svg", *sub, **attributes)
510 """Same as canvas(), but draws an outline around the drawable area,
511 so that you know how close your image is to the edges."""
512 svg =
canvas(*sub, **attr)
513 match = re.match(
"[, \t]*([0-9e.+\-]+)[, \t]+([0-9e.+\-]+)[, \t]+([0-9e.+\-]+)[, \t]+([0-9e.+\-]+)[, \t]*", svg[
"viewBox"])
514 if match ==
None:
raise ValueError(
"canvas viewBox is incorrectly formatted")
515 x, y, width, height = [float(x)
for x
in match.groups()]
516 svg.prepend(
SVG(
"rect", x=x, y=y, width=width, height=height, stroke=
"none", fill=
"cornsilk"))
517 svg.append(
SVG(
"rect", x=x, y=y, width=width, height=height, stroke=
"black", fill=
"none"))
520 def template(fileName, svg, replaceme="REPLACEME"):
521 """Loads an SVG image from a file, replacing instances of
522 <REPLACEME /> with a given svg object.
524 fileName required name of the template SVG
525 svg required SVG object for replacement
526 replaceme default="REPLACEME" fake SVG element to be replaced by the given object
528 >>> print load("template.svg")
529 None <svg (2 sub) style=u'stroke:black; fill:none; stroke-width:0.5pt; stroke-linejoi
530 [0] <rect height=u'100' width=u'100' stroke=u'none' y=u'0' x=u'0' fill=u'yellow'
533 >>> print template("template.svg", SVG("circle", cx=50, cy=50, r=30))
534 None <svg (2 sub) style=u'stroke:black; fill:none; stroke-width:0.5pt; stroke-linejoi
535 [0] <rect height=u'100' width=u'100' stroke=u'none' y=u'0' x=u'0' fill=u'yellow'
536 [1] <circle cy=50 cx=50 r=30 />
538 output =
load(fileName)
540 if isinstance(s, SVG)
and s.t == replaceme:
547 """Loads an SVG image from a file."""
551 """Loads an SVG image from a stream (can be a string or a file object)."""
553 from xml.sax
import handler, make_parser
554 from xml.sax.handler
import feature_namespaces, feature_external_ges, feature_external_pes
556 class ContentHandler(handler.ContentHandler):
560 self.all_whitespace = re.compile(
"^\s*$")
562 def startElement(self, name, attr):
564 s.attr =
dict(attr.items())
565 if len(self.stack) > 0:
566 last = self.stack[-1]
570 def characters(self, ch):
571 if not isinstance(ch, basestring)
or self.all_whitespace.match(ch) ==
None:
572 if len(self.stack) > 0:
573 last = self.stack[-1]
574 if len(last.sub) > 0
and isinstance(last.sub[-1], basestring):
575 last.sub[-1] = last.sub[-1] +
"\n" + ch
579 def endElement(self, name):
580 if len(self.stack) > 0:
581 last = self.stack[-1]
582 if isinstance(last, SVG)
and last.t ==
"style" and "type" in last.attr
and last.attr[
"type"] ==
"text/css" and len(last.sub) == 1
and isinstance(last.sub[0], basestring):
583 last.sub[0] =
"<![CDATA[\n" + last.sub[0] +
"]]>"
585 self.output = self.stack.pop()
587 ch = ContentHandler()
588 parser = make_parser()
589 parser.setContentHandler(ch)
590 parser.setFeature(feature_namespaces, 0)
591 parser.setFeature(feature_external_ges, 0)
597 def totrans(expr, vars=(
"x",
"y"), globals=
None, locals=
None):
598 """Converts to a coordinate transformation (a function that accepts
599 two arguments and returns two values).
601 expr required a string expression or a function
602 of two real or one complex value
603 vars default=("x", "y") independent variable names;
604 a singleton ("z",) is interpreted
606 globals default=None dict of global variables
607 locals default=None dict of local variables
611 if expr.__code__.co_argcount == 2:
614 elif expr.__code__.co_argcount == 1:
615 split =
lambda z: (z.real, z.imag)
616 output =
lambda x, y:
split(expr(x + y*1j))
617 output.__name__ = expr.__name__
621 raise TypeError(
"must be a function of 2 or 1 variables")
625 if globals !=
None: g.update(globals)
626 output = eval(
"lambda %s, %s: (%s)" % (vars[0], vars[1], expr), g, locals)
627 output.__name__ =
"%s,%s -> %s" % (vars[0], vars[1], expr)
632 if globals !=
None: g.update(globals)
633 output = eval(
"lambda %s: (%s)" % (vars[0], expr), g, locals)
634 split =
lambda z: (z.real, z.imag)
636 output2.__name__ =
"%s -> %s" % (vars[0], expr)
640 raise TypeError(
"vars must have 2 or 1 elements")
642 def window(xmin, xmax, ymin, ymax, x=0, y=0, width=100, height=100, xlogbase=None, ylogbase=None, minusInfinity=-1000, flipx=False, flipy=True):
643 """Creates and returns a coordinate transformation (a function that
644 accepts two arguments and returns two values) that transforms from
645 (xmin, ymin), (xmax, ymax)
647 (x, y), (x + width, y + height).
649 xlogbase, ylogbase default=None, None if a number, transform
650 logarithmically with given base
651 minusInfinity default=-1000 what to return if
652 log(0 or negative) is attempted
653 flipx default=False if true, reverse the direction of x
654 flipy default=True if true, reverse the direction of y
656 (When composing windows, be sure to set flipy=False.)
676 if xlogbase !=
None and (ix1 <= 0.
or ix2 <= 0.):
raise ValueError(
"x range incompatible with log scaling: (%g, %g)" % (ix1, ix2))
678 if ylogbase !=
None and (iy1 <= 0.
or iy2 <= 0.):
raise ValueError(
"y range incompatible with log scaling: (%g, %g)" % (iy1, iy2))
680 def maybelog(t, it1, it2, ot1, ot2, logbase):
681 if t <= 0.:
return minusInfinity
683 return ot1 + 1.*(math.log(t, logbase) - math.log(it1, logbase))/(math.log(it2, logbase) - math.log(it1, logbase)) * (ot2 - ot1)
685 xlogstr, ylogstr =
"",
""
688 xfunc =
lambda x: ox1 + 1.*(x - ix1)/(ix2 - ix1) * (ox2 - ox1)
690 xfunc =
lambda x: maybelog(x, ix1, ix2, ox1, ox2, xlogbase)
691 xlogstr =
" xlog=%g" % xlogbase
694 yfunc =
lambda y: oy1 + 1.*(y - iy1)/(iy2 - iy1) * (oy2 - oy1)
696 yfunc =
lambda y: maybelog(y, iy1, iy2, oy1, oy2, ylogbase)
697 ylogstr =
" ylog=%g" % ylogbase
699 output =
lambda x, y: (xfunc(x), yfunc(y))
701 output.__name__ =
"(%g, %g), (%g, %g) -> (%g, %g), (%g, %g)%s%s" % (ix1, ix2, iy1, iy2, ox1, ox2, oy1, oy2, xlogstr, ylogstr)
705 """Creates and returns a coordinate transformation which rotates
706 around (cx,cy) by "angle" degrees."""
707 angle *= math.pi/180.
708 return lambda x, y: (cx + math.cos(angle)*(x - cx) - math.sin(angle)*(y - cy), cy + math.sin(angle)*(x - cx) + math.cos(angle)*(y - cy))
711 """Stores graphics primitive objects and applies a single coordinate
712 transformation to them. To compose coordinate systems, nest Fig
715 Fig(obj, obj, obj..., trans=function)
717 obj optional list a list of drawing primatives
718 trans default=None a coordinate transformation function
720 >>> fig = Fig(Line(0,0,1,1), Rect(0.2,0.2,0.8,0.8), trans="2*x, 2*y")
721 >>> print fig.SVG().xml()
723 <path d='M0 0L2 2' />
724 <path d='M0.4 0.4L1.6 0.4ZL1.6 1.6ZL0.4 1.6ZL0.4 0.4ZZ' />
726 >>> print Fig(fig, trans="x/2., y/2.").SVG().xml()
728 <path d='M0 0L1 1' />
729 <path d='M0.2 0.2L0.8 0.2ZL0.8 0.8ZL0.2 0.8ZL0.2 0.2ZZ' />
735 return "<Fig (%d items)>" % len(self.
d)
736 elif isinstance(self.
trans, basestring):
737 return "<Fig (%d items) x,y -> %s>" % (len(self.
d), self.
trans)
739 return "<Fig (%d items) %s>" % (len(self.
d), self.trans.__name__)
743 defaults = {
"trans":
None}
744 defaults.update(kwds)
747 self.
trans = kwds[
"trans"]; del kwds[
"trans"]
749 raise TypeError(
"Fig() got unexpected keyword arguments %s" % kwds.keys())
751 def SVG(self, trans=None):
752 """Apply the transformation "trans" and return an SVG object.
754 Coordinate transformations in nested Figs will be composed.
757 if trans ==
None: trans = self.
trans
758 if isinstance(trans, basestring): trans =
totrans(trans)
762 if isinstance(s, SVG):
765 elif isinstance(s, Fig):
767 if isinstance(strans, basestring): strans =
totrans(strans)
769 if trans ==
None: subtrans = strans
770 elif strans ==
None: subtrans = trans
771 else: subtrans =
lambda x,y:
trans(*strans(x, y))
773 output.sub += s.SVG(subtrans).sub
778 output.append(s.SVG(trans))
783 """Acts like Fig, but draws a coordinate axis. You also need to supply plot ranges.
785 Plot(xmin, xmax, ymin, ymax, obj, obj, obj..., keyword options...)
787 xmin, xmax required minimum and maximum x values (in the objs' coordinates)
788 ymin, ymax required minimum and maximum y values (in the objs' coordinates)
789 obj optional list drawing primatives
790 keyword options keyword list options defined below
792 The following are keyword options, with their default values:
794 trans None transformation function
795 x, y 5, 5 upper-left corner of the Plot in SVG coordinates
796 width, height 90, 90 width and height of the Plot in SVG coordinates
797 flipx, flipy False, True flip the sign of the coordinate axis
798 minusInfinity -1000 if an axis is logarithmic and an object is plotted at 0 or
799 a negative value, -1000 will be used as a stand-in for NaN
800 atx, aty 0, 0 the place where the coordinate axes cross
801 xticks -10 request ticks according to the standard tick specification
803 xminiticks True request miniticks according to the standard minitick
805 xlabels True request tick labels according to the standard tick label
807 xlogbase None if a number, the axis and transformation are logarithmic
808 with ticks at the given base (10 being the most common)
810 arrows None if a new identifier, create arrow markers and draw them
811 at the ends of the coordinate axes
812 text_attr {} a dictionary of attributes for label text
813 axis_attr {} a dictionary of attributes for the axis lines
818 return "<Plot (%d items)>" % len(self.
d)
820 return "<Plot (%d items) %s>" % (len(self.
d), self.trans.__name__)
822 def __init__(self, xmin, xmax, ymin, ymax, *d, **kwds):
823 self.xmin, self.xmax, self.ymin, self.
ymax = xmin, xmax, ymin, ymax
825 defaults = {
"trans":
None,
"x":5,
"y":5,
"width":90,
"height":90,
"flipx":
False,
"flipy":
True,
"minusInfinity":-1000, \
826 "atx":0,
"xticks":-10,
"xminiticks":
True,
"xlabels":
True,
"xlogbase":
None, \
827 "aty":0,
"yticks":-10,
"yminiticks":
True,
"ylabels":
True,
"ylogbase":
None, \
828 "arrows":
None,
"text_attr":{},
"axis_attr":{}}
829 defaults.update(kwds)
832 self.
trans = kwds[
"trans"]; del kwds[
"trans"]
833 self.
x = kwds[
"x"]; del kwds[
"x"]
834 self.
y = kwds[
"y"]; del kwds[
"y"]
835 self.
width = kwds[
"width"]; del kwds[
"width"]
836 self.
height = kwds[
"height"]; del kwds[
"height"]
837 self.
flipx = kwds[
"flipx"]; del kwds[
"flipx"]
838 self.
flipy = kwds[
"flipy"]; del kwds[
"flipy"]
840 self.
atx = kwds[
"atx"]; del kwds[
"atx"]
841 self.
xticks = kwds[
"xticks"]; del kwds[
"xticks"]
843 self.
xlabels = kwds[
"xlabels"]; del kwds[
"xlabels"]
844 self.
xlogbase = kwds[
"xlogbase"]; del kwds[
"xlogbase"]
845 self.
aty = kwds[
"aty"]; del kwds[
"aty"]
846 self.
yticks = kwds[
"yticks"]; del kwds[
"yticks"]
848 self.
ylabels = kwds[
"ylabels"]; del kwds[
"ylabels"]
849 self.
ylogbase = kwds[
"ylogbase"]; del kwds[
"ylogbase"]
850 self.
arrows = kwds[
"arrows"]; del kwds[
"arrows"]
851 self.
text_attr = kwds[
"text_attr"]; del kwds[
"text_attr"]
852 self.
axis_attr = kwds[
"axis_attr"]; del kwds[
"axis_attr"]
854 raise TypeError(
"Plot() got unexpected keyword arguments %s" % kwds.keys())
856 def SVG(self, trans=None):
857 """Apply the transformation "trans" and return an SVG object."""
858 if trans ==
None: trans = self.
trans
859 if isinstance(trans, basestring): trans =
totrans(trans)
864 d = [
Axes(self.xmin, self.xmax, self.ymin, self.
ymax, self.
atx, self.
aty, \
873 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
877 minitick_length = 0.75
878 text_xaxis_offset = 1.
879 text_yaxis_offset = 2.
880 text_xtitle_offset = 6.
881 text_ytitle_offset = 12.
884 return "<Frame (%d items)>" % len(self.
d)
886 def __init__(self, xmin, xmax, ymin, ymax, *d, **kwds):
887 """Acts like Fig, but draws a coordinate frame around the data. You also need to supply plot ranges.
889 Frame(xmin, xmax, ymin, ymax, obj, obj, obj..., keyword options...)
891 xmin, xmax required minimum and maximum x values (in the objs' coordinates)
892 ymin, ymax required minimum and maximum y values (in the objs' coordinates)
893 obj optional list drawing primatives
894 keyword options keyword list options defined below
896 The following are keyword options, with their default values:
898 x, y 20, 5 upper-left corner of the Frame in SVG coordinates
899 width, height 75, 80 width and height of the Frame in SVG coordinates
900 flipx, flipy False, True flip the sign of the coordinate axis
901 minusInfinity -1000 if an axis is logarithmic and an object is plotted at 0 or
902 a negative value, -1000 will be used as a stand-in for NaN
903 xtitle None if a string, label the x axis
904 xticks -10 request ticks according to the standard tick specification
906 xminiticks True request miniticks according to the standard minitick
908 xlabels True request tick labels according to the standard tick label
910 xlogbase None if a number, the axis and transformation are logarithmic
911 with ticks at the given base (10 being the most common)
913 text_attr {} a dictionary of attributes for label text
914 axis_attr {} a dictionary of attributes for the axis lines
917 self.xmin, self.xmax, self.ymin, self.
ymax = xmin, xmax, ymin, ymax
919 defaults = {
"x":20,
"y":5,
"width":75,
"height":80,
"flipx":
False,
"flipy":
True,
"minusInfinity":-1000, \
920 "xtitle":
None,
"xticks":-10,
"xminiticks":
True,
"xlabels":
True,
"x2labels":
None,
"xlogbase":
None, \
921 "ytitle":
None,
"yticks":-10,
"yminiticks":
True,
"ylabels":
True,
"y2labels":
None,
"ylogbase":
None, \
922 "text_attr":{},
"axis_attr":{}}
923 defaults.update(kwds)
926 self.
x = kwds[
"x"]; del kwds[
"x"]
927 self.
y = kwds[
"y"]; del kwds[
"y"]
928 self.
width = kwds[
"width"]; del kwds[
"width"]
929 self.
height = kwds[
"height"]; del kwds[
"height"]
930 self.
flipx = kwds[
"flipx"]; del kwds[
"flipx"]
931 self.
flipy = kwds[
"flipy"]; del kwds[
"flipy"]
933 self.
xtitle = kwds[
"xtitle"]; del kwds[
"xtitle"]
934 self.
xticks = kwds[
"xticks"]; del kwds[
"xticks"]
936 self.
xlabels = kwds[
"xlabels"]; del kwds[
"xlabels"]
937 self.
x2labels = kwds[
"x2labels"]; del kwds[
"x2labels"]
938 self.
xlogbase = kwds[
"xlogbase"]; del kwds[
"xlogbase"]
939 self.
ytitle = kwds[
"ytitle"]; del kwds[
"ytitle"]
940 self.
yticks = kwds[
"yticks"]; del kwds[
"yticks"]
942 self.
ylabels = kwds[
"ylabels"]; del kwds[
"ylabels"]
943 self.
y2labels = kwds[
"y2labels"]; del kwds[
"y2labels"]
944 self.
ylogbase = kwds[
"ylogbase"]; del kwds[
"ylogbase"]
947 self.text_attr.update(kwds[
"text_attr"]); del kwds[
"text_attr"]
950 self.axis_attr.update(kwds[
"axis_attr"]); del kwds[
"axis_attr"]
953 raise TypeError(
"Frame() got unexpected keyword arguments %s" % kwds.keys())
956 """Apply the window transformation and return an SVG object."""
961 left =
YAxis(self.ymin, self.
ymax, self.xmin, self.
yticks, self.
yminiticks, self.
ylabels, self.
ylogbase,
None,
None,
None, self.
text_attr, **self.
axis_attr)
962 right =
YAxis(self.ymin, self.
ymax, self.xmax, self.
yticks, self.
yminiticks, self.
y2labels, self.
ylogbase,
None,
None,
None, self.
text_attr, **self.
axis_attr)
963 bottom =
XAxis(self.xmin, self.xmax, self.ymin, self.
xticks, self.
xminiticks, self.
xlabels, self.
xlogbase,
None,
None,
None, self.
text_attr, **self.
axis_attr)
964 top =
XAxis(self.xmin, self.xmax, self.
ymax, self.
xticks, self.
xminiticks, self.
x2labels, self.
xlogbase,
None,
None,
None, self.
text_attr, **self.
axis_attr)
969 left.minitick_end = 0.
972 right.tick_start = 0.
974 right.minitick_start = 0.
977 right.text_attr[
"text-anchor"] =
"start"
979 bottom.tick_start = 0.
981 bottom.minitick_start = 0.
988 top.minitick_end = 0.
990 top.text_attr[
"dominant-baseline"] =
"text-after-edge"
1007 """Converts SVG("path", d="...") into Path(d=[...])."""
1008 if not isinstance(svg, SVG)
or svg.t !=
"path":
1009 raise TypeError(
"Only SVG <path /> objects can be converted into Paths")
1010 attr =
dict(svg.attr)
1013 for key
in attr.keys():
1014 if not isinstance(key, str):
1017 attr[str(key)] = value
1018 return Path(d, **attr)
1021 """Path represents an SVG path, an arbitrary set of curves and
1022 straight segments. Unlike SVG("path", d="..."), Path stores
1023 coordinates as a list of numbers, rather than a string, so that it is
1024 transformable in a Fig.
1026 Path(d, attribute=value)
1028 d required path data
1029 attribute=value pairs keyword list SVG attributes
1031 See http://www.w3.org/TR/SVG/paths.html for specification of paths
1034 Internally, Path data is a list of tuples with these definitions:
1036 * ("Z/z",): close the current path
1037 * ("H/h", x) or ("V/v", y): a horizontal or vertical line
1039 * ("M/m/L/l/T/t", x, y, global): moveto, lineto, or smooth
1040 quadratic curveto point (x, y). If global=True, (x, y) should
1042 * ("S/sQ/q", cx, cy, cglobal, x, y, global): polybezier or
1043 smooth quadratic curveto point (x, y) using (cx, cy) as a
1044 control point. If cglobal or global=True, (cx, cy) or (x, y)
1045 should not be transformed.
1046 * ("C/c", c1x, c1y, c1global, c2x, c2y, c2global, x, y, global):
1047 cubic curveto point (x, y) using (c1x, c1y) and (c2x, c2y) as
1048 control points. If c1global, c2global, or global=True, (c1x, c1y),
1049 (c2x, c2y), or (x, y) should not be transformed.
1050 * ("A/a", rx, ry, rglobal, x-axis-rotation, angle, large-arc-flag,
1051 sweep-flag, x, y, global): arcto point (x, y) using the
1052 aforementioned parameters.
1053 * (",/.", rx, ry, rglobal, angle, x, y, global): an ellipse at
1054 point (x, y) with radii (rx, ry). If angle is 0, the whole
1055 ellipse is drawn; otherwise, a partial ellipse is drawn.
1060 return "<Path (%d nodes) %s>" % (len(self.
d), self.
attr)
1063 if isinstance(d, basestring): self.
d = self.
parse(d)
1064 else: self.
d =
list(d)
1067 self.attr.update(attr)
1070 """Part of Path's text-command parsing algorithm; used internally."""
1071 while index < len(pathdata)
and pathdata[index]
in (
" ",
"\t",
"\r",
"\n",
","): index += 1
1072 return index, pathdata
1075 """Part of Path's text-command parsing algorithm; used internally."""
1078 if index >= len(pathdata):
return None, index, pathdata
1079 command = pathdata[index]
1080 if "A" <= command <=
"Z" or "a" <= command <=
"z":
1082 return command, index, pathdata
1084 return None, index, pathdata
1087 """Part of Path's text-command parsing algorithm; used internally."""
1090 if index >= len(pathdata):
return None, index, pathdata
1091 first_digit = pathdata[index]
1093 if "0" <= first_digit <=
"9" or first_digit
in (
"-",
"+",
"."):
1095 while index < len(pathdata)
and (
"0" <= pathdata[index] <=
"9" or pathdata[index]
in (
"-",
"+",
".",
"e",
"E")):
1100 return float(pathdata[start:end]), index, pathdata
1102 return None, index, pathdata
1105 """Part of Path's text-command parsing algorithm; used internally."""
1108 if index >= len(pathdata):
return None, index, pathdata
1109 first_digit = pathdata[index]
1111 if first_digit
in (
"0",
"1"):
1113 return int(first_digit), index, pathdata
1115 return None, index, pathdata
1118 """Parses text-commands, converting them into a list of tuples.
1119 Called by the constructor."""
1123 command, index, pathdata = self.
parse_command(index, pathdata)
1126 if command ==
None and index == len(pathdata):
break
1127 if command
in (
"Z",
"z"):
1128 output.append((command,))
1131 elif command
in (
"H",
"h",
"V",
"v"):
1132 errstring =
"Path command \"%s\" requires a number at index %d" % (command, index)
1133 num1, index, pathdata = self.
parse_number(index, pathdata)
1134 if num1 ==
None:
raise ValueError(errstring)
1137 output.append((command, num1))
1138 num1, index, pathdata = self.
parse_number(index, pathdata)
1141 elif command
in (
"M",
"m",
"L",
"l",
"T",
"t"):
1142 errstring =
"Path command \"%s\" requires an x,y pair at index %d" % (command, index)
1143 num1, index, pathdata = self.
parse_number(index, pathdata)
1144 num2, index, pathdata = self.
parse_number(index, pathdata)
1146 if num1 ==
None:
raise ValueError(errstring)
1149 if num2 ==
None:
raise ValueError(errstring)
1150 output.append((command, num1, num2,
False))
1152 num1, index, pathdata = self.
parse_number(index, pathdata)
1153 num2, index, pathdata = self.
parse_number(index, pathdata)
1156 elif command
in (
"S",
"s",
"Q",
"q"):
1157 errstring =
"Path command \"%s\" requires a cx,cy,x,y quadruplet at index %d" % (command, index)
1158 num1, index, pathdata = self.
parse_number(index, pathdata)
1159 num2, index, pathdata = self.
parse_number(index, pathdata)
1160 num3, index, pathdata = self.
parse_number(index, pathdata)
1161 num4, index, pathdata = self.
parse_number(index, pathdata)
1163 if num1 ==
None:
raise ValueError(errstring)
1166 if num2 ==
None or num3 ==
None or num4 ==
None:
raise ValueError(errstring)
1167 output.append((command, num1, num2,
False, num3, num4,
False))
1169 num1, index, pathdata = self.
parse_number(index, pathdata)
1170 num2, index, pathdata = self.
parse_number(index, pathdata)
1171 num3, index, pathdata = self.
parse_number(index, pathdata)
1172 num4, index, pathdata = self.
parse_number(index, pathdata)
1175 elif command
in (
"C",
"c"):
1176 errstring =
"Path command \"%s\" requires a c1x,c1y,c2x,c2y,x,y sextuplet at index %d" % (command, index)
1177 num1, index, pathdata = self.
parse_number(index, pathdata)
1178 num2, index, pathdata = self.
parse_number(index, pathdata)
1179 num3, index, pathdata = self.
parse_number(index, pathdata)
1180 num4, index, pathdata = self.
parse_number(index, pathdata)
1181 num5, index, pathdata = self.
parse_number(index, pathdata)
1182 num6, index, pathdata = self.
parse_number(index, pathdata)
1184 if num1 ==
None:
raise ValueError(errstring)
1187 if num2 ==
None or num3 ==
None or num4 ==
None or num5 ==
None or num6 ==
None:
raise ValueError(errstring)
1189 output.append((command, num1, num2,
False, num3, num4,
False, num5, num6,
False))
1191 num1, index, pathdata = self.
parse_number(index, pathdata)
1192 num2, index, pathdata = self.
parse_number(index, pathdata)
1193 num3, index, pathdata = self.
parse_number(index, pathdata)
1194 num4, index, pathdata = self.
parse_number(index, pathdata)
1195 num5, index, pathdata = self.
parse_number(index, pathdata)
1196 num6, index, pathdata = self.
parse_number(index, pathdata)
1199 elif command
in (
"A",
"a"):
1200 errstring =
"Path command \"%s\" requires a rx,ry,angle,large-arc-flag,sweep-flag,x,y septuplet at index %d" % (command, index)
1201 num1, index, pathdata = self.
parse_number(index, pathdata)
1202 num2, index, pathdata = self.
parse_number(index, pathdata)
1203 num3, index, pathdata = self.
parse_number(index, pathdata)
1206 num6, index, pathdata = self.
parse_number(index, pathdata)
1207 num7, index, pathdata = self.
parse_number(index, pathdata)
1209 if num1 ==
None:
raise ValueError(errstring)
1212 if num2 ==
None or num3 ==
None or num4 ==
None or num5 ==
None or num6 ==
None or num7 ==
None:
raise ValueError(errstring)
1214 output.append((command, num1, num2,
False, num3, num4, num5, num6, num7,
False))
1216 num1, index, pathdata = self.
parse_number(index, pathdata)
1217 num2, index, pathdata = self.
parse_number(index, pathdata)
1218 num3, index, pathdata = self.
parse_number(index, pathdata)
1221 num6, index, pathdata = self.
parse_number(index, pathdata)
1222 num7, index, pathdata = self.
parse_number(index, pathdata)
1227 """Apply the transformation "trans" and return an SVG object."""
1228 if isinstance(trans, basestring): trans =
totrans(trans)
1230 x, y, X, Y =
None,
None,
None,
None
1232 for datum
in self.
d:
1233 if not isinstance(datum, (tuple, list)):
1234 raise TypeError(
"pathdata elements must be tuples/lists")
1239 if command
in (
"Z",
"z"):
1240 x, y, X, Y =
None,
None,
None,
None
1244 elif command
in (
"H",
"h",
"V",
"v"):
1245 command, num1 = datum
1247 if command ==
"H" or (command ==
"h" and x ==
None): x = num1
1248 elif command ==
"h": x += num1
1249 elif command ==
"V" or (command ==
"v" and y ==
None): y = num1
1250 elif command ==
"v": y += num1
1252 if trans ==
None: X, Y = x, y
1253 else: X, Y = trans(x, y)
1255 output.append(
"L%g %g" % (X, Y))
1258 elif command
in (
"M",
"m",
"L",
"l",
"T",
"t"):
1259 command, num1, num2, isglobal12 = datum
1261 if trans ==
None or isglobal12:
1262 if command.isupper()
or X ==
None or Y ==
None:
1270 if command.isupper()
or x ==
None or y ==
None:
1277 COMMAND = command.capitalize()
1278 output.append(
"%s%g %g" % (COMMAND, X, Y))
1281 elif command
in (
"S",
"s",
"Q",
"q"):
1282 command, num1, num2, isglobal12, num3, num4, isglobal34 = datum
1284 if trans ==
None or isglobal12:
1285 if command.isupper()
or X ==
None or Y ==
None:
1292 if command.isupper()
or x ==
None or y ==
None:
1297 CX, CY = trans(cx, cy)
1299 if trans ==
None or isglobal34:
1300 if command.isupper()
or X ==
None or Y ==
None:
1308 if command.isupper()
or x ==
None or y ==
None:
1315 COMMAND = command.capitalize()
1316 output.append(
"%s%g %g %g %g" % (COMMAND, CX, CY, X, Y))
1319 elif command
in (
"C",
"c"):
1320 command, num1, num2, isglobal12, num3, num4, isglobal34, num5, num6, isglobal56 = datum
1322 if trans ==
None or isglobal12:
1323 if command.isupper()
or X ==
None or Y ==
None:
1324 C1X, C1Y = num1, num2
1330 if command.isupper()
or x ==
None or y ==
None:
1331 c1x, c1y = num1, num2
1335 C1X, C1Y = trans(c1x, c1y)
1337 if trans ==
None or isglobal34:
1338 if command.isupper()
or X ==
None or Y ==
None:
1339 C2X, C2Y = num3, num4
1345 if command.isupper()
or x ==
None or y ==
None:
1346 c2x, c2y = num3, num4
1350 C2X, C2Y = trans(c2x, c2y)
1352 if trans ==
None or isglobal56:
1353 if command.isupper()
or X ==
None or Y ==
None:
1361 if command.isupper()
or x ==
None or y ==
None:
1368 COMMAND = command.capitalize()
1369 output.append(
"%s%g %g %g %g %g %g" % (COMMAND, C1X, C1Y, C2X, C2Y, X, Y))
1372 elif command
in (
"A",
"a"):
1373 command, num1, num2, isglobal12, angle, large_arc_flag, sweep_flag, num3, num4, isglobal34 = datum
1378 if trans ==
None or isglobal34:
1379 if command.isupper()
or X ==
None or Y ==
None:
1387 if command.isupper()
or x ==
None or y ==
None:
1394 if x !=
None and y !=
None:
1395 centerx, centery = (x + oldx)/2., (y + oldy)/2.
1396 CENTERX, CENTERY = (X + OLDX)/2., (Y + OLDY)/2.
1398 if trans ==
None or isglobal12:
1405 RX, RY = trans(rx, ry)
1407 COMMAND = command.capitalize()
1408 output.append(
"%s%g %g %g %d %d %g %g" % (COMMAND, RX - CENTERX, RY - CENTERY, angle, large_arc_flag, sweep_flag, X, Y))
1410 elif command
in (
",",
"."):
1411 command, num1, num2, isglobal12, angle, num3, num4, isglobal34 = datum
1412 if trans ==
None or isglobal34:
1413 if command ==
"." or X ==
None or Y ==
None:
1421 if command ==
"." or x ==
None or y ==
None:
1428 if trans ==
None or isglobal12:
1435 RX, RY = trans(rx, ry)
1437 RX, RY = RX - X, RY - Y
1439 X1, Y1 = X + RX * math.cos(angle*math.pi/180.), Y + RX * math.sin(angle*math.pi/180.)
1440 X2, Y2 = X + RY * math.sin(angle*math.pi/180.), Y - RY * math.cos(angle*math.pi/180.)
1441 X3, Y3 = X - RX * math.cos(angle*math.pi/180.), Y - RX * math.sin(angle*math.pi/180.)
1442 X4, Y4 = X - RY * math.sin(angle*math.pi/180.), Y + RY * math.cos(angle*math.pi/180.)
1444 output.append(
"M%g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %g" \
1445 % (X1, Y1, RX, RY, angle, X2, Y2, RX, RY, angle, X3, Y3, RX, RY, angle, X4, Y4, RX, RY, angle, X1, Y1))
1447 return SVG(
"path", d=
"".
join(output), **self.
attr)
1451 def funcRtoC(expr, var="t", globals=None, locals=None):
1452 """Converts a complex "z(t)" string to a function acceptable for Curve.
1454 expr required string in the form "z(t)"
1455 var default="t" name of the independent variable
1456 globals default=None dict of global variables used in the expression;
1457 you may want to use Python's builtin globals()
1458 locals default=None dict of local variables
1461 if globals !=
None: g.update(globals)
1462 output = eval(
"lambda %s: (%s)" % (var, expr), g, locals)
1463 split =
lambda z: (z.real, z.imag)
1465 output2.__name__ =
"%s -> %s" % (var, expr)
1469 """Converts a "f(t), g(t)" string to a function acceptable for Curve.
1471 expr required string in the form "f(t), g(t)"
1472 var default="t" name of the independent variable
1473 globals default=None dict of global variables used in the expression;
1474 you may want to use Python's builtin globals()
1475 locals default=None dict of local variables
1478 if globals !=
None: g.update(globals)
1479 output = eval(
"lambda %s: (%s)" % (var, expr), g, locals)
1480 output.__name__ =
"%s -> %s" % (var, expr)
1483 def funcRtoR(expr, var="x", globals=None, locals=None):
1484 """Converts a "f(x)" string to a function acceptable for Curve.
1486 expr required string in the form "f(x)"
1487 var default="x" name of the independent variable
1488 globals default=None dict of global variables used in the expression;
1489 you may want to use Python's builtin globals()
1490 locals default=None dict of local variables
1493 if globals !=
None: g.update(globals)
1494 output = eval(
"lambda %s: (%s, %s)" % (var, var, expr), g, locals)
1495 output.__name__ =
"%s -> %s" % (var, expr)
1499 """Draws a parametric function as a path.
1501 Curve(f, low, high, loop, attribute=value)
1503 f required a Python callable or string in
1504 the form "f(t), g(t)"
1505 low, high required left and right endpoints
1506 loop default=False if True, connect the endpoints
1507 attribute=value pairs keyword list SVG attributes
1510 random_sampling =
True
1511 recursion_limit = 15
1512 linearity_limit = 0.05
1513 discontinuity_limit = 5.
1516 return "<Curve %s [%s, %s] %s>" % (self.
f, self.
low, self.
high, self.
attr)
1525 self.attr.update(attr)
1530 t, x, y, X, Y = self.
t, self.x, self.
y, self.X, self.
Y
1531 if t !=
None: t =
"%g" % t
1532 if x !=
None: x =
"%g" % x
1533 if y !=
None: y =
"%g" % y
1534 if X !=
None: X =
"%g" % X
1535 if Y !=
None: Y =
"%g" % Y
1536 return "<Curve.Sample t=%s x=%s y=%s X=%s Y=%s>" % (t, x, y, X, Y)
1540 def link(self, left, right): self.left, self.
right = left, right
1545 self.X, self.
Y = self.x, self.
y
1547 self.X, self.
Y = trans(self.x, self.
y)
1552 def __repr__(self):
return "<Curve.Samples (%d samples)>" % len(self)
1559 while current !=
None:
1561 current = current.right
1570 if current ==
None:
raise StopIteration
1571 self.
current = self.current.right
1576 """Adaptive-sampling algorithm that chooses the best sample points
1577 for a parametric curve between two endpoints and detects
1578 discontinuities. Called by SVG()."""
1579 oldrecursionlimit = sys.getrecursionlimit()
1583 if not (self.
low < self.
high):
raise ValueError(
"low must be less than high")
1585 low.link(
None, high)
1586 high.link(low,
None)
1588 low.evaluate(self.
f, trans)
1589 high.evaluate(self.
f, trans)
1596 while left.right !=
None:
1600 if right !=
None and left.X !=
None and left.Y !=
None and mid.X !=
None and mid.Y !=
None and right.X !=
None and right.Y !=
None:
1601 numer = left.X*(right.Y - mid.Y) + mid.X*(left.Y - right.Y) + right.X*(mid.Y - left.Y)
1602 denom = math.sqrt((left.X - right.X)**2 + (left.Y - right.Y)**2)
1616 sys.setrecursionlimit(oldrecursionlimit)
1619 """Part of the adaptive-sampling algorithm that chooses the best
1620 sample points. Called by sample()."""
1623 mid = self.
Sample(left.t + random.uniform(0.3, 0.7) * (right.t - left.t))
1625 mid = self.
Sample(left.t + 0.5 * (right.t - left.t))
1629 mid.link(left, right)
1630 mid.evaluate(self.
f, trans)
1633 numer = left.X*(right.Y - mid.Y) + mid.X*(left.Y - right.Y) + right.X*(mid.Y - left.Y)
1634 denom = math.sqrt((left.X - right.X)**2 + (left.Y - right.Y)**2)
1641 self.
subsample(left, mid, depth+1, trans)
1642 self.
subsample(mid, right, depth+1, trans)
1647 mid.y = mid.Y =
None
1650 """Apply the transformation "trans" and return an SVG object."""
1653 def Path(self, trans=None, local=False):
1654 """Apply the transformation "trans" and return a Path object in
1655 global coordinates. If local=True, return a Path in local coordinates
1656 (which must be transformed again)."""
1658 if isinstance(trans, basestring): trans =
totrans(trans)
1659 if isinstance(self.
f, basestring): self.
f =
funcRtoR2(self.
f)
1665 if s.X !=
None and s.Y !=
None:
1666 if s.left ==
None or s.left.Y ==
None:
1671 if local: output.append((command, s.x, s.y,
False))
1672 else: output.append((command, s.X, s.Y,
True))
1674 if self.
loop: output.append((
"Z",))
1680 """Draws a curve specified by a sequence of points. The curve may be
1681 piecewise linear, like a polygon, or a Bezier curve.
1683 Poly(d, mode, loop, attribute=value)
1685 d required list of tuples representing points
1686 and possibly control points
1687 mode default="L" "lines", "bezier", "velocity",
1688 "foreback", "smooth", or an abbreviation
1689 loop default=False if True, connect the first and last
1690 point, closing the loop
1691 attribute=value pairs keyword list SVG attributes
1693 The format of the tuples in d depends on the mode.
1695 "lines"/"L" d=[(x,y), (x,y), ...]
1696 piecewise-linear segments joining the (x,y) points
1697 "bezier"/"B" d=[(x, y, c1x, c1y, c2x, c2y), ...]
1698 Bezier curve with two control points (control points
1699 preceed (x,y), as in SVG paths). If (c1x,c1y) and
1700 (c2x,c2y) both equal (x,y), you get a linear
1701 interpolation ("lines")
1702 "velocity"/"V" d=[(x, y, vx, vy), ...]
1703 curve that passes through (x,y) with velocity (vx,vy)
1704 (one unit of arclength per unit time); in other words,
1705 (vx,vy) is the tangent vector at (x,y). If (vx,vy) is
1706 (0,0), you get a linear interpolation ("lines").
1707 "foreback"/"F" d=[(x, y, bx, by, fx, fy), ...]
1708 like "velocity" except that there is a left derivative
1709 (bx,by) and a right derivative (fx,fy). If (bx,by)
1710 equals (fx,fy) (with no minus sign), you get a
1712 "smooth"/"S" d=[(x,y), (x,y), ...]
1713 a "velocity" interpolation with (vx,vy)[i] equal to
1714 ((x,y)[i+1] - (x,y)[i-1])/2: the minimal derivative
1719 return "<Poly (%d nodes) mode=%s loop=%s %s>" % (len(self.
d), self.
mode, repr(self.
loop), self.
attr)
1721 def __init__(self, d=[], mode="L", loop=False, **attr):
1727 self.attr.update(attr)
1730 """Apply the transformation "trans" and return an SVG object."""
1733 def Path(self, trans=None, local=False):
1734 """Apply the transformation "trans" and return a Path object in
1735 global coordinates. If local=True, return a Path in local coordinates
1736 (which must be transformed again)."""
1737 if isinstance(trans, basestring): trans =
totrans(trans)
1739 if self.
mode[0] ==
"L" or self.
mode[0] ==
"l": mode =
"L"
1740 elif self.
mode[0] ==
"B" or self.
mode[0] ==
"b": mode =
"B"
1741 elif self.
mode[0] ==
"V" or self.
mode[0] ==
"v": mode =
"V"
1742 elif self.
mode[0] ==
"F" or self.
mode[0] ==
"f": mode =
"F"
1743 elif self.
mode[0] ==
"S" or self.
mode[0] ==
"s":
1746 vx, vy = [0.]*len(self.
d), [0.]*len(self.
d)
1747 for i
in xrange(len(self.
d)):
1748 inext = (i+1) % len(self.
d)
1749 iprev = (i-1) % len(self.
d)
1751 vx[i] = (self.
d[inext][0] - self.
d[iprev][0])/2.
1752 vy[i] = (self.
d[inext][1] - self.
d[iprev][1])/2.
1753 if not self.
loop and (i == 0
or i == len(self.
d)-1):
1754 vx[i], vy[i] = 0., 0.
1757 raise ValueError(
"mode must be \"lines\", \"bezier\", \"velocity\", \"foreback\", \"smooth\", or an abbreviation")
1760 indexes = range(len(self.
d))
1761 if self.
loop and len(self.
d) > 0: indexes.append(0)
1764 inext = (i+1) % len(self.
d)
1765 iprev = (i-1) % len(self.
d)
1767 x, y = self.
d[i][0], self.
d[i][1]
1769 if trans ==
None: X, Y = x, y
1770 else: X, Y = trans(x, y)
1773 if local: d.append((
"M", x, y,
False))
1774 else: d.append((
"M", X, Y,
True))
1777 if local: d.append((
"L", x, y,
False))
1778 else: d.append((
"L", X, Y,
True))
1781 c1x, c1y = self.
d[i][2], self.
d[i][3]
1782 if trans ==
None: C1X, C1Y = c1x, c1y
1783 else: C1X, C1Y = trans(c1x, c1y)
1785 c2x, c2y = self.
d[i][4], self.
d[i][5]
1786 if trans ==
None: C2X, C2Y = c2x, c2y
1787 else: C2X, C2Y = trans(c2x, c2y)
1789 if local: d.append((
"C", c1x, c1y,
False, c2x, c2y,
False, x, y,
False))
1790 else: d.append((
"C", C1X, C1Y,
True, C2X, C2Y,
True, X, Y,
True))
1793 c1x, c1y = self.
d[iprev][2]/3. + self.
d[iprev][0], self.
d[iprev][3]/3. + self.
d[iprev][1]
1794 c2x, c2y = self.
d[i][2]/-3. + x, self.
d[i][3]/-3. + y
1796 if trans ==
None: C1X, C1Y = c1x, c1y
1797 else: C1X, C1Y = trans(c1x, c1y)
1798 if trans ==
None: C2X, C2Y = c2x, c2y
1799 else: C2X, C2Y = trans(c2x, c2y)
1801 if local: d.append((
"C", c1x, c1y,
False, c2x, c2y,
False, x, y,
False))
1802 else: d.append((
"C", C1X, C1Y,
True, C2X, C2Y,
True, X, Y,
True))
1805 c1x, c1y = self.
d[iprev][4]/3. + self.
d[iprev][0], self.
d[iprev][5]/3. + self.
d[iprev][1]
1806 c2x, c2y = self.
d[i][2]/-3. + x, self.
d[i][3]/-3. + y
1808 if trans ==
None: C1X, C1Y = c1x, c1y
1809 else: C1X, C1Y = trans(c1x, c1y)
1810 if trans ==
None: C2X, C2Y = c2x, c2y
1811 else: C2X, C2Y = trans(c2x, c2y)
1813 if local: d.append((
"C", c1x, c1y,
False, c2x, c2y,
False, x, y,
False))
1814 else: d.append((
"C", C1X, C1Y,
True, C2X, C2Y,
True, X, Y,
True))
1817 c1x, c1y = vx[iprev]/3. + self.
d[iprev][0], vy[iprev]/3. + self.
d[iprev][1]
1818 c2x, c2y = vx[i]/-3. + x, vy[i]/-3. + y
1820 if trans ==
None: C1X, C1Y = c1x, c1y
1821 else: C1X, C1Y = trans(c1x, c1y)
1822 if trans ==
None: C2X, C2Y = c2x, c2y
1823 else: C2X, C2Y = trans(c2x, c2y)
1825 if local: d.append((
"C", c1x, c1y,
False, c2x, c2y,
False, x, y,
False))
1826 else: d.append((
"C", C1X, C1Y,
True, C2X, C2Y,
True, X, Y,
True))
1828 if self.
loop and len(self.
d) > 0: d.append((
"Z",))
1835 """Draws at text string at a specified point in local coordinates.
1837 x, y required location of the point in local coordinates
1838 d required text/Unicode string
1839 attribute=value pairs keyword list SVG attributes
1842 defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
1845 return "<Text %s at (%g, %g) %s>" % (repr(self.
d), self.
x, self.
y, self.
attr)
1852 self.attr.update(attr)
1855 """Apply the transformation "trans" and return an SVG object."""
1856 if isinstance(trans, basestring): trans =
totrans(trans)
1858 X, Y = self.
x, self.
y
1859 if trans !=
None: X, Y = trans(X, Y)
1860 return SVG(
"text", self.
d, x=X, y=Y, **self.
attr)
1863 """Draws at text string at a specified point in global coordinates.
1865 x, y required location of the point in global coordinates
1866 d required text/Unicode string
1867 attribute=value pairs keyword list SVG attributes
1869 defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
1872 return "<TextGlobal %s at (%s, %s) %s>" % (repr(self.
d), str(self.
x), str(self.
y), self.
attr)
1879 self.attr.update(attr)
1882 """Apply the transformation "trans" and return an SVG object."""
1883 return SVG(
"text", self.
d, x=self.
x, y=self.
y, **self.
attr)
1887 _symbol_templates = {
"dot":
SVG(
"symbol",
SVG(
"circle", cx=0, cy=0, r=1, stroke=
"none", fill=
"black"), viewBox=
"0 0 1 1", overflow=
"visible"), \
1888 "box":
SVG(
"symbol",
SVG(
"rect", x1=-1, y1=-1, x2=1, y2=1, stroke=
"none", fill=
"black"), viewBox=
"0 0 1 1", overflow=
"visible"), \
1889 "uptri":
SVG(
"symbol",
SVG(
"path", d=
"M -1 0.866 L 1 0.866 L 0 -0.866 Z", stroke=
"none", fill=
"black"), viewBox=
"0 0 1 1", overflow=
"visible"), \
1890 "downtri":
SVG(
"symbol",
SVG(
"path", d=
"M -1 -0.866 L 1 -0.866 L 0 0.866 Z", stroke=
"none", fill=
"black"), viewBox=
"0 0 1 1", overflow=
"visible"), \
1894 """Creates a new instance of an SVG symbol to avoid cross-linking objects.
1896 id required a new identifier (string/Unicode)
1897 shape default="dot" the shape name from _symbol_templates
1898 attribute=value list keyword list modify the SVG attributes of the new symbol
1900 output = copy.deepcopy(_symbol_templates[shape])
1908 """Dots draws SVG symbols at a set of points.
1910 d required list of (x,y) points
1911 symbol default=None SVG symbol or a new identifier to
1912 label an auto-generated symbol;
1913 if None, use pre-defined _circular_dot
1914 width, height default=1, 1 width and height of the symbols
1916 attribute=value pairs keyword list SVG attributes
1921 return "<Dots (%d nodes) %s>" % (len(self.
d), self.
attr)
1923 def __init__(self, d=[], symbol=None, width=1., height=1., **attr):
1929 self.attr.update(attr)
1933 elif isinstance(symbol, SVG):
1939 """Apply the transformation "trans" and return an SVG object."""
1940 if isinstance(trans, basestring): trans =
totrans(trans)
1943 id =
"#%s" % self.
symbol[
"id"]
1948 if trans ==
None: X, Y = x, y
1949 else: X, Y = trans(x, y)
1951 item =
SVG(
"use", x=X, y=Y, xlink__href=id)
1952 if self.
width !=
None: item[
"width"] = self.
width
1960 _marker_templates = {
"arrow_start":
SVG(
"marker",
SVG(
"path", d=
"M 9 3.6 L 10.5 0 L 0 3.6 L 10.5 7.2 L 9 3.6 Z"), viewBox=
"0 0 10.5 7.2", refX=
"9", refY=
"3.6", markerWidth=
"10.5", markerHeight=
"7.2", markerUnits=
"strokeWidth", orient=
"auto", stroke=
"none", fill=
"black"), \
1961 "arrow_end":
SVG(
"marker",
SVG(
"path", d=
"M 1.5 3.6 L 0 0 L 10.5 3.6 L 0 7.2 L 1.5 3.6 Z"), viewBox=
"0 0 10.5 7.2", refX=
"1.5", refY=
"3.6", markerWidth=
"10.5", markerHeight=
"7.2", markerUnits=
"strokeWidth", orient=
"auto", stroke=
"none", fill=
"black"), \
1965 """Creates a new instance of an SVG marker to avoid cross-linking objects.
1967 id required a new identifier (string/Unicode)
1968 shape required the shape name from _marker_templates
1969 attribute=value list keyword list modify the SVG attributes of the new marker
1971 output = copy.deepcopy(_marker_templates[shape])
1977 """Draws a line between two points.
1979 Line(x1, y1, x2, y2, arrow_start, arrow_end, attribute=value)
1981 x1, y1 required the starting point
1982 x2, y2 required the ending point
1983 arrow_start default=None if an identifier string/Unicode,
1984 draw a new arrow object at the
1985 beginning of the line; if a marker,
1986 draw that marker instead
1987 arrow_end default=None same for the end of the line
1988 attribute=value pairs keyword list SVG attributes
1993 return "<Line (%g, %g) to (%g, %g) %s>" % (self.x1, self.y1, self.x2, self.
y2, self.
attr)
1995 def __init__(self, x1, y1, x2, y2, arrow_start=None, arrow_end=None, **attr):
1996 self.x1, self.y1, self.x2, self.
y2 = x1, y1, x2, y2
2000 self.attr.update(attr)
2003 """Apply the transformation "trans" and return an SVG object."""
2007 if (self.arrow_start !=
False and self.arrow_start !=
None)
or (self.
arrow_end !=
False and self.
arrow_end !=
None):
2010 if self.arrow_start !=
False and self.arrow_start !=
None:
2011 if isinstance(self.arrow_start, SVG):
2012 defs.append(self.arrow_start)
2013 line.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start[
"id"]
2014 elif isinstance(self.arrow_start, basestring):
2015 defs.append(
make_marker(self.arrow_start,
"arrow_start"))
2016 line.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start
2018 raise TypeError(
"arrow_start must be False/None or an id string for the new marker")
2023 line.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end[
"id"]
2024 elif isinstance(self.
arrow_end, basestring):
2026 line.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end
2028 raise TypeError(
"arrow_end must be False/None or an id string for the new marker")
2030 return SVG(
"g", defs, line)
2034 def Path(self, trans=None, local=False):
2035 """Apply the transformation "trans" and return a Path object in
2036 global coordinates. If local=True, return a Path in local coordinates
2037 (which must be transformed again)."""
2038 self.
f =
lambda t: (self.x1 + t*(self.x2 - self.x1), self.y1 + t*(self.
y2 - self.y1))
2044 return Path([(
"M", self.x1, self.y1,
not local), (
"L", self.x2, self.
y2,
not local)], **self.
attr)
2046 return Curve.Path(self, trans, local)
2049 """Draws a line between two points, one or both of which is in
2052 Line(x1, y1, x2, y2, lcoal1, local2, arrow_start, arrow_end, attribute=value)
2054 x1, y1 required the starting point
2055 x2, y2 required the ending point
2056 local1 default=False if True, interpret first point as a
2057 local coordinate (apply transform)
2058 local2 default=False if True, interpret second point as a
2059 local coordinate (apply transform)
2060 arrow_start default=None if an identifier string/Unicode,
2061 draw a new arrow object at the
2062 beginning of the line; if a marker,
2063 draw that marker instead
2064 arrow_end default=None same for the end of the line
2065 attribute=value pairs keyword list SVG attributes
2070 local1, local2 =
"",
""
2071 if self.local1: local1 =
"L"
2072 if self.
local2: local2 =
"L"
2074 return "<LineGlobal %s(%s, %s) to %s(%s, %s) %s>" % (local1, str(self.x1), str(self.y1), local2, str(self.x2), str(self.
y2), self.
attr)
2076 def __init__(self, x1, y1, x2, y2, local1=False, local2=False, arrow_start=None, arrow_end=None, **attr):
2077 self.x1, self.y1, self.x2, self.
y2 = x1, y1, x2, y2
2082 self.attr.update(attr)
2085 """Apply the transformation "trans" and return an SVG object."""
2086 if isinstance(trans, basestring): trans =
totrans(trans)
2088 X1, Y1, X2, Y2 = self.x1, self.y1, self.x2, self.
y2
2090 if self.local1: X1, Y1 = trans(X1, Y1)
2091 if self.
local2: X2, Y2 = trans(X2, Y2)
2093 line =
SVG(
"path", d=
"M%s %s L%s %s" % (X1, Y1, X2, Y2), **self.
attr)
2095 if (self.arrow_start !=
False and self.arrow_start !=
None)
or (self.
arrow_end !=
False and self.
arrow_end !=
None):
2098 if self.arrow_start !=
False and self.arrow_start !=
None:
2099 if isinstance(self.arrow_start, SVG):
2100 defs.append(self.arrow_start)
2101 line.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start[
"id"]
2102 elif isinstance(self.arrow_start, basestring):
2103 defs.append(
make_marker(self.arrow_start,
"arrow_start"))
2104 line.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start
2106 raise TypeError(
"arrow_start must be False/None or an id string for the new marker")
2111 line.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end[
"id"]
2112 elif isinstance(self.
arrow_end, basestring):
2114 line.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end
2116 raise TypeError(
"arrow_end must be False/None or an id string for the new marker")
2118 return SVG(
"g", defs, line)
2123 """Draws a vertical line.
2125 VLine(y1, y2, x, attribute=value)
2127 y1, y2 required y range
2128 x required x position
2129 attribute=value pairs keyword list SVG attributes
2134 return "<VLine (%g, %g) at x=%s %s>" % (self.y1, self.
y2, self.
x, self.
attr)
2139 self.attr.update(attr)
2140 Line.__init__(self, x, y1, x, y2, **self.
attr)
2142 def Path(self, trans=None, local=False):
2143 """Apply the transformation "trans" and return a Path object in
2144 global coordinates. If local=True, return a Path in local coordinates
2145 (which must be transformed again)."""
2148 return Line.Path(self, trans, local)
2151 """Draws a horizontal line.
2153 HLine(x1, x2, y, attribute=value)
2155 x1, x2 required x range
2156 y required y position
2157 attribute=value pairs keyword list SVG attributes
2162 return "<HLine (%g, %g) at y=%s %s>" % (self.x1, self.x2, self.
y, self.
attr)
2167 self.attr.update(attr)
2168 Line.__init__(self, x1, y, x2, y, **self.
attr)
2170 def Path(self, trans=None, local=False):
2171 """Apply the transformation "trans" and return a Path object in
2172 global coordinates. If local=True, return a Path in local coordinates
2173 (which must be transformed again)."""
2176 return Line.Path(self, trans, local)
2181 """Draws a rectangle.
2183 Rect(x1, y1, x2, y2, attribute=value)
2185 x1, y1 required the starting point
2186 x2, y2 required the ending point
2187 attribute=value pairs keyword list SVG attributes
2192 return "<Rect (%g, %g), (%g, %g) %s>" % (self.x1, self.y1, self.x2, self.
y2, self.
attr)
2195 self.x1, self.y1, self.x2, self.
y2 = x1, y1, x2, y2
2198 self.attr.update(attr)
2201 """Apply the transformation "trans" and return an SVG object."""
2204 def Path(self, trans=None, local=False):
2205 """Apply the transformation "trans" and return a Path object in
2206 global coordinates. If local=True, return a Path in local coordinates
2207 (which must be transformed again)."""
2209 return Path([(
"M", self.x1, self.y1,
not local), (
"L", self.x2, self.y1,
not local), (
"L", self.x2, self.
y2,
not local), (
"L", self.x1, self.
y2,
not local), (
"Z",)], **self.
attr)
2216 self.
f =
lambda t: (self.x1 + t*(self.x2 - self.x1), self.y1)
2217 d1 = Curve.Path(self, trans, local).d
2219 self.
f =
lambda t: (self.x2, self.y1 + t*(self.
y2 - self.y1))
2220 d2 = Curve.Path(self, trans, local).d
2223 self.
f =
lambda t: (self.x2 + t*(self.x1 - self.x2), self.
y2)
2224 d3 = Curve.Path(self, trans, local).d
2227 self.
f =
lambda t: (self.x1, self.
y2 + t*(self.y1 - self.
y2))
2228 d4 = Curve.Path(self, trans, local).d
2231 return Path(d=(d1 + d2 + d3 + d4 + [(
"Z",)]), **self.
attr)
2236 """Draws an ellipse from a semimajor vector (ax,ay) and a semiminor
2239 Ellipse(x, y, ax, ay, b, attribute=value)
2241 x, y required the center of the ellipse/circle
2242 ax, ay required a vector indicating the length
2243 and direction of the semimajor axis
2244 b required the length of the semiminor axis.
2245 If equal to sqrt(ax2 + ay2), the
2247 attribute=value pairs keyword list SVG attributes
2249 (If sqrt(ax**2 + ay**2) is less than b, then (ax,ay) is actually the
2255 return "<Ellipse (%g, %g) a=(%g, %g), b=%g %s>" % (self.x, self.y, self.ax, self.ay, self.
b, self.
attr)
2258 self.x, self.y, self.ax, self.ay, self.
b = x, y, ax, ay, b
2261 self.attr.update(attr)
2264 """Apply the transformation "trans" and return an SVG object."""
2267 def Path(self, trans=None, local=False):
2268 """Apply the transformation "trans" and return a Path object in
2269 global coordinates. If local=True, return a Path in local coordinates
2270 (which must be transformed again)."""
2271 angle = math.atan2(self.ay, self.ax) + math.pi/2.
2272 bx = self.
b * math.cos(angle)
2273 by = self.
b * math.sin(angle)
2275 self.
f =
lambda t: (self.x + self.ax*math.cos(t) + bx*math.sin(t), self.y + self.ay*math.cos(t) + by*math.sin(t))
2279 return Curve.Path(self, trans, local)
2284 """Converts numbers to a Unicode string, taking advantage of special
2285 Unicode characters to make nice minus signs and scientific notation.
2289 if output[0] ==
u"-":
2290 output =
u"\u2013" + output[1:]
2292 index = output.find(
u"e")
2294 uniout = unicode(output[:index]) +
u"\u00d710"
2296 for n
in output[index+1:]:
2298 elif n ==
u"-": uniout +=
u"\u207b"
2300 if saw_nonzero: uniout +=
u"\u2070"
2310 elif u"4" <= n <=
u"9":
2312 if saw_nonzero: uniout += eval(
"u\"\\u%x\"" % (0x2070 + ord(n) - ord(
u"0")))
2315 if uniout[:2] ==
u"1\u00d7": uniout = uniout[2:]
2321 """Superclass for all graphics primatives that draw ticks,
2322 miniticks, and tick labels. This class only draws the ticks.
2324 Ticks(f, low, high, ticks, miniticks, labels, logbase, arrow_start,
2325 arrow_end, text_attr, attribute=value)
2327 f required parametric function along which ticks
2328 will be drawn; has the same format as
2329 the function used in Curve
2330 low, high required range of the independent variable
2331 ticks default=-10 request ticks according to the standard
2332 tick specification (see below)
2333 miniticks default=True request miniticks according to the
2334 standard minitick specification (below)
2335 labels True request tick labels according to the
2336 standard tick label specification (below)
2337 logbase default=None if a number, the axis is logarithmic with
2338 ticks at the given base (usually 10)
2339 arrow_start default=None if a new string identifier, draw an arrow
2340 at the low-end of the axis, referenced by
2341 that identifier; if an SVG marker object,
2343 arrow_end default=None if a new string identifier, draw an arrow
2344 at the high-end of the axis, referenced by
2345 that identifier; if an SVG marker object,
2347 text_attr default={} SVG attributes for the text labels
2348 attribute=value pairs keyword list SVG attributes for the tick marks
2350 Standard tick specification:
2352 * True: same as -10 (below).
2353 * Positive number N: draw exactly N ticks, including the endpoints. To
2354 subdivide an axis into 10 equal-sized segments, ask for 11 ticks.
2355 * Negative number -N: draw at least N ticks. Ticks will be chosen with
2356 "natural" values, multiples of 2 or 5.
2357 * List of values: draw a tick mark at each value.
2358 * Dict of value, label pairs: draw a tick mark at each value, labeling
2359 it with the given string. This lets you say things like {3.14159: "pi"}.
2360 * False or None: no ticks.
2362 Standard minitick specification:
2364 * True: draw miniticks with "natural" values, more closely spaced than
2366 * Positive number N: draw exactly N miniticks, including the endpoints.
2367 To subdivide an axis into 100 equal-sized segments, ask for 101 miniticks.
2368 * Negative number -N: draw at least N miniticks.
2369 * List of values: draw a minitick mark at each value.
2370 * False or None: no miniticks.
2372 Standard tick label specification:
2374 * True: use the unumber function (described below)
2375 * Format string: standard format strings, e.g. "%5.2f" for 12.34
2376 * Python callable: function that converts numbers to strings
2377 * False or None: no labels
2379 defaults = {
"stroke-width":
"0.25pt"}
2380 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
2383 minitick_start = -0.75
2389 return "<Ticks %s from %s to %s ticks=%s labels=%s %s>" % (self.
f, self.
low, self.
high, str(self.
ticks), str(self.
labels), self.
attr)
2391 def __init__(self, f, low, high, ticks=-10, miniticks=True, labels=True, logbase=None, arrow_start=None, arrow_end=None, text_attr={}, **attr):
2403 self.attr.update(attr)
2406 self.text_attr.update(text_attr)
2409 """Return the position, normalized local x vector, normalized
2410 local y vector, and angle of a tick at position t.
2412 Normally only used internally.
2414 if isinstance(trans, basestring): trans =
totrans(trans)
2418 f =
lambda t: trans(*self.
f(t))
2423 Xprime, Yprime =
f(t + eps)
2424 xhatx, xhaty = (Xprime - X)/eps, (Yprime - Y)/eps
2426 norm = math.sqrt(xhatx**2 + xhaty**2)
2427 if norm != 0: xhatx, xhaty = xhatx/norm, xhaty/norm
2428 else: xhatx, xhaty = 1., 0.
2430 angle = math.atan2(xhaty, xhatx) + math.pi/2.
2431 yhatx, yhaty = math.cos(angle), math.sin(angle)
2433 return (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle
2436 """Apply the transformation "trans" and return an SVG object."""
2437 if isinstance(trans, basestring): trans =
totrans(trans)
2441 minitickmarks =
Path([], **self.
attr)
2453 raise TypeError(
"arrow_start must be False/None or an id string for the new marker")
2458 elif isinstance(self.
arrow_end, basestring):
2461 raise TypeError(
"arrow_end must be False/None or an id string for the new marker")
2465 eps = _epsilon * (self.
high - self.
low)
2467 for t, label
in self.last_ticks.items():
2468 (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle = self.
orient_tickmark(t, trans)
2472 tickmarks.d.append((
"L", X - yhatx*self.
tick_end, Y - yhaty*self.
tick_end,
True))
2474 angle = (angle - math.pi/2.)*180./math.pi + self.
text_angle
2477 if _hacks[
"inkscape-text-vertical-shift"]:
2479 X += math.cos(angle*math.pi/180. + math.pi/2.) * 2.
2480 Y += math.sin(angle*math.pi/180. + math.pi/2.) * 2.
2482 X += math.cos(angle*math.pi/180. + math.pi/2.) * 2. * 2.5
2483 Y += math.sin(angle*math.pi/180. + math.pi/2.) * 2. * 2.5
2487 output.append(
SVG(
"text", label, transform=
"translate(%g, %g) rotate(%g)" % \
2492 for tt
in self.last_ticks.keys():
2493 if abs(t - tt) < eps:
2497 (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle = self.
orient_tickmark(t, trans)
2503 output.prepend(tickmarks.SVG(trans))
2504 output.prepend(minitickmarks.SVG(trans))
2508 """Evaluate and return optimal ticks and miniticks according to
2509 the standard minitick specification.
2511 Normally only used internally.
2515 format =
lambda x:
""
2517 elif self.
labels ==
True:
2520 elif isinstance(self.
labels, basestring):
2521 format =
lambda x: (self.
labels % x)
2523 elif callable(self.
labels):
2526 else:
raise TypeError(
"labels must be None/False, True, a format string, or a number->string function")
2532 if ticks ==
None or ticks ==
False:
return {}, []
2535 elif isinstance(ticks, (int, long)):
2536 if ticks ==
True: ticks = -10
2550 elif isinstance(self.
miniticks, (int, long)):
2553 elif getattr(self.
miniticks,
"__iter__",
False):
2560 raise TypeError(
"miniticks must be None/False, True, a number of desired miniticks, or a list of numbers")
2563 elif getattr(ticks,
"__iter__",
False):
2566 if not isinstance(ticks, dict):
2568 eps = _epsilon * (self.
high - self.
low)
2570 if format == unumber
and abs(x) < eps:
2586 elif isinstance(self.
miniticks, (int, long)):
2589 elif getattr(self.
miniticks,
"__iter__",
False):
2596 raise TypeError(
"miniticks must be None/False, True, a number of desired miniticks, or a list of numbers")
2599 raise TypeError(
"ticks must be None/False, a number of desired ticks, a list of numbers, or a dictionary of explicit markers")
2602 """Return less than -N or exactly N optimal linear ticks.
2604 Normally only used internally.
2606 if self.
low >= self.
high:
raise ValueError(
"low must be less than high")
2607 if N == 1:
raise ValueError(
"N can be 0 or >1 to specify the exact number of ticks or negative to specify a maximum")
2609 eps = _epsilon * (self.
high - self.
low)
2615 if format == unumber
and abs(x) < eps: label =
u"0"
2618 x += (self.
high - self.
low)/(N-1.)
2624 granularity = 10**math.ceil(math.log10(
max(
abs(self.
low),
abs(self.
high))))
2625 lowN = math.ceil(1.*self.
low / granularity)
2626 highN = math.floor(1.*self.
high / granularity)
2628 while (lowN > highN):
2629 countermod3 = counter % 3
2630 if countermod3 == 0: granularity *= 0.5
2631 elif countermod3 == 1: granularity *= 0.4
2632 else: granularity *= 0.5
2634 lowN = math.ceil(1.*self.
low / granularity)
2635 highN = math.floor(1.*self.
high / granularity)
2637 last_granularity = granularity
2642 for n
in range(int(lowN), int(highN)+1):
2644 if format == unumber
and abs(x) < eps: label =
u"0"
2648 if int(highN)+1 - int(lowN) >= N:
2649 if last_trial ==
None:
2653 low_in_ticks, high_in_ticks =
False,
False
2654 for t
in last_trial.keys():
2655 if 1.*
abs(t - self.
low)/last_granularity < _epsilon: low_in_ticks =
True
2656 if 1.*
abs(t - self.
high)/last_granularity < _epsilon: high_in_ticks =
True
2658 lowN = 1.*self.
low / last_granularity
2659 highN = 1.*self.
high / last_granularity
2660 if abs(lowN - round(lowN)) < _epsilon
and not low_in_ticks:
2662 if abs(highN - round(highN)) < _epsilon
and not high_in_ticks:
2666 last_granularity = granularity
2669 countermod3 = counter % 3
2670 if countermod3 == 0: granularity *= 0.5
2671 elif countermod3 == 1: granularity *= 0.4
2672 else: granularity *= 0.5
2674 lowN = math.ceil(1.*self.
low / granularity)
2675 highN = math.floor(1.*self.
high / granularity)
2678 """Return exactly N linear ticks.
2680 Normally only used internally.
2686 x += (self.
high - self.
low)/(N-1.)
2690 """Return optimal linear miniticks, given a set of ticks.
2692 Normally only used internally.
2694 if len(original_ticks) < 2: original_ticks =
ticks(self.
low, self.
high)
2695 original_ticks = original_ticks.keys()
2696 original_ticks.sort()
2698 if self.
low > original_ticks[0] + _epsilon
or self.
high < original_ticks[-1] - _epsilon:
2699 raise ValueError(
"original_ticks {%g...%g} extend beyond [%g, %g]" % (original_ticks[0], original_ticks[-1], self.
low, self.
high))
2702 for i
in range(len(original_ticks)-1):
2703 granularities.append(original_ticks[i+1] - original_ticks[i])
2704 spacing = 10**(math.ceil(math.log10(
min(granularities)) - 1))
2707 x = original_ticks[0] - math.ceil(1.*(original_ticks[0] - self.
low) / spacing) * spacing
2709 while x <= self.
high:
2711 already_in_ticks =
False
2712 for t
in original_ticks:
2713 if abs(x-t) < _epsilon * (self.
high - self.
low): already_in_ticks =
True
2714 if not already_in_ticks: output.append(x)
2719 """Return less than -N or exactly N optimal logarithmic ticks.
2721 Normally only used internally.
2723 if self.
low >= self.
high:
raise ValueError(
"low must be less than high")
2724 if N == 1:
raise ValueError(
"N can be 0 or >1 to specify the exact number of ticks or negative to specify a maximum")
2726 eps = _epsilon * (self.
high - self.
low)
2732 if format == unumber
and abs(x) < eps: label =
u"0"
2735 x += (self.
high - self.
low)/(N-1.)
2740 lowN = math.floor(math.log(self.
low, base))
2741 highN = math.ceil(math.log(self.
high, base))
2743 for n
in range(int(lowN), int(highN)+1):
2746 if self.
low <= x <= self.
high: output[x] = label
2748 for i
in range(1, len(output)):
2749 keys = output.keys()
2752 values = map(
lambda k: output[k], keys)
2753 if len(values) <= N:
2754 for k
in output.keys():
2759 if len(output) <= 2:
2760 output2 = self.
compute_ticks(N=-int(math.ceil(N/2.)), format=format)
2761 lowest =
min(output2)
2764 if k < lowest: output2[k] = output[k]
2770 """Return optimal logarithmic miniticks, given a set of ticks.
2772 Normally only used internally.
2774 if self.
low >= self.
high:
raise ValueError(
"low must be less than high")
2776 lowN = math.floor(math.log(self.
low, base))
2777 highN = math.ceil(math.log(self.
high, base))
2780 for n
in range(int(lowN), int(highN)+1):
2782 if self.
low <= x <= self.
high: num_ticks += 1
2783 for m
in range(2, int(math.ceil(base))):
2785 if self.
low <= minix <= self.
high: output.append(minix)
2787 if num_ticks <= 2:
return []
2793 """Draw an axis with tick marks along a parametric curve.
2795 CurveAxis(f, low, high, ticks, miniticks, labels, logbase, arrow_start, arrow_end,
2796 text_attr, attribute=value)
2798 f required a Python callable or string in
2799 the form "f(t), g(t)", just like Curve
2800 low, high required left and right endpoints
2801 ticks default=-10 request ticks according to the standard
2802 tick specification (see help(Ticks))
2803 miniticks default=True request miniticks according to the
2804 standard minitick specification
2805 labels True request tick labels according to the
2806 standard tick label specification
2807 logbase default=None if a number, the x axis is logarithmic
2808 with ticks at the given base (10 being
2810 arrow_start default=None if a new string identifier, draw an
2811 arrow at the low-end of the axis,
2812 referenced by that identifier; if an
2813 SVG marker object, use that marker
2814 arrow_end default=None if a new string identifier, draw an
2815 arrow at the high-end of the axis,
2816 referenced by that identifier; if an
2817 SVG marker object, use that marker
2818 text_attr default={} SVG attributes for the text labels
2819 attribute=value pairs keyword list SVG attributes
2821 defaults = {
"stroke-width":
"0.25pt"}
2822 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
2825 return "<CurveAxis %s [%s, %s] ticks=%s labels=%s %s>" % (self.
f, self.
low, self.
high, str(self.
ticks), str(self.
labels), self.
attr)
2827 def __init__(self, f, low, high, ticks=-10, miniticks=True, labels=True, logbase=None, arrow_start=None, arrow_end=None, text_attr={}, **attr):
2829 tattr.update(text_attr)
2830 Curve.__init__(self, f, low, high)
2831 Ticks.__init__(self, f, low, high, ticks, miniticks, labels, logbase, arrow_start, arrow_end, tattr, **attr)
2834 """Apply the transformation "trans" and return an SVG object."""
2835 func = Curve.SVG(self, trans)
2836 ticks = Ticks.SVG(self, trans)
2840 func.attr[
"marker-start"] =
"url(#%s)" % self.
arrow_start
2842 func.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start.id
2845 if isinstance(self.
arrow_end, basestring):
2846 func.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end
2848 func.attr[
"marker-end"] =
"url(#%s)" % self.arrow_end.id
2854 """Draws an axis with tick marks along a line.
2856 LineAxis(x1, y1, x2, y2, start, end, ticks, miniticks, labels, logbase,
2857 arrow_start, arrow_end, text_attr, attribute=value)
2859 x1, y1 required starting point
2860 x2, y2 required ending point
2861 start, end default=0, 1 values to start and end labeling
2862 ticks default=-10 request ticks according to the standard
2863 tick specification (see help(Ticks))
2864 miniticks default=True request miniticks according to the
2865 standard minitick specification
2866 labels True request tick labels according to the
2867 standard tick label specification
2868 logbase default=None if a number, the x axis is logarithmic
2869 with ticks at the given base (usually 10)
2870 arrow_start default=None if a new string identifier, draw an arrow
2871 at the low-end of the axis, referenced by
2872 that identifier; if an SVG marker object,
2874 arrow_end default=None if a new string identifier, draw an arrow
2875 at the high-end of the axis, referenced by
2876 that identifier; if an SVG marker object,
2878 text_attr default={} SVG attributes for the text labels
2879 attribute=value pairs keyword list SVG attributes
2881 defaults = {
"stroke-width":
"0.25pt"}
2882 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
2885 return "<LineAxis (%g, %g) to (%g, %g) ticks=%s labels=%s %s>" % (self.x1, self.y1, self.x2, self.
y2, str(self.
ticks), str(self.
labels), self.
attr)
2887 def __init__(self, x1, y1, x2, y2, start=0., end=1., ticks=-10, miniticks=True, labels=True, logbase=None, arrow_start=None, arrow_end=None, exclude=None, text_attr={}, **attr):
2892 tattr.update(text_attr)
2893 Line.__init__(self, x1, y1, x2, y2, **attr)
2894 Ticks.__init__(self,
None,
None,
None, ticks, miniticks, labels, logbase, arrow_start, arrow_end, tattr, **attr)
2897 if self.
exclude !=
None and not (isinstance(self.
exclude, (tuple, list))
and len(self.
exclude) == 2
and \
2898 isinstance(self.
exclude[0], (int, long, float))
and isinstance(self.
exclude[1], (int, long, float))):
2899 raise TypeError(
"exclude must either be None or (low, high)")
2901 ticks, miniticks = Ticks.interpret(self)
2902 if self.
exclude ==
None:
return ticks, miniticks
2905 for loc, label
in ticks.items():
2911 return ticks2, miniticks
2914 """Apply the transformation "trans" and return an SVG object."""
2915 line = Line.SVG(self, trans)
2924 line.attr[
"marker-start"] =
"url(#%s)" % self.
arrow_start
2926 line.attr[
"marker-start"] =
"url(#%s)" % self.arrow_start.id
2929 if isinstance(self.
arrow_end, basestring):
2930 line.attr[
"marker-end"] =
"url(#%s)" % self.
arrow_end
2932 line.attr[
"marker-end"] =
"url(#%s)" % self.arrow_end.id
2934 ticks = Ticks.SVG(self, trans)
2939 """Draws an x axis with tick marks.
2941 XAxis(xmin, xmax, aty, ticks, miniticks, labels, logbase, arrow_start, arrow_end,
2942 exclude, text_attr, attribute=value)
2944 xmin, xmax required the x range
2945 aty default=0 y position to draw the axis
2946 ticks default=-10 request ticks according to the standard
2947 tick specification (see help(Ticks))
2948 miniticks default=True request miniticks according to the
2949 standard minitick specification
2950 labels True request tick labels according to the
2951 standard tick label specification
2952 logbase default=None if a number, the x axis is logarithmic
2953 with ticks at the given base (usually 10)
2954 arrow_start default=None if a new string identifier, draw an arrow
2955 at the low-end of the axis, referenced by
2956 that identifier; if an SVG marker object,
2958 arrow_end default=None if a new string identifier, draw an arrow
2959 at the high-end of the axis, referenced by
2960 that identifier; if an SVG marker object,
2962 exclude default=None if a (low, high) pair, don't draw text
2963 labels within this range
2964 text_attr default={} SVG attributes for the text labels
2965 attribute=value pairs keyword list SVG attributes for all lines
2967 The exclude option is provided for Axes to keep text from overlapping
2968 where the axes cross. Normal users are not likely to need it.
2970 defaults = {
"stroke-width":
"0.25pt"}
2971 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5,
"dominant-baseline":
"text-before-edge"}
2976 return "<XAxis (%g, %g) at y=%g ticks=%s labels=%s %s>" % (self.xmin, self.xmax, self.
aty, str(self.
ticks), str(self.
labels), self.
attr)
2978 def __init__(self, xmin, xmax, aty=0, ticks=-10, miniticks=True, labels=True, logbase=None, arrow_start=None, arrow_end=None, exclude=None, text_attr={}, **attr):
2981 tattr.update(text_attr)
2982 LineAxis.__init__(self, xmin, aty, xmax, aty, xmin, xmax, ticks, miniticks, labels, logbase, arrow_start, arrow_end, exclude, tattr, **attr)
2985 """Apply the transformation "trans" and return an SVG object."""
2988 return LineAxis.SVG(self, trans)
2991 """Draws a y axis with tick marks.
2993 YAxis(ymin, ymax, atx, ticks, miniticks, labels, logbase, arrow_start, arrow_end,
2994 exclude, text_attr, attribute=value)
2996 ymin, ymax required the y range
2997 atx default=0 x position to draw the axis
2998 ticks default=-10 request ticks according to the standard
2999 tick specification (see help(Ticks))
3000 miniticks default=True request miniticks according to the
3001 standard minitick specification
3002 labels True request tick labels according to the
3003 standard tick label specification
3004 logbase default=None if a number, the y axis is logarithmic
3005 with ticks at the given base (usually 10)
3006 arrow_start default=None if a new string identifier, draw an arrow
3007 at the low-end of the axis, referenced by
3008 that identifier; if an SVG marker object,
3010 arrow_end default=None if a new string identifier, draw an arrow
3011 at the high-end of the axis, referenced by
3012 that identifier; if an SVG marker object,
3014 exclude default=None if a (low, high) pair, don't draw text
3015 labels within this range
3016 text_attr default={} SVG attributes for the text labels
3017 attribute=value pairs keyword list SVG attributes for all lines
3019 The exclude option is provided for Axes to keep text from overlapping
3020 where the axes cross. Normal users are not likely to need it.
3022 defaults = {
"stroke-width":
"0.25pt"}
3023 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5,
"text-anchor":
"end",
"dominant-baseline":
"middle"}
3028 return "<YAxis (%g, %g) at x=%g ticks=%s labels=%s %s>" % (self.ymin, self.ymax, self.
atx, str(self.
ticks), str(self.
labels), self.
attr)
3030 def __init__(self, ymin, ymax, atx=0, ticks=-10, miniticks=True, labels=True, logbase=None, arrow_start=None, arrow_end=None, exclude=None, text_attr={}, **attr):
3033 tattr.update(text_attr)
3034 LineAxis.__init__(self, atx, ymin, atx, ymax, ymin, ymax, ticks, miniticks, labels, logbase, arrow_start, arrow_end, exclude, tattr, **attr)
3037 """Apply the transformation "trans" and return an SVG object."""
3040 return LineAxis.SVG(self, trans)
3043 """Draw a pair of intersecting x-y axes.
3045 Axes(xmin, xmax, ymin, ymax, atx, aty, xticks, xminiticks, xlabels, xlogbase,
3046 yticks, yminiticks, ylabels, ylogbase, arrows, text_attr, attribute=value)
3048 xmin, xmax required the x range
3049 ymin, ymax required the y range
3050 atx, aty default=0, 0 point where the axes try to cross;
3051 if outside the range, the axes will
3052 cross at the closest corner
3053 xticks default=-10 request ticks according to the standard
3054 tick specification (see help(Ticks))
3055 xminiticks default=True request miniticks according to the
3056 standard minitick specification
3057 xlabels True request tick labels according to the
3058 standard tick label specification
3059 xlogbase default=None if a number, the x axis is logarithmic
3060 with ticks at the given base (usually 10)
3061 yticks default=-10 request ticks according to the standard
3063 yminiticks default=True request miniticks according to the
3064 standard minitick specification
3065 ylabels True request tick labels according to the
3066 standard tick label specification
3067 ylogbase default=None if a number, the y axis is logarithmic
3068 with ticks at the given base (usually 10)
3069 arrows default=None if a new string identifier, draw arrows
3070 referenced by that identifier
3071 text_attr default={} SVG attributes for the text labels
3072 attribute=value pairs keyword list SVG attributes for all lines
3074 defaults = {
"stroke-width":
"0.25pt"}
3075 text_defaults = {
"stroke":
"none",
"fill":
"black",
"font-size":5}
3078 return "<Axes x=(%g, %g) y=(%g, %g) at (%g, %g) %s>" % (self.xmin, self.
xmax, self.ymin, self.
ymax, self.atx, self.
aty, self.
attr)
3080 def __init__(self, xmin, xmax, ymin, ymax, atx=0, aty=0, xticks=-10, xminiticks=True, xlabels=True, xlogbase=None, yticks=-10, yminiticks=True, ylabels=True, ylogbase=None, arrows=None, text_attr={}, **attr):
3084 self.xticks, self.xminiticks, self.xlabels, self.
xlogbase = xticks, xminiticks, xlabels, xlogbase
3085 self.yticks, self.yminiticks, self.ylabels, self.
ylogbase = yticks, yminiticks, ylabels, ylogbase
3089 self.text_attr.update(text_attr)
3092 self.attr.update(attr)
3095 """Apply the transformation "trans" and return an SVG object."""
3096 atx, aty = self.atx, self.
aty
3097 if atx < self.xmin: atx = self.xmin
3098 if atx > self.
xmax: atx = self.
xmax
3099 if aty < self.ymin: aty = self.ymin
3100 if aty > self.
ymax: aty = self.
ymax
3102 xmargin = 0.1 *
abs(self.ymin - self.
ymax)
3103 xexclude = atx - xmargin, atx + xmargin
3105 ymargin = 0.1 *
abs(self.xmin - self.
xmax)
3106 yexclude = aty - ymargin, aty + ymargin
3109 xarrow_start = self.
arrows +
".xstart"
3110 xarrow_end = self.
arrows +
".xend"
3111 yarrow_start = self.
arrows +
".ystart"
3112 yarrow_end = self.
arrows +
".yend"
3114 xarrow_start = xarrow_end = yarrow_start = yarrow_end =
None
3116 xaxis =
XAxis(self.xmin, self.
xmax, aty, self.xticks, self.xminiticks, self.xlabels, self.
xlogbase, xarrow_start, xarrow_end, exclude=xexclude, text_attr=self.
text_attr, **self.
attr).
SVG(trans)
3117 yaxis =
YAxis(self.ymin, self.
ymax, atx, self.yticks, self.yminiticks, self.ylabels, self.
ylogbase, yarrow_start, yarrow_end, exclude=yexclude, text_attr=self.
text_attr, **self.
attr).
SVG(trans)
3118 return SVG(
"g", *(xaxis.sub + yaxis.sub))
3123 """Draws the horizontal lines of a grid over a specified region
3124 using the standard tick specification (see help(Ticks)) to place the
3127 HGrid(xmin, xmax, low, high, ticks, miniticks, logbase, mini_attr, attribute=value)
3129 xmin, xmax required the x range
3130 low, high required the y range
3131 ticks default=-10 request ticks according to the standard
3132 tick specification (see help(Ticks))
3133 miniticks default=False request miniticks according to the
3134 standard minitick specification
3135 logbase default=None if a number, the axis is logarithmic
3136 with ticks at the given base (usually 10)
3137 mini_attr default={} SVG attributes for the minitick-lines
3138 (if miniticks != False)
3139 attribute=value pairs keyword list SVG attributes for the major tick lines
3141 defaults = {
"stroke-width":
"0.25pt",
"stroke":
"gray"}
3142 mini_defaults = {
"stroke-width":
"0.25pt",
"stroke":
"lightgray",
"stroke-dasharray":
"1,1"}
3145 return "<HGrid x=(%g, %g) %g <= y <= %g ticks=%s miniticks=%s %s>" % (self.xmin, self.
xmax, self.
low, self.
high, str(self.
ticks), str(self.
miniticks), self.
attr)
3147 def __init__(self, xmin, xmax, low, high, ticks=-10, miniticks=False, logbase=None, mini_attr={}, **attr):
3151 self.mini_attr.update(mini_attr)
3153 Ticks.__init__(self,
None, low, high, ticks, miniticks,
None, logbase)
3156 self.attr.update(attr)
3159 """Apply the transformation "trans" and return an SVG object."""
3163 for t
in self.last_ticks.keys():
3164 ticksd +=
Line(self.xmin, t, self.
xmax, t).
Path(trans).d
3168 miniticksd +=
Line(self.xmin, t, self.
xmax, t).
Path(trans).d
3173 """Draws the vertical lines of a grid over a specified region
3174 using the standard tick specification (see help(Ticks)) to place the
3177 HGrid(ymin, ymax, low, high, ticks, miniticks, logbase, mini_attr, attribute=value)
3179 ymin, ymax required the y range
3180 low, high required the x range
3181 ticks default=-10 request ticks according to the standard
3182 tick specification (see help(Ticks))
3183 miniticks default=False request miniticks according to the
3184 standard minitick specification
3185 logbase default=None if a number, the axis is logarithmic
3186 with ticks at the given base (usually 10)
3187 mini_attr default={} SVG attributes for the minitick-lines
3188 (if miniticks != False)
3189 attribute=value pairs keyword list SVG attributes for the major tick lines
3191 defaults = {
"stroke-width":
"0.25pt",
"stroke":
"gray"}
3192 mini_defaults = {
"stroke-width":
"0.25pt",
"stroke":
"lightgray",
"stroke-dasharray":
"1,1"}
3195 return "<VGrid y=(%g, %g) %g <= x <= %g ticks=%s miniticks=%s %s>" % (self.ymin, self.
ymax, self.
low, self.
high, str(self.
ticks), str(self.
miniticks), self.
attr)
3197 def __init__(self, ymin, ymax, low, high, ticks=-10, miniticks=False, logbase=None, mini_attr={}, **attr):
3201 self.mini_attr.update(mini_attr)
3203 Ticks.__init__(self,
None, low, high, ticks, miniticks,
None, logbase)
3206 self.attr.update(attr)
3209 """Apply the transformation "trans" and return an SVG object."""
3213 for t
in self.last_ticks.keys():
3214 ticksd +=
Line(t, self.ymin, t, self.
ymax).
Path(trans).d
3218 miniticksd +=
Line(t, self.ymin, t, self.
ymax).
Path(trans).d
3223 """Draws a grid over a specified region using the standard tick
3224 specification (see help(Ticks)) to place the grid lines.
3226 Grid(xmin, xmax, ymin, ymax, ticks, miniticks, logbase, mini_attr, attribute=value)
3228 xmin, xmax required the x range
3229 ymin, ymax required the y range
3230 ticks default=-10 request ticks according to the standard
3231 tick specification (see help(Ticks))
3232 miniticks default=False request miniticks according to the
3233 standard minitick specification
3234 logbase default=None if a number, the axis is logarithmic
3235 with ticks at the given base (usually 10)
3236 mini_attr default={} SVG attributes for the minitick-lines
3237 (if miniticks != False)
3238 attribute=value pairs keyword list SVG attributes for the major tick lines
3240 defaults = {
"stroke-width":
"0.25pt",
"stroke":
"gray"}
3241 mini_defaults = {
"stroke-width":
"0.25pt",
"stroke":
"lightgray",
"stroke-dasharray":
"1,1"}
3244 return "<Grid x=(%g, %g) y=(%g, %g) ticks=%s miniticks=%s %s>" % (self.xmin, self.
xmax, self.ymin, self.
ymax, str(self.
ticks), str(self.
miniticks), self.
attr)
3246 def __init__(self, xmin, xmax, ymin, ymax, ticks=-10, miniticks=False, logbase=None, mini_attr={}, **attr):
3251 self.mini_attr.update(mini_attr)
3253 Ticks.__init__(self,
None,
None,
None, ticks, miniticks,
None, logbase)
3256 self.attr.update(attr)
3259 """Apply the transformation "trans" and return an SVG object."""
3266 for t
in self.last_xticks.keys():
3267 ticksd +=
Line(t, self.ymin, t, self.
ymax).
Path(trans).d
3268 for t
in self.last_yticks.keys():
3269 ticksd +=
Line(self.xmin, t, self.
xmax, t).
Path(trans).d
3273 miniticksd +=
Line(t, self.ymin, t, self.
ymax).
Path(trans).d
3275 miniticksd +=
Line(self.xmin, t, self.
xmax, t).
Path(trans).d
3282 """Draws x error bars at a set of points. This is usually used
3283 before (under) a set of Dots at the same points.
3285 XErrorBars(d, attribute=value)
3287 d required list of (x,y,xerr...) points
3288 attribute=value pairs keyword list SVG attributes
3292 * 3 elements, the third is the symmetric error bar
3293 * 4 elements, the third and fourth are the asymmetric lower and
3294 upper error bar. The third element should be negative,
3295 e.g. (5, 5, -1, 2) is a bar from 4 to 7.
3296 * more than 4, a tick mark is placed at each value. This lets
3297 you nest errors from different sources, correlated and
3298 uncorrelated, statistical and systematic, etc.
3300 defaults = {
"stroke-width":
"0.25pt"}
3303 return "<XErrorBars (%d nodes)>" % len(self.
d)
3309 self.attr.update(attr)
3312 """Apply the transformation "trans" and return an SVG object."""
3313 if isinstance(trans, basestring): trans =
totrans(trans)
3319 if len(p) == 3: bars = [x - p[2], x + p[2]]
3320 else: bars = [x + pi
for pi
in p[2:]]
3322 start, end =
min(bars),
max(bars)
3323 output.append(
LineAxis(start, y, end, y, start, end, bars,
False,
False, **self.
attr).
SVG(trans))
3328 """Draws y error bars at a set of points. This is usually used
3329 before (under) a set of Dots at the same points.
3331 YErrorBars(d, attribute=value)
3333 d required list of (x,y,yerr...) points
3334 attribute=value pairs keyword list SVG attributes
3338 * 3 elements, the third is the symmetric error bar
3339 * 4 elements, the third and fourth are the asymmetric lower and
3340 upper error bar. The third element should be negative,
3341 e.g. (5, 5, -1, 2) is a bar from 4 to 7.
3342 * more than 4, a tick mark is placed at each value. This lets
3343 you nest errors from different sources, correlated and
3344 uncorrelated, statistical and systematic, etc.
3346 defaults = {
"stroke-width":
"0.25pt"}
3349 return "<YErrorBars (%d nodes)>" % len(self.
d)
3355 self.attr.update(attr)
3358 """Apply the transformation "trans" and return an SVG object."""
3359 if isinstance(trans, basestring): trans =
totrans(trans)
3365 if len(p) == 3: bars = [y - p[2], y + p[2]]
3366 else: bars = [y + pi
for pi
in p[2:]]
3368 start, end =
min(bars),
max(bars)
3369 output.append(
LineAxis(x, start, x, end, start, end, bars,
False,
False, **self.
attr).
SVG(trans))
def depth_first
end nested class
Abs< T >::type abs(const T &t)
static std::string join(char **cmd)
def sample
end nested class
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run