diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/asymptote/texfile.cc |
Initial commit
Diffstat (limited to 'graphics/asymptote/texfile.cc')
-rw-r--r-- | graphics/asymptote/texfile.cc | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/graphics/asymptote/texfile.cc b/graphics/asymptote/texfile.cc new file mode 100644 index 0000000000..fc0e575c22 --- /dev/null +++ b/graphics/asymptote/texfile.cc @@ -0,0 +1,735 @@ +/***** + * texfile.cc + * John Bowman 2003/03/14 + * + * Encapsulates the writing of commands to a TeX file. + *****/ + +#include <ctime> +#include <cfloat> + +#include "texfile.h" +#include "errormsg.h" + +using std::ofstream; +using settings::getSetting; +using settings::ps2tex; +using settings::tex2ps; +using vm::array; +using vm::read; + +namespace camp { + +texfile::texfile(const string& texname, const bbox& box, bool pipe) + : box(box) +{ + texengine=getSetting<string>("tex"); + inlinetex=getSetting<bool>("inlinetex"); + Hoffset=inlinetex ? box.right : box.left; + out=new ofstream(texname.c_str()); + if(!out || !*out) + reportError("Cannot write to "+texname); + out->setf(std::ios::fixed); + out->precision(6); + texdocumentclass(*out,pipe); + resetpen(); + level=0; +} + +texfile::~texfile() +{ + if(out) { + delete out; + out=NULL; + } +} + +void texfile::miniprologue() +{ + texpreamble(*out,processData().TeXpreamble,false); + if(settings::latex(texengine)) { + *out << "\\pagestyle{empty}" << newl + << "\\textheight=2048pt" << newl + << "\\textwidth=2048pt" << newl + << "\\begin{document}" << newl; + latexfontencoding(*out); + } else if(settings::context(texengine)) { + *out << "\\setuppagenumbering[location=]" << newl + << "\\usetypescript[modern]" << newl + << "\\starttext\\hbox{%" << newl; + } else *out << "\\nopagenumbers" << newl; +} + +void texfile::prologue() +{ + if(inlinetex) { + string prename=buildname(settings::outname(),"pre"); + std::ofstream *outpreamble=new std::ofstream(prename.c_str()); + texpreamble(*outpreamble,processData().TeXpreamble,true,false); + outpreamble->close(); + } + + texdefines(*out,processData().TeXpreamble,false); + double width=box.right-box.left; + double height=box.top-box.bottom; + if(!inlinetex) { + if(settings::context(texengine)) { + *out << "\\definepapersize[asy][width=" << width << "bp,height=" + << height << "bp]" << newl + << "\\setuppapersize[asy][asy]" << newl; + } else if(settings::pdf(texengine)) { + double voffset=0.0; + if(settings::latex(texengine)) { + if(height < 12.0) voffset=height-12.0; + } else if(height < 10.0) voffset=height-10.0; + + if(width > 0) + *out << "\\pdfpagewidth=" << width << "bp" << newl; + *out << "\\ifx\\pdfhorigin\\undefined" << newl + << "\\hoffset=-1in" << newl + << "\\voffset=" << voffset-72.0 << "bp" << newl; + if(height > 0) + *out << "\\pdfpageheight=" << height << "bp" + << newl; + *out << "\\else" << newl + << "\\pdfhorigin=0bp" << newl + << "\\pdfvorigin=" << voffset << "bp" << newl; + if(height > 0) + *out << "\\pdfpageheight=" << height << "bp" << newl; + *out << "\\fi" << newl; + } + } + + if(settings::xe(texengine) && !inlinetex) + *out << "\\usepackage{everypage}%" << newl; + + if(settings::latex(texengine)) { + *out << "\\setlength{\\unitlength}{1pt}" << newl; + if(!inlinetex) { + *out << "\\pagestyle{empty}" << newl + << "\\textheight=" << height+18.0 << "bp" << newl + << "\\textwidth=" << width+18.0 << "bp" << newl; + if(settings::pdf(texengine)) + *out << "\\oddsidemargin=-17.61pt" << newl + << "\\evensidemargin=\\oddsidemargin" << newl + << "\\topmargin=-37.01pt" << newl; + *out << "\\begin{document}" << newl; + } + latexfontencoding(*out); + } else { + if(!inlinetex) { + if(settings::context(texengine)) { + *out << "\\setuplayout[" + << "backspace=0pt,topspace=0pt," + << "header=0pt,headerdistance=0pt,footer=0pt]" << newl + << "\\setuppagenumbering[location=]" << endl + << "\\usetypescript[modern]" << newl + << "\\starttext\\hbox{%" << newl; + } else { + *out << "\\footline={}" << newl; + if(settings::pdf(texengine)) { + *out << "\\hoffset=-20pt" << newl + << "\\voffset=0pt" << newl; + } else { + *out << "\\hoffset=36.6pt" << newl + << "\\voffset=54.0pt" << newl; + } + } + } + } + + beginpage(); +} + +void texfile::beginlayer(const string& psname, bool postscript) +{ + if(box.right > box.left && box.top > box.bottom) { + if(postscript) { + if(settings::context(texengine)) + *out << "\\externalfigure[" << psname << "]%" << newl; + else { + *out << "{\\catcode`\"=12%" << newl + << "\\includegraphics"; + bool pdf=settings::pdf(texengine); + string quote; + string name=stripExt(psname); + if(inlinetex) { + size_t pos=name.rfind("-"); + if(pos < string::npos) name="\\ASYprefix\\jobname"+name.substr(pos); + } else { + if(!pdf) name=psname; + if(stripDir(name) != name) + quote="\""; + } + + if(pdf) *out << "{" << quote << name << quote << ".pdf}%" << newl; + else { + *out << "[bb=" << box.left << " " << box.bottom << " " + << box.right << " " << box.top << "]" + << "{" << quote << name << quote << "}%" << newl; + } + *out << "}%" << newl; + } + if(!inlinetex) + *out << "\\kern " << (box.left-box.right)*ps2tex << "pt%" << newl; + } else { + *out << "\\leavevmode\\vbox to " << (box.top-box.bottom)*ps2tex + << "pt{}%" << newl; + if(inlinetex) + *out << "\\kern " << (box.right-box.left)*ps2tex << "pt%" << newl; + } + } +} + +void texfile::endlayer() +{ + if(inlinetex && (box.right > box.left && box.top > box.bottom)) + *out << "\\kern " << (box.left-box.right)*ps2tex << "pt%" << newl; +} + +void texfile::writeshifted(path p, bool newPath) +{ + write(p.transformed(shift(pair(-Hoffset,-box.bottom))),newPath); +} + +void texfile::setlatexcolor(pen p) +{ + if(p.cmyk() && (!lastpen.cmyk() || + (p.cyan() != lastpen.cyan() || + p.magenta() != lastpen.magenta() || + p.yellow() != lastpen.yellow() || + p.black() != lastpen.black()))) { + *out << "\\definecolor{ASYcolor}{cmyk}{" + << p.cyan() << "," << p.magenta() << "," << p.yellow() << "," + << p.black() << "}\\color{ASYcolor}" << newl; + } else if(p.rgb() && (!lastpen.rgb() || + (p.red() != lastpen.red() || + p.green() != lastpen.green() || + p.blue() != lastpen.blue()))) { + *out << "\\definecolor{ASYcolor}{rgb}{" + << p.red() << "," << p.green() << "," << p.blue() + << "}\\color{ASYcolor}" << newl; + } else if(p.grayscale() && (!lastpen.grayscale() || + p.gray() != lastpen.gray())) { + *out << "\\definecolor{ASYcolor}{gray}{" + << p.gray() + << "}\\color{ASYcolor}" << newl; + } +} + +void texfile::setfont(pen p) +{ + bool latex=settings::latex(texengine); + + if(latex) setlatexfont(*out,p,lastpen); + settexfont(*out,p,lastpen,latex); + + lastpen.setfont(p); +} + +void texfile::setpen(pen p) +{ + bool latex=settings::latex(texengine); + + p.convert(); + if(p == lastpen) return; + + if(latex) setlatexcolor(p); + else setcolor(p,settings::beginspecial(texengine),settings::endspecial()); + + setfont(p); +} + +void texfile::beginpicture(const bbox& b) +{ + verbatim(settings::beginpicture(texengine)); + if(!settings::context(texengine)) { + verbatim("("); + double width=b.right-b.left; + double height=b.top-b.bottom; + write(width*ps2tex); + verbatim(","); + write(height*ps2tex); + verbatim(")"); + } + verbatimline("%"); +} + +void texfile::endpicture(const bbox& b) +{ + verbatimline(settings::endpicture(texengine)); + verbatim("\\kern"); + double width=b.right-b.left; + write(-width*ps2tex); + verbatimline("pt%"); +} + +void texfile::gsave(bool) +{ + *out << settings::beginspecial(texengine); + psfile::gsave(true); + *out << settings::endspecial() << newl; +} + +void texfile::grestore(bool) +{ + *out << settings::beginspecial(texengine); + psfile::grestore(true); + *out << settings::endspecial() << newl; +} + +void texfile::beginspecial() +{ + *out << settings::beginspecial(texengine); +} + +void texfile::endspecial() +{ + *out << settings::endspecial() << newl; +} + +void texfile::beginraw() +{ + *out << "\\ASYraw{" << newl; +} + +void texfile::endraw() +{ + *out << "}%" << newl; +} + +void texfile::put(const string& label, const transform& T, const pair& z, + const pair& align) +{ + double sign=settings::pdf(texengine) ? 1.0 : -1.0; + + if(label.empty()) return; + + bool trans=!T.isIdentity(); + + *out << "\\ASYalign"; + if(trans) *out << "T"; + *out << "(" << (z.getx()-Hoffset)*ps2tex + << "," << (z.gety()-box.bottom)*ps2tex + << ")(" << align.getx() + << "," << align.gety() + << ")"; + if(trans) + *out << "{" << T.getxx() << " " << sign*T.getyx() + << " " << sign*T.getxy() << " " << T.getyy() << "}"; + *out << "{" << label << "}%" << newl; +} + +void texfile::epilogue(bool pipe) +{ + endpage(); + if(settings::latex(texengine)) + *out << "\\end{document}" << newl; + else if(settings::context(texengine)) + *out << "}\\stoptext" << newl; + else + *out << "\\bye" << newl; + out->flush(); +} + +string svgtexfile::nl="{?nl}%\n"; + +void svgtexfile::beginspecial() +{ + inspecial=true; + out->unsetf(std::ios::fixed); + *out << "\\catcode`\\#=11%" << newl + << "\\special{dvisvgm:raw" << nl; +} + +void svgtexfile::endspecial() +{ + inspecial=false; + *out << "}\\catcode`\\#=6%" << newl; + out->setf(std::ios::fixed); +} + +void svgtexfile::begintransform() +{ + bbox b=box; + b.left=-Hoffset; + b=svgbbox(b); + *out << "<g transform='matrix(" << tex2ps << " 0 0 " << tex2ps <<" " + << b.left << " " << b.top << ")'>" << nl; +} + +void svgtexfile::endtransform() +{ + *out << "</g>"; +} + +void svgtexfile::gsave(bool) +{ + if(clipstack.size() < 1) + clipstack.push(0); + else + clipstack.push(clipcount); + *out << "\\special{dvisvgm:raw <g>}%" << newl; + pens.push(lastpen); +} + +void svgtexfile::grestore(bool) +{ + if(pens.size() < 1 || clipstack.size() < 1) + reportError("grestore without matching gsave"); + lastpen=pens.top(); + pens.pop(); + clipstack.pop(); + *out << "\\special{dvisvgm:raw </g>}%" << newl; +} + +void svgtexfile::clippath() +{ + if(clipstack.size() > 0) { + size_t count=clipstack.top(); + if(count > 0) + *out << "clip-path='url(#clip" << count << ")' "; + } +} + +void svgtexfile::beginpath() +{ + *out << "<path "; + clippath(); + *out << "d='"; +} + +void svgtexfile::endpath() +{ + *out << "/>" << nl; +} + +void svgtexfile::dot(path p, pen q, bool newPath) +{ + beginspecial(); + begintransform(); + *out << "<circle "; + clippath(); + pair z=p.point((Int) 0); + *out << "cx='" << z.getx()*ps2tex + << "' cy='" << -z.gety()*ps2tex + << "' r='" << 0.5*q.width()*ps2tex; +} + +void svgtexfile::beginclip() +{ + beginspecial(); + begintransform(); + *out << "<clipPath "; + clippath(); + ++clipcount; + *out << "id='clip" << clipcount << "'>" << nl; + beginpath(); + if(clipstack.size() > 0) + clipstack.pop(); + clipstack.push(clipcount); +} + +void svgtexfile::endclip0(const pen &p) +{ + *out << "'"; + fillrule(p,"clip"); + endpath(); + *out << "</clipPath>" << nl; +} + +void svgtexfile::endclip(const pen &p) +{ + endclip0(p); + endtransform(); + endspecial(); +} + +void svgtexfile::fillrule(const pen& p, const string& type) +{ + if(p.Fillrule() != lastpen.Fillrule()) + *out << " " << type << "-rule='" << + (p.evenodd() ? "evenodd" : "nonzero") << "'"; + lastpen.setfillrule(p); +} + +void svgtexfile::color(const pen &p, const string& type) +{ + *out << "' " << type << "='#" << rgbhex(p) << "'"; + double opacity=p.opacity(); + if(opacity != 1.0) + *out << " opacity='" << opacity << "'"; +} + +void svgtexfile::fill(const pen &p) +{ + color(p,"fill"); + fillrule(p); + endpath(); + endtransform(); + endspecial(); +} + +void svgtexfile::properties(const pen& p) +{ + if(p.cap() != lastpen.cap()) + *out << " stroke-linecap='" << PSCap[p.cap()] << "'"; + + if(p.join() != lastpen.join()) + *out << " stroke-linejoin='" << Join[p.join()] << "'"; + + if(p.miter() != lastpen.miter()) + *out << " stroke-miterlimit='" << p.miter()*ps2tex << "'"; + + if(p.width() != lastpen.width()) + *out << " stroke-width='" << p.width()*ps2tex << "'"; + + const LineType *linetype=p.linetype(); + const LineType *lastlinetype=lastpen.linetype(); + + if(!(linetype->pattern == lastlinetype->pattern)) { + size_t n=linetype->pattern.size(); + if(n > 0) { + *out << " stroke-dasharray='"; + *out << vm::read<double>(linetype->pattern,0)*ps2tex; + for(size_t i=1; i < n; ++i) + *out << "," << vm::read<double>(linetype->pattern,i)*ps2tex; + *out << "'"; + } + } + + if(linetype->offset != lastlinetype->offset) + *out << " stroke-dashoffset='" << linetype->offset*ps2tex << "'"; + + lastpen=p; +} + +void svgtexfile::stroke(const pen &p, bool dot) +{ + if(dot) + color(p,"fill"); + else { + color(p,"fill='none' stroke"); + properties(p); + } + endpath(); + endtransform(); + endspecial(); +} + +void svgtexfile::strokepath() +{ + reportWarning("SVG does not support strokepath"); +} + +void svgtexfile::begingradientshade(bool axial, ColorSpace colorspace, + const pen& pena, const pair& a, double ra, + const pen& penb, const pair& b, double rb) +{ + string type=axial ? "linear" : "radial"; + beginspecial(); + *out << "<" << type << "Gradient id='grad" << gradientcount; + if(axial) { + *out << "' x1='" << a.getx()*ps2tex << "' y1='" << -a.gety()*ps2tex + << "' x2='" << b.getx()*ps2tex << "' y2='" << -b.gety()*ps2tex; + } else { + *out << "' cx='" << b.getx()*ps2tex << "' cy='" << -b.gety()*ps2tex + << "' r='" << rb*ps2tex; + } + *out <<"' gradientUnits='userSpaceOnUse'>" << nl + << "<stop offset='0' stop-color='#" << rgbhex(pena) << "'/>" << nl + << "<stop offset='1' stop-color='#" << rgbhex(penb) << "'/>" << nl + << "</" << type << "Gradient>" << nl; + begintransform(); + beginpath(); +} + +void svgtexfile::gradientshade(bool axial, ColorSpace colorspace, + const pen& pena, const pair& a, double ra, + bool, const pen& penb, const pair& b, + double rb, bool) + +{ + *out << "' fill='url(#grad" << gradientcount << ")'"; + fillrule(pena); + endpath(); + ++gradientcount; + endtransform(); + endspecial(); +} + +// Return the point on the line through p and q that is closest to z. +pair closest(pair p, pair q, pair z) +{ + pair u=q-p; + double denom=dot(u,u); + return denom == 0.0 ? p : p+dot(z-p,u)/denom*u; +} + +void svgtexfile::gouraudshade(const pen& p0, const pair& z0, + const pen& p1, const pair& z1, + const pen& p2, const pair& z2) +{ + string hex[]={rgbhex(p0),rgbhex(p1),rgbhex(p2)}; + pair Z[]={z0,z1,z2}; + + *out << "<defs>" << nl + << "<filter id='colorAdd'>" << nl + << "<feBlend in='SourceGraphic' in2='BackgroundImage'/>" << nl + << "</filter>"; + + for(size_t k=0; k < 3; ++k) { + pair z=Z[k]; + pair opp=closest(Z[(k+1) % 3],Z[(k+2) % 3],z); + *out << "<linearGradient id='grad-" << gouraudcount << "-" << k + << "' gradientUnits='userSpaceOnUse'" << nl + << " x1='" << z.getx()*ps2tex << "' y1='" << -z.gety()*ps2tex + << "' x2='" << opp.getx()*ps2tex << "' y2='" << -opp.gety()*ps2tex + << "'>" << nl + << "<stop offset='0' stop-color='#" << hex[k] + << "' stop-opacity='1'/>" << nl + << "<stop offset='1' stop-color='#" << hex[k] + << "' stop-opacity='0'/>" << nl + << "</linearGradient>" << nl; + } + *out << "<polygon "; + clippath(); + *out << "id='triangle" << gouraudcount << "' points='" + << z0.getx()*ps2tex << "," << -z0.gety()*ps2tex << " " + << z1.getx()*ps2tex << "," << -z1.gety()*ps2tex << " " + << z2.getx()*ps2tex << "," << -z2.gety()*ps2tex << "'/>" << nl + << "</defs>" << nl; + *out << "<use xlink:href='#triangle" << gouraudcount + << "' fill='url(#grad-" << gouraudcount << "-" + << "0)'/>" << nl + << "<use xlink:href='#triangle" << gouraudcount + << "' fill='url(#grad-" << gouraudcount << "-" + << "1)' filter='url(#colorAdd)'/>" << nl + << "<use xlink:href='#triangle" << gouraudcount + << "' fill='url(#grad-" << gouraudcount << "-" + << "2)' filter='url(#colorAdd)'/>" << nl; + ++gouraudcount; +} + +void svgtexfile::begingouraudshade(const vm::array& pens, + const vm::array& vertices, + const vm::array& edges) +{ + size_t size=pens.size(); + if(size == 0) return; + beginclip(); +} + +void svgtexfile::gouraudshade(const pen& pentype, + const array& pens, const array& vertices, + const array& edges) +{ + size_t size=pens.size(); + if(size == 0) return; + + endclip0(pentype); + + pen *p0=NULL,*p1=NULL,*p2=NULL; + pair z0,z1,z2; + + for(size_t i=0; i < size; i++) { + Int edge=read<Int>(edges,i); + + switch(edge) { + case 0: + p0=read<pen *>(pens,i); + z0=read<pair>(vertices,i); + ++i; + if(i < size) { + p1=read<pen *>(pens,i); + z1=read<pair>(vertices,i); + ++i; + if(i < size) { + p2=read<pen *>(pens,i); + z2=read<pair>(vertices,i); + } + } + break; + case 1: + p0=read<pen *>(pens,i); + z0=read<pair>(vertices,i); + break; + case 2: + p1=read<pen *>(pens,i); + z1=read<pair>(vertices,i); + break; + default: + break; + } + if(p0 == NULL || p1 == NULL || p2 == NULL) + reportError("invalid edge flag"); + gouraudshade(*p0,z0,*p1,z1,*p2,z2); + } + endtransform(); + endspecial(); +} + +void svgtexfile::begintensorshade(const vm::array& pens, + const vm::array& boundaries, + const vm::array& z) +{ + beginspecial(); + *out << "<defs>" << nl; + + path g=read<path>(boundaries,0); + pair Z[]={g.point((Int) 0),g.point((Int) 3),g.point((Int) 2), + g.point((Int) 1)}; + + array *pi=read<array *>(pens,0); + if(checkArray(pi) != 4) + reportError("specify 4 pens for each path"); + string hex[]={rgbhex(read<pen>(pi,0)),rgbhex(read<pen>(pi,3)), + rgbhex(read<pen>(pi,2)),rgbhex(read<pen>(pi,1))}; + + *out << "<filter id='colorAdd'>" << nl + << "<feBlend in='SourceGraphic' in2='BackgroundImage'/>" << nl + << "</filter>"; + + pair mean=0.25*(Z[0]+Z[1]+Z[2]+Z[3]); + for(size_t k=0; k < 4; ++k) { + pair opp=(k % 2 == 0) ? Z[(k+2) % 4] : mean; + *out << "<linearGradient id='grad" << tensorcount << "-" << k + << "' gradientUnits='userSpaceOnUse'" << nl + << " x1='" << Z[k].getx()*ps2tex << "' y1='" << -Z[k].gety()*ps2tex + << "' x2='" << opp.getx()*ps2tex << "' y2='" << -opp.gety()*ps2tex + << "'>" << nl + << "<stop offset='0' stop-color='#" << hex[k] + << "' stop-opacity='1'/>" << nl + << "<stop offset='1' stop-color='#" << hex[k] + << "' stop-opacity='0'/>" << nl + << "</linearGradient>" << nl; + } + beginpath(); +} + +void svgtexfile::tensorshade(const pen& pentype, const vm::array& pens, + const vm::array& boundaries, const vm::array& z) +{ + *out << "' id='path" << tensorcount << "'"; + fillrule(pentype); + endpath(); + *out << "</defs></g>" << nl; + begintransform(); + *out << "<use xlink:href='#path" << tensorcount + << "' fill='url(#grad" << tensorcount << "-" + << "0)'/>" << nl + << "<use xlink:href='#path" << tensorcount + << "' fill='url(#grad" << tensorcount << "-" + << "2)' filter='url(#colorAdd)'/>" << nl + << "<use xlink:href='#path" << tensorcount + << "' fill='url(#grad" << tensorcount << "-" + << "1)' filter='url(#colorAdd)'/>" << nl + << "<use xlink:href='#path" << tensorcount + << "' fill='url(#grad" << tensorcount << "-" + << "3)' filter='url(#colorAdd)'/>" << nl; + + ++tensorcount; + endspecial(); +} + +} //namespace camp |