visual graphing program [fltk]

Bjarne Stroustrup “Programming Principles and Practice Using C++”
Chapter 16 Exercise 10
Using GUI library called FLTK (Fast Light Tool Kit, “full tick”).

Output:

//	Philipp Siedler
//	Bjarne Stroustrup's PP
//	Chapter 16 Exercise 10

#define _USE_MATH_DEFINES
#include "Simple_window.h"
#include "Graph.h"
#include <cmath>
#include "GUI.h"
#include "std_lib_facilities.h"
#include <functional>

// layout
constexpr int xmax = 600;
constexpr int ymax = 600;

constexpr int x_orig = xmax / 2;
constexpr int y_orig = ymax / 2;
Point orig(x_orig, y_orig);

constexpr int r_min = -10;
constexpr int r_max = 10;

constexpr int n_points = 40;

constexpr int x_scale = 20;
constexpr int y_scale = 20;

// layout
constexpr int xoffset = 100;
constexpr int yoffset = 100;

constexpr int xspace = 100;
constexpr int yspace = 100;

constexpr int xlength = xmax - xoffset - xspace;
constexpr int ylength = ymax - yoffset - yspace;

typedef double Fcti(double);

double mysin(double x) { return sin(x); }
double mycos(double x) { return cos(x); }

template <class T> class myFct : public Shape {
public:
	myFct(Fcti* f, double r1, double r2, Point orig, int count = 100, double xscale = 25, double yscale = 25, T precision = 1.0);
	~myFct() {}

	void set_f(Fcti* p) { f = p; }
	void set_r1(double r) { r1 = r; }
	void set_r2(double r) { r2 = r; }
	void set_orig(Point o) { orig = o; }
	void set_count(int c) { count = c; }
	void set_xscale(double x) { xscale = x; }
	void set_yscale(double y) { yscale = y; }
	void set_precision(T p) { precision = p; }

	double get_r1() { return r1; }
	double get_r2() { return r2; }
	Point get_orig() { return orig; }
	int get_count() { return count; }
	double get_xscale() { return xscale; }
	double get_yscale() { return yscale; }
	T get_precision() { return precision; }

	void calc();
	void reset(Fcti* f, double r1, double r2, Point orig, int count = 100, double xscale = 25, double yscale = 25, T precision = 1.0);

private:
	Fcti* f;
	double r1;
	double r2;
	Point orig;
	int count;
	double xscale;
	double yscale;
	T precision;
};

template <class T> myFct<T>::myFct(Fcti* f, double r1, double r2, Point orig, int count, double xscale, double yscale, T precision)
	:f(f), r1(r1), r2(r2), orig(orig), count(count), xscale(xscale), yscale(yscale), precision(precision)
{
	calc();
}

template <class T> void myFct<T>::calc()
{
	if (r2 - r1 <= 0) error("bad graphing range");
	if (count <= 0) error("non-positive graphing count");
	double dist = (r2 - r1) / count;
	double r = r1;

	// had to add void clear_points() { points.clear(); }
	// to the protected section of the Shape class - annoying
	clear_points();	

	for (int i = 0; i < count; ++i) {
		int x = orig.x + int(int(r * xscale) / precision) * precision;
		int y = orig.y - int(int(f(r) * yscale) / precision) * precision;
		add(Point(x, y));
		r += dist;
	}
}

template <class T> void myFct<T>::reset(Fcti* f, double r1, double r2, Point orig, int count, double xscale, double yscale, T precision)
{
	set_f(f);
	set_r1(r1);
	set_r2(r2);
	set_orig(orig);
	set_count(count);
	set_xscale(xscale);
	set_yscale(yscale);
	set_precision(precision);

	calc();
}

// ------------------------------ GRAPHING END-------------------------

struct application_window : Window {
	enum graph_enum
	{
		sin_g, cos_g
	};

	application_window(Point xy, int w, int h, const string& title);

	void draw_graph();

private:

	//------------- LAYOUT

	// quit button
	Button quit_button;

	// convert button
	Button graph_button;

	// graph type button;
	Button graph_type;
	Menu graph_type_menu;

	// in
	In_box x_parameter;
	Out_box base_equation;
	Out_box equation;

	//
	graph_enum g_type;

	// axis
	Axis x_axis;
	Axis y_axis;

	// function
	myFct<double> funct;

	//double x_manipulation(double x, int m) { return x * m; }
	double my_sin(double x) { return sin(x); }
	double my_cos(double x) { return cos(x); }

	// actions invoked by callbacks
	void quit() { hide(); }

	void graph_type_menu_pressed() { graph_type.hide(); graph_type_menu.show(); }

	void sin_graph();
	void cos_graph();	

	// callback functions
	static void cb_quit(Address, Address pw) { reference_to<application_window>(pw).quit(); }
	static void cb_calculate(Address, Address pw) { reference_to<application_window>(pw).draw_graph(); }

	static void cb_graph_type_menu_pressed(Address, Address pw) { reference_to<application_window>(pw).graph_type_menu_pressed(); }

	static void cb_sin_graph(Address, Address pw) { reference_to<application_window>(pw).sin_graph(); }
	static void cb_cos_graph(Address, Address pw) { reference_to<application_window>(pw).cos_graph(); }

	//------------- LAYOUT END
};

application_window::application_window(Point xy, int w, int h, const string& title)
	: Window(xy, w, h, title)
	, quit_button(Point(x_max() - 70, 0), 70, 20, "Quit", cb_quit)
	, graph_button(Point(x_max() - 70, 20), 70, 20, "Graph", cb_calculate)
	, graph_type(Point(20, 0), 100, 20, "Graph Type", cb_graph_type_menu_pressed)
	, graph_type_menu(Point(20, 0), 100, 20, Menu::vertical, "Graph Type")
	, base_equation(Point(x_max() / 2, 0), 90, 20, "equation")
	, x_parameter(Point(100, 40), 20, 20, "t-paramter")
	, equation(Point(x_max() / 2, 40), 90, 20, "equation")
	, x_axis(Axis::x, Point(orig.x - 200, orig.y), 400, 20, "1 == 20")
	, y_axis(Axis::y, Point(orig.x, orig.y + 200), 400, 20, "1 == 20")
	, funct(mysin, r_min, r_max, orig, n_points, x_scale, y_scale)
{
	attach(quit_button);
	attach(graph_button);
	attach(graph_type);
	attach(base_equation);
	attach(x_parameter);
	attach(equation);

	graph_type_menu.attach(new Button(Point(0, 0), 0, 0, "sin", cb_sin_graph));
	graph_type_menu.attach(new Button(Point(0, 0), 0, 0, "cos", cb_cos_graph));

	attach(graph_type_menu);

	x_axis.set_color(Color::black);
	y_axis.set_color(Color::black);
	attach(x_axis);
	attach(y_axis);

	funct.set_color(FL_BLACK);
	attach(funct);
	funct.set_color(Color::invisible);

	graph_type_menu.hide();
}

void application_window::sin_graph()
{
	graph_type_menu.hide();
	graph_type.show();

	base_equation.put("y = sin(x) * t");
	g_type = sin_g;
}

void application_window::cos_graph()
{
	graph_type_menu.hide();
	graph_type.show();

	base_equation.put("y = cos(x) * t");
	g_type = cos_g;
}

void application_window::draw_graph()
{	
	// GRAPHS
	if(g_type == sin_g)
	{		
		equation.put("y = sin(x) * " + x_parameter.get_string());

		// most painful lambda to function pointer work-around ever.
		// saved the day:
		// https://deviorel.wordpress.com/2015/01/27/obtaining-function-pointers-from-lambdas-in-c/

		int t = x_parameter.get_int();
		auto la = [=](double x) { return sin(x) * t; };

		static function< double(double) > static_variable;
		static_variable = la;
		double(*ptr)(double) = [](double x) { return static_variable(x); };

		funct.reset(
			[](double x) { return static_variable(x); }
			, r_min
			, r_max
			, orig
			, n_points
			, x_scale
			, y_scale
		);

	}
	if(g_type == cos_g)
	{
		equation.put("y = cos(x) * " + x_parameter.get_string());

		int t = x_parameter.get_int();
		auto la = [=](double x) { return cos(x) * t; };

		static function< double(double) > static_variable;
		static_variable = la;
		double(*ptr)(double) = [](double x) { return static_variable(x); };

		funct.reset(
			[](double x) { return static_variable(x); }
			, r_min
			, r_max
			, orig
			, n_points
			, x_scale
			, y_scale
		);
	}

	funct.set_color(Color::visible);
	redraw();
}

// ------------------------------ INTERFACE END-------------------------

int main()
try {
	application_window win(Point(100, 100), xmax, ymax + 20, "currency converter");

	return gui_main();
}
catch (exception& e) {
	cerr << "exception: " << e.what() << endl;
	char c;
	while (cin >> c&& c != ';');
	return 1;
}
catch (...) {
	cerr << "exception\n";
	char c;
	while (cin >> c && c != ';');
	return 2;
}

One thought on “visual graphing program [fltk]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.