Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Plotting Single-Variable and Two-Variable Function Graphs

Tech May 9 3

Table of Contents

  • Overview
  • String Expression Parsing
  • Graph Rendering
  • Assigning Functions as Properties
  • References and Notes

Overview

This article builds upon the foundations discussed in "Functional Programming Basics Everyone Should Know" to demonstrate the crucial role of functions in functional programming paradigms. The key concept emphasized is that functions, much like ordinary data types, can be assigned to variables, stored, passed as parameters, and returned from other functions.

The accompanying demonstration application parses arbitrary string-based mathematical expressions and generates corresponding functions (single-variable, two-variable, and three-variable). For single-variable or two-variable expressions, the application renders the function graphs. Single-variable functions produce planar curves, while two-variable functions generate three-dimensional surfaces. The following illustration shows the output:

The expression parser recognizes only three independent variables: X, Y, and Z.

Github Source Download

String Expression Parsing

String parsing is the core component of this implementation.

The question arises: how do we parse a string mathematical expression such as x^2+sin(x)*cos(y) and subsequently evaluate it? The underlying principle is straightforward. Since each expression contains a limited set of valid symbols—including X, Y, Z, +, -, *, /, and mathematical functions like log, sin, and cos—we can identify and filter these valid symbols, then construct a syntax tree based on operator precedence.

As illustrated above, a tree structure stores the final syntax tree. Values are obtained by substituting X, Y (for two-variable functions) into this tree.

The primary challenges in expression parsing involve syntax tree construction and evaluation. Building the syntax tree is somewhat involved; refer to the source code for details. The evaluation process works as follows: determine whether the current symbol (node) represents a unary operation (such as cos, sin, or negation) or a binary operation (such as +, -, *, /). For unary operations like cosine, first evaluate the child node (which has only one child), then apply the cosine operation to the result (Math.Cos(childValue)). For binary operations like addition, first evaluate both the left and right child nodes, then combine the results using the appropriate operator (leftValue + rightValue). This recursive evaluation continues until the final function value is computed.

Graph Rendering

Graph rendering is relatively straightforward compared to parsing. Based on the syntax tree from the previous step, we can create corresponding single-variable, two-variable, and three-variable functions (in delegate form). The delegate definitions are as follows:

/// <summary>
/// Represents a single-variable function
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public delegate double SingleVarFunc(double input);

/// <summary>
/// Represents a two-variable function
/// </summary>
/// <param name="paramA"></param>
/// <param name="paramB"></param>
/// <returns></returns>
public delegate double TwoVarFunc(double paramA, double paramB);

/// <summary>
/// Represents a three-variable function
/// </summary>
/// <param name="arg1"></param>
/// <param name="arg2"></param>
/// <param name="arg3"></param>
/// <returns></returns>
public delegate double ThreeVarFunc(double arg1, double arg2, double arg3);

As demonstrated, single-variable functions accept one parameter and return one value; two-variable functions accept two parameters and return one value; three-variable functions accept three parameters and return one value. The delegate generation process looks like this:

(SingleVarFunc)((double input) => { return syntaxTree.Evaluate(input, 0, 0); });
(TwoVarFunc)((double a, double b) => { return syntaxTree.Evaluate(a, b, 0); });
(ThreeVarFunc)((double p, double q, double r) => { return syntaxTree.Evaluate(p, q, r); });

Providing the corresponding x, y, z values and invoking the delegate yields the function result.

Single-Variable Function Plotting

Select an interval for X (for example, [-10, 10]), use a step size of 0.1, and compute the Y value (function value) for each X. Connecting these points produces the single-variable function graph.

Two-Variable Function Plotting

Similarly, select intervals for both X and Y (for example, X in [-10, 10] and Y in [-10, 10]), with both variables advancing in incerments of 0.1. Compute the Z value (function value) for each (X, Y) pair, then render these three-dimensional points as a surface.

Assigning Functions as Properties

After obtaining the function (delegate object) through the previous steps, we assign the delegate direct as a property to the graph rendering control, which updates the display accordingly.

// Single-variable
if (inputExpression.Text.ToLower().Contains('x') && !inputExpression.Text.ToLower().Contains('y') && !inputExpression.Text.ToLower().Contains('z'))
{
    SingleVarFunc function = (new SyntaxManager().ParseSingleVariable(inputExpression.Text));
    curveRenderer.Function = function;
    displayTabs.SelectedIndex = 0;
}
// Two-variable
else if (inputExpression.Text.ToLower().Contains('x') && inputExpression.Text.ToLower().Contains('y') && !inputExpression.Text.ToLower().Contains('z'))
{
    TwoVarFunc function = (new SyntaxManager().ParseTwoVariable(inputExpression.Text));
    surfaceRenderer.BinaryFunction = function;
    displayTabs.SelectedIndex = 1;
}
// Three-variable
else
{
    ThreeVarFunc function = (new SyntaxManager().ParseThreeVariable(inputExpression.Text));
    MessageBox.Show("Three-variable function graphs cannot be rendered!");
}

The above code demonstrates assigning functions as property values.

References and Notes

  1. The 3D graphics rendering in this demonstration references an OpenTK example from the internet: http://download.csdn.net/detail/dragonflies/3418135#comment
  2. Online function graph generators were used to verify the acuracy of the generated graphs.
  3. Note that no interval validation is performed during function plotting. For instance, log(X) requires X to be positive; otherwise, plotting will fail. Users must ensure inputs satisfy mathematical constraints.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.