Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
UppHub
Status & Roadmap
FAQ
Authors & License
Forums
Funding U++
Search on this site











SourceForge.net Logo

SourceForge.net Logo

GitHub Logo

Discord Logo

FnGraph

 

Drawing a graph of text expression

Description

The example show how to draw graph from text input in U++ framework.

More information about this example you can find in dedicated CodeProject article: http://www.codeproject.com/Articles/1074135/Evaluating-expression-with-descend-parser-and-Uplu.

Result

 

 

 

main.cpp

 

#include <CtrlLib/CtrlLib.h>

#include <Painter/Painter.h>

 

using namespace Upp;

 

struct ExpressionEvaluator {

    CParser p; // this will hold our lexical context

    double  x; // 'variable'

 

    double Factor();

    double Exponent();

    double Term();

    double Expression();

};

 

double ExpressionEvaluator::Expression()

{ // resolve + -

    double y = Term(); // at least one term

    for(;;)

        if(p.Char('+'))

            y = y + Term(); // add another term

        else

        if(p.Char('-'))

            y = y - Term(); // subtract another term

        else

            return y; // no more + - operators

}

 

double ExpressionEvaluator::Term()

{ // resolve * /

    double y = Exponent(); // at least one member

    for(;;)

        if(p.Char('*'))

            y = y * Exponent(); // multiply by another member

        else

        if(p.Char('/'))

            y = y / Exponent(); // divide by another member

        else

            return y; // no more * / operators

}

 

double ExpressionEvaluator::Exponent()

{ // resolve power ^

    double y = Factor(); // at least one factor

    for(;;)

        if(p.Char('^'))

            y = pow(y, Factor()); // power by another factor

        else

            return y; // no more power ^ operators

}

 

double ExpressionEvaluator::Factor()

{

    if(p.Char('-')) // unary -

        return -Factor();

    if(p.Id("abs")) // some functions...

        return fabs(Factor());

    if(p.Id("sqrt"))

        return sqrt(Factor());

    if(p.Id("exp"))

        return exp(Factor());

    if(p.Id("exp2"))

        return exp2(Factor());

    if(p.Id("ln"))

        return log(Factor());

    if(p.Id("log"))

        return log10(Factor());

    if(p.Id("log2"))

        return log2(Factor());

    if(p.Id("sin"))

        return sin(Factor());

    if(p.Id("cos"))

        return cos(Factor());

    if(p.Id("tan"))

        return tan(Factor());

    if(p.Id("asin"))

        return asin(Factor());

    if(p.Id("acos"))

        return acos(Factor());

    if(p.Id("atan"))

        return atan(Factor());

    if(p.Id("sinh"))

        return sinh(Factor());

    if(p.Id("cosh"))

        return cosh(Factor());

    if(p.Id("tanh"))

        return tanh(Factor());

    if(p.Id("asinh"))

        return asinh(Factor());

    if(p.Id("acosh"))

        return acosh(Factor());

    if(p.Id("atanh"))

        return atanh(Factor());

    if(p.Id("x")) // our variable

        return x;

    if(p.Id("e")) // e constant

        return M_E;

    if(p.Id("pi")) // pi constant

        return M_PI;

    double x;

    if(p.Char('(')) { // resolve parenthesis - recurse back to Sum (+ - operators)

        x = Expression();

        p.PassChar(')'); // make sure there is closing parenthesis

    }

    else

        x = p.ReadDouble(); // last possibility is that we are at number...

    if(p.Char('!')) { // compute factorial

        if(x >= 0 && x < 120) {

            int n = (int)x;

            if(n == x) {

                x = 1;

                for(int q = 1; q <= n; q++)

                    x *= q;

                return x;

            }

        }

        p.ThrowError("invalid argument");

    }

    return x;

}

 

double Evaluate(const char *s, double x)

{ // evaluate expression for given variable, return Null on any failure or out-of-bounds result (NaN)

    ExpressionEvaluator v;

    v.x = x;

    v.p.Set(s);

    try {

        double y = v.Expression();

        return y >= -1e200 && y < 1e200 ? y : Null;

    }

    catch(CParser::Error) {}

    return Null;

}

 

struct FnGraph : public TopWindow {

    virtual void Paint(Draw& w);

    virtual void MouseWheel(Point p, int zdelta, dword keyflags);

 

    int        zoom = 9;

    EditString expression; // function to display

    

    void RefreshExpression() { Refresh(); }

    

    typedef FnGraph CLASSNAME;

    

    FnGraph();

};

 

FnGraph::FnGraph()

{

    Title("Graph of a function");

    Add(expression.TopPos(0).HSizePos()); // place widget to the top, horizontally fill the window

    expression << THISBACK(RefreshExpression); // when expression changes, repaint the graph

    Sizeable().Zoomable(); // make the window resizable

}

 

void FnGraph::MouseWheel(Point, int zdelta, dword keyflags)

{

    zoom = clamp(zoom + -sgn(zdelta), 1, 20);

    Refresh();

}

 

void FnGraph::Paint(Draw& w_)

{

    Size sz = GetSize();

    DrawPainter w(w_, sz); // Use Painter for smooth sw rendering

    w.Clear(White()); // clear the background

    int ecy = expression.GetSize().cy; // query the height of widget

    w.Offset(0, ecy); // move coordinates out of widget

    sz.cy -= ecy; // and reduce the size

    if(sz.cy < 1) // if too small, do nothing (avoid div by zero)

        return;

    sz = sz / 2 * 2 - 1; // this trick will force axes to .5, results in sharper AA rendering

    double pixels_per_unit = sz.cy / zoom; // we want to display y range -4.5 .. 4.5

    double xaxis = sz.cy / 2.0; // vertical position of x axis

    double yaxis = sz.cx / 2.0; // horizontal position of y axis

    w.Move(0, xaxis).Line(sz.cx, xaxis).Stroke(1, Blue()); // draw x axis

    w.Move(yaxis, 0).Line(yaxis, sz.cy).Stroke(1, Blue()); // draw y axis

    Font font = Serif(15);

    if(pixels_per_unit > 20) // if big enough, paint some axis markers and numbers...

        for(int i = 1; i < 2 * sz.cx / pixels_per_unit; i++)

            for(int sgn = -1; sgn < 2; sgn += 2) {

                String n = AsString(sgn * i);

                Size tsz = GetTextSize(n, font);

 

                double x = yaxis + sgn * i * pixels_per_unit;

                w.Move(x, xaxis - 5).Line(x, xaxis + 5).Stroke(1, Blue());

                w.Text(int(x - tsz.cx / 2.0), int(xaxis + 6), n, font).Fill(Blue());

 

                double y = xaxis - sgn * i * pixels_per_unit;

                w.Move(yaxis - 5, y).Line(yaxis + 5, y).Stroke(1, Blue());

                w.Text(int(yaxis + 6), int(y - tsz.cy / 2.0), n, font).Fill(Blue());

            }

    Vector<String> xp = Split(~~expression, ';'); // get individual functions

    for(int ii = 0; ii < xp.GetCount(); ii++) {

        double y0 = Null; // store previous value

        for(int i = 0; i < sz.cx; i++) { // now iterate through all x pointes and draw the graph

            double x = (i - sz.cx / 2.0) / pixels_per_unit;

            double y = Evaluate(xp[ii], x);

            if(!IsNull(y)) {

                double gy = sz.cy / 2.0 - y * pixels_per_unit;

                if(IsNull(y0)) // previous value was defined

                    w.Move(i, gy);

                else

                    w.Line(i, gy);

            }

            y0 = y;

        }

        w.Stroke(1, Color(!!(ii & 1) * 150, !!(ii & 2) * 150, !!(ii & 4) * 150)); // finally paint the graph line

    }

}

 

GUI_APP_MAIN

{

    FnGraph().Run();

}

 

 

 

 

Do you want to contribute?