/***** * drawpath.cc * Andy Hammerlindl 2002/06/06 * * Stores a path that has been added to a picture. *****/ #include #include #include #include "drawpath.h" #include "psfile.h" #include "util.h" using vm::array; using vm::read; namespace camp { double PatternLength(double arclength, const array& pat, bool cyclic, double penwidth) { double sum=0.0; size_t n=pat.size(); for(unsigned i=0; i < n; i ++) sum += read(pat,i)*penwidth; if(sum == 0.0) return 0.0; if(n % 2 == 1) sum *= 2.0; // On/off pattern repeats after 2 cycles. double pat0=read(pat,0); // Fix bounding box resolution problem. Example: // asy -f pdf testlinetype; gv -scale -2 testlinetype.pdf if(!cyclic && pat0 == 0) sum += 1.0e-3*penwidth; double terminator=(cyclic && arclength >= 0.5*sum) ? 0.0 : pat0*penwidth; int ncycle=(int)((arclength-terminator)/sum+0.5); return (ncycle >= 1 || terminator >= 0.75*arclength) ? ncycle*sum+terminator : 0.0; } bool isdashed(pen& p) { const LineType *linetype=p.linetype(); size_t n=linetype->pattern.size(); return n > 0; } pen adjustdash(pen& p, double arclength, bool cyclic) { pen q=p; // Adjust dash sizes to fit arclength; also compensate for linewidth. const LineType *linetype=q.linetype(); size_t n=linetype->pattern.size(); if(n > 0) { double penwidth=linetype->scale ? q.width() : 1.0; double factor=penwidth; if(linetype->adjust && arclength) { double denom=PatternLength(arclength,linetype->pattern,cyclic,penwidth); if(denom != 0.0) factor *= arclength/denom; } if(factor != 1.0) q.adjust(max(factor,0.1)); } return q; } // Account for square or extended pen cap contributions to bounding box. void cap(bbox& b, double t, path p, pen pentype) { transform T=pentype.getTransform(); double h=0.5*pentype.width(); pair v=p.dir(t); transform S=rotate(conj(v))*shiftless(T); double xx=S.getxx(), xy=S.getxy(); double yx=S.getyx(), yy=S.getyy(); double y=hypot(yx,yy); if(y == 0.0) return; double numer=xx*yx+xy*yy; double x=numer/y; pair z=shift(T)*p.point(t); switch(pentype.cap()) { case SquareCap: { pair d=rotate(v)*pair(x,y)*h; b += z+d; b += z-d; break; } case RoundCap: { b += pad(z,pentype.bounds()); break; } case ExtendedCap: { transform R=rotate(v); double w=(xx*yy-xy*yx)/y; pair dp=R*pair(x+w,y)*h; pair dm=R*pair(x-w,y)*h; b += z+dp; b += z+dm; b += z-dp; b += z-dm; break; } } } // Account for square or extended pen cap contributions to bounding box. void join(bbox& b, double t, path p, pen pentype) { transform T=pentype.getTransform(); double h=0.5*pentype.width(); pair v=p.dir(t); transform S=rotate(conj(v))*shiftless(T); double xx=S.getxx(), xy=S.getxy(); double yx=S.getyx(), yy=S.getyy(); double y=hypot(yx,yy); if(y == 0.0) return; double numer=xx*yx+xy*yy; double x=numer/y; pair z=shift(T)*p.point(t); switch(pentype.join()) { case MiterJoin: { double phi=angle(-p.predir(t),false)-angle(p.postdir(t),false); static const double twopi=2.0*pi; if(phi > pi) phi -= twopi; if(phi < -pi) phi += twopi; double s=sin(0.5*phi); if(s != 0.0) { double factor=1.0/s; if(abs(factor) < pentype.miter()) { b += z-rotate(v)*pair(x,y)*h*factor; break; } } // Fall through } case BevelJoin: { pair Z=pair(x,y)*h; pair d=rotate(p.predir(t))*Z; b += z+d; b += z-d; pair D=rotate(p.postdir(t))*Z; b += z+D; b += z-D; break; } case RoundJoin: { b += pad(z,pentype.bounds()); break; } } } void drawPathPenBase::strokebounds(bbox& b, const path& p) { Int l=p.length(); if(l < 0) return; b += p.internalbounds(pentype.bounds()); unsigned int n=p.size(); if(p.cyclic()) join(b,0,p,pentype); for(unsigned int i=1; i < n-1; ++i) join(b,i,p,pentype); if(p.cyclic()) { join(b,n-1,p,pentype); } else { cap(b,0,p,pentype); cap(b,l,p,pentype); } } bool drawPath::draw(psfile *out) { Int n = p.size(); if (n == 0 || pentype.invisible()) return true; pen q=isdashed(pentype) ? adjustdash(pentype, p.transformed(inverse(pentype.getTransform())).arclength(), p.cyclic()) : pentype; penSave(out); penTranslate(out); if(n > 1) out->write(p); else out->dot(p,q); penConcat(out); out->setpen(q); out->stroke(q,n == 1); penRestore(out); return true; } drawElement *drawPath::transformed(const transform& t) { return new drawPath(transpath(t),transpen(t),KEY); } } //namespace camp