/** \file cbezier.c
 * Bezier Command. Draw or modify a Bezier (Track or Line).
 */
 /*  XTrkCad - Model Railroad CAD
 *
 * Cubic Bezier curves have a definitional representation as an a set of four points.
 * The first and fourth are the end points, while the middle two are control points.
 * The control points positions define the angle at the ends and by their relative positions the overall
 * curvature. This representation is a familiar approach for those who know drawing programs such as Adobe
 * Illustrator or CorelDraw.
 *
 * In XTrackCAD, the Bezier form is also represented and drawn as a set of
 * joined circular arcs that approximate the Bezier form within a small tolerance. This is because
 * many of the operations we need to do are either computationally difficult or
 * impossible using the Bezier equations. For example, creating a parallel Bezier
 * which is necessary to draw a track with two lines or sleepers has no easy, stable solution.
 * But the program is already able to do these tasks for straight lines and curves.
 *
 * Note that every time we change the Bezier points we have to recalculate the arc approximation,
 * but that means that the majority of the time we are using the simpler approximation.
 *
 * We do not allow Bezier curves that have loops or cusps as they make no sense for tracks and
 * can easily be approximated for lines with multiple unaligned Bezier curves.
 *
 * This program borrows from particular ideas about converting Bezier curves that Pomax placed into
 * open source. The originals in Javascript can be found at github.com/Pomax.
 * The web pages that explain many other techniques are located at https://pomax.github.io/bezierinfo
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#include "common.h"
#include "draw.h"
#include "ccurve.h"
#include "cbezier.h"
#include "tbezier.h"
#include "cstraigh.h"
#include "drawgeom.h"
#include "cjoin.h"
#include "common.h"
#include "track.h"
#include "wcolors.h"
#include "param.h"
#include "fileio.h"
#include "layout.h"
#include "cundo.h"
#include "compound.h"

extern drawCmd_t tempD;


/*
 * STATE INFO
 */
enum Bezier_States { NONE,
	POS_1,
	CONTROL_ARM_1,
	POS_2,
	CONTROL_ARM_2,
	PICK_POINT,
	POINT_PICKED,
	TRACK_SELECTED };

typedef struct {
		curveData_t curveData;
		double start;
		double end;
		coOrd pos0;
		coOrd pos1;
		} bCurveData_t;

static struct {
		enum Bezier_States state;
		coOrd pos[4];
        int selectPoint;
        wDrawColor color;
        DIST_T width;
		track_p trk[2];
		EPINX_T ep[2];
		dynArr_t crvSegs_da;
		int crvSegs_da_cnt;
		trkSeg_t cp1Segs_da[4];
		int cp1Segs_da_cnt;
		trkSeg_t cp2Segs_da[4];
		int cp2Segs_da_cnt;
		BOOL_T unlocked;
		track_p selectTrack;
		BOOL_T track;
		DIST_T minRadius;
		DIST_T trackGauge;
		} Da;

static dynArr_t anchors_da;
#define anchors(N) DYNARR_N(trkSeg_t,anchors_da,N)

/**
 * Draw a ControlArm.
 * A control arm has two filled or unfilled circles for endpoints and a straight line between them.
 * If the end or control point is not selectable we don't mark it with a circle.
 * If a selectable end or control point is unlocked place a filled circle on it, otherwise an empty circle.
 * A red color indicates that this arm, end or control point is "active" as it was selected.
 */
int createControlArm(
                     trkSeg_t sp[],   //seg pointer for up to 3 trkSegs (ends and line)
                     coOrd pos0,     //end on curve
                     coOrd pos1,     // control point at other end of line
				     BOOL_T track,   // isTrack()? (otherwise Line)
				     BOOL_T selectable, 	// can this arm be selected?
                     BOOL_T cp_direction_locked, //isFixed to track
				     int point_selected, //number of point 0, 1 or -1
					 wDrawColor color   //drawColorBlack or drawColorWhite
                      )
{
    DIST_T d, w;
    d = tempD.scale*0.25;
    w = tempD.scale/tempD.dpi; /*double width*/
    sp[0].u.l.pos[0] = pos0;
    sp[0].u.l.pos[1] = pos1;
    sp[0].type = SEG_STRLIN;
    sp[0].width = w;
    sp[0].color = (point_selected>=0)?drawColorRed:drawColorBlack;
    int n = 0;
    if (selectable) {
    	for (int j=0;j<2;j++) {
    		if (j==0 && cp_direction_locked) continue;   //Don't show select circle if end locked
    		n++;
    		sp[n].u.c.center = j==0?pos0:pos1;
    		sp[n].u.c.radius = d/4;
    		sp[n].width = w;
    		sp[n].color = (j==point_selected)?drawColorRed:drawColorBlack;
    		if (j==point_selected && cp_direction_locked) {
    			sp[n].type = SEG_FILCRCL;
    		} else {
    			sp[n].type = SEG_CRVLIN;
    			sp[n].u.c.a0 = 0.0;
    			sp[n].u.c.a1 = 360.0;
    		}
    	}
    }
    return n+1;
}

coOrd getPoint(coOrd pos[4], double s) {
	double mt = 1-s;
	double a = mt*mt*mt;
	double b = mt*mt*s*3;
	double c = mt*s*s*3;
	double d = s*s*s;
	coOrd ret;
	ret.x = a*pos[0].x + b*pos[1].x + c*pos[2].x + d*pos[3].x;
	ret.y = a*pos[0].y + b*pos[1].y + c*pos[2].y + d*pos[3].y;
	return ret;
}
/*
 * Get Error between a Bezier and an arc centered at pc that goes from start to end
 *
 * Because the curve is defined to pass through the start and the end and the middle, the test is
 * to see how much of an error there is between those points. If the sum of the errors is off by more \
 * than 0.5 pixels - that will mean it is not a good fit.
 *
 */
double BezError(coOrd pos[4], coOrd center, coOrd start_point, double start, double end) {
	double quarter = (end - start) / 4;  // take point at 1/4 and 3/4 and check
	coOrd c1 = getPoint(pos, start + quarter);
	coOrd c2 = getPoint(pos, end - quarter);
	double ref = FindDistance(center, start_point); //radius
	double d1  = FindDistance(center, c1);          // distance to quarter
	double d2  = FindDistance(center, c2);          // distance to three quarters
	return fabs(d1-ref) + fabs(d2-ref);               //total error at quarter points
};

/*
 * Get distance between a point and a line segment
 */

double DistanceToLineSegment(coOrd p, coOrd l1, coOrd l2) {
	double A = p.x - l1.x;
    double B = p.y - l1.y;
    double C = l2.x - l1.x;
    double D = l2.y - l1.y;

    double dot = A * C + B * D;
    double len_sq = C * C + D * D;
    double param = -1;
    if (len_sq != 0) //non 0 length line
    	param = dot / len_sq;

    double xx, yy;

    if (param < 0) {      // zero length line or beyond end use point 1
    	xx = l1.x;
    	yy = l1.y;
    }  else if (param > 1) {  // beyond point 2 end of line segment
    	xx = l2.x;
    	yy = l2.y;
    }  else {					// In the middle
    	xx = l1.x + param * C;
    	yy = l1.y + param * D;
    }

    double dx = p.x - xx;        //distance to perpendicular (or end point)
    double dy = p.y - yy;
    return sqrt(dx * dx + dy * dy);
}

/*
 * Get Error between a straight line segment and the Bezier curve.
 * Sum distance to straight line of quarter points.
 */

double BezErrorLine(coOrd pos[4], coOrd start_point, coOrd end_point, double start, double end) {
	double quarter = (end - start) / 4;  // take point at 1/4 and 3/4 and check
	coOrd c1 = getPoint(pos, start + quarter);
	coOrd c2 = getPoint(pos, end - quarter);
	double d1 = DistanceToLineSegment(c1, start_point, end_point);
	double d2 = DistanceToLineSegment(c2, start_point, end_point);
	return fabs(d1)+fabs(d2);
}

/*
 * Add element to DYNARR pointed to by caller from segment handed in
 */
void addSegBezier(dynArr_t * array_p, trkSeg_p seg) {
	trkSeg_p s;


	DYNARR_APPEND(trkSeg_t,* array_p, 1);          //Adds 1 to cnt
	s = &DYNARR_N(trkSeg_t,*array_p,(array_p->cnt)-1);
	s->type = seg->type;
	s->color = seg->color;
	s->width = seg->width;
	s->bezSegs.cnt = 0;
	s->bezSegs.ptr=NULL;
	s->bezSegs.max = 0;
	if ((s->type == SEG_BEZLIN || s->type == SEG_BEZTRK) && seg->bezSegs.cnt) {
		s->u.b.angle0 = seg->u.b.angle0;  //Copy all the rest
		s->u.b.angle3 = seg->u.b.angle3;
		s->u.b.length = seg->u.b.length;
		s->u.b.minRadius = seg->u.b.minRadius;
		for (int i=0;i<4;i++) s->u.b.pos[i] = seg->u.b.pos[i];
		s->u.b.radius0 = seg->u.b.radius3;
		s->bezSegs.cnt = 0;
		if (s->bezSegs.ptr) MyFree(s->bezSegs.ptr);
		s->bezSegs.max = 0;
		s->bezSegs.ptr = NULL; //Make sure new space as addr copied in earlier from seg
		for (int i = 0; i<seg->bezSegs.cnt; i++) {
			addSegBezier(&s->bezSegs,(((trkSeg_p)seg->bezSegs.ptr)+i)); //recurse for copying embedded Beziers as in Cornu joint
		}
	} else {
		s->u = seg->u;
	}
}

enum BezierType {PLAIN, LOOP, CUSP, INFLECTION, DOUBLEINFLECTION, LINE, ENDS, COINCIDENT } bType;

/*
 * Analyse Bezier.
 *
 * Using results from Maureen C. Stone of XeroxParc and Tony deRose of U of Washington
 * characterise the curve type and find out what features it has.
 * We will eliminate cusps and loops as not useful forms. Line, Plain, Inflection and DoubleInflection are ok.
 *
 */
EXPORT enum BezierType AnalyseCurve(coOrd inpos[4], double *Rfx, double *Rfy, double *cusp) {

	*Rfx = *Rfy = 0;
	if (Da.track && inpos[0].x == inpos[3].x && inpos[0].y == inpos[3].y ) {
		return ENDS;
	}

	DIST_T d01 = FindDistance(inpos[0],inpos[1]);
	DIST_T d12 = FindDistance(inpos[1],inpos[2]);
	DIST_T d02 = FindDistance(inpos[0],inpos[2]);
	if (d01+d12 == d02) {								//straight
		DIST_T d23 = FindDistance(inpos[2],inpos[3]);
		DIST_T d03 = FindDistance(inpos[0],inpos[3]);
		if (d02+d23 == d03) return LINE;
	}
	int common_points = 0;
	for (int i=0;i<3;i++) {
		if (inpos[i].x == inpos[i+1].x && inpos[i].y == inpos[i+1].y) common_points++;
	}
	for (int i=0;i<2;i++) {
		if (inpos[i].x == inpos[i+2].x && inpos[i].y == inpos[i+2].y) common_points++;
	}

	if (common_points>2) {
		return COINCIDENT;
	}

	coOrd pos[4];
	coOrd offset2, offset = inpos[0];

	for (int i=0;i<4;i++) {     				//move to zero origin
		pos[i].x = inpos[i].x-offset.x;
		pos[i].y = inpos[i].y-offset.y;
	}

	offset2.x = -offset.x + pos[3].x;
	offset2.y = -offset.y + pos[3].y;
	if (pos[1].y == 0.0) {   					//flip order of points
		for (int i=0;i<4;i++) {
			coOrd temp_pos = pos[i];
			pos[i].x = pos[3-i].x - offset2.x;
			pos[i].y = pos[3-i].y - offset2.y;
			pos[3-i] = temp_pos;
		}
		if (pos[1].y == 0.0) {					//Both ways round the second point has no y left after translation
			return PLAIN;
		}
	}
	double f21 = (pos[2].y)/(pos[1].y);
	double f31 = (pos[3].y)/(pos[1].y);
	if (fabs(pos[2].x-(pos[1].x*f21)) <0.0001) return PLAIN;   //defend against divide by zero
	double fx = (pos[3].x-(pos[1].x*f31))/(pos[2].x-(pos[1].x*f21));
	double fy = f31+(1-f21)*fx;
	*Rfx = fx;
	*Rfy = fy;
	*cusp = fabs(fy - (-(fx*fx)+2*fx+3)/4);

	if (fy > 1.0) return INFLECTION;
	if (fx >= 1.0) return PLAIN;
	if (fabs(fy - (-(fx*fx)+2*fx+3)/4) <0.100) return CUSP;
	if (fy < (-(fx*fx)+2*fx+3)/4) {
		if (fx <= 0.0 && fy >= (3*fx-(fx*fx))/3) return LOOP;
		if (fx > 0.0 && fy >= (sqrt(3*(4*fx-fx*fx))-fx)/2) return LOOP;
		return PLAIN;
	}

	return DOUBLEINFLECTION;
}

/*
 * ConvertToArcs
 * Take a Bezier curve and turn it into a set of circular arcs, such that the error between the arc and the
 * Bezier is under 0.5 pixels at maxiumum zoom.
 *
 * This enables us to use normal methods (operating over the array of arcs)
 * to perform actions on the Bezier and also to export it to DXF.
 *
 */
EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawColor color, DIST_T width) {
	 double t_s = 0.0, t_e = 1.0;
	 double errorThreshold = 0.05;
	 bCurveData_t prev_arc;
	 prev_arc.end = 0.0;
	 bCurveData_t arc;
	 segs->cnt = 0; //wipe out
	 BOOL_T safety;
	 int col = 0;

	 double prev_e = 0.0;
	      // we do a binary search to find the "good `t` closest to no-longer-good"
	 do {
	     safety=FALSE;
	     // step 1: start with the maximum possible arc length
	      t_e = 1.0;
	     // points:
	     coOrd start_point, mid_point, end_point;
	     // booleans:
	     BOOL_T curr_good = FALSE, prev_good = FALSE, done = FALSE;
	      // numbers:
	      double t_m, step = 0;
	      // step 2: find the best possible arc
	      do {						// !done
	          prev_good = curr_good;   //remember last time
	          t_m = (t_s + t_e)/2;
	          step++;
	          start_point = getPoint(pos, t_s); //Start of arc
	          mid_point = getPoint(pos, t_m);  //Middle of trial arc
	          end_point = getPoint(pos, t_e);  //End of trial Arc

	          PlotCurve( crvCmdFromChord, start_point, end_point, mid_point,
	        		  	  &(arc.curveData), FALSE, 0.0 );  //Find Arc through three points

	          arc.start = t_s; 		//remember start
	          arc.end = t_e;			//remember end
	          arc.pos0 = start_point; //remember start point (used for Straight)
	          arc.pos1 = end_point;	// Remember end point (used for Straight)

	          if (arc.curveData.type == curveTypeStraight) {
	          	  double error = BezErrorLine(pos,start_point,end_point, t_s, t_e);
	          	  curr_good = (error <= errorThreshold/4);
	          	  //arc.curveData.a0 = FindAngle(start_point,end_point);
	          	  //arc.curveData.a1 = FindAngle(end_point,start_point);

	          } else if (arc.curveData.type == curveTypeNone) {
	        	  return FALSE;			//Something wrong
	          } else {
	        	  double error = BezError(pos, arc.curveData.curvePos, start_point, t_s, t_e);
	          	  curr_good = (error <= errorThreshold/4);
	          };

	          done = prev_good && !curr_good; //Was better than this last time?
	          if(!done) {
	          	  // this arc is fine: we can move 'e' up to see if we can find a wider arc
	          	  if(curr_good) {
	          		  prev_e = t_e;		  //remember good end only
	          		  prev_arc = arc;
	          		  // if e is already at max, then we're done for this arc.
	          		  if (t_e >= 1.0) {
	          			  // make sure we cap at t=1
	          			  arc.end = prev_e = 1.0;
	          			  // if we capped the arc segment to t=1 we also need to make sure that
	          			  // the arc's end angle is correct with respect to the bezier end point.
	          			  if (t_e > 1.0) {
	          				  if (arc.curveData.type != curveTypeStraight) {
	          					  coOrd d;
	          					  d.x = arc.curveData.curvePos.x + fabs(arc.curveData.curveRadius) * cos(D2R(arc.curveData.a1));
	          					  d.y = arc.curveData.curvePos.y + fabs(arc.curveData.curveRadius) * sin(D2R(arc.curveData.a1));

	          					  arc.curveData.a1 += FindAngle(d, getPoint(pos,1.0));
	          					  t_e = 1.0;
	          				  }
	          			  }
	          			  prev_arc = arc;
	          			  done = TRUE;
	          			  break;
	          		  }
	          		  // if not, move it up by half the iteration distance or to end
	          		  t_e = t_e + (t_e-t_s)/2;
	          		  if (t_e > 1.0) t_e = 1.0;
	          	  }
	          	  // this is a bad arc: we need to move 'e' down to find a good arc
	          	  else {
	          		  t_e = t_m;
	          	  }
	          }         // If !Done end
	        } while(!done && safety++<100);
	        if(safety>=100) {
	          return FALSE;				//Failed to make into arcs
	        }
	        prev_arc = prev_arc.end==0.0?arc:prev_arc;
	        trkSeg_t curveSeg;  			//Now set up tempSeg to copy into array
	        curveSeg.width = track?0:width;
	        if ( prev_arc.curveData.type == curveTypeCurve ) {
	        		if (track)
	        			curveSeg.color = (fabs(prev_arc.curveData.curveRadius)<(GetLayoutMinTrackRadius()-EPSILON))?exceptionColor:normalColor;
	        		else
	        			curveSeg.color = color;
	        		curveSeg.type = track?SEG_CRVTRK:SEG_CRVLIN;
	        		curveSeg.u.c.a0 = prev_arc.curveData.a0;
	        		curveSeg.u.c.a1 = prev_arc.curveData.a1;
	        		curveSeg.u.c.center = prev_arc.curveData.curvePos;
	        		if (prev_arc.curveData.negative)
	        			curveSeg.u.c.radius = -prev_arc.curveData.curveRadius;
	        		else
	        			curveSeg.u.c.radius = prev_arc.curveData.curveRadius;
	        } else {											//Straight Line because all points co-linear
	        	curveSeg.type = track?SEG_STRTRK:SEG_STRLIN;
	        	if (track)
	        		curveSeg.color = wDrawColorBlack;
	        	else
	        		curveSeg.color = color;
	        	curveSeg.u.l.angle = FindAngle(prev_arc.pos0,prev_arc.pos1);
	        	curveSeg.u.l.pos[0] = prev_arc.pos0;
	        	curveSeg.u.l.pos[1] = prev_arc.pos1;
	        	curveSeg.u.l.option = 0;
	        }
	        addSegBezier(segs, &curveSeg);		//Add to array of segs used
	        t_s = prev_e;
	        col++;
	      } while(prev_e < 1.0);

	      return TRUE;
};
/*
 * Draw Bezier while editing it. It consists of three elements - the curve and one or two control arms.
 *
 */

static void DrawBezCurve(trkSeg_p control_arm1,
					int cp1Segs_cnt,
					trkSeg_p control_arm2,
					int cp2Segs_cnt,
					trkSeg_p curveSegs,
					int crvSegs_cnt,
					wDrawColor color
					) {
	if (crvSegs_cnt && curveSegs)
		DrawSegs( &tempD, zero, 0.0, curveSegs, crvSegs_cnt, Da.trackGauge, color );
	if (cp1Segs_cnt && control_arm1)
		DrawSegs( &tempD, zero, 0.0, control_arm1, cp1Segs_cnt, Da.trackGauge, drawColorBlack );
	if (cp2Segs_cnt && control_arm2)
		DrawSegs( &tempD, zero, 0.0, control_arm2, cp2Segs_cnt, Da.trackGauge, drawColorBlack );

}

/*
 * Undraw the temp Bezier
 */

/*
 * If Track, make it red if the radius is below minimum
 */
void DrawTempBezier(BOOL_T track) {
  if (track) DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,fabs(Da.minRadius)<(GetLayoutMinTrackRadius()-EPSILON)?exceptionColor:normalColor);
  else
	DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,drawColorBlack); //Add Second Arm
}

void CreateBothControlArms(int selectPoint, BOOL_T track) {
	if (selectPoint == -1) {
		Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
				Da.pos[1], track, TRUE, Da.trk[0]!=NULL, -1,
				drawColorBlack);
		Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
				Da.pos[2], track, TRUE, Da.trk[1]!=NULL, -1,
				drawColorBlack);
	} else if (selectPoint == 0 || selectPoint == 1) {
		Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
				Da.pos[1], track, TRUE, Da.trk[0]!=NULL, selectPoint,
				drawColorBlack);
		Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
				Da.pos[2], track, FALSE, Da.trk[1]!=NULL, -1,
				drawColorBlack);
	} else {
		Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
				Da.pos[1], track, FALSE, Da.trk[0]!=NULL, -1,
				drawColorBlack);
		Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
				Da.pos[2], track, TRUE, Da.trk[1]!=NULL,
				3-selectPoint, drawColorBlack);
	}
}

void CreateMoveAnchor(coOrd pos,BOOL_T fill) {
	double d = tempD.scale*0.15;
	DYNARR_APPEND(trkSeg_t,anchors_da,1);
	int inx = anchors_da.cnt-1;
	anchors(inx).type = fill?SEG_FILCRCL:SEG_CRVLIN;
	anchors(inx).u.c.a0 = 0.0;
	anchors(inx).u.c.a1 = 360.0;
	anchors(inx).width = 0;
	anchors(inx).color = wDrawColorBlue;
	anchors(inx).u.c.radius = d/4;
	anchors(inx).u.c.center = pos;
}

/*
 * AdjustBezCurve
 *
 * Called to adjust the curve either when creating it or modifying it
 * States are "PICK_POINT" and "POINT_PICKED" and "TRACK_SELECTED".
 *
 * In PICK_POINT, the user can select an end-point to drag and release in POINT_PICKED. They can also
 * hit Enter (which saves the changes) or ESC (which cancels them).
 *
 * Only those points which can be picked are shown with circles - locked end-points are not shown.
 *
 * SHIFT at release will lock , re-locking any end-points that are aligned with like items at the same position
 * (Track to unconnected Track, Line to any Line end).
 *
 */
EXPORT STATUS_T AdjustBezCurve(
		wAction_t action,
		coOrd pos,
		BOOL_T track,
		wDrawColor color,
		DIST_T width,
		bezMessageProc message )
{
	track_p t;
	DIST_T d;
	ANGLE_T angle1, angle2;
	static coOrd pos0, pos3, p;
	enum BezierType b;
	DIST_T dd;
	EPINX_T ep;
	double fx, fy, cusp;
	int controlArm = -1;


	if (Da.state != PICK_POINT && Da.state != POINT_PICKED && Da.state != TRACK_SELECTED) return C_CONTINUE;

	switch ( action & 0xFF) {

	case C_START:
			Da.selectPoint = -1;
			CreateBothControlArms(Da.selectPoint, track);
			if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track,color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
			Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
			Da.unlocked = FALSE;
			if (track)
				InfoMessage( _("Select End-Point - Ctrl unlocks end-point") );
			else
				InfoMessage( _("Select End-Point") );
			return C_CONTINUE;

	case wActionMove:
		DYNARR_RESET(trkSeg_t,anchors_da);
		if (Da.state != PICK_POINT) return C_CONTINUE;
		if (Da.state != PICK_POINT) return C_CONTINUE;
		for (int i=0;i<4;i++) {
			if (i==0 && Da.trk[0]) continue;
			if (i==3 && Da.trk[1]) continue;   //ignore locked points
			d = FindDistance(Da.pos[i],pos);
			if (IsClose(d))	CreateMoveAnchor(Da.pos[i],TRUE);
		}
		break;

	case C_DOWN:
		if (Da.state != PICK_POINT) return C_CONTINUE;
		dd = DIST_INF;
		Da.selectPoint = -1;
		for (int i=0;i<4;i++) {
			d = FindDistance(Da.pos[i],pos);
			if (d < dd) {
				if (i==0 && Da.trk[0]) continue;
				if (i==3 && Da.trk[1]) continue;   //ignore locked points
				dd = d;
				Da.selectPoint = i;
			}

		}
		if (!IsClose(dd) )	Da.selectPoint = -1;
		DYNARR_RESET(trkSeg_t,anchors_da);
		if (Da.selectPoint == -1) {
			InfoMessage( _("Not close enough to any valid, selectable point, reselect") );
			return C_CONTINUE;
		} else {
			pos = Da.pos[Da.selectPoint];
			CreateMoveAnchor(pos,TRUE);
			Da.state = POINT_PICKED;
			InfoMessage( _("Drag point %d to new location and release it"),Da.selectPoint+1 );
		}
		CreateBothControlArms(Da.selectPoint, track);
		if (ConvertToArcs(Da.pos, &Da.crvSegs_da, track, color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
		Da.minRadius = BezierMinRadius(Da.pos, Da.crvSegs_da);
		return C_CONTINUE;

	case C_MOVE:
		if (Da.state != POINT_PICKED) {
			InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
			return C_CONTINUE;
		}
		DYNARR_RESET(trkSeg_t,anchors_da);
		//If locked, reset pos to be on line from other track
		if (Da.selectPoint == 1 || Da.selectPoint == 2) {  //CPs
			int controlArm = Da.selectPoint-1;			//Snap to direction of track
			if (Da.trk[controlArm]) {
				angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[controlArm], Da.ep[controlArm]));
				angle2 = NormalizeAngle(FindAngle(pos, Da.pos[Da.selectPoint==1?0:3])-angle1);
				if (angle2 > 90.0 && angle2 < 270.0)
					Translate( &pos, Da.pos[Da.selectPoint==1?0:3], angle1, -FindDistance( Da.pos[Da.selectPoint==1?0:3], pos )*cos(D2R(angle2)) );
				else pos = Da.pos[Da.selectPoint==1?0:3];
			} // Dont Snap control points
		} else SnapPos(&pos);
		Da.pos[Da.selectPoint] = pos;
		CreateMoveAnchor(pos,TRUE);
		CreateBothControlArms(Da.selectPoint, track);
		if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track, color, Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
		Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
		if (Da.track) {
			b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
			if (b==ENDS) {
				wBeep();
				InfoMessage(_("Bezier Curve Invalid has identical end points Change End Point"),b==CUSP?"Cusp":"Loop");
			} else if ( b == CUSP || b == LOOP) {
				wBeep();
				InfoMessage(_("Bezier Curve Invalid has %s Change End Point"),b==CUSP?"Cusp":"Loop");
			} else if ( b == COINCIDENT ) {
				wBeep();
				InfoMessage(_("Bezier Curve Invalid has three co-incident points"),b==CUSP?"Cusp":"Loop");
			} else if ( b == LINE ) {
				InfoMessage(_("Bezier is Straight Line"));
			} else
				InfoMessage( _("Bezier %s : Min Radius=%s Length=%s fx=%0.3f fy=%0.3f cusp=%0.3f"),track?"Track":"Line",
													FormatDistance(Da.minRadius>=100000?0:Da.minRadius),
													FormatDistance(BezierLength(Da.pos,Da.crvSegs_da)),fx,fy,cusp);
		} else
				InfoMessage( _("Bezier %s : Min Radius=%s Length=%s"),track?"Track":"Line",
									FormatDistance(Da.minRadius>=100000?0:Da.minRadius),
									FormatDistance(BezierLength(Da.pos,Da.crvSegs_da)));
		return C_CONTINUE;

	case C_UP:
		if (Da.state != POINT_PICKED) return C_CONTINUE;
		//Take last pos and decide if it should be snapped to a track because SHIFT is held (pos0 and pos3)
		ep = 0;
		BOOL_T found = FALSE;
		DYNARR_RESET(trkSeg_t,anchors_da);
		p = pos;
		if (track && (Da.selectPoint == 0 || Da.selectPoint == 3)) {  //EPs
			if ((MyGetKeyState() & WKEY_SHIFT) != 0) {   //Snap Track
				if ((t = OnTrackIgnore(&p, FALSE, TRUE, Da.selectTrack)) != NULL) { //Snap to endPoint
					ep = PickUnconnectedEndPointSilent(p, t);
					if (ep != -1) {
						Da.trk[Da.selectPoint/3] = t;
						Da.ep[Da.selectPoint/3] = ep;
						pos0 = Da.pos[(Da.selectPoint == 0)?1:2];
						pos = GetTrkEndPos(t, ep);
						found = TRUE;
					}
				} else {
					wBeep();
					InfoMessage(_("No unconnected End Point to lock to"));
				}
			}
		}
		if (found) {
			angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[Da.selectPoint/3], Da.ep[Da.selectPoint/3]));
			angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1);
			Translate(&Da.pos[Da.selectPoint==0?1:2], Da.pos[Da.selectPoint==0?0:3], angle1, FindDistance(Da.pos[Da.selectPoint==0?1:2],pos)*cos(D2R(angle2)));
		}

		Da.selectPoint = -1;
		CreateBothControlArms(Da.selectPoint,track);
		if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track,color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
		Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
		if (Da.track) {
			b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
			if (b==ENDS) {
				wBeep();
				InfoMessage(_("Bezier curve invalid has identical end points Change End Point"),b==CUSP?"Cusp":"Loop");
			} else if ( b == CUSP || b == LOOP) {
				wBeep();
				InfoMessage(_("Bezier curve invalid has %s Change End Point"),b==CUSP?"Cusp":"Loop");
			} else if ( b == COINCIDENT ) {
				wBeep();
				InfoMessage(_("Bezier curve invalid has three co-incident points"),b==CUSP?"Cusp":"Loop");
			} else if ( b == LINE) {
				InfoMessage(_("Bezier curve is straight line"));
			}
				InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
		} else
			InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
		Da.state = PICK_POINT;

		return C_CONTINUE;

	case C_OK:                            //C_OK is not called by Modify.
		if ( Da.state == PICK_POINT ) {
			char c = (unsigned char)(action >> 8);
			if (Da.track && Da.pos[0].x == Da.pos[3].x && Da.pos[0].y == Da.pos[3].y ) {
				wBeep();
				ErrorMessage(_("Invalid Bezier Track - end points are identical"));
				return C_CONTINUE;
			}
			if (Da.track) {
				b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
				if ( b == CUSP || b == LOOP ) {
					wBeep();
					ErrorMessage(_("Invalid Bezier Curve has a %s - Adjust"),b==CUSP?"Cusp":"Loop");
					return C_CONTINUE;
				} else if (b==COINCIDENT) {
					wBeep();
					ErrorMessage(_("Invalid Bezier Curve has three coincident points - Adjust"));
					return C_CONTINUE;
				} else if(b==ENDS) {
					ErrorMessage(_("Invalid Bezier Track - end points are identical"));
					return C_CONTINUE;
				}
			}
			Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
			UndoStart( _("Create Bezier"), "newBezier - CR" );
			if (Da.track) {
				t = NewBezierTrack( Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt);
				for (int i=0;i<2;i++)
							if (Da.trk[i] != NULL) ConnectAbuttingTracks(t,i,Da.trk[i],Da.ep[i]);
			}
			else t = NewBezierLine(Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt,color,width);
			UndoEnd();
			if (Da.crvSegs_da.ptr) MyFree(Da.crvSegs_da.ptr);
			DYNARR_RESET(trkSeg_t,anchors_da);
			Da.crvSegs_da.ptr = NULL;
			Da.crvSegs_da.cnt = 0;
			Da.crvSegs_da.max = 0;
			DrawNewTrack(t);
			Da.state = NONE;
			return C_TERMINATE;

		}
		return C_CONTINUE;

	case C_REDRAW:
		if (Da.state != NONE)
			DrawTempBezier(Da.track);
		if (anchors_da.cnt>0)
			DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack );
		return C_CONTINUE;

	default:
		return C_CONTINUE;
	}

	return C_CONTINUE;


}

/*
 * CmdBezModify
 *
 * Called from Modify Command - this function deals with the real (old) track and calls AdjustBezCurve to tune up the new one
 * Sequence is this -
 * - The C_START is called from CmdModify C_DOWN action if a track has being selected. The old track is hidden, the editable one is shown.
 * - C_MOVES will be ignored until a C_UP ends the track selection and moves the state to PICK_POINT,
 * - C_DOWN then hides the track and shows the Bezier handles version. Selects a point (if close enough and available) and the state moves to POINT_PICKED
 * - C_MOVE drags the point around modifying the curve
 * - C_UP puts the state back to PICK_POINT (pick another)
 * - C_OK (Enter/Space) creates the new track, deletes the old and shows the changed track.
 * - C_CANCEL (Esc) sets the state to NONE and reshows the original track unchanged.
 *
 * Note: Available points are shown - if a Bezier track is attached to its neighbor, only the control point on that side is selectable.
 * Any free end-point can be locked to a unconnected end point using SHIFT during drag.
 */
STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos, DIST_T trackG) {
	BOOL_T track = TRUE;
	double width = 1.0;
	long mode = 0;
	long cmd;

	struct extraDataBezier_t *xx = GET_EXTRA_DATA(trk, T_NOTRACK, extraDataBezier_t);
	cmd = VP2L(commandContext);
	Da.trackGauge = trackG;

	switch (action&0xFF) {
	case C_START:
		Da.state = NONE;
		DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
		Da.cp1Segs_da_cnt = 0;
		Da.cp2Segs_da_cnt = 0;
		Da.selectPoint = -1;
		Da.selectTrack = NULL;

		if (IsTrack(trk)) {
			Da.track = TRUE;
			Da.trk[0] = GetTrkEndTrk( trk, 0 );
			if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk);
			Da.trk[1] = GetTrkEndTrk( trk, 1 );
			if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk);
		}
		else Da.track = FALSE;

		Da.selectTrack = trk;

	    for (int i=0;i<4;i++) Da.pos[i] = xx->pos[i];              //Copy parms from old trk
		InfoMessage(_("%s picked - now select a Point"),track?"Track":"Line");
		Da.state = TRACK_SELECTED;
		DrawTrack(Da.selectTrack,&mainD,wDrawColorWhite);                    //Wipe out real track, draw replacement
		return AdjustBezCurve(C_START, pos, Da.track, xx->segsColor, xx->segsWidth, InfoMessage);

	case wActionMove:
		if (Da.state == NONE) return C_CONTINUE;
		return AdjustBezCurve(wActionMove, pos, Da.track, xx->segsColor, xx->segsWidth, InfoMessage);
	case C_DOWN:
		if (Da.state == TRACK_SELECTED) return C_CONTINUE;                   //Ignore until first up
		UndrawNewTrack( Da.selectTrack );
		return AdjustBezCurve(C_DOWN, pos, Da.track, xx->segsColor, xx->segsWidth, InfoMessage);


	case C_MOVE:
		if (Da.state == TRACK_SELECTED) return C_CONTINUE;                   //Ignore until first up and down
		return AdjustBezCurve(C_MOVE, pos, Da.track, xx->segsColor, xx->segsWidth, InfoMessage);

	case C_UP:
		if (Da.state == TRACK_SELECTED) {
			Da.state = PICK_POINT;                                           //First time up, next time pick a point
		}
		return AdjustBezCurve(C_UP, pos, Da.track, xx->segsColor, xx->segsWidth,  InfoMessage);					//Run Adjust

	case C_TEXT:
		if ((action>>8) != 32)
			return C_CONTINUE;
		/* no break */
	case C_OK:
		if (Da.state != PICK_POINT) {										//Too early - abandon
			InfoMessage(_("No changes made"));
			Da.state = NONE;
			return C_CANCEL;
		}
		UndoStart( _("Modify Bezier"), "newBezier - CR" );
		UndoModify( trk );

		Da.state = NONE;
		wDrawColor color = wDrawColorBlack;
		DIST_T width = 0;
		if ( !Da.track ) {
			color = xx->segsColor;
			width = xx->segsWidth;
		}
		SetBezierData( trk, Da.pos, xx->segsColor, xx->segsWidth );

		DrawNewTrack( trk );
		UndoEnd();
		InfoMessage(_("Modify Bezier Complete"));
		return C_TERMINATE;

	case C_CANCEL:
		InfoMessage(_("Modify Bezier Cancelled"));
		Da.state = NONE;
		return C_TERMINATE;

	case C_REDRAW:
		return AdjustBezCurve(C_REDRAW, pos, Da.track, xx->segsColor, xx->segsWidth, InfoMessage);
	}

	return C_CONTINUE;

}

/*
 * Find length by adding up the underlying segments. The segments can be straights, curves or bezier.
 */
DIST_T BezierLength(coOrd pos[4],dynArr_t segs) {

	DIST_T dd = 0.0;
	if (segs.cnt == 0 ) return dd;
	for (int i = 0;i<segs.cnt;i++) {
		trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
		if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
			dd += fabs(t.u.c.radius*D2R(t.u.c.a1));
		} else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
			dd +=BezierLength(t.u.b.pos,t.bezSegs);
		} else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) {
			dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]);
		}
	}
	return dd;
}

DIST_T BezierOffsetLength(dynArr_t segs, double offset) {
	DIST_T dd = 0.0;
	if (segs.cnt == 0 ) return dd;
	for (int i = 0;i<segs.cnt;i++) {
		trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
		if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
			dd += fabs((t.u.c.radius+(t.u.c.radius>0?offset:-offset))*D2R(t.u.c.a1));
		} else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
			dd +=BezierOffsetLength(t.bezSegs,offset);
		} else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) {
			dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]);
		}
	}
	return dd;
}


DIST_T BezierMinRadius(coOrd pos[4],dynArr_t segs) {
	DIST_T r = DIST_INF, rr;
	if (segs.cnt == 0 ) return r;
	for (int i = 0;i<segs.cnt;i++) {
		trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
		if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
			rr = fabs(t.u.c.radius);
		} else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
			rr = BezierMinRadius(t.u.b.pos, t.bezSegs);
		} else rr = DIST_INF;
		if (rr<r) r = rr;
	}
	return r;
}

static void CreateEndAnchor(coOrd p, wBool_t lock) {
	DIST_T d = tempD.scale*0.15;

	DYNARR_APPEND(trkSeg_t,anchors_da,1);
	int i = anchors_da.cnt-1;
	anchors(i).type = lock?SEG_FILCRCL:SEG_CRVLIN;
	anchors(i).color = wDrawColorBlue;
	anchors(i).u.c.center = p;
	anchors(i).u.c.radius = d/2;
	anchors(i).u.c.a0 = 0.0;
	anchors(i).u.c.a1 = 360.0;
	anchors(i).width = 0;
}

/*
 * Create a Bezier Curve (Track or Line)
 * Sequence is
 * 1. Place 1st End (snapping if needed)
 * 2.Drag out 1st Control Arm (in one direction if snapped)
 * 3.Place 2nd End (again snapping)
 * 4.Drag out 2nd Control Arm (constrained if snapped)
 * 5 to n. Select and drag around points until done
 * n+1. Confirm with enter or Cancel with Esc
 */
STATUS_T CmdBezCurve( wAction_t action, coOrd pos )
{
	track_p t;
	static int segCnt;
	static BOOL_T lock;
	static coOrd movePos;
	STATUS_T rc = C_CONTINUE;
	long curveMode = 0;
	long cmd;
	if (action>>8) {
		cmd = action>>8;
	} else cmd = VP2L(commandContext);

	Da.width = (double)lineWidth/mainD.dpi;

	Da.trackGauge = trackGauge;

	switch (action&0xFF) {

	case C_START:

		Da.track = (cmd == bezCmdModifyTrack || cmd == bezCmdCreateTrack)?TRUE:FALSE;
		if (Da.track )
			Da.color = wDrawColorBlack;
		else
			Da.color = lineColor;

		Da.state = POS_1;
		Da. selectPoint = -1;
		for (int i=0;i<4;i++) {
			Da.pos[i] = zero;
		}
		Da.trk[0] = Da.trk[1] = NULL;
		//tempD.orig = mainD.orig;

		DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
		Da.cp1Segs_da_cnt = 0;
		Da.cp2Segs_da_cnt = 0;
		InfoMessage( _("Place 1st endpoint of Bezier - snap to %s"), Da.track?"unconnected Track":"line" );
		return C_CONTINUE;


	case C_DOWN:
		DYNARR_RESET(trkSeg_t,anchors_da);
		if ( Da.state == POS_1 || Da.state == POS_2) {   //Set the first or third point
			coOrd p = pos;
			BOOL_T found = FALSE;
			int end = Da.state==POS_1?0:1;
			EPINX_T ep;
			if (Da.track) {
				if (lock) {
					pos = movePos;
					if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) {
						ep = PickUnconnectedEndPointSilent(p, t);
						if (ep != -1) {
							if (GetTrkGauge(t) != GetScaleTrackGauge(GetLayoutCurScale())) {
								wBeep();
								InfoMessage(_("Track is different gauge"));
								ep = -1;
								t = NULL;
							} else {
								Da.trk[end] = t;
								Da.ep[end] = ep;
								pos = GetTrkEndPos(t, ep);
								found = TRUE;
							}
						}
					}
				}
			} else {													//Snap Bez Line to Lines
				if (lock)
					pos = movePos;
			}
			if (Da.state == POS_1) {
				Da.pos[0] = pos;
				Da.pos[1] = pos;
				Da.state = CONTROL_ARM_1;  //Draw the first control arm
				Da.selectPoint = 1;
				InfoMessage( _("Drag end of first control arm") );
				Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack);
            } else {
				Da.pos[3] = pos;  //2nd End Point
				Da.pos[2] = pos;  //2nd Ctl Point
				Da.state = POINT_PICKED; // Drag out the second control arm
				Da.selectPoint = 2;
				InfoMessage( _("Drag end of second control arm") );
				Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,FALSE,Da.trk[0]!=NULL,-1,wDrawColorBlack);
				Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3], Da.pos[2], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack);
				if (ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
			}
			return C_CONTINUE;
		} else  {
			return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
		}
		return C_CONTINUE;

	case wActionMove:
		DYNARR_RESET(trkSeg_t,anchors_da);
		lock = FALSE;
		if ( Da.state != POS_1 && Da.state != POS_2) return C_CONTINUE;  //Don't snap CPs
		if (Da.track)  {
			if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap) {
				if ((t = OnTrack(&pos, FALSE, TRUE)) != NULL) {
					EPINX_T ep = PickUnconnectedEndPointSilent(pos, t);
					if (ep != -1) {
						lock = TRUE;
						movePos = pos;
						if (GetTrkGauge(t) == GetScaleTrackGauge(GetLayoutCurScale())) {
							pos = GetTrkEndPos(t, ep);
							CreateEndAnchor(pos,FALSE);
						}
					}
				}
			}
		} else {
			if (((MyGetKeyState() & WKEY_ALT) == 0) == magneticSnap) {
				if ((t = OnTrack(&pos,FALSE, FALSE)) != NULL) {
					CreateEndAnchor(pos,TRUE);
					lock = TRUE;
					movePos = pos;
				}
			}
		}
		if (!lock && SnapPos(&pos)) {
			CreateEndAnchor(pos,TRUE);
			lock = TRUE;
			movePos = pos;
		}
		if (anchors_da.cnt)	return C_CONTINUE;
		/* no break */
	case C_MOVE:
		if (Da.state == POS_1) {
			InfoMessage( _("Place 1st endpoint of Bezier - snap to %s"), Da.track?"unconnected track":"line" );
			return C_CONTINUE;
		}
		if (Da.state == POS_2) {
			InfoMessage( _("Select other end of Bezier - snap to %s end"), Da.track?"unconnected track":"line" );
		}
		if (Da.state == CONTROL_ARM_1 ) {
			if (Da.trk[0]) {
				EPINX_T ep = 0;
				ANGLE_T angle1,angle2;
				angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[0],Da.ep[0]));
				angle2 = NormalizeAngle(FindAngle(pos, Da.pos[0])-angle1);
				if (angle2 > 90.0 && angle2 < 270.0)
					Translate( &pos, Da.pos[0], angle1, -FindDistance( Da.pos[0], pos )*cos(D2R(angle2)));
				else pos = Da.pos[0];
			} // Don't Snap control points
			Da.pos[1] = pos;
			Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, TRUE, Da.trk[0]!=NULL, 1, wDrawColorBlack);
		} else {
			return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
		}
		return C_CONTINUE;

	case C_UP:
		if (Da.state == CONTROL_ARM_1) {
			if (Da.trk[0]) {
				EPINX_T ep = Da.ep[0];
				ANGLE_T angle1,angle2;
				angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[0],Da.ep[0]));
				angle2 = NormalizeAngle(FindAngle(pos, Da.pos[0])-angle1);
				if (angle2 > 90.0 && angle2 < 270.0)
					Translate( &pos, Da.pos[0], angle1, -FindDistance( Da.pos[0], pos )*cos(D2R(angle2)));
				else pos = Da.pos[0];
			} // Don't Snap control points
			Da.pos[1] = pos;
			if (FindDistance(Da.pos[0],Da.pos[1]) <=minLength) {
				InfoMessage( _("Control Arm 1 is too short, try again") );
				Da.state = POS_1;
				return C_CONTINUE;
			}
			Da.state = POS_2;
			InfoMessage( _("Select other end of Bezier - snap to %s end"), Da.track?"Unconnected Track":"Line" );
			Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, FALSE, Da.trk[0]!=NULL, -1, wDrawColorBlack);
			return C_CONTINUE;
		} else {
			return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
		}
	case C_TEXT:
			if (Da.state != PICK_POINT || (action>>8) != ' ')  //Space is same as Enter.
				return C_CONTINUE;
			/* no break */
    case C_OK:
    	if (Da.state != PICK_POINT) return C_CONTINUE;
        return AdjustBezCurve( C_OK, pos, Da.track, Da.color, Da.width, InfoMessage);

	case C_REDRAW:
		if ( Da.state != NONE ) {
			DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, Da.color);
		}
		if (anchors_da.cnt)
			DrawSegs( &tempD, zero, 0.0, &anchors(0), anchors_da.cnt, trackGauge, wDrawColorBlack );
		return C_CONTINUE;

	case C_CANCEL:
		if (Da.state != NONE) {
			Da.cp1Segs_da_cnt = 0;
			Da.cp2Segs_da_cnt = 0;
			Da.crvSegs_da_cnt = 0;
			for (int i=0;i<2;i++) {
				Da.trk[i] = NULL;
				Da.ep[i] = -1;
			}
			if (Da.crvSegs_da.ptr) MyFree(Da.crvSegs_da.ptr);
			Da.crvSegs_da.ptr = NULL;
			Da.crvSegs_da.cnt = 0;
			Da.crvSegs_da.max = 0;
		}
		Da.state = NONE;
		return C_CONTINUE;
		
	default:

	return C_CONTINUE;
	}

}

void UpdateParms(wDrawColor color,long width) {
	Da.color = lineColor;
	Da.width = (double)lineWidth/mainD.dpi;
	if (Da.crvSegs_da.cnt) {
		ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width);
	}
	DrawTempBezier(Da.track);

}


#include "bitmaps/bezier-track.xpm"
#include "bitmaps/bezier-line.xpm"

EXPORT void InitCmdBezier( wMenu_p menu )
{	

}
