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