1 /***************************************************************************
2  * axis.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 <time.h>
21 #include <ctype.h>
22 #include "mgl2/data.h"
23 #include "mgl2/canvas.h"
24 #include "mgl2/prim.h"
25 #include "mgl2/eval.h"
26 std::wstring MGL_EXPORT mgl_ftoa(double v, const char *fmt);
27 //-----------------------------------------------------------------------------
mgl_localtime(const time_t * clock,tm * result,bool use_utc)28 MGL_NO_EXPORT inline struct tm *mgl_localtime (const time_t *clock, tm *result, bool use_utc)
29 {	if (!clock || !result) return NULL;
30 	const tm *res = use_utc?gmtime(clock):localtime(clock);
31 	memcpy(result,res,sizeof(tm));	return result;	}
32 //-----------------------------------------------------------------------------
mgl_have_color(const char * stl)33 long MGL_EXPORT_PURE mgl_have_color(const char *stl)
34 {
35 	return mgl_get_num_color(stl,0);
36 // 	long j=0;
37 // 	if(stl)	for(long i=0;stl[i];i++)
38 // 	{
39 // 		if(strchr(MGL_COLORS,stl[i]))	j++;
40 // 		if(stl[i]=='{' && stl[i+1]=='x')	j++;
41 // 	}
42 // 	return j;
43 }
44 //-----------------------------------------------------------------------------
mgl_wcstrim(wchar_t * str)45 void MGL_EXPORT mgl_wcstrim(wchar_t *str)
46 {
47 	if(!str || *str==0)	return;
48 	size_t n=mgl_wcslen(str), k, i;
49 	for(k=0;k<n;k++)	if(str[k]>' ')	break;
50 	for(i=n;i>k;i--)	if(str[i-1]>' ')	break;
51 	for(size_t j=0;j<i-k;j++)	str[j]=str[j+k];
52 	str[i-k]=0;
53 }
54 //-----------------------------------------------------------------------------
55 //		Ticks setup
56 //-----------------------------------------------------------------------------
SetAxisStl(const char * stl,const char * tck,const char * sub)57 void mglCanvas::SetAxisStl(const char *stl, const char *tck, const char *sub)
58 {
59 	if(!stl || !(*stl))	mgl_strncpy(AxisStl,"k",32);
60 	else 				mgl_strncpy(AxisStl,stl,32);
61 	if(!tck || !(*tck))	mgl_strncpy(TickStl,AxisStl,32);
62 	else 				mgl_strncpy(TickStl,tck,32);
63 	if(!sub || !(*sub))	mgl_strncpy(SubTStl,TickStl,32);
64 	else 				mgl_strncpy(SubTStl,sub,32);
65 }
66 //-----------------------------------------------------------------------------
SetTickLen(mreal tlen,mreal stt)67 void mglCanvas::SetTickLen(mreal tlen, mreal stt)
68 {	TickLen = tlen?tlen:0.02;	st_t=stt>0?stt:1;	}
69 //-----------------------------------------------------------------------------
SetTicks(char dir,mreal d,int ns,mreal org,const wchar_t * lbl)70 void mglCanvas::SetTicks(char dir, mreal d, int ns, mreal org, const wchar_t *lbl)
71 {
72 	if(!strchr("xyzca",dir))	return;
73 	mglAxis &aa = GetAxis(dir);
74 
75 	if(aa.f==1)	aa.t.clear();
76 	aa.d=d;	aa.f=0;	aa.ns=ns;	aa.o=org;
77 	aa.txt.clear();
78 	if(!lbl || *lbl==0)	aa.fact.clear();
79 	else	aa.fact = lbl;
80 }
81 //-----------------------------------------------------------------------------
AddTick(char dir,double v,const wchar_t * lbl)82 void mglCanvas::AddTick(char dir, double v, const wchar_t *lbl)
83 {
84 	if(!strchr("xyzca",dir))	return;
85 	mglAxis &aa = GetAxis(dir);
86 	bool ff = GetFormula(dir);
87 
88 	UpdateAxis();	AdjustTicks(aa,ff);
89 	if(!v || !lbl || !lbl[0])	{	aa.f = 0;	return;	}
90 	aa.f = 2;	aa.ns=0;	aa.ds=0;
91 	aa.AddLabel(lbl,v);
92 }
93 //-----------------------------------------------------------------------------
AddTick(char dir,double v,const char * lbl)94 void mglCanvas::AddTick(char dir, double v, const char *lbl)
95 {	MGL_TO_WCS(lbl,AddTick(dir,v,wcs));	}
96 //-----------------------------------------------------------------------------
SetTicksVal(char dir,HCDT v,const wchar_t * lbl,bool add)97 void mglCanvas::SetTicksVal(char dir, HCDT v, const wchar_t *lbl, bool add)
98 {
99 	if(!strchr("xyzca",dir))	return;
100 	mglAxis &aa = GetAxis(dir);
101 	bool ff = GetFormula(dir);
102 
103 	if(add)	{	UpdateAxis();	AdjustTicks(aa,ff);	}
104 	else	aa.txt.clear();
105 	if(!v || !lbl || !lbl[0])	{	aa.f = 0;	return;	}
106 	aa.f = 2;	aa.ns=0;	aa.ds=0;
107 	long i=0,l=0,n=v->GetNx();
108 	for(long j=0;i<n && lbl[j];j++)
109 	{
110 		if(lbl[j]=='\n')
111 		{
112 			aa.AddLabel(std::wstring(lbl+l,j-l),v->v(i));
113 			i++;	l=j+1;
114 		}
115 		if(j>1 && lbl[j]=='n' && lbl[j-1]=='\\')
116 		{
117 			aa.AddLabel(std::wstring(lbl+l,j-l-1),v->v(i));
118 			i++;	l=j+1;
119 		}
120 	}
121 	if(i<n && lbl[l])	aa.AddLabel(lbl+l,v->v(i));
122 }
123 //-----------------------------------------------------------------------------
SetTicksVal(char dir,HCDT v,const char * lbl,bool add)124 void mglCanvas::SetTicksVal(char dir, HCDT v, const char *lbl, bool add)
125 {	MGL_TO_WCS(lbl,SetTicksVal(dir,v,wcs,add));	}
126 //-----------------------------------------------------------------------------
SetTicksVal(char dir,const wchar_t * lbl,bool add)127 void mglCanvas::SetTicksVal(char dir, const wchar_t *lbl, bool add)
128 {
129 	long i=0,len=mgl_wcslen(lbl);
130 	for(long j=1;j<len;j++)
131 		if(lbl[j]=='\n' || (lbl[j]=='n' && lbl[j-1]=='\\'))	i++;
132 	if(i>63)	i=63;
133 	mglData val(i+1);	val.Fill(Min.x,Max.x);
134 	SetTicksVal(dir, &val, lbl, add);
135 }
136 //-----------------------------------------------------------------------------
SetTicksVal(char dir,const char * lbl,bool add)137 void mglCanvas::SetTicksVal(char dir, const char *lbl, bool add)
138 {
139 	long i=0,len=strlen(lbl);
140 	for(long j=1;j<len;j++)
141 		if(lbl[j]=='\n' || (lbl[j]=='n' && lbl[j-1]=='\\'))	i++;
142 	if(i>63)	i=63;
143 	mglData val(i+1);	val.Fill(Min.x,Max.x);
144 	SetTicksVal(dir, &val, lbl, add);
145 }
146 //-----------------------------------------------------------------------------
SetTicksVal(char dir,HCDT v,const wchar_t ** lbl,bool add)147 void mglCanvas::SetTicksVal(char dir, HCDT v, const wchar_t **lbl, bool add)
148 {
149 	if(!strchr("xyzca",dir))	return;
150 	mglAxis &aa = GetAxis(dir);
151 	bool ff = GetFormula(dir);
152 
153 	if(add)	{	UpdateAxis();	AdjustTicks(aa,ff);	}
154 	else	aa.txt.clear();
155 	if(!v || !lbl)	{	aa.f = 0;	return;	}
156 	aa.f = 2;	aa.ns=0;	aa.ds=0;
157 	long n=v->GetNx();
158 	for(long i=0;i<n;i++)	aa.AddLabel(lbl[i],v->v(i));
159 }
160 //-----------------------------------------------------------------------------
SetTicksVal(char dir,HCDT v,const char ** lbl,bool add)161 void mglCanvas::SetTicksVal(char dir, HCDT v, const char **lbl, bool add)
162 {
163 	if(!strchr("xyzca",dir))	return;
164 	mglAxis &aa = GetAxis(dir);
165 	bool ff = GetFormula(dir);
166 
167 	aa.txt.clear();
168 	if(add)	{	UpdateAxis();	AdjustTicks(aa,ff);	}
169 	if(!v || !lbl)	{	aa.f = 0;	return;	}
170 	aa.f = 2;	aa.ns=0;	aa.ds=0;
171 	for(long i=0;i<v->GetNx();i++)	MGL_TO_WCS(lbl[i],aa.AddLabel(wcs,v->v(i)));
172 }
173 //-----------------------------------------------------------------------------
SetTickTempl(char dir,const wchar_t * t)174 void mglCanvas::SetTickTempl(char dir, const wchar_t *t)
175 {
176 	if(!strchr("xyzca",dir))	return;
177 	mglAxis &aa = GetAxis(dir);
178 
179 	if(aa.f==1)	aa.f = 0;	// remove time ticks
180 	if(!t || !t[0])	aa.t.clear();	else aa.t=t;
181 }
182 //-----------------------------------------------------------------------------
SetTickTempl(char dir,const char * t)183 void mglCanvas::SetTickTempl(char dir, const char *t)
184 {
185 	if(!strchr("xyzca",dir))	return;
186 	mglAxis &aa = GetAxis(dir);
187 
188 	if(aa.f==1)	aa.f = 0;	// remove time ticks
189 	if(!t || !t[0])	aa.t.clear();
190 	else	MGL_TO_WCS(t,aa.t=wcs);
191 }
192 //-----------------------------------------------------------------------------
mgl_adj_val(double v,mreal * ds=0)193 static double mgl_adj_val(double v,mreal *ds=0)
194 {
195 	double n = floor(log10(v)), s;
196 	v = floor(v*pow(10.,-n));	n = pow(10.,n);
197 
198 	if(v==1)	{	v = n/5;	s=n/10;	}
199 	else if(v<4){	v = n/2;	s=n/10;	}
200 	else if(v<7){	v = n;		s=n/5;	}
201 	else		{	v = 2*n;	s=n/2;	}
202 	if(ds)	*ds=s;
203 	return v;
204 }
205 //-----------------------------------------------------------------------------
SetTickTime(char dir,mreal d,const char * t)206 void mglCanvas::SetTickTime(char dir, mreal d, const char *t)
207 {
208 	if(!strchr("xyzca",dir))	return;
209 	mglAxis &aa = (dir=='x' ? ax : (dir=='y' ? ay : (dir=='z' ? az : ac)));
210 	UpdateAxis();
211 
212 	time_t tt;	tm t1,t2;
213 	tt=(time_t)aa.v1;	mgl_localtime(&tt, &t1, get(MGL_USE_GMTIME));
214 	tt=(time_t)aa.v2;	mgl_localtime(&tt, &t2, get(MGL_USE_GMTIME));
215 	if(aa.v1<aa.v2)	// adjust periodic values
216 	{
217 		if(abs(t1.tm_year-t2.tm_year)==1)	t2.tm_yday += 365;
218 		if(abs(t1.tm_yday-t2.tm_yday)==1)	t2.tm_hour += 24;
219 		if(abs(t1.tm_hour-t2.tm_hour)==1)	t2.tm_min += 60;
220 		if(abs(t1.tm_min-t2.tm_min)==1)		t2.tm_sec += 60;
221 	}
222 	else
223 	{
224 		if(abs(t1.tm_year-t2.tm_year)==1)	t1.tm_yday += 365;
225 		if(abs(t1.tm_yday-t2.tm_yday)==1)	t1.tm_hour += 24;
226 		if(abs(t1.tm_hour-t2.tm_hour)==1)	t1.tm_min += 60;
227 		if(abs(t1.tm_min-t2.tm_min)==1)		t1.tm_sec += 60;
228 	}
229 	if(!t || !t[0])		// adjust template
230 	{
231 		t = abs(t1.tm_yday-t2.tm_yday)>1 ? "%x" : "%X";
232 		if(abs(t1.tm_year-t2.tm_year)>3)	t = "%Y";
233 	}
234 	mreal ds=0;
235 	if(d==0)	// try to select optimal step
236 	{
237 		if(abs(t1.tm_year-t2.tm_year)>1)	// number of second in year NOTE: improve it
238 		{	d = 365.25*24*3600*mgl_adj_val(abs(t1.tm_year-t2.tm_year),&ds);
239 			ds *= 365.25*24*3600;	}
240 		// NOTE here should be months ... but it is too unregular ... so omit it now
241 // 		else if(t1.tm_mon!=t2.tm_mon)	d = 30*24*3600;	// number of second in month
242 		else if(abs(t1.tm_yday-t2.tm_yday)>=14)	// number of second in week
243 		{	d = mgl_adj_val(abs(t1.tm_yday-t2.tm_yday)/7,&ds);
244 			ds = ((ds<1)?1./7:ds)*7*24*3600;	d = ((d>=1)?d:1)*7*24*3600;	}
245 		else if(abs(t1.tm_yday-t2.tm_yday)>1)	// localtime("%x") cannot print time < 1 day
246 		{	d = 24*3600.*mgl_adj_val(abs(t1.tm_yday-t2.tm_yday),&ds);
247 			ds *= 24*3600;	if(d<24*3600)	{	d=24*3600;	ds=d/2;}	}
248 		else if(abs(t1.tm_hour-t2.tm_hour)>1)
249 		{	d = 3600.*mgl_adj_val(abs(t1.tm_hour-t2.tm_hour),&ds);	ds *=3600;	}
250 		else if(abs(t1.tm_min-t2.tm_min)>1)
251 		{	d = 60*mgl_adj_val(abs(t1.tm_min-t2.tm_min),&ds);	ds *=60;	}
252 		else if(abs(t1.tm_sec-t2.tm_sec)>1)	// localtime("%X") cannot print time < 1 sec
253 		{	d = mgl_adj_val(abs(t1.tm_sec-t2.tm_sec),&ds);
254 			if(d<1)	{	d=1;	ds=0.5;}	}
255 		else	// adjust msec. NOTE: this is not supported by localtime() !!!
256 			d = mgl_adj_val(fabs(aa.v2-aa.v1),&ds);
257 	}
258 
259 	aa.ds = ds;	aa.dv = d;	aa.f = 1;	aa.txt.clear();
260 	MGL_TO_WCS(t,aa.t=wcs);
261 
262 	if(strchr("xyztuvw",aa.ch))
263 		aa.org.Set(GetOrgX(aa.ch,aa.inv), GetOrgY(aa.ch,aa.inv), GetOrgZ(aa.ch,aa.inv));
264 	if(aa.ch=='x')	aa.v0 = aa.org.x;
265 	if(aa.ch=='y')	aa.v0 = aa.org.y;
266 	if(aa.ch=='z')	aa.v0 = aa.org.z;
267 
268 	mreal v, v0 = mgl_isnan(aa.o) ? aa.v0 : aa.o, v1;
269 	if(aa.v2>aa.v1)
270 	{	v1 = aa.v2;		v0 = v0 - aa.dv*floor((v0-aa.v1)/aa.dv+1e-3);	}
271 	else
272 	{	v1 = aa.v1;		v0 = v0 - aa.dv*floor((v0-aa.v2)/aa.dv+1e-3);	}
273 	if(v0+aa.dv!=v0 && v1+aa.dv!=v1)	for(v=v0;v<=v1;v+=aa.dv)
274 	{
275 		wchar_t buf[64];
276 		tt = (time_t)v;	tm tp;		mgl_localtime(&tt, &tp, get(MGL_USE_GMTIME));
277 		wcsftime(buf,64,aa.t.c_str(),&tp);	aa.AddLabel(buf,v);
278 	}
279 }
280 //-----------------------------------------------------------------------------
AdjustTicks(const char * dir,bool force,const std::string & stl)281 void mglCanvas::AdjustTicks(const char *dir, bool force, const std::string &stl)
282 {
283 	if(force)	SetTuneTicks(3);
284 	UpdateAxis();
285 	if(strchr(dir,'x') || strchr(dir,'X'))	// NOTE dir have to be non-NULL here !!!
286 	{	if(force)	ax.d=0;	ax.stl=stl;	AdjustTicks(ax,fx!=0);	}
287 	if(strchr(dir,'y') || strchr(dir,'Y'))
288 	{	if(force)	ay.d=0;	ay.stl=stl;	AdjustTicks(ay,fy!=0);	}
289 	if(strchr(dir,'z') || strchr(dir,'Z'))
290 	{	if(force)	az.d=0;	az.stl=stl;	AdjustTicks(az,fz!=0);	}
291 	if(strchr(dir,'a') || strchr(dir,'c'))
292 	{	if(force)	ac.d=0;	ac.stl=stl;	AdjustTicks(ac,fa!=0);	}
293 }
294 //-----------------------------------------------------------------------------
AdjustTicks(mglAxis & aa,bool ff)295 void mglCanvas::AdjustTicks(mglAxis &aa, bool ff)
296 {
297 	double d = fabs(aa.v2-aa.v1);
298 	if(aa.f>0)	return;
299 	if(ff && mgl_islog(aa.v1,aa.v2))
300 	{	aa.dv = 0;	aa.ds=0;	}
301 	else if(aa.d>0)
302 	{	aa.dv = aa.d;	aa.ds = aa.d/(abs(aa.ns)+1);	}
303 	else if(aa.d>-1.5)	// like =0 or =-1
304 	{	aa.dv = mgl_adj_val(d,&aa.ds);	aa.o=0;	}
305 	else
306 	{
307 		d /= -aa.d;
308 		long n = lrint(floor(log10(d)));
309 		aa.dv = pow(10.,n)*mgl_int(d*pow(10.,-n));
310 		aa.o=0;	aa.ds = pow(10.,n);
311 	}
312 	LabelTicks(aa);
313 }
314 //-----------------------------------------------------------------------------
mgl_tick_ext(mreal a,mreal b,wchar_t s[32],mreal & v)315 static int mgl_tick_ext(mreal a, mreal b, wchar_t s[32], mreal &v)
316 {
317 	int kind = 0;
318 	if(fabs(a-b)<=0.01*fabs(a))
319 	{
320 		kind = 1;
321 		v = fabs(a-b);
322 		if(v>1000.f)
323 		{
324 			int k=int(log10(v)-0.01);
325 			kind=3;		v=mgl_ipow(10.,k);
326 			mglprintf(s, 32, L" (@{\\times{}10^{%d}})", k);
327 		}
328 		else if(v<0.02f)
329 		{
330 			int k=int(log10(v)-0.01)-1;
331 			kind=3;		v=mgl_ipow(10.,k);
332 			mglprintf(s, 32, L" (@{\\times{}10^{%d}})", k);
333 		}
334 	}
335 	else
336 	{
337 		v = fabs(b)>fabs(a)?fabs(b):fabs(a);
338 		if(v>=1000.f)
339 		{
340 			kind = 2;
341 			int k=int(log10(v)-0.01);
342 			v=mgl_ipow(10.,k);
343 			mglprintf(s, 32, L" \\times 10^{%d}", k);
344 		}
345 		else if(v<=1e-3f)
346 		{
347 			kind = 2;
348 			int k=int(log10(v)-0.01)-1;
349 			v=mgl_ipow(10.,k);
350 			mglprintf(s, 32, L" \\times 10^{%d}", k);
351 		}
352 	}
353 	return kind;
354 }
355 //-----------------------------------------------------------------------------
356 /*static std::wstring mgl_format(mreal v1, mreal v2, bool zero)
357 {
358 	v1=fabs(v1);	v2=fabs(v2);	if(v1>v2)	v2=v1;
359 	wchar_t str[5]=L"%.3g";
360 	int prec=int(2-log10(v2));	if(prec<0)	prec=0;
361 	if(v2<=0.001 || v2>=10000)
362 	{	str[2] = '2';	str[3]='e';	}
363 	else if(zero)
364 	{	str[2] = '0'+prec;	str[3]='f';	}
365 	return str;
366 }*/
367 //-----------------------------------------------------------------------------
mgl_tick_text(mreal z,mreal z0,mreal d,mreal v,int kind,const std::wstring & fact,mreal step,const char * stl)368 static std::wstring mgl_tick_text(mreal z, mreal z0, mreal d, mreal v, int kind, const std::wstring &fact, mreal step, const char *stl)
369 {
370 	std::wstring str;
371 	bool ff = step>0 && !fact.empty();// && mgl_wcslen(str)+fact.length()<62;
372 	if(ff)	z/=step;
373 	mreal u = fabs(z)<d ? 0:z;
374 	if((kind&1) && z>z0)	u = fabs(z-z0)<d ? 0:(z-z0);
375 	if(kind==2 || (kind==3 && z>z0))	u /= v;
376 	str = mgl_ftoa(u,stl?stl:"");
377 	if((kind&1) && z>z0)	str = L"@{(+"+str+L")}";
378 //	if(kind&1)	fmt = z>z0?L"@{(+"+fmt+L")}":L"%g";
379 //	mglprintf(str,64,fmt.c_str(), u);
380 	if(ff)
381 	{
382 		if(str==L"-1" || str==L"+1" || str==L"\u22121")	str = str[0] + fact;
383 		else if(str==L"1")	str = fact;
384 		else if(str!=L"0")	str += fact;
385 	}
386 	return str;
387 }
388 //-----------------------------------------------------------------------------
LabelTicks(mglAxis & aa)389 void mglCanvas::LabelTicks(mglAxis &aa)
390 {
391 	if(strchr("xyztuvw",aa.ch))
392 		aa.org.Set(GetOrgX(aa.ch,aa.inv), GetOrgY(aa.ch,aa.inv), GetOrgZ(aa.ch,aa.inv));
393 	if(aa.ch=='x')	aa.v0 = aa.org.x;
394 	if(aa.ch=='y')	aa.v0 = aa.org.y;
395 	if(aa.ch=='z')	aa.v0 = aa.org.z;
396 
397 	wchar_t buf[64]=L"";
398 	if(aa.f)	return;
399 	aa.txt.clear();
400 	bool minus = mglchr(aa.stl.c_str(),'-') && !mglchr(aa.stl.c_str(),'+');
401 	if(aa.dv==0 && aa.v1>0)	// positive log-scale
402 	{
403 		mreal v1 = aa.v1, v2 = aa.v2;
404 		if(v1>v2)	{	v1 = aa.v2; v2 = aa.v1;	}
405 		mreal v0 = exp(M_LN10*floor(0.1+log10(v1)));
406 		int ds = int(floor(0.1+log10(v2/v0))/7)+1;
407 		for(mreal v=v0;v<=v2*MGL_EPSILON;v*=10)	if(v*MGL_EPSILON>=v1)
408 		{
409 			int d = int(floor(0.1+log10(v)));
410 			if(d==0)	wcscpy(buf,L"1");
411 			else if(d==1)	wcscpy(buf,L"10");
412 			else if(d>0)	mglprintf(buf,64,L"10^{%d}",d);
413 			else	mglprintf(buf,64,minus?L"10^{-%d}":L"10^{\u2212%d}",-d);
414 			if(d%ds!=0)	*buf=0;	//	remove too often log ticks
415 			aa.AddLabel(buf,v);
416 		}
417 	}
418 	else if(aa.dv==0 && aa.v2<0)	// negative log-scale
419 	{
420 		mreal v1 = aa.v1, v2 = aa.v2;
421 		if(v1>v2)	{	v1 = aa.v2; v2 = aa.v1;	}
422 		mreal v0 = -exp(M_LN10*floor(0.1+log10(-v2)));
423 		int ds = int(floor(0.1+log10(v1/v0))/7)+1;
424 		for(mreal v=v0;v>=v1*MGL_EPSILON;v*=10)	if(v*MGL_EPSILON<=v2)
425 		{
426 			int d = int(floor(0.1+log10(-v)));
427 			if(d==0)	wcscpy(buf,minus?L"-1":L"\u22121");
428 			else if(d==1)	wcscpy(buf,minus?L"-10":L"\u221210");
429 			else if(d>0)	mglprintf(buf,64,minus?L"-10^{%d}":L"\u221210^{%d}",d);
430 			else	mglprintf(buf,64,minus?L"-10^{-%d}":L"\u221210^{\u2212%d}",-d);
431 			if(d%ds!=0)	*buf=0;	//	remove too often log ticks
432 			aa.AddLabel(buf,v);
433 		}
434 	}
435 	else if(aa.dv)	// ticks drawing
436 	{
437 		mreal w=0;
438 		int kind=0;
439 		wchar_t s[32]=L"";
440 		if(aa.t.empty() && TuneTicks && !strchr(aa.stl.c_str(),'!'))
441 			kind = mgl_tick_ext(aa.v2, aa.v1, s, w);
442 		if(((TuneTicks&1)==0 && kind==2) || ((TuneTicks&2)==0 && kind!=2))
443 			kind=0;
444 
445 		mreal v0 = mgl_isnan(aa.o) ? aa.v0 : aa.o, v1;
446 		if(mgl_isnan(v0))	v0=0;
447 		if(aa.v2>aa.v1)
448 		{	v1 = aa.v2;		v0 = v0 - aa.dv*floor((v0-aa.v1)/aa.dv+1e-3);	}
449 		else
450 		{	v1 = aa.v1;		v0 = v0 - aa.dv*floor((v0-aa.v2)/aa.dv+1e-3);	}
451 
452 		if(v0+aa.dv!=v0 && v1+aa.dv!=v1)
453 		{
454 			if(aa.t.empty())	for(mreal v=v0;v<=v1;v+=aa.dv)
455 				aa.AddLabel(mgl_tick_text(v,v0,aa.dv/100,w,kind,aa.fact,aa.d,aa.stl.c_str()),v);
456 			else	for(mreal v=v0;v<=v1;v+=aa.dv)
457 			{
458 				if(aa.t[0]!='&')	mglprintf(buf, 64, aa.t.c_str(), fabs(v)<aa.dv/100 ? 0 : v);
459 				else	mglprintf(buf, 64, aa.t.c_str()+1, mgl_int(fabs(v)<aa.dv/100 ? 0 : v));
460 				mgl_wcstrim(buf);	aa.AddLabel(buf,v);
461 			}
462 		}
463 		if(kind&2)	aa.AddLabel(s,FactorPos*(aa.v2-aa.v1)+aa.v1);
464 	}
465 }
466 //-----------------------------------------------------------------------------
Axis(const char * dir,const char * stl,const char * opt)467 void mglCanvas::Axis(const char *dir, const char *stl, const char *opt)
468 {
469 	int text = !(mglchr(dir,'_') || mglchr(dir,'~'))?1:0;
470 	if(mglchr(dir,':'))	text = text|2;
471 	bool inv = mglchr(dir,'^');
472 	bool ret = get(MGL_ENABLE_RTEXT);
473 	if(mglchr(dir,'U'))	clr(MGL_ENABLE_RTEXT);
474 
475 	std::string Tstl;
476 	for(const char *s="+E0123456789-fF!";*s;s++)	if(mglchr(dir,*s))	Tstl += *s;
477 
478 	const char *ar = "AKDTVISO";
479 	char arr=0;
480 	for(size_t i=0;i<strlen(ar);i++)
481 		if(strchr(dir,ar[i]))	{	arr=ar[i];	break;	}
482 	if(!mglchrs(dir,"xXyYzZ"))	dir="xyz";
483 
484 	mreal angl = SaveState(opt);
485 	AdjustTicks(dir,mglchr(stl,'a'),Tstl);
486 
487 	ax.pos = strchr(dir,'X') ? 'T':'t';
488 	ay.pos = strchr(dir,'Y') ? 'T':'t';
489 	az.pos = strchr(dir,'Z') ? 'T':'t';
490 	ax.inv = ay.inv = az.inv = false;
491 
492 	if(strchr(dir,'X') || strchr(dir,'x'))
493 	{	ax.inv = inv;	DrawAxis(ax, text, arr, stl, angl);	}
494 	if(strchr(dir,'Z') || strchr(dir,'z'))
495 	{	az.inv = inv;	DrawAxis(az, text, arr, stl, angl);	}
496 	if((TernAxis&3))
497 	{
498 		mglAxis ty(ay);		ty.pos='t';	ty.ch='T';
499 		ty.dir.Set(-1,1);		ty.org.Set(1,0,ay.org.z);
500 		DrawAxis(ty, text, arr, stl, angl);	ty.ch='t';
501 		ty.dir.Set(0,-1);		ty.org.Set(0,1,ay.org.z);
502 		DrawAxis(ty, text, arr, stl, angl);
503 	}
504 	else if(strchr(dir,'Y') || strchr(dir,'y'))
505 	{	ay.inv = inv;	DrawAxis(ay, text, arr, stl, angl);	}
506 	set(ret, MGL_ENABLE_RTEXT);
507 	LoadState();
508 }
509 //-----------------------------------------------------------------------------
DrawAxis(mglAxis & aa,int text,char arr,const char * stl,mreal angl)510 void mglCanvas::DrawAxis(mglAxis &aa, int text, char arr,const char *stl,mreal angl)
511 {
512 	aa.angl = angl;
513 	if(strchr("xyz",aa.ch))
514 		aa.org.Set(GetOrgX(aa.ch,aa.inv), GetOrgY(aa.ch,aa.inv), GetOrgZ(aa.ch,aa.inv));
515 	if(aa.ch=='x')	aa.v0 = aa.org.x;
516 	if(aa.ch=='y')	aa.v0 = aa.org.y;
517 	if(aa.ch=='z')	aa.v0 = aa.org.z;
518 
519 	mglPoint d(aa.dir), o(aa.org), q(NAN);	// "transverse" org
520 	if(strchr("xyz",aa.ch))	o -= d*(o*d);
521 	mglPoint av=(Min+Max)/2, dv,da,db, p;
522 	dv.Set(mgl_sign(av.x-o.x), mgl_sign(av.y-o.y), mgl_sign(av.z-o.z));
523 	da = aa.a*(dv*aa.a);	db = aa.b*(dv*aa.b);
524 
525 	static int cgid=1;	StartGroup("Axis",cgid++);
526 
527 	bool have_color=mgl_have_color(stl);
528 	bool dif_color = !have_color && aa.dv==0 && strcmp(TickStl,SubTStl);
529 	long kq = AllocPnts(31);
530 	if(text&2)	// line throw point (0,0,0)
531 	{
532 		SetPenPal("k:");
533 #pragma omp parallel for
534 		for(long i=0;i<31;i++)
535 			AddPntQ(kq+i, &B, d*(aa.v1+(aa.v2-aa.v1)*i/30.), CDef,q,-1,3);
536 		curve_plot(31,kq);
537 	}
538 	SetPenPal(have_color ? stl:AxisStl);
539 
540 	kq = AllocPnts(31);
541 #pragma omp parallel for
542 	for(long i=0;i<31;i++)
543 		AddPntQ(kq+i, &B, o + d*(aa.v1+(aa.v2-aa.v1)*i/30.), CDef,q,-1,3);
544 	curve_plot(31,kq);
545 	if(arr)
546 	{
547 		p = o + d*(aa.v1+(aa.v2-aa.v1)*1.05);
548 		long k1 = AddPnt(&B, p,CDef,q,-1,3);
549 		line_plot(k1,kq+30);	arrow_plot(k1,kq+30,arr);
550 	}
551 
552 	long k2 = aa.txt.size();
553 	mreal v, u, v0 = mgl_isnan(aa.o) ? aa.v0 : aa.o;
554 	if(*TickStl && !have_color)	SetPenPal(TickStl);
555 	if(k2>0)	for(long i=0;i<k2;i++)
556 	{
557 		v = aa.txt[i].val;	u = fabs(v);
558 		if((v-aa.v2)*(v-aa.v1)<=0)	tick_draw(o+d*v, da, db, 0);
559 		if(dif_color)	SetPenPal(SubTStl);
560 		if(aa.dv==0 && aa.v2>aa.v1 && fabs(u-exp(M_LN10*floor(0.1+log10(u))))<0.01*u)
561 			for(long j=2;j<10 && v*j<aa.v2;j++)	tick_draw(o+d*(v*j),da,db,1);
562 		if(aa.dv==0 && aa.v2<aa.v1 && fabs(u-exp(M_LN10*floor(0.1+log10(u))))<0.01*u)
563 			for(long j=2;j<10 && v*j<aa.v1;j++)	tick_draw(o+d*(v*j),da,db,1);
564 		if(dif_color)	SetPenPal(TickStl);
565 	}
566 	if(aa.ds>0 && !get(MGL_NOSUBTICKS) && (fabs(aa.v1)>1e-150 || fabs(aa.v2)>1e-150))
567 	{
568 		if(aa.v2>aa.v1)	v0 = v0 - aa.ds*floor((v0-aa.v1)/aa.ds+1e-3);
569 		else			v0 = v0 - aa.ds*floor((v0-aa.v2)/aa.ds+1e-3);
570 		if(v0+aa.ds!=v0 && aa.v2+aa.ds!=aa.v2)
571 		{
572 			if(*SubTStl && !have_color)	SetPenPal(SubTStl);
573 			for(v=v0;(v-aa.v2)*(v-aa.v1)<=0;v+=aa.ds)
574 				tick_draw(o+d*v,da,db,1);
575 		}
576 	}
577 	if(!have_color)	SetPenPal(AxisStl);
578 	if(text&1)	DrawLabels(aa);
579 	EndGroup();
580 }
581 //-----------------------------------------------------------------------------
DrawLabels(mglAxis & aa,bool inv,const mglMatrix * M)582 void mglCanvas::DrawLabels(mglAxis &aa, bool inv, const mglMatrix *M)
583 {
584 	if(M==0)	M=&B;
585 	if(strchr("xyz",aa.ch))
586 		aa.org.Set(GetOrgX(aa.ch,aa.inv), GetOrgY(aa.ch,aa.inv), GetOrgZ(aa.ch,aa.inv));
587 	if(aa.ch=='x')	aa.v0 = aa.org.x;
588 	if(aa.ch=='y')	aa.v0 = aa.org.y;
589 	if(aa.ch=='z')	aa.v0 = aa.org.z;
590 
591 	mglPoint d(aa.dir), o(aa.org), q(NAN);	// "transverse" org
592 	if(strchr("xyz",aa.ch))	o -= d*(o*d);
593 	mglPoint s=(Min+Max)/2, dv(mgl_sign(s.x-o.x), mgl_sign(s.y-o.y), mgl_sign(s.z-o.z));
594 	mglPoint a = aa.a*(dv*aa.a) + aa.b*(dv*aa.b);
595 	if(aa.ch=='c')	a = aa.a;
596 
597 	long n = aa.txt.size();
598 	mreal *w=new mreal[n], wsp = 2*TextWidth(" ",FontDef,-1);
599 	long *kk=new long[n], kq = AllocPnts(n);
600 #pragma omp parallel for
601 	for(long i=0;i<n;i++)	// fill base label properties
602 	{
603 		w[i] = TextWidth(aa.txt[i].text.c_str(),FontDef,-1);
604 		AddPntQ(kq+i, M, o+d*aa.txt[i].val,-1,d,0,7);
605 	}
606 	for(long i=0;i<n;i++)	kk[i] = kq+i;
607 	mreal c=INFINITY, l=0, h = TextHeight(FontDef,-1);	// find sizes
608 	for(long i=0;i<n-1;i++)
609 	{
610 		// exclude factors
611 		if(aa.ch!='c' && (aa.txt[i].val<aa.v1 || aa.txt[i+1].val<aa.v1 || aa.txt[i].val>aa.v2 || aa.txt[i+1].val>aa.v2))
612 			continue;
613 		if(kk[i]<0 || kk[i+1]<0)	continue;
614 		mreal v = (GetPntP(kk[i+1])-GetPntP(kk[i])).norm();	// distance between ticks
615 		mreal vv = (w[i]+w[i+1])/2-wsp;	// length of labels
616 		if(v>0 && l < vv/v)	l = vv/v;
617 		if(c>v)	c = v;
618 	}
619 	h /= c;
620 
621 	mreal tet=0;
622 	if(mgl_isnum(aa.angl))	tet = aa.angl*M_PI/180;	// manual rotation
623 	else if(get(MGL_ENABLE_RTEXT) && get(MGL_TICKS_ROTATE) && l>1)	// try rotate first
624 	{
625 		mreal t1 = 1.1*h<1 ? asin(1.1*h) : M_PI/2;
626 		mreal r2 = l*l+h*h;
627 		mreal t2 = r2*1.21>1 ? asin((l*sqrt(r2-1/1.21)+h/1.1)/r2):M_PI/2;
628 		tet = t1<t2 ? t1:t2;
629 	}
630 	mreal sn = sin(tet), cs=cos(tet);
631 	if(sn)
632 	{
633 		mreal l1=h/fabs(sn), l2=fabs(l*cs+h*sn);
634 		l = l2>l1?l1:l2;
635 	}
636 	char *align=new char[n], *up=new char[n];
637 	for(long i=0;i<n;i++)	if(kk[i]>=0)	// select proper align
638 	{
639 		mglPoint p(a),r(o+d*aa.txt[i].val);
640 		ScalePoint(M, r, p, false);
641 		mglPnt &pp = Pnt[kk[i]];
642 		mreal ux=pp.u*cs + pp.v*sn, uy=pp.v*cs - pp.u*sn;
643 		bool Nrot = !get(MGL_ENABLE_RTEXT) || !get(MGL_TICKS_ROTATE);
644 		bool Vcnt = ux==0 && uy!=0 && Nrot;
645 		bool algn = tet!=0;		// TODO add proper align for arbitrary tet!
646 		if(ux*ux+uy*uy!=0 && Nrot)	{	ux=1;	uy=0;	algn=true;	}
647 		if(ux<0 || (ux==0 && uy<0))	{	ux=-ux;	uy=-uy;	pp.w=-pp.w;	}
648 		pp.u = ux;	pp.v = uy;
649 		mreal pu = p.x*ux+p.y*uy, pv = p.y*ux-p.x*uy; /*, su = ps.x*ux+ps.y*uy;*/
650 		if(Vcnt)	up[i]='V';
651 		else if(aa.ch!='c')	up[i] = ((pv>0) ^ inv) ? 'T':'t';
652 		else		up[i]=(aa.ns==0 || aa.ns==3)?'t':'T';
653 		int t=0;
654 		if(algn)
655 		{
656 			if(aa.ch!='c')	t= (pu==0)?0:(pu<0? -1:1);
657 			else	t=inv?-1:1;
658 		}
659 		char val[3]={'L','C','R'};	align[i] = val[t+1];
660 	}
661 	long k = get(MGL_TICKS_SKIP) ? 1+l : 1;
662 
663 	for(long i=0;i<n;i++)	if(kk[i]>=0)
664 	{
665 		mreal v = aa.txt[i].val;
666 		if(get(MGL_NO_ORIGIN) && v==aa.v0)	continue;
667 		if(v>aa.v1 && v<aa.v2 && i%k!=0)	continue;
668 		char pos[4]={up[i],':',align[i],0};
669 		text_plot(kk[i], aa.txt[i].text.c_str(), pos, -1, aa.sh+0.05,CDef);
670 	}
671 	delete []w;	delete []kk;	delete []align;	delete []up;
672 }
673 //-----------------------------------------------------------------------------
GetLabelPos(mreal c,long kk,mglAxis & aa)674 char mglCanvas::GetLabelPos(mreal c, long kk, mglAxis &aa)
675 {
676 	if(strchr("xyz",aa.ch))
677 		aa.org.Set(GetOrgX(aa.ch,aa.inv), GetOrgY(aa.ch,aa.inv), GetOrgZ(aa.ch,aa.inv));
678 	mglPoint d = aa.dir, o = aa.org;	// "transverse" org
679 	if(strchr("xyz",aa.ch))	o -= d*(o*d);
680 	mglPoint p,q, s=(Min+Max)/2, nn;
681 	s = s - d*(s*d);
682 
683 	int ts = 1;
684 	if(aa.ch=='c')	ts=(aa.ns==0 || aa.ns==3)?1:-1;
685 	if(aa.ch=='T')	ts=-1;
686 
687 	p = o+d*c;	nn = s-o;	ScalePoint(&B,p,nn);
688 	mglPnt &qq = Pnt[kk];
689 
690 	if(aa.ch=='c')	qq.u = qq.v = NAN;
691 	if(!get(MGL_DISABLE_SCALE))	ts = mgl_sign(qq.v*nn.x-qq.u*nn.y);
692 	if(aa.ch=='T')	ts *= -1;
693 	if(aa.pos=='T')	ts *= -1;
694 	return ts>0 ? 't':'T';
695 }
696 //-----------------------------------------------------------------------------
tick_draw(mglPoint o,mglPoint d1,mglPoint d2,int f)697 void mglCanvas::tick_draw(mglPoint o, mglPoint d1, mglPoint d2, int f)
698 {
699 	if(TickLen==0)	return;
700 	// try to exclude ticks out of axis range
701 	if(f && ((o.x-Min.x)*(o.x-Max.x)>0 || (o.y-Min.y)*(o.y-Max.y)>0 || (o.z-Min.z)*(o.z-Max.z)>0))
702 		return;
703 	mreal v = font_factor*TickLen/sqrt(1.f+f*st_t);
704 	mglPoint p=o;
705 
706 	ScalePoint(&B,o, d1, false);	d1.Normalize();
707 	ScalePoint(&B,p, d2, false);	d2.Normalize();
708 	long k1 = AddPnt(&B, p+d1*v, CDef, mglPoint(NAN), 0, 0);
709 	long k2 = AddPnt(&B, p, CDef, mglPoint(NAN), 0, 0);
710 	long k3 = AddPnt(&B, p+d2*v, CDef, mglPoint(NAN), 0, 0);
711 	line_plot(k1,k2);	line_plot(k2,k3);
712 }
713 //-----------------------------------------------------------------------------
Grid(const char * dir,const char * pen,const char * opt)714 void mglCanvas::Grid(const char *dir, const char *pen, const char *opt)
715 {
716 	SaveState(opt);
717 	bool at_tick=mglchr(dir,'!');
718 	if(!mglchrs(dir,"xyz"))	dir="xyz";
719 	AdjustTicks(dir,false);
720 	SetPenPal(pen);
721 
722 	static int cgid=1;	StartGroup("AxisGrid",cgid++);
723 	if(strchr(dir,'x'))	DrawGrid(ax,at_tick);
724 	if(strchr(dir,'y'))	DrawGrid(ay,at_tick);
725 	if(strchr(dir,'z'))	DrawGrid(az,at_tick);
726 	EndGroup();
727 }
728 //-----------------------------------------------------------------------------
mgl_drw_grid(HMGL gr,double val,const mglPoint & d,const mglPoint & oa,const mglPoint & ob,const mglPoint & da1,const mglPoint & db1,const mglPoint & da2,const mglPoint & db2)729 static void mgl_drw_grid(HMGL gr, double val, const mglPoint &d, const mglPoint &oa, const mglPoint &ob, const mglPoint &da1, const mglPoint &db1, const mglPoint &da2, const mglPoint &db2)
730 {
731 	mglPoint q(oa+d*val);	// lines along 'a'
732 	long kq = gr->AllocPnts(31);
733 #pragma omp parallel for
734 	for(long i=0;i<31;i++)
735 	{	mreal v=i/30.;	gr->AddPntQ(kq+i,q+da1*(1-v)+da2*v);	}
736 	gr->curve_plot(31,kq);
737 	q = ob+d*val;		// lines along 'b'
738 	kq = gr->AllocPnts(31);
739 #pragma omp parallel for
740 	for(long i=0;i<31;i++)
741 	{	mreal v = i/30.;	gr->AddPntQ(kq+i,q+db1*(1-v)+db2*v);	}
742 	gr->curve_plot(31,kq);
743 }
DrawGrid(mglAxis & aa,bool at_tick)744 void mglCanvas::DrawGrid(mglAxis &aa, bool at_tick)
745 {
746 	mglPoint pp[8]={Min,Min,Min,Min,Max,Max,Max,Max},nan(NAN), oo[8], org=Min;
747 	pp[1].x=Max.x;	pp[2].y=Max.y;	pp[3].z=Max.z;
748 	pp[4].x=Min.x;	pp[5].y=Min.y;	pp[6].z=Min.z;
749 	mreal zm=INFINITY;
750 	memcpy(oo,pp,8*sizeof(mglPoint));
751 	for(int i=0;i<8;i++)	// find deepest point
752 	{
753 		ScalePoint(&B,pp[i],nan,false);
754 		if(pp[i].z<zm)	{	zm=pp[i].z;	org=oo[i];	}
755 	}
756 	if(mgl_isnum(Org.x)) 	org.x = Org.x;
757 	if(mgl_isnum(Org.y)) 	org.y = Org.y;
758 	if(mgl_isnum(Org.z)) 	org.z = Org.z;
759 	mglPoint d=aa.dir, da1,da2,db1,db2,oa,ob, p,q;
760 	da1 = aa.a*(aa.a*Min);	da2 = aa.a*(aa.a*Max);
761 	db1 = aa.b*(aa.b*Min);	db2 = aa.b*(aa.b*Max);
762 	oa  = aa.b*(aa.b*org);	ob  = aa.a*(aa.a*org);
763 
764 	if(at_tick && aa.ds>0 && !get(MGL_NOSUBTICKS))
765 	{
766 		mreal v0 = mgl_isnan(aa.o) ? aa.v0 : aa.o;
767 		if(aa.v2>aa.v1)	v0 = v0 - aa.ds*floor((v0-aa.v1)/aa.ds+1e-3);
768 		else			v0 = v0 - aa.ds*floor((v0-aa.v2)/aa.ds+1e-3);
769 		fflush(stdout);		// somehow this help to bypass bug in GCC 32bit
770 		if(v0+aa.ds!=v0 && aa.v2+aa.ds!=aa.v2)
771 			for(mreal v=v0;(v-aa.v2)*(v-aa.v1)<=0;v+=aa.ds)
772 				mgl_drw_grid(this, v, d, oa, ob, da1, db1, da2, db2);
773 	}
774 	if(aa.dv)	at_tick = false;
775 	long n=aa.txt.size();
776 	if(n>0)	for(long i=0;i<n;i++)
777 	{
778 		mreal v = aa.txt[i].val;
779 		mgl_drw_grid(this, v, d, oa, ob, da1, db1, da2, db2);
780 		if(at_tick)
781 		{
782 			mreal u = fabs(v);
783 			if(aa.v2>aa.v1 && fabs(u-exp(M_LN10*floor(0.1+log10(u))))<0.01*u)
784 				for(long j=2;j<10 && v*j<aa.v2;j++)
785 					mgl_drw_grid(this, v*j, d, oa, ob, da1, db1, da2, db2);
786 			if(aa.v2<aa.v1 && fabs(u-exp(M_LN10*floor(0.1+log10(u))))<0.01*u)
787 				for(long j=2;j<10 && v*j<aa.v1;j++)
788 					mgl_drw_grid(this, v*j, d, oa, ob, da1, db1, da2, db2);
789 		}
790 	}
791 }
792 //-----------------------------------------------------------------------------
Label(char dir,const char * str,mreal pos,const char * opt)793 void mglCanvas::Label(char dir, const char *str, mreal pos, const char *opt)
794 {
795 	MGL_TO_WCS(str,Labelw(dir, wcs, pos, opt));
796 }
797 //-----------------------------------------------------------------------------
Labelw(char dir,const wchar_t * text,mreal pos,const char * opt)798 void mglCanvas::Labelw(char dir, const wchar_t *text, mreal pos, const char *opt)
799 {
800 	mreal shift =  SaveState(opt), t=0;	if(mgl_isnan(shift))	shift=0;
801 	shift -= 0.1;
802 	mglPoint p,q;
803 	mglAxis *aa=0;
804 
805 	mglAxis ty(ay);
806 
807 	if(dir=='c')
808 	{
809 		AdjustTicks(ac,fc!=0);	aa = &ac;	// TODO
810 		p = ac.org+(ac.a.y?1.15:1)*ac.dir;	q = ac.dir;
811 		pos = ac.a.x>0?1:-1;	shift += ac.sh;
812 	}
813 	if(dir=='x')
814 	{
815 		AdjustTicks(ax,fx!=0);	aa = &ax;
816 		if(ax.dv)	t = (Min.x+Max.x+pos*(Max.x-Min.x))/2;
817 		else	t = Min.x*pow(Max.x/Min.x, (pos+1)/2);
818 		p.Set(t, GetOrgY(ax.ch,ax.inv), GetOrgZ(ax.ch,ax.inv));
819 		q.Set(1,0,0);	shift += ax.sh;
820 	}
821 	if(dir=='y' && !(TernAxis&3))
822 	{
823 		AdjustTicks(ay,fy!=0);	aa = &ay;
824 		if(ay.dv)	t = (Min.y+Max.y+pos*(Max.y-Min.y))/2;
825 		else	t = Min.y*pow(Max.y/Min.y, (pos+1)/2);
826 		p.Set(GetOrgX(ay.ch,ay.inv), t, GetOrgZ(ay.ch,ay.inv));
827 		q.Set(0,1,0);	shift += ay.sh;
828 		if(TernAxis&3)
829 		{
830 			q.Set(-1,1,0);	pos=-pos;
831 		}
832 	}
833 	if(dir=='y' && (TernAxis&3))
834 	{
835 		ty.ch='T';	ty.dir.Set(-1,1);	ty.org.Set(1,0,ay.org.z);
836 		AdjustTicks(ty,fy!=0);	aa = &ty;
837 		if(ty.dv)	t = (Min.y+Max.y+pos*(Max.y-Min.y))/2;
838 		else	t = Min.y*pow(Max.y/Min.y, (pos+1)/2);
839 		p.Set(GetOrgX(ty.ch,ty.inv), t, GetOrgZ(ty.ch,ty.inv));
840 		q.Set(0,1,0);	shift += ty.sh;
841 		if(TernAxis&3)
842 		{
843 			q.Set(-1,1,0);	pos=-pos;
844 		}
845 	}
846 	if(dir=='t' && (TernAxis&3))
847 	{
848 		ty.ch='t';	ty.dir.Set(0,-1);	ty.org.Set(0,1,ay.org.z);
849 		AdjustTicks(ty,fy!=0);	pos = -pos;	aa = &ty;
850 		if(ty.dv)	t = (Min.y+Max.y+pos*(Max.y-Min.y))/2;
851 		else	t = Min.y*pow(Max.y/Min.y, (pos+1)/2);
852 		p.Set(GetOrgX(ty.ch,ty.inv), t, GetOrgZ(ty.ch,ty.inv));
853 		q.Set(0,1,0);	shift += ty.sh;
854 	}
855 	if(dir=='z')
856 	{
857 		AdjustTicks(az,fz!=0);	aa = &az;
858 		if(az.dv)	t = (Min.z+Max.z+pos*(Max.z-Min.z))/2;
859 		else	t = Min.z*pow(Max.z/Min.z, (pos+1)/2);
860 		p.Set(GetOrgX(az.ch,az.inv), GetOrgY(az.ch,az.inv), t);
861 		q.Set(0,0,1);	shift += az.sh;
862 	}
863 	if(aa)
864 	{
865 		char font[64],ff[3]=":C";	memset(font,0,64);
866 		if(pos<-0.2)	ff[1]='L';
867 		if(pos>0.2)	ff[1]='R';
868 		mgl_strncpy(font,FontDef,32);	strcat(font,ff);
869 		long kk = AddPnt(&B, p,-1,q,0,7);	ff[1]=0;
870 		if(kk>=0)
871 		{
872 			mglPnt &pp = Pnt[kk];
873 			if(pp.u<0 || (pp.u==0 && pp.v<0))
874 			{	pp.u=-pp.u;	pp.v=-pp.v;	pp.w=-pp.w;	}
875 			if(dir=='c' &&  ac.a.y!=0)
876 			{
877 				ff[0] = ac.a.y>0?'T':'t';	strcat(font,ff);
878 				text_plot(kk,text,font,-1.4,ac.a.y>0?shift:0);
879 			}
880 			else
881 			{
882 				ff[0] = GetLabelPos(t, kk, *aa);	strcat(font,ff);
883 				text_plot(kk,text,font,-1.4,(ff[0]=='T'?0.3:0.35)+shift);
884 			}
885 		}
886 	}
887 	LoadState();
888 }
889 //-----------------------------------------------------------------------------
Box(const char * col,bool ticks)890 void mglCanvas::Box(const char *col, bool ticks)
891 {
892 	mglPoint o = Org;
893 	mreal tl=TickLen;
894 	if(!ticks)	TickLen=0;
895 	set(MGL_NOSUBTICKS);	Org = Min;
896 	static int cgid=1;	StartGroup("Box",cgid++);
897 	Axis("xyz_",col);
898 	if(TernAxis&1)
899 	{
900 		Org.x=Max.x;	Org.y=Min.y;	Org.z=Max.z;
901 		DrawAxis(ax, 0, 0,col);	DrawAxis(az, 0, 0,col);
902 		Org.x=Min.x;	Org.y=Max.y;	Org.z=Max.z;
903 		DrawAxis(az, 0, 0,col);
904 
905 		mglAxis ty(ay);				ty.ch='T';
906 		ty.dir.Set(-1,1);	ty.org.Set(1,0,Max.z);
907 		DrawAxis(ty, 0, 0,col);	ty.ch='t';
908 		ty.dir.Set(0,-1);	ty.org.Set(0,1,Max.z);
909 		DrawAxis(ty, 0, 0,col);
910 	}
911 	else if(TernAxis&2)
912 	{
913 		mglAxis ty(az);
914 		ty.ch='T';	ty.a.Set(1,0);	ty.b.Set(-1,1);
915 		ty.dir.Set(-1,0,1);	ty.org.Set(1,0,0);
916 		DrawAxis(ty, 0, 0,col);
917 		ty.ch='t';	ty.a.Set(0,1);	ty.b.Set(-1,1);
918 		ty.dir.Set(0,-1,1);	ty.org.Set(0,1,0);
919 		DrawAxis(ty, 0, 0,col);
920 	}
921 	else
922 	{
923 		Org.z=Max.z;	Axis("xy_",col);
924 		Org = Max;		Axis("xyz_",col);
925 		Org.z=Min.z;	Axis("xy_",col);
926 		Org.x=Min.x;	DrawAxis(az,0,0,col);
927 		Org.x=Max.x;	Org.y=Min.y;	DrawAxis(az,0,0,col);
928 		if(mglchr(col,'@'))
929 		{
930 			// edge points
931 			mglPoint p[8]={Min,Min,Min,Min,Max,Max,Max,Max},nan(NAN),oo[8];
932 			p[1].x=Max.x;	p[2].y=Max.y;	p[3].z=Max.z;
933 			p[4].x=Min.x;	p[5].y=Min.y;	p[6].z=Min.z;
934 			mreal zm=INFINITY;	int im=0;
935 			memcpy(oo,p,8*sizeof(mglPoint));
936 			for(int i=0;i<8;i++)	// find deepest point
937 			{
938 				ScalePoint(&B,p[i],nan,false);
939 				if(p[i].z<zm)	{	zm=p[i].z;	im=i;	}
940 			}
941 			// now draw faces
942 			char color[10]="{y9}";
943 			for(int i=0;col[i];i++)
944 			{
945 				if(col[i]=='{' && col[i+1]=='x')
946 				{	memcpy(color,col+i,9);	color[9]=0;	break;	}
947 				if(strchr(MGL_COLORS,col[i]))
948 				{
949 					if(i>1 && col[i-1]=='{')	{	color[1]=col[i];	color[2]=col[i+1];	break;	}
950 					else	{	color[0]=col[i];	color[1]=0;	break;	}
951 				}
952 			}
953 			SetPenPal(color);
954 			mreal dx = (Max.x-Min.x)/30, dy = (Max.y-Min.y)/30, dz = (Max.z-Min.z)/30;
955 			long kq = AllocPnts(3*31*31);
956 #pragma omp parallel for collapse(2)
957 			for(long i=0;i<31;i++)	for(long j=0;j<31;j++)
958 			{
959 				long i0=kq+3*(i+31*j);
960 				AddPntQ(i0,  mglPoint(oo[im].x,Min.y+dy*i,Min.z+dz*j));
961 				AddPntQ(i0+1,mglPoint(Min.x+dx*i,oo[im].y,Min.z+dz*j));
962 				AddPntQ(i0+2,mglPoint(Min.x+dx*i,Min.y+dy*j,oo[im].z));
963 			}
964 			for(long i=0;i<30;i++)	for(long j=0;j<30;j++)
965 			{
966 				long i0=kq+3*(i+31*j);
967 				quad_plot(i0,  i0+3,i0+93,i0+96);
968 				quad_plot(i0+1,i0+4,i0+94,i0+97);
969 				quad_plot(i0+2,i0+5,i0+95,i0+98);
970 			}
971 		}
972 	}
973 	EndGroup();
974 	clr(MGL_NOSUBTICKS);	Org=o;	TickLen=tl;
975 }
976 //-----------------------------------------------------------------------------
Colorbar(const char * sch)977 void mglCanvas::Colorbar(const char *sch)
978 {
979 	bool in = mglchr(sch,'I');
980 	mreal sx = (fabs(B.b[0])+fabs(B.b[1])+fabs(B.b[2]))/B.pf/B1.b[0], x=1;
981 	mreal sy = (fabs(B.b[3])+fabs(B.b[4])+fabs(B.b[5]))/B.pf/B1.b[4], y=0;
982 	if(mglchr(sch,'<'))	{	x=in?(1-sx)/2:0.05;	y=0;	}
983 	else if(mglchr(sch,'^'))	{	x=0;	y=in?(1+sy)/2:0.95;	}
984 	else if(mglchr(sch,'_'))	{	x=0;	y=in?(1-sy)/2:0.05;	}
985 	else	{	x=in?(1+sx)/2:0.95;	y=0;	}
986 	Colorbar(sch, x, y, 1, 1);
987 }
988 //-----------------------------------------------------------------------------
Colorbar(const char * sch,mreal x,mreal y,mreal w,mreal h)989 void mglCanvas::Colorbar(const char *sch, mreal x, mreal y, mreal w, mreal h)
990 {
991 	bool in = mglchr(sch,'I');
992 	bool text = !mglchr(sch,'~');
993 	int where = 0;		// ‘0’ - right, ‘1’ - left, ‘2’ - above, ‘3’ - under
994 	if(mglchr(sch,'>'))	where = in?1:0;
995 	if(mglchr(sch,'<'))	where = in?0:1;
996 	if(mglchr(sch,'^'))	where = in?3:2;
997 	if(mglchr(sch,'_'))	where = in?2:3;
998 	if(mglchr(sch,'A'))	{	Push();	Identity();	}
999 
1000 	ac.stl.clear();
1001 	for(const char *s="+E0123456789-fF!";*s;s++)	if(mglchr(sch,*s))	ac.stl += *s;
1002 	AdjustTicks("c",mglchr(sch,'a'),ac.stl.c_str());
1003 
1004 	long n=256, s = AddTexture(sch);
1005 	mglData v(n);
1006 	if(ac.d || fa==0 || Min.c*Max.c<=0)	v.Fill(Min.c,Max.c);
1007 	else if(Min.c>0)
1008 	{	v.Fill(log(Min.c), log(Max.c));		v.Modify("exp(u)");		}
1009 	else if(Max.c<0)
1010 	{	v.Fill(log(-Min.c), log(-Max.c));	v.Modify("-exp(u)");	}
1011 	mreal *c=new mreal[n];
1012 	for(long i=0;i<n;i++)	c[i] = GetC(s,v.a[i]);
1013 	colorbar(&v, c, where, x, y, w, h, text);
1014 	delete []c;
1015 	if(mglchr(sch,'A'))	Pop();
1016 }
1017 //-----------------------------------------------------------------------------
Colorbar(HCDT v,const char * sch)1018 void mglCanvas::Colorbar(HCDT v, const char *sch)
1019 {
1020 	bool in = mglchr(sch,'I');
1021 	mreal sx = (fabs(B.b[0])+fabs(B.b[1])+fabs(B.b[2]))/B.pf/B1.b[0], x=1;
1022 	mreal sy = (fabs(B.b[3])+fabs(B.b[4])+fabs(B.b[5]))/B.pf/B1.b[4], y=0;
1023 	if(mglchr(sch,'>'))	{	x=in?(1+sx)/2:1;	y=0;	}
1024 	if(mglchr(sch,'<'))	{	x=in?(1-sx)/2:0;	y=0;	}
1025 	if(mglchr(sch,'^'))	{	x=0;	y=in?(1+sy)/2:1;	}
1026 	if(mglchr(sch,'_'))	{	x=0;	y=in?(1-sy)/2:0;	}
1027 	Colorbar(v, sch, x, y, 1, 1);
1028 }
1029 //-----------------------------------------------------------------------------
Colorbar(HCDT v,const char * sch,mreal x,mreal y,mreal w,mreal h)1030 void mglCanvas::Colorbar(HCDT v, const char *sch, mreal x, mreal y, mreal w, mreal h)
1031 {
1032 	bool in = mglchr(sch,'I');
1033 	bool text = !mglchr(sch,'~');
1034 	int where = 0;
1035 	if(mglchr(sch,'>'))	where = in?1:0;
1036 	if(mglchr(sch,'<'))	where = in?0:1;
1037 	if(mglchr(sch,'^'))	where = in?3:2;
1038 	if(mglchr(sch,'_'))	where = in?2:3;
1039 	if(mglchr(sch,'A'))	{	Push();	Identity();	}
1040 
1041 	ac.stl.clear();
1042 	for(const char *s="+E0123456789-fF!";*s;s++)	if(mglchr(sch,*s))	ac.stl += *s;
1043 	AdjustTicks("c",mglchr(sch,'a'),ac.stl.c_str());
1044 
1045 	mreal *c=new mreal[v->GetNx()];
1046 	if(!mgl_have_color(sch))	sch = MGL_DEF_PAL;
1047 	long s = AddTexture(sch);
1048 	int nc = GetNumPal(s*256);
1049 	mreal dc = nc>1 ? 1/(MGL_EPSILON*(nc-1)):0;
1050 	for(long i=0;i<v->GetNx();i++)	c[i] = s+i*dc;
1051 	colorbar(v, c, where, x, y, w, h, text);
1052 	delete []c;
1053 	if(mglchr(sch,'A'))	Pop();
1054 }
1055 //-----------------------------------------------------------------------------
colorbar(HCDT vv,const mreal * c,int where,mreal x,mreal y,mreal w,mreal h,bool text)1056 void mglCanvas::colorbar(HCDT vv, const mreal *c, int where, mreal x, mreal y, mreal w, mreal h, bool text)
1057 {
1058 	static int cgid=1;	StartGroup("Colorbar",cgid++);
1059 
1060 	long n=vv->GetNx();
1061 	mreal s3=B.pf,ss=1/s3;		// NOTE: colorbar was wider ss=0.9;
1062 	mglPoint p1,p2;
1063 	mglMatrix M=B1;	M.pf=s3;	M.norot=true;
1064 
1065 	set(MGL_DISABLE_SCALE);		// NOTE this make colorbar non-thread-safe!!!
1066 	x = s3*(2*x-1);	y = s3*(2*y-1);	w *= s3;	h *= s3;
1067 	mask = MGL_SOLID_MASK;
1068 
1069 	const long kq = AllocPnts(n*2);
1070 	for(long i=0;i<n;i++)
1071 	{
1072 		mreal d = GetA(vv->v(i))*2-1;
1073 		p1 = p2 = mglPoint((ss*d+1)*w+x, (ss*d+1)*h+y, s3);
1074 		switch(where)
1075 		{
1076 			case 1:	p1.x = x;	p2.x = x+0.1*w;	break;
1077 			case 2:	p1.y = y-0.1*h;	p2.y = y;	break;
1078 			case 3:	p1.y = y;	p2.y = y+0.1*h;	break;
1079 			default:p1.x = x-0.1*w;	p2.x = x;	break;
1080 		}
1081 		AddPntQ(kq+i, &M, p1,c[i]);
1082 		AddPntQ(kq+i+n, &M, p2,c[i]);
1083 	}
1084 	for(long i=0;i<n-1;i++)	quad_plot(kq+i, kq+i+n, kq+i+1, kq+i+1+n);
1085 
1086 	if(n<64)
1087 	{
1088 		ac.txt.clear();
1089 		if(ac.t.empty())
1090 			for(long i=0;i<n;i++)
1091 			{
1092 				mreal d = vv->v(i);
1093 				ac.AddLabel(mgl_ftoa(d,ac.stl.c_str()),d);
1094 			}
1095 		else
1096 		{
1097 			wchar_t buf[64];
1098 			for(long i=0;i<n;i++)
1099 			{
1100 				mreal d = vv->v(i);
1101 				mglprintf(buf,64,ac.t.c_str(),d);
1102 				ac.AddLabel(buf,d);
1103 			}
1104 		}
1105 	}
1106 	else	{	UpdateAxis();	AdjustTicks(ac,fa!=0);	}
1107 	// hint for using standard label drawing function
1108 	SetPenPal(TickStl);
1109 	for(size_t i=0;i<ac.txt.size();i++)
1110 	{
1111 		mreal d = fa?fa->Calc(0,0,0,ac.txt[i].val):ac.txt[i].val;
1112 		ac.txt[i].val = d = 2*(d-FMin.c)/(FMax.c-FMin.c)-1;
1113 		if(fabs(d)>1)	continue;	// this is factor
1114 //		mreal d = ac.txt[i].val = GetA(ac.txt[i].val)*2-1;
1115 		p1 = p2 = mglPoint((ss*d+1)*w+x, (ss*d+1)*h+y, s3);
1116 		switch(where)
1117 		{
1118 			case 1:	p1.x = x;	p2.x = x+0.1*w;	break;
1119 			case 2:	p1.y = y-0.1*h;	p2.y = y;	break;
1120 			case 3:	p1.y = y;	p2.y = y+0.1*h;	break;
1121 			default:p1.x = x-0.1*w;	p2.x = x;	break;
1122 		}
1123 		mglPoint p3(0.75*p1.x+0.25*p2.x, 0.75*p1.y+0.25*p2.y, s3);
1124 		mglPoint p4(0.25*p1.x+0.75*p2.x, 0.25*p1.y+0.75*p2.y, s3);
1125 //		line_plot(AddPnt(&M, p1), AddPnt(&M, p2));
1126 		line_plot(AddPnt(&M, p1), AddPnt(&M, p3));
1127 		line_plot(AddPnt(&M, p4), AddPnt(&M, p2));
1128 	}
1129 	ac.dir.Set(ss*w,ss*h,0);	ac.a.Set(0,0,0);
1130 	ac.org.Set(w+x,h+y,s3+1);	ac.b.Set(0,0,0);
1131 	switch(where)
1132 	{
1133 		case 1:	ac.dir.x = 0;	ac.a.x= 1;	ac.org.x = x+0.1*w-(get(MGL_ENABLE_RTEXT)?0:0.06);	break;
1134 		case 2:	ac.dir.y = 0;	ac.a.y=-1;	ac.org.y = y-0.1*h;	break;
1135 		case 3:	ac.dir.y = 0;	ac.a.y= 1;	ac.org.y = y+0.1*h;	break;
1136 		default:ac.dir.x = 0;	ac.a.x=-1;	ac.org.x = x-0.1*w;	break;
1137 	}
1138 	SetPenPal(AxisStl);
1139 	bool inv = where!=3 && where!=0;
1140 	ac.ns = where;	ac.angl=NAN;	// NOTE ns isn't used for colorbar
1141 	if(text)	DrawLabels(ac,inv,&M);
1142 	clr(MGL_DISABLE_SCALE);	EndGroup();
1143 }
1144 //-----------------------------------------------------------------------------
1145