Version 1 of Rendering mathematical formulae

Updated 2002-12-18 07:56:12

Arjen Markus (18 december 2002) The script below is a first (well, second) attempt to render mathematical formulae in a canvas. It is far from complete, and if I ever finish it, it will probably end up totally differently, but it works to a certain degree with a certain class of formulae.

Several people, among whom Lars Hellstrom and Kevin Kenny, provided useful information on existing algorithms, such as the one used in TeX, and pointers to articles by Brian Kernighan [L1 ].

See also: Binomial coefficients for a quick-and-dirty solution.


# mathform.tcl -- # Simple script to render mathematical formulae #

namespace eval ::MathFormula:: {

   variable poshoriz   0
   variable offhoriz   0
   variable posvert    0
   variable offvert    0
   variable offset_add 0

}

# drawFormula -- # Draw the formula at a given position (lower-left) # # Arguments: # canvas The canvas to use # xpos X-position to start # ypos Y-position to start # formula String holding the formula # # Result: # None # # Side effect: # The formula is rendered on the screen # # proc ::MathFormula::drawFormula {canvas xpos ypos formula} {

   variable poshoriz
   variable offhoriz
   variable posvert
   variable offvert
   variable offset_add

   set poshoriz   $xpos
   set posvert    $ypos
   set offhoriz   0
   set offvert    0
   set offset_add 0

   set tokens [analyseFormula $formula]
   foreach {token xp yp advance} $tokens {
      renderToken $canvas $token $xp $yp $advance
   }

}

# renderToken -- # Render the given token # # Arguments: # canvas Canvas in which to draw # token Token to be drawn # xp X-position relative to current drawing position # yp Y-position relative to current drawing position # # Result: # None # # Side effect: # The token is drawn on the canvas # proc ::MathFormula::renderToken {canvas token xp yp advance} {

   variable poshoriz
   variable posvert
   variable offhoriz
   variable offvert

   set xpos [expr {$poshoriz+$xp}]
   set ypos [expr {$posvert+$yp}]

   switch -- $token {
   "INT" { # Integral
         set item [$canvas create line -3  9 -1 10 0 0 1 -10 3 -9 -width 1 -fill black -smooth 1]
         $canvas move $item $xpos $ypos
         incr xpos 5
       }
   "SUM" { # Sum
         set item [$canvas create line  8  2  0  2  5 -4  0 -9  8 -9 -width 1 -fill black]
         $canvas move $item $xpos $ypos
         incr xpos 8
      }
   "Inf" { # Infinity
         set item1 [$canvas create oval -5 -2  0  2  -outline black]
         set item2 [$canvas create oval  0 -2  5  2  -outline black]
         $canvas move $item1 $xpos $ypos
         $canvas move $item2 $xpos $ypos
      }
   default { # Letters
         set item [$canvas create text $xpos $ypos -text $token -fill black -anchor w -font "Times 12"]
         set bbox [$canvas bbox $item]
         set width [expr {[lindex $bbox 2]-[lindex $bbox 0]}]
         set xp   $width
         set xpos [expr {$xpos+$width}]
      }
   }

   if { $advance } {
      set poshoriz $xpos
   } else {
      set offhoriz [expr {$xp>$offhoriz? $xp : $offhoriz}]
   }

}

# analyseFormula -- # Analyse the formula and determine the positions of the tokens # # Arguments: # formula Formula to be drawn # # Result: # List of tokens with relative positions and advancing information # proc ::MathFormula::analyseFormula {formula} {

   set result  [list]
   set advance 1
   set xp      0
   set yp      0

   foreach token $formula {
      switch -- $token {
      "_" { # Subscript
            set yp 5
            set advance 0
            continue
          }
      "^" { # Superscript
            set yp -3
            set advance 0
            continue
          }
      "~" { # Force space
            set token   " "
            set advance 1
          }
      "`" { # Force extra space
            incr xp     1
            set advance 1
            continue
          }
      "INT" { # Integral
            set xp  0
            set yp  0
            set advance 1
          }
      "SUM" { # Sum
            set xp  0
            set yp  2
            set advance 1
          }
      "from" { # Lower bound
            set xp -10
            set yp  14
            set advance 0
            continue
          }
      "to" { # Upper bound
            set xp    1
            set yp  -10
            set advance 0
            continue
          }
      "Inf" { # Infinity
            incr yp     -2  ;# Increase, not set!
            set advance  1
          }
      default { # Letters
            set advance 1
         }
      }
      lappend result $token $xp $yp $advance
      if { $advance } {
         set xp 0
         set yp 0
      }
   }

   return $result

}

# Main -- # Set up a canvas, analyse the formula and draw it #

canvas .c -background white pack .c -fill both

set pi "\u3c0" set alpha "\u3b1" set DELTA "\u394" set omega "\u3c9" set string "SUM from i=0 to Inf ~ A _ i ~ = ~ INT from 0 to $pi cos ^ 2 x dx"

::MathFormula::drawFormula .c 10 20 "Not ~ p ` erfect ~ yet, ~ but ..." ::MathFormula::drawFormula .c 10 60 $string ::MathFormula::drawFormula .c 10 100 "0.1 = e ^ -kT90" ::MathFormula::drawFormula .c 10 140 "f(x) = x ^ 3 - x ^ 2 + x - 1" ::MathFormula::drawFormula .c 10 180 "h(t) = cos( $omega t - $alpha )"


[ Category Mathematics | Category Graphics ]