diff options
Diffstat (limited to 'tksao/frame/epanda.C')
-rw-r--r-- | tksao/frame/epanda.C | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/tksao/frame/epanda.C b/tksao/frame/epanda.C new file mode 100644 index 0000000..5ec98d5 --- /dev/null +++ b/tksao/frame/epanda.C @@ -0,0 +1,735 @@ +// Copyright (C) 1999-2016 +// Smithsonian Astrophysical Observatory, Cambridge, MA, USA +// For conditions of distribution and use, see copyright notice in "copyright" + +#include <tk.h> + +#include "epanda.h" +#include "fitsimage.h" + +Epanda::Epanda(Base* p, const Vector& ctr, + double a1, double a2, int an, + const Vector& r1, const Vector& r2, int rn, + double ang, + const char* clr, int* dsh, + int wth, const char* fnt, const char* txt, + unsigned short prop, const char* cmt, + const List<Tag>& tg, const List<CallBack>& cb) + : BaseEllipse(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb) +{ + numAnnuli_ = rn+1; + annuli_ = new Vector[numAnnuli_]; + + for (int ii=0; ii<numAnnuli_; ii++) + annuli_[ii] = ((r2-r1)/rn)*ii+r1; + + setAngles(a1,a2,an); + + strcpy(type_, "epanda"); + numHandle = 4 + numAnnuli_ + numAngles_; + + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + updateBBox(); +} + +Epanda::Epanda(Base* p, const Vector& ctr, + int an, double* a, + int rn, Vector* r, + double ang, + const char* clr, int* dsh, + int wth, const char* fnt, const char* txt, + unsigned short prop, const char* cmt, + const List<Tag>& tg, const List<CallBack>& cb) + : BaseEllipse(p, ctr, ang, clr, dsh, wth, fnt, txt, prop, cmt, tg, cb) +{ + numAnnuli_ = rn; + annuli_ = new Vector[numAnnuli_]; + + for (int ii=0; ii<numAnnuli_; ii++) + annuli_[ii] = r[ii]; + sortAnnuli(); + + setAngles(an,a); + + strcpy(type_, "epanda"); + numHandle = 4 + numAnnuli_ + numAngles_; + + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + updateBBox(); +} + +Epanda::Epanda(const Epanda& a) : BaseEllipse(a) {} + +void Epanda::renderX(Drawable drawable, Coord::InternalSystem sys, + RenderMode mode) +{ + BaseEllipse::renderX(drawable, sys, mode); + + GC lgc = renderXGC(mode); + + Vector r0 = annuli_[0]; + Vector r1 = annuli_[numAnnuli_-1]; + + for (int ii=0; ii<numAngles_; ii++) { + Vector rr0 = fwdMap(intersect(r0,-angles_[ii]),sys); + Vector rr1 = fwdMap(intersect(r1,-angles_[ii]),sys); + + if (mode == SRC) { + if (selected) { + if (ii == 0) + XSetForeground(display, gc, parent->getColor("red")); + else if (ii == numAngles_-1) + XSetForeground(display, gc, parent->getColor("blue")); + else + XSetForeground(display, gc, color); + } + else + XSetForeground(display, gc, color); + } + + XDrawLine(display, drawable, lgc, rr0[0], rr0[1], rr1[0], rr1[1]); + } +} + +void Epanda::renderPS(int mode) +{ + BaseEllipse::renderPS(mode); + + renderPSGC(mode); + + Vector r0 = annuli_[0]; + Vector r1 = annuli_[numAnnuli_-1]; + + for (int ii=0; ii<numAngles_; ii++) { + Vector rr0 = fwdMap(intersect(r0,-angles_[ii]),Coord::CANVAS); + Vector rr1 = fwdMap(intersect(r1,-angles_[ii]),Coord::CANVAS); + + ostringstream str; + str << "newpath " + << rr0.TkCanvasPs(parent->canvas) << ' ' + << "moveto " + << rr1.TkCanvasPs(parent->canvas) << ' ' + << "lineto stroke" << endl << ends; + Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); + } +} + +#ifdef MAC_OSX_TK +void Epanda::renderMACOSX() +{ + BaseEllipse::renderMACOSX(); + + renderMACOSXGC(); + + Vector r0 = annuli_[0]; + Vector r1 = annuli_[numAnnuli_-1]; + + for (int ii=0; ii<numAngles_; ii++) { + Vector rr0 = fwdMap(intersect(r0,-angles_[ii]),Coord::CANVAS); + Vector rr1 = fwdMap(intersect(r1,-angles_[ii]),Coord::CANVAS); + + macosxDrawLine(rr0,rr1); + } +} +#endif + +#ifdef __WIN32 +void Epanda::renderWIN32() +{ + BaseEllipse::renderWIN32(); + + renderWIN32GC(); + + Vector r0 = annuli_[0]; + Vector r1 = annuli_[numAnnuli_-1]; + + for (int ii=0; ii<numAngles_; ii++) { + Vector rr0 = fwdMap(intersect(r0,-angles_[ii]),Coord::CANVAS); + Vector rr1 = fwdMap(intersect(r1,-angles_[ii]),Coord::CANVAS); + + win32DrawLine(rr0,rr1); + } +} +#endif + +// Support + +void Epanda::updateHandles() +{ + BaseEllipse::updateHandles(); + + Vector rr = annuli_[numAnnuli_-1]; + for (int ii=0; ii<numAngles_; ii++) + handle[ii+4+numAnnuli_] = fwdMap(intersect(rr,-angles_[ii]),Coord::CANVAS); +} + +void Epanda::edit(const Vector& v, int h) +{ + Matrix mm = bckMatrix(); + Vector n = v * mm; + + if (h<5) { + // don't go thru the center + if (n[0]!=0 && n[1]!=0) { + Vector o = annuli_[numAnnuli_-1]; + for (int i=0; i<numAnnuli_; i++) { + annuli_[i][0] *= fabs(n[0]/o[0]); + annuli_[i][1] *= fabs(n[1]/o[1]); + } + } + } + else if (h<(5+numAnnuli_)) { + // we must have some length + double l = n.length(); + annuli_[h-5] = annuli_[numAnnuli_-1] * l/annuli_[numAnnuli_-1][0]; + } + else { + angles_[h-5-numAnnuli_] = -((v * mm).angle()); + sortAngles(); + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + } + + updateBBox(); + doCallBack(CallBack::EDITCB); +} + +void Epanda::editEnd() +{ + sortAnnuli(); + sortAngles(); + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + updateBBox(); + doCallBack(CallBack::EDITENDCB); +} + +int Epanda::addAnnuli(const Vector& v) +{ + Matrix mm = bckMatrix(); + double l = (v * mm).length(); + Vector rr = annuli_[numAnnuli_-1] * l/annuli_[numAnnuli_-1][0]; + + // we need to insert into the next to the last location + // new size array + Vector* old = annuli_; + annuli_ = new Vector[numAnnuli_+1]; + + // copy old values + for (int i=0; i<numAnnuli_; i++) + annuli_[i] = old[i]; + + // save last + annuli_[numAnnuli_] = old[numAnnuli_-1]; + + // delete old + if (old) + delete [] old; + + // new size on end + annuli_[numAnnuli_-1] = rr; + + numAnnuli_++; + numHandle++; + + return 4+numAnnuli_-1; +} + +int Epanda::addAngles(const Vector& v) +{ + Matrix mm = bckMatrix(); + addAngle(-((v * mm).angle())); + numHandle++; + + return 4+numAnnuli_+numAngles_-1; +} + +void Epanda::setAnglesAnnuli(double a1, double a2, int an, + Vector r1, Vector r2, int rn) +{ + numAnnuli_ = rn+1; + if (annuli_) + delete [] annuli_; + annuli_ = new Vector[numAnnuli_]; + + for (int i=0; i<numAnnuli_; i++) + annuli_[i] = ((r2-r1)/rn)*i+r1; + + sortAnnuli(); + setAngles(a1,a2,an); + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + numHandle = 4 + numAnnuli_ + numAngles_; + + updateBBox(); + doCallBack(CallBack::EDITCB); +} + +void Epanda::setAnglesAnnuli(const double* a, int an, const Vector* r, int rn) +{ + numAnnuli_ = rn; + if (annuli_) + delete [] annuli_; + annuli_ = new Vector[numAnnuli_]; + + for (int i=0; i<numAnnuli_; i++) + annuli_[i] = r[i]; + + sortAnnuli(); + setAngles(an,a); + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + numHandle = 4 + numAnnuli_ + numAngles_; + + updateBBox(); + doCallBack(CallBack::EDITCB); +} + +void Epanda::deleteAnglesAnnuli(int h) +{ + if (h>4) { + int hh = h-4-1; + + if (numAnnuli_>2 && hh<numAnnuli_) { + // new annuli_ array + Vector* old = annuli_; + annuli_ = new Vector[numAnnuli_-1]; + + // copy up to annuli_ in question + for (int i=0; i<hh; i++) + annuli_[i] = old[i]; + + // copy remainder + for (int i=hh; i<numAnnuli_-1; i++) + annuli_[i] = old[i+1]; + + if (old) + delete [] old; + numAnnuli_--; + } + else if (numAngles_>2 && hh<(numAnnuli_+numAngles_)) { + hh -= numAnnuli_; + deleteAngle(hh); + } + + numHandle = 4 + numAnnuli_ + numAngles_; + + startAng_ = angles_[0]; + stopAng_ = angles_[numAngles_-1]; + + updateBBox(); + doCallBack(CallBack::EDITCB); + } +} + +int Epanda::isIn(const Vector& vv, Coord::InternalSystem sys, int nn, int aa) +{ + Vector pp = bckMap(vv,sys); + return BaseEllipse::isIn(vv,sys,nn) && isInAngle(pp,aa); +} + +void Epanda::analysis(AnalysisTask mm, int which) +{ + switch (mm) { + case PANDA: + if (!analysisPanda_ && which) { + addCallBack(CallBack::MOVECB, analysisPandaCB_[0], + parent->options->cmdName); + addCallBack(CallBack::EDITCB, analysisPandaCB_[0], + parent->options->cmdName); + addCallBack(CallBack::EDITENDCB, analysisPandaCB_[0], + parent->options->cmdName); + addCallBack(CallBack::ROTATECB, analysisPandaCB_[0], + parent->options->cmdName); + addCallBack(CallBack::UPDATECB, analysisPandaCB_[0], + parent->options->cmdName); + addCallBack(CallBack::DELETECB, analysisPandaCB_[1], + parent->options->cmdName); + } + if (analysisPanda_ && !which) { + deleteCallBack(CallBack::MOVECB, analysisPandaCB_[0]); + deleteCallBack(CallBack::EDITCB, analysisPandaCB_[0]); + deleteCallBack(CallBack::EDITENDCB, analysisPandaCB_[0]); + deleteCallBack(CallBack::ROTATECB, analysisPandaCB_[0]); + deleteCallBack(CallBack::UPDATECB, analysisPandaCB_[0]); + deleteCallBack(CallBack::DELETECB, analysisPandaCB_[1]); + } + + analysisPanda_ = which; + break; + case STATS: + if (!analysisStats_ && which) { + addCallBack(CallBack::MOVECB, analysisStatsCB_[0], + parent->options->cmdName); + addCallBack(CallBack::EDITCB, analysisStatsCB_[0], + parent->options->cmdName); + addCallBack(CallBack::EDITENDCB, analysisStatsCB_[0], + parent->options->cmdName); + addCallBack(CallBack::ROTATECB, analysisStatsCB_[0], + parent->options->cmdName); + addCallBack(CallBack::UPDATECB, analysisStatsCB_[0], + parent->options->cmdName); + addCallBack(CallBack::DELETECB, analysisStatsCB_[1], + parent->options->cmdName); + } + if (analysisStats_ && !which) { + deleteCallBack(CallBack::MOVECB, analysisStatsCB_[0]); + deleteCallBack(CallBack::EDITCB, analysisStatsCB_[0]); + deleteCallBack(CallBack::EDITENDCB, analysisStatsCB_[0]); + deleteCallBack(CallBack::ROTATECB, analysisStatsCB_[0]); + deleteCallBack(CallBack::UPDATECB, analysisStatsCB_[0]); + deleteCallBack(CallBack::DELETECB, analysisStatsCB_[1]); + } + + analysisStats_ = which; + break; + default: + // na + break; + } +} + +void Epanda::analysisPanda(Coord::CoordSystem sys) +{ + double* xx; + double* yy; + double* ee; + + BBox* bb = new BBox[numAnnuli_]; + Matrix mm = Rotate(angle) * Translate(center); + + for (int ii=0; ii<numAnnuli_; ii++) { + Vector vv = annuli_[ii]; + bb[ii] = BBox(-vv * mm); + bb[ii].bound( vv * mm); + bb[ii].bound(Vector( vv[0],-vv[1]) * mm); + bb[ii].bound(Vector(-vv[0], vv[1]) * mm); + } + + int num = parent->markerAnalysisPanda(this, &xx, &yy, &ee, + numAnnuli_-1, annuli_, + numAngles_-1, angles_, + bb, sys); + analysisXYEResult(xx, yy, ee, num); +} + +void Epanda::analysisStats(Coord::CoordSystem sys, Coord::SkyFrame sky) +{ + ostringstream str; + BBox* bb = new BBox[numAnnuli_]; + Matrix mm = Rotate(angle) * Translate(center); + + for (int ii=0; ii<numAnnuli_; ii++) { + Vector vv = annuli_[ii]; + bb[ii] = BBox(-vv * mm); + bb[ii].bound( vv * mm); + bb[ii].bound(Vector( vv[0],-vv[1]) * mm); + bb[ii].bound(Vector(-vv[0], vv[1]) * mm); + } + + parent->markerAnalysisStats(this, str, numAnnuli_-1, numAngles_-1, bb, sys, sky); + str << ends; + Tcl_AppendResult(parent->interp, str.str().c_str(), NULL); +} + +// list + +void Epanda::list(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format, int conj, int strip) +{ + int regular = 1; + if (numAngles_>2) { + double delta; + if (angles_[1] > angles_[0]) + delta = angles_[1]-angles_[0]; + else + delta = angles_[1]+M_TWOPI-angles_[0]; + + for (int ii=2; ii<numAngles_; ii++) { + double diff; + if (angles_[ii] > angles_[ii-1]) + diff = angles_[ii]-angles_[ii-1]; + else + diff = angles_[ii]+M_TWOPI-angles_[ii-1]; + + if (!teq(diff,delta,FLT_EPSILON)) { + regular = 0; + break; + } + } + } + + if (numAnnuli_>2) { + double delta = annuli_[1][0]-annuli_[0][0]; + for (int i=2; i<numAnnuli_; i++) { + double diff = annuli_[i][0]-annuli_[i-1][0]; + if (!teq(diff,delta,FLT_EPSILON)) { + regular = 0; + break; + } + } + } + + if (regular) + listA(str, sys, sky, format, conj, strip); + else + listB(str, sys, sky, format, conj, strip); +} + +void Epanda::listA(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format, int conj, int strip) +{ + FitsImage* ptr = parent->findFits(sys,center); + listPre(str, sys, sky, ptr, strip, 0); + + switch (sys) { + case Coord::IMAGE: + case Coord::PHYSICAL: + case Coord::DETECTOR: + case Coord::AMPLIFIER: + listANonCel(ptr, str, sys); + break; + default: + if (ptr->hasWCSCel(sys)) { + double a1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys,sky)); + double a2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys,sky)); + Vector r1 = ptr->mapLenFromRef(annuli_[0],sys,Coord::ARCSEC); + Vector r2 = ptr->mapLenFromRef(annuli_[numAnnuli_-1],sys,Coord::ARCSEC); + double aa = parent->mapAngleFromRef(angle,sys,sky); + if (a2<=a1+FLT_EPSILON) + a2 += 360; + + switch (format) { + case Coord::DEGREES: + { + Vector vv = ptr->mapFromRef(center,sys,sky); + str << type_ << '(' + << setprecision(10) << vv << ',' + << setprecision(8) << a1 << ',' << a2 <<',' << numAngles_-1 << ',' + << setprecision(3) << fixed << setunit('"') << r1 << ',' + << setunit('"') << r2 << ','; + str.unsetf(ios_base::floatfield); + str << setprecision(8) << numAnnuli_-1 << ',' + << setprecision(8) << radToDeg(aa) << ')'; + } + break; + case Coord::SEXAGESIMAL: + listRADEC(ptr,center,sys,sky,format); + str << type_ << '(' + << ra << ',' << dec << ',' + << setprecision(8) << a1 << ',' << a2 <<',' << numAngles_-1 << ',' + << setprecision(3) << fixed << setunit('"') << r1 << ',' + << setunit('"') << r2 << ','; + str.unsetf(ios_base::floatfield); + str << setprecision(8) << numAnnuli_-1 << ',' + << setprecision(8) << radToDeg(aa) << ')'; + break; + } + } + else + listANonCel(ptr, str, sys); + } + + listPost(str, conj, strip); +} + +void Epanda::listANonCel(FitsImage* ptr, ostream& str, Coord::CoordSystem sys) +{ + double a1 = radToDeg(parent->mapAngleFromRef(angles_[0],sys)); + double a2 = radToDeg(parent->mapAngleFromRef(angles_[numAngles_-1],sys)); + if (a2<=a1+FLT_EPSILON) + a2 += 360; + + Vector vv = ptr->mapFromRef(center,sys); + Vector r1 = ptr->mapLenFromRef(annuli_[0],sys); + Vector r2 = ptr->mapLenFromRef(annuli_[numAnnuli_-1],sys); + double aa = parent->mapAngleFromRef(angle,sys); + str << type_ << '(' << setprecision(8) << vv << ',' + << a1 << ',' << a2 << ',' << numAngles_-1 << ',' + << r1 << ',' << r2 << ',' << numAnnuli_-1 << ',' + << radToDeg(aa) << ')'; +} + +void Epanda::listB(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format, int conj, int strip) +{ + FitsImage* ptr = parent->findFits(sys,center); + + switch (sys) { + case Coord::IMAGE: + case Coord::PHYSICAL: + case Coord::DETECTOR: + case Coord::AMPLIFIER: + listBNonCel(ptr, str, sys, sky, format, conj, strip); + break; + default: + if (ptr->hasWCSCel(sys)) { + switch (format) { + case Coord::DEGREES: + { + Vector vv = ptr->mapFromRef(center,sys,sky); + for (int jj=1; jj<numAngles_; jj++) { + for (int ii=1; ii<numAnnuli_; ii++) { + listPre(str, sys, sky, ptr, strip, 0); + str << type_ << '(' << setprecision(10) << vv << ','; + listBCel(ptr, ii, jj, str, sys, sky, format, conj, strip); + } + } + } + break; + case Coord::SEXAGESIMAL: + listRADEC(ptr,center,sys,sky,format); + for (int jj=1; jj<numAngles_; jj++) { + for (int ii=1; ii<numAnnuli_; ii++) { + listPre(str, sys, sky, ptr, strip, 0); + str << type_ << '(' << ra << ',' << dec << ','; + listBCel(ptr, ii, jj, str, sys, sky, format, conj, strip); + } + } + break; + } + break; + } + else + listBNonCel(ptr, str, sys, sky, format, conj, strip); + } +} + +void Epanda::listBNonCel(FitsImage* ptr, ostream& str, + Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format, int conj, int strip) +{ + Vector vv = ptr->mapFromRef(center,sys); + double aa = parent->mapAngleFromRef(angle,sys); + for (int jj=1; jj<numAngles_; jj++) { + double a1 = radToDeg(parent->mapAngleFromRef(angles_[jj-1],sys)); + double a2 = radToDeg(parent->mapAngleFromRef(angles_[jj],sys)); + if (a2<=a1+FLT_EPSILON) + a2 += 360; + + for (int ii=1; ii<numAnnuli_; ii++) { + listPre(str, sys, sky, ptr, strip, 0); + + Vector r1 = ptr->mapLenFromRef(annuli_[ii-1],sys); + Vector r2 = ptr->mapLenFromRef(annuli_[ii],sys); + str << type_ << '(' + << setprecision(8) << vv << ',' + << a1 << ',' << a2 << ",1," + << r1 << ',' << r2 << ",1," + << radToDeg(aa) << ')'; + + if (!strip) { + if (conj) + str << " ||"; + + str << " # epanda="; + if (ii==1 && jj==1 && !strip) { + str << '('; + for (int kk=0; kk<numAngles_; kk++) { + double ar = parent->mapAngleFromRef(angles_[kk],sys); + str << radToDeg(ar) << ((kk<numAngles_-1) ? ' ' : ')'); + } + str << '('; + str << setseparator(' '); + for (int kk=0; kk<numAnnuli_; kk++) { + Vector rr = ptr->mapLenFromRef(annuli_[kk],sys); + str << rr << ((kk<numAnnuli_-1) ? ' ' : ')'); + } + str << setseparator(','); + str << '(' << radToDeg(aa) << ')'; + + listProps(str); + } + else + str << "ignore"; + + str << (strip ? ';' : '\n'); + } + else { + if (conj) + str << "||"; + else + str << ";"; + } + } + } +} + +void Epanda::listBCel(FitsImage* ptr, int ii, int jj, ostream& str, + Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format, int conj, int strip) +{ + double a1 = radToDeg(parent->mapAngleFromRef(angles_[jj-1],sys,sky)); + double a2 = radToDeg(parent->mapAngleFromRef(angles_[jj],sys,sky)); + if (a2<=a1+FLT_EPSILON) + a2 += 360; + + Vector r1 = ptr->mapLenFromRef(annuli_[ii-1],sys,Coord::ARCSEC); + Vector r2 = ptr->mapLenFromRef(annuli_[ii],sys,Coord::ARCSEC); + double aa = parent->mapAngleFromRef(angle,sys,sky); + + str << setprecision(10) << a1 << ',' << a2 << ",1," + << setprecision(3) << fixed << setunit('"') << r1 << ',' + << setunit('"') << r2 << ",1,"; + str.unsetf(ios_base::floatfield); + str << setprecision(8) << radToDeg(aa) << ')'; + + if (!strip) { + if (conj) + str << " ||"; + + str << " # epanda="; + if (ii==1 && jj==1 && !strip) { + str << '(' << setprecision(8); + for (int kk=0; kk<numAngles_; kk++) { + double ar = parent->mapAngleFromRef(angles_[kk],sys,sky); + str << radToDeg(ar) << ((kk<numAngles_-1) ? ' ' : ')'); + } + + str << '('; + str << setseparator(' ') << setprecision(3) << fixed; + for (int kk=0; kk<numAnnuli_; kk++) { + Vector rr = ptr->mapLenFromRef(annuli_[kk],sys,Coord::ARCSEC); + str << setunit('"') << rr << ((kk<numAnnuli_-1) ? ' ' : ')'); + } + str.unsetf(ios_base::floatfield); + str << setseparator(',') << '(' << setprecision(8) << radToDeg(aa) << ')'; + listProps(str); + } + else + str << "ignore"; + + str << (strip ? ';' : '\n'); + } + else { + if (conj) + str << "||"; + else + str << ";"; + } +} + +void Epanda::listXML(ostream& str, Coord::CoordSystem sys, Coord::SkyFrame sky, + Coord::SkyFormat format) +{ + FitsImage* ptr = parent->findFits(sys,center); + + XMLRowInit(); + XMLRow(XMLSHAPE,type_); + + XMLRowCenter(ptr,sys,sky,format); + XMLRowRadius(ptr,sys,annuli_,numAnnuli_); + XMLRowAng(sys,sky); + XMLRowAng(sys,sky,angles_,numAngles_); + + XMLRowProps(ptr,sys); + XMLRowEnd(str); +} + |