// Copyright (C) 1999-2017 // Smithsonian Astrophysical Observatory, Cambridge, MA, USA // For conditions of distribution and use, see copyright notice in "copyright" #include "baseellipse.h" #include "fitsimage.h" #define XPOINT_BLOCK 1024 BaseEllipse::BaseEllipse(Base* p, const Vector& ctr, double ang) : BaseMarker(p, ctr, ang) { xpoint_ =NULL; xpointSize_ =0; xpointNum_ =0; } BaseEllipse::BaseEllipse(Base* p, const Vector& ctr, double ang, const char* clr, int* dsh, int w, const char* f, const char* t, unsigned short prop, const char* c, const List& tag, const List& cb) : BaseMarker(p, ctr, ang, clr, dsh, w, f, t, prop, c, tag, cb) { xpoint_ =NULL; xpointSize_ =0; xpointNum_ =0; } BaseEllipse::BaseEllipse(const BaseEllipse& a) : BaseMarker(a) { xpoint_ =NULL; xpointSize_ =0; xpointNum_ =0; } BaseEllipse::~BaseEllipse() { if (xpoint_) free(xpoint_); } // renderX void BaseEllipse::renderX(Drawable drawable, Coord::InternalSystem sys, RenderMode mode) { double ang = calcAngle(); Vector r = annuli_[numAnnuli_-1]; Vector z = parent->zoom(); int isOrient = parent->getOrientation() == Coord::NORMAL && parent->getWCSOrientation() == Coord::NORMAL; int isRound = r[0] == r[1]; int isScale = z[0] == z[1]; int isAngle = teq(ang,0,FLT_EPSILON) || teq(ang,M_PI,FLT_EPSILON); int is360 = teq(startAng_,0,FLT_EPSILON) && teq(stopAng_,M_TWOPI,FLT_EPSILON); if (isRound && isScale && isOrient && parent->isAzElZero()) renderXCircle(drawable, sys, ang, mode); else if (isAngle && is360 && parent->isAzElZero()) renderXEllipse(drawable, sys, ang, mode); else renderXBezier(drawable, sys, mode); } void BaseEllipse::renderXCircle(Drawable drawable, Coord::InternalSystem sys, double ang, RenderMode mode) { GC lgc = renderXGC(mode); // this routine is only valid for circles with equal zoom in x & y Vector cc = parent->mapFromRef(center,sys); for (int i=0; imapFromRef(center,sys); for (int i=0; i=b1 && a1b1 && a2<=b2) s2 =1; if ((s1 && !s2) || (s1 && s2)) renderXBezierPrep(drawable, sys, mode, a1,a2,b1,b2,r); if (s1&&s2) s1=s2=0; } // close the loop xpointNum_++; if (xpointNum_*sizeof(XPoint) >= xpointSize_) { xpointSize_ += XPOINT_BLOCK*sizeof(XPoint); xpoint_ = (XPoint*)realloc(xpoint_, xpointSize_); } XPoint* ptr = xpoint_+xpointNum_; (*ptr).x = (*xpoint_).x; (*ptr).y = (*xpoint_).y; // if dashed, fake it GC lgc; if ((properties & SOURCE) && !(properties & DASH)) lgc = renderXGC(mode); else { // set width, color, dash switch (mode) { case SRC: XSetForeground(display, gc, color); renderXLineNoDash(gc); lgc = gc; break; case XOR: renderXLineNoDash(gcxor); lgc = gcxor; break; } } renderXBezierDraw(drawable, lgc, mode); if (xpoint_) free(xpoint_); xpoint_ =NULL; xpointSize_ =0; xpointNum_ =0; } } void BaseEllipse::renderXBezierDraw(Drawable drawable, GC lgc, RenderMode mode) { if ((properties & SOURCE) && !(properties & DASH)) XDrawLines(display, drawable, lgc, xpoint_, xpointNum_, CoordModeOrigin); else renderXBezierDashDraw(drawable, lgc); } void BaseEllipse::renderXBezierDashDraw(Drawable drawable, GC lgc) { // crude attempt to clip unwanted drawlines // only works for SRC for (int ii=0; ii= b1 && a1 <= b2)) a1 = b1; if (!(a2 >= b1 && a2 <= b2)) a2 = b2; if (a1>a2) { renderXBezierArc(drawable, sys, mode, b1,a2,r); renderXBezierArc(drawable, sys, mode, a1,b2,r); } else renderXBezierArc(drawable, sys, mode, a1,a2,r); } void BaseEllipse::renderXBezierArc(Drawable drawable, Coord::InternalSystem sys, RenderMode mode, double a1, double a2, Vector& rr) { // don't render if zero angle if (a1==a2) return; // don't render if zero length if (rr[0]==0 || rr[1]==0) return; // bezier curve, valid for arcs of dd[1] ? dd[0] : dd[1]; // calculate incr // this is a crude attempt to limit the number of iterations // we want a min for very small segments, but not that large for // high zoom or elongated curves float aa = int(log(max))*5; float incr = 1./(aa > 2 ? aa : 2); float tt = incr; while (tt<=1+FLT_EPSILON) { float xx = pow(tt,3)*(t2x+3*(x1x-x2x)-t1x) +3*pow(tt,2)*(t1x-2*x1x+x2x) +3*tt*(x1x-t1x)+t1x; float yy = pow(tt,3)*(t2y+3*(x1y-x2y)-t1y) +3*pow(tt,2)*(t1y-2*x1y+x2y) +3*tt*(x1y-t1y)+t1y; xpointNum_++; if (xpointNum_*sizeof(XPoint) >= xpointSize_) { xpointSize_ += XPOINT_BLOCK*sizeof(XPoint); xpoint_ = (XPoint*)realloc(xpoint_, xpointSize_); } XPoint* ptr = xpoint_+xpointNum_; (*ptr).x = xx; (*ptr).y = yy; tt += incr; } } // this routine maps the desired angle to an angle to be used with XDrawArc double BaseEllipse::xyz(Vector rr, double aa) { // just in case if (!rr[0] || !rr[1]) return aa; int flip=0; while (aa>M_PI) { aa -= M_PI; flip++; } double tt = rr[1]*rr[1]*cos(aa)*cos(aa)+rr[0]*rr[0]*sin(aa)*sin(aa); double ss =0; if (tt>0) ss = 1./sqrt(tt); double bb = rr[1]*ss*cos(aa); return acos(bb)+M_PI*flip; } void BaseEllipse::renderXInclude(Drawable drawable, Coord::InternalSystem sys, RenderMode mode) { if (!(properties & INCLUDE)) { // Matrix mm = fwdMatrix(); double theta = degToRad(45); Vector r1 = fwdMap(Vector(annuli_[numAnnuli_-1][0]*cos(theta), annuli_[numAnnuli_-1][1]*sin(theta)), sys); Vector r2 = fwdMap(Vector(-annuli_[numAnnuli_-1][0]*cos(theta), -annuli_[numAnnuli_-1][1]*sin(theta)), sys); GC lgc = renderXGC(mode); if (mode == SRC) XSetForeground(display, gc, parent->getColor("red")); XDrawLine(display, drawable, lgc, r1[0], r1[1], r2[0], r2[1]); } } // renderPS void BaseEllipse::renderPS(int mode) { Vector r = annuli_[numAnnuli_-1]; Vector z = parent->zoom(); int isOrient = parent->getOrientation() == Coord::NORMAL && parent->getWCSOrientation() == Coord::NORMAL; int isRound = r[0] == r[1]; int isScale = z[0] == z[1]; if (isRound && isScale && isOrient && parent->isAzElZero()) renderPSCircle(mode); else renderPSEllipse(mode); } void BaseEllipse::renderPSDraw() { ostringstream str; str << "stroke" << endl << ends; Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); } void BaseEllipse::renderPSFill() { ostringstream str; str << "fill" << endl << ends; Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); } void BaseEllipse::renderPSCircle(int mode) { renderPSGC(mode); Vector cc = parent->mapFromRef(center,Coord::CANVAS); double ang = calcAngle(); for (int ii=0; iiinterp, str.str().c_str(), NULL); } Vector r = annuli_[ii]; Vector ur = fwdMap(r,Coord::CANVAS); double l = (ur-cc).length() * cos(M_PI_4); // don't render zero length arcs if (!l) continue; // Must be very sure that a1canvas) << ' ' << l << ' ' << a1 << ' ' << a2 << ' ' << "arc" << endl << ends; Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); } renderPSDraw(); } } void BaseEllipse::renderPSEllipse(int mode) { renderPSGC(mode); double a1 = startAng_; double a2 = stopAng_; if (a2<=a1) a2 += M_TWOPI; for (int ii=0; iiinterp, str.str().c_str(), NULL); Vector r = annuli_[ii]; int s1 =0; int s2 =0; for (int jj=0; jj<8; jj++) { double b1 = jj*M_PI_2; double b2 = (jj+1)*M_PI_2; if (!s1 && a1>=b1 && a1b1 && a2<=b2) s2 =1; if ((s1 && !s2) || (s1 && s2)) renderPSEllipsePrep(a1,a2,b1,b2,r); if (s1&&s2) s1=s2=0; } renderPSDraw(); } } void BaseEllipse::renderPSEllipsePrep(double a1, double a2, double b1, double b2, Vector& rr) { if (!(a1 >= b1 && a1 <= b2)) a1 = b1; if (!(a2 >= b1 && a2 <= b2)) a2 = b2; if (a1>a2) { renderPSEllipseArc(b1,a2,rr); renderPSEllipseArc(a1,b2,rr); } else renderPSEllipseArc(a1,a2,rr); } void BaseEllipse::renderPSEllipseArc(double a1, double a2, Vector& rr) { // don't render zero length arcs if (a1 == a2) return; if (!rr[0] || !rr[1]) return; // bezier curve, valid for arcs of canvas) << ' ' << "moveto " << xx1.TkCanvasPs(parent->canvas) << ' ' << xx2.TkCanvasPs(parent->canvas) << ' ' << tt1.TkCanvasPs(parent->canvas) << ' ' << "curveto" << endl << ends; Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); } void BaseEllipse::renderPSInclude(int mode) { if (!(properties & INCLUDE)) { double theta = degToRad(45); Vector r1 = fwdMap(Vector(annuli_[numAnnuli_-1][0]*cos(theta), annuli_[numAnnuli_-1][1]*sin(theta)), Coord::CANVAS); Vector r2 = fwdMap(Vector(-annuli_[numAnnuli_-1][0]*cos(theta), -annuli_[numAnnuli_-1][1]*sin(theta)), Coord::CANVAS); renderPSColor(mode, parent->getXColor("red")); ostringstream str; str << "newpath " << r1.TkCanvasPs(parent->canvas) << ' ' << "moveto " << r2.TkCanvasPs(parent->canvas) << ' ' << "lineto stroke" << endl << ends; Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); } } #ifdef MAC_OSX_TK void BaseEllipse::renderMACOSX() { Vector r = annuli_[numAnnuli_-1]; Vector z = parent->zoom(); int isOrient = parent->getOrientation() == Coord::NORMAL && parent->getWCSOrientation() == Coord::NORMAL; int isRound = r[0] == r[1]; int isScale = z[0] == z[1]; if (isRound && isScale && isOrient & parent->isAzElZero()) renderMACOSXCircle(); else renderMACOSXEllipse(); } void BaseEllipse::renderMACOSXCircle() { renderMACOSXGC(); Vector cc = parent->mapFromRef(center,Coord::CANVAS); double ang = calcAngle(); for (int ii=0; ii=b1 && a1b1 && a2<=b2) s2 =1; if ((s1 && !s2) || (s1 && s2)) renderMACOSXEllipsePrep(a1,a2,b1,b2,r); if (s1&&s2) s1=s2=0; renderMACOSXDraw(); } } } void BaseEllipse::renderMACOSXEllipsePrep(double a1, double a2, double b1, double b2, Vector& rr) { if (!(a1 >= b1 && a1 <= b2)) a1 = b1; if (!(a2 >= b1 && a2 <= b2)) a2 = b2; if (a1>a2) { renderMACOSXEllipseArc(b1,a2,rr); renderMACOSXEllipseArc(a1,b2,rr); } else renderMACOSXEllipseArc(a1,a2,rr); } void BaseEllipse::renderMACOSXEllipseArc(double a1, double a2, Vector& rr) { // don't render zero length arcs if (a1 == a2) return; if (!rr[0] || !rr[1]) return; // bezier curve, valid for arcs of getXColor("red")); macosxDrawLine(r1,r2); } } #endif #ifdef __WIN32 void BaseEllipse::renderWIN32() { Vector r = annuli_[numAnnuli_-1]; Vector z = parent->zoom(); int isOrient = parent->getOrientation() == Coord::NORMAL && parent->getWCSOrientation() == Coord::NORMAL; int isRound = r[0] == r[1]; int isScale = z[0] == z[1]; if (isRound && isScale && isOrient && parent->isAzElZero()) renderWIN32Circle(); else renderWIN32Ellipse(); } void BaseEllipse::renderWIN32Circle() { renderWIN32GC(); Vector cc = parent->mapFromRef(center,Coord::CANVAS); double ang = calcAngle(); for (int i=0; i=b1 && a1b1 && a2<=b2) s2 =1; if ((s1 && !s2) || (s1 && s2)) renderWIN32EllipsePrep(a1,a2,b1,b2,r); if (s1&&s2) s1=s2=0; renderWIN32Draw(); } } } void BaseEllipse::renderWIN32EllipsePrep(double a1, double a2, double b1, double b2, Vector& rr) { if (!(a1 >= b1 && a1 <= b2)) a1 = b1; if (!(a2 >= b1 && a2 <= b2)) a2 = b2; if (a1>a2) { renderWIN32EllipseArc(b1,a2,rr); renderWIN32EllipseArc(a1,b2,rr); } else renderWIN32EllipseArc(a1,a2,rr); } void BaseEllipse::renderWIN32EllipseArc(double a1, double a2, Vector& rr) { // don't render zero length arcs if (a1 == a2) return; if (!rr[0] || !rr[1]) return; // bezier curve, valid for arcs of getXColor("red")); win32DrawLine(r1,r2); } } #endif // Support void BaseEllipse::updateHandles() { // handles are in canvas coords // we can't garantee that the annuli_ have been sorted yet if (handle) delete [] handle; handle = new Vector[numHandle]; Vector max; for(int ii=0; ii1) for (int ii=0; ii0) ss = rr[0]*rr[1]/sqrt(tt); return Vector(ss*cos(aa),ss*sin(aa)); }