/***** * runpicture.in * * Runtime functions for picture operations. * *****/ pen => primPen() pair => primPair() triple => primTriple() path => primPath() path3 => primPath3() picture* => primPicture() Intarray* => IntArray() Intarray2* => IntArray2() realarray* => realArray() realarray2* => realArray2() patharray* => pathArray() penarray* => penArray() penarray2* => penArray2() pairarray* => pairArray() pairarray2* => pairArray2() triplearray* => tripleArray() triplearray2* => tripleArray2() transform => primTransform() callablePen* => penFunction() #include "picture.h" #include "drawelement.h" #include "path.h" #include "array.h" #include "arrayop.h" #include "drawpath.h" #include "drawfill.h" #include "drawclipbegin.h" #include "drawclipend.h" #include "drawgsave.h" #include "drawgrestore.h" #include "drawgroup.h" #include "drawverbatim.h" #include "drawlabel.h" #include "drawlayer.h" #include "drawimage.h" #include "drawpath3.h" #include "drawsurface.h" using namespace camp; using namespace settings; using namespace vm; typedef array Intarray; typedef array Intarray2; typedef array realarray; typedef array realarray2; typedef array pairarray; typedef array pairarray2; typedef array triplearray; typedef array triplearray2; typedef array patharray; typedef array penarray; typedef array penarray2; typedef callable callablePen; using types::IntArray; using types::IntArray2; using types::realArray; using types::realArray2; using types::pairArray; using types::pairArray2; using types::tripleArray; using types::tripleArray2; using types::pathArray; using types::penArray; using types::penArray2; static transform ZeroTransform=transform(0.0,0.0,0.0,0.0,0.0,0.0); transform getTransform(xmap_t &xmap, picture::nodelist::iterator p) { string s=(*p)->KEY; transform t; // Don't apply xmap without an explicit corresponding key size_t n=s.length(); if(n == 0 || s.substr(n-1) != "1") return t; xmap_t::iterator q=xmap.find(s.substr(0,n-2)); if(q != xmap.end()) { xtransform_t& v=q->second; if(!v.empty()) { t=v.front(); v.pop_front(); } } return t; } function *transformFunction() { return new function(primTransform()); } function *penFunction() { return new function(primPen(),primInt(),primInt()); } // Ignore unclosed begingroups but not spurious endgroups. const char *nobegin="endgroup without matching begingroup"; array *emptyarray=new array(0); array *nop(array *a) { return a; } triple Zero; string defaultformat3="prc"; // Autogenerated routines: picture* :newPicture() { return new picture(); } bool empty(picture *f) { return f->null(); } void erase(picture *f) { f->nodes.clear(); } pair min(picture *f) { return f->bounds().Min(); } pair max(picture *f) { return f->bounds().Max(); } pair size(picture *f) { bbox b=f->bounds(); return b.Max()-b.Min(); } void _draw(picture *f, path g, pen p) { f->append(new drawPath(g,p)); } void fill(picture *f, patharray *g, pen p=CURRENTPEN, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawFill(*copyarray(g),false,p)); } void latticeshade(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, penarray2 *p, transform t=identity, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawLatticeShade(*copyarray(g),stroke,fillrule,*copyarray(p), t)); } void axialshade(picture *f, patharray *g, bool stroke=false, pen pena, pair a, bool extenda=true, pen penb, pair b, bool extendb=true, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawAxialShade(*copyarray(g),stroke,pena,a,extenda,penb,b, extendb)); } void radialshade(picture *f, patharray *g, bool stroke=false, pen pena, pair a, real ra, bool extenda=true, pen penb, pair b, real rb, bool extendb=true, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawRadialShade(*copyarray(g),stroke,pena,a,ra,extenda, penb,b,rb,extendb)); } void gouraudshade(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, penarray *p, pairarray *z, Intarray *edges, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; checkArrays(p,z); checkArrays(z,edges); f->append(new drawGouraudShade(*copyarray(g),stroke,fillrule,*copyarray(p), *copyarray(z),*copyarray(edges))); } void gouraudshade(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, penarray *p, Intarray *edges, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; size_t n=checkArrays(p,edges); size_t m=checkArray(g); array *z=new array(n); Int k=0; Int in=(Int) n; for(size_t j=0; j < m; ++j) { path *P=read(g,j); assert(P); Int stop=Min(P->size(),in-k); mem::vector& nodes=P->Nodes(); for(Int i=0; i < stop; ++i) (*z)[k++]=nodes[i].point; } checkArrays(p,z); f->append(new drawGouraudShade(*copyarray(g),stroke,fillrule,*copyarray(p), *z,*copyarray(edges))); } void tensorshade(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, penarray2 *p, patharray *b=NULL, pairarray2 *z=emptyarray, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; array *(*copyarray2)(array *a)=copy ? copyArray2 : nop; size_t n=checkArrays(p,b ? b : g); array& G=*copyarray(g); array& B=b ? *copyarray(b) : G; size_t nz=checkArray(z); if(nz != 0) checkEqual(nz,n); f->append(new drawTensorShade(G,stroke,fillrule,*copyarray2(p),B, *copyarray2(z))); } void functionshade(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, string shader=emptystring, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawFunctionShade(*copyarray(g),stroke,fillrule,shader)); } // Clip a picture to a superpath using the given fill rule. // Subsequent additions to the picture will not be affected by the clipping. void clip(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; drawClipBegin *begin=new drawClipBegin(*copyarray(g),stroke,fillrule,true); f->enclose(begin,new drawClipEnd(true,begin)); } void beginclip(picture *f, patharray *g, bool stroke=false, pen fillrule=CURRENTPEN, bool copy=true) { array *(*copyarray)(array *a)=copy ? copyArray : nop; f->append(new drawClipBegin(*copyarray(g),stroke,fillrule,false)); } void endclip(picture *f) { f->append(new drawClipEnd(false)); } void gsave(picture *f) { f->append(new drawGsave()); } void grestore(picture *f) { f->append(new drawGrestore()); } void begingroup(picture *f) { f->append(new drawBegin()); } void endgroup(picture *f) { f->append(new drawEnd()); } void _begingroup3(picture *f, string name, real compression, real granularity, bool closed, bool tessellate, bool dobreak, bool nobreak, triple center, Int interaction) { f->append(new drawBegin3(name,compression,granularity, closed,tessellate,dobreak,nobreak, center,(Interaction) intcast(interaction))); } void endgroup3(picture *f) { f->append(new drawEnd3()); } void add(picture *dest, picture *src) { dest->add(*src); } void prepend(picture *dest, picture *src) { dest->prepend(*src); } void postscript(picture *f, string s) { f->append(new drawVerbatim(PostScript,s)); } void tex(picture *f, string s) { f->append(new drawVerbatim(TeX,s)); } void postscript(picture *f, string s, pair min, pair max) { f->append(new drawVerbatim(PostScript,s,min,max)); } void tex(picture *f, string s, pair min, pair max) { f->append(new drawVerbatim(TeX,s,min,max)); } void texpreamble(string s) { string t=s+"\n"; processDataStruct &pd=processData(); pd.TeXpipepreamble.push_back(t); pd.TeXpreamble.push_back(t); } void deletepreamble() { if(getSetting("inlinetex")) { unlink(buildname(outname(),"pre").c_str()); } } void _labelpath(picture *f, string s, string size, path g, string justify, pair offset, pen p) { f->append(new drawLabelPath(s,size,g,justify,offset,p)); } void texreset() { processDataStruct &pd=processData(); pd.TeXpipepreamble.clear(); pd.TeXpreamble.clear(); pd.tex.pipeclose(); } void layer(picture *f) { f->append(new drawLayer()); } void newpage(picture *f) { f->append(new drawNewPage()); } void _image(picture *f, realarray2 *data, pair initial, pair final, penarray *palette=NULL, transform t=identity, bool copy=true, bool antialias=false) { array *(*copyarray)(array *a)=copy ? copyArray : nop; array *(*copyarray2)(array *a)=copy ? copyArray2 : nop; f->append(new drawPaletteImage(*copyarray2(data),*copyarray(palette), t*matrix(initial,final),antialias)); } void _image(picture *f, penarray2 *data, pair initial, pair final, transform t=identity, bool copy=true, bool antialias=false) { array *(*copyarray2)(array *a)=copy ? copyArray2 : nop; f->append(new drawNoPaletteImage(*copyarray2(data),t*matrix(initial,final), antialias)); } void _image(picture *f, callablePen *F, Int width, Int height, pair initial, pair final, transform t=identity, bool antialias=false) { f->append(new drawFunctionImage(Stack,F,width,height, t*matrix(initial,final),antialias)); } string nativeformat() { return nativeformat(); } bool latex() { return latex(getSetting("tex")); } bool pdf() { return pdf(getSetting("tex")); } void _shipout(string prefix=emptystring, picture *f, picture *preamble=NULL, string format=emptystring, bool wait=false, bool view=true, transform T=identity) { if(prefix.empty()) prefix=outname(); picture *result=new picture; unsigned level=0; xmap_t xmap=processData().xmap; transform Tinv=inverse(T); for(picture::nodelist::iterator p=f->nodes.begin(); p != f->nodes.end(); ++p) { transform t=getTransform(xmap,p); bool Delete=(t == ZeroTransform); if(!Delete && !t.isIdentity()) t=T*t*Tinv; picture *group=new picture; assert(*p); if((*p)->endgroup()) error(nobegin); if((*p)->begingroup()) { ++level; while(p != f->nodes.end() && level) { if(!Delete) { drawElement *e=t.isIdentity() ? *p : (*p)->transformed(t); group->append(e); } ++p; if(p == f->nodes.end()) break; assert(*p); if((*p)->begingroup()) ++level; if((*p)->endgroup()) { if(level) --level; else error(nobegin); } } } if(p == f->nodes.end()) break; assert(*p); if(!Delete) { drawElement *e=t.isIdentity() ? *p : (*p)->transformed(t); group->append(e); result->add(*group); } delete group; } result->shipout(preamble,prefix,format,wait,view); delete result; } void shipout3(string prefix, picture *f, string format=emptystring, real width, real height, real angle, real zoom, triple m, triple M, pair shift, pair margin, realarray2 *t, realarray *background, triplearray *lights, realarray2 *diffuse, realarray2 *specular, bool view=true) { size_t n=checkArrays(lights,diffuse); checkEqual(n,checkArray(specular)); real *T,*Background,*Diffuse,*Specular; triple *Lights; copyArray2C(T,t,true,4); copyArrayC(Background,background); copyArrayC(Lights,lights); copyArray2C(Diffuse,diffuse,false,4,UseGC); copyArray2C(Specular,specular,false,4,UseGC); f->shipout3(prefix,format,width,height,angle,zoom,m,M,shift,margin,T, Background,n,Lights,Diffuse,Specular,view); delete[] Background; delete[] T; } void shipout3(string prefix, picture *f, string format=defaultformat3) { f->shipout3(prefix,format); } void xmap(string key, transform t=identity) { processDataStruct *P=&processData(); xmap_t &xmap=P->xmap; xmap_t::iterator p=xmap.find(key); if(p != xmap.end()) p->second.push_back(t); else { xtransform_t *v=new xtransform_t(); v->push_back(t); xmap[key]=*v; } P->xmapCount++; } void deconstruct(picture *f, picture *preamble=NULL, transform T=identity) { unsigned level=0; bool first=pdf(getSetting("tex")); string prefix=outname(); const string xformat="svg"; openpipeout(); const string Done="Done"; const string Error="Error"; xmap_t xmap=processData().xmap; transform Tinv=inverse(T); picture *F=new picture(true); for(picture::nodelist::iterator p=f->nodes.begin(); p != f->nodes.end();) { picture *group=new picture; transform t=getTransform(xmap,p); bool Delete=(t == ZeroTransform); if(!Delete && !t.isIdentity()) t=T*t*Tinv; assert(*p); if((*p)->endgroup()) { fprintf(pipeout,"%s\n",Error.c_str()); fflush(pipeout); error(nobegin); } bool clip=false; if((*p)->begingroup()) { string key=(*p)->KEY; ++level; while(p != f->nodes.end() && level) { if(!Delete) { drawElement *e=t.isIdentity() ? *p : (*p)->transformed(t); group->append(e); if((*p)->endclip()) clip=true; } ++p; if(p == f->nodes.end()) break; assert(*p); if((*p)->begingroup()) ++level; if((*p)->endgroup()) { if(level) --level; else { fprintf(pipeout,"%s\n",Error.c_str()); fflush(pipeout); error(nobegin); } if(level == 0) (*p)->KEY=key; } } } if(p != f->nodes.end()) { if(!Delete) { drawElement *e=t.isIdentity() ? *p : (*p)->transformed(t); group->append(e); bbox b=group->bounds(); if(!b.empty && b.right > b.left && b.top > b.bottom) { if((*p)->endclip()) clip=true; if(first) first=false; else F->append(new drawNewPage(b)); F->append(new drawBBox(b)); for(picture::nodelist::iterator g=group->nodes.begin(); !(g == group->nodes.end()); ++g) F->append(*g); fprintf(pipeout,"KEY=%s%d\n",e->KEY.c_str(),clip); const char *oldlocale=setlocale(LC_NUMERIC,NULL); bool override=oldlocale && strcmp(oldlocale,"C") != 0; if(override) { oldlocale=StrdupNoGC(oldlocale); setlocale(LC_NUMERIC,"C"); } fprintf(pipeout,"%g %g %g %g\n",b.left,b.bottom,b.right,b.top); if(override) { setlocale(LC_NUMERIC,oldlocale); delete[] oldlocale; } fflush(pipeout); } } else { drawElement *e=t.isIdentity() ? *p : (*p)->transformed(t); fprintf(pipeout,"KEY=%s2\n",e->KEY.c_str()); fprintf(pipeout,"0 0 0 0\n"); } ++p; } delete group; } string outname=buildname(prefix,xformat); F->shipout(preamble,stripExt(outname),xformat,false,false); fprintf(pipeout,"%s\n",Done.c_str()); fflush(pipeout); delete F; } // Three-dimensional picture and surface operations // Bezier curve void _draw(picture *f, path3 g, triple center=Zero, penarray *p, real opacity, real shininess, real metallic, real fresnel0, Int interaction=0) { size_t n=g.size(); for(unsigned int i=0; i < n; ++i) f->append(new drawPath3(g.subpath((Int) i,Int(i+1)),center,*p,opacity, shininess,metallic,fresnel0, (Interaction) intcast(interaction))); } // Bezier patch void draw(picture *f, triplearray2 *P, triple center, bool straight, penarray *p, real opacity, real shininess, real metallic, real fresnel0, penarray *colors, Int interaction, Int digits, bool primitive=false) { f->append(new drawBezierPatch(*P,center,straight,*p,opacity,shininess, metallic,fresnel0,*colors, (Interaction) intcast(interaction), digits,primitive)); } // Bezier triangle void drawbeziertriangle(picture *f, triplearray2 *P, triple center, bool straight, penarray *p, real opacity, real shininess, real metallic, real fresnel0, penarray *colors, Int interaction, Int digits, bool primitive=false) { f->append(new drawBezierTriangle(*P,center,straight,*p,opacity,shininess, metallic,fresnel0,*colors, (Interaction) intcast(interaction), digits,primitive)); } // General NURBS curve void draw(picture *f, triplearray *P, realarray *knot, realarray *weights=emptyarray, pen p) { f->append(new drawNurbsPath3(*P,knot,weights,p)); } // General NURBS surface void draw(picture *f, triplearray2 *P, realarray *uknot, realarray *vknot, realarray2 *weights=emptyarray, penarray *p, real opacity, real shininess,real metallic, real fresnel0, penarray *colors) { f->append(new drawNurbs(*P,uknot,vknot,weights,*p,opacity,shininess, metallic,fresnel0,*colors)); } // Sphere primitive void drawSphere(picture *f, realarray2 *t, bool half=false, penarray *p, real opacity, real shininess, real metallic, real fresnel0, Int type) { f->append(new drawSphere(*t,half,*p,opacity,shininess,metallic,fresnel0, intcast(type))); } // Cylinder primitive void drawCylinder(picture *f, realarray2 *t, penarray *p, real opacity, real shininess, real metallic, real fresnel0, bool core=false) { f->append(new drawCylinder(*t,*p,opacity,shininess,metallic,fresnel0,core)); } // Disk primitive void drawDisk(picture *f, realarray2 *t, penarray *p, real opacity, real shininess, real metallic, real fresnel0) { f->append(new drawDisk(*t,*p,opacity,shininess,metallic,fresnel0)); } // Tube primitive void drawTube(picture *f, triplearray *g, real width, penarray *p, real opacity, real shininess, real metallic, real fresnel0, triple min, triple max, bool core=false) { f->append(new drawTube(*g,width,*p,opacity,shininess,metallic,fresnel0, min,max,core)); } // Draw pixel void drawpixel(picture *f, triple v, pen p, real width=1.0) { f->append(new drawPixel(v,p,width)); } // Draw triangles void draw(picture *f, triplearray *v, Intarray2 *vi, triple center=Zero, triplearray *n, Intarray2 *ni, penarray *p, real opacity, real shininess, real metallic, real fresnel0, penarray *c=emptyarray, Intarray2 *ci=emptyarray, Int interaction) { f->append(new drawTriangles(*v,*vi,center,*n,*ni,*p,opacity,shininess, metallic,fresnel0,*c,*ci, (Interaction) intcast(interaction))); } triple min3(picture *f) { return f->bounds3().Min(); } triple max3(picture *f) { return f->bounds3().Max(); } triple size3(picture *f) { bbox3 b=f->bounds3(); return b.Max()-b.Min(); } pair minratio(picture *f) { return f->ratio(::min); } pair maxratio(picture *f) { return f->ratio(::max); } bool is3D(picture *f) { return f->have3D(); }