diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/utils/asymptote/picture.cc | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/utils/asymptote/picture.cc')
-rw-r--r-- | Build/source/utils/asymptote/picture.cc | 1515 |
1 files changed, 1515 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/picture.cc b/Build/source/utils/asymptote/picture.cc new file mode 100644 index 00000000000..9cb847ae099 --- /dev/null +++ b/Build/source/utils/asymptote/picture.cc @@ -0,0 +1,1515 @@ +/***** + * picture.cc + * Andy Hammerlindl 2002/06/06 + * + * Stores a picture as a list of drawElements and handles its output to + * PostScript. + *****/ + +#include "errormsg.h" +#include "picture.h" +#include "util.h" +#include "settings.h" +#include "interact.h" +#include "drawverbatim.h" +#include "drawlabel.h" +#include "drawlayer.h" +#include "drawsurface.h" +#include "drawpath3.h" + +#ifdef __MSDOS__ +#include "sys/cygwin.h" +#endif + +using std::ifstream; +using std::ofstream; +using vm::array; + +using namespace settings; +using namespace gl; + +texstream::~texstream() { + string texengine=getSetting<string>("tex"); + bool context=settings::context(texengine); + string name; + if(!context) + name=stripFile(outname()); + name += "texput."; + unlink((name+"aux").c_str()); + unlink((name+"log").c_str()); + unlink((name+"out").c_str()); + if(settings::pdf(texengine)) { + unlink((name+"pdf").c_str()); + unlink((name+"m9").c_str()); + } else + unlink((name+"pbsdat").c_str()); + if(context) { + unlink("cont-new.log"); + unlink((name+"tex").c_str()); + unlink((name+"top").c_str()); + unlink((name+"tua").c_str()); + unlink((name+"tui").c_str()); + } +} + +namespace camp { + +extern void draw(); + +bool isIdTransform3(const double* t) +{ + return (t == NULL || (t[0]==1 && t[1]==0 && t[2]==0 && t[3]==0 && + t[4]==0 && t[5]==1 && t[6]==0 && t[7]==0 && + t[8]==0 && t[9]==0 && t[10]==1 && t[11]==0 && + t[12]==0 && t[13]==0 && t[14]==0 && t[15]==1)); +} + +// copy array to 4x4 transform matrix with range checks +void copyArray4x4C(double*& dest, const vm::array *a) +{ + double tt[16]; + const size_t n=checkArray(a); + const string fourbyfour="4x4 array of doubles expected"; + if(n != 4) reportError(fourbyfour); + + for(size_t i=0; i < 4; i++) { + const vm::array *ai=vm::read<vm::array*>(a,i); + const size_t aisize=checkArray(ai); + double *tti=tt+4*i; + if(aisize == 4) { + for(size_t j=0; j < 4; j++) + tti[j]=vm::read<double>(ai,j); + } else reportError(fourbyfour); + } + + copyTransform3(dest,tt); +} + +void copyTransform3(double*& d, const double* s, GCPlacement placement) + +{ + if(s != NULL) { + if(d == NULL) + d=placement == NoGC ? new double[16] : new(placement) double[16]; + memcpy(d,s,sizeof(double)*16); + } +} + +// t = s*r +void multiplyTransform3(double*& t, const double* s, const double* r) +{ + if(isIdTransform3(s)) { + copyTransform3(t,r); + } else if(isIdTransform3(r)) { + copyTransform3(t,s); + } else { + t=new(UseGC) double[16]; + for(size_t i=0; i < 4; i++) { + size_t i4=4*i; + const double *si=s+i4; + const double& s0=si[0]; + const double& s1=si[1]; + const double& s2=si[2]; + const double& s3=si[3]; + double *ti=t+i4; + ti[0]=s0*r[0]+s1*r[4]+s2*r[8]+s3*r[12]; + ti[1]=s0*r[1]+s1*r[5]+s2*r[9]+s3*r[13]; + ti[2]=s0*r[2]+s1*r[6]+s2*r[10]+s3*r[14]; + ti[3]=s0*r[3]+s1*r[7]+s2*r[11]+s3*r[15]; + } + } +} + +double xratio(const triple& v) {return v.getx()/v.getz();} +double yratio(const triple& v) {return v.gety()/v.getz();} + +class matrixstack { + mem::stack<const double*> mstack; + +public: + // return current transform + const double* T() const + { + if(mstack.empty()) + return NULL; + else + return mstack.top(); + } + // we store the accumulated transform of all pushed transforms + void push(const double *r) + { + double* T3 = NULL; + multiplyTransform3(T3,T(),r); + mstack.push(T3); + } + void pop() + { + if(!mstack.empty()) + mstack.pop(); + } + +}; + +const char *texpathmessage() { + ostringstream buf; + buf << "the directory containing your " << getSetting<string>("tex") + << " engine (" << texcommand() << ")"; + return Strdup(buf.str()); +} + +picture::~picture() +{ +} + +void picture::enclose(drawElement *begin, drawElement *end) +{ + assert(begin); + assert(end); + nodes.push_front(begin); + lastnumber=0; + lastnumber3=0; + + for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->islayer()) { + nodes.insert(p,end); + ++p; + while(p != nodes.end() && (*p)->islayer()) ++p; + if(p == nodes.end()) return; + nodes.insert(p,begin); + } + } + nodes.push_back(end); +} + +// Insert at beginning of picture. +void picture::prepend(drawElement *p) +{ + assert(p); + nodes.push_front(p); + lastnumber=0; + lastnumber3=0; +} + +void picture::append(drawElement *p) +{ + assert(p); + nodes.push_back(p); +} + +void picture::add(picture &pic) +{ + if (&pic == this) return; + + // STL's funny way of copying one list into another. + copy(pic.nodes.begin(), pic.nodes.end(), back_inserter(nodes)); +} + +// Insert picture pic at beginning of picture. +void picture::prepend(picture &pic) +{ + if (&pic == this) return; + + copy(pic.nodes.begin(), pic.nodes.end(), inserter(nodes, nodes.begin())); + lastnumber=0; + lastnumber3=0; +} + +bool picture::havelabels() +{ + size_t n=nodes.size(); + if(n > lastnumber && !labels && getSetting<string>("tex") != "none") { + // Check to see if there are any labels yet + nodelist::iterator p=nodes.begin(); + for(size_t i=0; i < lastnumber; ++i) ++p; + for(; p != nodes.end(); ++p) { + assert(*p); + if((*p)->islabel()) { + labels=true; + break; + } + } + } + return labels; +} + +bool picture::have3D() +{ + for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->is3D()) + return true; + } + return false; +} + +bool picture::havepng() +{ + for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->svgpng()) + return true; + } + return false; +} + +bool picture::havenewpage() +{ + for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->isnewpage()) + return true; + } + return false; +} + +bbox picture::bounds() +{ + size_t n=nodes.size(); + if(n == lastnumber) return b_cached; + + if(lastnumber == 0) { // Maybe these should be put into a structure. + b_cached=bbox(); + labelbounds.clear(); + bboxstack.clear(); + } + + if(havelabels()) texinit(); + + nodelist::iterator p=nodes.begin(); + processDataStruct& pd=processData(); + + for(size_t i=0; i < lastnumber; ++i) ++p; + for(; p != nodes.end(); ++p) { + assert(*p); + (*p)->bounds(b_cached,pd.tex,labelbounds,bboxstack); + + // Optimization for interpreters with fixed stack limits. + if((*p)->endclip()) { + nodelist::iterator q=p; + if(q != nodes.begin()) { + --q; + assert(*q); + if((*q)->endclip()) + (*q)->save(false); + } + } + } + + lastnumber=n; + return b_cached; +} + +bbox3 picture::bounds3() +{ + size_t n=nodes.size(); + if(n == lastnumber3) return b3; + + if(lastnumber3 == 0) + b3=bbox3(); + + matrixstack ms; + size_t i=0; + for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->begingroup3()) + ms.push((*p)->transf3()); + else if((*p)->endgroup3()) + ms.pop(); + else + (*p)->bounds(ms.T(),b3); + i++; + } + + lastnumber3=n; + return b3; +} + +pair picture::ratio(double (*m)(double, double)) +{ + bool first=true; + pair b; + bounds3(); + double fuzz=Fuzz*(b3.Max()-b3.Min()).length(); + matrixstack ms; + for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->begingroup3()) + ms.push((*p)->transf3()); + else if((*p)->endgroup3()) + ms.pop(); + else + (*p)->ratio(ms.T(),b,m,fuzz,first); + } + return b; +} + +void texinit() +{ + drawElement::lastpen=pen(initialpen); + processDataStruct &pd=processData(); + // Output any new texpreamble commands + if(pd.tex.isopen()) { + if(pd.TeXpipepreamble.empty()) return; + texpreamble(pd.tex,pd.TeXpipepreamble,true); + pd.TeXpipepreamble.clear(); + return; + } + + bool context=settings::context(getSetting<string>("tex")); + string dir=stripFile(outname()); + string logname; + if(!context) logname=dir; + logname += "texput.log"; + const char *cname=logname.c_str(); + ofstream writeable(cname); + if(!writeable) + reportError("Cannot write to "+logname); + else + writeable.close(); + unlink(cname); + + mem::vector<string> cmd; + cmd.push_back(texprogram()); + if(context) { + cmd.push_back("--pipe"); + } else { + if(!dir.empty()) + cmd.push_back("-output-directory="+dir.substr(0,dir.length()-1)); + string jobname="texput"; + if(getSetting<bool>("inlineimage") || getSetting<bool>("inlinetex")) { + string name=stripDir(stripExt((outname()))); + size_t pos=name.rfind("-"); + if(pos < string::npos) { + name=stripExt(name).substr(0,pos); + unlink((name+".aux").c_str()); + jobname=name.substr(0,pos); + cmd.push_back("-jobname="+jobname); +#ifdef __MSDOS__ + cmd.push_back("NUL"); // For MikTeX +#endif + } + } + cmd.push_back("\\scrollmode"); + } + + pd.tex.open(cmd,"texpath"); + pd.tex.wait("\n*"); + pd.tex << "\n"; + texdocumentclass(pd.tex,true); + + texdefines(pd.tex,pd.TeXpreamble,true); + pd.TeXpipepreamble.clear(); +} + +int opentex(const string& texname, const string& prefix, bool dvi) +{ + string aux=auxname(prefix,"aux"); + unlink(aux.c_str()); + bool context=settings::context(getSetting<string>("tex")); + mem::vector<string> cmd; + cmd.push_back(texprogram()); + if(dvi) + cmd.push_back("-output-format=dvi"); + if(context) { + cmd.push_back("--nonstopmode"); + cmd.push_back(texname); + } else { + string dir=stripFile(texname); + if(!dir.empty()) + cmd.push_back("-output-directory="+dir.substr(0,dir.length()-1)); + cmd.push_back("\\nonstopmode\\input"); + cmd.push_back(stripDir(texname)); + } + + bool quiet=verbose <= 1; + int status=System(cmd,quiet ? 1 : 0,true,"texpath",texpathmessage()); + if(!status && getSetting<bool>("twice")) + status=System(cmd,quiet ? 1 : 0,true,"texpath",texpathmessage()); + if(status) { + if(quiet) { + cmd[1]=context ? "--scrollmode" : "\\scrollmode\\input"; + System(cmd,0); + } + } + return status; +} + +char *dvisvgmCommand(mem::vector<string>& cmd, const string &in, const string& out) +{ + cmd.push_back(getSetting<string>("dvisvgm")); + cmd.push_back("-n"); + cmd.push_back("-v3"); + string libgs=getSetting<string>("libgs"); + if(!libgs.empty()) + cmd.push_back("--libgs="+libgs); +// cmd.push_back("--optimize"); // Requires dvisvgm > 2.9.1 + push_split(cmd,getSetting<string>("dvisvgmOptions")); + char *tmpdir=mkdtemp(StrdupMalloc(tempdir+"/dvisvgmXXXXXX")); + if(tmpdir) + cmd.push_back("--tmpdir="+string(tmpdir)); + cmd.push_back("-o"+out); + cmd.push_back(in); + return tmpdir; +} + +void rmtmpdir(char *tmpdir) +{ + if(tmpdir) { + recursive_delete(tmpdir); + free(tmpdir); + } +} + +bool picture::texprocess(const string& texname, const string& outname, + const string& prefix, const pair& bboxshift, + bool svg) +{ + int status=1; + ifstream outfile; + + outfile.open(texname.c_str()); + bool keep=getSetting<bool>("keep"); + + if(outfile) { + outfile.close(); + + status=opentex(texname,prefix); + string texengine=getSetting<string>("tex"); + + if(status == 0) { + string dviname=auxname(prefix,"dvi"); + mem::vector<string> cmd; + + if(svg) { + char *tmpdir=dvisvgmCommand(cmd,dviname,outname); + ostringstream buf; + bbox B=svgbbox(b,bboxshift); + buf << "--bbox=" + << B.left << "bp " + << B.bottom << "bp " + << B.right << "bp " + << B.top << "bp"; + cmd.push_back(buf.str()); + status=System(cmd,0,true,"dvisvgm"); + rmtmpdir(tmpdir); + if(!keep) + unlink(dviname.c_str()); + } else { + if(!settings::pdf(texengine)) { + string psname=auxname(prefix,"ps"); + double height=b.top-b.bottom+1.0; + + // Magic dvips offsets: + double hoffset=-128.4; + double vertical=height; + if(!latex(texengine)) vertical += 2.0; + double voffset=(vertical < 13.0) ? -137.8+vertical : -124.8; + double paperHeight=getSetting<double>("paperheight"); + + hoffset += b.left+bboxshift.getx(); + voffset += paperHeight-height-b.bottom-bboxshift.gety(); + + string dvipsrc=getSetting<string>("dir"); + if(dvipsrc.empty()) dvipsrc=systemDir; + dvipsrc += dirsep+"nopapersize.ps"; + setenv("DVIPSRC",dvipsrc.c_str(),1); + string papertype=getSetting<string>("papertype") == "letter" ? + "letterSize" : "a4size"; + cmd.push_back(getSetting<string>("dvips")); + cmd.push_back("-R"); + cmd.push_back("-Pdownload35"); + cmd.push_back("-D600"); + cmd.push_back("-O"+String(hoffset)+"bp,"+String(voffset)+"bp"); + bool ps=havenewpage(); + if(ps) + cmd.push_back("-T"+String(getSetting<double>("paperwidth"))+"bp,"+ + String(paperHeight)+"bp"); + else + cmd.push_back("-E"); + push_split(cmd,getSetting<string>("dvipsOptions")); + if(ps && getSetting<string>("papertype") != "") + cmd.push_back("-t"+papertype); + if(verbose <= 1) cmd.push_back("-q"); + cmd.push_back("-o"+psname); + cmd.push_back(dviname); + status=System(cmd,0,true,"dvips"); + if(status == 0) { + ifstream fin(psname.c_str()); + psfile fout(outname,false); + + string s; + bool first=true; + transform t=shift(bboxshift)*T; + bool shift=!t.isIdentity(); + + const string beginspecial="TeXDict begin @defspecial"; + const size_t beginlength=beginspecial.size(); + const string endspecial="@fedspecial end"; + const size_t endlength=endspecial.size(); + + while(getline(fin,s)) { + if (s[0] == '%') { + if (s.find("%%DocumentPaperSizes:") == 0) + continue; + + if(s.find("%!PS-Adobe-") == 0) { + fout.header(!ps); + continue; + } + + if (first && s.find("%%BoundingBox:") == 0) { + bbox box=b; + box.shift(bboxshift); + if(verbose > 2) BoundingBox(cout,box); + fout.BoundingBox(box); + first=false; + continue; + } + } + + if (shift) { + if (s.compare(0, beginlength, beginspecial) == 0) { + fout.verbatimline(s); + fout.gsave(); + fout.concat(t); + continue; + } + if (s.compare(0, endlength, endspecial) == 0) { + fout.grestore(); + fout.verbatimline(s); + continue; + } + } + + // For the default line, output it unchanged. + fout.verbatimline(s); + } + } + if(!keep) { + unlink(dviname.c_str()); + unlink(psname.c_str()); + } + } + } + } + + if(!keep) { + unlink(texname.c_str()); + if(!getSetting<bool>("keepaux")) + unlink(auxname(prefix,"aux").c_str()); + unlink(auxname(prefix,"log").c_str()); + unlink(auxname(prefix,"out").c_str()); + if(settings::context(texengine)) { + unlink(auxname(prefix,"top").c_str()); + unlink(auxname(prefix,"tua").c_str()); + unlink(auxname(prefix,"tuc").c_str()); + unlink(auxname(prefix,"tui").c_str()); + unlink(auxname(prefix,"tuo").c_str()); + } + } + if(status == 0) return true; + } + return false; +} + +int picture::epstopdf(const string& epsname, const string& pdfname) +{ + string compress=getSetting<bool>("compress") ? "true" : "false"; + mem::vector<string> cmd; + cmd.push_back(getSetting<string>("gs")); + cmd.push_back("-q"); + cmd.push_back("-dNOPAUSE"); + cmd.push_back("-dBATCH"); + cmd.push_back("-P"); + if(safe) + cmd.push_back("-dSAFER"); + cmd.push_back("-dALLOWPSTRANSPARENCY"); // Support transparency extensions. + cmd.push_back("-sDEVICE=pdfwrite"); + cmd.push_back("-dEPSCrop"); + cmd.push_back("-dSubsetFonts=true"); + cmd.push_back("-dEmbedAllFonts=true"); + cmd.push_back("-dMaxSubsetPct=100"); + cmd.push_back("-dEncodeColorImages="+compress); + cmd.push_back("-dEncodeGrayImages="+compress); + cmd.push_back("-dCompatibilityLevel=1.4"); + if(!getSetting<bool>("autorotate")) + cmd.push_back("-dAutoRotatePages=/None"); + cmd.push_back("-g"+String(max(ceil(getSetting<double>("paperwidth")),1.0)) + +"x"+String(max(ceil(getSetting<double>("paperheight")),1.0))); + cmd.push_back("-dDEVICEWIDTHPOINTS="+String(max(b.right-b.left,3.0))); + cmd.push_back("-dDEVICEHEIGHTPOINTS="+String(max(b.top-b.bottom,3.0))); + push_split(cmd,getSetting<string>("gsOptions")); + cmd.push_back("-sOutputFile="+stripDir(pdfname)); + if(safe) { + cmd.push_back("-c"); + cmd.push_back(".setsafe"); + cmd.push_back("-f"); + } + cmd.push_back(stripDir(epsname)); + + char *oldPath=NULL; + string dir=stripFile(pdfname); + if(!dir.empty()) { + oldPath=getPath(); + setPath(dir.c_str()); + } + int status=System(cmd,0,true,"gs","Ghostscript"); + if(oldPath != NULL) + setPath(oldPath); + return status; +} + +int picture::pdftoeps(const string& pdfname, const string& epsname, bool eps) +{ + mem::vector<string> cmd; + cmd.push_back(getSetting<string>("gs")); + cmd.push_back("-q"); + cmd.push_back("-dNOCACHE"); + cmd.push_back("-dNOPAUSE"); + cmd.push_back("-dBATCH"); + cmd.push_back("-P"); + if(safe) + cmd.push_back("-dSAFER"); + string texengine=getSetting<string>("tex"); + cmd.push_back("-sDEVICE="+getSetting<string>(eps ? "epsdriver": "psdriver")); + cmd.push_back("-sOutputFile="+stripDir(epsname)); + cmd.push_back(stripDir(pdfname)); + + char *oldPath=NULL; + string dir=stripFile(epsname); + if(!dir.empty()) { + oldPath=getPath(); + setPath(dir.c_str()); + } + int status=System(cmd,0,true,"gs","Ghostscript"); + if(oldPath != NULL) + setPath(oldPath); + return status; +} + +bool picture::reloadPDF(const string& Viewer, const string& outname) const +{ + static bool needReload=true; + static bool haveReload=false; + + string reloadprefix="reload"; + if(needReload) { + needReload=false; + string name=getPath()+string("/")+outname; + // Write javascript code to redraw picture. + 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); + haveReload=true; + } + + if(haveReload) { + mem::vector<string> cmd; + push_command(cmd,Viewer); + string pdfreloadOptions=getSetting<string>("pdfreloadOptions"); + if(!pdfreloadOptions.empty()) + cmd.push_back(pdfreloadOptions); + cmd.push_back(reloadprefix+".pdf"); + System(cmd,0,false); + } + return true; +} + +int picture::epstosvg(const string& epsname, const string& outname) +{ + mem::vector<string> cmd; + char *tmpdir=dvisvgmCommand(cmd,epsname,outname); + cmd.push_back("-E"); + int status=System(cmd,0,true,"dvisvgm"); + rmtmpdir(tmpdir); + if(!getSetting<bool>("keep")) + unlink(epsname.c_str()); + return status; +} + +int picture::pdftosvg(const string& pdfname, const string& outname) +{ + mem::vector<string> cmd; + char *tmpdir=dvisvgmCommand(cmd,pdfname,outname); + cmd.push_back("--pdf"); + int status=System(cmd,0,true,"dvisvgm"); + rmtmpdir(tmpdir); + if(status == 0 && !getSetting<bool>("keep")) + unlink(pdfname.c_str()); + return status; +} + +void htmlView(string name) +{ + mem::vector<string> cmd; + push_command(cmd,getSetting<string>("htmlviewer")); +#ifdef __MSDOS__ + ssize_t size=cygwin_conv_path(CCP_POSIX_TO_WIN_A, + locateFile(name,true).c_str(),NULL,0); + if(size <= 0) return; + char filename[size]; + size=cygwin_conv_path(CCP_POSIX_TO_WIN_A,locateFile(name,true).c_str(), + filename,size); + cmd.push_back("file://"+string(filename)); +#else + cmd.push_back(locateFile(name,true)); +#endif + push_split(cmd,getSetting<string>("htmlviewerOptions")); + System(cmd,2,false); +} + +bool picture::postprocess(const string& prename, const string& outname, + const string& outputformat, + bool wait, bool view, bool pdftex, + bool epsformat, bool svg) +{ + int status=0; + bool pdf=settings::pdf(getSetting<string>("tex")); + bool pdfformat=(pdf && outputformat == "") || outputformat == "pdf"; + + mem::vector<string> cmd; + if(pdftex || !epsformat) { + if(pdfformat) { + if(pdftex) { + status=rename(prename.c_str(),outname.c_str()); + if(status != 0) + reportError("Cannot rename "+prename+" to "+outname); + } else status=epstopdf(prename,outname); + } else if(epsformat) { + if(svg) { + bool haveShading=pdf && havepng(); + if(!haveShading) + status=pdftosvg(prename,outname); + if(haveShading || status != 0) { + // Dvisvgm version < 2.4 doesn't support --pdf + // Dvisvgm --pdf doesn't support shading + string psname=stripExt(prename)+".ps"; + status=pdftoeps(prename,psname,false); + if(status != 0) return false; + status=epstosvg(psname,outname); + } + epsformat=false; + } else + status=pdftoeps(prename,outname); + } else { + double render=fabs(getSetting<double>("render")); + if(render == 0) render=1.0; + double res=render*72.0; + Int antialias=getSetting<Int>("antialias"); + if(outputformat == "png" && antialias == 2) { + cmd.push_back(getSetting<string>("gs")); + cmd.push_back("-q"); + cmd.push_back("-dNOPAUSE"); + cmd.push_back("-dBATCH"); + cmd.push_back("-P"); + cmd.push_back("-sDEVICE=pngalpha"); + cmd.push_back("-dEPSCrop"); + if(safe) + cmd.push_back("-dSAFER"); + cmd.push_back("-r"+String(res)+"x"+String(res)); + push_split(cmd,getSetting<string>("gsOptions")); + cmd.push_back("-sOutputFile="+outname); + cmd.push_back(prename); + status=System(cmd,0,true,"gs","Ghostscript"); + } else if(!svg && !getSetting<bool>("xasy")) { + double expand=antialias; + if(expand < 2.0) expand=1.0; + res *= expand; + cmd.push_back(getSetting<string>("convert")); + cmd.push_back("-density"); + cmd.push_back(String(res)+"x"+String(res)); + if(expand == 1.0) + cmd.push_back("+antialias"); + push_split(cmd,getSetting<string>("convertOptions")); + cmd.push_back("-resize"); + cmd.push_back(String(100.0/expand)+"%x"); + if(outputformat == "jpg") cmd.push_back("-flatten"); + cmd.push_back(prename); + cmd.push_back(outputformat+":"+outname); + status=System(cmd,0,true,"convert"); + } + } + if(!getSetting<bool>("keep")) + unlink(prename.c_str()); + } + if(status != 0) return false; + + if(verbose > 0) + cout << "Wrote " << outname << endl; + + return display(outname,outputformat,wait,view,epsformat); +} + +bool picture::display(const string& outname, const string& outputformat, + bool wait, bool view, bool epsformat) +{ + int status=0; + static mem::map<CONST string,int> pids; + bool View=settings::view() && view; + + if(View) { + bool pdf=settings::pdf(getSetting<string>("tex")); + bool pdfformat=(pdf && outputformat == "") || outputformat == "pdf"; + + if(epsformat || pdfformat) { + // Check to see if there is an existing viewer for this outname. + mem::map<CONST string,int>::iterator p=pids.find(outname); + bool running=(p != pids.end()); + string Viewer=pdfformat ? getSetting<string>("pdfviewer") : + getSetting<string>("psviewer"); + int pid; + if(running) { + pid=p->second; + if(pid) + running=(waitpid(pid, &status, WNOHANG) != pid); + } + + bool pdfreload=pdfformat && getSetting<bool>("pdfreload"); + if(running) { + // Tell gv/acroread to reread file. + if(Viewer == "gv") kill(pid,SIGHUP); + else if(pdfreload) + reloadPDF(Viewer,outname); + } else { + mem::vector<string> cmd; + push_command(cmd,Viewer); + string viewerOptions=getSetting<string>(pdfformat ? + "pdfviewerOptions" : + "psviewerOptions"); + if(!viewerOptions.empty()) + push_split(cmd,viewerOptions); + cmd.push_back(outname); + status=System(cmd,0,wait, + pdfformat ? "pdfviewer" : "psviewer", + pdfformat ? "your PDF viewer" : "your PostScript viewer", + &pid); + if(status != 0) return false; + + if(!wait) pids[outname]=pid; + + if(pdfreload) { + // Work around race conditions in acroread initialization script + usleep(getSetting<Int>("pdfreloaddelay")); + // Only reload if pdf viewer process is already running. + if(waitpid(pid, &status, WNOHANG) == pid) + reloadPDF(Viewer,outname); + } + } + } else { + if(outputformat == "svg" || outputformat == "html") + htmlView(outname); + else { + mem::vector<string> cmd; + push_command(cmd,getSetting<string>("display")); + cmd.push_back(outname); + string application="your "+outputformat+" viewer"; + status=System(cmd,0,wait,"display",application.c_str()); + if(status != 0) return false; + } + } + } + + return true; +} + +string Outname(const string& prefix, const string& outputformat, + bool standardout, string aux="") +{ + return standardout ? "-" : buildname(prefix,outputformat,aux); +} + +bool picture::shipout(picture *preamble, const string& Prefix, + const string& format, bool wait, bool view) +{ + bool keep=getSetting<bool>("keep"); + + string aux=""; + b=bounds(); + bool empty=b.empty; + + string outputformat=format.empty() ? defaultformat() : format; + + bool htmlformat=outputformat == "html"; + if(htmlformat) { + outputformat="svg"; + aux="_"; + if(view) view=false; + else htmlformat=false; + } + + bool svgformat=outputformat == "svg"; + + string texengine=getSetting<string>("tex"); + string texengineSave; + + if(!empty && svgformat && texengine == "latex" && havepng()) { + texengineSave=texengine; + Setting("tex")=texengine="pdflatex"; + } + + bool usetex=texengine != "none"; + bool TeXmode=getSetting<bool>("inlinetex") && usetex; + bool pdf=settings::pdf(texengine); + + bool standardout=Prefix == "-"; + string prefix=standardout ? standardprefix : stripExt(Prefix); + + string preformat=nativeformat(); + bool epsformat=outputformat == "eps"; + bool pdfformat=pdf || outputformat == "pdf"; + bool dvi=false; + bool svg=svgformat && usetex && + (!have3D() || getSetting<double>("render") == 0.0); + if(svg) { + if(pdf) epsformat=true; + else dvi=true; + } + + string outname=Outname(prefix,outputformat,standardout,aux); + string epsname=epsformat ? (standardout ? "" : outname) : + auxname(prefix,"eps"); + + bool Labels=labels || TeXmode; + + if(outputformat == "png" && (b.right-b.left < 1.0 || b.top-b.bottom < 1.0)) + empty=true; + + if(empty && !Labels) { // Output a null file + bbox b; + b.left=b.bottom=0; + b.right=b.top=1; + psfile out(epsname,false); + out.prologue(b); + out.epilogue(); + out.close(); + return postprocess(epsname,outname,outputformat,wait,view,false, + epsformat,false); + } + + Labels |= svg; + + if(Labels) + prefix=cleanpath(prefix); + + string prename=((epsformat && !pdf) || !Labels) ? epsname : + auxname(prefix,preformat); + + SetPageDimensions(); + + pair aligndir=getSetting<pair>("aligndir"); + string origin=getSetting<string>("align"); + + pair bboxshift=(origin == "Z" && epsformat) ? + pair(0.0,0.0) : pair(-b.left,-b.bottom); + + if(epsformat) { + bboxshift += getSetting<pair>("offset"); + double yexcess=max(getSetting<double>("paperheight")- + (b.top-b.bottom+1.0),0.0); + double xexcess=max(getSetting<double>("paperwidth")- + (b.right-b.left+1.0),0.0); + if(aligndir == pair(0,0)) { + if(origin != "Z" && origin != "B") { + if(origin == "T") bboxshift += pair(0.0,yexcess); + else bboxshift += pair(0.5*xexcess,0.5*yexcess); + } + } else { + double scale=max(fabs(aligndir.getx()),fabs(aligndir.gety())); + if(scale != 0) aligndir *= 0.5/scale; + bboxshift += + pair((aligndir.getx()+0.5)*xexcess,(aligndir.gety()+0.5)*yexcess); + } + } else if(svg) + bboxshift += pair(-b.left,b.top); + + bool status=true; + + string texname; + texfile *tex=NULL; + + if(Labels) { + texname=TeXmode ? buildname(prefix,"tex") : auxname(prefix,"tex"); + tex=dvi ? new svgtexfile(texname,b) : new texfile(texname,b); + tex->prologue(); + } + + nodelist::iterator layerp=nodes.begin(); + nodelist::iterator p=layerp; + unsigned layer=0; + mem::list<string> files; + + bbox bshift=b; + + int svgcount=0; + + typedef mem::list<drawElement *> clipstack; + clipstack begin; + + while(p != nodes.end()) { + string psname,pdfname; + if(Labels) { + ostringstream buf; + buf << prefix << "_" << layer; + psname=buildname(buf.str(),"eps"); + if(pdf) pdfname=buildname(buf.str(),"pdf"); + } else { + psname=epsname; + bshift.shift(bboxshift); + } + files.push_back(psname); + if(pdf) files.push_back(pdfname); + psfile out(psname,pdfformat); + out.prologue(bshift); + + if(!Labels) { + out.gsave(); + out.translate(bboxshift); + } + + if(preamble) { + // Postscript preamble. + nodelist Nodes=preamble->nodes; + nodelist::iterator P=Nodes.begin(); + if(P != Nodes.end()) { + out.resetpen(); + for(; P != Nodes.end(); ++P) { + assert(*P); + (*P)->draw(&out); + } + } + } + out.resetpen(); + + bool postscript=false; + drawLabel *L=NULL; + + if(dvi) + for(nodelist::const_iterator r=begin.begin(); r != begin.end(); ++r) + (*r)->draw(&out); + + processDataStruct &pd=processData(); + + for(; p != nodes.end(); ++p) { + assert(*p); + if(Labels && (*p)->islayer()) break; + + if(dvi && (*p)->svg()) { + picture *f=(*p)->svgpng() ? new picture : NULL; + nodelist::const_iterator q=layerp; + for(;;) { + if((*q)->beginclip()) + begin.push_back(*q); + else if((*q)->endclip()) { + if(begin.size() < 1) + reportError("endclip without matching beginclip"); + begin.pop_back(); + } + if(q == p) break; + ++q; + } + + if(f) { + for(nodelist::const_iterator r=begin.begin(); r != begin.end(); ++r) + f->append(*r); + + f->append(*(q++)); + } + + while(q != nodes.end() && !(*q)->islayer()) ++q; + + clipstack end; + + for(nodelist::const_iterator r=--q;; --r) { + if((*r)->beginclip() && end.size() >= 1) + end.pop_back(); + else if((*r)->endclip()) + end.push_back(*r); + if(r == p) break; + } + + for(nodelist::reverse_iterator r=end.rbegin(); r != end.rend(); + ++r) { + (*r)->draw(&out); + if(f) + f->append(*r); + } + + if(f) { + ostringstream buf; + buf << prefix << "_" << svgcount; + ++svgcount; + string pngname=buildname(buf.str(),"png"); + f->shipout(preamble,buf.str(),"png",false,false); + pair m=f->bounds().Min(); + pair M=f->bounds().Max(); + delete f; + + pair size=M-m; + ostringstream cmd; + cmd << "\\special{dvisvgm:img " << size.getx()*ps2tex << " " + << size.gety()*ps2tex << " " << pngname << "}"; + static pen P; + static pair zero; + L=new drawLabel(cmd.str(),"",identity,pair(m.getx(),M.gety()),zero,P); + texinit(); + L->bounds(b_cached,pd.tex,labelbounds,bboxstack); + postscript=true; + } + break; + } else postscript |= (*p)->draw(&out); + } + + if(Labels) { + if(!svg || pdf) + tex->beginlayer(pdf ? pdfname : psname,postscript); + } else out.grestore(); + + out.epilogue(); + out.close(); + + if(Labels) { + tex->resetpen(); + if(pdf && !b.empty) { + status=(epstopdf(psname,pdfname) == 0); + if(!keep) unlink(psname.c_str()); + } + + if(status) { + for (p=layerp; p != nodes.end(); ++p) { + assert(*p); + bool islayer=(*p)->islayer(); + if(dvi && (*p)->svg()) { + islayer=true; + if((*p)->svgpng()) + L->write(tex,b); + else + (*p)->draw(tex); + } else + (*p)->write(tex,b); + if(islayer) { + tex->endlayer(); + layerp=++p; + layer++; + break; + } + } + } + } + } + + bool context=settings::context(texengine); + if(status) { + if(TeXmode) { + if(Labels && verbose > 0) cout << "Wrote " << texname << endl; + delete tex; + } else { + if(Labels) { + tex->epilogue(); + if(context) prefix=stripDir(prefix); + status=texprocess(texname,dvi ? outname : prename,prefix, + bboxshift,dvi); + delete tex; + if(!keep) { + for(mem::list<string>::iterator p=files.begin(); p != files.end(); + ++p) + unlink(p->c_str()); + } + } + if(status) { + if(context) prename=stripDir(prename); + status=postprocess(prename,outname,outputformat,wait, + view,pdf && Labels,epsformat,svg); + if(pdfformat && !keep) { + unlink(auxname(prefix,"m9").c_str()); + unlink(auxname(prefix,"pbsdat").c_str()); + } + } + } + } + + if(!status) reportError("shipout failed"); + + if(!texengineSave.empty()) Setting("tex")=texengineSave; + + if(htmlformat) { + jsfile out; + out.svgtohtml(prefix); + string name=buildname(prefix,"html"); + display(name,"html",wait,true,false); + if(!keep) + unlink(outname.c_str()); + } + + return true; +} + +// render viewport with width x height pixels. +void picture::render(double size2, const triple& Min, const triple& Max, + double perspective, bool remesh) const +{ + for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if(remesh) (*p)->meshinit(); + (*p)->render(size2,Min,Max,perspective,remesh); + } + +#ifdef HAVE_GL + drawBuffers(); +#endif +} + +struct Communicate : public gc { + string prefix; + picture* pic; + string format; + double width; + double height; + double angle; + double zoom; + triple m; + triple M; + pair shift; + pair margin; + double *t; + double *background; + size_t nlights; + triple *lights; + double *diffuse; + double *specular; + bool view; +}; + +Communicate com; + +extern bool allowRender; + +void glrenderWrapper() +{ +#ifdef HAVE_GL +#ifdef HAVE_PTHREAD + wait(initSignal,initLock); + endwait(initSignal,initLock); +#endif + if(allowRender) + glrender(com.prefix,com.pic,com.format,com.width,com.height,com.angle, + com.zoom,com.m,com.M,com.shift,com.margin,com.t,com.background, + com.nlights,com.lights,com.diffuse,com.specular,com.view); +#endif +} + +bool picture::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) +{ + if(getSetting<bool>("interrupt")) + return true; + + bool webgl=format == "html"; + +#ifndef HAVE_LIBGLM + if(webgl) + camp::reportError("to support WebGL rendering, please install glm header files, run ./configure, and recompile"); +#endif + + picture *pic = new picture; + + matrixstack ms; + for(nodelist::const_iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if((*p)->begingroup3()) + ms.push((*p)->transf3()); + else if((*p)->endgroup3()) + ms.pop(); + else + pic->append((*p)->transformed(ms.T())); + } + + pic->b3=bbox3(); + for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) { + assert(*p); + (*p)->bounds(pic->b3); + } + pic->lastnumber3=pic->nodes.size(); + + for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) { + assert(*p); + (*p)->displacement(); + } + + const string outputformat=format.empty() ? + getSetting<string>("outformat") : format; + +#ifdef HAVE_LIBGLM + static int oldpid=0; + bool View=settings::view() && view; +#endif + +#ifdef HAVE_GL + bool offscreen=false; +#ifdef HAVE_LIBOSMESA + offscreen=true; +#endif +#ifdef HAVE_PTHREAD + bool animating=getSetting<bool>("animating"); + bool Wait=!interact::interactive || !View || animating; +#endif +#endif + + if(!webgl) { +#ifdef HAVE_GL + if(glthread && !offscreen) { +#ifdef HAVE_PTHREAD + if(gl::initialize) { + gl::initialize=false; + com.prefix=prefix; + com.pic=pic; + com.format=outputformat; + com.width=width; + com.height=height; + com.angle=angle; + com.zoom=zoom; + com.m=m; + com.M=M; + com.shift=shift; + com.margin=margin; + com.t=t; + com.background=background; + com.nlights=nlights; + com.lights=lights; + com.diffuse=diffuse; + com.specular=specular; + com.view=View; + if(Wait) + pthread_mutex_lock(&readyLock); + wait(initSignal,initLock); + endwait(initSignal,initLock); + static bool initialize=true; + if(initialize) { + wait(initSignal,initLock); + endwait(initSignal,initLock); + initialize=false; + } + if(Wait) { + pthread_cond_wait(&readySignal,&readyLock); + pthread_mutex_unlock(&readyLock); + } + return true; + } + if(Wait) + pthread_mutex_lock(&readyLock); +#endif + } else { + int pid=fork(); + if(pid == -1) + camp::reportError("Cannot fork process"); + if(pid != 0) { + oldpid=pid; + waitpid(pid,NULL,interact::interactive && View ? WNOHANG : 0); + return true; + } + } +#endif + } + +#if HAVE_LIBGLM + glrender(prefix,pic,outputformat,width,height,angle,zoom,m,M,shift,margin,t, + background,nlights,lights,diffuse,specular,View,oldpid); + + if(webgl) { + jsfile js; + string name=buildname(prefix,format); + js.open(name); + + for(nodelist::iterator p=pic->nodes.begin(); p != pic->nodes.end(); ++p) { + assert(*p); + (*p)->write(&js); + } + js.finish(name); + if(View) + htmlView(name); + return true; + } +#endif + +#ifdef HAVE_GL +#ifdef HAVE_PTHREAD + if(glthread && !offscreen && Wait) { + pthread_cond_wait(&readySignal,&readyLock); + pthread_mutex_unlock(&readyLock); + } + return true; +#endif +#endif + + return false; +} + +bool picture::shipout3(const string& prefix, const string format) +{ + bounds3(); + bool status; + + string name=buildname(prefix,"prc"); + prcfile prc(name); + + static const double limit=2.5*10.0/INT_MAX; + double compressionlimit=max(length(b3.Max()),length(b3.Min()))*limit; + + groups.push_back(groupmap()); + for(nodelist::iterator p=nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + (*p)->write(&prc,&billboard,compressionlimit,groups); + } + groups.pop_back(); + status=prc.finish(); + + if(!status) reportError("shipout3 failed"); + + if(verbose > 0) cout << "Wrote " << name << endl; + + return true; +} + +picture *picture::transformed(const transform& t) +{ + picture *pic = new picture; + + nodelist::iterator p; + for (p = nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + pic->append((*p)->transformed(t)); + } + pic->T=transform(t*T); + + return pic; +} + +picture *picture::transformed(const array& t) +{ + picture *pic = new picture; + double* T=NULL; + copyArray4x4C(T,&t); + size_t level = 0; + for (nodelist::iterator p = nodes.begin(); p != nodes.end(); ++p) { + assert(*p); + if(level==0) + pic->append((*p)->transformed(T)); + else + pic->append(*p); + if((*p)->begingroup3()) + level++; + if((*p)->endgroup3()) { + if(level==0) + reportError("endgroup3 without matching begingroup3"); + else + level--; + } + } + + return pic; +} + + +} // namespace camp |