/*****
* 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<path *>(g,j);
assert(P);
Int stop=Min(P->size(),in-k);
mem::vector<solvedKnot>& 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<bool>("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<string>("tex"));
}
bool pdf()
{
return pdf(getSetting<string>("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;
picture::nodelist::iterator p;
xmap_t xmap=processData().xmap;
transform Tinv=inverse(T);
for(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);
}
}
result->shipout(preamble,prefix,format,wait,view);
}
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)
{
xmap_t &xmap=processData().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;
}
}
void deconstruct(picture *f, picture *preamble=NULL, transform T=identity)
{
unsigned level=0;
string prefix=outname();
const string xformat="svg";
openpipeout();
const string Done="Done";
const string Error="Error";
unsigned arg=0;
xmap_t xmap=processData().xmap;
transform Tinv=inverse(T);
for(picture::nodelist::iterator p=f->nodes.begin();;) {
if(p == f->nodes.end()) break;
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()) {
++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(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;
ostringstream buf;
buf << prefix << "_" << arg;
string outname=buildname(buf.str(),xformat);
group->shipout(preamble,outname,xformat,false,false);
fprintf(pipeout,"KEY=%s%d\n",e->KEY.c_str(),clip || f->havepng());
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);
++arg;
}
}
++p;
}
}
fprintf(pipeout,"%s\n",Done.c_str());
fflush(pipeout);
}
// 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,
triplearray *n, Intarray2 *ni,
penarray *p, real opacity, real shininess,
real metallic, real fresnel0,
penarray *c=emptyarray, Intarray2 *ci=emptyarray)
{
f->append(new drawTriangles(*v,*vi,*n,*ni,*p,opacity,shininess,metallic,
fresnel0,*c,*ci));
}
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();
}