1 /***************************************************************************
2  * wx.cpp is part of Math Graphic Library                              *
3  * Copyright (C) 2007-2016 Alexey Balakin <mathgl.abalakin@gmail.ru>       *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License  as       *
7  *   published by the Free Software Foundation; either version 3 of the    *
8  *   License, or (at your option) any later version.                       *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU Lesser General Public     *
16  *   License along with this program; if not, write to the                 *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 //-----------------------------------------------------------------------------
21 #include <wx/dcclient.h>
22 #include <wx/msgdlg.h>
23 #include <wx/clipbrd.h>
24 #include <wx/dataobj.h>
25 #include <wx/menu.h>
26 #include <wx/scrolwin.h>
27 
28 #undef _
29 
30 #include "mgl2/canvas_wnd.h"
31 #include "mgl2/wx.h"
32 //-----------------------------------------------------------------------------
33 class MGL_EXPORT mglCanvasWX : public mglCanvasWnd
34 {
35 friend class wxMathGL;
36 public:
37 	int sshow;			///< Current state of animation switch (toggle button)
38 	wxMathGL *WMGL;		///< Control which draw graphics
39 	wxWindow *Wnd;		///< Pointer to window
40 
41 	mglCanvasWX();
42 	virtual ~mglCanvasWX();
43 
44 	/// Create a window for plotting. Now implemeted only for GLUT.
45 	void Window(int argc, char **argv, int (*draw)(mglBase *gr, void *p), const char *title,
46 				void *par=NULL, void (*reload)(void *p)=NULL, bool maximize=false);
47 	/// Switch on/off transparency (do not overwrite switches in user drawing function)
48 	void ToggleAlpha();
49 	/// Switch on/off lighting (do not overwrite switches in user drawing function)
50 	void ToggleLight();
51 	void ToggleRotate();	///< Switch on/off rotation by mouse
52 	void ToggleZoom();		///< Switch on/off zooming by mouse
53 	void ToggleNo();		///< Switch off all zooming and rotation
54 	void Update();			///< Update picture by calling user drawing function
55 	void Adjust();			///< Adjust size of bitmap to window size
56 	void GotoFrame(int d);	///< Show arbitrary frame (use relative step)
57 	void Animation();	///< Run animation (I'm too lasy to change it)
58 
59 protected:
60 	wxScrolledWindow *scroll;	///< Scrolling area
61 	wxMenu *popup;			///< Popup menu
62 //	wxSpinCtrl *tet, *phi;	///< Spin box for angles // TODO
63 
64 	void MakeMenu();		///< Create menu, toolbar and popup menu
65 };
66 //-----------------------------------------------------------------------------
67 const wxString ScriptName(L"default");
68 enum
69 {
70 	TIMER_ID=1000,
71 	LAST_ID
72 };
BEGIN_EVENT_TABLE(wxMathGL,wxWindow)73 BEGIN_EVENT_TABLE(wxMathGL, wxWindow)
74 	EVT_TIMER	(TIMER_ID,	wxMathGL::OnNextSlide)
75 	EVT_PAINT	(wxMathGL::OnPaint)
76 	EVT_SIZE	(wxMathGL::OnSize)
77 	EVT_LEFT_DOWN	(wxMathGL::OnMouseLeftDown)
78 	EVT_RIGHT_DOWN	(wxMathGL::OnMouseDown)
79 	EVT_MIDDLE_DOWN	(wxMathGL::OnMouseDown)
80 	EVT_LEFT_UP		(wxMathGL::OnMouseLeftUp)
81 	EVT_RIGHT_UP	(wxMathGL::OnMouseRightUp)
82 	EVT_MOTION		(wxMathGL::OnMouseMove)
83 END_EVENT_TABLE()
84 //-----------------------------------------------------------------------------
85 //
86 //		class wxMathGL
87 //
88 //-----------------------------------------------------------------------------
89 wxMathGL::wxMathGL(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : wxWindow(parent,id,pos,size,style,name)
90 {
91 	AutoResize = false;	draw_par = 0;	draw_func = 0;
92 	gr = new mglCanvas;	popup = 0;		draw_cl = 0;
93 	phi = tet = per = 0;	x0=y0=xe=ye=0;
94 	x1 = y1 = 0;	x2 = y2 = 1;
95 	alpha = light = zoom = rotate = false;
96 //	SetSize(600, 400);
97 	timer = new wxTimer(this,TIMER_ID);
98 }
99 //-----------------------------------------------------------------------------
~wxMathGL()100 wxMathGL::~wxMathGL()	{	if(mgl_use_graph(gr,-1)<1)	mgl_delete_graph(gr);	}
101 //-----------------------------------------------------------------------------
GetRatio()102 double wxMathGL::GetRatio()	{	return double(mgl_get_width(gr))/mgl_get_height(gr);	}
103 //-----------------------------------------------------------------------------
SetGraph(HMGL GR)104 void wxMathGL::SetGraph(HMGL GR)
105 {
106 	mglCanvas *gg = dynamic_cast<mglCanvas *>(GR);
107 	if(!gg)	return;
108 	if(mgl_use_graph(gr,-1)<1)	mgl_delete_graph(gr);
109 	gr=gg;	mgl_use_graph(gg,1);
110 }
111 //-----------------------------------------------------------------------------
OnPaint(wxPaintEvent &)112 void wxMathGL::OnPaint(wxPaintEvent& )
113 {
114 	wxPaintDC dc(this);
115 	dc.DrawBitmap(pic,0,0);
116 //	if(zoom)	dc.DrawRectangle(x0,y0,xe-x0,ye-y0);
117 	if(mgl_get_flag(gr,MGL_SHOW_POS) && !MousePos.IsEmpty())
118 		dc.DrawText(MousePos,0,12);
119 	// TODO: add grid drawing here (from Qt)
120 	// TODO: add active points drawing here (from Qt)
121 }
122 //-----------------------------------------------------------------------------
OnSize(wxSizeEvent & event)123 void wxMathGL::OnSize(wxSizeEvent& event)
124 {
125 	wxSize ev = event.GetSize();
126 	if(mgl_get_width(gr)==ev.GetWidth() && mgl_get_height(gr)==ev.GetHeight())
127 		return;
128 	if(AutoResize && ev.GetWidth()>0 && ev.GetHeight()>0)
129 	{	mgl_set_size(gr, ev.GetWidth(), ev.GetHeight());	Update();	}
130 	else 	SetSize(mgl_get_width(gr), mgl_get_height(gr));
131 }
132 //-----------------------------------------------------------------------------
OnNextSlide(wxTimerEvent &)133 void wxMathGL::OnNextSlide(wxTimerEvent& )	{	NextSlide();	}
134 //-----------------------------------------------------------------------------
SetPer(int p)135 void wxMathGL::SetPer(int p)
136 {	if(100*per!=p && p>=0 && p<100)	{	per = 0.01*p;	Repaint();	}	}
137 //-----------------------------------------------------------------------------
SetPhi(int p)138 void wxMathGL::SetPhi(int p)
139 {	if(phi!=p)	{	phi = p; 	Repaint();	}	}
140 //-----------------------------------------------------------------------------
SetTet(int t)141 void wxMathGL::SetTet(int t)
142 {	if(tet!=t)	{	tet = t; 	Repaint();	}	}
143 //-----------------------------------------------------------------------------
SetAlpha(bool a)144 void wxMathGL::SetAlpha(bool a)
145 {	if(alpha!=a)	{	alpha = a;	Update();	}	}
146 //-----------------------------------------------------------------------------
SetLight(bool l)147 void wxMathGL::SetLight(bool l)
148 {	if(light!=l)	{	light = l;	Update();	}	}
149 //-----------------------------------------------------------------------------
SetZoom(bool z)150 void wxMathGL::SetZoom(bool z)
151 {	if(zoom!=z)	{	zoom=z;	rotate=false;	Repaint();	}	}
152 //-----------------------------------------------------------------------------
SetRotate(bool r)153 void wxMathGL::SetRotate(bool r)
154 {	if(rotate!=r)	{	zoom=false;	rotate=r;	Repaint();	}	}
155 //-----------------------------------------------------------------------------
ShiftDown()156 void wxMathGL::ShiftDown()
157 {	mreal d=(y2-y1)/3;	y1+=d;	y2+=d;	Repaint();	}
158 //-----------------------------------------------------------------------------
ShiftUp()159 void wxMathGL::ShiftUp()
160 {	mreal d=(y2-y1)/3;	y1-=d;	y2-=d;	Repaint();	}
161 //-----------------------------------------------------------------------------
ShiftRight()162 void wxMathGL::ShiftRight()
163 {	mreal d=(x2-x1)/3;	x1-=d;	x2-=d;	Repaint();	}
164 //-----------------------------------------------------------------------------
ShiftLeft()165 void wxMathGL::ShiftLeft()
166 {	mreal d=(x2-x1)/3;	x1+=d;	x2+=d;	Repaint();	}
167 //-----------------------------------------------------------------------------
Restore()168 void wxMathGL::Restore()
169 {
170 	SetPhi(0);	SetTet(0);	SetPer(0);
171 	x1=y1=0;	x2=y2=1;	zoom=rotate=false;
172 	Repaint();
173 }
174 //-----------------------------------------------------------------------------
ZoomIn()175 void wxMathGL::ZoomIn()
176 {
177 	mreal d;
178 	d = (y2-y1)/4;	y1 += d;	y2 -= d;
179 	d = (x2-x1)/4;	x1 += d;	x2 -= d;
180 	Repaint();
181 }
182 //-----------------------------------------------------------------------------
ZoomOut()183 void wxMathGL::ZoomOut()
184 {
185 	mreal d;
186 	d = (y2-y1)/2;	y1 -= d;	y2 += d;
187 	d = (x2-x1)/2;	x1 -= d;	x2 += d;
188 	Repaint();
189 }
190 //-----------------------------------------------------------------------------
Update()191 void wxMathGL::Update()
192 {
193 	if(draw_func || draw_cl)
194 	{
195 		if(mgl_get_flag(gr,MGL_CLF_ON_UPD))	mgl_set_def_param(gr);
196 		mgl_set_def_param(gr);		mgl_reset_frames(gr);
197 		mgl_set_alpha(gr,alpha);	mgl_set_light(gr,light);
198 		if(draw_func)	draw_func(gr, draw_par);	// drawing itself
199 		else 	if(draw_cl)	{	mglGraph g(gr);	draw_cl->Draw(&g);	}
200 		const char *buf = mgl_get_mess(gr);
201 		if(*buf)
202 		{
203 			wxMessageDialog dlg(this, wxString(buf,wxConvLocal), appName, wxOK);
204 			dlg.ShowModal();
205 		}
206 	}
207 	else if(mgl_get_num_frame(gr)>0)
208 	{
209 		mgl_set_alpha(gr,alpha);	mgl_set_light(gr,light);
210 //		mgl_zoom(gr,x1,y1,x2,y2);	mgl_view(gr,-phi,-tet,0);
211 		mgl_get_frame(gr,0);
212 	}
213 	MousePos.Empty();	Repaint();
214 }
215 //-----------------------------------------------------------------------------
ConvertFromGraph(HMGL gr)216 wxBitmap MGL_EXPORT ConvertFromGraph(HMGL gr)
217 {
218 	const unsigned char *bb = mgl_get_rgb(gr);
219 	int w=mgl_get_width(gr), h=mgl_get_height(gr);
220 	unsigned char *tmp = (unsigned char *)malloc(3*w*h);
221 	memcpy(tmp,bb,3*w*h);
222 	wxImage img(w, h);	img.SetData(tmp);
223 	return wxBitmap(img);
224 }
225 //-----------------------------------------------------------------------------
Repaint()226 void wxMathGL::Repaint()
227 {
228 	mgl_zoom(gr,x1,y1,x2,y2);	mgl_view(gr,-phi,-tet,0);	mgl_ask_perspective(gr, per);
229 	pic = ConvertFromGraph(gr);
230 	wxSize sz=GetSize();
231 	if(pic.GetWidth()!=sz.GetWidth() || pic.GetHeight()!=sz.GetHeight())
232 		SetSize(pic.GetWidth(), pic.GetHeight());
233 	Refresh();
234 }
235 //-----------------------------------------------------------------------------
OnMouseLeftDown(wxMouseEvent & ev)236 void wxMathGL::OnMouseLeftDown(wxMouseEvent &ev)
237 {
238 	long x=ev.GetX(), y=ev.GetY();
239 	if(!zoom && !rotate)
240 	{
241 		mglPoint p = gr->CalcXYZ(x, y);
242 		MousePos.Printf(wxT("x=%g, y=%g, z=%g"),p.x,p.y,p.z);
243 		Refresh();
244 //		emit mouseClick(p.x,p.y,p.z);
245 	}
246 	xe=x0=x;	ye=y0=y;	ev.Skip();
247 }
248 //-----------------------------------------------------------------------------
OnMouseDown(wxMouseEvent & ev)249 void wxMathGL::OnMouseDown(wxMouseEvent &ev)
250 {	xe=x0=ev.GetX();	ye=y0=ev.GetY();	ev.Skip();	}
251 //-----------------------------------------------------------------------------
OnMouseLeftUp(wxMouseEvent &)252 void wxMathGL::OnMouseLeftUp(wxMouseEvent &)
253 {
254 	if(zoom)
255 	{
256 		int w1=GetSize().GetWidth(),h1=GetSize().GetHeight();
257 		mreal _x1,_x2,_y1,_y2;
258 		_x1 = x1+(x2-x1)*(x0-GetPosition().x)/mreal(w1);
259 		_y1 = y2-(y2-y1)*(ye-GetPosition().y)/mreal(h1);
260 		_x2 = x1+(x2-x1)*(xe-GetPosition().x)/mreal(w1);
261 		_y2 = y2-(y2-y1)*(y0-GetPosition().y)/mreal(h1);
262 		x1=_x1;		x2=_x2;		y1=_y1;		y2=_y2;
263 		if(x1>x2)	{	_x1=x1;	x1=x2;	x2=_x1;	}
264 		if(y1>y2)	{	_x1=y1;	y1=y2;	y2=_x1;	}
265 		x0 = xe;	y0 = ye;
266 		Update();
267 	}
268 }
269 //-----------------------------------------------------------------------------
OnMouseRightUp(wxMouseEvent & ev)270 void wxMathGL::OnMouseRightUp(wxMouseEvent &ev)
271 {	if(popup && !rotate)	PopupMenu(popup, ev.GetPosition());	}
272 //-----------------------------------------------------------------------------
OnMouseMove(wxMouseEvent & ev)273 void wxMathGL::OnMouseMove(wxMouseEvent &ev)
274 {
275 	long w=GetSize().GetWidth(), h=GetSize().GetHeight();
276 	xe=ev.GetX();	ye=ev.GetY();
277 	if(rotate)
278 	{
279 		if(ev.ButtonDown(wxMOUSE_BTN_LEFT))	// rotate
280 		{
281 			mreal ff = 240/sqrt(mreal(w*h));
282 			phi += int((x0-xe)*ff);
283 			tet += int((y0-ye)*ff);
284 			if(phi>180)		phi-=360;
285 			if(phi<-180)	phi+=360;
286 			if(tet>180)		tet-=360;
287 			if(tet<-180)	tet+=360;
288 //			Update();
289 		}
290 		if(ev.ButtonDown(wxMOUSE_BTN_RIGHT))	// zoom and perspective
291 		{
292 			mreal ff = 2.*(y0-ye)/w, gg = 0.5*(xe-x0)/h;
293 			mreal cx = (x1+x2)/2, cy = (y1+y2)/2;
294 			x1 = cx+(x1-cx)*exp(-ff);	x2 = cx+(x2-cx)*exp(-ff);
295 			y1 = cy+(y1-cy)*exp(-ff);	y2 = cy+(y2-cy)*exp(-ff);
296 			per = per + gg;
297 			if(per<0)	per = 0;
298 			if(per>=1)	per = 0.9999;
299 //			Update();
300 		}
301 		if(ev.ButtonDown(wxMOUSE_BTN_MIDDLE))	// shift
302 		{
303 			mreal ff = 1./sqrt(mreal(w*h));
304 			mreal dx = (x0-xe)*ff*(x2-x1), dy = (y0-ye)*ff*(y2-y1);
305 			x1 += dx;	x2 += dx;	y1 -= dy;	y2 -= dy;
306 		}
307 		x0 = xe;	y0 = ye;
308 		Update();
309 	}
310 //	if(zoom)	Update();
311 	if(zoom)	Refresh(0);
312 }
313 //-----------------------------------------------------------------------------
mglSetExtension(wxString & fname,const char * ext)314 wxString mglSetExtension(wxString &fname, const char *ext)
315 {
316 	wxString oname;
317 	if(fname.Right(4)!=wxChar('.')+wxString(ext,*wxConvCurrent))
318 		oname = fname+wxChar('.')+wxString(ext,*wxConvCurrent);
319 	return oname;
320 }
321 //-----------------------------------------------------------------------------
322 // NOTE: this is replacement for wxString::char_str() which is for v.2.8 or later
mglw_str(const wxString & str)323 const char *mglw_str(const wxString &str)
324 {
325 	static char *buf=0;
326 	if(buf)	delete []buf;
327 	size_t i, n=str.Len();
328 	buf = new char[n+1];	buf[n]=0;
329 	for(i=0;i<n;i++)	buf[i] = str.GetChar(i);
330 	return buf;
331 }
332 //-----------------------------------------------------------------------------
ExportPNG(wxString fname)333 void wxMathGL::ExportPNG(wxString fname)
334 {
335 	if(fname.IsEmpty())	fname = ScriptName;
336 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
337 	else	mgl_write_png(gr,mglw_str(mglSetExtension(fname,"png")), mglw_str(appName));
338 }
339 //-----------------------------------------------------------------------------
ExportPNGs(wxString fname)340 void wxMathGL::ExportPNGs(wxString fname)
341 {
342 	if(fname.IsEmpty())	fname = ScriptName;
343 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
344 	else	mgl_write_png_solid(gr,mglw_str(mglSetExtension(fname,"png")), mglw_str(appName));
345 }
346 //-----------------------------------------------------------------------------
ExportJPG(wxString fname)347 void wxMathGL::ExportJPG(wxString fname)
348 {
349 	if(fname.IsEmpty())	fname = ScriptName;
350 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
351 	else	mgl_write_jpg(gr,mglw_str(mglSetExtension(fname,"jpg")), mglw_str(appName));
352 }
353 //-----------------------------------------------------------------------------
ExportBPS(wxString fname)354 void wxMathGL::ExportBPS(wxString fname)
355 {
356 	if(fname.IsEmpty())	fname = ScriptName;
357 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
358 	else
359 		mgl_write_bps(gr,mglw_str(mglSetExtension(fname,"eps")), mglw_str(appName));
360 }
361 //-----------------------------------------------------------------------------
ExportEPS(wxString fname)362 void wxMathGL::ExportEPS(wxString fname)
363 {
364 	if(fname.IsEmpty())	fname = ScriptName;
365 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
366 	else
367 		mgl_write_eps(gr,mglw_str(mglSetExtension(fname,"eps")), mglw_str(appName));
368 }
369 //-----------------------------------------------------------------------------
ExportSVG(wxString fname)370 void wxMathGL::ExportSVG(wxString fname)
371 {
372 	if(fname.IsEmpty())	fname = ScriptName;
373 	if(fname.IsEmpty())	wxMessageBox(appName, wxT("No filename."),wxOK|wxICON_ERROR ,this);
374 	else
375 		mgl_write_svg(gr,mglw_str(mglSetExtension(fname,"eps")), mglw_str(appName));
376 }
377 //-----------------------------------------------------------------------------
Copy()378 void wxMathGL::Copy()
379 {
380 	if (wxTheClipboard->Open())
381 	{
382 		wxTheClipboard->SetData( new wxBitmapDataObject(pic) );
383 		wxTheClipboard->Close();
384 	}
385 }
386 //-----------------------------------------------------------------------------
SetSize(int w,int h)387 void wxMathGL::SetSize(int w, int h)
388 {	mgl_set_size(gr,w,h);	wxWindow::SetSize(w, h);	Update();	}
389 //-----------------------------------------------------------------------------
Adjust()390 void wxMathGL::Adjust()
391 {
392 	wxSize sz=GetSize();
393 	mgl_set_size(gr,sz.GetWidth(),sz.GetHeight());
394 	Repaint();
395 }
396 //-----------------------------------------------------------------------------
NextSlide()397 void wxMathGL::NextSlide()
398 {
399 	mglCanvasWnd *g = dynamic_cast<mglCanvasWnd *>(gr);
400 	if(g && g->GetNumFig()>1)	g->NextFrame();
401 }
402 //-----------------------------------------------------------------------------
PrevSlide()403 void wxMathGL::PrevSlide()
404 {
405 	mglCanvasWnd *g = dynamic_cast<mglCanvasWnd *>(gr);
406 	if(g && g->GetNumFig()>1)	g->PrevFrame();
407 }
408 //-----------------------------------------------------------------------------
Animation(bool st)409 void wxMathGL::Animation(bool st)
410 {
411 	if(st)	timer->Start(int(mgl_wnd_get_delay(gr)*1000));
412 	else	timer->Stop();
413 }
414 //-----------------------------------------------------------------------------
About()415 void wxMathGL::About()
416 {
417 	wxString s = wxT("MathGL v. 2.") + wxString::Format(wxT("%g"),MGL_VER2) +
418 		wxT("\n(c) Alexey Balakin, 2007\nhttp://mathgl.sourceforge.net/");
419 	wxMessageBox(s, wxT("MathGL - about"), wxOK|wxICON_INFORMATION, this);
420 }
421 //-----------------------------------------------------------------------------
422