diff options
Diffstat (limited to 'app/bin/tcornu.c')
-rw-r--r-- | app/bin/tcornu.c | 1321 |
1 files changed, 1321 insertions, 0 deletions
diff --git a/app/bin/tcornu.c b/app/bin/tcornu.c new file mode 100644 index 0000000..9d9587a --- /dev/null +++ b/app/bin/tcornu.c @@ -0,0 +1,1321 @@ +/** \file tcornu.c + * + * CORNU SPIRAL TRACK + * + * A Cornu is a spiral arc defined by a polynomial that has the property + * that curvature varies linearly with distance along the curve. It is a family + * of curves that include Euler spirals. + * + * In order to be useful in XtrkCAD it is defined as a set of Bezier curves each of + * which is defined as a set of circular arcs and lines. + * + * The derivation of the Beziers is done by the Cornu library which must be recalled + * whenever a change is made in the end conditions. + * + * A cornu has a minimum radius and a maximum rate of change of radius. + * + * Acknowledgment is given to Dr. Raph Levien whose seminal PhD work on Cornu curves and + * generous open-sourcing of his libraries both inspired and powers this function. + * + * + * XTrkCad - Model Railroad CAD + * + * 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 "track.h" +#include "draw.h" +#include "cbezier.h" +#include "tbezier.h" +#include "tcornu.h" +#include "ccornu.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "utility.h" +#include "common.h" +#include "i18n.h" +#include "param.h" +#include "math.h" +#include "string.h" +#include "cundo.h" +#include "layout.h" +#include "fileio.h" +#include "assert.h" + +EXPORT TRKTYP_T T_CORNU = -1; + +struct extraData { + cornuData_t cornuData; + }; + +static int log_cornu = 0; + +static DIST_T GetLengthCornu( track_p ); + +/**************************************** + * + * UTILITIES + * + */ + +/* + * Run after any changes to the Cornu points + */ +void SetUpCornuParmFromTracks(track_p trk[2],cornuParm_t * cp, struct extraData* xx) { + if (!trk[0]) { + cp->center[0] = xx->cornuData.c[0]; + cp->angle[0] = xx->cornuData.a[0]; + cp->radius[0] = xx->cornuData.r[0]; + } + if (!trk[1]) { + cp->center[1] = xx->cornuData.c[1]; + cp->angle[1] = xx->cornuData.a[1]; + cp->radius[1] = xx->cornuData.r[1]; + } +} + +EXPORT BOOL_T FixUpCornu(coOrd pos[2], track_p trk[2], EPINX_T ep[2], struct extraData* xx) { + + cornuParm_t cp; + + SetUpCornuParmFromTracks(trk,&cp,xx); + + if (!CallCornu(pos, trk, ep, &xx->cornuData.arcSegs, &cp)) return FALSE; + + xx->cornuData.r[0] = cp.radius[0]; + if (cp.radius[0]==0) { + xx->cornuData.a[0] = cp.angle[0]; + } else { + xx->cornuData.c[0] = cp.center[0]; + } + xx->cornuData.r[1] = cp.radius[1]; + if (cp.radius[1]==0) { + xx->cornuData.a[1] = cp.angle[1]; + } else { + xx->cornuData.c[1] = cp.center[1]; + } + + xx->cornuData.minCurveRadius = CornuMinRadius(pos,xx->cornuData.arcSegs); + xx->cornuData.windingAngle = CornuTotalWindingArc(pos,xx->cornuData.arcSegs); + DIST_T last_c; + if (xx->cornuData.r[0] == 0) last_c = 0; + else last_c = 1/xx->cornuData.r[0]; + xx->cornuData.maxRateofChange = CornuMaxRateofChangeofCurvature(pos,xx->cornuData.arcSegs,&last_c); + xx->cornuData.length = CornuLength(pos, xx->cornuData.arcSegs); + return TRUE; +} + +EXPORT BOOL_T FixUpCornu0(coOrd pos[2],coOrd center[2],ANGLE_T angle[2],DIST_T radius[2],struct extraData* xx) { + DIST_T last_c; + if (!CallCornu0(pos, center, angle, radius,&xx->cornuData.arcSegs,FALSE)) return FALSE; + xx->cornuData.minCurveRadius = CornuMinRadius(pos, + xx->cornuData.arcSegs); + if (xx->cornuData.r[0] == 0) last_c = 0; + else last_c = 1/xx->cornuData.r[0]; + xx->cornuData.maxRateofChange = CornuMaxRateofChangeofCurvature(pos,xx->cornuData.arcSegs,&last_c); + xx->cornuData.length = CornuLength(pos, xx->cornuData.arcSegs); + xx->cornuData.windingAngle = CornuTotalWindingArc(pos,xx->cornuData.arcSegs); + return TRUE; +} + +EXPORT char * CreateSegPathList(track_p trk) { + char * cp = "\0\0"; + if (GetTrkType(trk) != T_CORNU) return cp; + struct extraData *xx = GetTrkExtraData(trk); + if (xx->cornuData.cornuPath) free(xx->cornuData.cornuPath); + xx->cornuData.cornuPath = malloc(xx->cornuData.arcSegs.cnt+2); + int j= 0; + for (int i = 0;i<xx->cornuData.arcSegs.cnt;i++,j++) { + xx->cornuData.cornuPath[j] = i+1; + } + xx->cornuData.cornuPath[j] = cp[0]; + xx->cornuData.cornuPath[j+1] = cp[0]; + return xx->cornuData.cornuPath; +} + + +static void GetCornuAngles( ANGLE_T *a0, ANGLE_T *a1, track_p trk ) +{ + assert( trk != NULL ); + + *a0 = NormalizeAngle( GetTrkEndAngle(trk,0) ); + *a1 = NormalizeAngle( GetTrkEndAngle(trk,1) ); + + LOG( log_cornu, 4, ( "getCornuAngles: = %0.3f %0.3f\n", *a0, *a1 ) ) +} + + +static void ComputeCornuBoundingBox( track_p trk, struct extraData * xx ) +{ + coOrd orig, size; + + GetSegBounds(zero,0,xx->cornuData.arcSegs.cnt,xx->cornuData.arcSegs.ptr, &orig, &size); + + coOrd hi, lo; + + lo.x = orig.x; + lo.y = orig.y; + hi.x = orig.x+size.x; + hi.y = orig.y+size.y; + + SetBoundingBox( trk, hi, lo ); +} + + +DIST_T CornuDescriptionDistance( + coOrd pos, + track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + coOrd p1; + + if ( GetTrkType( trk ) != T_CORNU || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 ) + return 100000; + + p1.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x-xx->cornuData.pos[0].x)/2) + xx->cornuData.descriptionOff.x; + p1.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y-xx->cornuData.pos[0].y)/2) + xx->cornuData.descriptionOff.y; + + return FindDistance( p1, pos ); +} + + +static void DrawCornuDescription( + track_p trk, + drawCmd_p d, + wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(trk); + wFont_p fp; + coOrd pos; + + if (layoutLabels == 0) + return; + if ((labelEnable&LABELENABLE_TRKDESC)==0) + return; + pos.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x - xx->cornuData.pos[0].x)/2); + pos.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y - xx->cornuData.pos[0].y)/2); + pos.x += xx->cornuData.descriptionOff.x; + pos.y += xx->cornuData.descriptionOff.y; + fp = wStandardFont( F_TIMES, FALSE, FALSE ); + sprintf( message, _("Cornu Curve: length=%0.3f min radius=%0.3f"), + xx->cornuData.length, xx->cornuData.minCurveRadius); + DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 ); +} + + +STATUS_T CornuDescriptionMove( + track_p trk, + wAction_t action, + coOrd pos ) +{ + struct extraData *xx = GetTrkExtraData(trk); + static coOrd p0,p1; + static BOOL_T editState; + wDrawColor color; + + if (GetTrkType(trk) != T_CORNU) return C_TERMINATE; + + p0.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x - xx->cornuData.pos[0].x)/2); + p0.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y - xx->cornuData.pos[0].y)/2); + + switch (action) { + case C_DOWN: + case C_MOVE: + case C_UP: + editState = TRUE; + p1 = pos; + color = GetTrkColor( trk, &mainD ); + xx->cornuData.descriptionOff.x = pos.x - p0.x; + xx->cornuData.descriptionOff.y = pos.y - p0.y; + DrawCornuDescription( trk, &mainD, color ); + if (action == C_UP) { + editState = FALSE; + } + MainRedraw(); + MapRedraw(); + return action==C_UP?C_TERMINATE:C_CONTINUE; + + case C_REDRAW: + if (editState) + DrawLine( &mainD, p1, p0, 0, wDrawColorBlack ); + break; + + } + return C_CONTINUE; +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static struct { + coOrd pos[2]; + ANGLE_T angle[2]; + DIST_T radius[2]; + coOrd center[2]; + FLOAT_T elev[2]; + FLOAT_T length; + FLOAT_T grade; + DIST_T minRadius; + DIST_T maxRateOfChange; + ANGLE_T windingAngle; + unsigned int layerNumber; + dynArr_t segs; + long width; + wDrawColor color; + } cornData; + +typedef enum { P0, A0, R0, C0, Z0, P1, A1, R1, C1, Z1, RA, RR, WA, LN, GR, LY } cornuDesc_e; +static descData_t cornuDesc[] = { +/*P0*/ { DESC_POS, N_("End Pt 1: X,Y"), &cornData.pos[0] }, +/*A0*/ { DESC_ANGLE, N_("End Angle"), &cornData.angle[0] }, +/*R0*/ { DESC_DIM, N_("Radius "), &cornData.radius[0] }, +/*C0*/ { DESC_POS, N_("Center X,Y"), &cornData.center[0] }, +/*Z0*/ { DESC_DIM, N_("Z1"), &cornData.elev[0] }, +/*P1*/ { DESC_POS, N_("End Pt 2: X,Y"), &cornData.pos[1] }, +/*A1*/ { DESC_ANGLE, N_("End Angle"), &cornData.angle[1] }, +/*R1*/ { DESC_DIM, N_("Radius"), &cornData.radius[1] }, +/*C1*/ { DESC_POS, N_("Center X,Y"), &cornData.center[1] }, +/*Z1*/ { DESC_DIM, N_("Z2"), &cornData.elev[1] }, +/*RA*/ { DESC_DIM, N_("Minimum Radius"), &cornData.minRadius }, +/*RR*/ { DESC_DIM, N_("Maximum Rate Of Change Of Curvature"), &cornData.maxRateOfChange }, +/*WA*/ { DESC_ANGLE, N_("Total Winding Angle"), &cornData.windingAngle }, +/*LN*/ { DESC_DIM, N_("Length"), &cornData.length }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &cornData.grade }, +/*LY*/ { DESC_LAYER, N_("Layer"), &cornData.layerNumber }, + { DESC_NULL } }; + + +static void UpdateCornu( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + struct extraData *xx = GetTrkExtraData(trk); + BOOL_T updateEndPts; + EPINX_T ep; + + cornuParm_t cp; + + + if ( inx == -1 ) + return; + updateEndPts = FALSE; + switch ( inx ) { + case P0: + if (GetTrkEndTrk(trk,0)) break; + updateEndPts = TRUE; + xx->cornuData.pos[0] = cornData.pos[0]; + cornuDesc[P0].mode |= DESC_CHANGE; + /* no break */ + case P1: + if (GetTrkEndTrk(trk,1)) break; + updateEndPts = TRUE; + xx->cornuData.pos[1]= cornData.pos[1]; + cornuDesc[P1].mode |= DESC_CHANGE; + break; + case A0: + if (GetTrkEndTrk(trk,0)) break; + updateEndPts = TRUE; + xx->cornuData.a[0] = cornData.angle[0]; + cornuDesc[A0].mode |= DESC_CHANGE; + break; + case A1: + if (GetTrkEndTrk(trk,1)) break; + updateEndPts = TRUE; + xx->cornuData.a[1]= cornData.angle[1]; + cornuDesc[A1].mode |= DESC_CHANGE; + break; + case C0: + if (GetTrkEndTrk(trk,0)) break; + updateEndPts = TRUE; + xx->cornuData.c[0] = cornData.center[0]; + cornuDesc[C0].mode |= DESC_CHANGE; + break; + case C1: + if (GetTrkEndTrk(trk,1)) break; + updateEndPts = TRUE; + xx->cornuData.c[1] = cornData.center[1]; + cornuDesc[C1].mode |= DESC_CHANGE; + break; + case R0: + if (GetTrkEndTrk(trk,0)) break; + updateEndPts = TRUE; + xx->cornuData.r[0] = cornData.radius[0]; + cornuDesc[R0].mode |= DESC_CHANGE; + break; + case R1: + if (GetTrkEndTrk(trk,1)) break; + updateEndPts = TRUE; + xx->cornuData.r[1]= cornData.radius[1]; + cornuDesc[R1].mode |= DESC_CHANGE; + break; + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), cornData.elev[ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &cornData.elev[1-ep], NULL ); + if ( cornData.length > minLength ) + cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0; + else + cornData.grade = 0.0; + cornuDesc[GR].mode |= DESC_CHANGE; + cornuDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + return; + case LY: + SetTrkLayer( trk, cornData.layerNumber); + break; + default: + AbortProg( "updateCornu: Bad inx %d", inx ); + } + track_p tracks[2]; + tracks[0] = GetTrkEndTrk(trk,0); + tracks[1] = GetTrkEndTrk(trk,1); + + if (updateEndPts) { + if ( GetTrkEndTrk(trk,0) == NULL ) { + SetTrkEndPoint( trk, 0, cornData.pos[0], xx->cornuData.a[0]); + cornuDesc[A0].mode |= DESC_CHANGE; + } + if ( GetTrkEndTrk(trk,1) == NULL ) { + SetTrkEndPoint( trk, 1, cornData.pos[1], xx->cornuData.a[1]); + cornuDesc[A1].mode |= DESC_CHANGE; + } + } + + EPINX_T new_ep[2]; + track_p ts[2]; + ts[0] = GetTrkEndTrk(trk,0); + ts[1] = GetTrkEndTrk(trk,1); + SetUpCornuParmFromTracks(ts,&cp,xx); + CallCornu(xx->cornuData.pos, tracks, new_ep, &xx->cornuData.arcSegs, &cp); + + + //FixUpCornu(xx->bezierData.pos, xx, IsTrack(trk)); + ComputeCornuBoundingBox(trk, xx); + DrawNewTrack( trk ); +} + + +static void DescribeCornu( track_p trk, char * str, CSIZE_T len ) +{ + struct extraData *xx = GetTrkExtraData(trk); + DIST_T d; + + d = xx->cornuData.length; + sprintf( str, _("Cornu Track(%d): Layer=%u MinRadius=%s Length=%s EP=[%0.3f,%0.3f] [%0.3f,%0.3f]"), + GetTrkIndex(trk), + GetTrkLayer(trk)+1, + FormatDistance(xx->cornuData.minCurveRadius), + FormatDistance(d), + PutDim(xx->cornuData.pos[0].x),PutDim(xx->cornuData.pos[0].y), + PutDim(xx->cornuData.pos[1].x),PutDim(xx->cornuData.pos[1].y) + ); + + cornData.length = xx->cornuData.length; + cornData.minRadius = xx->cornuData.minCurveRadius; + cornData.maxRateOfChange = xx->cornuData.maxRateofChange; + cornData.windingAngle = xx->cornuData.windingAngle; + cornData.layerNumber = GetTrkLayer(trk); + cornData.pos[0] = xx->cornuData.pos[0]; + cornData.pos[1] = xx->cornuData.pos[1]; + cornData.angle[0] = xx->cornuData.a[0]; + cornData.angle[1] = xx->cornuData.a[1]; + cornData.center[0] = xx->cornuData.c[0]; + cornData.center[1] = xx->cornuData.c[1]; + cornData.radius[0] = xx->cornuData.r[0]; + cornData.radius[1] = xx->cornuData.r[1]; + if (GetTrkType(trk) == T_CORNU) { + ComputeElev( trk, 0, FALSE, &cornData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &cornData.elev[1], NULL ); + + if ( cornData.length > minLength ) + cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0; + else + cornData.grade = 0.0; + } + BOOL_T trk0 = (GetTrkEndTrk(trk,0)!=NULL); + BOOL_T trk1 = (GetTrkEndTrk(trk,1)!=NULL); + + cornuDesc[P0].mode = !trk0?0:DESC_RO; + cornuDesc[P1].mode = !trk1?0:DESC_RO; + cornuDesc[LN].mode = DESC_RO; + cornuDesc[Z0].mode = EndPtIsDefinedElev(trk,0)?0:DESC_RO; + cornuDesc[Z1].mode = EndPtIsDefinedElev(trk,1)?0:DESC_RO; + + + cornuDesc[A0].mode = !trk0?0:DESC_RO; + cornuDesc[A1].mode = !trk1?0:DESC_RO; + cornuDesc[C0].mode = !trk0?0:DESC_RO; + cornuDesc[C1].mode = !trk1?0:DESC_RO; + cornuDesc[R0].mode = !trk0?0:DESC_RO; + cornuDesc[R1].mode = !trk1?0:DESC_RO; + cornuDesc[GR].mode = DESC_RO; + cornuDesc[RA].mode = DESC_RO; + cornuDesc[RR].mode = DESC_RO; + cornuDesc[WA].mode = DESC_RO; + cornuDesc[LY].mode = DESC_NOREDRAW; + + DoDescribe( _("Cornu Track"), trk, cornuDesc, UpdateCornu ); + + +} + + +static DIST_T DistanceCornu( track_p t, coOrd * p ) +{ + struct extraData *xx = GetTrkExtraData(t); + //return BezierMathDistance(p,xx->bezierData.pos,100, &s); + + DIST_T d = 100000.0; + coOrd p2 = xx->cornuData.pos[0]; //Set initial point + segProcData_t segProcData; + for (int i = 0;i<xx->cornuData.arcSegs.cnt;i++) { + trkSeg_t seg = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i); + if (seg.type == SEG_FILCRCL) continue; + segProcData.distance.pos1 = * p; + SegProc(SEGPROC_DISTANCE,&seg,&segProcData); + if (segProcData.distance.dd<d) { + d = segProcData.distance.dd; + p2 = segProcData.distance.pos1; + } + } + //d = BezierDistance( p, xx->bezierData.pos[0], xx->bezierData.pos[1], xx->bezierData.pos[2], xx->bezierData.pos[1], 100, NULL ); + * p = p2; + return d; +} + +static void DrawCornu( track_p t, drawCmd_p d, wDrawColor color ) +{ + struct extraData *xx = GetTrkExtraData(t); + long widthOptions = DTS_LEFT|DTS_RIGHT; + + if (GetTrkWidth(t) == 2) + widthOptions |= DTS_THICK2; + if (GetTrkWidth(t) == 3) + widthOptions |= DTS_THICK3; + + + if ( ((d->funcs->options&wDrawOptTemp)==0) && + (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) && + labelScale >= d->scale && + ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) { + DrawCornuDescription( t, d, color ); + } + DIST_T scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + if ( tieDrawMode!=TIEDRAWMODE_NONE && + d!=&mapD && + (d->options&DC_TIES)!=0 && + d->scale<scale2rail/2 ) + DrawSegsO(d,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions|DTS_TIES); + DrawSegsO(d,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions); + if ( (d->funcs->options & wDrawOptTemp) == 0 && + (d->options&DC_QUICK) == 0 ) { + DrawEndPt( d, t, 0, color ); + DrawEndPt( d, t, 1, color ); + } +} + +void FreeSubSegs(trkSeg_t* s) { + if (s->type == SEG_BEZTRK || s->type == SEG_BEZLIN) { + if (s->bezSegs.ptr) { + MyFree(s->bezSegs.ptr); + } + s->bezSegs.max = 0; + s->bezSegs.cnt = 0; + s->bezSegs.ptr = NULL; + } +} + +static void DeleteCornu( track_p t ) +{ + struct extraData *xx = GetTrkExtraData(t); + + for (int i=0;i<xx->cornuData.arcSegs.cnt;i++) { + trkSeg_t s = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i); + FreeSubSegs(&s); + } + if (xx->cornuData.arcSegs.ptr) + MyFree(xx->cornuData.arcSegs.ptr); + xx->cornuData.arcSegs.max = 0; + xx->cornuData.arcSegs.cnt = 0; + xx->cornuData.arcSegs.ptr = NULL; +} + +static BOOL_T WriteCornu( track_p t, FILE * f ) +{ + struct extraData *xx = GetTrkExtraData(t); + long options; + BOOL_T rc = TRUE; + BOOL_T track =(GetTrkType(t)==T_CORNU); + options = GetTrkWidth(t) & 0x0F; + if ( ( GetTrkBits(t) & TB_HIDEDESC ) == 0 ) options |= 0x80; + rc &= fprintf(f, "%s %d %d %ld 0 0 %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f \n", + "CORNU",GetTrkIndex(t), GetTrkLayer(t), (long)options, + GetTrkScaleName(t), GetTrkVisible(t), + xx->cornuData.pos[0].x, xx->cornuData.pos[0].y, + xx->cornuData.a[0], + xx->cornuData.r[0], + xx->cornuData.c[0].x,xx->cornuData.c[0].y, + xx->cornuData.pos[1].x, xx->cornuData.pos[1].y, + xx->cornuData.a[1], + xx->cornuData.r[1], + xx->cornuData.c[1].x,xx->cornuData.c[1].y )>0; + if (track) { + rc &= WriteEndPt( f, t, 0 ); + rc &= WriteEndPt( f, t, 1 ); + } + rc &= WriteSegs( f, xx->cornuData.arcSegs.cnt, xx->cornuData.arcSegs.ptr ); + //rc &= fprintf(f, "\tEND\n" )>0; + return rc; +} + +static void ReadCornu( char * line ) +{ + struct extraData *xx; + track_p t; + wIndex_t index; + BOOL_T visible; + DIST_T r0,r1; + ANGLE_T a0,a1; + coOrd p0, p1, c0, c1; + char scale[10]; + wIndex_t layer; + long options; + char * cp = NULL; + + if (!GetArgs( line+6, "dLl00sdpffppffp", + &index, &layer, &options, scale, &visible, &p0, &a0, &r0, &c0, &p1, &a1, &r1, &c1 ) ) { + return; + } + t = NewTrack( index, T_CORNU, 0, sizeof *xx ); + + xx = GetTrkExtraData(t); + SetTrkVisible(t, visible); + SetTrkScale(t, LookupScale(scale)); + SetTrkLayer(t, layer ); + SetTrkWidth(t, (int)(options&0x0F)); + if ( ( options & 0x80 ) == 0 ) SetTrkBits(t,TB_HIDEDESC); + xx->cornuData.pos[0] = p0; + xx->cornuData.pos[1] = p1; + xx->cornuData.a[0] = a0; + xx->cornuData.r[0] = r0; + xx->cornuData.a[1] = a1; + xx->cornuData.c[0] = c0; + xx->cornuData.c[1] = c1; + xx->cornuData.r[1] = r1; + xx->cornuData.descriptionOff.x = xx->cornuData.descriptionOff.y = 0.0; + ReadSegs(); + FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a, xx->cornuData.r, xx); + ComputeCornuBoundingBox(t,xx); + SetEndPts(t,2); +} + +static void MoveCornu( track_p trk, coOrd orig ) +{ + struct extraData *xx = GetTrkExtraData(trk); + UndoModify(trk); + for (int i=0;i<2;i++) { + xx->cornuData.pos[i].x += orig.x; + xx->cornuData.pos[i].y += orig.y; + xx->cornuData.c[i].x += orig.x; + xx->cornuData.c[i].y += orig.y; + } + RebuildCornu(trk); +} + +static void RotateCornu( track_p trk, coOrd orig, ANGLE_T angle ) +{ + struct extraData *xx = GetTrkExtraData(trk); + UndoModify(trk); + for (int i=0;i<2;i++) { + Rotate( &xx->cornuData.pos[i], orig, angle ); + Rotate( &xx->cornuData.c[i], orig, angle); + xx->cornuData.a[i] = NormalizeAngle(xx->cornuData.a[i]+angle); + } + RebuildCornu(trk); +} + +static void RescaleCornu( track_p trk, FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + UndoModify(trk); + for (int i=0;i<2;i++) { + xx->cornuData.pos[i].x *= ratio; + xx->cornuData.pos[i].y *= ratio; + } + RebuildCornu(trk); + +} + +EXPORT BOOL_T SetCornuEndPt(track_p trk, EPINX_T inx, coOrd pos, coOrd center, ANGLE_T angle, DIST_T radius) { + struct extraData *xx = GetTrkExtraData(trk); + + xx->cornuData.pos[inx] = pos; + xx->cornuData.c[inx] = center; + xx->cornuData.a[inx] = angle; + xx->cornuData.r[inx] = radius; + if (!RebuildCornu(trk)) return FALSE; + SetTrkEndPoint( trk, inx, xx->cornuData.pos[inx], xx->cornuData.a[inx]); + return TRUE; +} + + +/** + * Split the Track at approximately the point pos. + */ +static BOOL_T SplitCornu( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T * ep0, EPINX_T * ep1 ) +{ + struct extraData *xx = GetTrkExtraData(trk); + track_p trk1; + DIST_T radius = 0.0; + coOrd center; + int inx; + BOOL_T track; + track = IsTrack(trk); + + cornuParm_t new; + + double dd = DistanceCornu(trk, &pos); + if (dd>minLength) return FALSE; + BOOL_T back, neg; + + ANGLE_T angle = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + + trkSeg_p segPtr = &DYNARR_N(trkSeg_t, xx->cornuData.arcSegs, inx); + + GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg); + segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx); + + if (segPtr->type == SEG_STRTRK) { + radius = 0.0; + center = zero; + } else if (segPtr->type == SEG_CRVTRK) { + center = segPtr->u.c.center; + radius = fabs(segPtr->u.c.radius); + } + if (ep) { + new.pos[0] = pos; + new.pos[1] = xx->cornuData.pos[1]; + new.angle[0] = NormalizeAngle(angle+(neg==back?180:0)); + new.angle[1] = xx->cornuData.a[1]; + new.center[0] = center; + new.center[1] = xx->cornuData.c[1]; + new.radius[0] = radius; + new.radius[1] = xx->cornuData.r[1]; + } else { + new.pos[1] = pos; + new.pos[0] = xx->cornuData.pos[0]; + new.angle[1] = NormalizeAngle(angle+(neg==back?0:180)); + new.angle[0] = xx->cornuData.a[0]; + new.center[1] = center; + new.center[0] = xx->cornuData.c[0]; + new.radius[1] = radius; + new.radius[0] = xx->cornuData.r[0]; + } + + trk1 = NewCornuTrack(new.pos,new.center,new.angle,new.radius,NULL,0); + if (trk1==NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + new.pos[0].x,new.pos[0].y, + new.pos[1].x,new.pos[1].y, + new.center[0].x,new.center[0].y, + new.center[1].x,new.center[1].y, + new.angle[0],new.angle[1], + FormatDistance(new.radius[0]),FormatDistance(new.radius[1])); + UndoEnd(); + return FALSE; + } + + UndoModify(trk); + xx->cornuData.pos[ep] = pos; + xx->cornuData.a[ep] = NormalizeAngle(new.angle[1-ep]+180); + xx->cornuData.r[ep] = new.radius[1-ep]; + xx->cornuData.c[ep] = new.center[1-ep]; + + RebuildCornu(trk); + + SetTrkEndPoint(trk, ep, xx->cornuData.pos[ep], xx->cornuData.a[ep]); + + *leftover = trk1; + *ep0 = 1-ep; + *ep1 = ep; + + return TRUE; +} + +BOOL_T MoveCornuEndPt ( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) { + track_p trk2; + if (SplitTrack(*trk,pos,*ep,&trk2,TRUE)) { + struct extraData *xx = GetTrkExtraData(*trk); + if (trk2) DeleteTrack(trk2,TRUE); + SetTrkEndPoint( *trk, *ep, *ep?xx->cornuData.pos[1]:xx->cornuData.pos[0], *ep?xx->cornuData.a[1]:xx->cornuData.a[0] ); + return TRUE; + } + return FALSE; +} +static int log_traverseCornu = 0; +/* + * TraverseCornu is used to position a train/car. + * We find a new position and angle given a current pos, angle and a distance to travel. + * + * The output can be TRUE -> we have moved the point to a new point or to the start/end of the next track + * FALSE -> we have not found that point because pos was not on/near the track + * + * If true we supply the remaining distance to go (always positive). + * We detect the movement direction by comparing the current angle to the angle of the track at the point. + * + * The entire Cornu may be reversed or forwards depending on the way it was drawn. + * + * Each Bezier segment within that Cornu structure therefore may be processed forwards or in reverse. + * So for each segment we call traverse1 to get the direction and extra distance to go to get to the current point + * and then use that for traverse2 to actually move to the new point + * + * If we exceed the current point's segment we move on to the next until the end of this track or we have found the spot. + * + */ +static BOOL_T TraverseCornu( traverseTrack_p trvTrk, DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData *xx = GetTrkExtraData(trk); + DIST_T dist = *distR; + segProcData_t segProcData; + BOOL_T cornu_backwards= FALSE; + BOOL_T neg = FALSE; + DIST_T d = 10000; + coOrd pos1 = trvTrk->pos, pos2 = trvTrk->pos; + ANGLE_T a1,a2; + int inx, segInx = 0; + EPINX_T ep; + BOOL_T back; +LOG( log_traverseCornu, 1, ( "TravCornu-In [%0.3f %0.3f] A%0.3f D%0.3f \n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR )) + trkSeg_p segPtr = (trkSeg_p)xx->cornuData.arcSegs.ptr; + + a2 = GetAngleSegs( //Find correct Segment and nearest point in it + xx->cornuData.arcSegs.cnt,segPtr, + &pos2, &segInx, &d , &back , NULL, &neg); //d = how far pos2 from old pos2 = trvTrk->pos + + if ( d > 10 ) { + ErrorMessage( "traverseCornu: Position is not near track: %0.3f", d ); + return FALSE; //This means the input pos is not on or close to the track. + } + if (back) a2 = NormalizeAngle(a2+180); //If reverse segs - reverse angle + a1 = NormalizeAngle(a2-trvTrk->angle); //Establish if we are going fwds or backwards globally + if (a1<270 && a1>90) { //Must add 180 if the seg is reversed or inverted (but not both) + cornu_backwards = TRUE; + ep = 0; + } else { + cornu_backwards = FALSE; + ep = 1; + } + if (neg) { + cornu_backwards = !cornu_backwards; //Reversed direction + ep = 1-ep; + } + segProcData.traverse1.pos = pos2; //actual point on curve + segProcData.traverse1.angle = trvTrk->angle; //direction car is going for Traverse 1 +LOG( log_traverseCornu, 1, ( " TravCornu-GetSubA A%0.3f I%d N%d B%d CB%d\n", a2, segInx, neg, back, cornu_backwards )) + inx = segInx; + while (inx >=0 && inx<xx->cornuData.arcSegs.cnt) { + segPtr = (trkSeg_p)xx->cornuData.arcSegs.ptr+inx; //move in to the identified Bezier segment + SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData ); + BOOL_T backwards = segProcData.traverse1.backwards; //do we process this segment backwards? + BOOL_T reverse_seg = segProcData.traverse1.reverse_seg; //Info only + int BezSegInx = segProcData.traverse1.BezSegInx; //Which subSeg was it? + BOOL_T segs_backwards = segProcData.traverse1.segs_backwards; + + dist += segProcData.traverse1.dist; //Add in the part of the Bezier to get to pos + + segProcData.traverse2.dist = dist; //Set up Traverse2 + segProcData.traverse2.segDir = backwards; + segProcData.traverse2.BezSegInx = BezSegInx; + segProcData.traverse2.segs_backwards = segs_backwards; +LOG( log_traverseCornu, 2, ( " TravCornu-Tr1 SI%d D%0.3f B%d RS%d \n", BezSegInx, dist, backwards, reverse_seg ) ) + SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData ); //Angle at pos2 + if ( segProcData.traverse2.dist <= 0 ) { //-ve or zero distance left over so stop there + *distR = 0; + trvTrk->pos = segProcData.traverse2.pos; //Use finishing pos + trvTrk->angle = segProcData.traverse2.angle; //Use finishing angle +LOG( log_traverseCornu, 1, ( "TravCornu-Ex1 -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) ) + return TRUE; + } + dist = segProcData.traverse2.dist; //How far left? + coOrd pos = segProcData.traverse2.pos; //Will always be at a Bezseg end + ANGLE_T angle = segProcData.traverse2.angle; //Angle of end therefore + + segProcData.traverse1.angle = angle; //Set up Traverse1 + segProcData.traverse1.pos = pos; + inx = cornu_backwards?inx-1:inx+1; //Here's where the global segment direction comes in +LOG( log_traverseCornu, 2, ( " TravCornu-Loop D%0.3f A%0.3f I%d \n", dist, angle, inx ) ) + } + //Ran out of Bez-Segs so punt to next Track + *distR = dist; //Tell caller what dist is left + + trvTrk->pos = GetTrkEndPos(trk,ep); //Which end were we heading for? + trvTrk->angle = NormalizeAngle(GetTrkEndAngle(trk, ep)+(cornu_backwards?180:0)); + trvTrk->trk = GetTrkEndTrk(trk,ep); //go onto next track (or NULL) + + if (trvTrk->trk==NULL) { + trvTrk->pos = pos1; + return TRUE; + } +LOG( log_traverseCornu, 1, ( "TravCornu-Ex2 --> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) ) + return TRUE; + +} + + +static BOOL_T EnumerateCornu( track_p trk ) +{ + + if (trk != NULL) { + struct extraData *xx = GetTrkExtraData(trk); + DIST_T d; + d = xx->cornuData.length; + ScaleLengthIncrement( GetTrkScale(trk), d ); + } + return TRUE; +} + +static BOOL_T MergeCornu( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + struct extraData *xx0 = GetTrkExtraData(trk0); + struct extraData *xx1 = GetTrkExtraData(trk1); + track_p trk_after,trk_before; + EPINX_T ep_before,ep_after=-1; + coOrd p[2]; + coOrd c[2]; + ANGLE_T a[2]; + DIST_T r[2]; + + + if (!IsTrack(trk0) || !IsTrack(trk1) ) return FALSE; + if (GetTrkType(trk0) != GetTrkType(trk1)) return FALSE; + if (GetEndPtConnectedToMe(trk0,trk1) != ep0) return FALSE; + if (GetEndPtConnectedToMe(trk1,trk0) != ep1) return FALSE; + + if (ep0 == ep1) + return FALSE; + + UndoStart( _("Merge Cornu"), "MergeCornu( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + p[0] = xx0->cornuData.pos[0]; + p[1] = xx1->cornuData.pos[1]; + a[0] = xx0->cornuData.a[0]; + a[1] = xx1->cornuData.a[1]; + c[0] = xx0->cornuData.c[0]; + c[1] = xx1->cornuData.c[1]; + r[0] = xx0->cornuData.r[0]; + r[1] = xx1->cornuData.r[1]; + track_p trk3 = NewCornuTrack(p,c,a,r,NULL,0); + if (trk3==NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + p[0].x,p[0].y, + p[1].x,p[1].y, + c[0].x,c[0].y, + c[1].x,c[1].y, + a[0],a[1], + FormatDistance(r[0]),FormatDistance(r[1])); + UndoEnd(); + return FALSE; + } + + UndoModify( trk0 ); + UndoModify( trk1 ); + UndrawNewTrack( trk0 ); + UndrawNewTrack( trk1 ); + trk_after = GetTrkEndTrk( trk1, 1-ep1 ); + if (trk_after) { + ep_after = GetEndPtConnectedToMe( trk_after, trk1 ); + DisconnectTracks( trk1, 1-ep1, trk_after, ep_after ); + } + trk_before = GetTrkEndTrk( trk0, 1-ep0 ); + if (trk_before) { + ep_before = GetEndPtConnectedToMe( trk_before, trk0 ); + DisconnectTracks( trk0, 1-ep1, trk_before, ep_before ); + } + + DeleteTrack( trk1, TRUE ); + DeleteTrack( trk0, TRUE ); + if (trk_after) { + SetTrkEndPoint( trk_after, ep_after, xx0->cornuData.pos[1], NormalizeAngle(xx0->cornuData.a[1]+180)); + ConnectTracks( trk3, 1, trk_after, ep_after); + } + if (trk_before) { + SetTrkEndPoint( trk_before, ep_before, xx0->cornuData.pos[0], NormalizeAngle(xx0->cornuData.a[0]+180)); + ConnectTracks( trk3, 0, trk_before, ep_before); + } + DrawNewTrack( trk3 ); + UndoEnd(); + + + return TRUE; +} + +BOOL_T GetBezierSegmentsFromCornu(track_p trk, dynArr_t * segs) { + struct extraData * xx = GetTrkExtraData(trk); + for (int i=0;i<xx->cornuData.arcSegs.cnt;i++) { + DYNARR_APPEND(trkSeg_t, * segs, 10); + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,* segs,segs->cnt-1); + segPtr->type = SEG_BEZTRK; + segPtr->color = wDrawColorBlack; + segPtr->width = 0; + if (segPtr->bezSegs.ptr) MyFree(segPtr->bezSegs.ptr); + segPtr->bezSegs.cnt = 0; + segPtr->bezSegs.max = 0; + segPtr->bezSegs.ptr = NULL; + trkSeg_p p = (trkSeg_t *) xx->cornuData.arcSegs.ptr+i; + for (int j=0;j<4;j++) segPtr->u.b.pos[j] = p->u.b.pos[j]; + FixUpBezierSeg(segPtr->u.b.pos,segPtr,TRUE); + } + return TRUE; +} + +static DIST_T GetLengthCornu( track_p trk ) +{ + struct extraData *xx = GetTrkExtraData(trk); + DIST_T length = 0.0; + segProcData_t segProcData; + for(int i=0;i<xx->cornuData.arcSegs.cnt;i++) { + trkSeg_t seg = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i); + if (seg.type == SEG_FILCRCL) continue; + SegProc(SEGPROC_LENGTH, &seg, &segProcData); + length += segProcData.length.length; + } + return length; +} + + +static BOOL_T GetParamsCornu( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + int segInx, segInx2; + BOOL_T back, negative; + DIST_T d; + struct extraData *xx = GetTrkExtraData(trk); + params->type = curveTypeCornu; + params->track_angle = GetAngleSegs( //Find correct Segment and nearest point in it + xx->cornuData.arcSegs.cnt,xx->cornuData.arcSegs.ptr, + &pos, &segInx, &d , &back, &segInx2, &negative ); + trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,segInx); + if (negative != back) params->track_angle = NormalizeAngle(params->track_angle+180); //Cornu is in reverse + if (segPtr->type == SEG_STRTRK) { + params->arcR = 0.0; + } else if (segPtr->type == SEG_CRVTRK) { + params->arcR = fabs(segPtr->u.c.radius); + params->arcP = segPtr->u.c.center; + params->arcA0 = segPtr->u.c.a0; + params->arcA1 = segPtr->u.c.a1; + } else if (segPtr->type == SEG_BEZTRK) { + trkSeg_p segPtr2 = &DYNARR_N(trkSeg_t,segPtr->bezSegs,segInx2); + if (segPtr2->type == SEG_STRTRK) { + params->arcR = 0.0; + } else if (segPtr2->type == SEG_CRVTRK) { + params->arcR = fabs(segPtr2->u.c.radius); + params->arcP = segPtr2->u.c.center; + params->arcA0 = segPtr2->u.c.a0; + params->arcA1 = segPtr2->u.c.a1; + } + } + for (int i=0;i<2;i++) { + params->cornuEnd[i] = xx->cornuData.pos[i]; + params->cornuAngle[i] = xx->cornuData.a[i]; + params->cornuRadius[i] = xx->cornuData.r[i]; + params->cornuCenter[i] = xx->cornuData.c[i]; + } + params->len = xx->cornuData.length; + if ( inx == PARAMS_PARALLEL ) { + params->ep = 0; + } else if (inx == PARAMS_CORNU) { + params->ep = PickEndPoint( pos, trk); + } else { + params->ep = PickUnconnectedEndPointSilent( pos, trk ); + } + if (params->ep>=0) { + params->angle = GetTrkEndAngle(trk,params->ep); + } + + return TRUE; + +} + + + +static BOOL_T QueryCornu( track_p trk, int query ) +{ + struct extraData * xx = GetTrkExtraData(trk); + switch ( query ) { + case Q_CAN_GROUP: + return FALSE; + break; + case Q_FLIP_ENDPTS: + case Q_HAS_DESC: + return TRUE; + break; + case Q_EXCEPTION: + return xx->cornuData.minCurveRadius < (GetLayoutMinTrackRadius()-EPSILON); + break; + case Q_IS_CORNU: + return TRUE; + break; + case Q_ISTRACK: + return TRUE; + break; + case Q_CAN_PARALLEL: + return TRUE; + break; + // case Q_MODIFY_CANT_SPLIT: Remove Split Restriction + // case Q_CANNOT_BE_ON_END: Remove Restriction - Can have Cornu with no ends + case Q_CANNOT_PLACE_TURNOUT: + return TRUE; + break; + case Q_IGNORE_EASEMENT_ON_EXTEND: + return TRUE; + break; + case Q_MODIFY_CAN_SPLIT: + case Q_CAN_EXTEND: + return TRUE; + default: + return FALSE; + } +} + + +static void FlipCornu( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->cornuData.pos[0], orig, angle ); + FlipPoint( &xx->cornuData.pos[1], orig, angle ); + FlipPoint( &xx->cornuData.c[0], orig, angle); + FlipPoint( &xx->cornuData.c[1], orig, angle); + xx->cornuData.a[0] = NormalizeAngle( 2*angle - xx->cornuData.a[0] ); + xx->cornuData.a[1] = NormalizeAngle( 2*angle - xx->cornuData.a[1] ); + + RebuildCornu(trk); + +} + +static ANGLE_T GetAngleCornu( + track_p trk, + coOrd pos, + EPINX_T * ep0, + EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + ANGLE_T angle; + BOOL_T back, neg; + int indx; + angle = GetAngleSegs( xx->cornuData.arcSegs.cnt, (trkSeg_p)xx->cornuData.arcSegs.ptr, &pos, &indx, NULL, &back, NULL, &neg ); + if ( ep0 ) *ep0 = -1; + if ( ep1 ) *ep1 = -1; + return angle; +} + +BOOL_T GetCornuSegmentFromTrack(track_p trk, trkSeg_p seg_p) { + //TODO If we support Group + return TRUE; +} + + +static BOOL_T MakeParallelCornu( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + struct extraData * xx = GetTrkExtraData(trk); + coOrd np[4], p, nc[2]; + ANGLE_T atrk, diff_a, na[2]; + DIST_T nr[2]; + + + //Produce cornu that is translated parallel to the existing Cornu + // - not a precise result if the cornu end angles are not in the same general direction. + // The expectation is that the user will have to adjust it - unless and until we produce + // a new algo to adjust the control points to be parallel to the endpoints. + + p = pos; + DistanceCornu(trk, &p); //Find nearest point on curve + atrk = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&p,NULL,NULL,NULL,NULL, NULL); + diff_a = NormalizeAngle(FindAngle(pos,p)-atrk); //we know it will be +/-90... + //find parallel move x and y for points + BOOL_T above = FALSE; + if ( diff_a < 180 ) above = TRUE; //Above track + if (xx->cornuData.a[0] <180) above = !above; + Translate(&np[0],xx->cornuData.pos[0],xx->cornuData.a[0]+(above?90:-90),sep); + Translate(&np[1],xx->cornuData.pos[1],xx->cornuData.a[1]+(above?-90:90),sep); + na[0]=xx->cornuData.a[0]; + na[1]=xx->cornuData.a[1]; + if (xx->cornuData.r[0]) { + //Find angle between center and end angle of track + ANGLE_T ea0 = + NormalizeAngle(FindAngle(xx->cornuData.c[0],xx->cornuData.pos[0])-xx->cornuData.a[0]); + DIST_T sep0 = sep; + if (ea0>180) sep0 = -sep; + nr[0]=xx->cornuData.r[0]+(above?sep0:-sep0); //Needs adjustment + nc[0]=xx->cornuData.c[0]; + } else { + nr[0] = 0; + nc[0] = zero; + } + + if (xx->cornuData.r[1]) { + ANGLE_T ea1 = + NormalizeAngle(FindAngle(xx->cornuData.c[1],xx->cornuData.pos[1])-xx->cornuData.a[1]); + DIST_T sep1 = sep; + if (ea1<180) sep1 = -sep; + nr[1]=xx->cornuData.r[1]+(above?sep1:-sep1); //Needs adjustment + nc[1]=xx->cornuData.c[1]; + } else { + nr[1] = 0; + nc[1] = zero; + } + + if ( newTrkR ) { + *newTrkR = NewCornuTrack( np, nc, na, nr, NULL, 0); + if (*newTrkR==NULL) { + wBeep(); + InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"), + np[0].x,np[0].y, + np[1].x,np[1].y, + nc[0].x,nc[0].y, + nc[1].x,nc[1].y, + na[0],na[1], + FormatDistance(nr[0]),FormatDistance(nr[1])); + return FALSE; + } + + } else { + tempSegs_da.cnt = 0; + CallCornu0(np,nc,na,nr,&tempSegs_da,FALSE); + } + if ( p0R ) *p0R = np[0]; + if ( p1R ) *p1R = np[1]; + return TRUE; +} + +/* + * When an undo is run, the array of segs is missing - they are not saved to the Undo log. So Undo calls this routine to + * ensure + * - that the Segs are restored and + * - other fields reset. + */ +EXPORT BOOL_T RebuildCornu (track_p trk) +{ + struct extraData *xx; + xx = GetTrkExtraData(trk); + xx->cornuData.arcSegs.max = 0; + xx->cornuData.arcSegs.cnt = 0; + //if (xx->cornuData.arcSegs.ptr) MyFree(xx->cornuData.arcSegs.ptr); + xx->cornuData.arcSegs.ptr = NULL; + if (!FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a,xx->cornuData.r, xx)) return FALSE; + ComputeCornuBoundingBox(trk, xx); + return TRUE; +} + + +static trackCmd_t cornuCmds = { + "CORNU", + DrawCornu, + DistanceCornu, + DescribeCornu, + DeleteCornu, + WriteCornu, + ReadCornu, + MoveCornu, + RotateCornu, + RescaleCornu, + NULL, + GetAngleCornu, + SplitCornu, + TraverseCornu, + EnumerateCornu, + NULL, /* redraw */ + NULL, /* trim */ + MergeCornu, + NULL, /* modify */ + GetLengthCornu, + GetParamsCornu, + MoveCornuEndPt, /* Move EndPt */ + QueryCornu, + NULL, /* ungroup */ + FlipCornu, + NULL, + NULL, + NULL, + MakeParallelCornu, + NULL, + RebuildCornu + }; + + + + + +/**************************************** + * + * GRAPHICS COMMANDS + * + */ + + + + +track_p NewCornuTrack(coOrd pos[2], coOrd center[2],ANGLE_T angle[2], DIST_T radius[2], trkSeg_t * tempsegs, int count) +{ + struct extraData *xx; + track_p p; + p = NewTrack( 0, T_CORNU, 2, sizeof *xx ); + xx = GetTrkExtraData(p); + xx->cornuData.pos[0] = pos[0]; + xx->cornuData.pos[1] = pos[1]; + xx->cornuData.a[0] = angle[0]; + xx->cornuData.a[1] = angle[1]; + xx->cornuData.r[0] = radius[0]; + xx->cornuData.r[1] = radius[1]; + xx->cornuData.c[0] = center[0]; + xx->cornuData.c[1] = center[1]; + + if (!FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a,xx->cornuData.r, xx)) { + ErrorMessage("Create Cornu Failed"); + return NULL; + } +LOG( log_cornu, 1, ( "NewCornuTrack( EP1 %0.3f, %0.3f, EP2 %0.3f, %0.3f ) = %d\n", pos[0].x, pos[0].y, pos[1].x, pos[1].y, GetTrkIndex(p) ) ) + ComputeCornuBoundingBox( p, xx ); + SetTrkEndPoint( p, 0, pos[0], xx->cornuData.a[0]); + SetTrkEndPoint( p, 1, pos[1], xx->cornuData.a[1]); + CheckTrackLength( p ); + SetTrkBits( p, TB_HIDEDESC ); + return p; +} + + +EXPORT void InitTrkCornu( void ) +{ + T_CORNU = InitObject( &cornuCmds ); + log_cornu = LogFindIndex( "Cornu" ); + log_traverseCornu = LogFindIndex( "traverseCornu" ); +} + |