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