1 /***************************************************************************
2  * canvas.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 #include <limits.h>
21 #include "mgl2/font.h"
22 #include "mgl2/canvas.h"
23 //-----------------------------------------------------------------------------
mglCanvas(int w,int h)24 mglCanvas::mglCanvas(int w, int h) : mglBase()
25 {
26 	clr(MGL_DISABLE_SCALE);
27 	set(MGL_VECT_FRAME);	// NOTE: require a lot of memory!
28 	Z=0;	C=G=G4=GB=0;	OI=0;	gif=0;
29 	CurFrameId=0;	Delay=0.5;
30 	Width=Height=Depth=0;	ObjId=-1;
31 	fscl=ftet=0;		PlotId = _("frame");
32 	pnt_col = 0;
33 
34 	ac.ch='c';
35 	ax.dir.Set(1,0,0);	ax.a.Set(0,1,0);	ax.b.Set(0,0,1);	ax.ch='x';
36 	ay.dir.Set(0,1,0);	ay.a.Set(1,0,0);	ay.b.Set(0,0,1);	ay.ch='y';
37 	az.dir.Set(0,0,1);	az.a.Set(0,1,0);	az.b.Set(1,0,0);	az.ch='z';
38 
39 	SetSize(w,h);	SetQuality(MGL_DRAW_NORM);	DefaultPlotParam();
40 }
41 //-----------------------------------------------------------------------------
~mglCanvas()42 mglCanvas::~mglCanvas()
43 {
44 	if(G)	{	delete []G;	delete []C;	delete []Z;	delete []G4;delete []GB;delete []OI;	}
45 	if(pnt_col)	delete []pnt_col;
46 }
47 //-----------------------------------------------------------------------------
PushDrwDat()48 long mglCanvas::PushDrwDat()
49 {
50 	mglDrawDat d;
51 	d.Pnt=Pnt;	d.Prm=Prm;	d.Sub=Sub;	d.Glf=Glf;	d.Ptx=Ptx;	d.Txt=Txt;
52 #pragma omp critical(drw)
53 	MGL_PUSH(DrwDat,d,mutexDrw);
54 	return DrwDat.size();
55 }
56 //-----------------------------------------------------------------------------
ResetFrames()57 void mglCanvas::ResetFrames()	{	CurFrameId=0;	DrwDat.clear();	}
58 //-----------------------------------------------------------------------------
SetFrame(long i)59 void mglCanvas::SetFrame(long i)
60 {
61 	if(get(MGL_VECT_FRAME) && i>=0 && i<long(DrwDat.size()))
62 	{
63 		Finish();	CurFrameId--;
64 		mglDrawDat d;
65 		d.Pnt=Pnt;	d.Prm=Prm;	d.Sub=Sub;	d.Glf=Glf;	d.Ptx=Ptx;	d.Txt=Txt;
66 #if MGL_HAVE_PTHREAD
67 		pthread_mutex_lock(&mutexDrw);
68 		DrwDat[i] = d;
69 		pthread_mutex_unlock(&mutexDrw);
70 #else
71 #pragma omp critical(drw)
72 		DrwDat[i] = d;
73 #endif
74 	}
75 }
76 //-----------------------------------------------------------------------------
GetFrame(long k)77 void mglCanvas::GetFrame(long k)
78 {
79 	if(k<0 || (size_t)k>=DrwDat.size())	return;
80 	ClearFrame();
81 	const mglDrawDat &d=DrwDat[k];
82 #if MGL_HAVE_PTHREAD
83 	pthread_mutex_lock(&mutexPnt);
84 	pthread_mutex_lock(&mutexPrm);
85 	pthread_mutex_lock(&mutexSub);
86 	pthread_mutex_lock(&mutexGlf);
87 	pthread_mutex_lock(&mutexPtx);
88 	pthread_mutex_lock(&mutexTxt);
89 #endif
90 #pragma omp critical
91 	{	Pnt=d.Pnt;	Prm=d.Prm;	Sub=d.Sub;	Glf=d.Glf;	Ptx=d.Ptx;	Txt=d.Txt;	ClearPrmInd();	}
92 #if MGL_HAVE_PTHREAD
93 	pthread_mutex_unlock(&mutexTxt);
94 	pthread_mutex_unlock(&mutexPtx);
95 	pthread_mutex_unlock(&mutexGlf);
96 	pthread_mutex_unlock(&mutexSub);
97 	pthread_mutex_unlock(&mutexPrm);
98 	pthread_mutex_unlock(&mutexPnt);
99 #endif
100 }
101 //-----------------------------------------------------------------------------
ClearFrame()102 void mglCanvas::ClearFrame()
103 {
104 #if MGL_HAVE_PTHREAD
105 	pthread_mutex_lock(&mutexPnt);
106 	pthread_mutex_lock(&mutexPrm);
107 	pthread_mutex_lock(&mutexGlf);
108 	pthread_mutex_lock(&mutexPtx);
109 	pthread_mutex_lock(&mutexTxt);
110 	pthread_mutex_lock(&mutexSub);
111 	pthread_mutex_lock(&mutexLeg);
112 	pthread_mutex_lock(&mutexGrp);
113 	pthread_mutex_lock(&mutexAct);
114 #endif
115 
116 #pragma omp critical
117 	{
118 		StartAutoGroup(NULL);
119 		Leg.clear();	Grp.clear();	Act.clear();	Glf.clear();
120 		Pnt.clear();	Prm.clear();	Ptx.clear();	ClearPrmInd();
121 		Txt.clear();	Txt.reserve(3);
122 //		mglBlock inpl = Sub[0];	Sub.clear();	Sub.push_back(inpl);	// NOTE at least one inplot should present!!!
123 		mglTexture t1(MGL_DEF_PAL,-1), t2(MGL_DEF_SCH,1);
124 		Txt.push_back(t1);	Txt.push_back(t2);	// No extra lock is required
125 	}
126 #if MGL_HAVE_PTHREAD
127 	pthread_mutex_unlock(&mutexAct);
128 	pthread_mutex_unlock(&mutexGrp);
129 	pthread_mutex_unlock(&mutexLeg);
130 	pthread_mutex_unlock(&mutexSub);
131 	pthread_mutex_unlock(&mutexTxt);
132 	pthread_mutex_unlock(&mutexPtx);
133 	pthread_mutex_unlock(&mutexGlf);
134 	pthread_mutex_unlock(&mutexPrm);
135 	pthread_mutex_unlock(&mutexPnt);
136 #endif
137 	ClfZB(true);
138 }
139 //-----------------------------------------------------------------------------
ShowFrame(long k)140 void mglCanvas::ShowFrame(long k)
141 {
142 	if(k<0 || (size_t)k>=DrwDat.size())	return;
143 	ClfZB();
144 	size_t npnt=Pnt.size(), nglf=Glf.size(), nptx=Ptx.size(), ntxt=Txt.size(), nsub=Sub.size();
145 #if MGL_HAVE_PTHREAD
146 	pthread_mutex_lock(&mutexPnt);
147 	pthread_mutex_lock(&mutexPrm);
148 	pthread_mutex_lock(&mutexSub);
149 	pthread_mutex_lock(&mutexGlf);
150 	pthread_mutex_lock(&mutexPtx);
151 	pthread_mutex_lock(&mutexTxt);
152 #endif
153 #pragma omp critical
154 	{
155 		const mglDrawDat &d=DrwDat[k];
156 		Glf.resize(d.Glf.size());	for(size_t i=0;i<d.Glf.size();i++)	Glf.push_back(d.Glf[i]);
157 		Ptx.resize(d.Ptx.size());	for(size_t i=0;i<d.Ptx.size();i++)	Ptx.push_back(d.Ptx[i]);
158 		Sub.resize(d.Sub.size());	for(size_t i=0;i<d.Sub.size();i++)	Sub.push_back(d.Sub[i]);
159 		Txt.reserve(d.Pnt.size());	for(size_t i=0;i<d.Txt.size();i++)	Txt.push_back(d.Txt[i]);
160 		Pnt.reserve(d.Pnt.size());	ClearPrmInd();
161 		for(size_t i=0;i<d.Pnt.size();i++)
162 		{
163 			mglPnt p = d.Pnt[i]; 	p.c += ntxt;
164 			if(p.sub>=0)	p.sub += nsub;
165 			else	p.sub -= nsub;
166 			Pnt.push_back(p);
167 		}
168 		Prm.reserve(d.Prm.size());
169 		for(size_t i=0;i<d.Prm.size();i++)
170 		{
171 			mglPrim p = d.Prm[i];
172 			p.n1 += npnt;
173 
174 			switch(p.type)
175 			{
176 			case 1:	p.n2 += npnt;	break;
177 			case 2:	p.n2 += npnt;	p.n3 += npnt;	break;
178 			case 3:	p.n2 += npnt;	p.n3 += npnt;	p.n4 += npnt;	break;
179 			case 4: p.n4 += nglf;	break;
180 			case 5:	p.n2 += npnt;	break;
181 			case 6: p.n3 += nptx;	break;
182 			}
183 			Prm.push_back(p);
184 		}
185 	}
186 #if MGL_HAVE_PTHREAD
187 	pthread_mutex_unlock(&mutexPnt);
188 	pthread_mutex_unlock(&mutexPrm);
189 	pthread_mutex_unlock(&mutexSub);
190 	pthread_mutex_unlock(&mutexGlf);
191 	pthread_mutex_unlock(&mutexPtx);
192 	pthread_mutex_unlock(&mutexTxt);
193 #endif
194 }
195 //-----------------------------------------------------------------------------
GetBits()196 const unsigned char *mglCanvas::GetBits()	{	Finish();	return G;	}
197 //-----------------------------------------------------------------------------
GetRatio() const198 mreal mglCanvas::GetRatio() const	{	return inW/inH;	}
199 //-----------------------------------------------------------------------------
add_prim(mglPrim & a)200 void mglCanvas::add_prim(mglPrim &a)
201 {
202 	if(a.n1>=0)
203 	{
204 		a.z = Pnt[a.n1].z;	// this is a bit less accurate but simpler for transformation
205 		a.id = ObjId;
206 #pragma omp critical(prm)
207 		MGL_PUSH(Prm,a,mutexPrm);
208 		ClearPrmInd();	clr(MGL_FINISHED);
209 	}
210 }
211 //-----------------------------------------------------------------------------
212 extern uint64_t mgl_mask_def[16];
DefaultPlotParam()213 void mglCanvas::DefaultPlotParam()
214 {
215 /* NOTE: following variables and mutex will not be changed by DefaultPlotParam()
216 long InUse;			///< Smart pointer (number of users)
217 mglFont *fnt;		///< Class for printing vector text
218 int Quality;		///< Quality of plot (0x0-pure, 0x1-fast; 0x2-fine; 0x4 - low memory)
219 int Width;			///< Width of the image
220 int Height;			///< Height of the image
221 int Depth;			///< Depth of the image
222 int CurFrameId;		///< Number of automaticle created frames
223 GifFileType *gif;*/
224 	SetDrawReg(1,1,0);		Perspective(0);	SetPenDelta(1);	SetBBox();
225 	memcpy(mgl_mask_val, mgl_mask_def, 16*sizeof(uint64_t));	// should be > 16*8
226 	ax.Clear();	ay.Clear();	az.Clear();	ac.Clear();
227 	mgl_clear_fft();		DefMaskAn=0;	ResetMask();
228 	SetTickRotate(true);	SetTickSkip(true);
229 	SetWarn(mglWarnNone,"");	mgl_clear_global_warn();
230 	ObjId = -1;	HighId = INT_MIN;
231 	SetFunc(0,0);	CutOff(0);	Ternary(0);
232 	Stop=false;	event_cb = NULL;	event_par=NULL;
233 	SetRanges(mglPoint(-1,-1,-1,-1), mglPoint(1,1,1,1));
234 	SetOrigin(NAN,NAN,NAN,NAN);	set(0, MGL_NO_ORIGIN);
235 	SetBarWidth(0.7);	SetMarkSize(1);	SetArrowSize(1);
236 	SetAlphaDef(0.5);		FontDef[0]=0;
237 	SetTranspType(0);		SetMeshNum(0);	// NOTE: default MeshNum=0
238 	SetRotatedText(true);	CurrPal = 0;
239 	SetLegendMarks();		SetFontSize(4);
240 	SetTuneTicks(3);		SetAmbient();	SetDiffuse();
241 	clr(MGL_DISABLE_SCALE);
242 	clr(MGL_USE_GMTIME);	clr(MGL_NOSUBTICKS);
243 	SetDifLight(false);		SetReduceAcc(false);
244 	SetDefScheme(MGL_DEF_SCH);	SetPalette(MGL_DEF_PAL);
245 	SetPenPal("k-1");		Alpha(false);
246 	stack.clear();	Restore();	DefColor('k');
247 	SetPlotFactor(0);	Sub.clear();
248 	InPlot(0,1,0,1,false);	clr(MGL_FULL_CURV);
249 	SetTickLen(0);	SetCut(true);
250 	AdjustTicks("xyzc",true);	Clf('w');
251 
252 	for(int i=0;i<10;i++)	{	AddLight(i, mglPoint(0,0,1));	Light(i,false);	}
253 	Light(0,true);	Light(false);	SetDifLight(true);
254 }
255 //-----------------------------------------------------------------------------
256 //	Optimal axis position
257 //-----------------------------------------------------------------------------
FindOptOrg(char dir,int ind) const258 mreal mglCanvas::FindOptOrg(char dir, int ind) const
259 {
260 	static mglPoint px, py, pz;
261 	static mglMatrix bb;
262 	mglPoint nn[8]={mglPoint(0,0,0), mglPoint(0,0,1), mglPoint(0,1,0,0), mglPoint(0,1,1),
263 					mglPoint(1,0,0), mglPoint(1,0,1), mglPoint(1,1,0), mglPoint(1,1,1)}, pp[8];
264 	memcpy(pp, nn, 8*sizeof(mglPoint));
265 	// do nothing if transformation matrix is the same
266 	if(B!=bb)
267 	{
268 		bb = B;
269 		for(long i=0;i<8;i++)	PostScale(&B,pp[i]);
270 		// find point with minimal y
271 		long j=0;
272 		for(long i=1;i<8;i++)	if(pp[i].y<pp[j].y)	j=i;
273 		pp[0]=pp[j];
274 		// first select 3 closest points
275 		pp[1].x=1-nn[j].x;	pp[1].y=nn[j].y;	pp[1].z=nn[j].z;	PostScale(&B,pp[1]);	pp[1]-=pp[0];
276 		pp[2].x=nn[j].x;	pp[2].y=1-nn[j].y;	pp[2].z=nn[j].z;	PostScale(&B,pp[2]);	pp[2]-=pp[0];
277 		pp[3].x=nn[j].x;	pp[3].y=nn[j].y;	pp[3].z=1-nn[j].z;	PostScale(&B,pp[3]);	pp[3]-=pp[0];
278 		// find cosine of axis projection
279 		mreal tx=fabs(pp[1].x/pp[1].y), ty=fabs(pp[2].x/pp[2].y), tz=fabs(pp[3].x/pp[3].y);
280 		px=py=pz=nn[j];
281 		if(tz==0 && (ty==0 || tx==0))	// (x- & z-) or (y- & z-) axis are vertical
282 		{	if(pp[1].x>pp[2].x)	pz.y=1-pz.y;	else	pz.x=1-pz.x;	}
283 		else if(tx==0 && ty==0)	// x- && y-axis is vertical
284 		{
285 			py.x=1-py.x;
286 			if(pp[1].x>pp[3].x)
287 			{	px.z=1-px.z;	py.z=1-py.z;	}
288 		}
289 		else if(tz<tx && tz<ty)	// z-axis is vertical
290 		{	if(pp[1].x>pp[2].x)	pz.y=1-pz.y;	else	pz.x=1-pz.x;	}
291 		else if(ty<tx && ty<tz)	// y-axis is vertical
292 		{	if(pp[1].x>pp[3].x)	py.z=1-py.z;	else	py.x=1-py.x;	}
293 		else if(tx<ty && tx<tz)	// x-axis is vertical
294 		{	if(pp[3].x>pp[2].x)	px.y=1-px.y;	else	px.z=1-px.z;	}
295 	}
296 	// return to normal variables
297 	mglPoint rx = Min+(Max-Min)/px;
298 	mglPoint ry = Min+(Max-Min)/py;
299 	mglPoint rz = Min+(Max-Min)/pz;
300 	mreal res = rx.val(ind);
301 	if(dir=='y')	res = ry.val(ind);
302 	if(dir=='z')	res = rz.val(ind);
303 	return res;
304 }
305 //-----------------------------------------------------------------------------
GetOrgX(char dir,bool inv) const306 mreal mglCanvas::GetOrgX(char dir, bool inv) const
307 {
308 	mreal res = Org.x;
309 	if(mgl_isnan(res))
310 	{
311 		if(strchr("xyz",dir))	res = FindOptOrg(dir,0);
312 		else if(dir=='t')		res = Min.x;
313 		else res = B.b[6]>0 ? Max.x:Min.x;
314 		if(inv)	res = Min.x+Max.x-res;
315 	}
316 	return res;
317 }
318 //-----------------------------------------------------------------------------
GetOrgY(char dir,bool inv) const319 mreal mglCanvas::GetOrgY(char dir, bool inv) const
320 {
321 	mreal res = Org.y;
322 	if(mgl_isnan(res))
323 	{
324 		if(strchr("xyz",dir))	res = FindOptOrg(dir,1);
325 		else if(dir=='t')	res = Min.y;
326 		else res = B.b[7]>0 ? Max.y:Min.y;
327 		if(inv)	res = Min.y+Max.y-res;
328 	}
329 	return res;
330 }
331 //-----------------------------------------------------------------------------
GetOrgZ(char dir,bool inv) const332 mreal mglCanvas::GetOrgZ(char dir, bool inv) const
333 {
334 	mreal res = Org.z;
335 	if(mgl_isnan(res))
336 	{
337 		if(strchr("xyz",dir))	res = FindOptOrg(dir,2);
338 		else if(dir=='t')	res = Min.z;
339 		else res = B.b[8]>0 ? Max.z:Min.z;
340 		if(inv)	res = Min.z+Max.z-res;
341 	}
342 	return res;
343 }
344 //-----------------------------------------------------------------------------
345 //	Put primitives
346 //-----------------------------------------------------------------------------
347 #define MGL_MARK_PLOT	if(Quality&MGL_DRAW_LMEM)	\
348 						{	mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);	d.PenWidth=pw;	\
349 							d.PDef = PDef;	d.pPos = pPos;	mark_draw(Pnt[p],type,size,&d);	}\
350 						else{	mglPrim a;	a.w = pw;	a.s = size;	\
351 							a.n1 = p;	a.n4 = type;	a.angl=0;	add_prim(a);	}
mark_plot(long p,char type,mreal size)352 void mglCanvas::mark_plot(long p, char type, mreal size)
353 {
354 	if(p<0 || mgl_isnan(Pnt[p].x) || mgl_isnan(size))	return;
355 	if(type>128 || type<0)
356 	{	smbl_plot(p,type-128,20*MarkSize*(size?fabs(size):1));	return;	}
357 	long pp=p;
358 	mreal pw = 0.15/sqrt(font_factor);
359 	size = size?fabs(size):1;
360 	size *= MarkSize*0.35*font_factor;
361 	if(type=='.')	size = fabs(PenWidth)*sqrt(font_factor/400);
362 	if(TernAxis&12) for(int i=0;i<4;i++)
363 	{	p = ProjScale(i, pp);	if(p>=0)	{MGL_MARK_PLOT}	}
364 	else	{	MGL_MARK_PLOT	}
365 }
366 //-----------------------------------------------------------------------------
367 #define MGL_LINE_PLOT	if(Quality&MGL_DRAW_LMEM)	\
368 						{	mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);	d.PenWidth=pw;	\
369 							d.PDef = PDef;	d.pPos = pPos;	line_draw(Pnt[p1],Pnt[p2],&d);	}\
370 						else	{	mglPrim a(1);	a.n3=PDef;	a.s = pPos;	\
371 							a.n1 = p1;	a.n2 = p2;	a.w = pw;	a.angl=0;	add_prim(a);	}
line_plot(long p1,long p2)372 void mglCanvas::line_plot(long p1, long p2)
373 {
374 	if(PDef==0)	return;
375 	if(SamePnt(p1,p2))	return;
376 	if(p1>p2)	{	long kk=p1;	p1=p2;	p2=kk;	}	// rearrange start/end for proper dashing
377 	long pp1=p1,pp2=p2;
378 	mreal pw = fabs(PenWidth)*sqrt(font_factor/400), d=0;
379 	if(TernAxis&12) for(int i=0;i<4;i++)
380 	{	p1 = ProjScale(i, pp1);	p2 = ProjScale(i, pp2);
381 		if(p1>=0&&p2>=0)
382 		{
383 			d += hypot(Pnt[p1].x-Pnt[p2].x, Pnt[p1].y-Pnt[p2].y);
384 			MGL_LINE_PLOT
385 			pPos = fmod(pPos+d/pw, 16);
386 		}
387 	}
388 	else
389 	{
390 		d = hypot(Pnt[p1].x-Pnt[p2].x, Pnt[p1].y-Pnt[p2].y);
391 		MGL_LINE_PLOT
392 		pPos = fmod(pPos+d/pw, 16);
393 	}
394 }
395 //-----------------------------------------------------------------------------
396 #define MGL_TRIG_PLOT	if(Quality&MGL_DRAW_LMEM)	\
397 						{	mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);	d.PenWidth=pw;	\
398 							trig_draw(Pnt[p1],Pnt[p2],Pnt[p3],true,&d);	}\
399 						else{	mglPrim a(2);	a.n1 = p1;	a.n2 = p2;	a.n3 = p3;	\
400 							a.m=mask;	a.angl=MaskAn;	a.w = pw;	add_prim(a);}
trig_plot(long p1,long p2,long p3)401 void mglCanvas::trig_plot(long p1, long p2, long p3)
402 {
403 	if(SamePnt(p1,p2) || SamePnt(p1,p3))	return;
404 	long pp1=p1,pp2=p2,pp3=p3;
405 	mreal pw = fabs(PenWidth)*sqrt(font_factor/400);
406 	if(TernAxis&12) for(int i=0;i<4;i++)
407 	{	p1 = ProjScale(i, pp1);	p2 = ProjScale(i, pp2);
408 		p3 = ProjScale(i, pp3);	if(p1>=0&&p2>=0&&p3>=0)	{MGL_TRIG_PLOT}	}
409 	else	{	MGL_TRIG_PLOT	}
410 }
411 //-----------------------------------------------------------------------------
412 #define MGL_QUAD_PLOT	if(Quality&MGL_DRAW_LMEM)	\
413 						{	mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);	d.PenWidth=pw;	\
414 							quad_draw(Pnt[p1],Pnt[p2],Pnt[p3],Pnt[p4],&d);	}\
415 						else{	mglPrim a(3);	a.n1 = p1;	a.n2 = p2;	a.n3 = p3;	a.n4 = p4;	\
416 							a.m=mask;	a.angl=MaskAn;	a.w = pw;	add_prim(a);	}
quad_plot(long p1,long p2,long p3,long p4)417 void mglCanvas::quad_plot(long p1, long p2, long p3, long p4)
418 {
419 	if(SamePnt(p1,p2))	{	trig_plot(p4,p2,p3);	return;	}
420 	if(SamePnt(p2,p4))	{	trig_plot(p1,p4,p3);	return;	}
421 	if(SamePnt(p1,p3))	{	trig_plot(p1,p2,p4);	return;	}
422 	if(SamePnt(p3,p4))	{	trig_plot(p1,p2,p3);	return;	}
423 	long pp1=p1,pp2=p2,pp3=p3,pp4=p4;
424 	mreal pw = fabs(PenWidth)*sqrt(font_factor/400);
425 	if(TernAxis&12) for(int i=0;i<4;i++)
426 	{	p1 = ProjScale(i, pp1);	p2 = ProjScale(i, pp2);
427 		p3 = ProjScale(i, pp3);	p4 = ProjScale(i, pp4);
428 		if(p1>=0&&p2>=0&&p3>=0&&p4>=0)	{MGL_QUAD_PLOT}	}
429 	else	{	MGL_QUAD_PLOT	}
430 }
431 //-----------------------------------------------------------------------------
text_plot(long p,const wchar_t * text,const char * font,mreal size,mreal sh,mreal col,bool rot)432 mreal mglCanvas::text_plot(long p,const wchar_t *text,const char *font,mreal size,mreal sh,mreal col,bool rot)
433 {
434 	if(p<0 || mgl_isnan(Pnt[p].x) || !text || *text==0)	return 0;
435 	if(size<0)	size *= -FontSize;
436 	if(!font)	font="";
437 
438 	if(TernAxis&4)	// text at projections
439 	{
440 		mreal res;
441 		TernAxis = TernAxis&(~4);
442 		for(int i=0;i<4;i++)
443 			res = text_plot(ProjScale(i,p,true),text,font,size/2,sh,col,rot);
444 		TernAxis = TernAxis|4;
445 		return res;
446 	}
447 	else if(TernAxis&8)	// text at projections
448 	{
449 		mreal res;
450 		TernAxis = TernAxis&(~8);
451 //		for(int i=0;i<4;i++)
452 			res = text_plot(ProjScale(3,p,true),text,font,size/2,sh,col,rot);
453 		TernAxis = TernAxis|8;
454 		return res;
455 	}
456 
457 
458 	mglPnt q=Pnt[p];
459 	mreal ll = q.u*q.u+q.v*q.v;
460 	bool inv=false;
461 //	if(rot && (q.u<0 || (q.u==0 && q.v<0)))		// NOTE this is 1st part of rotation changes (see also GetGlyphPhi())
462 //	{	q.u=-q.u;	q.v=-q.v;	q.w=-q.w;	inv=true;	}
463 
464 	mreal fsize=size/6.5*font_factor, h = fnt->Height(font)*fsize, w, shift = -(sh+0.02)*h;
465 	// text drawing itself
466 
467 #if MGL_HAVE_PTHREAD
468 pthread_mutex_lock(&mutexPtx);
469 #endif
470 #pragma omp critical(ptx)
471 	{
472 		Bt = B;	Bt.norot=(q.sub<0);	// NOTE check this later for mglInPlot
473 		inv = inv ^ (strchr(font,'T')!=0);
474 		if(strchr(font,'V'))	shift = 0.1*h;
475 		else
476 		{
477 			if(inv)	shift = 0.2*h-shift;
478 			shift += 0.015*h;	// Correction for glyph rotation around proper point
479 		}
480 
481 		int align;
482 		float col1=col, col2=col;
483 		if(mglGetStyle(font,0,&align))
484 		{
485 			col1 = AddTexture(font);
486 			col2 = col1+1/MGL_FEPSILON;
487 		}
488 		else if(col<0)
489 			col1 = col2 = AddTexture(char(0.5-col));
490 		align = align&3;
491 
492 		Bt.x = q.x;	Bt.y = q.y - shift;	Bt.z = q.z;
493 		if(ll>0)
494 		{
495 			Bt.x += shift*q.v/sqrt(ll);	Bt.y += shift*(1-q.u/sqrt(ll));
496 			if(q.u==0 && !get(MGL_ENABLE_RTEXT))	Bt.y -= 0.1*h;
497 		}
498 		fscl = fsize;	forg = p;
499 
500 		if(mgl_isnan(ll) || !get(MGL_ENABLE_RTEXT))	ftet = 0;
501 		else if(ll)	ftet = -180*atan2(q.v,q.u)/M_PI;
502 		else 	ftet = NAN;
503 
504 		if(!(Quality&MGL_DRAW_LMEM))	// add text itself
505 		{
506 			mglColor mc = Txt[long(col1)].GetC(col1);
507 			mglPrim a(6);	a.n1 = p;
508 			a.n2 = int(255*mc.r) + 256*(int(255*mc.g) + 256*int(255*mc.b));
509 			a.n3 = Ptx.size();	Ptx.push_back(mglText(text,font));
510 			a.s = size;	a.w = shift;	a.p=ftet;
511 			add_prim(a);
512 		}
513 
514 		q.c=col1;	q.ta=0;	Txt[long(col1)].GetC(col1,0,q);
515 		q.u = q.v = NAN;	q.a=q.ta=1;
516 		memset(Bt.b,0,9*sizeof(float));
517 		Bt.b[0] = Bt.b[4] = Bt.b[8] = fscl;
518 		float opf = Bt.pf;
519 		Bt.RotateN(ftet,0,0,1);	Bt.pf = Bt.norot?1.55:opf;
520 		if(strchr(font,'@'))	// draw box around text
521 		{
522 			long k1,k2,k3,k4;	mglPnt pt;	mglPoint pp;
523 			float y1, y2;
524 			w = fnt->Width(text,font, &y1,&y2);	h = fnt->Height(font);
525 			float d=-w*align/2.-h*0.2;	w+=h*0.4;
526 			pt = q;	pp.Set(d,y1-h*0.2);		PostScale(&Bt,pp);
527 			pt.x=pt.xx=pp.x;	pt.y=pt.yy=pp.y;
528 #pragma omp critical(pnt)
529 			{k1=Pnt.size();	MGL_PUSH(Pnt,pt,mutexPnt);}
530 			pt = q;	pp.Set(w+d,y1-h*0.2);		PostScale(&Bt,pp);
531 			pt.x=pt.xx=pp.x;	pt.y=pt.yy=pp.y;
532 #pragma omp critical(pnt)
533 			{k2=Pnt.size();	MGL_PUSH(Pnt,pt,mutexPnt);}
534 			pt = q;	pp.Set(d,y2+h*0.2);			PostScale(&Bt,pp);
535 			pt.x=pt.xx=pp.x;	pt.y=pt.yy=pp.y;
536 #pragma omp critical(pnt)
537 			{k3=Pnt.size();	MGL_PUSH(Pnt,pt,mutexPnt);}
538 			pt = q;	pp.Set(w+d,y2+h*0.2);		PostScale(&Bt,pp);
539 			pt.x=pt.xx=pp.x;	pt.y=pt.yy=pp.y;
540 #pragma omp critical(pnt)
541 			{k4=Pnt.size();	MGL_PUSH(Pnt,pt,mutexPnt);}
542 			PDef = 0xffff;	// reset to solid line
543 			line_plot(k1,k2);	line_plot(k1,k3);
544 			line_plot(k4,k2);	line_plot(k4,k3);
545 			mreal bl = AddTexture('w');
546 			k1 = CopyNtoC(k1,bl);	k2 = CopyNtoC(k2,bl);
547 			k3 = CopyNtoC(k3,bl);	k4 = CopyNtoC(k4,bl);
548 			quad_plot(k1,k2,k3,k4);
549 		}
550 		const char *ffont = font;
551 		while(*ffont && *ffont!=':')	ffont++;
552 		fsize *= fnt->Puts(text,ffont,col1,col2)/2;
553 	}
554 #if MGL_HAVE_PTHREAD
555 	pthread_mutex_unlock(&mutexPtx);
556 #endif
557 	return fsize;
558 }
559 //-----------------------------------------------------------------------------
Glyph(mreal x,mreal y,mreal f,int s,long j,mreal col)560 void mglCanvas::Glyph(mreal x, mreal y, mreal f, int s, long j, mreal col)
561 {
562 	mglPrim a(4);	// NOTE: no projection since text_plot() did it
563 	a.s = fscl/Bt.pf;
564 	a.w = get(MGL_ENABLE_RTEXT)?ftet:1e5;
565 	a.p = f/fnt->GetFact(s&3);
566 	mreal cc = col<0 ? AddTexture(char(0.5-col)):col;
567 	if(cc<0)	cc = CDef;
568 	a.n1 = AddPnt(&Bt, mglPoint(Bt.x,Bt.y,Bt.z), cc, mglPoint(x,y,NAN), -1, -1);
569 	a.n2 = forg; 	a.n3 = s;	a.n4 = AddGlyph(s,j);
570 	if(a.n1<0)	return;
571 
572 	if(Quality&MGL_DRAW_LMEM)
573 	{
574 		mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);
575 		d.PDef = s;		d.pPos = a.s;	d.PenWidth=a.w;
576 		glyph_draw(a,&d);
577 	}
578 	else	add_prim(a);
579 }
580 //-----------------------------------------------------------------------------
581 #define MGL_GLYPH_PLOT	if(Quality&MGL_DRAW_LMEM)	glyph_draw(a,&d);\
582 						else	add_prim(a);
smbl_plot(long p1,char id,double size)583 void mglCanvas::smbl_plot(long p1, char id, double size)
584 {
585 	if(p1<0 || mgl_isnan(Pnt[p1].x))	return;
586 	mglPnt q=Pnt[p1];
587 	mreal ftet=NAN, ll = q.u*q.u+q.v*q.v;
588 	if(mgl_isnan(ll) || !get(MGL_ENABLE_RTEXT))	ftet = 0;
589 	else if(ll)	ftet = -180*atan2(q.v,q.u)/M_PI;
590 	long pk;	q.u=q.v=0;	q.w=NAN;
591 #pragma omp critical(pnt)
592 	{pk=Pnt.size();	MGL_PUSH(Pnt,q,mutexPnt);}
593 
594 	mglPrim a(4);
595 	a.s = fabs(size)/6.5*font_factor/B.pf;
596 	a.w = get(MGL_ENABLE_RTEXT)?ftet:1e5;
597 	a.p = 1./(mgl_fact*mgl_fgen);
598 	a.n1 = pk;	a.n2 = p1; 	a.n3 = size<0?4:0;	a.n4 = AddGlyph(id);
599 	if(a.n4<0)	return;	// no symbol is defined by user
600 	mglDrawReg d;	d.set(this,dr_x,dr_y,dr_p);
601 	d.PDef = size<0?4:0;	d.pPos = a.s;	d.PenWidth=a.w;
602 	if(TernAxis&12) for(int i=0;i<4;i++)
603 	{	a.n1 = ProjScale(i, pk);	MGL_GLYPH_PLOT	}
604 	else	{	MGL_GLYPH_PLOT	}
605 }
606 //-----------------------------------------------------------------------------
607 //	Plot positioning functions
608 //-----------------------------------------------------------------------------
InPlot(mreal x1,mreal x2,mreal y1,mreal y2,const char * st)609 void mglCanvas::InPlot(mreal x1,mreal x2,mreal y1,mreal y2, const char *st)
610 {
611 	if(Width<=0 || Height<=0 || Depth<=0)	return;
612 	if(!st)		{	InPlot(x1,x2,y1,y2,false);	return;	}
613 	inW = Width*(x2-x1);	inH = Height*(y2-y1);
614 	inX=Width*x1;	inY=Height*y1;	ZMin=1;
615 
616 	if(strchr(st,'T'))	{	y1*=0.9;	y2*=0.9;	}	// general title
617 	bool r = !(strchr(st,'r') || strchr(st,'R') || strchr(st,'>') || strchr(st,'g'));
618 	bool l = !(strchr(st,'l') || strchr(st,'L') || strchr(st,'<') || strchr(st,'g'));
619 	bool u = !(strchr(st,'u') || strchr(st,'U') || strchr(st,'_') || strchr(st,'g'));
620 	bool a = !(strchr(st,'a') || strchr(st,'A') || strchr(st,'^') || strchr(st,'g') || strchr(st,'t'));
621 	// let use simplified scheme -- i.e. no differences between axis, colorbar and/or title
622 	mreal xs=(x1+x2)/2, ys=(y1+y2)/2, f1 = 1.3, f2 = 1.1;
623 	if(strchr(st,'#'))	f1=f2=1.55;
624 	if(r && l)	{	x2=xs+(x2-xs)*f1;	x1=xs+(x1-xs)*f1;	}
625 	else if(r)	{	x2=xs+(x2-xs)*f1;	x1=xs+(x1-xs)*f2;	}
626 	else if(l)	{	x2=xs+(x2-xs)*f2;	x1=xs+(x1-xs)*f1;	}
627 	if(a && u)	{	y2=ys+(y2-ys)*f1;	y1=ys+(y1-ys)*f1;	}
628 	else if(a)	{	y2=ys+(y2-ys)*f1;	y1=ys+(y1-ys)*f2;	}
629 	else if(u)	{	y2=ys+(y2-ys)*f2;	y1=ys+(y1-ys)*f1;	}
630 
631 	B.clear();
632 	if(get(MGL_AUTO_FACTOR)) B.pf = 1.55;	// Automatically change plot factor !!!
633 	B.x = (x1+x2)/2*Width;
634 	B.y = (y1+y2)/2*Height;
635 	B.b[0] = Width*(x2-x1);	B.b[4] = Height*(y2-y1);
636 	B.b[8] = sqrt(B.b[0]*B.b[4]);
637 	B.z = (1.f-B.b[8]/(2*Depth))*Depth;
638 	B1=B;	font_factor = B.b[0] < B.b[4] ? B.b[0] : B.b[4];
639 
640 	mglBlock p;	p.AmbBr = AmbBr;	p.DifBr = DifBr;	p.B = B;
641 	for(int i=0;i<10;i++)	p.light[i] = light[i];
642 	p.id = ObjId;	p.n1=x1*Width;	p.n2=x2*Width;	p.n3=y1*Height;	p.n4=y2*Height;
643 #pragma omp critical(sub)
644 	MGL_PUSH(Sub,p,mutexSub);
645 }
646 //-----------------------------------------------------------------------------
InPlot(mglMatrix & M,mreal x1,mreal x2,mreal y1,mreal y2,bool rel)647 void mglCanvas::InPlot(mglMatrix &M,mreal x1,mreal x2,mreal y1,mreal y2, bool rel)
648 {
649 	if(Width<=0 || Height<=0 || Depth<=0)	return;
650 	M.clear();
651 	if(get(MGL_AUTO_FACTOR)) M.pf = 1.55;	// Automatically change plot factor !!!
652 	if(rel)
653 	{
654 		M.x = B1.x + (x1+x2-1)/2*B1.b[0]/1.55;
655 		M.y = B1.y + (y1+y2-1)/2*B1.b[4]/1.55;
656 		M.b[0] = B1.b[0]*(x2-x1);	M.b[4] = B1.b[4]*(y2-y1);
657 		M.b[8] = sqrt(M.b[0]*M.b[4]);
658 		M.z = B1.z + (1.f-M.b[8]/(2*Depth))*B1.b[8];
659 	}
660 	else
661 	{
662 		M.x = (x1+x2)/2*Width;
663 		M.y = (y1+y2)/2*Height;
664 		M.b[0] = Width*(x2-x1);	M.b[4] = Height*(y2-y1);
665 		M.b[8] = sqrt(M.b[0]*M.b[4]);
666 		M.z = (1.f-M.b[8]/(2*Depth))*Depth;
667 		B1=M;
668 	}
669 	inW=M.b[0];	inH=M.b[4];	ZMin=1;
670 	inX=Width*x1;	inY=Height*y1;
671 	if(!rel || !get(MGL_NO_SCALE_REL))	font_factor = M.b[0] < M.b[4] ? M.b[0] : M.b[4];
672 
673 	mglBlock p;	p.AmbBr = AmbBr;	p.DifBr = DifBr;	p.B = M;
674 	for(int i=0;i<10;i++)	p.light[i] = light[i];
675 	p.id = ObjId;	p.n1=x1*Width;	p.n2=x2*Width;	p.n3=y1*Height;	p.n4=y2*Height;
676 #pragma omp critical(sub)
677 	MGL_PUSH(Sub,p,mutexSub);
678 }
679 //-----------------------------------------------------------------------------
StickPlot(int num,int id,mreal tet,mreal phi)680 void mglCanvas::StickPlot(int num, int id, mreal tet, mreal phi)
681 {
682 	mreal dx,dy,wx,wy,x1,y1,f1,f2;
683 	mglPoint p1(-1,0,0), p2(1,0,0);
684 	// first iteration
685 	InPlot(0,1,0,1,true);	Rotate(tet, phi);
686 	PostScale(GetB(),p1);	PostScale(GetB(),p2);	f1 = B.pf;
687 	dx=(p2.x-p1.x)*1.55/B1.b[0];	dy=(p2.y-p1.y)*1.55/B1.b[4];
688 	wx=1/(1+(num-1)*fabs(dx));		wy=1/(1+(num-1)*fabs(dy));
689 	x1=dx>0?dx*id:dx*(id-num+1);	y1=dy>0?dy*id:dy*(id-num+1);
690 	InPlot(x1*wx,(x1+1)*wx,y1*wy,(y1+1)*wy,true);	Rotate(tet,phi);
691 	f2 = B.pf;	dx*=f1/f2;	dy*=f1/f2;	// add correction due to PlotFactor
692 	wx=1/(1+(num-1)*fabs(dx));		wy=1/(1+(num-1)*fabs(dy));
693 	x1=dx>0?dx*id:dx*(id-num+1);	y1=dy>0?dy*id:dy*(id-num+1);
694 	InPlot(x1*wx,(x1+1)*wx,y1*wy,(y1+1)*wy,true);	Rotate(tet,phi);
695 	f1=f2;	f2 = B.pf;	dx*=f1/f2;	dy*=f1/f2;	// add correction due to PlotFactor
696 	wx=1/(1+(num-1)*fabs(dx));		wy=1/(1+(num-1)*fabs(dy));
697 	x1=dx>0?dx*id:dx*(id-num+1);	y1=dy>0?dy*id:dy*(id-num+1);
698 	InPlot(x1*wx,(x1+1)*wx,y1*wy,(y1+1)*wy,true);	Rotate(tet,phi);
699 }
700 //-----------------------------------------------------------------------------
Rotate(mreal tetz,mreal tetx,mreal tety)701 void mglCanvas::Rotate(mreal tetz,mreal tetx,mreal tety)
702 {
703 	B.Rotate(tetz,tetx,tety);
704 	if(get(MGL_AUTO_FACTOR))
705 	{
706 		float w=(fabs(B.b[3])+fabs(B.b[4])+fabs(B.b[5]))/B1.b[4];
707 		float h=(fabs(B.b[0])+fabs(B.b[1])+fabs(B.b[2]))/B1.b[0];
708 		B.pf = 1.55+0.6147*(w<h ? (h-1):(w-1));
709 	}
710 	size_t n = Sub.size();	if(n>0)	Sub[n-1].B = B;
711 }
712 //-----------------------------------------------------------------------------
Rotate(mreal tetz,mreal tetx,mreal tety)713 void mglMatrix::Rotate(mreal tetz,mreal tetx,mreal tety)
714 {
715 //	RotateN(TetX,1.,0.,0.);
716 //	RotateN(TetY,0.,1.,0.);
717 //	RotateN(TetZ,0.,0.,1.);
718 	float R[9], O[9];
719 	float cx=cos(tetx*M_PI/180), sx=-sin(tetx*M_PI/180), cy=cos(tety*M_PI/180), sy=-sin(tety*M_PI/180), cz=cos(tetz*M_PI/180), sz=-sin(tetz*M_PI/180);
720 	R[0] = cx*cy;			R[1] = -cy*sx;			R[2] = sy;
721 	R[3] = cx*sy*sz+cz*sx;	R[4] = cx*cz-sx*sy*sz;	R[5] =-cy*sz;
722 	R[6] = sx*sz-cx*cz*sy;	R[7] = cx*sz+cz*sx*sy;	R[8] = cy*cz;
723 	memcpy(O,b,9*sizeof(float));
724 	b[0] = R[0]*O[0] + R[3]*O[1] + R[6]*O[2];
725 	b[1] = R[1]*O[0] + R[4]*O[1] + R[7]*O[2];
726 	b[2] = R[2]*O[0] + R[5]*O[1] + R[8]*O[2];
727 	b[3] = R[0]*O[3] + R[3]*O[4] + R[6]*O[5];
728 	b[4] = R[1]*O[3] + R[4]*O[4] + R[7]*O[5];
729 	b[5] = R[2]*O[3] + R[5]*O[4] + R[8]*O[5];
730 	b[6] = R[0]*O[6] + R[3]*O[7] + R[6]*O[8];
731 	b[7] = R[1]*O[6] + R[4]*O[7] + R[7]*O[8];
732 	b[8] = R[2]*O[6] + R[5]*O[7] + R[8]*O[8];
733 }
734 //-----------------------------------------------------------------------------
RotateN(mreal Tet,mreal x,mreal y,mreal z)735 void mglCanvas::RotateN(mreal Tet,mreal x,mreal y,mreal z)
736 {
737 	B.RotateN(Tet,x,y,z);
738 	if(get(MGL_AUTO_FACTOR))
739 	{
740 		float w=(fabs(B.b[3])+fabs(B.b[4])+fabs(B.b[5]))/B1.b[4];
741 		float h=(fabs(B.b[0])+fabs(B.b[1])+fabs(B.b[2]))/B1.b[0];
742 		B.pf = 1.55+0.6147*(w<h ? (h-1):(w-1));
743 	}
744 	size_t n = Sub.size();	if(n>0)	Sub[n-1].B = B;
745 }
746 //-----------------------------------------------------------------------------
RotateN(mreal Tet,mreal vx,mreal vy,mreal vz)747 void mglMatrix::RotateN(mreal Tet,mreal vx,mreal vy,mreal vz)
748 {
749 	float R[9],T[9],c=cos(Tet*M_PI/180),s=-sin(Tet*M_PI/180),r=1-c,n=sqrt(vx*vx+vy*vy+vz*vz);
750 	vx/=n;	vy/=n;	vz/=n;
751 	T[0] = vx*vx*r+c;		T[1] = vx*vy*r-vz*s;	T[2] = vx*vz*r+vy*s;
752 	T[3] = vx*vy*r+vz*s;	T[4] = vy*vy*r+c;		T[5] = vy*vz*r-vx*s;
753 	T[6] = vx*vz*r-vy*s;	T[7] = vy*vz*r+vx*s;	T[8] = vz*vz*r+c;
754 	memcpy(R,b,9*sizeof(float));
755 	b[0] = T[0]*R[0] + T[3]*R[1] + T[6]*R[2];
756 	b[1] = T[1]*R[0] + T[4]*R[1] + T[7]*R[2];
757 	b[2] = T[2]*R[0] + T[5]*R[1] + T[8]*R[2];
758 	b[3] = T[0]*R[3] + T[3]*R[4] + T[6]*R[5];
759 	b[4] = T[1]*R[3] + T[4]*R[4] + T[7]*R[5];
760 	b[5] = T[2]*R[3] + T[5]*R[4] + T[8]*R[5];
761 	b[6] = T[0]*R[6] + T[3]*R[7] + T[6]*R[8];
762 	b[7] = T[1]*R[6] + T[4]*R[7] + T[7]*R[8];
763 	b[8] = T[2]*R[6] + T[5]*R[7] + T[8]*R[8];
764 }
765 //-----------------------------------------------------------------------------
View(mreal tetx,mreal tetz,mreal tety)766 void mglCanvas::View(mreal tetx,mreal tetz,mreal tety)
767 {	Bp.Rotate(-tetz,-tetx,-tety);	}
768 //-----------------------------------------------------------------------------
Zoom(mreal x1,mreal y1,mreal x2,mreal y2)769 void mglCanvas::Zoom(mreal x1, mreal y1, mreal x2, mreal y2)
770 {
771 	Bp.pf=0;	Bp.clear();		ClfZB();
772 	if(x1==x2 || y1==y2)	{	x1=y1=0;	x2=y2=1;	}
773 	x1=2*x1-1;	x2=2*x2-1;	y1=2*y1-1;	y2=2*y2-1;
774 	Bp.b[0]=2/fabs(x2-x1);	Bp.b[4]=2/fabs(y2-y1);
775 	Bp.x=(x1+x2)/fabs(x2-x1);Bp.y=(y1+y2)/fabs(y2-y1);
776 }
777 //-----------------------------------------------------------------------------
GetSplId(long x,long y) const778 int mglCanvas::GetSplId(long x,long y) const
779 {
780 	long id=-1;
781 	for(long i=Sub.size()-1;i>=0;i--)
782 	{
783 		const mglBlock &p = Sub[i];
784 		if(p.n1<=x && p.n2>=x && p.n3<=y && p.n4>=y)
785 		{	id=p.id;	break;	}
786 	}
787 	return id;
788 }
789 //-----------------------------------------------------------------------------
Aspect(mreal Ax,mreal Ay,mreal Az)790 void mglCanvas::Aspect(mreal Ax,mreal Ay,mreal Az)
791 {
792 	if(mgl_isnan(Ax))
793 	{
794 		mreal dy = (Max.y-Min.y), dx = (Max.x-Min.x), dz = (Max.z-Min.z);
795 		if(mgl_islog(Min.x,Max.x) && fx)	dx = log10(Max.x/Min.x);
796 		if(mgl_islog(Min.y,Max.y) && fy)	dy = log10(Max.y/Min.y);
797 		if(mgl_islog(Min.z,Max.z) && fz)	dz = log10(Max.z/Min.z);
798 		mreal gy=exp(M_LN10*floor(0.5+log10(fabs(dy/dx))));
799 		mreal gz=exp(M_LN10*floor(0.5+log10(fabs(dz/dx))));
800 		if(Ay>0)	gy*=Ay;
801 		if(Az>0)	gz*=Az;
802 		Ax = inH*dx;	Ay = inW*dy*gy;	Az = sqrt(inW*inH)*dz*gz;
803 	}
804 	mreal a = fabs(Ax) > fabs(Ay) ? fabs(Ax) : fabs(Ay);
805 	a = a > fabs(Az) ? a : fabs(Az);
806 	if(a==0)	{	SetWarn(mglWarnZero,"Aspect");	return;	}
807 	Ax/=a;	Ay/=a;	Az/=a;
808 	B.b[0] *= Ax;	B.b[3] *= Ax;	B.b[6] *= Ax;
809 	B.b[1] *= Ay;	B.b[4] *= Ay;	B.b[7] *= Ay;
810 	B.b[2] *= Az;	B.b[5] *= Az;	B.b[8] *= Az;
811 	size_t n = Sub.size();	if(n>0)	Sub[n-1].B = B;
812 }
813 //-----------------------------------------------------------------------------
Shear(mreal Sx,mreal Sy)814 void mglCanvas::Shear(mreal Sx,mreal Sy)
815 {
816 	float Fx=1+fabs(Sx)*inH/inW, Fy=1+fabs(Sy)*inW/inH;
817 	const float R[6]={B.b[0],B.b[1],B.b[2],B.b[3],B.b[4],B.b[5]};
818 	B.b[0] = (R[0]+Sx*R[3])/Fx;	B.b[1] = (R[1]+Sx*R[4])/Fx;	B.b[2] = (R[2]+Sx*R[5])/Fx;
819 	B.b[3] = (R[3]+Sy*R[0])/Fy;	B.b[4] = (R[4]+Sy*R[1])/Fy;	B.b[5] = (R[5]+Sy*R[2])/Fy;
820 	size_t n = Sub.size();	if(n>0)	Sub[n-1].B = B;
821 }
822 //-----------------------------------------------------------------------------
ShearPlot(int num,int id,mreal sx,mreal sy,mreal xd,mreal yd)823 void mglCanvas::ShearPlot(int num, int id, mreal sx, mreal sy, mreal xd, mreal yd)
824 {
825 	InPlot(0,1,0,1,true);
826 	if(!(fabs(xd)<=1 && fabs(yd)<=1))	{	xd=1;	yd=0;	}
827 	mreal wx,wy,dx,dy,wf,hf,x1,y1;
828 	int ix=sy>=0?id:num-id-1, iy=sx>=0?id:num-id-1;
829 	for(int i=0;i<3;i++)	// iterations to solve cubic equation
830 	{
831 		wx = fabs(sx)*inH/inW;	dx = xd + yd*wx;	wf = 1+wx+(num-1)*fabs(dx);
832 		wy = fabs(sy)*inW/inH;	dy = yd + xd*wy;	hf = 1+wy+(num-1)*fabs(dy);
833 		x1=(dx>=0?ix:(ix-num+1))*dx;
834 		y1=(dy>=0?iy:(iy-num+1))*dy;
835 		InPlot(x1/wf,(x1+1+wx)/wf,y1/hf,(y1+1+wy)/hf,true);
836 	}
837 	Shear(sx,sy);
838 }
839 //-----------------------------------------------------------------------------
840 //	Lighting and transparency
841 //-----------------------------------------------------------------------------
Fog(mreal d,mreal dz)842 void mglCanvas::Fog(mreal d, mreal dz)	{	FogDist=d;	FogDz = dz;	}
843 //-----------------------------------------------------------------------------
Light(int n,bool enable)844 void mglCanvas::Light(int n, bool enable)
845 {
846 	if(n<0 || n>9)	{	SetWarn(mglWarnLId,"Light");	return;	}
847 	light[n].n = enable;
848 	size_t m=Sub.size();	if(m>0)	Sub[m-1].light[n].n = enable;
849 }
850 //-----------------------------------------------------------------------------
AddLight(int n,mglPoint r,mglPoint d,char col,mreal br,mreal ap)851 void mglCanvas::AddLight(int n, mglPoint r, mglPoint d, char col, mreal br, mreal ap)
852 {
853 	if(n<0 || n>9)	{	SetWarn(mglWarnLId,"AddLight");	return;	}
854 	light[n].n = true;	light[n].a = ap>0?ap*ap:3;
855 	light[n].b = br;	light[n].r = r;
856 	light[n].d = d;		light[n].c.Set(col);
857 	size_t m=Sub.size();	if(m>0)	Sub[m-1].light[n] = light[n];
858 }
859 //-----------------------------------------------------------------------------
arrow_plot(long n1,long n2,char st)860 void mglCanvas::arrow_plot(long n1, long n2, char st)
861 {
862 	if(n1<0 || n2<0 || !strchr("AVKSDTIOX",st))	return;
863 	float ll = PenWidth*ArrowSize*0.35*font_factor;
864 	uint64_t m=mask;	int ma=MaskAn;
865 	ResetMask();
866 	if((Quality&3)==3)
867 		arrow_plot_3d(n1, n2, st, ll);
868 	else
869 		arrow_draw(n1, n2, st, ll);
870 	mask=m;	MaskAn=ma;
871 }
872 //-----------------------------------------------------------------------------
mgl_ftoa(double v,const char * fmt)873 std::wstring MGL_EXPORT mgl_ftoa(double v, const char *fmt)
874 {
875 	char se[70], sf[70], ff[8]="%.3f", ee[8]="%.3e";
876 	int dig=3;
877 	for(const char *s="0123456789";*s;s++)	if(mglchr(fmt,*s))	dig = *s-'0';
878 	if(mglchr(fmt,'E'))	ee[3] = 'E';
879 	bool plus = mglchr(fmt,'+');
880 	bool tex = mglchr(fmt,'F');
881 	int fdig = int(log10(v));	fdig = fdig>0?(fdig<dig?dig-fdig:0):dig;
882 	ff[2] = fdig+'0';	ee[2] = dig+'0';
883 	snprintf(se,64,ee,v);	snprintf(sf,64,ff,v);
884 	se[63] = sf[63] = 0;
885 	long le=strlen(se), lf=strlen(sf), i;
886 
887 	// clear fix format
888 	for(i=lf-1;i>=lf-fdig && sf[i]=='0';i--)	sf[i]=0;
889 	if(sf[i]=='.')	sf[i]=0;
890 	lf = strlen(sf);
891 	// parse -nan numbers
892 	if(!strcmp(sf,"-nan"))	memcpy(sf,"nan",4);
893 
894 
895 	// clear exp format
896 	int st = se[0]=='-'?1:0;
897 	if(strcmp(sf,"nan"))
898 	{
899 		if(plus || se[3+st+dig]=='-')	// first remove zeros after 'e'
900 		{
901 			for(i=(dig>0?4:3)+st+dig;i<le && se[i]=='0';i++);
902 			memmove(se+(dig>0?4:3)+st+dig,se+i,le-i+1);
903 		}
904 		else
905 		{
906 			for(i=(dig>0?3:2)+st+dig;i<le && (se[i]=='0' || se[i]=='+');i++);
907 			memmove(se+(dig>0?3:2)+st+dig,se+i,le-i+1);
908 		}
909 	}
910 	le=strlen(se);
911 	// don't allow '+' at the end
912 	if(le>0 && se[le-1]=='+')	se[--le]=0;
913 	// remove single 'e'
914 	if(le>0 && (se[le-1]=='e' || se[le-1]=='E'))	se[--le]=0;
915 	for(i=1+st+dig;i>st && se[i]=='0';i--);	// remove final '0'
916 	if(se[i]=='.')	i--;
917 	memmove(se+i+1,se+2+st+dig,le-dig);	le=strlen(se);
918 	// add '+' sign if required
919 	if(plus && !strchr("-0niNI",se[0]))
920 	{	for(size_t i=le+1;i>0;i--)	se[i]=se[i-1];
921 		for(size_t i=lf+1;i>0;i--)	sf[i]=sf[i-1];
922 		se[0] = sf[0] = '+';	}
923 	if((lf>le && !mglchr(fmt,'f')) || !strcmp(sf,"0") || !strcmp(sf,"-0"))	strcpy(sf,se);
924 	lf = strlen(sf);
925 	std::wstring res;	res.reserve(lf+8);
926 
927 	if(mglchr(fmt,'-') && !(plus||tex))		// replace '-' by "\minus"
928 		for(i=0;i<lf;i++)	res += sf[i];
929 	else
930 		for(i=0;i<lf;i++)	res += sf[i]!='-'?wchar_t(sf[i]):0x2212;
931 	if(tex)	// TeX notation: 'e' -> "\cdot 10^{...}"
932 	{
933 		if(res[0]=='1' && (res[1]=='e' || res[1]=='E'))
934 		{	res.replace(0,2,L"10^{");	res += L'}';	}
935 		else if(wcschr(L"+-\u2212",res[0]) && res[1]=='1' && (res[2]=='e' || res[2]=='E'))
936  		{	res.replace(1,2,L"10^{");	res += L'}';	}
937 		else
938 		{
939 			size_t p;
940 			for(p=1;p<res.length();p++)	if(res[p]==L'e' || res[p]==L'E')	break;
941 			if(p<res.length())
942 			{	res.replace(p,1,L"⋅10^{");	res += L'}';	}
943 		}
944 	}
945 	return res;
946 }
947 //-----------------------------------------------------------------------------
Legend(const std::vector<mglText> & leg,mreal x,mreal y,const char * font,const char * opt)948 void mglCanvas::Legend(const std::vector<mglText> &leg, mreal x, mreal y, const char *font, const char *opt)
949 {
950 	long n=leg.size();
951 	mreal iw, ih;
952 	if(n<1)	{	SetWarn(mglWarnLeg,"Legend");	return;	}
953 	mreal ll = SaveState(opt);	if(mgl_isnan(ll))	ll=0.1;
954 	if(saved)	MarkSize=MSS;	// restore back size of marks
955 	static int cgid=1;	StartGroup("Legend",cgid++);
956 	if(ll<=0 || mgl_isnan(ll))	ll=0.1;
957 	ll *=font_factor;
958 	mreal size = 0.8*FontSize;
959 	// setup font and parse absolute coordinates
960 	if(!font)	font="#";
961 	char *pA, *ff = new char[strlen(font)+3];
962 	const char *fmt = strchr(font,':');
963 	strcpy(ff,fmt?fmt:"");	strcat(ff,":L");	Push();
964 	if((pA=strchr(ff,'A')))
965 	{	*pA = ' ';	InPlot(0,1,0,1,false);	iw=B1.b[0];	ih=B1.b[4];	}
966 	else if(strchr(font,'A'))
967 	{	InPlot(0,1,0,1,false);	iw=B1.b[0];	ih=B1.b[4];	}
968 	else	{	iw=B1.b[0]/B1.pf;	ih=B1.b[4]/B1.pf;	}
969 	// find sizes
970 	mreal h=TextHeight(font,size);
971 	mreal dx = 0.03*iw, dy = 0.03*ih, w=0, t, sp=TextWidth(" ",font,size);
972 	for(long i=0;i<n;i++)		// find text length
973 	{
974 		t = TextWidth(leg[i].text.c_str(),font,size)+sp;
975 		if(leg[i].stl.empty())	t -= ll;
976 		w = w>t ? w:t;
977 	}
978 	w += ll+0.01*iw;	// add space for lines
979 	long j = long((ih*0.95)/h);	if(j<1)	j=1;
980 	long ncol = 1+(n-1)/j, nrow = (n+ncol-1)/ncol;
981 	if(strchr(font,'-'))	// horizontal legend
982 	{
983 		j = long((iw*0.95)/w);	if(j<1)	j=1;
984 		nrow = 1+(n-1)/j;
985 		ncol = (n+nrow-1)/nrow;
986 	}
987 	if(strchr(font,'^'))	// use "external" positioning
988 	{
989 		x = x>=0.5 ? x*iw : x*iw-w*ncol-2*dx;
990 		y = y>=0.5 ? y*ih : y*ih-h*nrow-2*dy;
991 	}
992 	else
993 	{
994 		x *= iw-w*ncol-2*dx;
995 		y *= ih-h*nrow-2*dy;
996 	}
997 	x += B.x-iw/2+dx;	y += B.y-ih/2+dy;
998 	// draw it
999 	mglPoint p,q(NAN,NAN,NAN);
1000 
1001 	mreal cc = AddTexture(font);
1002 	SetMask("");
1003 	mreal c1,c2;	//=AddTexture(char(k1?k1:'w')), c2=AddTexture(char(k2?k2:'k'));
1004 	if(cc<2 || Txt[long(cc+0.5)].n==0)
1005 	{	c1 = AddTexture('w');	cc = c2 = AddTexture('k');	}
1006 	else switch(Txt[long(cc+0.5)].n)
1007 	{
1008 	case 1:	c1 = AddTexture('w');	c2 = AddTexture('k');	break;
1009 	case 2:	c1 = cc;	cc+=1/MGL_FEPSILON;	c2 = AddTexture('k');	break;
1010 	default:	c1 = cc;	c2 = cc+0.5;	cc += 1/MGL_FEPSILON;	break;
1011 	}
1012 	if((Flag&3)==2)	{	mreal tt=c1;	c2=c1;	c1=tt;	}
1013 
1014 	mglMatrix M=B;	M.norot=true;
1015 	if(strchr(font,'#'))	// draw bounding box
1016 	{
1017 		SetPenPal("k-");
1018 		long k1=AddPnt(&M,mglPoint(x,y,Depth/1.01),c1,q,1,0);
1019 		long k2=AddPnt(&M,mglPoint(x+w*ncol,y,Depth/1.01),c1,q,1,0);
1020 		long k3=AddPnt(&M,mglPoint(x,y+h*nrow,Depth/1.01),c1,q,1,0);
1021 		long k4=AddPnt(&M,mglPoint(x+w*ncol,y+h*nrow,Depth/1.01),c1,q,1,0);
1022 		quad_plot(k1,k2,k3,k4);
1023 		k1=CopyNtoC(k1,c2);	k2=CopyNtoC(k2,c2);
1024 		k3=CopyNtoC(k3,c2);	k4=CopyNtoC(k4,c2);
1025 		line_plot(k1,k2);	line_plot(k2,k4);
1026 		line_plot(k4,k3);	line_plot(k3,k1);
1027 	}
1028 	for(long i=0;i<n;i++)	// draw lines and legend
1029 	{
1030 		long iy=nrow-(i%nrow)-1,ix=i/nrow;
1031 		char m=SetPenPal(leg[i].stl.c_str());
1032 		long k1=AddPnt(&M,mglPoint(x+ix*w+0.1*ll,y+iy*h+0.45*h,Depth),CDef,q,-1,0);
1033 		long k2=AddPnt(&M,mglPoint(x+ix*w+0.9*ll,y+iy*h+0.45*h,Depth),CDef,q,-1,0);	pPos=0;
1034 		if(!leg[i].stl.empty())	line_plot(k1,k2);
1035 		if(m)	for(j=0;j<LegendMarks;j++)
1036 		{
1037 			p.Set(x+ix*w+0.1f*ll + (j+1)*0.8f*ll/(1.+LegendMarks),y+iy*h+0.45*h,Depth);
1038 			mark_plot(AddPnt(&M,p,CDef,q,-1,0),m);
1039 		}
1040 		p.Set(x+ix*w+((!leg[i].stl.empty())?ll:0.01*iw), y+iy*h+0.15*h, Depth);
1041 		text_plot(AddPnt(&M,p,-1,q,-1,0), leg[i].text.c_str(), ff, size,0,cc);
1042 	}
1043 	Pop();	EndGroup();	delete []ff;
1044 }
1045 //-----------------------------------------------------------------------------
Table(mreal x,mreal y,HCDT val,const wchar_t * text,const char * frm,const char * opt)1046 void mglCanvas::Table(mreal x, mreal y, HCDT val, const wchar_t *text, const char *frm, const char *opt)
1047 {
1048 //	if(x>=1) 	{	SetWarn(mglWarnSpc,"Table");	return;	}
1049 	long i,j,m=val->GetNy(),n=val->GetNx();
1050 //	mreal pos=SaveState(opt);
1051 	mreal vw = SaveState(opt);
1052 	static int cgid=1;	StartGroup("Table",cgid++);
1053 	bool grid = mglchr(frm,'#'), eqd = mglchr(frm,'='), lim = mglchr(frm,'|');
1054 	if(mgl_isnan(vw))	vw=1;	else 	lim = true;
1055 	if(!text)	text=L"";
1056 	x=x<0?0:x; 	y=y<0?0:y; 	y=y>1?1:y;
1057 //	if(vw>1-x)	vw=1-x;
1058 
1059 	char fmt[8]="3",ss[2]=" ";
1060 	for(const char *s="0123456789";*s;s++)	if(mglchr(frm,*s))	fmt[0]=*s;
1061 	for(const char *s="f+E-F";*s;s++)	if(mglchr(frm,*s))
1062 	{	ss[0] = *s;	strcat(fmt,ss);	}
1063 	std::vector<std::wstring> str;
1064 	for(i=0;i<n;i++)		// prepare list of strings first
1065 	{
1066 		std::wstring buf;
1067 		for(j=0;j+1<m;j++)
1068 			buf += mgl_ftoa(val->v(i,j),fmt)+L'\n';
1069 		buf += mgl_ftoa(val->v(i,m-1),fmt);
1070 		str.push_back(buf);
1071 	}
1072 
1073 	mreal sp=2*TextWidth(" ",frm,-1), w=*text ? sp+TextWidth(text,frm,-1):0, w1=0, ww, h;
1074 	for(i=0;i<n;i++)		// find width for given font size
1075 	{
1076 		ww = TextWidth(str[i].c_str(),frm,-1)+sp;
1077 		w1 = w1<ww?ww:w1;
1078 		if(!eqd)	w += ww;
1079 	}
1080 	if(eqd)	w += n*w1;
1081 	// reduce font size if table have to be inside inplot
1082 	mreal fsize=FontSize;
1083 	if(lim && w>vw*inW)
1084 	{	h=vw*inW/w;	SetFontSize(-h); 	w*=h; 	w1*=h;	sp*=h;	}
1085 	h = TextHeight(frm,-1);	// now we can determine text height
1086 
1087 	x = x*(inW-w)+B.x-inW/2;
1088 	y = y*(inH-h*m)+B.y-inH/2;
1089 
1090 	mglPoint p,q(NAN,NAN);
1091 	mreal xx,yy;
1092 	if(grid)	// draw bounding box
1093 	{
1094 		SetPenPal("k-");
1095 		long k1,k2;
1096 		k1=AddPnt(&B,mglPoint(x,y,Depth),-1,q,-1,0);
1097 		k2=AddPnt(&B,mglPoint(x,y+m*h,Depth),-1,q,-1,0);
1098 		line_plot(k1,k2);
1099 		ww = *text ? TextWidth(text,frm,-1)+sp:0;
1100 		k1=AddPnt(&B,mglPoint(x+ww,y,Depth),-1,q,-1,0);
1101 		k2=AddPnt(&B,mglPoint(x+ww,y+m*h,Depth),-1,q,-1,0);
1102 		line_plot(k1,k2);
1103 		for(i=0,xx=x+ww,yy=y;i<n;i++)
1104 		{
1105 			xx += eqd ? w1:(TextWidth(str[i].c_str(),frm,-1)+sp);
1106 			k1=AddPnt(&B,mglPoint(xx,yy,Depth),-1,q,-1,0);
1107 			k2=AddPnt(&B,mglPoint(xx,yy+m*h,Depth),-1,q,-1,0);
1108 			line_plot(k1,k2);
1109 		}
1110 		for(i=0,xx=x,yy=y;i<=m;i++)
1111 		{
1112 			k1=AddPnt(&B,mglPoint(xx,yy,Depth),-1,q,-1,0);
1113 			k2=AddPnt(&B,mglPoint(xx+w,yy,Depth),-1,q,-1,0);
1114 			line_plot(k1,k2);	yy += h;
1115 		}
1116 	}
1117 	int align;	mglGetStyle(frm, 0, &align);
1118 	if(*text)
1119 	{
1120 		ww = TextWidth(text,frm,-1)+sp;
1121 		long k1=AddPnt(&B,mglPoint(x+ww*align/2.,y+h*(m-0.9),Depth),-1,q,-1,0);
1122 		text_plot(k1,text,frm);
1123 	}
1124 	else 	ww = 0;
1125 	for(i=0,xx=x+ww,yy=y+h*(m-0.9);i<n;i++)	// draw lines and legend
1126 	{
1127 		ww = eqd ? w1:(TextWidth(str[i].c_str(),frm,-1)+sp);
1128 		long k1=AddPnt(&B,mglPoint(xx+ww*align/2.,yy,Depth),-1,q,-1,0);
1129 		text_plot(k1,str[i].c_str(),frm);	xx += ww;
1130 	}
1131 	FontSize = fsize;	EndGroup();
1132 }
1133 //-----------------------------------------------------------------------------
Title(const char * title,const char * stl,mreal size)1134 void mglCanvas::Title(const char *title,const char *stl,mreal size)
1135 {
1136 	if(!title)	title="";
1137 	MGL_TO_WCS(title,Title(wcs, stl,size));
1138 }
1139 //-----------------------------------------------------------------------------
Title(const wchar_t * title,const char * stl,mreal size)1140 void mglCanvas::Title(const wchar_t *title,const char *stl,mreal size)
1141 {
1142 	mreal s = size>0 ? size/FontSize:-size, h=TextHeight(stl,size)*s/2;
1143 	if(h>=inH)	{	SetWarn(mglWarnSpc,"Title");	return;	}
1144 	static int cgid=1;	StartGroup("Title",cgid++);
1145 	int align;
1146 	bool box=mglchr(stl,'#'), col = mglGetStyle(stl,0,&align);
1147 	align = align&3;
1148 	mreal y=inY+inH-h, zpos = 0;//3*Depth;
1149 	mglPoint p(inX + inW*align/2.,y,zpos),q(NAN,NAN,NAN);
1150 	mglMatrix M=B;	M.norot=true;
1151 	if(title)	text_plot(AddPnt(&M,p,-1,q,-1,0),title,stl,size);
1152 	if(box)	//	draw boungind box
1153 	{
1154 		mreal c1=AddTexture('w'), c2=col?AddTexture(stl):AddTexture('k');
1155 		if((Flag&3)==2 && !col)	{	mreal cc=c1;	c2=c1;	c1=cc;	}
1156 		else if((Flag&3)==2)	c1=AddTexture('k');
1157 		long k1,k2,k3,k4;
1158 		k1=AddPnt(&M,mglPoint(inX,y-h*0.4,zpos),c1,q,-1,0);
1159 		k2=AddPnt(&M,mglPoint(inX+inW,y-h*0.4,zpos),c1,q,-1,0);
1160 		k3=AddPnt(&M,mglPoint(inX,y+h,zpos),c1,q,-1,0);
1161 		k4=AddPnt(&M,mglPoint(inX+inW,y+h,zpos),c1,q,-1,0);
1162 		quad_plot(k1,k2,k3,k4);
1163 		k1=CopyNtoC(k1,c2);	k2=CopyNtoC(k2,c2);
1164 		k3=CopyNtoC(k3,c2);	k4=CopyNtoC(k4,c2);
1165 		line_plot(k1,k2);	line_plot(k2,k4);
1166 		line_plot(k4,k3);	line_plot(k3,k1);
1167 	}
1168 	B1.y -= h/2;	B1.b[4] -= h;	B=B1;
1169 	inH-=h;	font_factor = B.b[0] < B.b[4] ? B.b[0] : B.b[4];
1170 	EndGroup();
1171 }
1172 //-----------------------------------------------------------------------------
StartAutoGroup(const char * lbl)1173 void mglCanvas::StartAutoGroup (const char *lbl)
1174 {
1175 	static int id=1;
1176 	if(lbl==NULL)	{	id=1;	grp_counter=0;	return;	}
1177 	grp_counter++;
1178 	if(grp_counter>1)	return;	// do nothing in "subgroups"
1179 	if(ObjId<0)	{	ObjId = -id;	id++;	}
1180 	size_t len = Grp.size();
1181 	if(ObjId>=0 && (len==0 || (len>0 && ObjId!=Grp[len-1].Id)))
1182 #pragma omp critical(grp)
1183 	{	MGL_PUSH(Grp,mglGroup(lbl,ObjId),mutexGrp);}
1184 	else if(ObjId<0)
1185 #pragma omp critical(grp)
1186 	{	MGL_PUSH(Grp,mglGroup(lbl,ObjId),mutexGrp);}
1187 }
1188 //-----------------------------------------------------------------------------
EndGroup()1189 void mglCanvas::EndGroup()
1190 {
1191 	LoadState();	SetMask("");
1192 	if(Quality&MGL_DRAW_LMEM)
1193 	{
1194 		Pnt.clear();	Prm.clear();	Ptx.clear();	ClearPrmInd();
1195 		Glf.clear();	Act.clear(); 	Grp.clear();
1196 	}
1197 	if(grp_counter>0)	grp_counter--;
1198 }
1199 //-----------------------------------------------------------------------------
IsActive(int xs,int ys,int & n)1200 int mglCanvas::IsActive(int xs, int ys,int &n)
1201 {
1202 	long i, h = (Width>Height ? Height:Width)/100;
1203 	for(i=0;i<(long)Act.size();i++)
1204 	{
1205 		const mglActivePos &p=Act[i];
1206 		if(abs(xs-p.x)<=h && abs(ys-p.y)<=h)
1207 		{	n=p.n;	return p.id;		}
1208 	}
1209 	n=-1;	return GetObjId(xs,ys);
1210 }
1211 //-----------------------------------------------------------------------------
Push()1212 void mglCanvas::Push()
1213 {
1214 #pragma omp critical(stk)
1215 	{MGL_PUSH(stack,B,mutexStk);}
1216 }
1217 //-----------------------------------------------------------------------------
Pop()1218 void mglCanvas::Pop()
1219 {
1220 	B = stack.back();
1221 #if MGL_HAVE_PTHREAD
1222 	pthread_mutex_lock(&mutexStk);
1223 	stack.pop_back();
1224 	pthread_mutex_unlock(&mutexStk);
1225 #else
1226 #pragma omp critical(stk)
1227 	stack.pop_back();
1228 #endif
1229 }
1230