1 /***************************************************************************
2  * export_2d.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 "mgl2/canvas.h"
21 #include "mgl2/canvas_cf.h"
22 #include "mgl2/font.h"
23 #include <time.h>
24 #include <algorithm>
25 #include <sys/stat.h>
26 #undef _GR_
27 #define _GR_	((mglCanvas *)(*gr))
28 #define _Gr_	((mglCanvas *)(gr))
29 void mgl_printf(void *fp, bool gz, const char *str, ...);
30 //-----------------------------------------------------------------------------
mgl_get_dash(unsigned short d,mreal w,char dlm)31 static const char *mgl_get_dash(unsigned short d, mreal w,char dlm)
32 {
33 	static std::string s;
34 	if(d==0xffff)	return "";
35 	int f=0, p=d&1, n=p?0:1;
36 	s = p ? "" : "0";
37 	for(int i=0;i<16;i++)
38 	{
39 		int j = i;//15-i;
40 		if(((d>>j)&1) == p)	f++;
41 		else
42 		{
43 			s += mgl_str_num(f*w)+dlm;
44 			p = (d>>j)&1;	f = 1;	n++;
45 		}
46 	}
47 	s += mgl_str_num(f*w) + ((n%2) ? "" : " 0");
48 	return s.c_str();
49 }
50 //-----------------------------------------------------------------------------
mgl_is_same(HMGL gr,long i,mreal wp,uint32_t cp,int st)51 bool MGL_LOCAL_PURE mgl_is_same(HMGL gr, long i, mreal wp,uint32_t cp, int st)
52 {
53 	const mglPrim &pr=_Gr_->GetPrm(i);
54 	if(abs(pr.type)!=1)	return false;
55 	if(pr.w>=1 && wp!=pr.w)	return false;
56 	if(pr.w<1 && wp!=1)	return false;
57 	if(st!=pr.n3)	return false;
58 	return (cp==_Gr_->GetPrmCol(i));
59 }
60 //-----------------------------------------------------------------------------
put_line(HMGL gr,long i,mreal wp,uint32_t cp,int st)61 std::vector<long> static put_line(HMGL gr, long i, mreal wp, uint32_t cp,int st)
62 {
63 	std::vector<long> ids;
64 	long n1=gr->GetPrm(i).n1, n2=gr->GetPrm(i).n2;
65 	if(n1>n2)	{	n1=gr->GetPrm(i).n2;	n2=gr->GetPrm(i).n1;	}
66 	if(n1<0 || n2<0)	return ids;
67 	const mglPnt &pp1 = gr->GetPnt(n1), &pp2 = gr->GetPnt(n2);
68 	mreal x0=pp1.x, y0=pp1.y;
69 	bool ok=true;
70 	long j;	// first point
71 	while(ok)	// try to find starting point
72 	{
73 		for(ok=false,j=i+1;j<gr->GetPrmNum();j++)
74 		{
75 			mglPrim &q = gr->GetPrm(j);
76 			if(q.type>1)	break;
77 			if(mgl_is_same(gr, j, wp,cp,st) && q.type==1 && q.n1>=0 && q.n2>=0)	// previous point
78 			{
79 				const mglPnt &p1 = gr->GetPnt(q.n1);
80 				const mglPnt &p2 = gr->GetPnt(q.n2);
81 				if(p2.x==x0 && p2.y==y0)
82 				{
83 					ok = true;	ids.push_back(q.n1);
84 					x0 = p1.x;	y0 = p1.y;	q.type = -1;
85 				}
86 				else if(p1.x==x0 && p1.y==y0)
87 				{
88 					ok = true;	ids.push_back(q.n2);
89 					x0 = p2.x;	y0 = p2.y;	q.type = -1;
90 				}
91 			}
92 		}
93 	}
94 	std::reverse(ids.begin(),ids.end());
95 	ids.push_back(n1);	ids.push_back(n2);
96 	x0 = pp2.x;	y0 = pp2.y;	ok = true;
97 	while(ok)	// try to find starting point
98 	{
99 		for(ok=false,j=i+1;j<gr->GetPrmNum();j++)
100 		{
101 			mglPrim &q = gr->GetPrm(j);
102 			if(q.type>1)	break;
103 			if(mgl_is_same(gr, j,wp,cp,st) && q.type==1 && q.n1>=0 && q.n2>=0)	// next point
104 			{
105 				const mglPnt &p1 = gr->GetPnt(q.n1);
106 				const mglPnt &p2 = gr->GetPnt(q.n2);
107 				if(p2.x==x0 && p2.y==y0)
108 				{
109 					ok = true;	ids.push_back(q.n1);
110 					x0 = p1.x;	y0 = p1.y;	q.type = -1;
111 				}
112 				else if(p1.x==x0 && p1.y==y0)
113 				{
114 					ok = true;	ids.push_back(q.n2);
115 					x0 = p2.x;	y0 = p2.y;	q.type = -1;
116 				}
117 			}
118 		}
119 	}
120 	return ids;
121 }
122 //-----------------------------------------------------------------------------
123 //put_desc(fp,"%c%c%c_%04x {", "np %d %d mt %d %d ll %d %d ll cp fill\n",
124 //"np %d %d mt ", "%d %d ll ", "cp dr\n", "} def")
put_desc(HMGL gr,void * fp,bool gz,const char * pre,const char * ln1,const char * ln2,const char * ln3,const char * suf)125 void static put_desc(HMGL gr, void *fp, bool gz, const char *pre, const char *ln1, const char *ln2, const char *ln3, const char *suf)
126 {
127 	long n=0;
128 	for(long i=0;i<gr->GetPrmNum();i++)	if(gr->GetPrm(i).type==4)	n++;
129 	if(n==0)	return;		// no glyphs
130 	wchar_t *g = new wchar_t[n];
131 	int *s = new int[n];	n=0;
132 	for(long i=0;i<gr->GetPrmNum();i++)
133 	{
134 		const mglPrim &q = gr->GetPrm(i);
135 		if(q.type!=4 || (q.n3&8))	continue;	// not a glyph
136 		bool is=false;
137 		for(long j=0;j<n;j++)	if(g[j]==q.n4 && s[j]==(q.n3&7))	is = true;
138 		if(is)	continue;		// glyph is described
139 		// have to describe
140 		g[n]=q.n4;	s[n]=q.n3&7;	n++;	// add to list of described
141 		// "%c%c%c_%04x {"
142 		mgl_printf(fp, gz, pre, q.n3&1?'b':'n', q.n3&2?'i':'n', q.n4);
143 		const mglGlyph &g = gr->GetGlf(q.n4);
144 		int nl=g.nl;
145 		const short *ln=g.line;
146 		bool np=true;
147 		if(ln && nl>0)	for(long ik=0;ik<nl;ik++)
148 		{
149 			long ii = 2*ik;
150 			if(ln[ii]==0x3fff && ln[ii+1]==0x3fff)	// line breakthrough
151 			{	mgl_printf(fp, gz, "%s",ln3);	np=true;	continue;	}
152 			else if(np)	mgl_printf(fp, gz, ln1,ln[ii],ln[ii+1]);
153 			else		mgl_printf(fp, gz, ln2,ln[ii],ln[ii+1]);
154 			np=false;
155 		}
156 		mgl_printf(fp, gz, "%s%s",ln3,suf);	// finish glyph description suf="} def"
157 	}
158 	delete []g;		delete []s;
159 }
160 //-----------------------------------------------------------------------------
mgl_eps_pattern(void * fp,bool gz,const mglPrim & q)161 bool MGL_NO_EXPORT mgl_eps_pattern(void *fp, bool gz, const mglPrim &q)
162 {
163 	static uint64_t pd=MGL_SOLID_MASK;	// mask if !=MGL_SOLID_MASK
164 	static mreal pw=0;		// width (like pen width) if !=0
165 	static int ang=0;		// angle (default is 0,45,90,315)
166 	int aa = 45*int(0.5+q.angl/45.);
167 	if(q.m==MGL_SOLID_MASK || q.w<=0)	return false;
168 	if(q.m==pd && q.w==pw && aa==ang)	return true;
169 	pd = q.m;	pw=q.w;	ang=aa;
170 	mreal d = ang%90 ? 4*M_SQRT2*pw : 4*pw;
171 	mgl_printf(fp, gz, "<<\n/PaintType 2 /PatternType 1 /TilingType 1\n");
172 	mgl_printf(fp, gz, "/BBox [-%g -%g %g %g] /XStep %g /YStep %g\n", d,d,d,d, 2*d,2*d);
173 	if(ang%90)
174 	{
175 		mgl_printf(fp, gz, "/PaintProc { gsave %d rotate\n",-ang);
176 		for(int i=-8;i<8;i++)	for(int j=-8;j<8;j++)
177 		{
178 			int ii = (8+i)&7, jj = (8+j)&7;	// TODO reduce number of used rectangles
179 			if(pd & (1L<<(ii+8*jj)))
180 				mgl_printf(fp, gz, "%g %g %g %g rf\n",i*pw,j*pw,pw,pw);
181 		}
182 		mgl_printf(fp, gz, "grestore}\n>> pat\n");
183 	}
184 	else
185 	{
186 		mgl_printf(fp, gz, "/PaintProc { gsave %d rotate\n",-ang);
187 		for(int i=-4;i<4;i++)	for(int j=-4;j<4;j++)
188 		{
189 			int ii = (8+i)&7, jj = (8+j)&7;
190 			if(pd & (1L<<(ii+8*jj)))
191 				mgl_printf(fp, gz, "%g %g %g %g rf\n",i*pw,j*pw,pw,pw);
192 		}
193 		mgl_printf(fp, gz, "grestore}\n>> pat\n");
194 	}
195 	return true;
196 }
197 //-----------------------------------------------------------------------------
mgl_write_eps(HMGL gr,const char * fname,const char * descr)198 void MGL_EXPORT mgl_write_eps(HMGL gr, const char *fname,const char *descr)
199 {
200 	if(!fname || *fname==0)	return;
201 	if(gr->GetPrmNum()<1)	return;
202 	_Gr_->clr(MGL_FINISHED);	_Gr_->PreparePrim(1);
203 	time_t now;	time(&now);
204 
205 	bool gz = fname[strlen(fname)-1]=='z';
206 	void *fp;
207 	if(!strcmp(fname,"-"))	fp = stdout;		// allow to write in stdout
208 	else		fp = gz ? (void*)gzopen(fname,"wt") : (void*)fopen(fname,"wt");
209 	if(!fp)		{	gr->SetWarn(mglWarnOpen,fname);	return;	}
210 	int w = _Gr_->GetWidth(), h = _Gr_->GetHeight();
211 
212 	int x1=gr->BBoxX1, x2=gr->BBoxX2<0?w:gr->BBoxX2, y1=gr->BBoxY1, y2=gr->BBoxY2<0?h:gr->BBoxY2;
213 	if(x2>w)	x2=w;
214 	if(y2>h)	y2=h;
215 	if(x1<0 || x1>=x2)	{	x1=0;	x2=w;	}
216 	if(y1<0 || y1>=y2)	{	y1=0;	y2=h;	}
217 
218 	if(gz)
219 	{
220 		unsigned len = strlen(fname), pos=0;
221 		char *buf = new char[len+4];
222 		memcpy(buf,fname,len);
223 		if(buf[len-3]=='.')	pos = len-2;
224 		else if(buf[len-2]=='.')	pos = len-1;
225 		else	{	buf[len-1]='.';	pos = len;	}
226 		if(pos)	{	buf[pos]=buf[pos+1]='b';	buf[pos+2]=0;	}
227 		FILE *fb = fopen(buf,"w");
228 		fprintf(fb, "%%%%BoundingBox: %d %d %d %d\n", x1, h-y2, x2, h-y1);
229 		fclose(fb);	delete []buf;
230 	}
231 
232 	const std::string loc = setlocale(LC_NUMERIC, "C");
233 	mgl_printf(fp, gz, "%%!PS-Adobe-3.0 EPSF-3.0\n%%%%BoundingBox: %d %d %d %d\n", x1, h-y2, x2, h-y1);
234 	mgl_printf(fp, gz, "%%%%Created by MathGL library\n%%%%Title: %s\n",descr ? descr : fname);
235 	mgl_printf(fp, gz, "%%%%CreationDate: %s\n",ctime(&now));
236 	mgl_printf(fp, gz, "%%%%LanguageLevel: 2\n50 dict begin");
237 	mgl_printf(fp, gz, "/lw {setlinewidth} def\n/rgb {setrgbcolor} def\n");
238 	mgl_printf(fp, gz, "/np {newpath} def\n/cp {closepath} def\n");
239 	mgl_printf(fp, gz, "/ll {lineto} def\n/mt {moveto} def\n");
240 	mgl_printf(fp, gz, "/rl {rlineto} def\n/rm {rmoveto} def\n/dr {stroke} def\n");
241 	mgl_printf(fp, gz, "/ss {%g} def\n",0.35*gr->mark_size());
242 	mgl_printf(fp, gz, "/s2 {%g} def\n",0.7*gr->mark_size());
243 	mgl_printf(fp, gz, "/sm {-%g} def\n",0.35*gr->mark_size());
244 	mgl_printf(fp, gz, "/m_c {ss 0.3 mul 0 360 arc} def\n");
245 	mgl_printf(fp, gz, "/d0 {[] 0 setdash} def\n/sd {setdash} def\n");
246 	mgl_printf(fp, gz, "/pat {[1 0 0 1 0 0] makepattern /Mask exch def [/Pattern /DeviceRGB] setcolorspace} def\n");
247 	mgl_printf(fp, gz, "/mask {Mask setpattern} def\n/rf {rectfill} def\n");
248 
249 	bool m_p=false,m_x=false,m_d=false,m_v=false,m_t=false,
250 	m_s=false,m_a=false,m_o=false,m_T=false,
251 	m_V=false,m_S=false,m_D=false,m_Y=false,m_l=false,
252 	m_L=false,m_r=false,m_R=false,m_X=false,m_P=false;
253 	// add mark definition if present
254 	for(long i=0;i<gr->GetPrmNum();i++)
255 	{
256 		const mglPrim &q = gr->GetPrm(i);
257 		if(q.type>0)	continue;
258 		if(q.n4=='+')	m_p = true;
259 		if(q.n4=='x')	m_x = true;
260 		if(q.n4=='s')	m_s = true;
261 		if(q.n4=='d')	m_d = true;
262 		if(q.n4=='v')	m_v = true;
263 		if(q.n4=='^')	m_t = true;
264 		if(q.n4=='*')	m_a = true;
265 		if(q.n4=='o' || q.n4=='O' || q.n4=='C')	m_o = true;
266 		if(q.n4=='S')	m_S = true;
267 		if(q.n4=='D')	m_D = true;
268 		if(q.n4=='V')	m_V = true;
269 		if(q.n4=='T')	m_T = true;
270 		if(q.n4=='<')	m_l = true;
271 		if(q.n4=='L')	m_L = true;
272 		if(q.n4=='>')	m_r = true;
273 		if(q.n4=='R')	m_R = true;
274 		if(q.n4=='Y')	m_Y = true;
275 		if(q.n4=='P')	m_P = true;
276 		if(q.n4=='X')	m_X = true;
277 	}
278 	if(m_P)	{	m_p=true;	m_s=true;	}
279 	if(m_X)	{	m_x=true;	m_s=true;	}
280 	if(m_p)	mgl_printf(fp, gz, "/m_p {sm 0 rm s2 0 rl sm sm rm 0 s2 rl d0} def\n");
281 	if(m_x)	mgl_printf(fp, gz, "/m_x {sm sm rm s2 s2 rl 0 sm 2 mul rm sm 2 mul s2 rl d0} def\n");
282 	if(m_s)	mgl_printf(fp, gz, "/m_s {sm sm rm 0 s2 rl s2 0 rl 0 sm 2 mul rl cp d0} def\n");
283 	if(m_d)	mgl_printf(fp, gz, "/m_d {sm 0 rm ss ss rl ss sm rl sm sm rl cp d0} def\n");
284 	if(m_v)	mgl_printf(fp, gz, "/m_v {sm ss 2 div rm s2 0 rl sm sm 1.5 mul rl d0 cp} def\n");
285 	if(m_t)	mgl_printf(fp, gz, "/m_t {sm sm 2 div rm s2 0 rl sm ss 1.5 mul rl d0 cp} def\n");
286 	if(m_a)	mgl_printf(fp, gz, "/m_a {sm 0 rm s2 0 rl sm 1.6 mul sm 0.8 mul rm ss 1.2 mul ss 1.6 mul rl 0 sm 1.6 mul rm sm 1.2 mul ss 1.6 mul rl d0} def\n");
287 	if(m_o)	mgl_printf(fp, gz, "/m_o {ss 0 360 d0 arc} def\n");
288 	if(m_S)	mgl_printf(fp, gz, "/m_S {sm sm rm 0 s2 rl s2 0 rl 0 sm 2 mul rl cp} def\n");
289 	if(m_D)	mgl_printf(fp, gz, "/m_D {sm 0 rm ss ss rl ss sm rl sm sm rl cp} def\n");
290 	if(m_V)	mgl_printf(fp, gz, "/m_V {sm ss 2 div rm s2 0 rl sm sm 1.5 mul rl cp} def\n");
291 	if(m_T)	mgl_printf(fp, gz, "/m_T {sm sm 2 div rm s2 0 rl sm ss 1.5 mul rl cp} def\n");
292 	if(m_Y)	mgl_printf(fp, gz, "/m_Y {0 sm rm 0 ss rl sm ss rl s2 0 rm sm sm rl d0} def\n");
293 	if(m_r)	mgl_printf(fp, gz, "/m_r {sm 2 div sm rm 0 s2 rl ss 1.5 mul sm rl d0 cp} def\n");
294 	if(m_l)	mgl_printf(fp, gz, "/m_l {ss 2 div sm rm 0 s2 rl sm 1.5 mul sm rl d0 cp} def\n");
295 	if(m_R)	mgl_printf(fp, gz, "/m_R {sm 2 div sm rm 0 s2 rl ss 1.5 mul sm rl cp} def\n");
296 	if(m_L)	mgl_printf(fp, gz, "/m_L {ss 2 div sm rm 0 s2 rl sm 1.5 mul sm rl cp} def\n");
297 	if(m_P)	mgl_printf(fp, gz, "/m_P {m_p 0 sm rm m_s} def\n");
298 	if(m_X)	mgl_printf(fp, gz, "/m_X {m_x ss sm rm m_s} def\n");
299 	//	if(m_C)	mgl_printf(fp, gz, "/m_C {m_c m_o} def\n");
300 	mgl_printf(fp, gz, "0 setlinecap\n1 setlinejoin\n\n");	// manual setting round line cap
301 
302 	// Write background image first
303 	const unsigned char *img = mgl_get_background(gr);
304 	bool same = true;
305 	unsigned char white[3]={255,255,255};
306 #pragma omp parallel for
307 	for(long i=0;i<w*h;i++)	if(memcmp(img,img+4*i,3))	same=false;
308 	if(!same)
309 	{
310 		mgl_printf(fp, gz, "gsave\t%d %d translate\n",x1,h-y2);
311 		mgl_printf(fp, gz, "%d %d 8 [1 0 0 1 0 0] {<", x2-x1,y2-y1);
312 		for(long j=y2-1;j>=y1;j--)	for(long i=x1;i<x2;i++)
313 		{
314 			if((i+w*(h-j-1))%40==0 && i+j>0)	mgl_printf(fp, gz, "\n");
315 			mgl_printf(fp, gz, "%02x%02x%02x",img[4*(i+w*j)],img[4*(i+w*j)+1],img[4*(i+w*j)+2]);
316 		}
317 		mgl_printf(fp, gz, "\n>} false 3 colorimage\ngrestore\n\n");
318 	}
319 	else if(memcmp(img,white,3))
320 		mgl_printf(fp, gz, "np 0 0 mt 0 %d ll %d %d ll %d 0 ll cp %g %g %g rgb fill\n", y2-y1, x2-x1, y2-y1, x2-x1, img[0]/255., img[1]/255., img[2]/255.);
321 
322 	// write definition for all glyphs
323 	put_desc(gr,fp,gz,"/%c%c_%04x { np\n", "\t%d %d mt ", "%d %d ll ", "cp\n", "} def\n");
324 	// write primitives
325 	mreal wp=-1;
326 	float qs_old=gr->mark_size()/gr->FontFactor();
327 	mglRGBA cp;
328 	int st=0;
329 	char str[256]="", msk[256]="";
330 	for(long i=0;i<gr->GetPrmNum();i++)
331 	{
332 		const mglPrim &q = gr->GetPrm(i);
333 		if(q.type<0)	continue;	// q.n1>=0 always
334 		cp.c = _Gr_->GetPrmCol(i);
335 		const mglPnt &p1 = gr->GetPnt(q.n1);
336 		if(q.type>1)
337 		{
338 			snprintf(str,256,"%.2g %.2g %.2g rgb ", cp.r[0]/255.,cp.r[1]/255.,cp.r[2]/255.);	str[255]=0;
339 			snprintf(msk,256,"%.2g %.2g %.2g mask ", cp.r[0]/255.,cp.r[1]/255.,cp.r[2]/255.);	msk[255]=0;
340 		}
341 
342 		if(q.type==0)	// mark
343 		{
344 			mreal x0 = p1.x,y0 = p1.y;
345 //			snprintf(str,256,"%.2g lw %.2g %.2g %.2g rgb ", 50*q.s*q.w>1?50*q.s*q.w:1, cp.r[0]/255.,cp.r[1]/255.,cp.r[2]/255.);
346 			snprintf(str,256,"%.2g lw %.2g %.2g %.2g rgb ", q.w>1?q.w:1, cp.r[0]/255.,cp.r[1]/255.,cp.r[2]/255.);
347 			str[255]=0;	wp=1;	// NOTE: this may renew line style if a mark inside!
348 			if(q.s!=qs_old)
349 			{
350 				mgl_printf(fp, gz, "/ss {%g} def\n",q.s);
351 				mgl_printf(fp, gz, "/s2 {%g} def\n",q.s*2);
352 				mgl_printf(fp, gz, "/sm {-%g} def\n",q.s);
353 				qs_old = q.s;
354 			}
355 			switch(q.n4)
356 			{
357 				case '+':	mgl_printf(fp, gz, "np %g %g mt m_p %sdr\n",x0,y0,str);	break;
358 				case 'x':	mgl_printf(fp, gz, "np %g %g mt m_x %sdr\n",x0,y0,str);	break;
359 				case 's':	mgl_printf(fp, gz, "np %g %g mt m_s %sdr\n",x0,y0,str);	break;
360 				case 'd':	mgl_printf(fp, gz, "np %g %g mt m_d %sdr\n",x0,y0,str);	break;
361 				case '*':	mgl_printf(fp, gz, "np %g %g mt m_a %sdr\n",x0,y0,str);	break;
362 				case 'v':	mgl_printf(fp, gz, "np %g %g mt m_v %sdr\n",x0,y0,str);	break;
363 				case '^':	mgl_printf(fp, gz, "np %g %g mt m_t %sdr\n",x0,y0,str);	break;
364 				case 'S':	mgl_printf(fp, gz, "np %g %g mt m_S %sfill\n",x0,y0,str);	break;
365 				case 'D':	mgl_printf(fp, gz, "np %g %g mt m_D %sfill\n",x0,y0,str);	break;
366 				case 'V':	mgl_printf(fp, gz, "np %g %g mt m_V %sfill\n",x0,y0,str);	break;
367 				case 'T':	mgl_printf(fp, gz, "np %g %g mt m_T %sfill\n",x0,y0,str);	break;
368 				case 'o':	mgl_printf(fp, gz, "%g %g m_o %sdr\n",x0,y0,str);break;
369 				case 'O':	mgl_printf(fp, gz, "%g %g m_o %sfill\n",x0,y0,str);break;
370 				case 'Y':	mgl_printf(fp, gz, "np %g %g mt m_Y %sdr\n",x0,y0,str);	break;
371 				case '<':	mgl_printf(fp, gz, "np %g %g mt m_l %sdr\n",x0,y0,str);	break;
372 				case '>':	mgl_printf(fp, gz, "np %g %g mt m_r %sdr\n",x0,y0,str);	break;
373 				case 'L':	mgl_printf(fp, gz, "np %g %g mt m_L %sfill\n",x0,y0,str);	break;
374 				case 'R':	mgl_printf(fp, gz, "np %g %g mt m_R %sfill\n",x0,y0,str);	break;
375 				case 'P':	mgl_printf(fp, gz, "np %g %g mt m_P %sdr\n",x0,y0,str);	break;
376 				case 'X':	mgl_printf(fp, gz, "np %g %g mt m_X %sdr\n",x0,y0,str);	break;
377 				case 'C':	mgl_printf(fp, gz, "%g %g m_o %g %g m_c %sdr\n",x0,y0,x0,y0,str);	break;
378 				case '.':	mgl_printf(fp, gz, "%g %g m_c %sfill\n",x0,y0,str);
379 			}
380 		}
381 		else if(q.type==3)	// quad
382 		{
383 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3), &p4=gr->GetPnt(q.n4);
384 			if(cp.r[3])	// TODO && gr->quad_vis(p1,p2,p3,p4))
385 			{
386 				bool mask = mgl_eps_pattern(fp,gz,q);
387 				mgl_printf(fp, gz, "np %g %g mt %g %g ll %g %g ll %g %g ll cp %sfill\n", p1.x, p1.y, p2.x, p2.y, p4.x, p4.y, p3.x, p3.y, mask?msk:str);
388 			}
389 		}
390 		else if(q.type==2)	// trig
391 		{
392 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3);
393 			if(cp.r[3])	// TODO && gr->trig_vis(p1,p2,p3))
394 			{
395 				bool mask = mgl_eps_pattern(fp,gz,q);
396 				mgl_printf(fp, gz, "np %g %g mt %g %g ll %g %g ll cp %sfill\n", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, mask?msk:str);
397 			}
398 		}
399 		else if(q.type==1)	// line
400 		{
401 			snprintf(str,256,"%.2g lw %.2g %.2g %.2g rgb ", q.w>1 ? q.w:1., cp.r[0]/255.,cp.r[1]/255.,cp.r[2]/255.);
402 			str[255]=0;	wp = q.w>1  ? q.w:1;	st = q.n3;
403 			std::vector<long> ids = put_line(gr,i,wp,cp.c,st);
404 			for(size_t j=0;j<ids.size();j++)
405 			{
406 				const mglPnt &p = gr->GetPnt(ids[j]);
407 				float x0 = p.x, y0 = p.y;
408 				mgl_printf(fp, gz, j==0?"np %g %g mt ":"%g %g ll ",x0,y0);
409 			}
410 			const char *sd = mgl_get_dash(q.n3,q.w,' ');
411 			if(sd && sd[0])	mgl_printf(fp, gz, "%s [%s] %g sd dr\n",str,sd,q.w*q.s);
412 			else			mgl_printf(fp, gz, "%s d0 dr\n",str);
413 		}
414 		else if(q.type==4)	// glyph
415 		{
416 			float phi = gr->GetGlyphPhi(gr->GetPnt(q.n2),q.w);
417 			if(mgl_isnan(phi))	continue;
418 			mreal 	ss = q.s/2, xx = p1.u, yy = p1.v, zz = q.p;
419 			mgl_printf(fp, gz, "gsave\t%g %g translate %g %g scale %g rotate %s\n",
420 					   p1.x, p1.y, ss, ss, -phi, str);
421 			if(q.n3&8)	// this is "line"
422 			{
423 				mreal dy = 0.004,f=fabs(zz);
424 				mgl_printf(fp, gz, "np %g %g mt %g %g ll %g %g ll %g %g ll cp ",
425 						   xx,yy+dy, xx+f,yy+dy, xx+f,yy-dy, xx,yy-dy);
426 			}
427 			else
428 				mgl_printf(fp, gz, "%.3g %.3g translate %g %g scale %c%c_%04x ",
429 						   xx, yy, zz, zz, q.n3&1?'b':'n', q.n3&2?'i':'n', q.n4);
430 			if(q.n3&4)	mgl_printf(fp, gz, "dr");
431 			else	mgl_printf(fp, gz, "eofill");
432 			mgl_printf(fp, gz, " grestore\n");
433 		}
434 	}
435 	for(long i=0;i<gr->GetPrmNum();i++)
436 	{
437 		mglPrim &q = gr->GetPrm(i);
438 		if(q.type==-1)	q.type = 1;
439 	}
440 	mgl_printf(fp, gz, "\nshowpage\n%%%%EOF\n");
441 	if(strcmp(fname,"-"))	{	if(gz)	gzclose((gzFile)fp);	else	fclose((FILE *)fp);	}
442 	setlocale(LC_NUMERIC, loc.c_str());
443 }
mgl_write_eps_(uintptr_t * gr,const char * fname,const char * descr,int l,int n)444 void MGL_EXPORT mgl_write_eps_(uintptr_t *gr, const char *fname,const char *descr,int l,int n)
445 {	char *s=new char[l+1];	memcpy(s,fname,l);	s[l]=0;
446 	char *d=new char[n+1];	memcpy(d,descr,n);	d[n]=0;
447 	mgl_write_eps(_GR_,s,d);	delete []s;		delete []d;	}
448 //-----------------------------------------------------------------------------
mgl_write_svg(HMGL gr,const char * fname,const char * descr)449 void MGL_EXPORT mgl_write_svg(HMGL gr, const char *fname,const char *descr)
450 {
451 	if(!fname || *fname==0)	return;
452 	if(gr->GetPrmNum()<1)	return;
453 	_Gr_->clr(MGL_FINISHED);	_Gr_->PreparePrim(1);
454 	time_t now;	time(&now);
455 
456 	bool gz = fname[strlen(fname)-1]=='z';
457 	long hh = _Gr_->GetHeight(), ww = _Gr_->GetWidth();
458 	void *fp = stdout;		// allow to write in stdout
459 	bool head = true;
460 	if(strcmp(fname,"-"))	fp = gz ? (void*)gzopen(fname,"wt") : (void*)fopen(fname,"wt");
461 	else	head = false;
462 	if(!fp)		{	gr->SetWarn(mglWarnOpen,fname);	return;	}
463 
464 	int x1=gr->BBoxX1, x2=gr->BBoxX2<0?ww:gr->BBoxX2, y1=gr->BBoxY1, y2=gr->BBoxY2<0?hh:gr->BBoxY2;
465 	if(x2>ww)	x2=ww;
466 	if(y2>hh)	y2=hh;
467 	if(x1<0 || x1>=x2)	{	x1=0;	x2=ww;	}
468 	if(y1<0 || y1>=y2)	{	y1=0;	y2=hh;	}
469 	ww = x2-x1;	hh = y2-y1;
470 
471 	const std::string loc = setlocale(LC_NUMERIC, "C");
472 	if(head)
473 	{
474 		mgl_printf(fp, gz, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
475 		mgl_printf(fp, gz, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20000303 Stylable//EN\" \"http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd\">\n");
476 		mgl_printf(fp, gz, "<svg width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", ww, hh);
477 		mgl_printf(fp, gz, "<!--Created by MathGL library-->\n");
478 		mgl_printf(fp, gz, "<!--Title: %s-->\n<!--CreationDate: %s-->\n\n",descr?descr:fname,ctime(&now));
479 	}
480 	else
481 	{
482 		mgl_printf(fp, gz, "<!--Created by MathGL library-->\n");
483 		mgl_printf(fp, gz, "<svg width=\"%d\" height=\"%d\">\n", ww, hh);
484 	}
485 
486 	// write definition for all glyphs
487 	put_desc(gr,fp,gz,"<defs><g id=\"%c%c_%04x\"><path d=\"", "\tM %d %d ",
488 			 "L %d %d ", "Z\n", "\"/></g></defs>\n");
489 
490 	// Write background image first
491 	const unsigned char *img = mgl_get_background(gr);
492 	bool same = true;
493 	unsigned char white[3]={255,255,255};
494 #pragma omp parallel for
495 	for(long i=0;i<ww*hh;i++)	if(memcmp(img,img+4*i,3))	same=false;
496 	if(!same)
497 	{	// TODO write as <image width="100" height="100" xlink:href="data:image/png;base64,...">
498 /*		mgl_printf(fp, gz, "%d %d 8 [1 0 0 1 0 0] {<", ww,hh,1+ww*hh/40);
499 		for(long j=hh-1;j>=0;j--)	for(long i=0;i<ww;i++)
500 		{
501 			if((i+ww*(hh-j-1))%40==0 && i+j>0)	mgl_printf(fp, gz, "\n");
502 			mgl_printf(fp, gz, "%02x%02x%02x",img[4*(i+ww*j)],img[4*(i+ww*j)+1],img[4*(i+ww*j)+2]);
503 		}
504 		mgl_printf(fp, gz, "\n>} false 3 colorimage\n\n");*/
505 	}
506 	else if(memcmp(img,white,3))
507 	{
508 		mgl_printf(fp, gz, "<g fill=\"#%02x%02x%02x\" opacity=\"%g\">\n", img[0], img[1], img[2], img[3]/255.);
509 		mgl_printf(fp, gz, "<path d=\"M 0 0 L 0 %ld L %ld %ld L %ld 0 Z\"/> </g>\n", hh, ww, hh, ww);
510 	}
511 
512 
513 	// currentColor -> inherit ???
514 	mgl_printf(fp, gz, "<g fill=\"none\" stroke=\"none\" stroke-width=\"0.5\" stroke-linecap=\"butt\" stroke-linejoin=\"round\">\n");
515 	// write primitives
516 	mreal wp=-1;
517 	int st=0;
518 	mglRGBA cp;
519 
520 //	hh += (y2+y1)/2;
521 	for(long i=0;i<gr->GetPrmNum();i++)
522 	{
523 		const mglPrim &q = gr->GetPrm(i);
524 		if(q.type<0)	continue;	// q.n1>=0 always
525 		cp.c = _Gr_->GetPrmCol(i);
526 		const mglPnt &p1=gr->GetPnt(q.n1);
527 		if(q.type==0)
528 		{
529 			mreal x=p1.x-x1,y=hh-p1.y,s=q.s;
530 			if(!strchr("xsSoO",q.n4))	s *= 1.1;
531 			wp = 1;
532 			if(strchr("SDVTLR",q.n4))
533 				mgl_printf(fp, gz, "<g fill=\"#%02x%02x%02x\">\n", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
534 			else
535 				mgl_printf(fp, gz, "<g stroke=\"#%02x%02x%02x\"  stroke-width=\"%g\">\n", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]), q.w>1?q.w:1);
536 //				mgl_printf(fp, gz, "<g stroke=\"#%02x%02x%02x\"  stroke-width=\"%g\">\n", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]), 50*q.s*q.w>1?50*q.s*q.w:1);
537 			switch(q.n4)
538 			{
539 			case 'P':
540 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g M %g %g L %g %g M %g %g L %g %g L %g %g L %g %g L %g %g\"/>\n",
541 							x-s,y,x+s,y,x,y-s,x,y+s, x-s,y-s,x+s,y-s,x+s,y+s,x-s,y+s,x-s,y-s);	break;
542 			case '+':
543 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g M %g %g L %g %g\"/>\n", x-s,y,x+s,y,x,y-s,x,y+s);	break;
544 			case 'X':
545 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g M %g %g L %g %g M %g %g L %g %g L %g %g L %g %g L %g %g\"/>\n",
546 							x-s,y-s,x+s,y+s,x+s,y-s,x-s,y+s, x-s,y-s,x+s,y-s,x+s,y+s,x-s,y+s,x-s,y-s);	break;
547 			case 'x':
548 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g M %g %g L %g %g\"/>\n", x-s,y-s,x+s,y+s,x+s,y-s,x-s,y+s);	break;
549 			case 's':
550 			case 'S':
551 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g L %g %gZ\"/>\n", x-s,y-s,x+s,y-s,x+s,y+s,x-s,y+s);	break;
552 			case 'd':
553 			case 'D':
554 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g L %g %gZ\"/>\n", x-s,y,x,y-s,x+s,y,x,y+s);	break;
555 			case 'v':
556 			case 'V':
557 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %gZ\"/>\n", x-s,y-s/2,x+s,y-s/2,x,y+s);	break;
558 			case '^':
559 			case 'T':
560 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %gZ\"/>\n", x-s,y+s/2,x+s,y+s/2,x,y-s);	break;
561 			case '<':
562 			case 'L':
563 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %gZ\"/>\n", x+s/2,y+s,x+s/2,y-s,x-s,y);	break;
564 			case '>':
565 			case 'R':
566 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %gZ\"/>\n", x-s/2,y+s,x-s/2,y-s,x+s,y);	break;
567 			case 'Y':
568 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g M %g %g L %g %g\"/>\n", x,y-s, x,y, x+s,y+s, x,y, x-s,y+s);	break;
569 			case 'C':
570 				mgl_printf(fp, gz, "<circle style=\"fill:#%02x%02x%02x\" cx=\"%g\" cy=\"%g\" r=\"0.15\"/>\n<circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
571 							int(cp.r[0]),int(cp.r[1]),int(cp.r[2]),x,y,x,y,s);	break;
572 			case 'o':
573 				mgl_printf(fp, gz, "<circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n", x,y,s);	break;
574 			case 'O':
575 				mgl_printf(fp, gz, "<circle style=\"fill:#%02x%02x%02x\" cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
576 							int(cp.r[0]),int(cp.r[1]),int(cp.r[2]),x,y,s);	break;
577 			case '*':
578 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g M %g %g L %g %g M %g %g L %g %g\"/>\n",
579 							x-s,y,x+s,y,x-0.6*s,y-0.8*s,x+0.6*s,y+0.8*s,x+0.6*s,y-0.8*s,x-0.6*s,y+0.8*s);	break;
580 			case '.':
581 				mgl_printf(fp, gz, "<circle style=\"fill:#%02x%02x%02x\" cx=\"%g\" cy=\"%g\" r=\"0.15\"/>\n",
582 							int(cp.r[0]),int(cp.r[1]),int(cp.r[2]),x,y);	break;
583 			}
584 			mgl_printf(fp, gz, "</g>\n");
585 		}
586 		else if(q.type==1)
587 		{
588 			mgl_printf(fp,gz,"<g stroke=\"#%02x%02x%02x\"",int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
589 			if(q.n3)
590 			{
591 				mgl_printf(fp, gz, " stroke-dasharray=\"%s\"", mgl_get_dash(q.n3,q.w,','));
592 //				mgl_printf(fp, gz, " stroke-dashoffset=\"%g\"", q.s*q.w);
593 				mgl_printf(fp, gz, " stroke-dashoffset=\"%g\"", q.w);
594 			}
595 			if(q.w>1)	mgl_printf(fp, gz, " stroke-width=\"%g\"", q.w);
596 			wp = q.w>1  ? q.w:1;	st = q.n3;
597 			std::vector<long> ids = put_line(gr,i,wp,cp.c,st);
598 			for(size_t j=0;j<ids.size();j++)
599 			{
600 				const mglPnt &p = gr->GetPnt(ids[j]);
601 				mgl_printf(fp, gz, j==0?"><path d=\" M %g %g":" L %g %g",p.x-x1,hh-p.y);
602 			}
603 			mgl_printf(fp, gz, "\"/> </g>\n");
604 		}
605 		else if(q.type==2 && cp.r[3])
606 		{
607 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3);
608 			mgl_printf(fp, gz, "<g fill=\"#%02x%02x%02x\" opacity=\"%g\">\n", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]),cp.r[3]/255.);
609 			mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g Z\"/> </g>\n", p1.x-x1, hh-p1.y, p2.x-x1, hh-p2.y, p3.x-x1, hh-p3.y);
610 		}
611 		else if(q.type==3 && cp.r[3])
612 		{
613 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3), &p4=gr->GetPnt(q.n4);
614 			mgl_printf(fp, gz, "<g fill=\"#%02x%02x%02x\" opacity=\"%g\">\n", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]),cp.r[3]/255.);
615 			mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g L %g %g Z\"/> </g>\n", p1.x-x1, hh-p1.y, p2.x-x1, hh-p2.y, p4.x-x1, hh-p4.y, p3.x-x1, hh-p3.y);
616 		}
617 		else if(q.type==4)
618 		{
619 			float phi = gr->GetGlyphPhi(gr->GetPnt(q.n2),q.w);
620 			if(mgl_isnan(phi))	continue;
621 			mreal ss = q.s/2, xx = p1.u, yy = p1.v, zz = q.p;
622 			if(q.n3&8)	// this is "line"
623 			{
624 				mgl_printf(fp, gz, "<g transform=\"translate(%g,%g) scale(%.3g,%.3g) rotate(%g)\"", p1.x-x1, hh-p1.y, ss, -ss, -phi);
625 				if(q.n3&4)
626 					mgl_printf(fp, gz, " stroke=\"#%02x%02x%02x\">", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
627 				else
628 					mgl_printf(fp, gz, " fill=\"#%02x%02x%02x\">", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
629 				mreal dy = 0.004,f=fabs(zz);
630 				mgl_printf(fp, gz, "<path d=\"M %g %g L %g %g L %g %g L %g %g\"/></g>\n", xx,yy+dy, xx+f,yy+dy, xx+f,yy-dy, xx,yy-dy);
631 			}
632 			else
633 			{
634 				ss *= zz;
635 				mgl_printf(fp, gz, "<g transform=\"translate(%g,%g) scale(%.3g,%.3g) rotate(%g)\"", p1.x-x1, hh-p1.y, ss, -ss, -q.w);
636 				if(q.n3&4)
637 					mgl_printf(fp, gz, " stroke=\"#%02x%02x%02x\">", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
638 				else
639 					mgl_printf(fp, gz, " fill=\"#%02x%02x%02x\">", int(cp.r[0]),int(cp.r[1]),int(cp.r[2]));
640 				mgl_printf(fp, gz, "<use x=\"%g\" y=\"%g\" xlink:href=\"#%c%c_%04x\"/></g>\n", xx/zz, yy/zz, q.n3&1?'b':'n', q.n3&2?'i':'n', q.n4);
641 			}
642 		}
643 	}
644 
645 	for(long i=0;i<gr->GetPrmNum();i++)
646 	{	mglPrim &q=gr->GetPrm(i);	if(q.type==-1)	q.type = 1;	}
647 	mgl_printf(fp, gz, "</g></svg>");
648 	if(strcmp(fname,"-"))	{	if(gz)	gzclose((gzFile)fp);	else	fclose((FILE *)fp);	}
649 	setlocale(LC_NUMERIC, loc.c_str());
650 }
mgl_write_svg_(uintptr_t * gr,const char * fname,const char * descr,int l,int n)651 void MGL_EXPORT mgl_write_svg_(uintptr_t *gr, const char *fname,const char *descr,int l,int n)
652 {	char *s=new char[l+1];	memcpy(s,fname,l);	s[l]=0;
653 	char *d=new char[n+1];	memcpy(d,descr,n);	d[n]=0;
654 	mgl_write_svg(_GR_,s,d);	delete []s;		delete []d;	}
655 //-----------------------------------------------------------------------------
mgl_write_tex(HMGL gr,const char * fname,const char * descr)656 void MGL_EXPORT mgl_write_tex(HMGL gr, const char *fname,const char *descr)
657 {
658 	if(gr->GetPrmNum()<1)	return;
659 	_Gr_->clr(MGL_FINISHED);	_Gr_->PreparePrim(1);
660 
661 	FILE *fp = fopen(fname,"w");
662 	if(!fp)		{	gr->SetWarn(mglWarnOpen,fname);	return;	}
663 	const std::string loc = setlocale(LC_NUMERIC, "C");	fwide(fp,1);
664 	fwprintf(fp, L"%% Created by MathGL library\n%% Title: %s\n\n",descr?descr:fname);
665 	// provide marks
666 	fwprintf(fp, L"\\providecommand{\\mglp}[4]{\\draw[#3] (#1-#4, #2) -- (#1+#4,#2) (#1,#2-#4) -- (#1,#2+#4);}\n");
667 	fwprintf(fp, L"\\providecommand{\\mglx}[4]{\\draw[#3] (#1-#4, #2-#4) -- (#1+#4,#2+#4) (#1+#4,#2-#4) -- (#1-#4,#2+#4);}\n");
668 	fwprintf(fp, L"\\providecommand{\\mgls}[4]{\\draw[#3] (#1-#4, #2-#4) -- (#1+#4,#2-#4) -- (#1+#4,#2+#4) -- (#1-#4,#2+#4) -- cycle;}\n");
669 	fwprintf(fp, L"\\providecommand{\\mglS}[4]{\\fill[#3] (#1-#4, #2-#4) -- (#1+#4,#2-#4) -- (#1+#4,#2+#4) -- (#1-#4,#2+#4) -- cycle;}\n");
670 	fwprintf(fp, L"\\providecommand{\\mgld}[4]{\\draw[#3] (#1, #2-#4) -- (#1+#4,#2) -- (#1,#2+#4) -- (#1-#4,#2) -- cycle;}\n");
671 	fwprintf(fp, L"\\providecommand{\\mglD}[4]{\\fill[#3] (#1, #2-#4) -- (#1+#4,#2) -- (#1,#2+#4) -- (#1-#4,#2) -- cycle;}\n");
672 	fwprintf(fp, L"\\providecommand{\\mglv}[4]{\\draw[#3] (#1-#4, #2+#4/2) -- (#1+#4,#2+#4/2) -- (#1,#2-#4) -- cycle;}\n");
673 	fwprintf(fp, L"\\providecommand{\\mglV}[4]{\\fill[#3] (#1-#4, #2+#4/2) -- (#1+#4,#2+#4/2) -- (#1,#2-#4) -- cycle;}\n");
674 	fwprintf(fp, L"\\providecommand{\\mglt}[4]{\\draw[#3] (#1-#4, #2-#4/2) -- (#1+#4,#2-#4/2) -- (#1,#2+#4) -- cycle;}\n");
675 	fwprintf(fp, L"\\providecommand{\\mglT}[4]{\\fill[#3] (#1-#4, #2-#4/2) -- (#1+#4,#2-#4/2) -- (#1,#2+#4) -- cycle;}\n");
676 	fwprintf(fp, L"\\providecommand{\\mgll}[4]{\\draw[#3] (#1+#4/2, #2-#4) -- (#1+#4/2,#2+#4) -- (#1-#4,#2) -- cycle;}\n");
677 	fwprintf(fp, L"\\providecommand{\\mglL}[4]{\\fill[#3] (#1+#4/2, #2-#4) -- (#1+#4/2,#2+#4) -- (#1-#4,#2) -- cycle;}\n");
678 	fwprintf(fp, L"\\providecommand{\\mglr}[4]{\\draw[#3] (#1-#4/2, #2-#4) -- (#1-#4/2,#2+#4) -- (#1+#4,#2) -- cycle;}\n");
679 	fwprintf(fp, L"\\providecommand{\\mglR}[4]{\\fill[#3] (#1-#4/2, #2-#4) -- (#1-#4/2,#2+#4) -- (#1+#4,#2) -- cycle;}\n");
680 	fwprintf(fp, L"\\providecommand{\\mglR}[4]{\\draw[#3] (#1, #2-#4) -- (#1,#2) -- (#1-#4,#2+#4) (#1,#2) -- (#1+#4,#2+#4);}\n");
681 	fwprintf(fp, L"\\providecommand{\\mgla}[4]{\\draw[#3] (#1-#4, #2) -- (#1+#4,#2) (#1-0.6*#4,#2-0.8*#4) -- (#1+0.6*#4,#2+0.8*#4) (#1-0.6*#4,#2+0.8*#4) -- (#1+0.6*#4,#2-0.8*#4);}\n");
682 	fwprintf(fp, L"\\providecommand{\\mglY}[4]{\\draw[#3] (#1, #2-#4) -- (#1,#2) (#1-#4,#2+#4) -- (#1,#2) (#1+#4,#2+#4) -- (#1,#2);}\n");
683 	fwprintf(fp, L"\\providecommand{\\mglo}[4]{\\draw[#3] (#1, #2) circle (#4);}\n");
684 	fwprintf(fp, L"\\providecommand{\\mglO}[4]{\\fill[#3] (#1, #2) circle (#4);}\n");
685 	fwprintf(fp, L"\\providecommand{\\mglc}[3]{\\draw[#3] (#1, #2) circle (%g);}\n\n", 4e-4*gr->mark_size());
686 	fwprintf(fp, L"\\begin{tikzpicture}\n");
687 
688 	// write primitives first
689 	mreal wp=-1;
690 	int st=0;
691 	mglRGBA cp;
692 	char cname[128];
693 
694 	for(long i=0;i<gr->GetPrmNum();i++)
695 	{
696 		const mglPrim &q = gr->GetPrm(i);
697 		if(q.type<0)	continue;	// q.n1>=0 always
698 		cp.c = _Gr_->GetPrmCol(i);
699 		snprintf(cname,128,"color={rgb,255:red,%d;green,%d;blue,%d}",cp.r[0],cp.r[1],cp.r[2]);	cname[127]=0;
700 
701 		const mglPnt &p1=gr->GetPnt(q.n1);
702 		mreal x=p1.x/100,y=p1.y/100,s=q.s/100;
703 		if(q.type==0)
704 		{
705 			if(!strchr("xsSoO",q.n4))	s *= 1.1;
706 			wp = 1;
707 			switch(q.n4)	// NOTE: no thickness for marks in TeX
708 			{
709 				case 'P':
710 					fwprintf(fp, L"\\mglp{%.4g}{%.4g}{%s}{%.4g} \\mgls{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s,x,y,cname,s);	break;
711 				case 'X':
712 					fwprintf(fp, L"\\mglx{%.4g}{%.4g}{%s}{%.4g} \\mgls{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s,x,y,cname,s);	break;
713 				case 'C':
714 					fwprintf(fp, L"\\mglc{%.4g}{%.4g}{%s}{%.4g} \\mglo{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s,x,y,cname,s);	break;
715 				case '+':	fwprintf(fp, L"\\mglp{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
716 				case 'x':	fwprintf(fp, L"\\mglx{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
717 				case 's':	fwprintf(fp, L"\\mgls{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
718 				case 'S':	fwprintf(fp, L"\\mglS{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
719 				case 'd':	fwprintf(fp, L"\\mgld{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
720 				case 'D':	fwprintf(fp, L"\\mglD{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
721 				case '^':	fwprintf(fp, L"\\mglt{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
722 				case 'T':	fwprintf(fp, L"\\mglT{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
723 				case 'v':	fwprintf(fp, L"\\mglv{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
724 				case 'V':	fwprintf(fp, L"\\mglV{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
725 				case '<':	fwprintf(fp, L"\\mgll{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
726 				case 'L':	fwprintf(fp, L"\\mglL{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
727 				case '>':	fwprintf(fp, L"\\mglr{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
728 				case 'R':	fwprintf(fp, L"\\mglR{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
729 				case 'Y':	fwprintf(fp, L"\\mglY{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
730 				case 'o':	fwprintf(fp, L"\\mglo{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
731 				case 'O':	fwprintf(fp, L"\\mglO{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
732 				case '*':	fwprintf(fp, L"\\mgla{%.4g}{%.4g}{%s}{%.4g}\n", x,y,cname,s);	break;
733 				default:	fwprintf(fp, L"\\mglc{%.4g}{%.4g}{%s}\n", x,y,cname);	break;
734 			}
735 		}
736 		else if(q.type==2 && cp.r[3])
737 		{
738 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3);
739 			if(cp.r[3]<255)
740 				fwprintf(fp, L"\\fill[%s, fill opacity=%.4g] (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- cycle;\n", cname,cp.r[3]/255., x,y, p2.x/100,p2.y/100, p3.x/100,p3.y/100);
741 			else
742 				fwprintf(fp, L"\\fill[%s, fill] (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- cycle;\n", cname, x,y, p2.x/100,p2.y/100, p3.x/100,p3.y/100);
743 		}
744 		else if(q.type==3 && cp.r[3])
745 		{
746 			const mglPnt &p2=gr->GetPnt(q.n2), &p3=gr->GetPnt(q.n3), &p4=gr->GetPnt(q.n4);
747 			if(cp.r[3]<255)
748 				fwprintf(fp, L"\\fill[%s, fill opacity=%.4g] (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- cycle;\n", cname,cp.r[3]/255., x,y, p2.x/100,p2.y/100, p4.x/100,p4.y/100, p3.x/100,p3.y/100);
749 			else
750 				fwprintf(fp, L"\\fill[%s, fill] (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- (%.4g,%.4g) -- cycle;\n", cname, x,y, p2.x/100,p2.y/100, p4.x/100,p4.y/100, p3.x/100,p3.y/100);
751 
752 		}
753 		else if(q.type==1)	// lines
754 		{
755 			//const char *dash[]={"", "8 8","4 4","1 3","7 4 1 4","3 2 1 2"};
756 			const char *w[]={"semithick","thick","very thick","ultra thick"};
757 			int iw=int(q.w-0.5);	if(iw>3)	iw=3;
758 			if(iw<0)	fwprintf(fp,L"\\draw[%s] ",cname);
759 			else		fwprintf(fp,L"\\draw[%s,%s] ",cname,w[iw]);
760 			// TODO: add line dashing
761 			wp = q.w>1  ? q.w:1;	st = q.n3;
762 			std::vector<long> ids = put_line(gr,i,wp,cp.c,st);
763 			for(size_t j=0;j<ids.size();j++)
764 			{
765 				const mglPnt &p = gr->GetPnt(ids[j]);
766 				float x0 = p.x, y0 = p.y;
767 				fwprintf(fp, j==0?L"(%.4g,%.4g)":L" -- (%.4g,%.4g)",0.01*x0,y0*0.01);
768 			}
769 			fwprintf(fp, L";\n");
770 		}
771 		else if(q.type==6 && mgl_isnum(q.p))	// text
772 		{
773 			const mglText &t = gr->GetPtx(q.n3);
774 			mreal dy = q.w*cos(q.p*M_PI/180)/100, dx = q.w*sin(q.p*M_PI/180)/100;
775 			int f,a;	mglGetStyle(t.stl.c_str(), &f, &a);
776 			std::string ss=cname;
777 			if((a&3)==0)	ss.append(",anchor=base west");
778 			if((a&3)==1)	ss.append(",anchor=base");
779 			if((a&3)==2)	ss.append(",anchor=base east");
780 //			if(f&MGL_FONT_ITAL)	ss.append(",font=\\itshape");
781 //			if(f&MGL_FONT_BOLD)	ss.append(",font=\\bfshape");
782 			if(t.text.find('\\')!=std::string::npos || t.text.find('{')!=std::string::npos || t.text.find('_')!=std::string::npos || t.text.find('^')!=std::string::npos)
783 				fwprintf(fp,L"\\draw[%s] (%.4g,%.4g) node[rotate=%.2g]{$%ls$};\n", ss.c_str(),x-dx,y-dy, -q.p, t.text.c_str());
784 			else
785 				fwprintf(fp,L"\\draw[%s] (%.4g,%.4g) node[rotate=%.2g]{%ls};\n", ss.c_str(),x-dx,y-dy, -q.p, t.text.c_str());
786 		}
787 	}
788 	fwprintf(fp, L"\\end{tikzpicture}\n");
789 	for(long i=0;i<gr->GetPrmNum();i++)
790 	{	mglPrim &q=gr->GetPrm(i);	if(q.type==-1)	q.type = 1;	}
791 	fclose(fp);
792 	setlocale(LC_NUMERIC, loc.c_str());
793 
794 	// provide main file for viewing figure
795 	fp=fopen("mglmain.tex","wt");
796 	if(fp)
797 	{
798 		fprintf(fp, "%% this file just show figure\n");
799 		fprintf(fp, "\\documentclass{article}\n\\usepackage{tikz}\n");
800 		fprintf(fp, "\\usepackage[T2A]{fontenc}\n\\usepackage[utf8]{inputenc}\n");
801 		fprintf(fp, "\\begin{document}\n\\input{%s}\n\\end{document}\n",fname);
802 		fclose(fp);
803 	}
804 }
mgl_write_tex_(uintptr_t * gr,const char * fname,const char * descr,int l,int n)805 void MGL_EXPORT mgl_write_tex_(uintptr_t *gr, const char *fname,const char *descr,int l,int n)
806 {	char *s=new char[l+1];	memcpy(s,fname,l);	s[l]=0;
807 	char *d=new char[n+1];	memcpy(d,descr,n);	d[n]=0;
808 	mgl_write_tex(_GR_,s,d);	delete []s;		delete []d;	}
809 //-----------------------------------------------------------------------------
810