1 /*****
2 * picture.cc
3 * Andy Hammerlindl 2002/06/06
4 *
5 * Stores a picture as a list of drawElements and handles its output to
6 * PostScript.
7 *****/
8
9 #include "errormsg.h"
10 #include "picture.h"
11 #include "util.h"
12 #include "settings.h"
13 #include "interact.h"
14 #include "drawverbatim.h"
15 #include "drawlabel.h"
16 #include "drawlayer.h"
17 #include "drawsurface.h"
18 #include "drawpath3.h"
19
20 #ifdef __MSDOS__
21 #include "sys/cygwin.h"
22 #endif
23
24 using std::ifstream;
25 using std::ofstream;
26 using vm::array;
27
28 using namespace settings;
29 using namespace gl;
30
~texstream()31 texstream::~texstream() {
32 string texengine=getSetting<string>("tex");
33 bool context=settings::context(texengine);
34 string name;
35 if(!context)
36 name=stripFile(outname());
37 name += "texput.";
38 unlink((name+"aux").c_str());
39 unlink((name+"log").c_str());
40 unlink((name+"out").c_str());
41 if(settings::pdf(texengine)) {
42 unlink((name+"pdf").c_str());
43 unlink((name+"m9").c_str());
44 } else
45 unlink((name+"pbsdat").c_str());
46 if(context) {
47 unlink("cont-new.log");
48 unlink((name+"tex").c_str());
49 unlink((name+"top").c_str());
50 unlink((name+"tua").c_str());
51 unlink((name+"tui").c_str());
52 }
53 }
54
55 namespace camp {
56
57 extern void draw();
58
isIdTransform3(const double * t)59 bool isIdTransform3(const double* t)
60 {
61 return (t == NULL || (t[0]==1 && t[1]==0 && t[2]==0 && t[3]==0 &&
62 t[4]==0 && t[5]==1 && t[6]==0 && t[7]==0 &&
63 t[8]==0 && t[9]==0 && t[10]==1 && t[11]==0 &&
64 t[12]==0 && t[13]==0 && t[14]==0 && t[15]==1));
65 }
66
67 // copy array to 4x4 transform matrix with range checks
copyArray4x4C(double * & dest,const vm::array * a)68 void copyArray4x4C(double*& dest, const vm::array *a)
69 {
70 double tt[16];
71 const size_t n=checkArray(a);
72 const string fourbyfour="4x4 array of doubles expected";
73 if(n != 4) reportError(fourbyfour);
74
75 for(size_t i=0; i < 4; i++) {
76 const vm::array *ai=vm::read<vm::array*>(a,i);
77 const size_t aisize=checkArray(ai);
78 double *tti=tt+4*i;
79 if(aisize == 4) {
80 for(size_t j=0; j < 4; j++)
81 tti[j]=vm::read<double>(ai,j);
82 } else reportError(fourbyfour);
83 }
84
85 copyTransform3(dest,tt);
86 }
87
copyTransform3(double * & d,const double * s,GCPlacement placement)88 void copyTransform3(double*& d, const double* s, GCPlacement placement)
89
90 {
91 if(s != NULL) {
92 if(d == NULL)
93 d=placement == NoGC ? new double[16] : new(placement) double[16];
94 memcpy(d,s,sizeof(double)*16);
95 }
96 }
97
98 // t = s*r
multiplyTransform3(double * & t,const double * s,const double * r)99 void multiplyTransform3(double*& t, const double* s, const double* r)
100 {
101 if(isIdTransform3(s)) {
102 copyTransform3(t,r);
103 } else if(isIdTransform3(r)) {
104 copyTransform3(t,s);
105 } else {
106 t=new(UseGC) double[16];
107 for(size_t i=0; i < 4; i++) {
108 size_t i4=4*i;
109 const double *si=s+i4;
110 const double& s0=si[0];
111 const double& s1=si[1];
112 const double& s2=si[2];
113 const double& s3=si[3];
114 double *ti=t+i4;
115 ti[0]=s0*r[0]+s1*r[4]+s2*r[8]+s3*r[12];
116 ti[1]=s0*r[1]+s1*r[5]+s2*r[9]+s3*r[13];
117 ti[2]=s0*r[2]+s1*r[6]+s2*r[10]+s3*r[14];
118 ti[3]=s0*r[3]+s1*r[7]+s2*r[11]+s3*r[15];
119 }
120 }
121 }
122
xratio(const triple & v)123 double xratio(const triple& v) {return v.getx()/v.getz();}
yratio(const triple & v)124 double yratio(const triple& v) {return v.gety()/v.getz();}
125
126 class matrixstack {
127 mem::stack<const double*> mstack;
128
129 public:
130 // return current transform
T() const131 const double* T() const
132 {
133 if(mstack.empty())
134 return NULL;
135 else
136 return mstack.top();
137 }
138 // we store the accumulated transform of all pushed transforms
push(const double * r)139 void push(const double *r)
140 {
141 double* T3 = NULL;
142 multiplyTransform3(T3,T(),r);
143 mstack.push(T3);
144 }
pop()145 void pop()
146 {
147 if(!mstack.empty())
148 mstack.pop();
149 }
150
151 };
152
texpathmessage()153 const char *texpathmessage() {
154 ostringstream buf;
155 buf << "the directory containing your " << getSetting<string>("tex")
156 << " engine (" << texcommand() << ")";
157 return Strdup(buf.str());
158 }
159
~picture()160 picture::~picture()
161 {
162 }
163
enclose(drawElement * begin,drawElement * end)164 void picture::enclose(drawElement *begin, drawElement *end)
165 {
166 assert(begin);
167 assert(end);
168 nodes.push_front(begin);
169 lastnumber=0;
170 lastnumber3=0;
171
172 for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) {
173 assert(*p);
174 if((*p)->islayer()) {
175 nodes.insert(p,end);
176 ++p;
177 while(p != nodes.end() && (*p)->islayer()) ++p;
178 if(p == nodes.end()) return;
179 nodes.insert(p,begin);
180 }
181 }
182 nodes.push_back(end);
183 }
184
185 // Insert at beginning of picture.
prepend(drawElement * p)186 void picture::prepend(drawElement *p)
187 {
188 assert(p);
189 nodes.push_front(p);
190 lastnumber=0;
191 lastnumber3=0;
192 }
193
append(drawElement * p)194 void picture::append(drawElement *p)
195 {
196 assert(p);
197 nodes.push_back(p);
198 }
199
add(picture & pic)200 void picture::add(picture &pic)
201 {
202 if (&pic == this) return;
203
204 // STL's funny way of copying one list into another.
205 copy(pic.nodes.begin(), pic.nodes.end(), back_inserter(nodes));
206 }
207
208 // Insert picture pic at beginning of picture.
prepend(picture & pic)209 void picture::prepend(picture &pic)
210 {
211 if (&pic == this) return;
212
213 copy(pic.nodes.begin(), pic.nodes.end(), inserter(nodes, nodes.begin()));
214 lastnumber=0;
215 lastnumber3=0;
216 }
217
havelabels()218 bool picture::havelabels()
219 {
220 size_t n=nodes.size();
221 if(n > lastnumber && !labels && getSetting<string>("tex") != "none") {
222 // Check to see if there are any labels yet
223 nodelist::iterator p=nodes.begin();
224 for(size_t i=0; i < lastnumber; ++i) ++p;
225 for(; p != nodes.end(); ++p) {
226 assert(*p);
227 if((*p)->islabel()) {
228 labels=true;
229 break;
230 }
231 }
232 }
233 return labels;
234 }
235
have3D()236 bool picture::have3D()
237 {
238 for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) {
239 assert(*p);
240 if((*p)->is3D())
241 return true;
242 }
243 return false;
244 }
245
havepng()246 bool picture::havepng()
247 {
248 for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) {
249 assert(*p);
250 if((*p)->svgpng())
251 return true;
252 }
253 return false;
254 }
255
havenewpage()256 bool picture::havenewpage()
257 {
258 for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) {
259 assert(*p);
260 if((*p)->isnewpage())
261 return true;
262 }
263 return false;
264 }
265
bounds()266 bbox picture::bounds()
267 {
268 size_t n=nodes.size();
269 if(n == lastnumber) return b_cached;
270
271 if(lastnumber == 0) { // Maybe these should be put into a structure.
272 b_cached=bbox();
273 labelbounds.clear();
274 bboxstack.clear();
275 }
276
277 if(havelabels()) texinit();
278
279 nodelist::iterator p=nodes.begin();
280 processDataStruct& pd=processData();
281
282 for(size_t i=0; i < lastnumber; ++i) ++p;
283 for(; p != nodes.end(); ++p) {
284 assert(*p);
285 (*p)->bounds(b_cached,pd.tex,labelbounds,bboxstack);
286
287 // Optimization for interpreters with fixed stack limits.
288 if((*p)->endclip()) {
289 nodelist::iterator q=p;
290 if(q != nodes.begin()) {
291 --q;
292 assert(*q);
293 if((*q)->endclip())
294 (*q)->save(false);
295 }
296 }
297 }
298
299 lastnumber=n;
300 return b_cached;
301 }
302
bounds3()303 bbox3 picture::bounds3()
304 {
305 size_t n=nodes.size();
306 if(n == lastnumber3) return b3;
307
308 if(lastnumber3 == 0)
309 b3=bbox3();
310
311 matrixstack ms;
312 size_t i=0;
313 for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) {
314 assert(*p);
315 if((*p)->begingroup3())
316 ms.push((*p)->transf3());
317 else if((*p)->endgroup3())
318 ms.pop();
319 else
320 (*p)->bounds(ms.T(),b3);
321 i++;
322 }
323
324 lastnumber3=n;
325 return b3;
326 }
327
ratio(double (* m)(double,double))328 pair picture::ratio(double (*m)(double, double))
329 {
330 bool first=true;
331 pair b;
332 bounds3();
333 double fuzz=Fuzz*(b3.Max()-b3.Min()).length();
334 matrixstack ms;
335 for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) {
336 assert(*p);
337 if((*p)->begingroup3())
338 ms.push((*p)->transf3());
339 else if((*p)->endgroup3())
340 ms.pop();
341 else
342 (*p)->ratio(ms.T(),b,m,fuzz,first);
343 }
344 return b;
345 }
346
texinit()347 void texinit()
348 {
349 drawElement::lastpen=pen(initialpen);
350 processDataStruct &pd=processData();
351 // Output any new texpreamble commands
352 if(pd.tex.isopen()) {
353 if(pd.TeXpipepreamble.empty()) return;
354 texpreamble(pd.tex,pd.TeXpipepreamble,true);
355 pd.TeXpipepreamble.clear();
356 return;
357 }
358
359 bool context=settings::context(getSetting<string>("tex"));
360 string dir=stripFile(outname());
361 string logname;
362 if(!context) logname=dir;
363 logname += "texput.log";
364 const char *cname=logname.c_str();
365 ofstream writeable(cname);
366 if(!writeable)
367 reportError("Cannot write to "+logname);
368 else
369 writeable.close();
370 unlink(cname);
371
372 mem::vector<string> cmd;
373 cmd.push_back(texprogram());
374 if(context) {
375 cmd.push_back("--pipe");
376 } else {
377 if(!dir.empty())
378 cmd.push_back("-output-directory="+dir.substr(0,dir.length()-1));
379 string jobname="texput";
380 if(getSetting<bool>("inlineimage") || getSetting<bool>("inlinetex")) {
381 string name=stripDir(stripExt((outname())));
382 size_t pos=name.rfind("-");
383 if(pos < string::npos) {
384 name=stripExt(name).substr(0,pos);
385 unlink((name+".aux").c_str());
386 jobname=name.substr(0,pos);
387 cmd.push_back("-jobname="+jobname);
388 #ifdef __MSDOS__
389 cmd.push_back("NUL"); // For MikTeX
390 #endif
391 }
392 }
393 cmd.push_back("\\scrollmode");
394 }
395
396 pd.tex.open(cmd,"texpath");
397 pd.tex.wait("\n*");
398 pd.tex << "\n";
399 texdocumentclass(pd.tex,true);
400
401 texdefines(pd.tex,pd.TeXpreamble,true);
402 pd.TeXpipepreamble.clear();
403 }
404
opentex(const string & texname,const string & prefix,bool dvi)405 int opentex(const string& texname, const string& prefix, bool dvi)
406 {
407 string aux=auxname(prefix,"aux");
408 unlink(aux.c_str());
409 bool context=settings::context(getSetting<string>("tex"));
410 mem::vector<string> cmd;
411 cmd.push_back(texprogram());
412 if(dvi)
413 cmd.push_back("-output-format=dvi");
414 if(context) {
415 cmd.push_back("--nonstopmode");
416 cmd.push_back(texname);
417 } else {
418 string dir=stripFile(texname);
419 if(!dir.empty())
420 cmd.push_back("-output-directory="+dir.substr(0,dir.length()-1));
421 cmd.push_back("\\nonstopmode\\input");
422 cmd.push_back(stripDir(texname));
423 }
424
425 bool quiet=verbose <= 1;
426 int status=System(cmd,quiet ? 1 : 0,true,"texpath",texpathmessage());
427 if(!status && getSetting<bool>("twice"))
428 status=System(cmd,quiet ? 1 : 0,true,"texpath",texpathmessage());
429 if(status) {
430 if(quiet) {
431 cmd[1]=context ? "--scrollmode" : "\\scrollmode\\input";
432 System(cmd,0);
433 }
434 }
435 return status;
436 }
437
dvisvgmCommand(mem::vector<string> & cmd,const string & in,const string & out)438 char *dvisvgmCommand(mem::vector<string>& cmd, const string &in, const string& out)
439 {
440 cmd.push_back(getSetting<string>("dvisvgm"));
441 cmd.push_back("-n");
442 cmd.push_back("-v3");
443 string libgs=getSetting<string>("libgs");
444 if(!libgs.empty())
445 cmd.push_back("--libgs="+libgs);
446 // cmd.push_back("--optimize"); // Requires dvisvgm > 2.9.1
447 push_split(cmd,getSetting<string>("dvisvgmOptions"));
448 char *tmpdir=mkdtemp(StrdupMalloc(tempdir+"/dvisvgmXXXXXX"));
449 if(tmpdir)
450 cmd.push_back("--tmpdir="+string(tmpdir));
451 cmd.push_back("-o"+out);
452 cmd.push_back(in);
453 return tmpdir;
454 }
455
rmtmpdir(char * tmpdir)456 void rmtmpdir(char *tmpdir)
457 {
458 if(tmpdir) {
459 recursive_delete(tmpdir);
460 free(tmpdir);
461 }
462 }
463
texprocess(const string & texname,const string & outname,const string & prefix,const pair & bboxshift,bool svg)464 bool picture::texprocess(const string& texname, const string& outname,
465 const string& prefix, const pair& bboxshift,
466 bool svg)
467 {
468 int status=1;
469 ifstream outfile;
470
471 outfile.open(texname.c_str());
472 bool keep=getSetting<bool>("keep");
473
474 if(outfile) {
475 outfile.close();
476
477 status=opentex(texname,prefix);
478 string texengine=getSetting<string>("tex");
479
480 if(status == 0) {
481 string dviname=auxname(prefix,"dvi");
482 mem::vector<string> cmd;
483
484 if(svg) {
485 char *tmpdir=dvisvgmCommand(cmd,dviname,outname);
486 ostringstream buf;
487 bbox B=svgbbox(b,bboxshift);
488 buf << "--bbox="
489 << B.left << "bp "
490 << B.bottom << "bp "
491 << B.right << "bp "
492 << B.top << "bp";
493 cmd.push_back(buf.str());
494 status=System(cmd,0,true,"dvisvgm");
495 rmtmpdir(tmpdir);
496 if(!keep)
497 unlink(dviname.c_str());
498 } else {
499 if(!settings::pdf(texengine)) {
500 string psname=auxname(prefix,"ps");
501 double height=b.top-b.bottom+1.0;
502
503 // Magic dvips offsets:
504 double hoffset=-128.4;
505 double vertical=height;
506 if(!latex(texengine)) vertical += 2.0;
507 double voffset=(vertical < 13.0) ? -137.8+vertical : -124.8;
508 double paperHeight=getSetting<double>("paperheight");
509
510 hoffset += b.left+bboxshift.getx();
511 voffset += paperHeight-height-b.bottom-bboxshift.gety();
512
513 string dvipsrc=getSetting<string>("dir");
514 if(dvipsrc.empty()) dvipsrc=systemDir;
515 dvipsrc += dirsep+"nopapersize.ps";
516 setenv("DVIPSRC",dvipsrc.c_str(),1);
517 string papertype=getSetting<string>("papertype") == "letter" ?
518 "letterSize" : "a4size";
519 cmd.push_back(getSetting<string>("dvips"));
520 cmd.push_back("-R");
521 cmd.push_back("-Pdownload35");
522 cmd.push_back("-D600");
523 cmd.push_back("-O"+String(hoffset)+"bp,"+String(voffset)+"bp");
524 bool ps=havenewpage();
525 if(ps)
526 cmd.push_back("-T"+String(getSetting<double>("paperwidth"))+"bp,"+
527 String(paperHeight)+"bp");
528 else
529 cmd.push_back("-E");
530 push_split(cmd,getSetting<string>("dvipsOptions"));
531 if(ps && getSetting<string>("papertype") != "")
532 cmd.push_back("-t"+papertype);
533 if(verbose <= 1) cmd.push_back("-q");
534 cmd.push_back("-o"+psname);
535 cmd.push_back(dviname);
536 status=System(cmd,0,true,"dvips");
537 if(status == 0) {
538 ifstream fin(psname.c_str());
539 psfile fout(outname,false);
540
541 string s;
542 bool first=true;
543 transform t=shift(bboxshift)*T;
544 bool shift=!t.isIdentity();
545
546 const string beginspecial="TeXDict begin @defspecial";
547 const size_t beginlength=beginspecial.size();
548 const string endspecial="@fedspecial end";
549 const size_t endlength=endspecial.size();
550
551 while(getline(fin,s)) {
552 if (s[0] == '%') {
553 if (s.find("%%DocumentPaperSizes:") == 0)
554 continue;
555
556 if(s.find("%!PS-Adobe-") == 0) {
557 fout.header(!ps);
558 continue;
559 }
560
561 if (first && s.find("%%BoundingBox:") == 0) {
562 bbox box=b;
563 box.shift(bboxshift);
564 if(verbose > 2) BoundingBox(cout,box);
565 fout.BoundingBox(box);
566 first=false;
567 continue;
568 }
569 }
570
571 if (shift) {
572 if (s.compare(0, beginlength, beginspecial) == 0) {
573 fout.verbatimline(s);
574 fout.gsave();
575 fout.concat(t);
576 continue;
577 }
578 if (s.compare(0, endlength, endspecial) == 0) {
579 fout.grestore();
580 fout.verbatimline(s);
581 continue;
582 }
583 }
584
585 // For the default line, output it unchanged.
586 fout.verbatimline(s);
587 }
588 }
589 if(!keep) {
590 unlink(dviname.c_str());
591 unlink(psname.c_str());
592 }
593 }
594 }
595 }
596
597 if(!keep) {
598 unlink(texname.c_str());
599 if(!getSetting<bool>("keepaux"))
600 unlink(auxname(prefix,"aux").c_str());
601 unlink(auxname(prefix,"log").c_str());
602 unlink(auxname(prefix,"out").c_str());
603 if(settings::context(texengine)) {
604 unlink(auxname(prefix,"top").c_str());
605 unlink(auxname(prefix,"tua").c_str());
606 unlink(auxname(prefix,"tuc").c_str());
607 unlink(auxname(prefix,"tui").c_str());
608 unlink(auxname(prefix,"tuo").c_str());
609 }
610 }
611 if(status == 0) return true;
612 }
613 return false;
614 }
615
epstopdf(const string & epsname,const string & pdfname)616 int picture::epstopdf(const string& epsname, const string& pdfname)
617 {
618 mem::vector<string> cmd;
619 cmd.push_back(getSetting<string>("gs"));
620 cmd.push_back("-q");
621 cmd.push_back("-dNOPAUSE");
622 cmd.push_back("-dBATCH");
623 cmd.push_back("-P");
624 if(safe)
625 cmd.push_back("-dSAFER");
626 cmd.push_back("-dALLOWPSTRANSPARENCY"); // Support transparency extensions.
627 cmd.push_back("-sDEVICE=pdfwrite");
628 cmd.push_back("-dEPSCrop");
629 cmd.push_back("-dSubsetFonts=true");
630 cmd.push_back("-dEmbedAllFonts=true");
631 cmd.push_back("-dMaxSubsetPct=100");
632 cmd.push_back("-dPDFSETTINGS=/prepress");
633 cmd.push_back("-dCompatibilityLevel=1.4");
634 if(!getSetting<bool>("autorotate"))
635 cmd.push_back("-dAutoRotatePages=/None");
636 cmd.push_back("-g"+String(max(ceil(getSetting<double>("paperwidth")),1.0))
637 +"x"+String(max(ceil(getSetting<double>("paperheight")),1.0)));
638 cmd.push_back("-dDEVICEWIDTHPOINTS="+String(max(b.right-b.left,3.0)));
639 cmd.push_back("-dDEVICEHEIGHTPOINTS="+String(max(b.top-b.bottom,3.0)));
640 push_split(cmd,getSetting<string>("gsOptions"));
641 cmd.push_back("-sOutputFile="+stripDir(pdfname));
642 if(safe) {
643 cmd.push_back("-c");
644 cmd.push_back(".setsafe");
645 cmd.push_back("-f");
646 }
647 cmd.push_back(stripDir(epsname));
648
649 char *oldPath=NULL;
650 string dir=stripFile(pdfname);
651 if(!dir.empty()) {
652 oldPath=getPath();
653 setPath(dir.c_str());
654 }
655 int status=System(cmd,0,true,"gs","Ghostscript");
656 if(oldPath != NULL)
657 setPath(oldPath);
658 return status;
659 }
660
pdftoeps(const string & pdfname,const string & epsname,bool eps)661 int picture::pdftoeps(const string& pdfname, const string& epsname, bool eps)
662 {
663 mem::vector<string> cmd;
664 cmd.push_back(getSetting<string>("gs"));
665 cmd.push_back("-q");
666 cmd.push_back("-dNOCACHE");
667 cmd.push_back("-dNOPAUSE");
668 cmd.push_back("-dBATCH");
669 cmd.push_back("-P");
670 if(safe)
671 cmd.push_back("-dSAFER");
672 string texengine=getSetting<string>("tex");
673
674 if(eps)
675 cmd.push_back("-sDEVICE="+getSetting<string>("epsdriver"));
676 else
677 cmd.push_back("-sDEVICE=ps2write");
678
679 cmd.push_back("-sOutputFile="+stripDir(epsname));
680 cmd.push_back(stripDir(pdfname));
681
682 char *oldPath=NULL;
683 string dir=stripFile(epsname);
684 if(!dir.empty()) {
685 oldPath=getPath();
686 setPath(dir.c_str());
687 }
688 int status=System(cmd,0,true,"gs","Ghostscript");
689 if(oldPath != NULL)
690 setPath(oldPath);
691 return status;
692 }
693
reloadPDF(const string & Viewer,const string & outname) const694 bool picture::reloadPDF(const string& Viewer, const string& outname) const
695 {
696 static bool needReload=true;
697 static bool haveReload=false;
698
699 string reloadprefix="reload";
700 if(needReload) {
701 needReload=false;
702 string name=getPath()+string("/")+outname;
703 // Write javascript code to redraw picture.
704 runString("settings.tex='pdflatex'; tex('\\ \\pdfannot width 0pt height 0pt { /AA << /PO << /S /JavaScript /JS (try{reload(\""+name+"\");} catch(e) {} closeDoc(this);) >> >> }'); shipout('"+reloadprefix+"',wait=false,view=false);erase();exit();",false);
705 haveReload=true;
706 }
707
708 if(haveReload) {
709 mem::vector<string> cmd;
710 push_command(cmd,Viewer);
711 string pdfreloadOptions=getSetting<string>("pdfreloadOptions");
712 if(!pdfreloadOptions.empty())
713 cmd.push_back(pdfreloadOptions);
714 cmd.push_back(reloadprefix+".pdf");
715 System(cmd,0,false);
716 }
717 return true;
718 }
719
epstosvg(const string & epsname,const string & outname)720 int picture::epstosvg(const string& epsname, const string& outname)
721 {
722 mem::vector<string> cmd;
723 char *tmpdir=dvisvgmCommand(cmd,epsname,outname);
724 cmd.push_back("-E");
725 int status=System(cmd,0,true,"dvisvgm");
726 rmtmpdir(tmpdir);
727 if(!getSetting<bool>("keep"))
728 unlink(epsname.c_str());
729 return status;
730 }
731
pdftosvg(const string & pdfname,const string & outname)732 int picture::pdftosvg(const string& pdfname, const string& outname)
733 {
734 mem::vector<string> cmd;
735 char *tmpdir=dvisvgmCommand(cmd,pdfname,outname);
736 cmd.push_back("--pdf");
737 int status=System(cmd,0,true,"dvisvgm");
738 rmtmpdir(tmpdir);
739 if(status == 0 && !getSetting<bool>("keep"))
740 unlink(pdfname.c_str());
741 return status;
742 }
743
htmlView(string name)744 void htmlView(string name)
745 {
746 mem::vector<string> cmd;
747 push_command(cmd,getSetting<string>("htmlviewer"));
748 #ifdef __MSDOS__
749 ssize_t size=cygwin_conv_path(CCP_POSIX_TO_WIN_A,
750 locateFile(name,true).c_str(),NULL,0);
751 if(size <= 0) return;
752 char filename[size];
753 size=cygwin_conv_path(CCP_POSIX_TO_WIN_A,locateFile(name,true).c_str(),
754 filename,size);
755 cmd.push_back("file://"+string(filename));
756 #else
757 cmd.push_back(locateFile(name,true));
758 #endif
759 push_split(cmd,getSetting<string>("htmlviewerOptions"));
760 System(cmd,2,false);
761 }
762
postprocess(const string & prename,const string & outname,const string & outputformat,bool wait,bool view,bool pdftex,bool epsformat,bool svg)763 bool picture::postprocess(const string& prename, const string& outname,
764 const string& outputformat,
765 bool wait, bool view, bool pdftex,
766 bool epsformat, bool svg)
767 {
768 int status=0;
769 bool pdf=settings::pdf(getSetting<string>("tex"));
770 bool pdfformat=(pdf && outputformat == "") || outputformat == "pdf";
771
772 mem::vector<string> cmd;
773 if(pdftex || !epsformat) {
774 if(pdfformat) {
775 if(pdftex) {
776 status=rename(prename.c_str(),outname.c_str());
777 if(status != 0)
778 reportError("Cannot rename "+prename+" to "+outname);
779 } else status=epstopdf(prename,outname);
780 } else if(epsformat) {
781 if(svg) {
782 bool haveShading=pdf && havepng();
783 if(!haveShading)
784 status=pdftosvg(prename,outname);
785 if(haveShading || status != 0) {
786 // Dvisvgm version < 2.4 doesn't support --pdf
787 // Dvisvgm --pdf doesn't support shading
788 string psname=stripExt(prename)+".ps";
789 status=pdftoeps(prename,psname,false);
790 if(status != 0) return false;
791 status=epstosvg(psname,outname);
792 }
793 epsformat=false;
794 } else
795 status=pdftoeps(prename,outname);
796 } else {
797 double render=fabs(getSetting<double>("render"));
798 if(render == 0) render=1.0;
799 double res=render*72.0;
800 Int antialias=getSetting<Int>("antialias");
801 if(outputformat == "png" && antialias == 2) {
802 cmd.push_back(getSetting<string>("gs"));
803 cmd.push_back("-q");
804 cmd.push_back("-dNOPAUSE");
805 cmd.push_back("-dBATCH");
806 cmd.push_back("-P");
807 cmd.push_back("-sDEVICE=pngalpha");
808 cmd.push_back("-dEPSCrop");
809 if(safe)
810 cmd.push_back("-dSAFER");
811 cmd.push_back("-r"+String(res)+"x"+String(res));
812 push_split(cmd,getSetting<string>("gsOptions"));
813 cmd.push_back("-sOutputFile="+outname);
814 cmd.push_back(prename);
815 status=System(cmd,0,true,"gs","Ghostscript");
816 } else if(!svg && !getSetting<bool>("xasy")) {
817 double expand=antialias;
818 if(expand < 2.0) expand=1.0;
819 res *= expand;
820 cmd.push_back(getSetting<string>("convert"));
821 cmd.push_back("-density");
822 cmd.push_back(String(res)+"x"+String(res));
823 if(expand == 1.0)
824 cmd.push_back("+antialias");
825 push_split(cmd,getSetting<string>("convertOptions"));
826 cmd.push_back("-resize");
827 cmd.push_back(String(100.0/expand)+"%x");
828 if(outputformat == "jpg") cmd.push_back("-flatten");
829 cmd.push_back(prename);
830 cmd.push_back(outputformat+":"+outname);
831 status=System(cmd,0,true,"convert");
832 }
833 }
834 if(!getSetting<bool>("keep"))
835 unlink(prename.c_str());
836 }
837 if(status != 0) return false;
838
839 if(verbose > 0)
840 cout << "Wrote " << outname << endl;
841
842 return display(outname,outputformat,wait,view,epsformat);
843 }
844
display(const string & outname,const string & outputformat,bool wait,bool view,bool epsformat)845 bool picture::display(const string& outname, const string& outputformat,
846 bool wait, bool view, bool epsformat)
847 {
848 int status=0;
849 static mem::map<CONST string,int> pids;
850 bool View=settings::view() && view;
851
852 if(View) {
853 bool pdf=settings::pdf(getSetting<string>("tex"));
854 bool pdfformat=(pdf && outputformat == "") || outputformat == "pdf";
855
856 if(epsformat || pdfformat) {
857 // Check to see if there is an existing viewer for this outname.
858 mem::map<CONST string,int>::iterator p=pids.find(outname);
859 bool running=(p != pids.end());
860 string Viewer=pdfformat ? getSetting<string>("pdfviewer") :
861 getSetting<string>("psviewer");
862 int pid;
863 if(running) {
864 pid=p->second;
865 if(pid)
866 running=(waitpid(pid, &status, WNOHANG) != pid);
867 }
868
869 bool pdfreload=pdfformat && getSetting<bool>("pdfreload");
870 if(running) {
871 // Tell gv/acroread to reread file.
872 if(Viewer == "gv") kill(pid,SIGHUP);
873 else if(pdfreload)
874 reloadPDF(Viewer,outname);
875 } else {
876 mem::vector<string> cmd;
877 push_command(cmd,Viewer);
878 string viewerOptions=getSetting<string>(pdfformat ?
879 "pdfviewerOptions" :
880 "psviewerOptions");
881 if(!viewerOptions.empty())
882 push_split(cmd,viewerOptions);
883 cmd.push_back(outname);
884 status=System(cmd,0,wait,
885 pdfformat ? "pdfviewer" : "psviewer",
886 pdfformat ? "your PDF viewer" : "your PostScript viewer",
887 &pid);
888 if(status != 0) return false;
889
890 if(!wait) pids[outname]=pid;
891
892 if(pdfreload) {
893 // Work around race conditions in acroread initialization script
894 usleep(getSetting<Int>("pdfreloaddelay"));
895 // Only reload if pdf viewer process is already running.
896 if(waitpid(pid, &status, WNOHANG) == pid)
897 reloadPDF(Viewer,outname);
898 }
899 }
900 } else {
901 if(outputformat == "svg" || outputformat == "html")
902 htmlView(outname);
903 else {
904 mem::vector<string> cmd;
905 push_command(cmd,getSetting<string>("display"));
906 cmd.push_back(outname);
907 string application="your "+outputformat+" viewer";
908 status=System(cmd,0,wait,"display",application.c_str());
909 if(status != 0) return false;
910 }
911 }
912 }
913
914 return true;
915 }
916
Outname(const string & prefix,const string & outputformat,bool standardout,string aux="")917 string Outname(const string& prefix, const string& outputformat,
918 bool standardout, string aux="")
919 {
920 return standardout ? "-" : buildname(prefix,outputformat,aux);
921 }
922
shipout(picture * preamble,const string & Prefix,const string & format,bool wait,bool view)923 bool picture::shipout(picture *preamble, const string& Prefix,
924 const string& format, bool wait, bool view)
925 {
926 bool keep=getSetting<bool>("keep");
927
928 string aux="";
929 b=bounds();
930 bool empty=b.empty;
931
932 string outputformat=format.empty() ? defaultformat() : format;
933
934 bool htmlformat=outputformat == "html";
935 if(htmlformat) {
936 outputformat="svg";
937 aux="_";
938 if(view) view=false;
939 else htmlformat=false;
940 }
941
942 bool svgformat=outputformat == "svg";
943
944 string texengine=getSetting<string>("tex");
945 string texengineSave;
946
947 if(!empty && svgformat && texengine == "latex" && havepng()) {
948 texengineSave=texengine;
949 Setting("tex")=texengine="pdflatex";
950 }
951
952 bool usetex=texengine != "none";
953 bool TeXmode=getSetting<bool>("inlinetex") && usetex;
954 bool pdf=settings::pdf(texengine);
955
956 bool standardout=Prefix == "-";
957 string prefix=standardout ? standardprefix : stripExt(Prefix);
958
959 string preformat=nativeformat();
960 bool epsformat=outputformat == "eps";
961 bool pdfformat=pdf || outputformat == "pdf";
962 bool dvi=false;
963 bool svg=svgformat && usetex &&
964 (!have3D() || getSetting<double>("render") == 0.0);
965 if(svg) {
966 if(pdf) epsformat=true;
967 else dvi=true;
968 }
969
970 string outname=Outname(prefix,outputformat,standardout,aux);
971 string epsname=epsformat ? (standardout ? "" : outname) :
972 auxname(prefix,"eps");
973
974 bool Labels=labels || TeXmode;
975
976 if(outputformat == "png" && (b.right-b.left < 1.0 || b.top-b.bottom < 1.0))
977 empty=true;
978
979 if(empty && !Labels) { // Output a null file
980 bbox b;
981 b.left=b.bottom=0;
982 b.right=b.top=1;
983 psfile out(epsname,false);
984 out.prologue(b);
985 out.epilogue();
986 out.close();
987 return postprocess(epsname,outname,outputformat,wait,view,false,
988 epsformat,false);
989 }
990
991 Labels |= svg;
992
993 if(Labels)
994 prefix=cleanpath(prefix);
995
996 string prename=((epsformat && !pdf) || !Labels) ? epsname :
997 auxname(prefix,preformat);
998
999 SetPageDimensions();
1000
1001 pair aligndir=getSetting<pair>("aligndir");
1002 string origin=getSetting<string>("align");
1003
1004 pair bboxshift=(origin == "Z" && epsformat) ?
1005 pair(0.0,0.0) : pair(-b.left,-b.bottom);
1006
1007 if(epsformat) {
1008 bboxshift += getSetting<pair>("offset");
1009 double yexcess=max(getSetting<double>("paperheight")-
1010 (b.top-b.bottom+1.0),0.0);
1011 double xexcess=max(getSetting<double>("paperwidth")-
1012 (b.right-b.left+1.0),0.0);
1013 if(aligndir == pair(0,0)) {
1014 if(origin != "Z" && origin != "B") {
1015 if(origin == "T") bboxshift += pair(0.0,yexcess);
1016 else bboxshift += pair(0.5*xexcess,0.5*yexcess);
1017 }
1018 } else {
1019 double scale=max(fabs(aligndir.getx()),fabs(aligndir.gety()));
1020 if(scale != 0) aligndir *= 0.5/scale;
1021 bboxshift +=
1022 pair((aligndir.getx()+0.5)*xexcess,(aligndir.gety()+0.5)*yexcess);
1023 }
1024 } else if(svg)
1025 bboxshift += pair(-b.left,b.top);
1026
1027 bool status=true;
1028
1029 string texname;
1030 texfile *tex=NULL;
1031
1032 if(Labels) {
1033 texname=TeXmode ? buildname(prefix,"tex") : auxname(prefix,"tex");
1034 tex=dvi ? new svgtexfile(texname,b) : new texfile(texname,b);
1035 tex->prologue();
1036 }
1037
1038 nodelist::iterator layerp=nodes.begin();
1039 nodelist::iterator p=layerp;
1040 unsigned layer=0;
1041 mem::list<string> files;
1042
1043 bbox bshift=b;
1044
1045 int svgcount=0;
1046
1047 typedef mem::list<drawElement *> clipstack;
1048 clipstack begin;
1049
1050 while(p != nodes.end()) {
1051 string psname,pdfname;
1052 if(Labels) {
1053 ostringstream buf;
1054 buf << prefix << "_" << layer;
1055 psname=buildname(buf.str(),"eps");
1056 if(pdf) pdfname=buildname(buf.str(),"pdf");
1057 } else {
1058 psname=epsname;
1059 bshift.shift(bboxshift);
1060 }
1061 files.push_back(psname);
1062 if(pdf) files.push_back(pdfname);
1063 psfile out(psname,pdfformat);
1064 out.prologue(bshift);
1065
1066 if(!Labels) {
1067 out.gsave();
1068 out.translate(bboxshift);
1069 }
1070
1071 if(preamble) {
1072 // Postscript preamble.
1073 nodelist Nodes=preamble->nodes;
1074 nodelist::iterator P=Nodes.begin();
1075 if(P != Nodes.end()) {
1076 out.resetpen();
1077 for(; P != Nodes.end(); ++P) {
1078 assert(*P);
1079 (*P)->draw(&out);
1080 }
1081 }
1082 }
1083 out.resetpen();
1084
1085 bool postscript=false;
1086 drawLabel *L=NULL;
1087
1088 if(dvi)
1089 for(nodelist::const_iterator r=begin.begin(); r != begin.end(); ++r)
1090 (*r)->draw(&out);
1091
1092 processDataStruct &pd=processData();
1093
1094 for(; p != nodes.end(); ++p) {
1095 assert(*p);
1096 if(Labels && (*p)->islayer()) break;
1097
1098 if(dvi && (*p)->svg()) {
1099 picture *f=(*p)->svgpng() ? new picture : NULL;
1100 nodelist::const_iterator q=layerp;
1101 for(;;) {
1102 if((*q)->beginclip())
1103 begin.push_back(*q);
1104 else if((*q)->endclip()) {
1105 if(begin.size() < 1)
1106 reportError("endclip without matching beginclip");
1107 begin.pop_back();
1108 }
1109 if(q == p) break;
1110 ++q;
1111 }
1112
1113 if(f) {
1114 for(nodelist::const_iterator r=begin.begin(); r != begin.end(); ++r)
1115 f->append(*r);
1116
1117 f->append(*(q++));
1118 }
1119
1120 while(q != nodes.end() && !(*q)->islayer()) ++q;
1121
1122 clipstack end;
1123
1124 for(nodelist::const_iterator r=--q;; --r) {
1125 if((*r)->beginclip() && end.size() >= 1)
1126 end.pop_back();
1127 else if((*r)->endclip())
1128 end.push_back(*r);
1129 if(r == p) break;
1130 }
1131
1132 for(nodelist::reverse_iterator r=end.rbegin(); r != end.rend();
1133 ++r) {
1134 (*r)->draw(&out);
1135 if(f)
1136 f->append(*r);
1137 }
1138
1139 if(f) {
1140 ostringstream buf;
1141 buf << prefix << "_" << svgcount;
1142 ++svgcount;
1143 string pngname=buildname(buf.str(),"png");
1144 f->shipout(preamble,buf.str(),"png",false,false);
1145 pair m=f->bounds().Min();
1146 pair M=f->bounds().Max();
1147 delete f;
1148
1149 pair size=M-m;
1150 ostringstream cmd;
1151 cmd << "\\special{dvisvgm:img " << size.getx()*ps2tex << " "
1152 << size.gety()*ps2tex << " " << pngname << "}";
1153 static pen P;
1154 static pair zero;
1155 L=new drawLabel(cmd.str(),"",identity,pair(m.getx(),M.gety()),zero,P);
1156 texinit();
1157 L->bounds(b_cached,pd.tex,labelbounds,bboxstack);
1158 postscript=true;
1159 }
1160 break;
1161 } else postscript |= (*p)->draw(&out);
1162 }
1163
1164 if(Labels) {
1165 if(!svg || pdf)
1166 tex->beginlayer(pdf ? pdfname : psname,postscript);
1167 } else out.grestore();
1168
1169 out.epilogue();
1170 out.close();
1171
1172 if(Labels) {
1173 tex->resetpen();
1174 if(pdf && !b.empty) {
1175 status=(epstopdf(psname,pdfname) == 0);
1176 if(!keep) unlink(psname.c_str());
1177 }
1178
1179 if(status) {
1180 for (p=layerp; p != nodes.end(); ++p) {
1181 assert(*p);
1182 bool islayer=(*p)->islayer();
1183 if(dvi && (*p)->svg()) {
1184 islayer=true;
1185 if((*p)->svgpng())
1186 L->write(tex,b);
1187 else
1188 (*p)->draw(tex);
1189 } else
1190 (*p)->write(tex,b);
1191 if(islayer) {
1192 tex->endlayer();
1193 layerp=++p;
1194 layer++;
1195 break;
1196 }
1197 }
1198 }
1199 }
1200 }
1201
1202 bool context=settings::context(texengine);
1203 if(status) {
1204 if(TeXmode) {
1205 if(Labels && verbose > 0) cout << "Wrote " << texname << endl;
1206 delete tex;
1207 } else {
1208 if(Labels) {
1209 tex->epilogue();
1210 if(context) prefix=stripDir(prefix);
1211 status=texprocess(texname,dvi ? outname : prename,prefix,
1212 bboxshift,dvi);
1213 delete tex;
1214 if(!keep) {
1215 for(mem::list<string>::iterator p=files.begin(); p != files.end();
1216 ++p)
1217 unlink(p->c_str());
1218 }
1219 }
1220 if(status) {
1221 if(context) prename=stripDir(prename);
1222 status=postprocess(prename,outname,outputformat,wait,
1223 view,pdf && Labels,epsformat,svg);
1224 if(pdfformat && !keep) {
1225 unlink(auxname(prefix,"m9").c_str());
1226 unlink(auxname(prefix,"pbsdat").c_str());
1227 }
1228 }
1229 }
1230 }
1231
1232 if(!status) reportError("shipout failed");
1233
1234 if(!texengineSave.empty()) Setting("tex")=texengineSave;
1235
1236 if(htmlformat) {
1237 jsfile out;
1238 out.svgtohtml(prefix);
1239 string name=buildname(prefix,"html");
1240 display(name,"html",wait,true,false);
1241 if(!keep)
1242 unlink(outname.c_str());
1243 }
1244
1245 return true;
1246 }
1247
1248 // render viewport with width x height pixels.
render(double size2,const triple & Min,const triple & Max,double perspective,bool remesh) const1249 void picture::render(double size2, const triple& Min, const triple& Max,
1250 double perspective, bool remesh) const
1251 {
1252 for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) {
1253 assert(*p);
1254 if(remesh) (*p)->meshinit();
1255 (*p)->render(size2,Min,Max,perspective,remesh);
1256 }
1257
1258 #ifdef HAVE_GL
1259 drawBuffers();
1260 #endif
1261 }
1262
1263 struct Communicate : public gc {
1264 string prefix;
1265 picture* pic;
1266 string format;
1267 double width;
1268 double height;
1269 double angle;
1270 double zoom;
1271 triple m;
1272 triple M;
1273 pair shift;
1274 pair margin;
1275 double *t;
1276 double *background;
1277 size_t nlights;
1278 triple *lights;
1279 double *diffuse;
1280 double *specular;
1281 bool view;
1282 };
1283
1284 Communicate com;
1285
1286 extern bool allowRender;
1287
glrenderWrapper()1288 void glrenderWrapper()
1289 {
1290 #ifdef HAVE_GL
1291 #ifdef HAVE_PTHREAD
1292 wait(initSignal,initLock);
1293 endwait(initSignal,initLock);
1294 #endif
1295 if(allowRender)
1296 glrender(com.prefix,com.pic,com.format,com.width,com.height,com.angle,
1297 com.zoom,com.m,com.M,com.shift,com.margin,com.t,com.background,
1298 com.nlights,com.lights,com.diffuse,com.specular,com.view);
1299 #endif
1300 }
1301
shipout3(const string & prefix,const string & format,double width,double height,double angle,double zoom,const triple & m,const triple & M,const pair & shift,const pair & margin,double * t,double * background,size_t nlights,triple * lights,double * diffuse,double * specular,bool view)1302 bool picture::shipout3(const string& prefix, const string& format,
1303 double width, double height, double angle, double zoom,
1304 const triple& m, const triple& M, const pair& shift,
1305 const pair& margin, double *t, double *background,
1306 size_t nlights, triple *lights, double *diffuse,
1307 double *specular, bool view)
1308 {
1309 if(getSetting<bool>("interrupt"))
1310 return true;
1311
1312 bool webgl=format == "html";
1313
1314 #ifndef HAVE_LIBGLM
1315 if(webgl)
1316 camp::reportError("to support WebGL rendering, please install glm header files, run ./configure, and recompile");
1317 #endif
1318
1319 picture *pic = new picture;
1320
1321 matrixstack ms;
1322 for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) {
1323 assert(*p);
1324 if((*p)->begingroup3())
1325 ms.push((*p)->transf3());
1326 else if((*p)->endgroup3())
1327 ms.pop();
1328 else
1329 pic->append((*p)->transformed(ms.T()));
1330 }
1331
1332 pic->b3=bbox3();
1333 for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) {
1334 assert(*p);
1335 (*p)->bounds(pic->b3);
1336 }
1337 pic->lastnumber3=pic->nodes.size();
1338
1339 for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) {
1340 assert(*p);
1341 (*p)->displacement();
1342 }
1343
1344 const string outputformat=format.empty() ?
1345 getSetting<string>("outformat") : format;
1346
1347 #ifdef HAVE_LIBGLM
1348 static int oldpid=0;
1349 bool View=settings::view() && view;
1350 #endif
1351
1352 #ifdef HAVE_GL
1353 bool offscreen=false;
1354 #ifdef HAVE_LIBOSMESA
1355 offscreen=true;
1356 #endif
1357 #ifdef HAVE_PTHREAD
1358 bool animating=getSetting<bool>("animating");
1359 bool Wait=!interact::interactive || !View || animating;
1360 #endif
1361 #endif
1362
1363 if(!webgl) {
1364 #ifdef HAVE_GL
1365 if(glthread && !offscreen) {
1366 #ifdef HAVE_PTHREAD
1367 if(gl::initialize) {
1368 gl::initialize=false;
1369 com.prefix=prefix;
1370 com.pic=pic;
1371 com.format=outputformat;
1372 com.width=width;
1373 com.height=height;
1374 com.angle=angle;
1375 com.zoom=zoom;
1376 com.m=m;
1377 com.M=M;
1378 com.shift=shift;
1379 com.margin=margin;
1380 com.t=t;
1381 com.background=background;
1382 com.nlights=nlights;
1383 com.lights=lights;
1384 com.diffuse=diffuse;
1385 com.specular=specular;
1386 com.view=View;
1387 if(Wait)
1388 pthread_mutex_lock(&readyLock);
1389 wait(initSignal,initLock);
1390 endwait(initSignal,initLock);
1391 static bool initialize=true;
1392 if(initialize) {
1393 wait(initSignal,initLock);
1394 endwait(initSignal,initLock);
1395 initialize=false;
1396 }
1397 if(Wait) {
1398 pthread_cond_wait(&readySignal,&readyLock);
1399 pthread_mutex_unlock(&readyLock);
1400 }
1401 return true;
1402 }
1403 if(Wait)
1404 pthread_mutex_lock(&readyLock);
1405 #endif
1406 } else {
1407 int pid=fork();
1408 if(pid == -1)
1409 camp::reportError("Cannot fork process");
1410 if(pid != 0) {
1411 oldpid=pid;
1412 waitpid(pid,NULL,interact::interactive && View ? WNOHANG : 0);
1413 return true;
1414 }
1415 }
1416 #endif
1417 }
1418
1419 #if HAVE_LIBGLM
1420 glrender(prefix,pic,outputformat,width,height,angle,zoom,m,M,shift,margin,t,
1421 background,nlights,lights,diffuse,specular,View,oldpid);
1422
1423 if(webgl) {
1424 jsfile js;
1425 string name=buildname(prefix,format);
1426 js.open(name);
1427
1428 for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) {
1429 assert(*p);
1430 (*p)->write(&js);
1431 }
1432 js.finish(name);
1433 if(View)
1434 htmlView(name);
1435 return true;
1436 }
1437 #endif
1438
1439 #ifdef HAVE_GL
1440 #ifdef HAVE_PTHREAD
1441 if(glthread && !offscreen && Wait) {
1442 pthread_cond_wait(&readySignal,&readyLock);
1443 pthread_mutex_unlock(&readyLock);
1444 }
1445 return true;
1446 #endif
1447 #endif
1448
1449 return false;
1450 }
1451
shipout3(const string & prefix,const string format)1452 bool picture::shipout3(const string& prefix, const string format)
1453 {
1454 bounds3();
1455 bool status;
1456
1457 string name=buildname(prefix,"prc");
1458 prcfile prc(name);
1459
1460 static const double limit=2.5*10.0/INT_MAX;
1461 double compressionlimit=max(length(b3.Max()),length(b3.Min()))*limit;
1462
1463 groups.push_back(groupmap());
1464 for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) {
1465 assert(*p);
1466 (*p)->write(&prc,&billboard,compressionlimit,groups);
1467 }
1468 groups.pop_back();
1469 status=prc.finish();
1470
1471 if(!status) reportError("shipout3 failed");
1472
1473 if(verbose > 0) cout << "Wrote " << name << endl;
1474
1475 return true;
1476 }
1477
transformed(const transform & t)1478 picture *picture::transformed(const transform& t)
1479 {
1480 picture *pic = new picture;
1481
1482 nodelist::iterator p;
1483 for (p = nodes.begin(); p != nodes.end(); ++p) {
1484 assert(*p);
1485 pic->append((*p)->transformed(t));
1486 }
1487 pic->T=transform(t*T);
1488
1489 return pic;
1490 }
1491
transformed(const array & t)1492 picture *picture::transformed(const array& t)
1493 {
1494 picture *pic = new picture;
1495 double* T=NULL;
1496 copyArray4x4C(T,&t);
1497 size_t level = 0;
1498 for (nodelist::iterator p = nodes.begin(); p != nodes.end(); ++p) {
1499 assert(*p);
1500 if(level==0)
1501 pic->append((*p)->transformed(T));
1502 else
1503 pic->append(*p);
1504 if((*p)->begingroup3())
1505 level++;
1506 if((*p)->endgroup3()) {
1507 if(level==0)
1508 reportError("endgroup3 without matching begingroup3");
1509 else
1510 level--;
1511 }
1512 }
1513
1514 return pic;
1515 }
1516
1517
1518 } // namespace camp
1519