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 )"