/***** * drawelement.h * Andy Hammerlindl 2002/06/06 * * Abstract base class of any drawable item in camp. *****/ #ifndef DRAWELEMENT_H #define DRAWELEMENT_H #include #include "common.h" #include "bbox.h" #include "bbox3.h" #include "pen.h" #include "psfile.h" #include "texfile.h" #include "prcfile.h" #include "jsfile.h" #include "v3dfile.h" #include "glrender.h" #include "arrayop.h" #include "material.h" namespace camp { static const double pixelResolution=1.0; // Adaptive rendering constant. enum Interaction {EMBEDDED=0,BILLBOARD}; void copyArray4x4C(double*& dest, const vm::array *a); class box { pair p[4]; public: box() {} box(const pair& a, const pair& b, const pair& c, const pair& d) { p[0]=a; p[1]=b; p[2]=c; p[3]=d; } // Returns true if the line a--b intersects box b. bool intersect(const pair& a, const pair& b) const { for(Int i=0; i < 4; ++i) { pair A=p[i]; pair B=p[i < 3 ? i+1 : 0]; double de=(b.x-a.x)*(A.y-B.y)-(A.x-B.x)*(b.y-a.y); if(de != 0.0) { de=1.0/de; double t=((A.x-a.x)*(A.y-B.y)-(A.x-B.x)*(A.y-a.y))*de; double T=((b.x-a.x)*(A.y-a.y)-(A.x-a.x)*(b.y-a.y))*de; if(0 <= t && t <= 1 && 0 <= T && T <= 1) return true; } } return false; } pair operator [] (Int i) const {return p[i];} bool intersect(const box& b) const { for(Int i=0; i < 4; ++i) { pair A=b[i]; pair B=b[i < 3 ? i+1 : 0]; if(intersect(A,B)) return true; } return false; } double xmax() { return max(max(max(p[0].x,p[1].x),p[2].x),p[3].x); } double ymax() { return max(max(max(p[0].y,p[1].y),p[2].y),p[3].y); } double xmin() { return min(min(min(p[0].x,p[1].x),p[2].x),p[3].x); } double ymin() { return min(min(min(p[0].y,p[1].y),p[2].y),p[3].y); } }; class bbox2 { public: double x,y,X,Y; bbox2(size_t n, const triple *v) { Bounds(v[0]); for(size_t i=1; i < n; ++i) bounds(v[i]); } bbox2(const triple& m, const triple& M) { Bounds(m); bounds(triple(m.getx(),m.gety(),M.getz())); bounds(triple(m.getx(),M.gety(),m.getz())); bounds(triple(m.getx(),M.gety(),M.getz())); bounds(triple(M.getx(),m.gety(),m.getz())); bounds(triple(M.getx(),m.gety(),M.getz())); bounds(triple(M.getx(),M.gety(),m.getz())); bounds(M); } bbox2(const triple& m, const triple& M, const Billboard& BB) { Bounds(BB.transform(m)); bounds(BB.transform(triple(m.getx(),m.gety(),M.getz()))); bounds(BB.transform(triple(m.getx(),M.gety(),m.getz()))); bounds(BB.transform(triple(m.getx(),M.gety(),M.getz()))); bounds(BB.transform(triple(M.getx(),m.gety(),m.getz()))); bounds(BB.transform(triple(M.getx(),m.gety(),M.getz()))); bounds(BB.transform(triple(M.getx(),M.gety(),m.getz()))); bounds(BB.transform(M)); } // Is 2D bounding box formed by projecting 3d points in vector v offscreen? bool offscreen() { double eps=1.0e-2; double min=-1.0-eps; double max=1.0+eps; return X < min || x > max || Y < min || y > max; } void Bounds(const triple& v) { pair V=Transform2T(gl::dprojView,v); x=X=V.getx(); y=Y=V.gety(); } void bounds(const triple& v) { pair V=Transform2T(gl::dprojView,v); double a=V.getx(); double b=V.gety(); if(a < x) x=a; else if(a > X) X=a; if(b < y) y=b; else if(b > Y) Y=b; } }; typedef mem::vector boxvector; typedef mem::list bboxlist; typedef mem::map groupmap; typedef mem::vector groupsmap; typedef mem::map centerMap; inline bool operator < (const triple& a, const triple& b) { return a.getx() < b.getx() || (a.getx() == b.getx() && (a.gety() < b.gety() || (a.gety() == b.gety() && (a.getz() < b.getz())))); } class drawElement : public gc { public: string KEY; drawElement(const string& key="") : KEY(key == "" ? processData().KEY : key) {} virtual ~drawElement() {} static mem::vector centers; static centerMap centermap; static size_t centerIndex; static pen lastpen; static const triple zero; // Adjust the bbox of the picture based on the addition of this // element. The iopipestream is needed for determining label sizes. virtual void bounds(bbox&, iopipestream&, boxvector&, bboxlist&) {} virtual void bounds(const double*, bbox3&) {} virtual void bounds(bbox3& b) { bounds(NULL, b); } // Compute bounds on ratio (x,y)/z for 3d picture (not cached). virtual void ratio(const double *t, pair &b, double (*m)(double, double), double fuzz, bool &first) {} virtual void minratio(const double *t, pair &b, double fuzz, bool &first) { ratio(t,b,camp::min,fuzz,first); } virtual void maxratio(const double *t,pair &b, double fuzz, bool &first) { ratio(t,b,camp::max,fuzz,first); } virtual void ratio(pair &b, double (*m)(double, double), double fuzz, bool &first) { ratio(NULL,b,m,fuzz,first); } virtual void minratio(pair &b, double fuzz, bool &first) { minratio(NULL,b,fuzz,first); } virtual void maxratio(pair &b, double fuzz, bool &first) { maxratio(NULL,b,fuzz,first); } virtual bool islabel() {return false;} virtual bool isnewpage() {return false;} virtual bool islayer() {return false;} virtual bool is3D() {return false;} // Implement element as raw SVG code? virtual bool svg() {return false;} // Implement SVG element as png image? virtual bool svgpng() {return false;} virtual bool beginclip() {return false;} virtual bool endclip() {return false;} virtual bool begingroup() {return false;} virtual bool begingroup3() {return false;} virtual bool endgroup() {return false;} virtual bool endgroup3() {return false;} virtual const double* transf3() {return NULL;} virtual void save(bool b) {} // Output to a PostScript file virtual bool draw(psfile *) { return false; } // Output to a TeX file virtual bool write(texfile *, const bbox&) { return false; } // Output to a PRC file virtual bool write(prcfile *out, unsigned int *count, double compressionlimit, groupsmap& groups) { return false; } // Output to a WebGL or v3d file virtual bool write(abs3Doutfile *out) { return false; } // Used to compute deviation of a surface from a quadrilateral. virtual void displacement() {} // Render with OpenGL virtual void render(double size2, const triple& Min, const triple& Max, double perspective, bool remesh) {} virtual void meshinit() {} size_t centerindex(const triple& center) { centerMap::iterator p=centermap.find(center); if(p != centermap.end()) centerIndex=p->second; else { centers.push_back(center); centermap[center]=centerIndex=centers.size(); } return centerIndex; } // Transform as part of a picture. virtual drawElement *transformed(const transform&) { return this; } virtual drawElement *transformed(const double* t) { return this; } }; // Hold transform of an object. class drawElementLC : public virtual drawElement { public: double *T; // Keep track of accumulative picture transform drawElementLC() : T(NULL) {} drawElementLC(const double *t) : T(NULL) { copyTransform3(T,t); } drawElementLC(const vm::array& t) : T(NULL) { copyArray4x4C(T,&t); } drawElementLC(const double* t, const drawElementLC *s) : drawElement(s->KEY), T(NULL) { multiplyTransform3(T,t,s->T); } virtual ~drawElementLC() {} virtual bool is3D() {return true;} virtual const double* transf3() {return T;} virtual drawElement* transformed(const double* t) { return new drawElementLC(t,this); } }; // Base class for drawElements that involve paths. class drawPathBase : public virtual drawElement { protected: path p; path transpath(const transform& t) const { return p.transformed(t); } public: drawPathBase() {} drawPathBase(path p) : p(p) {} virtual ~drawPathBase() {} virtual void bounds(bbox& b, iopipestream&, boxvector&, bboxlist&) { b += p.bounds(); } virtual void writepath(psfile *out,bool) { out->write(p); } virtual void writeclippath(psfile *out, bool newpath=true) { out->writeclip(p,newpath); } virtual void writeshiftedpath(texfile *out) { out->writeshifted(p); } }; // Base class for drawElements that involve paths and pens. class drawPathPenBase : public drawPathBase { protected: pen pentype; pen transpen(const transform& t) const { return camp::transformed(shiftless(t),pentype); } public: drawPathPenBase(path p, pen pentype) : drawPathBase(p), pentype(pentype) {} drawPathPenBase(pen pentype) : pentype(pentype) {} virtual bool empty() { return p.empty(); } virtual bool cyclic() { return p.cyclic(); } void strokebounds(bbox& b, const path& p); virtual void penSave(psfile *out) { if (!pentype.getTransform().isIdentity()) out->gsave(); } virtual void penTranslate(psfile *out) { out->translate(shiftpair(pentype.getTransform())); } virtual void penConcat(psfile *out) { out->concat(shiftless(pentype.getTransform())); } virtual void penRestore(psfile *out) { if (!pentype.getTransform().isIdentity()) out->grestore(); } }; // Base class for drawElements that involve superpaths and pens. class drawSuperPathPenBase : public drawPathPenBase { protected: vm::array P; size_t size; bbox bpath; vm::array transpath(const transform& t) const { vm::array *Pt=new vm::array(size); for(size_t i=0; i < size; i++) (*Pt)[i]=vm::read(P,i).transformed(t); return *Pt; } public: drawSuperPathPenBase(const vm::array& P, pen pentype) : drawPathPenBase(pentype), P(P), size(P.size()) {} bool empty() { for(size_t i=0; i < size; i++) if(vm::read(P,i).size() != 0) return false; return true; } bool cyclic() { for(size_t i=0; i < size; i++) if(!vm::read(P,i).cyclic()) return false; return true; } void bounds(bbox& b, iopipestream&, boxvector&, bboxlist&) { for(size_t i=0; i < size; i++) bpath += vm::read(P,i).bounds(); b += bpath; } void strokepath(psfile *out) { out->strokepath(); } void strokebounds(bbox& b) { for(size_t i=0; i < size; i++) drawPathPenBase::strokebounds(bpath,vm::read(P,i)); b += bpath; } void writepath(psfile *out, bool newpath=true) { if(size > 0) out->write(vm::read(P,0),newpath); for(size_t i=1; i < size; i++) out->write(vm::read(P,i),false); } void writeclippath(psfile *out, bool newpath=true) { if(size > 0) out->writeclip(vm::read(P,0),newpath); for(size_t i=1; i < size; i++) out->writeclip(vm::read(P,i),false); } void writeshiftedpath(texfile *out) { for(size_t i=0; i < size; i++) out->writeshifted(vm::read(P,i),i == 0); } }; #ifdef HAVE_LIBGLM void setcolors(const prc::RGBAColour& diffuse, const prc::RGBAColour& emissive, const prc::RGBAColour& specular, double shininess, double metallic, double fresnel0, abs3Doutfile *out=NULL); #endif } GC_DECLARE_PTRFREE(camp::box); GC_DECLARE_PTRFREE(camp::drawElement); #endif