Revision: 3620
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3620&view=rev
Author:   mdboom
Date:     2007-07-26 12:44:09 -0700 (Thu, 26 Jul 2007)

Log Message:
-----------
Lots of sizing and layout tweaks.  Support $\sqrt[3]{foo}.  The line
meets up with the check mark rather clumsily on the Agg backends due
to rounding errors and lack of subpixel placement...  will look into that.

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/lib/matplotlib/_mathtext_data.py
    trunk/matplotlib/lib/matplotlib/mathtext.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2007-07-26 18:40:36 UTC (rev 3619)
+++ trunk/matplotlib/CHANGELOG  2007-07-26 19:44:09 UTC (rev 3620)
@@ -23,8 +23,11 @@
           - Double sub/superscripts (eg. $x_i_j$) are considered
              ambiguous and raise an exception.  Use braces to disambiguate.
 
-          - $\frac{x}{y}$ can be used for displaying fractions
+          - $\frac{x}{y}$ can be used for displaying fractions.
 
+          - $\sqrt[3]{x}$ can be used to display the radical symbol
+             with a root number and body.
+
           - $\left(\frac{x}{y}\right)$ may be used to create
              parentheses and other delimiters that automatically
              resize to the height of their contents.

Modified: trunk/matplotlib/lib/matplotlib/_mathtext_data.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/_mathtext_data.py   2007-07-26 18:40:36 UTC 
(rev 3619)
+++ trunk/matplotlib/lib/matplotlib/_mathtext_data.py   2007-07-26 19:44:09 UTC 
(rev 3620)
@@ -170,6 +170,7 @@
     r'\swarrow'             : ('cmsy10', 116),
     r'\propto'              : ('cmsy10',  15),
     r'\prime'               : ('cmsy10',  73),
+    r"'"                    : ('cmsy10',  73),
     r'\infty'               : ('cmsy10',  32),
     r'\in'                  : ('cmsy10',  59),
     r'\ni'                  : ('cmsy10', 122),
@@ -253,6 +254,7 @@
     r'\prec'                : ('cmsy10',  93),
     r'\succ'                : ('cmsy10',  49),
     r'\rightarrow'          : ('cmsy10',  12),
+    r'\to'                  : ('cmsy10',  12),
     r'\spadesuit'           : ('cmsy10',   7),
     }
 

Modified: trunk/matplotlib/lib/matplotlib/mathtext.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/mathtext.py 2007-07-26 18:40:36 UTC (rev 
3619)
+++ trunk/matplotlib/lib/matplotlib/mathtext.py 2007-07-26 19:44:09 UTC (rev 
3620)
@@ -138,7 +138,8 @@
 from matplotlib.pyparsing import Literal, Word, OneOrMore, ZeroOrMore, \
      Combine, Group, Optional, Forward, NotAny, alphas, nums, alphanums, \
      StringStart, StringEnd, ParseFatalException, FollowedBy, Regex, \
-     operatorPrecedence, opAssoc, ParseResults, Or, Suppress, oneOf
+     operatorPrecedence, opAssoc, ParseResults, Or, Suppress, oneOf, \
+     ParseException, MatchFirst
 
 from matplotlib.afm import AFM
 from matplotlib.cbook import enumerate, iterable, Bunch, 
get_realpath_and_stat, \
@@ -151,13 +152,7 @@
 
 ####################
 
-# symbols that have the sub and superscripts over/under
-overunder_symbols = {
-    r'\sum'    : 1,
-    r'\int'    : 1,
-    r'\prod'   : 1,
-    r'\coprod' : 1,
-    }
+    
 # a character over another character
 charOverChars = {
     # The first 2 entires in the tuple are (font, char, sizescale) for
@@ -692,7 +687,11 @@
     def render_rect_filled(self, x1, y1, x2, y2):
         assert len(self.fonts)
         font = self.fonts.values()[0]
-        font.font.draw_rect_filled(x1, y1, max(x2 - 1, x1), max(y2 - 1, y1))
+        font.font.draw_rect_filled(
+            max(0, x1 - 1),
+            y1,
+            max(x2 - 1, x1),
+            max(y2 - 1, y1))
         
     def get_used_characters(self):
         return self.used_characters
@@ -956,14 +955,16 @@
 # The number of different sizes of chars to use, beyond which they will not
 # get any smaller
 NUM_SIZE_LEVELS = 3
-# Percentage of x-height that subscripts drop below the baseline
-SUBDROP         = 0.05
+# Percentage of x-height of additional horiz. space after sub/superscripts
+SCRIPT_SPACE    = 0.3
+# Percentage of x-height that sub/superscripts drop below the baseline
+SUBDROP         = 0.4
 # Percentage of x-height that superscripts drop below the baseline
-SUP1            = 0.2
+SUP1            = 0.7
 # Percentage of x-height that subscripts drop below the baseline
-SUB1            = 0.3
+SUB1            = 0.0
 # Percentage of x-height that superscripts are offset relative to the subscript
-DELTA           = 0.05
+DELTA           = 0.1
     
 class MathTextWarning(Warning):
     pass
@@ -991,10 +992,6 @@
     def set_link(self, other):
         self.link = other
 
-    def pack(self):
-        if self.link:
-            self.link.pack()
-
     def shrink(self):
         """Shrinks one level smaller.  There are only three levels of sizes,
         after which things will no longer get smaller."""
@@ -1062,7 +1059,10 @@
     def _update_metrics(self):
         metrics = self._metrics = self.font_output.get_metrics(
             self.font, self.c, self.fontsize, self.dpi)
-        self.width = metrics.width
+        if self.c == ' ':
+            self.width = metrics.advance
+        else:
+            self.width = metrics.width
         self.height = metrics.iceberg
         self.depth = -(metrics.iceberg - metrics.height)
         
@@ -1124,7 +1124,9 @@
                 elem = next
 
     def __repr__(self):
-        s = '[' + self.__internal_repr__() + " <%d %d %d %d> " % (self.width, 
self.height, self.depth, self.shift_amount)
+        s = '[%s <%d %d %d %d> ' % (self.__internal_repr__(),
+                                    self.width, self.height,
+                                    self.depth, self.shift_amount)
         if self.list_head:
             s += ' ' + self.list_head.__repr__()
         s += ']'
@@ -1162,6 +1164,7 @@
         Box.shrink(self)
         if self.size < NUM_SIZE_LEVELS:
             self.shift_amount *= SHRINK_FACTOR
+            self.glue_set     *= SHRINK_FACTOR
 
 class Hlist(List):
     """A horizontal list of boxes.
@@ -1186,12 +1189,6 @@
                 kern.link = next
             elem = next
 
-    def pack(self):
-        if self.list_head:
-            self.list_head.pack()
-        self.hpack()
-        Node.pack(self)
-            
     def hpack(self, w=0., m='additional'):
         """The main duty of hpack is to compute the dimensions of the
         resulting boxes, and to adjust the glue if one of those dimensions is
@@ -1265,12 +1262,6 @@
         List.__init__(self, elements)
         self.vpack()
 
-    def pack(self):
-        if self.list_head:
-            self.list_head.pack()
-        self.vpack()
-        Node.pack(self)
-        
     def vpack(self, h=0., m='additional', l=float('inf')):
         """The main duty of vpack is to compute the dimensions of the
         resulting boxes, and to adjust the glue if one of those dimensions is
@@ -1386,6 +1377,13 @@
             glue_spec = glue_spec.copy()
         self.glue_spec      = glue_spec
 
+    def shrink(self):
+        Node.shrink(self)
+        if self.size < NUM_SIZE_LEVELS:
+            if self.glue_spec.width != 0.:
+                self.glue_spec = self.glue_spec.copy()
+                self.glue_spec.width *= SHRINK_FACTOR
+        
 class GlueSpec(object):
     """@150, @151"""
     def __init__(self, width=0., stretch=0., stretch_order=0, shrink=0., 
shrink_order=0):
@@ -1406,7 +1404,7 @@
     def factory(cls, glue_type):
         return cls._types[glue_type]
     factory = classmethod(factory)
-
+    
 GlueSpec._types = {
     'fil':         GlueSpec(0., 1., 1, 0., 0),
     'fill':        GlueSpec(0., 1., 2, 0., 0),
@@ -1444,11 +1442,6 @@
     def __init__(self):
         Glue.__init__(self, 'neg_filll')
         
-class FixedGlue(Glue):
-    def __init__(self, width):
-        Glue.__init__(self, 'empty', copy=True)
-        self.glue_spec.width = width
-
 class SsGlue(Glue):
     def __init__(self):
         Glue.__init__(self, 'ss')
@@ -1463,7 +1456,7 @@
     """A convenience class to create an Vlist whose contents are centered
     within its enclosing box."""
     def __init__(self, elements):
-        Vlist.__init__(self, [Fill()] + elements + [Fill()])
+        Vlist.__init__(self, [SsGlue()] + elements + [SsGlue()])
         
 class Kern(Node):
     """A Kern node has a width field to specify a (normally negative)
@@ -1668,7 +1661,7 @@
 
 class Parser(object):
     _binary_operators = Set(r'''
-      + - *
+      + *
       \pm             \sqcap                   \rhd
       \mp             \sqcup                   \unlhd
       \times          \vee                     \unrhd
@@ -1711,6 +1704,16 @@
     _spaced_symbols = _binary_operators | _relation_symbols | _arrow_symbols
 
     _punctuation_symbols = Set(r', ; . ! \ldotp \cdotp'.split())
+
+    _overunder_symbols = Set(r'''
+       \sum \int \prod \coprod \oint \bigcap \bigcup \bigsqcup \bigvee
+       \bigwedge \bigodot \bigotimes \bigoplus \biguplus
+       '''.split()
+    )
+
+    _overunder_functions = Set(
+        r"lim liminf limsup sup max min".split()
+    )
     
     def __init__(self):
         # All forward declarations are here
@@ -1761,14 +1764,14 @@
                          r"[a-zA-Z0-9 ]",
                          r"[+\-*/]",
                          r"[<>=]",
-                         r"[:,.;!]",
-                         r"[EMAIL PROTECTED]&]",
-                         r"[[\]()]",
+                         r"[:,.;!'@[()]",
                          r"\\[$%{}]",
                        ])
                      + ")"
                      ).setParseAction(self.symbol).leaveWhitespace()
 
+        rightBracket = 
Literal("[").setParseAction(self.symbol).leaveWhitespace()
+
         accent       = Group(
                          Combine(bslash + accent)
                        + placeable
@@ -1800,11 +1803,30 @@
                      + group
                      ).setParseAction(self.frac).setName("frac")
 
+        sqrt         = Group(
+                       Suppress(
+                         bslash
+                       + Literal("sqrt")
+                       )
+                     + Optional(
+                         Suppress(Literal("["))
+                       + OneOrMore(
+                           symbol
+                         ^ font
+                         )
+                       + Suppress(Literal("]")),
+                         default = None
+                       )
+                     + group
+                     ).setParseAction(self.sqrt).setName("sqrt")
+
         placeable   <<(accent
                      ^ function  
                      ^ symbol
+                     ^ rightBracket
                      ^ group
                      ^ frac
+                     ^ sqrt
                      )
 
         simple      <<(space
@@ -1939,7 +1961,10 @@
         elif c in self._punctuation_symbols:
             return [Hlist([Char(c, self.get_state()),
                            self._make_space(0.3)])]
-        return [Char(toks[0], self.get_state())]
+        try:
+            return [Char(toks[0], self.get_state())]
+        except:
+            raise ParseException()
 
     _accent_map = {
         r'\hat'   : r'\circumflexaccent',
@@ -1971,7 +1996,7 @@
         centered.shift_amount = accent._metrics.xmin
         return Vlist([
                 centered,
-                FixedGlue(thickness * 2.0),
+                Vbox(0., thickness * 2.0),
                 Hlist([sym])
                 ])
 
@@ -1982,6 +2007,7 @@
         state.font = 'rm'
         hlist = Hlist([Char(c, state) for c in toks[0]])
         self.pop_state()
+        hlist.function_name = toks[0]
         return hlist
         
     def start_group(self, s, loc, toks):
@@ -2007,7 +2033,9 @@
 
     def is_overunder(self, nucleus):
         if isinstance(nucleus, Char):
-            return overunder_symbols.has_key(nucleus.c)
+            return nucleus.c in self._overunder_symbols
+        elif isinstance(nucleus, Hlist) and hasattr(nucleus, 'function_name'):
+            return nucleus.function_name in self._overunder_functions
         return False
     
     def subsuperscript(self, s, loc, toks):
@@ -2061,24 +2089,22 @@
             width = nucleus.width
             if super is not None:
                 super.shrink()
-                super.pack()
                 width = max(width, super.width)
             if sub is not None:
                 sub.shrink()
-                sub.pack()
                 width = max(width, sub.width)
                 
             if super is not None:
                 hlist = HCentered([super])
                 hlist.hpack(width, 'exactly')
-                vlist.extend([hlist, FixedGlue(rule_thickness * 2.0)])
+                vlist.extend([hlist, Vbox(0., rule_thickness * 2.0)])
             hlist = HCentered([nucleus])
             hlist.hpack(width, 'exactly')
             vlist.append(hlist)
             if sub is not None:
                 hlist = HCentered([sub])
                 hlist.hpack(width, 'exactly')
-                vlist.extend([FixedGlue(rule_thickness), hlist])
+                vlist.extend([Vbox(0., rule_thickness), hlist])
                 shift = hlist.height + hlist.depth + rule_thickness * 2.0
             vlist = Vlist(vlist)
             vlist.shift_amount = shift
@@ -2086,12 +2112,12 @@
             return [result]
 
         shift_up = nucleus.height - SUBDROP * xHeight
-        shift_down = nucleus.depth + SUBDROP * xHeight
+        shift_down = SUBDROP * xHeight
         if super is None:
             # @757
             sub.shrink()
             x = Hlist([sub])
-            #x.width += SCRIPT_SPACE
+            x.width += SCRIPT_SPACE * xHeight
             shift_down = max(shift_down, SUB1)
             clr = x.height - (abs(xHeight * 4.0) / 5.0)
             shift_down = max(shift_down, clr)
@@ -2099,7 +2125,7 @@
         else:
             super.shrink()
             x = Hlist([super])
-            #x.width += SCRIPT_SPACE
+            x.width += SCRIPT_SPACE * xHeight
             clr = SUP1 * xHeight
             shift_up = max(shift_up, clr)
             clr = x.depth + (abs(xHeight) / 4.0)
@@ -2109,13 +2135,13 @@
             else: # Both sub and superscript
                 sub.shrink()
                 y = Hlist([sub])
-                #y.width += SCRIPT_SPACE
+                y.width += SCRIPT_SPACE * xHeight
                 shift_down = max(shift_down, SUB1 * xHeight)
                 clr = 4.0 * rule_thickness - ((shift_up - x.depth) - (y.height 
- shift_down))
                 if clr > 0.:
                     shift_up += clr
                     shift_down += clr
-                x.shift_amount = DELTA
+                x.shift_amount = DELTA * xHeight
                 x = Vlist([x,
                            Kern((shift_up - x.depth) - (y.height - 
shift_down)),
                            y])
@@ -2127,33 +2153,78 @@
     def frac(self, s, loc, toks):
         assert(len(toks)==1)
         assert(len(toks[0])==2)
+        state = self.get_state()
+        thickness = state.font_output.get_underline_thickness(
+            state.font, state.fontsize, state.dpi)
+        
         num, den = toks[0]
         num.shrink()
         den.shrink()
         cnum = HCentered([num])
         cden = HCentered([den])
-        width = max(num.width, den.height)
+        width = max(num.width, den.width) + thickness * 10.
         cnum.hpack(width, 'exactly')
         cden.hpack(width, 'exactly')
-        state = self.get_state()
-        thickness = state.font_output.get_underline_thickness(
-            state.font, state.fontsize, state.dpi)
-        space = thickness * 3.0
         vlist = Vlist([cnum,
-                       FixedGlue(thickness * 2.0),
-                       Hrule(self.get_state()),
-                       FixedGlue(thickness * 3.0),
+                       Vbox(0, thickness * 2.0),
+                       Hrule(state),
+                       Vbox(0, thickness * 4.0),
                        cden
                        ])
 
+        # Shift so the fraction line sits in the middle of the
+        # equals sign
         metrics = state.font_output.get_metrics(
             state.font, '=', state.fontsize, state.dpi)
-        shift = cden.height - (metrics.ymax + metrics.ymin) / 2 + thickness * 
2.5
+        shift = (cden.height -
+                 (metrics.ymax + metrics.ymin) / 2 +
+                 thickness * 2.5)
         vlist.shift_amount = shift
 
-        hlist = Hlist([vlist, FixedGlue(thickness * 2.)])
+        hlist = Hlist([vlist, Hbox(thickness * 2.)])
         return [hlist]
 
+    def sqrt(self, s, loc, toks):
+        #~ print "sqrt", toks
+        root, body = toks[0]
+        state = self.get_state()
+        thickness = state.font_output.get_underline_thickness(
+            state.font, state.fontsize, state.dpi)
+
+        if root is None:
+            root = Box()
+        else:
+            root.shrink()
+            root.shrink()
+
+        # Add a little extra to the height so the body
+        # doesn't seem cramped
+        height = body.height - body.shift_amount + thickness * 5.0
+        depth = body.depth + body.shift_amount
+        check = AutoSizedDelim(r'\sqrt', height, depth, state)
+
+        height = check.height - check.shift_amount
+        depth = check.depth + check.shift_amount
+        rightside = Vlist([Hrule(state),
+                           Fill(),
+                           # Pack a little extra to the left and right
+                           # of the body
+                           Hlist([Hbox(thickness * 2.0),
+                                  body,
+                                  Hbox(thickness * 2.0)])])
+        # Stretch the glue between the hrule and the body
+        rightside.vpack(height + 1.0, depth, 'exactly')
+
+        root_vlist = Vlist([Hlist([root])])
+        root_vlist.shift_amount = -height * 0.5
+        
+        hlist = Hlist([root_vlist,
+                       Kern(-check.width * 0.5),
+                       check,
+                       Kern(-thickness * 0.5),
+                       rightside])
+        return [hlist]
+    
     def auto_sized_delimiter(self, s, loc, toks):
         #~ print "auto_sized_delimiter", toks
         front, middle, back = toks


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to