static char *sccs_id= (sccs_id, "@(#)OvalShape.c	3.6 7/28/92");

//$OvalShape,WedgeStretcher$
#include <math.h>

#include "OvalShape.h"
#include "DrawView.h"

inline float rad(float x)
{
    return x * PI / 180.0;
}

inline int ang2ang(int a)
{
    return (450-(a % 360)) % 360;
}

Point Angle2Point(Rectangle &r, int ang)
{
    Point p;
    float fa= rad(ang2ang(ang));
    p.x= int(((1.0 + cos(fa)) / 2.0) * float(r.extent.x-1) + 0.5);
    p.y= int(((1.0 - sin(fa)) / 2.0) * float(r.extent.y-1) + 0.5);
    return p + r.origin;
}

float Len(Point rad, Point p)
{
    return float(p.x*p.x)/(rad.x*rad.x) + float(p.y*p.y)/(rad.y*rad.y);
}

//---- Shape Methods -----------------------------------------------------------

static short OvalImage[]= {
#   include  "images/OvalShape.im"
};

MetaImpl(OvalShape, (I_S(startangle), I_S(endangle),
0));

OvalShape::OvalShape()
{
    startangle= 0;
    endangle= 360;
}

short *OvalShape::GetImage()
{
    return OvalImage;
}

void OvalShape::SetAngle(bool where, int ang)
{
    if (where)
	endangle= ang;
    else
	startangle= ang;
    Invalidate();
    Changed();
}

int OvalShape::GetAngle(bool where)
{
    return where ? endangle : startangle;
}

bool OvalShape::CanSplit()
{
    return !GetSplit();
}

Point OvalShape::Chop(Point p)
{
    return Angle2Point(bbox, bbox.PointToAngle(p));
}

Rectangle OvalShape::InvalRect()
{
    if (arrows)
	return bbox.Expand(Max(HandleSize, Point(4*pensize))/2+1);
    return Shape::InvalRect();
}

void OvalShape::Draw(Rectangle, Point p)
{
    Rectangle rr= RealRect(bbox, startangle);
    rr.origin+= p;
    GrFillWedge(rr, startangle, endangle);
    GrStrokeWedge(rr, startangle, endangle);
}

int HFlip(int a)
{
    a= 360-a;
    if (a > 360)
	a-= 360;
    else if (a < 0)
	a+= 360;
    return a;
}

int VFlip(int a)
{
    a= 180+360-a;
    if (a > 360)
	a-= 360;
    else if (a < 0)
	a+= 360;
    return a;
}

int FlipArrows(int a)
{
    if (a == 0 || a == 3)
	return a;
    return a^0x3;
}

void OvalShape::Flip(int n)
{
    int s, e;
    if (startangle == 0 && endangle == 360)
	return;
    switch (n) {
    case 1:
	s= VFlip(endangle);
	e= VFlip(startangle);
	arrows= FlipArrows(arrows);
	break;
    case 2:
	s= HFlip(endangle);
	e= HFlip(startangle);
	arrows= FlipArrows(arrows);
	break;
    case 3:
	s= HFlip(VFlip(startangle));
	e= HFlip(VFlip(endangle));
	break;
    default:
	return;
    }
    startangle= s;
    endangle= e;
}

void OvalShape::Outline(Point p1, Point p2)
{    
    if (startangle == 0 && endangle == 360) {
	GrStrokeOval(NormRect(p1, p2));
	return;
    }
    if (abs(p1.x-p2.x) < 2 || abs(p1.y-p2.y) < 2)
	return;
    int a, s, e;
    if (p2.x > p1.x) {
	if (p2.y < p1.y) {
	    s= VFlip(endangle);
	    e= VFlip(startangle);
	    a= FlipArrows(arrows);
	} else {
	    a= arrows;
	    s= startangle;
	    e= endangle;
	}
    } else {
	if (p2.y > p1.y) {
	    s= HFlip(endangle);
	    e= HFlip(startangle);
	    a= FlipArrows(arrows);
	} else {
	    s= HFlip(VFlip(startangle));
	    e= HFlip(VFlip(endangle));
	    a= arrows; // Forgotten in original source ? tom@tnosoes
	}
    }
    GrSetLineCap(a);
    GrStrokeWedge(RealRect(NormRect(p1, p2), s), s, e);
    GrSetLineCap(eDefaultCap);
}

Rectangle OvalShape::RealRect(Rectangle rr, int)
{
    return rr;
}

bool OvalShape::ContainsPoint(Point p)
{
	Rectangle rr= RealRect(bbox, startangle);
	Point center= rr.extent/2;
	
	if (startangle != 0 || endangle != 360) {
	    int ang= rr.PointToAngle(p);
	    if (startangle <= endangle) {
		if (ang < startangle || ang > endangle)
		    return FALSE;
	    } else {
		if (ang <= startangle && ang >= endangle)
		    return FALSE;
	    }
	}
	p-= rr.Center();
	if (Len(center+2, p) <= 1.0) {
	    if (pattern == ePatNone)
		return Len(center-(pensize+2), p) >= 1.0;
	    return TRUE;
	}
	return FALSE;
}

Point *OvalShape::MakeHandles(int *n)
{
    if (GetSplit()) {
	spts[0]= Angle2Point(bbox, endangle)-bbox.origin;
	spts[1]= Angle2Point(bbox, startangle)-bbox.origin;
	if (spts[0] == spts[1])
	    *n= 1;
	else
	    *n= 2;
	return spts;
    } else
	return Shape::MakeHandles(n);
}

ShapeStretcher *OvalShape::NewStretcher(DrawView *dv, int handle)
{
    if (GetSplit())
	return new WedgeStretcher(dv, this, handle, handle == 0);
    else
	return Shape::NewStretcher(dv, handle);
}

ostream& OvalShape::PrintOn(ostream& s)
{
    Shape::PrintOn(s);
    return s << startangle SP << endangle SP;
}
	
istream& OvalShape::ReadFrom(istream& s)
{
    Shape::ReadFrom(s);
    return s >> startangle >> endangle;
}

//---- Stretcher Methods -------------------------------------------------------

WedgeStretcher::WedgeStretcher(DrawView *dv, Shape *p, int h, bool w) : (dv, p, h)
{
    op= (OvalShape*)p;
    where= w;
    oldangle= op->GetAngle(where);
    angpt= Angle2Point(op->bbox, oldangle);
}

Command *WedgeStretcher::TrackMouse(TrackPhase tp, Point ap, Point pp, Point np)
{
    DrawCommand::TrackMouse(tp, ap, pp, np);
    newangle= op->bbox.PointToAngle(angpt+delta);
    anglept= Angle2Point(op->bbox, newangle);
    view->ShowInfo(tp, "%s: %d", where ? "end" : "start", newangle);
    if (tp == eTrackRelease && newangle == oldangle)
	    return gNoChanges;
    return this;
}

void WedgeStretcher::TrackFeedback(Point, Point, bool)
{
    GrLine(op->bbox.Center(), anglept);
}

void WedgeStretcher::TrackConstrain(Point, Point, Point*)
{
}

void WedgeStretcher::DoIt()
{
    op->SetAngle(where, newangle);
}

void WedgeStretcher::UndoIt()
{
    op->SetAngle(where, oldangle);
}
