1 |
/* |
2 |
tgf-gostream.cc, part of |
3 |
|
4 |
treegraph |
5 |
|
6 |
Tree formatting program |
7 |
|
8 |
Generates vector graphics (SVG,EPS) from .tgf-tree description files. |
9 |
|
10 |
Copyright (c) 2003-04 by Joern Mueller |
11 |
|
12 |
|
13 |
This program is free software; you can redistribute it and/or |
14 |
modify it under the terms of the GNU General Public License |
15 |
as published by the Free Software Foundation; either version 2 |
16 |
of the License, or (at your option) any later version. |
17 |
|
18 |
This program is distributed in the hope that it will be useful, |
19 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 |
GNU General Public License for more details. |
22 |
|
23 |
You should have received a copy of the GNU General Public License |
24 |
along with this program (GPL.html); if not, write to the Free Software |
25 |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
26 |
(also: http://www.gnu.org) |
27 |
*/ |
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
#include "tgf-gostream.h" |
36 |
#include "tgf-tree.h" |
37 |
#include "tgf-main.h" |
38 |
#include <cstdio> |
39 |
|
40 |
/* |
41 |
** |
42 |
** |
43 |
** |
44 |
** gostream |
45 |
** |
46 |
** |
47 |
** |
48 |
*/ |
49 |
|
50 |
void gostream::write_nodelabel(const fPoint& pos, const std::string& str) |
51 |
{ |
52 |
|
53 |
fontstyle tmp; |
54 |
tmp.fontsize=12; |
55 |
tmp.face=fs_bold; |
56 |
write_text(fstring(str,tmp),pos); |
57 |
} |
58 |
|
59 |
|
60 |
/* |
61 |
** |
62 |
** |
63 |
** |
64 |
** svg_ostream |
65 |
** |
66 |
** |
67 |
** |
68 |
*/ |
69 |
|
70 |
const float PiOver2=asin(1.0); |
71 |
|
72 |
|
73 |
void svg_ostream::moveto(float x,float y) |
74 |
{ |
75 |
s << 'M'<< x << ' ' << y; |
76 |
|
77 |
} |
78 |
|
79 |
|
80 |
void svg_ostream::lineto(float x,float y) |
81 |
{ |
82 |
s << 'L' << x << ' ' << y; |
83 |
|
84 |
} |
85 |
|
86 |
void svg_ostream::curveto(float x1,float y1, float x2,float y2, float x3,float y3) |
87 |
{ |
88 |
s<< 'C' << x1 <<' ' << y1 << ' '<< x2 <<' ' << y2 << ' '<< x3 <<' ' << y3; |
89 |
} |
90 |
|
91 |
void svg_ostream::write_head(const fRect& paper, const fRect& bbox, float lthick) |
92 |
{ |
93 |
s << "<?xml version=\"1.0\" standalone=\"no\"?>\n"; |
94 |
s << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"; |
95 |
s << "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"; |
96 |
//hier noch richtige Strichbreite einsetzen: |
97 |
s << "<svg style=\"fill:none;stroke:#000000;stroke-linecap:round;stroke-width:"<<lthick; |
98 |
s << ";font-family:Helvetica; font-style:normal; font-weight:normal; fill-opacity:1; fill-rule:evenodd; stroke-opacity:1; \"\n"; |
99 |
s << "width=\""<<paper.right<<"\"\n"; |
100 |
s << "height=\""<<paper.bottom<<"\"\n"; |
101 |
s << "transform=\"translate("<<0.5*(paper.right-bbox.right) <<","<< 0.5*(paper.bottom-bbox.bottom)<< ")\"\n"; |
102 |
s << ">\n"; |
103 |
|
104 |
|
105 |
} |
106 |
|
107 |
void svg_ostream::write_foot(void) |
108 |
{ |
109 |
s << "\n</svg>\n"; |
110 |
s.flush(); |
111 |
} |
112 |
|
113 |
void svg_ostream::write_text(const fstring& str, const fPoint& p, float angle) |
114 |
{ |
115 |
s << "<text style=\"fill:#000000;stroke:none;font-size:"<<str.fontsize<<"px"; |
116 |
if(str.face==fs_italic) |
117 |
s << ";font-weight:normal;font-style:italic"; |
118 |
else if(str.face==fs_bold) |
119 |
s << ";font-weight:bold;font-style:normal"; |
120 |
// else aus << "font-weight:normal;font-style:normal";//Standard: plain |
121 |
s << "\"\nx=\"0\" y=\"0\""; |
122 |
s << "\ntransform=\"matrix("; |
123 |
float c1=cos(angle),s1=sin(angle); |
124 |
s<<c1<<","<< s1<<","<<-s1<<","<<c1<<","; |
125 |
s<<p.x<<","<<p.y<<")\""; |
126 |
s <<">"; |
127 |
s << str; |
128 |
s << "</text>\n"; |
129 |
} |
130 |
|
131 |
void svg_ostream::write_rgabel(float l1,float y1 ,float l2,float y2,float xx,float maxr) |
132 |
{ |
133 |
float hdmt=0.5*maxr; |
134 |
|
135 |
s << "<path d=\""; |
136 |
s << "M" << xx+l1 << " " << y1; |
137 |
s << "H" << xx+maxr; |
138 |
s << "C" << xx+hdmt << " " << y1 << " " << xx << " " << y1+hdmt; |
139 |
s << " " << xx << " " << y1+maxr; |
140 |
s << "V" << y2-maxr; |
141 |
s << "C" << xx << " " << y2-hdmt << " " << xx+hdmt << " " << y2; |
142 |
s << " " << xx+maxr << " " << y2; |
143 |
s << "H" << xx+l2; |
144 |
s << "\" />\n";//path |
145 |
|
146 |
} |
147 |
|
148 |
void svg_ostream::write_egabel(float l1,float y1,float l2,float y2,float xx) |
149 |
{ |
150 |
s << "<path d=\""; |
151 |
s << "M" << xx+l1 << " " << y1; |
152 |
s << "H" << xx; |
153 |
s << "V" << y2; |
154 |
s << "H" << xx+l2; |
155 |
s << "\" />\n";//path |
156 |
} |
157 |
|
158 |
void svg_ostream::write_hline(float x1,float y1,float l) |
159 |
{ |
160 |
s << "<line x1=\"" << x1 << "\" y1=\"" << y1 << "\" "; |
161 |
s << "x2=\"" << x1+l << "\" y2=\"" << y1 << "\" />\n"; |
162 |
|
163 |
} |
164 |
|
165 |
void svg_ostream::write_klammer(const fstring& txt,float y1, |
166 |
float y2,float x,float ext, int style) |
167 |
{ |
168 |
|
169 |
y1-=ext; |
170 |
y2+=ext; |
171 |
|
172 |
if(!(style&klammer::eckig)){ |
173 |
|
174 |
s << "<path style=\"stroke-linejoin:round;stroke-linecap:round;stroke-width:0.8\"\n"; |
175 |
|
176 |
float ll=0.5*fabs(y2-y1); |
177 |
if(ll>20.0) ll=20.0; |
178 |
float frc=0.035*ll; |
179 |
|
180 |
float md=0.5*(y1+y2); |
181 |
s << "d=\""; |
182 |
|
183 |
moveto(x,y2); |
184 |
curveto(x+3,y2-4*frc, x+3,y2-4*frc, x+3,y2-8*frc); |
185 |
lineto(x+3,md+7*frc); |
186 |
curveto(x+3,md+5*frc, x+3,md+2*frc, x+6,md); |
187 |
|
188 |
moveto(x+6,md); |
189 |
curveto(x+3.5,md+3*frc, x+3.5,md+5*frc, x+3.5,md+7*frc); |
190 |
lineto(x+3.5,y2-8*frc); |
191 |
curveto(x+3.5,y2-5*frc, x+3.5,y2-3*frc, x,y2); |
192 |
|
193 |
moveto(x,y1); |
194 |
curveto(x+3,y1+4*frc, x+3,y1+4*frc, x+3,y1+8*frc); |
195 |
lineto(x+3,md-7*frc); |
196 |
curveto(x+3,md-5*frc, x+3,md-2*frc, x+6,md); |
197 |
|
198 |
moveto(x+6,md); |
199 |
curveto(x+3.5,md-3*frc, x+3.5,md-5*frc, x+3.5,md-7*frc); |
200 |
lineto(x+3.5,y1+8*frc); |
201 |
curveto(x+3.5,y1+5*frc, x+3.5,y1+3*frc, x,y1); |
202 |
s<<"\"\n/>"; |
203 |
|
204 |
} else { |
205 |
s<< "<g>"; |
206 |
s << "<path style=\"stroke-linejoin:round;stroke-linecap:butt;stroke-width:0.8\" "; |
207 |
s << "d=\"M"<<x<<' '<<y1; |
208 |
s << " H"<<x+4.0<<"V"<<y2<<"H"<<x<<"\" />\n"; |
209 |
s << "<path style=\"stroke-linecap:butt;stroke-width:1\" "; |
210 |
s << "d=\"M"<<x+4.0-0.7<<' '<<y1<<"V"<<y2<<"\" />\n"; |
211 |
s<<"</g>"; |
212 |
} |
213 |
|
214 |
|
215 |
|
216 |
|
217 |
fPoint m; |
218 |
if(style&klammer::gedreht){ |
219 |
m.x=x+8.0; |
220 |
m.y=(y1+y2)/2+0.33*txt.fontsize; |
221 |
} else { |
222 |
m.x=x+8.0+txt.fontsize; |
223 |
m.y=(y1+y2)/2+0.5*helv_stringwidth(txt); |
224 |
} |
225 |
write_text(txt,m,(style&klammer::gedreht)?0:-PiOver2); |
226 |
|
227 |
|
228 |
} |
229 |
|
230 |
|
231 |
|
232 |
void svg_ostream::write_rule(const fPoint& pos, float z,float wd) |
233 |
{ |
234 |
s<<"<g style=\"stroke-linecap:butt;stroke-width:1\">\n"; |
235 |
write_hline(pos.x,pos.y,wd); |
236 |
s<<"<g style=\"stroke-width:0.3\">\n"; |
237 |
|
238 |
int i; |
239 |
float x1,l; |
240 |
for(int i=0; i<=10; i++){ |
241 |
x1=pos.x+i*0.1*wd; |
242 |
l=2.0; |
243 |
if(i==0 || i==10) |
244 |
l=3.0; |
245 |
else if(i==5) |
246 |
l=2.5; |
247 |
s << "<line x1=\"" << x1 << "\" y1=\"" << pos.y-l << "\" "; |
248 |
s << "x2=\"" << x1 << "\" y2=\"" << pos.y+l << "\" />\n"; |
249 |
} |
250 |
s<< "</g></g>\n"; |
251 |
|
252 |
fontstyle tmp; |
253 |
tmp.fontsize=12; |
254 |
tmp.face=fs_bold; |
255 |
char st[10]; |
256 |
sprintf(st,"%.5g",z); |
257 |
fstring ft(st,tmp); |
258 |
fPoint m; |
259 |
m.x=pos.x+wd/2-0.5*helv_stringwidth(ft); |
260 |
m.y=pos.y+16; |
261 |
write_text(ft,m); |
262 |
|
263 |
} |
264 |
|
265 |
void svg_ostream::write_arrow(const fPoint& pos, float angle_deg) |
266 |
{ |
267 |
cout << "Arrow geht noch nicht.\n"; |
268 |
const float htief=9,hhbr=3.4,kerb=2.4; |
269 |
s<<"<g transform=\"translate("<<pos.x<<","<<pos.y<<")\">\n"; |
270 |
s<<"<g transform=\"rotate("<<angle_deg<<")\">\n"; |
271 |
s << "<path style=\"stroke-linejoin:round;stroke-width:0.8\" "; |
272 |
s << "d=\"M0 0L"<<htief<<" "<<hhbr; |
273 |
// s << " H"<<x+4.0<<"V"<<y2+ext<<"H"<<x<<"\" />\n"; |
274 |
|
275 |
s<<"</g></g>"; |
276 |
} |
277 |
|
278 |
void svg_ostream::write_nodelabel(const fPoint& pos, const std::string& str) |
279 |
{ |
280 |
s<<"<g style=\"fill:#FF0000\">"; |
281 |
gostream::write_nodelabel(pos,str); |
282 |
s << "</g>\n"; |
283 |
|
284 |
} |
285 |
|
286 |
|
287 |
|
288 |
/* |
289 |
** |
290 |
** |
291 |
** |
292 |
** ps_ostream |
293 |
** |
294 |
** |
295 |
** |
296 |
*/ |
297 |
ps_ostream::ps_ostream(std::ostream& s1) : gostream(s1) |
298 |
{ |
299 |
finited=false; |
300 |
|
301 |
} |
302 |
|
303 |
void ps_ostream::setfont(const fontstyle& f) |
304 |
//Reduces number of calls to scalefont/Makefont |
305 |
{ |
306 |
bool ch=(!finited||(f.face!=laststyle.face)||(f.fontsize!=laststyle.fontsize)); |
307 |
if(ch){ |
308 |
const float& q=f.fontsize; |
309 |
if(f.face==fs_italic){ |
310 |
s << "/Helvetica findfont ["<<q<<" 0 "<<0.25*q<<" "<<q<< " 0 0] makefont setfont\n"; |
311 |
} else if(f.face==fs_bold) |
312 |
s << "/Helvetica-Bold findfont "<<q<<" scalefont setfont\n"; |
313 |
else |
314 |
s << "/Helvetica findfont "<<q<<" scalefont setfont\n"; |
315 |
laststyle=f; |
316 |
finited=true; |
317 |
} |
318 |
} |
319 |
|
320 |
|
321 |
void ps_ostream::moveto(float x,float y) |
322 |
{ |
323 |
s << x << ' ' << ty(y) << " M "; |
324 |
|
325 |
} |
326 |
|
327 |
|
328 |
void ps_ostream::lineto(float x,float y) |
329 |
{ |
330 |
s << x << ' ' << ty(y) << " L "; |
331 |
|
332 |
} |
333 |
|
334 |
void ps_ostream::curveto(float x1,float y1, float x2,float y2, float x3,float y3) |
335 |
{ |
336 |
s<< x1 <<' ' << ty(y1) << ' '<< x2 <<' ' << ty(y2) << ' '<< x3 <<' ' << ty(y3) << " C "; |
337 |
} |
338 |
|
339 |
|
340 |
|
341 |
void ps_ostream::write_head(const fRect& paper, const fRect& bbox, float lthick) |
342 |
{ |
343 |
|
344 |
//bbox auf paper zentrieren. |
345 |
yh=bbox.bottom; //fuer rhd.-Koordinaten unter PostScript |
346 |
fPoint st(0.5*(paper.right-bbox.right),0.5*(paper.bottom-bbox.bottom)); |
347 |
s << "%!PS-Adobe-3.0 EPSF-3.0\n"; |
348 |
s << "%%Creator: treegraph " TGFVERSION "\n"; |
349 |
s << "%%BoundingBox: " << int(st.x) << ' ' << int(st.y) << ' '; |
350 |
s << int(st.x+bbox.right+1.0) << ' ' << int(st.y+bbox.bottom+1.0) <<endl; |
351 |
s << "%%DocumentNeededFonts: Helvetica Helvetica-Bold\n";//Italic wird aus Plain konstruiert |
352 |
s << "%%EndComments\n"; |
353 |
s << "%%BeginProlog\n"; |
354 |
// s << "150 dict begin "; |
355 |
s << "/M {moveto} bind def /L {lineto} bind def /C {curveto} bind def\n"; |
356 |
s << "/N {newpath} bind def /SW {show} bind def /S {stroke} bind def\n"; |
357 |
s << "/LS {lineto stroke} bind def\n"; |
358 |
s << "/SF {setfont} bind def /GS {gsave} bind def /GR {grestore} bind def\n"; |
359 |
s << st.x <<' '<< st.y<< " translate\n"; |
360 |
s << lthick << " setlinewidth 1 setlinecap\n"; |
361 |
s << "\n%%EndProlog\n"; |
362 |
|
363 |
} |
364 |
|
365 |
void ps_ostream::write_foot(void) |
366 |
{ |
367 |
s << "\n%%EOF\n"; |
368 |
s.flush(); |
369 |
//Hier noch was wegen dict |
370 |
} |
371 |
|
372 |
void ps_ostream::write_text(const fstring& str, const fPoint& p, float angle) |
373 |
{ |
374 |
setfont(str); |
375 |
s << p.x << " " << ty(p.y) << " M\n"; |
376 |
|
377 |
if(angle!=0.0){ |
378 |
s << "GS " << -angle*90/PiOver2 << " rotate "; |
379 |
} |
380 |
|
381 |
s << "("<<str<<") SW\n"; |
382 |
if(angle!=0.0){ |
383 |
s << "GR\n"; |
384 |
} |
385 |
|
386 |
} |
387 |
|
388 |
void ps_ostream::write_rgabel(float l1,float y1 ,float l2,float y2,float xx,float maxr) |
389 |
{ |
390 |
float hdmt=0.5*maxr; |
391 |
// s << "N "; |
392 |
moveto(xx+l1,y1); |
393 |
lineto(xx+maxr,y1); |
394 |
curveto(xx+hdmt,y1,xx,y1+hdmt,xx,y1+maxr); |
395 |
s<<endl; |
396 |
lineto(xx,y2-maxr); |
397 |
curveto(xx,y2-hdmt,xx+hdmt,y2,xx+maxr,y2); |
398 |
lineto(xx+l2,y2); |
399 |
s << "S\n"; |
400 |
|
401 |
|
402 |
} |
403 |
|
404 |
void ps_ostream::write_egabel(float l1,float y1,float l2,float y2,float xx) |
405 |
{ |
406 |
// s << "N "; |
407 |
moveto(xx+l1,y1); |
408 |
lineto(xx,y1); |
409 |
lineto(xx,y2); |
410 |
lineto(xx+l2,y2); |
411 |
s << "S\n"; |
412 |
} |
413 |
|
414 |
void ps_ostream::write_hline(float x1,float y1,float l) |
415 |
{ |
416 |
// s << "N "; |
417 |
moveto(x1,y1); |
418 |
lineto(x1+l,y1); |
419 |
s << "S\n"; |
420 |
} |
421 |
|
422 |
void ps_ostream::write_klammer(const fstring& txt,float y1, |
423 |
float y2,float x,float ext, int style) |
424 |
{ |
425 |
|
426 |
y1-=ext; |
427 |
y2+=ext; |
428 |
|
429 |
s << "GS\n"; |
430 |
|
431 |
if(!(style&klammer::eckig)){ |
432 |
|
433 |
float ll=0.5*fabs(y2-y1); |
434 |
if(ll>20.0) ll=20.0; |
435 |
float frc=0.035*ll; |
436 |
|
437 |
float md=0.5*(y1+y2); |
438 |
s << "0.8 setlinewidth 2 setlinejoin 1 setlinecap N\n"; |
439 |
|
440 |
moveto(x,y2); |
441 |
curveto(x+3,y2-4*frc, x+3,y2-4*frc, x+3,y2-8*frc); |
442 |
lineto(x+3,md+7*frc); |
443 |
curveto(x+3,md+5*frc, x+3,md+2*frc, x+6,md); |
444 |
s << endl; |
445 |
|
446 |
moveto(x+6,md); |
447 |
curveto(x+3.5,md+3*frc, x+3.5,md+5*frc, x+3.5,md+7*frc); |
448 |
lineto(x+3.5,y2-8*frc); |
449 |
curveto(x+3.5,y2-5*frc, x+3.5,y2-3*frc, x,y2); |
450 |
s<<endl; |
451 |
|
452 |
moveto(x,y1); |
453 |
curveto(x+3,y1+4*frc, x+3,y1+4*frc, x+3,y1+8*frc); |
454 |
lineto(x+3,md-7*frc); |
455 |
curveto(x+3,md-5*frc, x+3,md-2*frc, x+6,md); |
456 |
s << endl; |
457 |
|
458 |
moveto(x+6,md); |
459 |
curveto(x+3.5,md-3*frc, x+3.5,md-5*frc, x+3.5,md-7*frc); |
460 |
lineto(x+3.5,y1+8*frc); |
461 |
curveto(x+3.5,y1+5*frc, x+3.5,y1+3*frc, x,y1); |
462 |
|
463 |
} else { |
464 |
|
465 |
s << "0.8 setlinewidth 2 setlinejoin 0 setlinecap N\n"; |
466 |
moveto(x,y1); |
467 |
lineto(x+4.0,y1); |
468 |
lineto(x+4.0,y2); |
469 |
lineto(x,y2); |
470 |
s << "S\n"; |
471 |
s << "1 setlinewidth\n"; |
472 |
moveto(x+4.0-0.7,y1); |
473 |
lineto(x+4.0-0.7,y2); |
474 |
|
475 |
} |
476 |
|
477 |
|
478 |
s << "S GR\n"; |
479 |
|
480 |
fPoint m; |
481 |
if(style&klammer::gedreht){ |
482 |
m.x=x+8.0; |
483 |
m.y=(y1+y2)/2+0.33*txt.fontsize; |
484 |
} else { |
485 |
m.x=x+8.0+txt.fontsize; |
486 |
m.y=(y1+y2)/2+0.5*helv_stringwidth(txt); |
487 |
} |
488 |
write_text(txt,m,(style&klammer::gedreht)?0:-PiOver2); |
489 |
|
490 |
} |
491 |
|
492 |
|
493 |
|
494 |
void ps_ostream::write_rule(const fPoint& pos, float z,float wd) |
495 |
{ |
496 |
|
497 |
s<<"GS 0 setlinecap 1 setlinewidth\n"; |
498 |
write_hline(pos.x,pos.y,wd); |
499 |
s<<"0.3 setlinewidth\n"; |
500 |
|
501 |
int i; |
502 |
float x1,l; |
503 |
for(int i=0; i<=10; i++){ |
504 |
x1=pos.x+i*0.1*wd; |
505 |
l=2.0; |
506 |
if(i==0 || i==10) |
507 |
l=3.0; |
508 |
else if(i==5) |
509 |
l=2.5; |
510 |
// s << "N "; |
511 |
moveto(x1,pos.y-l); |
512 |
lineto(x1,pos.y+l); |
513 |
s<<"S\n"; |
514 |
} |
515 |
s<< "GR\n"; |
516 |
|
517 |
fontstyle tmp; |
518 |
tmp.fontsize=12; |
519 |
tmp.face=fs_bold; |
520 |
char st[10]; |
521 |
sprintf(st,"%.5g",z); |
522 |
fstring ft(st,tmp); |
523 |
fPoint m; |
524 |
m.x=pos.x+wd/2-0.5*helv_stringwidth(ft); |
525 |
m.y=pos.y+16; |
526 |
write_text(ft,m); |
527 |
|
528 |
|
529 |
} |
530 |
|
531 |
void ps_ostream::write_arrow(const fPoint& pos, float angle_deg) |
532 |
{ |
533 |
/* |
534 |
const float htief=9,hhbr=3.4,kerb=2.4; |
535 |
s<<"<g transform=\"translate("<<pos.x<<","<<pos.y<<")\">\n"; |
536 |
s<<"<g transform=\"rotate("<<angle_deg<<")\">\n"; |
537 |
s << "<path style=\"stroke-linejoin:round;stroke-width:0.8\""; |
538 |
s << "d=\"M0 0L"<<htief<<" "<<hhbr; |
539 |
// s << " H"<<x+4.0<<"V"<<y2+ext<<"H"<<x<<"\" />\n"; |
540 |
|
541 |
s<<"</g></g>"; |
542 |
*/ |
543 |
} |
544 |
|
545 |
void ps_ostream::write_nodelabel(const fPoint& pos, const std::string& str) |
546 |
{ |
547 |
s<<"1 0 0 setrgbcolor "; |
548 |
gostream::write_nodelabel(pos,str); |
549 |
s << "0 0 0 setrgbcolor \n"; |
550 |
|
551 |
} |