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 H and Kevin Kenny, provided useful information on existing algorithms, such as the one used in TeX, and pointers to articles by Brian Kernighan [1].Note: the picture shows the result of an earlier version. The one appearing below uses unicodes which have a very nice look, certainly on Windows. Again, far from complete.See also: Binomial coefficients for a quick-and-dirty solution.
AM (21 march 2005) I realised the other day that the code in Drawing Diagrams (or at least the sofar unpublished version) is a suitable replacement - it will allow me to follow Kernighan's footsteps ... For now, I will have to sit down and implement these ideas!AM (10 january 2007) I finally started implementing these ideas ... The program I have now is not ready yet, but it is getting into shape nicely.
FF (25 february 2007) I suggest you looking at the W3C MathML Page [2] where you can find much useful resources. I seem cannot find a program that directly generates an image from a MathML expression; thus this may be a good start for doing a MathML widget. Is anyone seriously interested?AM (26 februari 2007) I am ;) even though I find MathML to be, well, rather verbose for the job.FF (27 february 2007) well, in fact I meant to point you to some of its implementations: it should give you nice ideas looking at the existing renderer's code (not properly at the Mozilla's one, which could be surprisingly huge ;)).FF (13 may 2007) I added a little proc in the example MimeTexPreview for interfacing mimetex with Tcl canvas. Give it a check!
# 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 variable symbol array set symbol { ALPHA \u391 BETA \u392 GAMMA \u393 DELTA \u394 EPSILON \u395 ZETA \u396 ETA \u397 THETA \u398 IOTA \u399 KAPPA \u39a LAMBDA \u39b MU \u39c NU \u39d XI \u39e OMICRON \u39f PI \u400 RHO \u401 SIGMA \u403 TAU \u404 UPSILON \u405 PHI \u406 CHI \u407 PSI \u408 OMEGA \u409 alpha \u3b1 beta \u3b2 gamma \u3b3 delta \u3b4 epsilon \u3b5 zeta \u3b6 eta \u3b7 theta \u3b8 iota \u3b9 kappa \u3ba lambda \u3bb mu \u3bc nu \u3bd xi \u3be omicron \u3bf pi \u3c0 rho \u3c1 sigma \u3c3 tau \u3c4 upsilon \u3c5 phi \u3c6 chi \u3c7 psi \u3c8 omega \u3c9 NABLA \u2207 DEL \u2207 SUM \u2211 PROD \u220f INT \u222b Inf \u221e PARTIAL \u2202 ALL \u2200 EXIST \u2203 EMPTY \u2205 MEMBER \u2208 NOTMEMBER \u2209 APPROX \u224b != \u2260 <= \u2264 >= \u2265 << \u226a >> \u226b SQRT \u221a // \u221a 3// \u221b 4// \u221c DOT \u2219 INTERSECT \u2229 UNION \u222a ORDER \u223e AND \u22c0 OR \u22c1 } } # 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 variable symbol set xpos [expr {$poshoriz+$xp}] set ypos [expr {$posvert+$yp}] if { [info exists symbol($token)] } { set unicode $symbol($token) } else { set unicode $token } switch -- $token { "INT" { # Integral set item [$canvas create text $xpos $ypos -font "Times 12" -text $unicode] incr xpos 5 } "SUM" { # Sum set item [$canvas create text $xpos $ypos -font "Times 12" -text $unicode] incr xpos 8 } default { # Letters and other ordinary symbols set item [$canvas create text $xpos $ypos -text $unicode -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 xtop -5 set ytop -12 set xbot -8 set ybot 14 set advance 1 } "SUM" - "PROD" { # Sum & Product set xp 0 set yp 0 set xtop -20 set ytop -12 set xbot -12 set ybot 16 set advance 1 } "from" { # Lower bound set xp $xbot set yp $ybot set advance 0 continue } "to" { # Upper bound set xp $xtop set yp $ytop set advance 0 continue } 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 -height 400 pack .c -fill both 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 )" ::MathFormula::drawFormula .c 10 220 \ "PARTIAL phi / PARTIAL t = D NABLA ^ 2 phi" ::MathFormula::drawFormula .c 10 260 "PROD (1-1/n) ^ n" ::MathFormula::drawFormula .c 10 300 "ALL x MEMBER Z ~ EXIST y MEMBER Z ~ x >= y"