CMS 3D CMS Logo

Public Member Functions | Public Attributes | Static Public Attributes

svgfig::Ticks Class Reference

Inheritance diagram for svgfig::Ticks:
svgfig::CurveAxis svgfig::Grid svgfig::HGrid svgfig::LineAxis svgfig::VGrid svgfig::XAxis svgfig::YAxis

List of all members.

Public Member Functions

def __init__
def __repr__
def compute_logminiticks
def compute_logticks
def compute_miniticks
def compute_ticks
def interpret
def orient_tickmark
def regular_miniticks
def SVG

Public Attributes

 arrow_end
 arrow_start
 attr
 f
 high
 labels
 last_miniticks
 logbase
 low
 miniticks
 text_attr
 ticks

Static Public Attributes

dictionary defaults = {"stroke-width":"0.25pt"}
float minitick_end = 0.75
float minitick_start = 0.75
int text_angle = 0
dictionary text_defaults = {"stroke":"none", "fill":"black", "font-size":5}
float text_start = 2.5
float tick_end = 1.5
float tick_start = 1.5

Detailed Description

Superclass for all graphics primatives that draw ticks,
miniticks, and tick labels.  This class only draws the ticks.

Ticks(f, low, high, ticks, miniticks, labels, logbase, arrow_start,
      arrow_end, text_attr, attribute=value)

f                       required        parametric function along which ticks
                                        will be drawn; has the same format as
                                        the function used in Curve
low, high               required        range of the independent variable
ticks                   default=-10     request ticks according to the standard
                                        tick specification (see below)
miniticks               default=True    request miniticks according to the
                                        standard minitick specification (below)
labels                  True            request tick labels according to the
                                        standard tick label specification (below)
logbase                 default=None    if a number, the axis is logarithmic with
                                        ticks at the given base (usually 10)
arrow_start             default=None    if a new string identifier, draw an arrow
                                        at the low-end of the axis, referenced by
                                        that identifier; if an SVG marker object,
                                        use that marker
arrow_end               default=None    if a new string identifier, draw an arrow
                                        at the high-end of the axis, referenced by
                                        that identifier; if an SVG marker object,
                                        use that marker
text_attr               default={}      SVG attributes for the text labels
attribute=value pairs   keyword list    SVG attributes for the tick marks 

Standard tick specification:

    * True: same as -10 (below).
    * Positive number N: draw exactly N ticks, including the endpoints. To
      subdivide an axis into 10 equal-sized segments, ask for 11 ticks.
    * Negative number -N: draw at least N ticks. Ticks will be chosen with
      "natural" values, multiples of 2 or 5.
    * List of values: draw a tick mark at each value.
    * Dict of value, label pairs: draw a tick mark at each value, labeling
      it with the given string. This lets you say things like {3.14159: "pi"}.
    * False or None: no ticks.

Standard minitick specification:

    * True: draw miniticks with "natural" values, more closely spaced than
      the ticks.
    * Positive number N: draw exactly N miniticks, including the endpoints.
      To subdivide an axis into 100 equal-sized segments, ask for 101 miniticks.
    * Negative number -N: draw at least N miniticks.
    * List of values: draw a minitick mark at each value.
    * False or None: no miniticks. 

Standard tick label specification:

    * True: use the unumber function (described below)
    * Format string: standard format strings, e.g. "%5.2f" for 12.34
    * Python callable: function that converts numbers to strings
    * False or None: no labels 

Definition at line 2320 of file svgfig.py.


Constructor & Destructor Documentation

def svgfig::Ticks::__init__ (   self,
  f,
  low,
  high,
  ticks = -10,
  miniticks = True,
  labels = True,
  logbase = None,
  arrow_start = None,
  arrow_end = None,
  text_attr = {},
  attr 
)

Reimplemented in svgfig::CurveAxis.

Definition at line 2391 of file svgfig.py.

02391                                                                                                                                      {}, **attr):
02392     self.f = f
02393     self.low = low
02394     self.high = high
02395     self.ticks = ticks
02396     self.miniticks = miniticks
02397     self.labels = labels
02398     self.logbase = logbase
02399     self.arrow_start = arrow_start
02400     self.arrow_end = arrow_end
02401 
02402     self.attr = dict(self.defaults)
02403     self.attr.update(attr)
02404 
02405     self.text_attr = dict(self.text_defaults)
02406     self.text_attr.update(text_attr)
02407 

Member Function Documentation

def svgfig::Ticks::__repr__ (   self)

Reimplemented in svgfig::CurveAxis, svgfig::LineAxis, svgfig::XAxis, svgfig::YAxis, svgfig::HGrid, svgfig::VGrid, and svgfig::Grid.

Definition at line 2388 of file svgfig.py.

02389                     :
02390     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)

def svgfig::Ticks::compute_logminiticks (   self,
  base 
)
Return optimal logarithmic miniticks, given a set of ticks.

Normally only used internally.

Definition at line 2769 of file svgfig.py.

02770                                       :
02771     """Return optimal logarithmic miniticks, given a set of ticks.
02772 
02773     Normally only used internally.
02774     """
02775     if self.low >= self.high: raise ValueError, "low must be less than high"
02776 
02777     lowN = math.floor(math.log(self.low, base))
02778     highN = math.ceil(math.log(self.high, base))
02779     output = []
02780     num_ticks = 0
02781     for n in range(int(lowN), int(highN)+1):
02782       x = base**n
02783       if self.low <= x <= self.high: num_ticks += 1
02784       for m in range(2, int(math.ceil(base))):
02785         minix = m * x
02786         if self.low <= minix <= self.high: output.append(minix)
02787 
02788     if num_ticks <= 2: return []
02789     else: return output

def svgfig::Ticks::compute_logticks (   self,
  base,
  N,
  format 
)
Return less than -N or exactly N optimal logarithmic ticks.

Normally only used internally.

Definition at line 2718 of file svgfig.py.

02719                                              :
02720     """Return less than -N or exactly N optimal logarithmic ticks.
02721 
02722     Normally only used internally.
02723     """
02724     if self.low >= self.high: raise ValueError, "low must be less than high"
02725     if N == 1: raise ValueError, "N can be 0 or >1 to specify the exact number of ticks or negative to specify a maximum"
02726 
02727     eps = _epsilon * (self.high - self.low)
02728 
02729     if N >= 0:
02730       output = {}
02731       x = self.low
02732       for i in xrange(N):
02733         if format == unumber and abs(x) < eps: label = u"0"
02734         else: label = format(x)
02735         output[x] = label
02736         x += (self.high - self.low)/(N-1.)
02737       return output
02738 
02739     N = -N
02740 
02741     lowN = math.floor(math.log(self.low, base))
02742     highN = math.ceil(math.log(self.high, base))
02743     output = {}
02744     for n in range(int(lowN), int(highN)+1):
02745       x = base**n
02746       label = format(x)
02747       if self.low <= x <= self.high: output[x] = label
02748 
02749     for i in range(1, len(output)):
02750       keys = output.keys()
02751       keys.sort()
02752       keys = keys[::i]
02753       values = map(lambda k: output[k], keys)
02754       if len(values) <= N:
02755         for k in output.keys():
02756           if k not in keys:
02757             output[k] = ""
02758         break
02759 
02760     if len(output) <= 2:
02761       output2 = self.compute_ticks(N=-int(math.ceil(N/2.)), format=format)
02762       lowest = min(output2)
02763 
02764       for k in output:
02765         if k < lowest: output2[k] = output[k]
02766       output = output2
02767 
02768     return output

def svgfig::Ticks::compute_miniticks (   self,
  original_ticks 
)
Return optimal linear miniticks, given a set of ticks.

Normally only used internally.

Definition at line 2689 of file svgfig.py.

02690                                              :
02691     """Return optimal linear miniticks, given a set of ticks.
02692 
02693     Normally only used internally.
02694     """
02695     if len(original_ticks) < 2: original_ticks = ticks(self.low, self.high)
02696     original_ticks = original_ticks.keys()
02697     original_ticks.sort()
02698 
02699     if self.low > original_ticks[0] + _epsilon or self.high < original_ticks[-1] - _epsilon:
02700       raise ValueError, "original_ticks {%g...%g} extend beyond [%g, %g]" % (original_ticks[0], original_ticks[-1], self.low, self.high)
02701 
02702     granularities = []
02703     for i in range(len(original_ticks)-1):
02704       granularities.append(original_ticks[i+1] - original_ticks[i])
02705     spacing = 10**(math.ceil(math.log10(min(granularities)) - 1))
02706 
02707     output = []
02708     x = original_ticks[0] - math.ceil(1.*(original_ticks[0] - self.low) / spacing) * spacing
02709 
02710     while x <= self.high:
02711       if x >= self.low:
02712         already_in_ticks = False
02713         for t in original_ticks:
02714           if abs(x-t) < _epsilon * (self.high - self.low): already_in_ticks = True
02715         if not already_in_ticks: output.append(x)
02716       x += spacing
02717     return output

def svgfig::Ticks::compute_ticks (   self,
  N,
  format 
)
Return less than -N or exactly N optimal linear ticks.

Normally only used internally.

Definition at line 2601 of file svgfig.py.

02602                                     :
02603     """Return less than -N or exactly N optimal linear ticks.
02604 
02605     Normally only used internally.
02606     """
02607     if self.low >= self.high: raise ValueError, "low must be less than high"
02608     if N == 1: raise ValueError, "N can be 0 or >1 to specify the exact number of ticks or negative to specify a maximum"
02609 
02610     eps = _epsilon * (self.high - self.low)
02611 
02612     if N >= 0:
02613       output = {}
02614       x = self.low
02615       for i in xrange(N):
02616         if format == unumber and abs(x) < eps: label = u"0"
02617         else: label = format(x)
02618         output[x] = label
02619         x += (self.high - self.low)/(N-1.)
02620       return output
02621 
02622     N = -N
02623 
02624     counter = 0
02625     granularity = 10**math.ceil(math.log10(max(abs(self.low), abs(self.high))))
02626     lowN = math.ceil(1.*self.low / granularity)
02627     highN = math.floor(1.*self.high / granularity)
02628 
02629     while (lowN > highN):
02630       countermod3 = counter % 3
02631       if countermod3 == 0: granularity *= 0.5
02632       elif countermod3 == 1: granularity *= 0.4
02633       else: granularity *= 0.5
02634       counter += 1
02635       lowN = math.ceil(1.*self.low / granularity)
02636       highN = math.floor(1.*self.high / granularity)
02637 
02638     last_granularity = granularity
02639     last_trial = None
02640 
02641     while True:
02642       trial = {}
02643       for n in range(int(lowN), int(highN)+1):
02644         x = n * granularity
02645         if format == unumber and abs(x) < eps: label = u"0"
02646         else: label = format(x)
02647         trial[x] = label
02648 
02649       if int(highN)+1 - int(lowN) >= N:
02650         if last_trial == None:
02651           v1, v2 = self.low, self.high
02652           return {v1: format(v1), v2: format(v2)}
02653         else:
02654           low_in_ticks, high_in_ticks = False, False
02655           for t in last_trial.keys():
02656             if 1.*abs(t - self.low)/last_granularity < _epsilon: low_in_ticks = True
02657             if 1.*abs(t - self.high)/last_granularity < _epsilon: high_in_ticks = True
02658 
02659           lowN = 1.*self.low / last_granularity
02660           highN = 1.*self.high / last_granularity
02661           if abs(lowN - round(lowN)) < _epsilon and not low_in_ticks:
02662             last_trial[self.low] = format(self.low)
02663           if abs(highN - round(highN)) < _epsilon and not high_in_ticks:
02664             last_trial[self.high] = format(self.high)
02665           return last_trial
02666 
02667       last_granularity = granularity
02668       last_trial = trial
02669 
02670       countermod3 = counter % 3
02671       if countermod3 == 0: granularity *= 0.5
02672       elif countermod3 == 1: granularity *= 0.4
02673       else: granularity *= 0.5
02674       counter += 1
02675       lowN = math.ceil(1.*self.low / granularity)
02676       highN = math.floor(1.*self.high / granularity)

def svgfig::Ticks::interpret (   self)
Evaluate and return optimal ticks and miniticks according to
the standard minitick specification.

Normally only used internally.

Reimplemented in svgfig::LineAxis.

Definition at line 2507 of file svgfig.py.

02508                      :
02509     """Evaluate and return optimal ticks and miniticks according to
02510     the standard minitick specification.
02511 
02512     Normally only used internally.
02513     """
02514 
02515     if self.labels == None or self.labels == False:
02516       format = lambda x: ""
02517 
02518     elif self.labels == True:
02519       format = unumber
02520 
02521     elif isinstance(self.labels, basestring):
02522       format = lambda x: (self.labels % x)
02523 
02524     elif callable(self.labels):
02525       format = self.labels
02526 
02527     else: raise TypeError, "labels must be None/False, True, a format string, or a number->string function"
02528 
02529     # Now for the ticks
02530     ticks = self.ticks
02531 
02532     # Case 1: ticks is None/False
02533     if ticks == None or ticks == False: return {}, []
02534 
02535     # Case 2: ticks is the number of desired ticks
02536     elif isinstance(ticks, (int, long)):
02537       if ticks == True: ticks = -10
02538 
02539       if self.logbase == None:
02540         ticks = self.compute_ticks(ticks, format)
02541       else:
02542         ticks = self.compute_logticks(self.logbase, ticks, format)
02543 
02544       # Now for the miniticks
02545       if self.miniticks == True:
02546         if self.logbase == None:
02547           return ticks, self.compute_miniticks(ticks)
02548         else:
02549           return ticks, self.compute_logminiticks(self.logbase)
02550 
02551       elif isinstance(self.miniticks, (int, long)):
02552         return ticks, self.regular_miniticks(self.miniticks)
02553 
02554       elif getattr(self.miniticks, "__iter__", False):
02555         return ticks, self.miniticks
02556 
02557       elif self.miniticks == False or self.miniticks == None:
02558         return ticks, []
02559 
02560       else:
02561         raise TypeError, "miniticks must be None/False, True, a number of desired miniticks, or a list of numbers"
02562         
02563     # Cases 3 & 4: ticks is iterable
02564     elif getattr(ticks, "__iter__", False):
02565 
02566       # Case 3: ticks is some kind of list
02567       if not isinstance(ticks, dict):
02568         output = {}
02569         eps = _epsilon * (self.high - self.low)
02570         for x in ticks:
02571           if format == unumber and abs(x) < eps:
02572             output[x] = u"0"
02573           else:
02574             output[x] = format(x)
02575         ticks = output
02576 
02577       # Case 4: ticks is a dict
02578       else: pass
02579 
02580       # Now for the miniticks
02581       if self.miniticks == True:
02582         if self.logbase == None:
02583           return ticks, self.compute_miniticks(ticks)
02584         else:
02585           return ticks, self.compute_logminiticks(self.logbase)
02586 
02587       elif isinstance(self.miniticks, (int, long)):
02588         return ticks, self.regular_miniticks(self.miniticks)
02589 
02590       elif getattr(self.miniticks, "__iter__", False):
02591         return ticks, self.miniticks
02592 
02593       elif self.miniticks == False or self.miniticks == None:
02594         return ticks, []
02595 
02596       else:
02597         raise TypeError, "miniticks must be None/False, True, a number of desired miniticks, or a list of numbers"
02598         
02599     else:
02600       raise TypeError, "ticks must be None/False, a number of desired ticks, a list of numbers, or a dictionary of explicit markers"

def svgfig::Ticks::orient_tickmark (   self,
  t,
  trans = None 
)
Return the position, normalized local x vector, normalized
local y vector, and angle of a tick at position t.

Normally only used internally.

Definition at line 2408 of file svgfig.py.

02409                                           :
02410     """Return the position, normalized local x vector, normalized
02411     local y vector, and angle of a tick at position t.
02412 
02413     Normally only used internally.
02414     """
02415     if isinstance(trans, basestring): trans = totrans(trans)
02416     if trans == None:
02417       f = self.f
02418     else:
02419       f = lambda t: trans(*self.f(t))
02420 
02421     eps = _epsilon * abs(self.high - self.low)
02422 
02423     X, Y = f(t)
02424     Xprime, Yprime = f(t + eps)
02425     xhatx, xhaty = (Xprime - X)/eps, (Yprime - Y)/eps
02426 
02427     norm = math.sqrt(xhatx**2 + xhaty**2)
02428     if norm != 0: xhatx, xhaty = xhatx/norm, xhaty/norm
02429     else: xhatx, xhaty = 1., 0.
02430 
02431     angle = math.atan2(xhaty, xhatx) + math.pi/2.
02432     yhatx, yhaty = math.cos(angle), math.sin(angle)
02433 
02434     return (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle

def svgfig::Ticks::regular_miniticks (   self,
  N 
)
Return exactly N linear ticks.

Normally only used internally.

Definition at line 2677 of file svgfig.py.

02678                                 :
02679     """Return exactly N linear ticks.
02680 
02681     Normally only used internally.
02682     """
02683     output = []
02684     x = self.low
02685     for i in xrange(N):
02686       output.append(x)
02687       x += (self.high - self.low)/(N-1.)
02688     return output

def svgfig::Ticks::SVG (   self,
  trans = None 
)
Apply the transformation "trans" and return an SVG object.

Reimplemented in svgfig::CurveAxis, svgfig::LineAxis, svgfig::XAxis, svgfig::YAxis, svgfig::HGrid, svgfig::VGrid, and svgfig::Grid.

Definition at line 2435 of file svgfig.py.

02436                            :
02437     """Apply the transformation "trans" and return an SVG object."""
02438     if isinstance(trans, basestring): trans = totrans(trans)
02439 
02440     self.last_ticks, self.last_miniticks = self.interpret()
02441     tickmarks = Path([], **self.attr)
02442     minitickmarks = Path([], **self.attr)
02443     output = SVG("g")
02444 
02445     if (self.arrow_start != False and self.arrow_start != None) or (self.arrow_end != False and self.arrow_end != None):
02446       defs = SVG("defs")
02447 
02448       if self.arrow_start != False and self.arrow_start != None:
02449         if isinstance(self.arrow_start, SVG):
02450           defs.append(self.arrow_start)
02451         elif isinstance(self.arrow_start, basestring):
02452           defs.append(make_marker(self.arrow_start, "arrow_start"))
02453         else:
02454           raise TypeError, "arrow_start must be False/None or an id string for the new marker"
02455 
02456       if self.arrow_end != False and self.arrow_end != None:
02457         if isinstance(self.arrow_end, SVG):
02458           defs.append(self.arrow_end)
02459         elif isinstance(self.arrow_end, basestring):
02460           defs.append(make_marker(self.arrow_end, "arrow_end"))
02461         else:
02462           raise TypeError, "arrow_end must be False/None or an id string for the new marker"
02463 
02464       output.append(defs)
02465 
02466     eps = _epsilon * (self.high - self.low)
02467 
02468     for t, label in self.last_ticks.items():
02469       (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle = self.orient_tickmark(t, trans)
02470       
02471       if (not self.arrow_start or abs(t - self.low) > eps) and (not self.arrow_end or abs(t - self.high) > eps):
02472         tickmarks.d.append(("M", X - yhatx*self.tick_start, Y - yhaty*self.tick_start, True))
02473         tickmarks.d.append(("L", X - yhatx*self.tick_end, Y - yhaty*self.tick_end, True))
02474 
02475       angle = (angle - math.pi/2.)*180./math.pi + self.text_angle
02476 
02477       ########### a HACK! ############ (to be removed when Inkscape handles baselines)
02478       if _hacks["inkscape-text-vertical-shift"]:
02479         if self.text_start > 0:
02480           X += math.cos(angle*math.pi/180. + math.pi/2.) * 2.
02481           Y += math.sin(angle*math.pi/180. + math.pi/2.) * 2.
02482         else:
02483           X += math.cos(angle*math.pi/180. + math.pi/2.) * 2. * 2.5
02484           Y += math.sin(angle*math.pi/180. + math.pi/2.) * 2. * 2.5
02485       ########### end hack ###########
02486 
02487       if label != "":
02488         output.append(SVG("text", label, transform="translate(%g, %g) rotate(%g)" % \
02489                           (X - yhatx*self.text_start, Y - yhaty*self.text_start, angle), **self.text_attr))
02490 
02491     for t in self.last_miniticks:
02492       skip = False
02493       for tt in self.last_ticks.keys():
02494         if abs(t - tt) < eps:
02495           skip = True
02496           break
02497       if not skip:
02498         (X, Y), (xhatx, xhaty), (yhatx, yhaty), angle = self.orient_tickmark(t, trans)
02499 
02500       if (not self.arrow_start or abs(t - self.low) > eps) and (not self.arrow_end or abs(t - self.high) > eps):
02501         minitickmarks.d.append(("M", X - yhatx*self.minitick_start, Y - yhaty*self.minitick_start, True))
02502         minitickmarks.d.append(("L", X - yhatx*self.minitick_end, Y - yhaty*self.minitick_end, True))
02503 
02504     output.prepend(tickmarks.SVG(trans))
02505     output.prepend(minitickmarks.SVG(trans))
02506     return output


Member Data Documentation

Definition at line 2391 of file svgfig.py.

Definition at line 2391 of file svgfig.py.

Reimplemented in svgfig::HGrid, svgfig::VGrid, and svgfig::Grid.

Definition at line 2391 of file svgfig.py.

dictionary svgfig::Ticks::defaults = {"stroke-width":"0.25pt"} [static]

Reimplemented in svgfig::LineAxis.

Definition at line 2391 of file svgfig.py.

Reimplemented in svgfig::LineAxis, and svgfig::Grid.

Definition at line 2391 of file svgfig.py.

Definition at line 2391 of file svgfig.py.

Reimplemented in svgfig::HGrid, and svgfig::VGrid.

Definition at line 2435 of file svgfig.py.

Definition at line 2391 of file svgfig.py.

Reimplemented in svgfig::LineAxis.

Definition at line 2391 of file svgfig.py.

float svgfig::Ticks::minitick_end = 0.75 [static]

Definition at line 2384 of file svgfig.py.

float svgfig::Ticks::minitick_start = 0.75 [static]

Definition at line 2383 of file svgfig.py.

Definition at line 2391 of file svgfig.py.

int svgfig::Ticks::text_angle = 0 [static]

Reimplemented in svgfig::XAxis, and svgfig::YAxis.

Definition at line 2386 of file svgfig.py.

Definition at line 2391 of file svgfig.py.

dictionary svgfig::Ticks::text_defaults = {"stroke":"none", "fill":"black", "font-size":5} [static]

Reimplemented in svgfig::CurveAxis, svgfig::LineAxis, svgfig::XAxis, and svgfig::YAxis.

Definition at line 2380 of file svgfig.py.

float svgfig::Ticks::text_start = 2.5 [static]

Reimplemented in svgfig::XAxis, and svgfig::YAxis.

Definition at line 2385 of file svgfig.py.

float svgfig::Ticks::tick_end = 1.5 [static]

Definition at line 2382 of file svgfig.py.

float svgfig::Ticks::tick_start = 1.5 [static]

Definition at line 2381 of file svgfig.py.

Definition at line 2391 of file svgfig.py.