/* * * Copyright (c) 1995 * Knowledge Science Institute, University of Calgary * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. The Knowledge Science Institute makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ #ifdef __BCPLUSPLUS__ //---Borland C++ #include #pragma hdrstop #endif #include "graphic.h" #include #if defined(QUICKWIN) #include #endif #include #include #include /****************************************************************************** ************************** class Line ************************************** ******************************************************************************/ const POINT POINT_0_0 = { 0, 0}; const POINT POINT_10_10 = {10,10}; Line::Line(GraphicContainer* parent, POINTPAIR* eps, COLOR color, ArrowHead* start, ArrowHead* end) : VisualGraphic(parent), EndPoints(POINT_0_0,POINT_10_10), ArrowHeads(start,end) { MTRACE(Line,Line) MCONSTRUCTOR_IMP_CODE(Line); if (eps) setEndPoints(*eps); setColor(color); MEXIT_CODE(Line,Line) } Line::Line(const Line& g, GraphicContainer* parent) : VisualGraphic(g,parent), EndPoints(POINT_0_0,POINT_10_10), ArrowHeads(g.ArrowHeads) { MTRACE(Line,Line) MCONSTRUCTOR_IMP_CODE(Line); setEndPoints(g.getEndPoints()); setColor(g.getColor()); MEXIT_CODE(Line,Line) } Line::~Line() { MTRACE(Line,~Line) MDESTRUCTOR_IMP_CODE(Line); MTRACEOUT(Line,~Line) } void Line::classInvarient(char* file, int line) const { VisualGraphic::classInvarient(file,line); } Line& Line::operator=(const Line& c) { MENTRY_CODE(Line,operator=); VisualGraphic::operator=(c); EndPoints=c.EndPoints; Color=c.Color; ArrowHeads = c.ArrowHeads; MEXIT_CODE(Line,operator=); return *this; } int Line::paint(DEVICE dev,RECTANGLE* r) { MENTRY_CODE(Line,paint); //if ((!r) || (r && Overlaps(r,getBoundingRect())) int ret = draw(dev); MEXIT_CODE(Line,paint); return ret; } int Line::draw(DEVICE dev) { MENTRY_CODE(Line,draw); int ret = MDrawLine(dev,EndPoints.first,EndPoints.second,&Color); if (ArrowHeads.first) ArrowHeads.first ->draw(dev,EndPoints.first,EndPoints.second); if (ArrowHeads.second) ArrowHeads.second->draw(dev,EndPoints.second,EndPoints.first); MEXIT_CODE(Line,draw); return ret; } RECTANGLE Line::getRect() const { MENTRY_CODE(Line,getRect); RECTANGLE r; r.left = min(EndPoints.first.x,EndPoints.second.x); r.top = min(EndPoints.first.y,EndPoints.second.y); r.right = max(EndPoints.first.x,EndPoints.second.x); r.bottom = max(EndPoints.first.y,EndPoints.second.y); MEXIT_CODE(Line,getRect); return r; } void Line::setRect(RECTANGLE& r) { MENTRY_CODE(Line,setRect); queueForPaint(); EndPoints.first.x = r.left; EndPoints.first.y = r.top; EndPoints.second.x = r.right; EndPoints.second.y = r.bottom; queueForPaint(); MEXIT_CODE(Line,setRect); } //return true if p is within a 2d-sized box of around q static int proximal(const POINT& p, const POINT& q, int d) { return p.x>(q.x-d) && p.x<(q.x+d) && p.y>(q.y-d) && p.y<(q.y+d); }; //return true if p is in r or within d units of being in r static int inRect(const POINT& p, const RECTANGLE& r, int d=0) { return (p.x >= (r.left-d)) && (p.x <= (r.right+d)) && (p.y >= (r.top-d)) && (p.y <= (r.bottom+d)); }; int Line::contains(POINT& p) { MENTRY_CODE(Line,contains); const int s = 5; //sensitivity to closeness to the line int ret = 0; if (proximal(p,EndPoints.first ,s)) ret = 1; else if (proximal(p,EndPoints.second,s)) ret = 2; else { RECTANGLE r = getRect(); //if the point is in the rectangle, we should do more computation if (inRect(p,r,s)) { COORDINATE_INDICI dx,dy; dx = EndPoints.second.x - EndPoints.first.x; dy = EndPoints.second.y - EndPoints.first.y; if (!dy) ret = 3; //hit the line -- don't divide by zero else { float slope = ((float)dy)/dx; //inverted slope POINT ideal = {p.x, ((int)(slope*(p.x-EndPoints.first.x))) + EndPoints.first.y}; if (proximal(p,ideal,3)) ret = 3; //hit the line; } } } MEXIT_CODE(Line,contains); return ret; } void Line::copyFeatures(VisualGraphic& g) { MENTRY_CODE(Line,contains); VisualGraphic::copyFeatures(g); //Line* s = dynamic_cast(&g); //if (s) { // Color = s->Color; // } MEXIT_CODE(Line,contains); } POINTPAIR Line::setEndPoints(POINTPAIR& eps) { MENTRY_CODE(Line,setEndPoints); queueForPaint(); EndPoints = eps; queueForPaint(); MEXIT_CODE(Line,setEndPoints); return EndPoints; } POINT Line::getEdge(CompassPoints cp) { MENTRY_CODE(Line,getEdge); MENTRY_CODE(Line,getEdge); return EndPoints.first; } /****************************************************************************** ************************** class Connector ************************************** ******************************************************************************/ Connector::Connector(GraphicContainer* parent, POINT& handle, unsigned short nEndPoints) : VisualGraphic(parent), Terminals(nEndPoints,NULL), Handle(handle), TerminalCount(nEndPoints) { MTRACE(Connector,Connector) MCONSTRUCTOR_IMP_CODE(Connector); MEXIT_CODE(Connector,Connector) } Connector::Connector(const Connector& g, GraphicContainer* parent) : VisualGraphic(g,parent), Terminals(g.TerminalCount,NULL), Handle(g.Handle), TerminalCount(g.TerminalCount) { MTRACE(Connector,Connector) MCONSTRUCTOR_IMP_CODE(Connector); vector::const_iterator i; int j = 0; for (i=g.Terminals.begin(); i!=g.Terminals.end(); i++,j++) { if (*i) Terminals[j] = (*i)->clone(); } MEXIT_CODE(Connector,Connector) } Connector::~Connector() { MTRACE(Connector,~Connector) flush(); MDESTRUCTOR_IMP_CODE(Connector); MTRACEOUT(Connector,~Connector) } void Connector::classInvarient(char* file, int line) const { VisualGraphic::classInvarient(file,line); MCLASSINVTEST(TerminalCount?Terminals.size()<=TerminalCount:true); } Connector& Connector::operator=(const Connector& g) { VisualGraphic::operator=(g); TerminalCount = g.TerminalCount; Handle = g.Handle; flush(); vector::const_iterator i; for (i=g.Terminals.begin(); i!=g.Terminals.end(); i++) { Terminals.push_back((*i)->clone()); } return *this; } int Connector::paint(DEVICE dev,RECTANGLE* r) { MENTRY_CODE(Connector,paint); int ret = draw(dev); MEXIT_CODE(Connector,paint); return ret; } int Connector::draw(DEVICE dev) { MENTRY_CODE(Connector,draw); vector::iterator i; for (i=Terminals.begin(); i!=Terminals.end(); i++) { if (*i) (*i)->draw(dev); } RECTANGLE r = {Handle.x-1,Handle.y-1,Handle.x+1,Handle.y+1}; MRectangle(dev,r); int ret = 0; MEXIT_CODE(Connector,draw); return ret; } RECTANGLE Connector::getRect() const { MENTRY_CODE(Connector,getRect); RECTANGLE r={MAXRECTANGLE.right,MAXRECTANGLE.bottom,MAXRECTANGLE.left,MAXRECTANGLE.top};//an absurd rectangle! vector::const_iterator i; for (i=Terminals.begin(); i!=Terminals.end(); i++) { if (*i) { RECTANGLE r2 = (*i)->getBoundingRect(); r.left = min(r.left ,r2.left); r.top = min(r.top ,r2.top); r.right = max(r.right ,r2.right); r.bottom = max(r.bottom,r2.bottom); } } MEXIT_CODE(Connector,getRect); return r; } int ConnectorTerminalTargetNumber=-1; //global variable to hold the arm hit int Connector::contains(POINT& p) { //returns 0:miss; 1:hit the handle; 2:hit a line; >=3:hit the (i-3)th arc's distal terminal MENTRY_CODE(Connector,contains); ConnectorTerminalTargetNumber=-1; const int s = 5; //sensitivity to the handle location int ret = 0; if (proximal(p,Handle,s)) { ret = 1; //hit the handle } else { int count = 0; vector::iterator i; for (i=Terminals.begin(); i!=Terminals.end() && !ret; i++,count++) { if (*i) ret = (*i)->contains(p); //hit one of the terminal lines if (ret==2) { //must have hit the far end of line ret = count+3; //return INDICI+3 to indicate which terminal was hit } else if (ret) ret = 2; //hit the middle of a line if (ret) ConnectorTerminalTargetNumber=count; } } MEXIT_CODE(Connector,contains); return ret; } void Connector::setEndPoint(unsigned short n, POINT& pt, const char* lineType) { MENTRY_CODE(Connector,setEndPoint); if (TerminalCount || nqueueForPaint(Terminals[n]->getBoundingRect()); delete Terminals[n]; } Terminals[n] = GraphicsFactory::getInstance()->getLine(lineType); if (Terminals[n]) Terminals[n]->setStartPoint(Handle); } if (!Terminals[n]) { Terminals[n] = GraphicsFactory::getInstance()->getLine("default"); if (Terminals[n]) Terminals[n]->setStartPoint(Handle); } if (Terminals[n]) { if (getParent()) getParent()->queueForPaint(Terminals[n]->getBoundingRect()); Terminals[n]->setEndPoint(pt); Terminals[n]->setStartPoint(Handle); if (getParent()) getParent()->queueForPaint(Terminals[n]->getBoundingRect()); } } MEXIT_CODE(Connector,setEndPoint); } void Connector::setHandle(POINT handle) { MENTRY_CODE(Connector,setHandle); queueForPaint(); Handle = handle; vector::iterator i; for (i=Terminals.begin(); i!=Terminals.end(); i++) { if (*i) (*i)->setStartPoint(Handle); } queueForPaint(); MEXIT_CODE(Connector,setHandle); } POINT Connector::operator[](unsigned short n) { MENTRY_CODE(Connector,operator[]); POINT ret = {0,0}; if (TerminalCount || ngetEndPoints().second; } MEXIT_CODE(Connector,operator[]); return ret; } Line* Connector::getLine(unsigned short n) { MENTRY_CODE(Connector,getLine); Line* ret = NULL; if (TerminalCount || n::iterator i; for (i=Terminals.begin(); i!=Terminals.end(); i++) if (*i) delete *i; Terminals.erase(Terminals.begin(),Terminals.end()); MEXIT_CODE(Connector,flush); } POINT Connector::getEdge(CompassPoints cp) { MENTRY_CODE(Connector,getEdge); MENTRY_CODE(Connector,getEdge); return Handle; } /****************************************************************************** ************************** class Shape ************************************** ******************************************************************************/ Shape::Shape(GraphicContainer* parent, RECTANGLE* r, char* label, COLOR fill, COLOR border, COLOR text) : VisualGraphic(parent) { MTRACE(Shape,Shape) MCONSTRUCTOR_IMP_CODE(Shape); if (label) Text = new TextDrawing(label); else Text = NULL; setRect(r?*r:RECTANGLE_EMPTY); setFillColor(fill); setBorderColor(border); setTextColor(text); ShapeFlags = r?SH_AUTOSIZE|SH_RECOMPUTE:0; MEXIT_CODE(Shape,Shape) } Shape::Shape(const Shape& g, GraphicContainer* parent) : VisualGraphic(g,parent) { MTRACE(Shape,Shape) MCONSTRUCTOR_IMP_CODE(Shape); Text = NULL; setText(g.getText()); setRect(g.getRect()); setFillColor(g.getFillColor()); setBorderColor(g.getBorderColor()); setTextColor(g.getTextColor()); ShapeFlags = g.ShapeFlags; MEXIT_CODE(Shape,Shape) } Shape::~Shape() { MTRACE(Shape,~Shape) MDESTRUCTOR_IMP_CODE(Shape); if (Text) delete Text; MTRACEOUT(Shape,~Shape) } void Shape::classInvarient(char* file, int line) const { VisualGraphic::classInvarient(file,line); } Shape& Shape::operator=(const Shape& c) { VisualGraphic::operator=(c); //GraphicContainer::operator=(c); R=c.R; FillColor=c.FillColor; BorderColor=c.BorderColor; TextColor=c.TextColor; setText(c.getText()); ShapeFlags = c.ShapeFlags; return *this; } int Shape::paint(DEVICE dev,RECTANGLE* r) { MENTRY_CODE(Shape,paint); #ifdef EASYWIN cout << dynamic_cast(getParent())->id() << ' '; #else //if ((!r) || (r && Overlaps(r,getBoundingRect())) { RECTANGLE t = getRect(); if (t.right<=t.left || t.bottom<=t.top) ShapeFlags |= (SH_AUTOSIZE|SH_RECOMPUTE); if ((ShapeFlags&SH_AUTOSIZE) && (ShapeFlags&SH_RECOMPUTE)) { if (Text) { POINT s = Text->getSize(dev); RECTANGLE inner = getInnerRect(); if (!(s.x==inner.right-inner.left && s.y==inner.bottom-inner.top)) setInnerRectSize(s.x,s.y); } else {R.right=R.left+40; R.bottom=R.top+30;} if (getParent()) getParent()->notifyChanged(id()); queueForPaint(); } ShapeFlags &= ~SH_RECOMPUTE; //should have finished recomputing before drawing draw(dev); if (Text) { POINT c; RECTANGLE inner = getInnerRect(); c.x = inner.left + ((inner.right-inner.left)>>1); c.y = inner.top + ((inner.bottom-inner.top)>>1); Text->draw(dev,c,TextColor); } #endif MEXIT_CODE(Shape,paint); return 0; } Shape* Shape::copy(Shape& s) { MENTRY_CODE(Shape,copy); setRect(s.getRect()); setFillColor(s.getFillColor()); setBorderColor(s.getBorderColor()); setTextColor(s.getTextColor()); MEXIT_CODE(Shape,copy); return this; } int Shape::draw(DEVICE dev) { MENTRY_CODE(Shape,draw); int ret = MRectangle(dev,R,&FillColor,&BorderColor); MEXIT_CODE(Shape,draw); return ret; } RECTANGLE Shape::getInnerRect() const { MENTRY_CODE(Shape,getInnerRect); RECTANGLE r = {R.left+5,R.top+5,R.right-5,R.bottom-5}; MEXIT_CODE(Shape,getInnerRect); return r; } RECTANGLE Shape::setInnerRectSize(long width, long height) { MENTRY_CODE(Shape,setInnerRect); queueForPaint(); RECTANGLE r = {R.left,R.top,R.left+width+10,R.top+height+10}; R = r; queueForPaint(); MEXIT_CODE(Shape,setInnerRect); return r; } void Shape::setRect(RECTANGLE& r) { MENTRY_CODE(Shape,setRect); queueForPaint(); R = r; queueForPaint(); MEXIT_CODE(Shape,setRect); } RECTANGLE Shape::getBoundingRect() const { MENTRY_CODE(Shape,getBoundingRect); RECTANGLE ret = Graphic::getBoundingRect(); if (Text) { POINT p = Text->getSize(); if (p.x > (ret.right-ret.left)) { long extend = ((p.x - (ret.right-ret.left))>>1) + 3; ret.left -= extend; ret.right += extend; } if (p.y > (ret.bottom-ret.top)) { long extend = ((p.x - (ret.bottom-ret.top))>>1) + 3; ret.top -= extend; ret.bottom += extend; } } MEXIT_CODE(Shape,getBoundingRect); return ret; } const char* Shape::getText() const { MENTRY_CODE(Shape,getText); char* ret = NULL; if (Text) ret = (char*)Text->getText().c_str(); MEXIT_CODE(Shape,getText); return ret; } void Shape::setText(const char* c) { MENTRY_CODE(Shape,setText); queueForPaint(); if (c) { //either create new text or change it if (Text) Text->setText((char*)c); else Text = new TextDrawing(c); } else if (Text) { //whip out text delete Text; Text = NULL; } queueForPaint(); MEXIT_CODE(Shape,setText); } void Shape::copyFeatures(VisualGraphic& g) { MENTRY_CODE(Shape,copyFeatures); VisualGraphic::copyFeatures(g); queueForPaint(); Shape* s = dynamic_cast(&g); if (s) { //FillColor = s->FillColor; //BorderColor = s->BorderColor; //TextColor = s->TextColor; R.left = s->R.left; R.top = s->R.top; R.right = s->R.right; R.bottom = s->R.bottom; setText(s->getText()); } queueForPaint(); MEXIT_CODE(Shape,copyFeatures); } POINT Shape::getEdge(CompassPoints cp) { MENTRY_CODE(Shape,getEdge); POINT p; RECTANGLE r = getRect(); p.x = (cp&W) ?r.left :(cp&E) ?r.right :(r.left+((r.right-r.left)>>1)); p.y = (cp&S) ?r.bottom :(cp&N) ?r.top :(r.top +((r.bottom-r.top)>>1)); MENTRY_CODE(Shape,getEdge); return p; } /****************************************************************************** ************************** class RectangleShape ******************************* ******************************************************************************/ void RectangleShape::classInvarient(char* file, int line) const { Shape::classInvarient(file,line); } int RectangleShape::draw(DEVICE dev) { MENTRY_CODE(RectangleShape,draw); int ret = MRectangle(dev,R,&FillColor,&BorderColor); MEXIT_CODE(RectangleShape,draw); return ret; } RECTANGLE RectangleShape::getInnerRect() const { MENTRY_CODE(RectangleShape,getInnerRect); RECTANGLE r = {R.left+5,R.top+5,R.right-5,R.bottom-5}; MEXIT_CODE(RectangleShape,getInnerRect); return r; } RECTANGLE RectangleShape::setInnerRectSize(long width, long height) { MENTRY_CODE(RectangleShape,setInnerRect); RECTANGLE r = {R.left,R.top,R.left+width+10,R.top+height+10}; R = r; MEXIT_CODE(RectangleShape,setInnerRect); return r; } /****************************************************************************** ************************** class EllipseShape ******************************* ******************************************************************************/ void EllipseShape::classInvarient(char* file, int line) const { Shape::classInvarient(file,line); } int EllipseShape::draw(DEVICE dev) { MENTRY_CODE(EllipseShape,draw); int ret = MEllipse(dev,R,&FillColor,&BorderColor); MEXIT_CODE(EllipseShape,draw); return ret; } RECTANGLE EllipseShape::getInnerRect() const { MENTRY_CODE(EllipseShape,getInnerRect); int w = R.right-R.left; int h = R.bottom-R.top; w = 0.1465*w; h = 0.1465*h; RECTANGLE r = {R.left+w,R.top+h,R.right-w,R.bottom-h}; MEXIT_CODE(EllipseShape,getInnerRect); return r; } RECTANGLE EllipseShape::setInnerRectSize(long width, long height) { MENTRY_CODE(EllipseShape,setInnerRect); RECTANGLE r = {R.left,R.top,R.left+(long)(1.4142*width)+5,R.top+(long)(1.4142*height)+5}; R = r; MEXIT_CODE(EllipseShape,setInnerRect); return r; } POINT EllipseShape::getEdge(CompassPoints cp) { MENTRY_CODE(EllipseShape,getEdge); POINT p; RECTANGLE r = getRect(); p.x = (cp&W) ? (cp&N||cp&S)?r.left+.146*(r.right-r.left):r.left : (cp&E) ?(cp&N||cp&S)?r.right-.146*(r.right-r.left):r.right :(r.left+r.right)>>1; p.y = (cp&N) ? (cp&W||cp&E)?r.top+.146*(r.bottom-r.top):r.top : (cp&S) ?(cp&W||cp&E)?r.bottom-.146*(r.bottom-r.top):r.bottom :(r.top+r.bottom)>>1; MENTRY_CODE(EllipseShape,getEdge); return p; } /****************************************************************************** ************************** class RoundRectShape ******************************* ******************************************************************************/ void RoundRectShape::classInvarient(char* file, int line) const { Shape::classInvarient(file,line); } int RoundRectShape::draw(DEVICE dev) { MENTRY_CODE(RoundRectShape,draw); //set the rounded corners to be 5 (or less if the rectangle is too small) int w = R.right-R.left; int h = R.bottom-R.top; w = (w<30)?w>>1:15; h = (h<30)?h>>1:15; int ret = MRoundRect(dev,R,w,h,&FillColor,&BorderColor); MEXIT_CODE(RoundRectShape,draw); return ret; } int RoundRectShape::contains(POINT& p) { MENTRY_CODE(RoundRectShape,draw); //set the rounded corners to be 5 (or less if the rectangle is too small) int w = R.right-R.left; int h = R.bottom-R.top; w = (w<30)?w>>1:15; h = (h<30)?h>>1:15; int ret = MIsPointInRoundRect(p,R,w,h); MEXIT_CODE(RoundRectShape,draw); return ret; } RECTANGLE RoundRectShape::getInnerRect() const { MENTRY_CODE(RoundRectShape,getInnerRect); int w = R.right-R.left; int h = R.bottom-R.top; w = (w<30)?w>>3:3; h = (h<30)?h>>3:3; RECTANGLE r = {R.left+w,R.top+h,R.right-w,R.bottom-h}; MEXIT_CODE(RoundRectShape,getInnerRect); return r; } RECTANGLE RoundRectShape::setInnerRectSize(long width, long height) { MENTRY_CODE(RoundRectShape,setInnerRect); RECTANGLE r = {R.left,R.top,R.left+width+15,R.top+height+15}; R = r; MEXIT_CODE(RoundRectShape,setInnerRect); return r; } POINT RoundRectShape::getEdge(CompassPoints cp) { MENTRY_CODE(RoundRectShape,getEdge); POINT p; RECTANGLE r = getRect(); int w = r.right-r.left; int h = r.bottom-r.top; w = (w<30)?w>>1:15; h = (h<30)?h>>1:15; w *= .146; h *= .146; p.x = (cp&W) ? (cp&N||cp&S)?r.left+w:r.left : (cp&E) ?(cp&N||cp&S)?r.right-w:r.right :(r.left+r.right)>>1; p.y = (cp&N) ? (cp&W||cp&E)?r.top+h:r.top : (cp&S) ?(cp&W||cp&E)?r.bottom-h:r.bottom :(r.top+r.bottom)>>1; MEXIT_CODE(RoundRectShape,getEdge); return p; } /****************************************************************************** ***************************** class ArrowHead ********************************* ******************************************************************************/ void ArrowHead::classInvarient(char* file, int line) const { } //this method can be overridden to draw any shape at the angle of the line from //'head' to 'tail'. Points in the figure (arrowhead) are in given in polar //coordinates as though the specified line was (0,0)(n,0) (polar coordinates). //Any overridding method should use the getTheta() and translatePoint() //methods to convert the polar coordinates of the figure to the appropriate //cartisian points relative to the specified line. int ArrowHead::draw(DEVICE device, POINT& head, POINT& tail) { MENTRY_CODE(ArrowHead,draw); //use the getTheta() method in subclasses to get the angle of incidence (theta) // for the translatePoint() method float theta = getTheta(head,tail); POINT pts[3]; pts[0] = head; //tip //use the translatePoint() method to translate points in subclasses pts[1] = translatePoint(theta,head,10, 2.8648); //radius=10, p_theta=+20deg (in radians) pts[2] = translatePoint(theta,head,10,-2.8648); //radius=10, p_theta=-20deg (in radians) MPolygon(device,pts,3,&FillColor,&BorderColor); MEXIT_CODE(ArrowHead,draw); } POINT ArrowHead::translatePoint(float theta, POINT& head, unsigned int p_radius, float p_theta) { MENTRY_CODE(ArrowHead,translatePoint); POINT ret; float thetaPrime = p_theta + theta; // rotate in polor coordinates //convert polor to cartisian and translate to original coordinate ret.x = (-cos(thetaPrime) * p_radius) + head.x; ret.y = (-sin(thetaPrime) * p_radius) + head.y; MEXIT_CODE(ArrowHead,translatePoint); return ret; } float ArrowHead::getTheta(POINT&head, POINT& tail) { MENTRY_CODE(ArrowHead,getTheta); float y = tail.y-head.y; //translate to origin float x = tail.x-head.x; //translate to origin if (x==0 && y==0) return 0.0; //can't handle (0,0) float ret = atan2(y,x); MEXIT_CODE(ArrowHead,getTheta); return ret; }