The basic idea for recursively drawing equations is simple. Well, sort of simple.

Classes represent different kinds of equations. A particular class knows how to draw its kind of equation. It uses other classes to draw its pieces.

For example, the `FractionEquation` class draws a horizontal line with a numerator on top and a denominator on the bottom. It uses other objects to draw the numerator and denominator.

The following abstract `Equation` base class defines three methods: `SetFontSizes`, `GetSize`, and `Draw`.

abstract class Equation { // The font size used by this equation. public float FontSize = 20; // Set font sizes for sub-equations. public abstract void SetFontSizes(float font_size); // Return the equation's size. public abstract SizeF GetSize(Graphics gr, Font font); // Draw the equation. public abstract void Draw(Graphics gr, Font font, Pen pen, Brush brush, float x, float y); }

The `SetFontSizes` method sets the equation’s `FontSize` value. If the equation is made up of sub-equations, as most of the equation classes are, this method calls the sub-equations’ `SetFontSizes` methods passing them scaled font sizes.

The `GetSize` method calculates the equation’s size. If the equation includes sub-equations, it calls their `GetSize` methods to get their sizes and then uses them to calculate this equation’s size.

The `Draw` method draws the equation. If the equation includes sub-equations, it calls their `Draw` methods to draw them.

Subclasses of `Equation` implement these methods according to how they draw their particular kind of equation. The example program defines these classes:

StringEquation | Draws a string. This is the end of the chain of recursion. |

BarEquation | Draws vertical elements to the left and right of its contents. The elements can be bars, brackets, braces, pointy brackets, parentheses, or omitted. |

FractionEquation | Draws an equation above another with an optional horizontal line in between. |

IntegralEquation | Draws an integral symbol with equations specifying the value inside the integral and the values above and below the symbol. |

MatrixEquation | Draws equations in an array, optionally making all rows or columns the same size. |

PowerEquation | Draws an equation to the power of another equation. |

RootEquation | Draws a root with equations for the index and radicand. |

SigmaEquation | Draws a sigma summation with equations specifying the value inside the summation and the values above and below the sigma. |

The `StringEquation` class shown in the following code is the most basic. It simply draws a string.

// Draw some text. class StringEquation : Equation { // The text to draw. private string Text; // Initialize the text. public StringEquation(string text) { Text = text; } // Set font sizes for sub-equations. public override void SetFontSizes(float font_size) { FontSize = font_size; } // Return the equation's size. public override SizeF GetSize(Graphics gr, Font font) { using (Font new_font = new Font(font.FontFamily, FontSize, font.Style)) { return gr.MeasureString(Text, new_font); } } // Draw the equation. public override void Draw(Graphics gr, Font font, Pen pen, Brush brush, float x, float y) { using (Font new_font = new Font(font.FontFamily, FontSize, font.Style)) { gr.DrawString(Text, new_font, brush, x, y); } } }

This class provides some constructors. Its `SetFontSizes` method simply saves the font size. The `GetSize` method creates a font of the correct size and then measures the object’s string using that font. The `Draw` method creates a font of the correct size and then draws the string.

The following code shows a less trivial example: the `FractionEquation` class.

// Draw one item over another. class FractionEquation : Equation { // True to draw a separator line. public bool DrawSeparator; // The space between the top and bottom items. private const float Gap = 0; // Extra width for the separator (on each side). private const float ExtraWidth = 6; // The items to draw. private Equation Numerator, Denominator; // Initialize a new object. public FractionEquation(Equation top_item, Equation bottom_item, bool draw_separator) { Numerator = top_item; Denominator = bottom_item; DrawSeparator = draw_separator; } // Initialize a new object. public FractionEquation(string top_string, string bottom_string, bool draw_separator) : this(new StringEquation(top_string), new StringEquation(bottom_string), draw_separator) { } // Set font sizes for sub-equations. public override void SetFontSizes(float font_size) { FontSize = font_size; Numerator.SetFontSizes(font_size * 0.75f); Denominator.SetFontSizes(font_size * 0.75f); } // Return the object's size. public override SizeF GetSize(Graphics gr, Font font) { // Get the sizes of the items. SizeF top_size, bottom_size; float width, height; GetSizes(gr, font, out top_size, out bottom_size, out width, out height); // Calculate our size. return new SizeF(width, height); } // Draw the equation. public override void Draw(Graphics gr, Font font, Pen pen, Brush brush, float x, float y) { // Get the sizes of the items. SizeF top_size, bottom_size; float width, height; GetSizes(gr, font, out top_size, out bottom_size, out width, out height); // Draw the separator. if (DrawSeparator) { float separator_y = y + top_size.Height + Gap / 2; gr.DrawLine(pen, x, separator_y, x + width, separator_y); } // Draw the top. float top_x = x + (width - top_size.Width) / 2; Numerator.Draw(gr, font, pen, brush, top_x, y); // Draw the bottom. float bottom_x = x + (width - bottom_size.Width) / 2; float bottom_y = y + top_size.Height + Gap; Denominator.Draw(gr, font, pen, brush, bottom_x, bottom_y); } // Return various sizes. private void GetSizes(Graphics gr, Font font, out SizeF top_size, out SizeF bottom_size, out float width, out float height) { top_size = Numerator.GetSize(gr, font); bottom_size = Denominator.GetSize(gr, font); width = Math.Max(top_size.Width, bottom_size.Width) + 2 * ExtraWidth; height = top_size.Height + bottom_size.Height + Gap; } }

This class begins with some parameters that help determine how it draws a fraction. Its constructors initialize those settings.

The class’s `Numerator` and `Denominator` values hold `Equation` subclasses that should be drawn for the top and bottom of the fraction.

The `SetFontSizes` method saves the font size and then calls its sub-equations’ `SetFontSizes` methods passing them the font size scaled by 0.75. That makes the font used by the sub-equations slightly smaller and produces a nicer result. (This is even more important for some of the other equation types such as `IntegralEquation` and `SigmaEquation` where the results look really weird if the integral or summation’s limit equations are not in a smaller font.)

The `GetSize` method calls `GetSizes` to calculate the sizes of the `Numerator` and `Denominator`. It then uses those sizes to calculate the whole `FractionEquation` object’s size.

The `Draw` method also calls `GetSizes` to get the sizes of the `Numerator` and `Denominator`. It then draws the fraction. It calls the `Numerator` object’s and the `Denominator` object’s `Draw` methods to make them draw themselves.

The `GetSizes` method calls the `Numerator` object’s and `Denominator` object’s `GetSize` methods to get their sizes. It then adds some room for the line between the two and returns the combined fraction’s size.

The other equation subclasses work similarly. In each subclass:

- The
`SetFontSizes`method calls the sub-equations’`SetFontSizes`methods, possibly passing them a scaled down font size. - The
`GetSize`method calls sub-equations’`GetSize`methods and then uses the geometry of the particular subclass to decide how those sizes combine to make the whole equation’s size. - The
`Draw`method calls sub-equations’`Draw`methods and adds other symbols needed by this equation such as integral signs, brackets, or root symbols.

The subclasses are somewhat involved, but the basic ideas are simple. The only real differences are in how each draws its sub-equations. Download the example to see additional details.

Not the prettiest I have ever seen, but definitely usable. Thank you.

Yeah, you could do a lot of work to make it prettier. Particularly some of the symbols such as the integral and radical. But I wanted something simple to show the structure of the program and I figure everyone will have their own favorite drawing styles.

You could also add a lot more flexibility to things like character scaling, which fonts to use, and colors.

If anyone comes up with variations they want to show off, post here so everyone can see!