3.1.5 Draw and calculate

MathGL can be used to draw plots in parallel with some external calculations. The simplest way for this is the usage of mglDraw class. At this you should enable pthread for widgets by setting enable-pthr-widget=ON at configure stage (it is set by default). First, you need to inherit you class from mglDraw class, define virtual members Draw() and Calc() which will draw the plot and proceed calculations. You may want to add the pointer mglWnd *wnd; to window with plot for interacting with them. Finally, you may add any other data or member functions. The sample class is shown below

class myDraw : public mglDraw
{
	mglPoint pnt;	// some variable for changeable data
	long i;			// another variable to be shown
	mglWnd *wnd;	// external window for plotting
public:
	myDraw(mglWnd *w=0) : mglDraw()	{	wnd=w;	}
	void SetWnd(mglWnd *w)	{	wnd=w;	}
	int Draw(mglGraph *gr)
	{
		gr->Line(mglPoint(),pnt,"Ar2");
		char str[16];	snprintf(str,15,"i=%ld",i);
		gr->Puts(mglPoint(),str);
		return 0;
	}
	void Calc()
	{
		for(i=0;;i++)	// do calculation
		{
			long_calculations();// which can be very long
			Check();	// check if need pause
			pnt.Set(2*mgl_rnd()-1,2*mgl_rnd()-1);
			if(wnd)	wnd->Update();
		}
	}
} dr;

There is only one issue here. Sometimes you may want to pause calculations to view result carefully, or save state, or change something. So, you need to provide a mechanism for pausing. Class mglDraw provide function Check(); which check if toolbutton with pause is pressed and wait until it will be released. This function should be called in a "safety" places, where you can pause the calculation (for example, at the end of time step). Also you may add call exit(0); at the end of Calc(); function for closing window and exit after finishing calculations. Finally, you need to create a window itself and run calculations.

int main(int argc,char **argv)
{
	mglFLTK gr(&dr,"Multi-threading test");	// create window
	dr.SetWnd(&gr);	// pass window pointer to yours class
	dr.Run();	// run calculations
	gr.Run();	// run event loop for window
	return 0;
}

Note, that you can reach the similar functionality without using mglDraw class (i.e. even for pure C code).

mglFLTK *gr=NULL;	// pointer to window
void *calc(void *)	// function with calculations
{
	mglPoint pnt;	// some data for plot
	for(long i=0;;i++)		// do calculation
	{
		long_calculations();	// which can be very long
		pnt.Set(2*mgl_rnd()-1,2*mgl_rnd()-1);
		if(gr)
		{
			gr->Clf();			// make new drawing
			// draw something
			gr->Line(mglPoint(),pnt,"Ar2");
			char str[16];	snprintf(str,15,"i=%ld",i);
			gr->Puts(mglPoint(),str);
			// don't forgot to update window
			gr->Update();
		}
	}
}
int main(int argc,char **argv)
{
	static pthread_t thr;
	pthread_create(&thr,0,calc,0);	// create separate thread for calculations
	pthread_detach(thr);			// and detach it
	gr = new mglFLTK;	// now create window
	gr->Run();			// and run event loop
	return 0;
}

This sample is exactly the same as one with mglDraw class, but it don’t have functionality for pausing calculations. If you need it then you have to create global mutex (like pthread_mutex_t *mutex = pthread_mutex_init(&mutex,NULL);), set it to window (like gr->SetMutex(mutex);) and periodically check it at calculations (like pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex);).

Finally, you can put the event-handling loop in separate instead of yours code by using RunThr() function instead of Run() one. Unfortunately, such method work well only for FLTK windows and only if pthread support was enabled. Such limitation come from the Qt requirement to be run in the primary thread only. The sample code will be:

int main(int argc,char **argv)
{
	mglFLTK gr("test");
	gr.RunThr();	// <-- need MathGL version which use pthread for widgets
	mglPoint pnt;	// some data
	for(int i=0;i<10;i++)	// do calculation
	{
		long_calculations();// which can be very long
		pnt.Set(2*mgl_rnd()-1,2*mgl_rnd()-1);
		gr.Clf();			// make new drawing
		gr.Line(mglPoint(),pnt,"Ar2");
		char str[10] = "i=0";	str[3] = '0'+i;
		gr->Puts(mglPoint(),str);
		gr.Update();		// update window
	}
	return 0;	// finish calculations and close the window
}