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 = ∾ // 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