summaryrefslogtreecommitdiff
path: root/app/bin/cturnout.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin/cturnout.c')
-rw-r--r--app/bin/cturnout.c2626
1 files changed, 2626 insertions, 0 deletions
diff --git a/app/bin/cturnout.c b/app/bin/cturnout.c
new file mode 100644
index 0000000..55b7a4d
--- /dev/null
+++ b/app/bin/cturnout.c
@@ -0,0 +1,2626 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cturnout.c,v 1.8 2009-08-16 13:07:14 m_fischer Exp $
+ *
+ * T_TURNOUT
+ *
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * 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 <ctype.h>
+#include "track.h"
+#include "ccurve.h"
+#include "cstraigh.h"
+#include "compound.h"
+#include "cjoin.h"
+#include "i18n.h"
+
+#include <stdint.h>
+
+EXPORT TRKTYP_T T_TURNOUT = -1;
+
+#define TURNOUTCMD
+
+#define MIN_TURNOUT_SEG_CONNECT_DIST (0.1)
+
+EXPORT dynArr_t turnoutInfo_da;
+
+EXPORT turnoutInfo_t * curTurnout = NULL;
+EXPORT long curTurnoutEp = 0;
+
+static int log_turnout = 0;
+static int log_traverseTurnout = 0;
+
+static wMenu_p turnoutPopupM;
+
+#ifdef TURNOUTCMD
+static drawCmd_t turnoutD = {
+ NULL,
+ &screenDrawFuncs,
+ 0,
+ 1.0,
+ 0.0,
+ {0.0,0.0}, {0.0,0.0},
+ Pix2CoOrd, CoOrd2Pix };
+
+static wIndex_t turnoutHotBarCmdInx;
+static wIndex_t turnoutInx;
+static long hideTurnoutWindow;
+static void RedrawTurnout(void);
+static void SelTurnoutEndPt( wIndex_t, coOrd );
+
+static wPos_t turnoutListWidths[] = { 80, 80, 220 };
+static const char * turnoutListTitles[] = { N_("Manufacturer"), N_("Part No"), N_("Description") };
+static paramListData_t listData = { 13, 400, 3, turnoutListWidths, turnoutListTitles };
+static const char * hideLabels[] = { N_("Hide"), NULL };
+static paramDrawData_t turnoutDrawData = { 490, 200, (wDrawRedrawCallBack_p)RedrawTurnout, SelTurnoutEndPt, &turnoutD };
+static paramData_t turnoutPLs[] = {
+#define I_LIST (0)
+#define turnoutListL ((wList_p)turnoutPLs[I_LIST].control)
+ { PD_LIST, &turnoutInx, "list", PDO_NOPREF|PDO_DLGRESIZEW, &listData, NULL, BL_DUP },
+#define I_DRAW (1)
+#define turnoutDrawD ((wDraw_p)turnoutPLs[I_DRAW].control)
+ { PD_DRAW, NULL, "canvas", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_DLGRESIZE, &turnoutDrawData, NULL, 0 },
+#define I_NEW (2)
+#define turnoutNewM ((wMenu_p)turnoutPLs[I_NEW].control)
+ { PD_MENU, NULL, "new", PDO_DLGCMDBUTTON, NULL, N_("New") },
+#define I_HIDE (3)
+#define turnoutHideT ((wChoice_p)turnoutPLs[I_HIDE].control)
+ { PD_TOGGLE, &hideTurnoutWindow, "hide", PDO_DLGCMDBUTTON, /*CAST_AWAY_CONST*/(void*)hideLabels, NULL, BC_NOBORDER } };
+static paramGroup_t turnoutPG = { "turnout", 0, turnoutPLs, sizeof turnoutPLs/sizeof turnoutPLs[0] };
+#endif
+
+
+
+/****************************************
+ *
+ * TURNOUT LIST MANAGEMENT
+ *
+ */
+
+
+EXPORT turnoutInfo_t * CreateNewTurnout(
+ char * scale,
+ char * title,
+ wIndex_t segCnt,
+ trkSeg_p segData,
+ wIndex_t pathLen,
+ PATHPTR_T paths,
+ EPINX_T endPtCnt,
+ trkEndPt_t * endPts,
+ wBool_t updateList )
+{
+ turnoutInfo_t * to;
+ long changes=0;
+
+ to = FindCompound( FIND_TURNOUT, scale, title );
+ if (to == NULL) {
+ DYNARR_APPEND( turnoutInfo_t *, turnoutInfo_da, 10 );
+ to = (turnoutInfo_t*)MyMalloc( sizeof *to );
+ turnoutInfo(turnoutInfo_da.cnt-1) = to;
+ to->title = MyStrdup( title );
+ to->scaleInx = LookupScale( scale );
+ changes = CHANGE_PARAMS;
+ }
+ to->segCnt = segCnt;
+ to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt );
+ GetSegBounds( zero, 0.0, segCnt, to->segs, &to->orig, &to->size );
+ to->endCnt = endPtCnt;
+ to->endPt = (trkEndPt_t*)memdup( endPts, (sizeof *endPts) * to->endCnt );
+
+ to->pathLen = pathLen;
+ to->paths = (PATHPTR_T)memdup( paths, (sizeof *to->paths) * to->pathLen );
+ to->paramFileIndex = curParamFileIndex;
+ if (curParamFileIndex == PARAM_CUSTOM)
+ to->contentsLabel = "Custom Turnouts";
+ else
+ to->contentsLabel = curSubContents;
+#ifdef TURNOUTCMD
+ if (updateList && turnoutListL != NULL) {
+ FormatCompoundTitle( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, title );
+ if (message[0] != '\0')
+ wListAddValue( turnoutListL, message, NULL, to );
+ }
+#endif
+
+ to->barScale = curBarScale>0?curBarScale:-1;
+ to->special = TOnormal;
+ if (updateList && changes)
+ DoChangeNotification( changes );
+ return to;
+}
+
+
+
+EXPORT wIndex_t CheckPaths(
+ wIndex_t segCnt,
+ trkSeg_p segs,
+ PATHPTR_T paths )
+{
+ int pc, ps;
+ PATHPTR_T pp;
+ int inx, inx1;
+ static dynArr_t segMap_da;
+ int segInx[2], segEp[2];
+ int segTrkLast = -1;
+ trkSeg_t tempSeg;
+
+#define segMap(N) DYNARR_N( trkSeg_p, segMap_da, N )
+
+ DYNARR_RESET( trkSeg_p, segMap_da );
+ for ( inx=0; inx<segCnt; inx++ ) {
+ if ( IsSegTrack(&segs[inx]) ) {
+ if ( segTrkLast != inx-1 ) {
+ tempSeg = segs[inx];
+ segTrkLast++;
+ for ( inx1=inx; inx1>segTrkLast; inx1-- ) {
+ segs[inx1] = segs[inx1-1];
+ }
+ segs[segTrkLast] = tempSeg;
+ } else {
+ segTrkLast = inx;
+ }
+ DYNARR_APPEND( trkSeg_p, segMap_da, 10 );
+ segMap(segMap_da.cnt-1) = &segs[inx];
+ }
+ }
+
+ for ( pc=0,pp=paths; *pp; pp+=2,pc++ ) {
+ for ( ps=0,pp+=strlen((char *)pp)+1; pp[0]!=0 || pp[1]!=0; pp++,ps++ ) {
+#ifdef LATER
+ if (*pp >= '0' && *pp <= '9')
+ *pp -= '0';
+ else if (*pp >= 'A' && *pp <= 'Z')
+ *pp -= 'A' - 10;
+ if (*pp < 0 || *pp > segCnt) {
+ InputError( _("Turnout path[%d:%d] out of bounds: %d"),
+ FALSE, pc, ps, *pp);
+ return -1;
+ }
+#endif
+
+ if (pp[0]!=0 && pp[1]!=0 ) {
+ /* check connectivity */
+ DIST_T d;
+ GetSegInxEP( pp[0], &segInx[0], &segEp[0] );
+ GetSegInxEP( pp[1], &segInx[1], &segEp[1] );
+ if ( !IsSegTrack( &segs[segInx[0]] ) ) {
+ InputError( _("Turnout path[%d] %d is not a track segment"),
+ FALSE, pc, pp[0] );
+ return -1;
+ }
+ if ( !IsSegTrack( &segs[segInx[1]] ) ) {
+ InputError( _("Turnout path[%d] %d is not a track segment"),
+ FALSE, pc, pp[1] );
+ return -1;
+ }
+ d = FindDistance(
+ GetSegEndPt( &segs[segInx[0]], 1-segEp[0], FALSE, NULL ),
+ GetSegEndPt( &segs[segInx[1]], segEp[1], FALSE, NULL ) );
+ if (d > MIN_TURNOUT_SEG_CONNECT_DIST) {
+ InputError( _("Turnout path[%d] %d-%d not connected: %0.3f"),
+ FALSE, pc, pp[0], pp[1], d );
+ return -1;
+ }
+ }
+
+ }
+ }
+ return pp-paths+1;
+}
+
+
+static BOOL_T ReadTurnoutParam(
+ char * firstLine )
+{
+ char scale[10];
+ char *title;
+ turnoutInfo_t * to;
+
+ if ( !GetArgs( firstLine+8, "sq", scale, &title ) )
+ return FALSE;
+ DYNARR_RESET( trkEndPt_t, tempEndPts_da );
+ pathCnt = 0;
+ if (ReadSegs()) {
+ CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr );
+ to = CreateNewTurnout( scale, title, tempSegs_da.cnt, &tempSegs(0),
+ pathCnt, pathPtr, tempEndPts_da.cnt, &tempEndPts(0), FALSE );
+ if (to == NULL)
+ return FALSE;
+ if (tempSpecial[0] != '\0') {
+ if (strncmp( tempSpecial, ADJUSTABLE, strlen(ADJUSTABLE) ) == 0) {
+ to->special = TOadjustable;
+ GetArgs( tempSpecial+strlen(ADJUSTABLE), "ff",
+ &to->u.adjustable.minD, &to->u.adjustable.maxD );
+
+ } else {
+ InputError(_("Unknown special case"), TRUE);
+ }
+ }
+ if (tempCustom[0] != '\0') {
+ to->customInfo = MyStrdup( tempCustom );
+ }
+ }
+ MyFree( title );
+ return TRUE;
+}
+
+
+EXPORT turnoutInfo_t * TurnoutAdd( long mode, SCALEINX_T scale, wList_p list, coOrd * maxDim, EPINX_T epCnt )
+{
+ wIndex_t inx;
+ turnoutInfo_t * to, * to1 = NULL;
+ for ( inx = 0; inx < turnoutInfo_da.cnt; inx++ ) {
+ to = turnoutInfo(inx);
+ if ( IsParamValid(to->paramFileIndex) &&
+ to->segCnt > 0 &&
+ CompatibleScale( TRUE, to->scaleInx, scale ) &&
+ /*strcasecmp( to->scale, scaleName ) == 0 && */
+ ( epCnt <= 0 || epCnt == to->endCnt ) ) {
+ if (to1==NULL)
+ to1 = to;
+ FormatCompoundTitle( mode, to->title );
+ if (message[0] != '\0') {
+ wListAddValue( list, message, NULL, to );
+ if (maxDim) {
+ if (to->size.x > maxDim->x)
+ maxDim->x = to->size.x;
+ if (to->size.y > maxDim->y)
+ maxDim->y = to->size.y;
+ }
+ }
+ }
+ }
+ return to1;
+}
+
+/****************************************
+ *
+ * Adjustable Track Support
+ *
+ */
+
+
+static void ChangeAdjustableEndPt(
+ track_p trk,
+ EPINX_T ep,
+ DIST_T d )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ coOrd pos;
+ trkSeg_p segPtr;
+ ANGLE_T angle = GetTrkEndAngle( trk, ep );
+ Translate( &pos, GetTrkEndPos( trk, 1-ep ), angle, d );
+ UndoModify(trk);
+ SetTrkEndPoint( trk, ep, pos, angle );
+ if ( ep == 0 )
+ xx->orig = pos;
+ for ( segPtr=xx->segs; segPtr<&xx->segs[xx->segCnt]; segPtr++ ) {
+ switch (segPtr->type) {
+ case SEG_STRLIN:
+ case SEG_STRTRK:
+ segPtr->u.l.pos[1].x = d;
+ break;
+ default:
+ ;
+ }
+ }
+ ComputeBoundingBox( trk );
+ DrawNewTrack( trk );
+}
+
+
+EXPORT BOOL_T ConnectAdjustableTracks(
+ track_p trk1,
+ EPINX_T ep1,
+ track_p trk2,
+ EPINX_T ep2 )
+{
+ struct extraData * xx1;
+ struct extraData * xx2;
+ BOOL_T adj1, adj2;
+ coOrd p1, p2;
+ ANGLE_T a, a1, a2;
+ DIST_T d, maxD, d1, d2;
+ BOOL_T rc;
+ coOrd off;
+ DIST_T beyond;
+
+ xx1 = GetTrkExtraData(trk1);
+ xx2 = GetTrkExtraData(trk2);
+ adj1 = adj2 = FALSE;
+ if (GetTrkType(trk1) == T_TURNOUT && xx1->special == TOadjustable)
+ adj1 = TRUE;
+ if (GetTrkType(trk2) == T_TURNOUT && xx2->special == TOadjustable)
+ adj2 = TRUE;
+ if (adj1 == FALSE && adj2 == FALSE)
+ return FALSE;
+ a1 = GetTrkEndAngle( trk1, ep1 );
+ a2 = GetTrkEndAngle( trk2, ep2 );
+ a = NormalizeAngle( a1 - a2 + 180.0 + connectAngle/2.0);
+ if (a>connectAngle)
+ return FALSE;
+ UndoStart( _("Connect Adjustable Tracks"), "changeAdjustableEndPt" );
+ maxD = 0.0;
+ if (adj1) {
+ p1 = GetTrkEndPos( trk1, 1-ep1 );
+ Translate( &p1, p1, a1, xx1->u.adjustable.minD );
+ maxD += xx1->u.adjustable.maxD-xx1->u.adjustable.minD;
+ } else {
+ p1 = GetTrkEndPos( trk1, ep1 );
+ }
+ if (adj2) {
+ p2 = GetTrkEndPos( trk2, 1-ep2 );
+ Translate( &p2, p2, a2, xx2->u.adjustable.minD );
+ maxD += xx2->u.adjustable.maxD-xx2->u.adjustable.minD;
+ } else {
+ p2 = GetTrkEndPos( trk2, ep2 );
+ }
+ d = FindDistance( p1, p2 );
+ rc = TRUE;
+ if (d > maxD) {
+ d = maxD;
+ rc = FALSE;
+ }
+ FindPos( &off, &beyond, p1, p2, a1, 10000.0 );
+ if (fabs(off.y) > connectDistance)
+ rc = FALSE;
+ if (adj1) {
+ UndrawNewTrack( trk1 );
+ d1 = d * (xx1->u.adjustable.maxD-xx1->u.adjustable.minD)/maxD + xx1->u.adjustable.minD;
+ ChangeAdjustableEndPt( trk1, ep1, d1 );
+ }
+ if (adj2) {
+ UndrawNewTrack( trk2 );
+ d2 = d * (xx2->u.adjustable.maxD-xx2->u.adjustable.minD)/maxD + xx2->u.adjustable.minD;
+ ChangeAdjustableEndPt( trk2, ep2, d2 );
+ }
+ if (rc) {
+ DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite );
+ DrawEndPt( &mainD, trk2, ep2, wDrawColorWhite );
+ ConnectTracks( trk1, ep1, trk2, ep2 );
+ DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack );
+ DrawEndPt( &mainD, trk2, ep2, wDrawColorBlack );
+ }
+ return rc;
+}
+
+/****************************************
+ *
+ * Draw Turnout Roadbed
+ *
+ */
+
+int roadbedOnScreen = 0;
+
+
+void DrawTurnoutRoadbedSide( drawCmd_p d, wDrawColor color, coOrd orig, ANGLE_T angle, trkSeg_p sp, ANGLE_T side, int first, int last )
+{
+ segProcData_t data;
+ if (last<=first)
+ return;
+ data.drawRoadbedSide.first = first;
+ data.drawRoadbedSide.last = last;
+ data.drawRoadbedSide.side = side;
+ data.drawRoadbedSide.roadbedWidth = roadbedWidth;
+ data.drawRoadbedSide.rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5);
+ data.drawRoadbedSide.orig = orig;
+ data.drawRoadbedSide.angle = angle;
+ data.drawRoadbedSide.color = color;
+ data.drawRoadbedSide.d = d;
+ SegProc( SEGPROC_DRAWROADBEDSIDE, sp, &data );
+}
+
+
+static void ComputeAndDrawTurnoutRoadbedSide(
+ drawCmd_p d,
+ wDrawColor color,
+ coOrd orig,
+ ANGLE_T angle,
+ trkSeg_p segPtr,
+ int segCnt,
+ int segInx,
+ ANGLE_T side )
+{
+ unsigned long res, res1;
+ int b0, b1;
+ res = ComputeTurnoutRoadbedSide( segPtr, segCnt, segInx, side, roadbedWidth );
+ if (res == 0L) {
+ } else if (res == 0xFFFFFFFF) {
+ DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, 0, 32 );
+ } else {
+ for ( b0=0, res1=0x00000001; res1&&(res1&res); b0++,res1<<=1 );
+ for ( b1=32,res1=0x80000000; res1&&(res1&res); b1--,res1>>=1 );
+ DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, 0, b0 );
+ DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], side, b1, 32 );
+ }
+}
+
+
+static void DrawTurnoutRoadbed(
+ drawCmd_p d,
+ wDrawColor color,
+ coOrd orig,
+ ANGLE_T angle,
+ trkSeg_p segPtr,
+ int segCnt )
+{
+ int inx, trkCnt=0, segInx=0;
+ for (inx=0;inx<segCnt;inx++) {
+ if ( IsSegTrack(&segPtr[inx]) ) {
+ segInx = inx;
+ trkCnt++;
+ if (trkCnt>1)
+ break;
+ }
+ }
+ if (trkCnt==0)
+ return;
+ if (trkCnt == 1) {
+ DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], +90, 0, 32 );
+ DrawTurnoutRoadbedSide( d, color, orig, angle, &segPtr[segInx], -90, 0, 32 );
+ } else {
+ for (inx=0;inx<segCnt;inx++) {
+ if ( IsSegTrack(&segPtr[inx]) ) {
+ ComputeAndDrawTurnoutRoadbedSide( d, color, orig, angle, segPtr, segCnt, inx, +90 );
+ ComputeAndDrawTurnoutRoadbedSide( d, color, orig, angle, segPtr, segCnt, inx, -90 );
+ }
+ }
+ }
+}
+
+/****************************************
+ *
+ * HAND LAID TURNOUTS
+ *
+ */
+
+track_p NewHandLaidTurnout(
+ coOrd p0,
+ ANGLE_T a0,
+ coOrd p1,
+ ANGLE_T a1,
+ coOrd p2,
+ ANGLE_T a2,
+ ANGLE_T frogA )
+{
+ track_p trk;
+ struct extraData * xx;
+ trkSeg_t segs[2];
+ sprintf( message, "\tHand Laid Turnout, Angle=%0.1f\t", frogA );
+ DYNARR_SET( trkEndPt_t, tempEndPts_da, 2 );
+ memset( &tempEndPts(0), 0, tempEndPts_da.cnt * sizeof tempEndPts(0) );
+ tempEndPts(0).pos = p0;
+ tempEndPts(0).angle = a0;
+ tempEndPts(1).pos = p1;
+ tempEndPts(1).angle = a1;
+ tempEndPts(2).pos = p2;
+ tempEndPts(2).angle = a2;
+ Rotate( &p1, p0, -a0 );
+ p1.x -= p0.x;
+ p1.y -= p0.y;
+ segs[0].type = SEG_STRTRK;
+ segs[0].color = wDrawColorBlack;
+ segs[0].u.l.pos[0] = zero;
+ segs[0].u.l.pos[1] = p1;
+ Rotate( &p2, p0, -a0 );
+ p2.x -= p0.x;
+ p2.y -= p0.y;
+ segs[1].type = SEG_STRTRK;
+ segs[1].color = wDrawColorBlack;
+ segs[1].u.l.pos[0] = zero;
+ segs[1].u.l.pos[1] = p2;
+ trk = NewCompound( T_TURNOUT, 0, p0, a0, message, 3, &tempEndPts(0), 22, "Normal\0\1\0\0Reverse\0\2\0\0\0", 2, segs );
+ xx = GetTrkExtraData(trk);
+ xx->handlaid = TRUE;
+
+#ifdef LATER
+ trk = NewTrack( 0, T_TURNOUT, 3,
+ sizeof (*xx) + (3-1)*sizeof curTurnout->segs[0] + 1);
+ xx = GetTrkExtraData(trk);
+ xx->orig = p0;
+ xx->angle = a0;
+ xx->handlaid = TRUE;
+ xx->descriptionOff = zero;
+ xx->descriptionSize = zero;
+ sprintf( message, "\tHand Laid Turnout, Angle=%0.1f\t", frogA );
+ xx->title = MyStrdup( message );
+ xx->paths = xx->pathCurr = (PATHPTR_T)"Normal\0\1\0\0Reverse\0\2\0\0\0";
+ xx->pathLen = 21;
+ SetTrkEndPoint( trk, 0, p0, a0 );
+ SetTrkEndPoint( trk, 1, p1, a1 );
+ SetTrkEndPoint( trk, 2, p2, a2 );
+ xx->segCnt = 2;
+ Rotate( &p1, p0, -a0 );
+ p1.x -= p0.x;
+ p1.y -= p0.y;
+ xx->segs[0].type = SEG_STRTRK;
+ xx->segs[0].color = wDrawColorBlack;
+ xx->segs[0].u.l.pos[0] = zero;
+ xx->segs[0].u.l.pos[1] = p1;
+ Rotate( &p2, p0, -a0 );
+ p2.x -= p0.x;
+ p2.y -= p0.y;
+ xx->segs[1].type = SEG_STRTRK;
+ xx->segs[1].color = wDrawColorBlack;
+ xx->segs[1].u.l.pos[0] = zero;
+ xx->segs[1].u.l.pos[1] = p2;
+ ComputeBoundingBox( trk );
+ SetDescriptionOrig( trk );
+#endif
+ return trk;
+}
+
+/****************************************
+ *
+ * GENERIC FUNCTIONS
+ *
+ */
+
+static coOrd MapPathPos(
+ struct extraData * xx,
+ signed char segInx,
+ EPINX_T ep )
+{
+ trkSeg_p segPtr;
+ wIndex_t inx;
+ coOrd pos;
+
+ if ( segInx < 0 ) {
+ segInx = - segInx;
+ ep = 1-ep;
+ }
+
+ for ( inx=0,segPtr=xx->segs; inx<xx->segCnt; inx++,segPtr++ ) {
+ if ( !IsSegTrack(segPtr) ) continue;
+ if ( --segInx > 0 ) continue;
+ pos = GetSegEndPt( segPtr, ep, FALSE, NULL );
+ REORIGIN1( pos, xx->angle, xx->orig );
+ return pos;
+ }
+ fprintf( stderr, "mapPathPos: bad segInx: %d\n", segInx );
+ return zero;
+}
+
+
+static void DrawTurnout(
+ track_p trk,
+ drawCmd_p d,
+ wDrawColor color )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ wIndex_t i;
+ long widthOptions = 0;
+ DIST_T scale2rail;
+
+ if (GetTrkWidth(trk) == 2)
+ widthOptions = DTS_THICK2;
+ if (GetTrkWidth(trk) == 3)
+ widthOptions = DTS_THICK3;
+ 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, trk, xx->orig, xx->angle, xx->segs, xx->segCnt, GetTrkGauge(trk), color, widthOptions|DTS_TIES );
+ DrawSegsO( d, trk, xx->orig, xx->angle, xx->segs, xx->segCnt, GetTrkGauge(trk), color, widthOptions | DTS_NOCENTER ); // no curve center for turnouts
+ for (i=0; i<GetTrkEndPtCnt(trk); i++) {
+ DrawEndPt( d, trk, i, color );
+ }
+ if ( ((d->funcs->options&wDrawOptTemp)==0) &&
+ (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) &&
+ labelScale >= d->scale &&
+ ( GetTrkBits( trk ) & TB_HIDEDESC ) == 0 ) {
+ DrawCompoundDescription( trk, d, color );
+ if (!xx->handlaid)
+ LabelLengths( d, trk, color );
+ }
+ if ( roadbedWidth > GetTrkGauge(trk) &&
+ ( ((d->options&DC_PRINT) && d->scale <= (twoRailScale*2+1)/2.0) ||
+ (roadbedOnScreen && d->scale <= twoRailScale) ) )
+ DrawTurnoutRoadbed( d, color, xx->orig, xx->angle, xx->segs, xx->segCnt );
+
+}
+
+
+static void ReadTurnout(
+ char * line )
+{
+ ReadCompound( line+8, T_TURNOUT );
+ CheckPaths( tempSegs_da.cnt, &tempSegs(0), pathPtr );
+}
+
+
+static ANGLE_T GetAngleTurnout(
+ track_p trk,
+ coOrd pos,
+ EPINX_T *ep0,
+ EPINX_T *ep1 )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ wIndex_t segCnt, segInx;
+ ANGLE_T angle;
+
+ if ( ep0 && ep1 )
+ *ep0 = *ep1 = PickEndPoint( pos, trk );
+ for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ );
+ pos.x -= xx->orig.x;
+ pos.y -= xx->orig.y;
+ Rotate( &pos, zero, -xx->angle );
+ angle = GetAngleSegs( segCnt, xx->segs, pos, &segInx );
+ return NormalizeAngle( angle+xx->angle );
+}
+
+
+static BOOL_T SplitTurnoutCheckPath(
+ wIndex_t segInxEnd,
+ PATHPTR_T pp1,
+ int dir1,
+ PATHPTR_T pp2,
+ int dir2,
+ trkSeg_p segs,
+ coOrd epPos )
+{
+ wIndex_t segInx1, segInx2;
+ EPINX_T segEP;
+ coOrd pos;
+ DIST_T dist;
+
+ GetSegInxEP( pp2[0], &segInx2, &segEP );
+ if ( dir2 < 0 ) segEP = 1-segEP;
+ pos = GetSegEndPt( &segs[segInx2], segEP, FALSE, NULL );
+ dist = FindDistance( pos, epPos );
+ if ( dist>connectDistance )
+ return TRUE;
+ while ( pp2[0] ) {
+ GetSegInxEP( pp1[0], &segInx1, &segEP );
+ GetSegInxEP( pp2[0], &segInx2, &segEP );
+ if ( segInx1 != segInx2 )
+ break;
+ if ( segInxEnd == segInx2 )
+ return TRUE;
+ pp1 += dir1;
+ pp2 += dir2;
+ }
+ return FALSE;
+}
+
+
+static BOOL_T SplitTurnoutCheckEP(
+ wIndex_t segInx0,
+ coOrd epPos,
+ PATHPTR_T pp1,
+ int dir1,
+ PATHPTR_T pp,
+ trkSeg_p segs )
+{
+ while ( pp[0] ) {
+ pp += strlen((char *)pp)+1;
+ while ( pp[0] ) {
+ if (!SplitTurnoutCheckPath( segInx0, pp1, dir1, pp, 1, segs, epPos ))
+ return FALSE;
+ while ( pp[0] )
+ pp++;
+ if (!SplitTurnoutCheckPath( segInx0, pp1, dir1, pp-1, -1, segs, epPos ))
+ return FALSE;
+ pp++;
+ }
+ pp++;
+ }
+ return TRUE;
+}
+
+
+EXPORT EPINX_T TurnoutPickEndPt(
+ coOrd epPos,
+ track_p trk )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ wIndex_t segCnt, segInx, segInx0;
+ EPINX_T segEP;
+ PATHPTR_T cp, cq, pps[2];
+ coOrd pos;
+ DIST_T dist, dists[2];
+ int dir;
+ EPINX_T ep, epCnt, eps[2];
+ BOOL_T unique_eps[2];
+
+ for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ );
+ DistanceSegs( xx->orig, xx->angle, segCnt, xx->segs, &epPos, &segInx0 );
+ Rotate( &epPos, xx->orig, xx->angle );
+ epPos.x -= xx->orig.x;
+ epPos.y -= xx->orig.y;
+ epCnt = GetTrkEndPtCnt(trk);
+ cp = xx->paths;
+ eps[0] = eps[1] = -1;
+ unique_eps[0] = unique_eps[1] = TRUE;
+ while ( cp[0] ) {
+ cp += strlen((char *)cp)+1;
+ while ( cp[0] ) {
+ while ( cp[0] ) {
+ GetSegInxEP( cp[0], &segInx, &segEP );
+ if ( segInx == segInx0 ) {
+ for ( dir=0; dir<2; dir++ ) {
+ for ( cq=cp; cq[dir?-1:1]; cq += (dir?-1:1) );
+ GetSegInxEP( cq[0], &segInx, &segEP );
+ if ( dir==0 ) segEP = 1-segEP;
+ pos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL );
+ dist = FindDistance( pos, epPos );
+ if ( eps[dir] < 0 || dist < dists[dir] ) {
+ dists[dir] = dist;
+ pos.x += xx->orig.x;
+ pos.y += xx->orig.y;
+ Rotate( &pos, xx->orig, xx->angle );
+ for ( ep=0; ep<epCnt; ep++ ) {
+ if ( FindDistance( pos, GetTrkEndPos(trk,ep) ) < connectDistance )
+ break;
+ }
+ if ( ep<epCnt ) {
+ if ( eps[dir] >= 0 && eps[dir] != ep )
+ unique_eps[dir] = FALSE;
+ eps[dir] = ep;
+ dists[dir] = dist;
+ pps[dir] = cq;
+ }
+ }
+ }
+ }
+ cp++;
+ }
+ cp++;
+ }
+ cp++;
+ }
+
+ for ( dir=0; dir<2; dir++ ) {
+ if ( unique_eps[dir] && eps[dir] >= 0 ) {
+ GetSegInxEP( pps[dir][0], &segInx, &segEP );
+ if ( dir == 0 ) segEP = 1-segEP;
+ epPos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL );
+ if ( ! SplitTurnoutCheckEP( segInx0, epPos, pps[dir], dir?1:-1, xx->paths, xx->segs ) )
+ unique_eps[dir] = FALSE;
+ }
+ }
+
+ if ( unique_eps[0] == unique_eps[1] ) {
+ if ( eps[0] >= 0 && eps[1] >= 0 )
+ return ( dists[0] < dists[1] ) ? eps[0] : eps[1] ;
+ }
+ if ( unique_eps[0] && eps[0] >= 0 )
+ return eps[0];
+ if ( unique_eps[1] && eps[1] >= 0 )
+ return eps[1];
+ if ( eps[0] >= 0 && eps[1] >= 0 )
+ return ( dists[0] < dists[1] ) ? eps[0] : eps[1] ;
+ return eps[0] >= 0 ? eps[0] : eps[1] ;
+}
+
+
+static PATHPTR_T splitTurnoutPath;
+static PATHPTR_T splitTurnoutRoot;
+static int splitTurnoutDir;
+
+static void SplitTurnoutCheckEndPt(
+ PATHPTR_T path,
+ int dir,
+ trkSeg_p segs,
+ coOrd epPos,
+ coOrd splitPos )
+{
+ PATHPTR_T path0;
+ wIndex_t segInx;
+ EPINX_T segEP;
+ coOrd pos;
+ DIST_T dist, minDist;
+
+ path0 = path;
+ GetSegInxEP( path[0], &segInx, &segEP );
+ if ( dir < 0 ) segEP = 1-segEP;
+ pos = GetSegEndPt( &segs[segInx], segEP, FALSE, NULL );
+ dist = FindDistance( pos, epPos );
+ if ( dist>connectDistance )
+ return;
+ minDist = trackGauge;
+ while ( path[0] ) {
+ GetSegInxEP( path[0], &segInx, &segEP );
+ if ( dir < 0 ) segEP = 1-segEP;
+ pos = splitPos;
+ dist = DistanceSegs( zero, 0.0, 1, &segs[segInx], &pos, NULL );
+ if ( dist < minDist ) {
+ minDist = dist;
+ splitTurnoutPath = path;
+ splitTurnoutDir = -dir;
+ splitTurnoutRoot = path0;
+ }
+ path += dir;
+ }
+}
+
+
+static BOOL_T SplitTurnout(
+ track_p trk,
+ coOrd pos,
+ EPINX_T ep,
+ track_p *leftover,
+ EPINX_T * ep0,
+ EPINX_T * ep1 )
+{
+ struct extraData * xx = GetTrkExtraData( trk );
+ wIndex_t segInx0, segInx, segCnt;
+ EPINX_T segEP, epCnt, ep2=0, epN;
+ PATHPTR_T pp, pp1, pp2;
+ unsigned char c;
+ char * cp;
+ int negCnt, posCnt, pathCnt, dir;
+ segProcData_t segProcDataSplit;
+ segProcData_t segProcDataNewTrack;
+ track_p trk2=NULL;
+ static dynArr_t segIndexMap_da;
+#define segIndexMap(N) DYNARR_N( int, segIndexMap_da, N )
+ static dynArr_t newPath_da;
+#define newPath(N) DYNARR_N( char, newPath_da, N )
+ coOrd orig, size, epPos;
+ ANGLE_T epAngle;
+ PATHPTR_T path;
+ int s0, s1;
+ trkSeg_t newSeg;
+
+ if ( (MyGetKeyState()&WKEY_SHIFT) == 0 ) {
+ ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") );
+ return FALSE;
+ }
+
+ /*
+ * 1. Find segment on path that ends at 'ep'
+ */
+ epCnt = GetTrkEndPtCnt(trk);
+ epPos = GetTrkEndPos( trk, ep );
+ for ( segCnt=0; segCnt<xx->segCnt && IsSegTrack(&xx->segs[segCnt]); segCnt++ );
+ Rotate( &pos, xx->orig, -xx->angle );
+ pos.x -= xx->orig.x;
+ pos.y -= xx->orig.y;
+ Rotate( &epPos, xx->orig, -xx->angle );
+ epPos.x -= xx->orig.x;
+ epPos.y -= xx->orig.y;
+ splitTurnoutPath = NULL;
+ pp = xx->paths;
+ while ( pp[0] ) {
+ pp += strlen((char *)pp)+1;
+ while ( pp[0] ) {
+ SplitTurnoutCheckEndPt( pp, 1, xx->segs, epPos, pos );
+ if ( splitTurnoutPath != NULL )
+ goto foundSeg;
+ while ( pp[0] )
+ pp++;
+ SplitTurnoutCheckEndPt( pp-1, -1, xx->segs, epPos, pos );
+ if ( splitTurnoutPath != NULL )
+ goto foundSeg;
+ pp++;
+ }
+ pp++;
+ }
+ ErrorMessage( _("splitTurnout: can't find segment") );
+ return FALSE;
+foundSeg:
+
+ /*
+ * 2a. Check that all other paths thru found segment are the same
+ */
+ GetSegInxEP( splitTurnoutPath[0], &segInx0, &segEP );
+ pp = xx->paths;
+ pathCnt = 0;
+ while ( pp[0] ) {
+ pp += strlen((char *)pp)+1;
+ while ( pp[0] ) {
+ while ( pp[0] ) {
+ GetSegInxEP( pp[0], &segInx, &segEP );
+ if ( segInx == segInx0 ) {
+ pp1 = splitTurnoutPath;
+ pp2 = pp;
+ dir = (pp2[0]>0?1:-1) * splitTurnoutDir;
+ while ( pp1[0] && pp2[0] ) {
+ if ( splitTurnoutDir * pp1[0] != dir * pp2[0] )
+ break;
+ pp1 += splitTurnoutDir;
+ pp2 += dir;
+ }
+ if ( pp1[0]!='\0' || pp2[0]!='\0' ) {
+ ErrorMessage( MSG_SPLIT_POS_BTW_MERGEPTS );
+ return FALSE;
+ }
+ }
+ pp++;
+ }
+ pp++;
+ }
+ pp++;
+ }
+
+ /*
+ * 2b. Check that all paths from ep pass thru segInx0
+ */
+ if ( !SplitTurnoutCheckEP( segInx0, epPos, splitTurnoutRoot, -splitTurnoutDir, xx->paths, xx->segs ) ) {
+ ErrorMessage( MSG_SPLIT_PATH_NOT_UNIQUE );
+ return FALSE;
+ }
+
+
+ /*
+ * 3. Split the found segment.
+ */
+ segProcDataSplit.split.pos = pos;
+ s0 = (splitTurnoutPath[0] > 0) != (splitTurnoutDir > 0);
+ s1 = 1-s0;
+ SegProc( SEGPROC_SPLIT, xx->segs+segInx0, &segProcDataSplit );
+ if ( segProcDataSplit.split.length[s1] <= minLength ) {
+ if ( splitTurnoutPath[splitTurnoutDir] == '\0' )
+ return FALSE;
+ segProcDataSplit.split.length[s0] += segProcDataSplit.split.length[s1];
+ segProcDataSplit.split.length[s1] = 0;
+ segProcDataSplit.split.newSeg[s0] = xx->segs[segInx0];
+ epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s0], s1, FALSE, &epAngle );
+ } else if ( segProcDataSplit.split.length[s0] <= minLength ) {
+ segProcDataSplit.split.length[s1] += segProcDataSplit.split.length[s0];
+ segProcDataSplit.split.length[s0] = 0;
+ segProcDataSplit.split.newSeg[s1] = xx->segs[segInx0];
+ epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s1], s0, FALSE, &epAngle );
+ epAngle += 180.0;
+ } else {
+ epPos = GetSegEndPt( &segProcDataSplit.split.newSeg[s1], s0, FALSE, &epAngle );
+ epAngle += 180.0;
+ }
+#ifdef LATER
+ if ( segProcDataSplit.split.length[s1] <= minLength && splitTurnoutPath[1] == '\0' )
+ return FALSE;
+#endif
+
+ /*
+ * 4. Map the old segments to new
+ */
+ DYNARR_SET( int, segIndexMap_da, xx->segCnt );
+ for ( segInx=0; segInx<xx->segCnt; segInx++ )
+ segIndexMap(segInx) = segInx+1;
+ pp = splitTurnoutPath;
+ if ( segProcDataSplit.split.length[s0] > minLength )
+ pp += splitTurnoutDir;
+ negCnt = 0;
+ while ( *pp ) {
+ GetSegInxEP( *pp, &segInx, &segEP );
+ segIndexMap(segInx) = - segIndexMap(segInx);
+ negCnt++;
+ pp += splitTurnoutDir;
+ }
+ for ( segInx=posCnt=0; segInx<xx->segCnt; segInx++ ) {
+ if ( segIndexMap(segInx) > 0 )
+ segIndexMap(segInx) = ++posCnt;
+ }
+ DYNARR_SET( trkSeg_t, tempSegs_da, posCnt );
+ for ( segInx=posCnt=0; segInx<xx->segCnt; segInx++ ) {
+ if ( segIndexMap(segInx) > 0 ) {
+ if ( segInx == segInx0 ) {
+ tempSegs(segIndexMap(segInx)-1) = segProcDataSplit.split.newSeg[s0];
+ } else {
+ tempSegs(segIndexMap(segInx)-1) = xx->segs[segInx];
+ }
+ }
+ }
+
+ /*
+ * 5. Remap paths by removing trailing segments
+ */
+ DYNARR_SET( char, newPath_da, xx->pathLen );
+ pp = xx->paths;
+ pp1 = (PATHPTR_T)&newPath(0);
+ while ( *pp ) {
+ strcpy( (char *)pp1, (char *)pp );
+ pp += strlen( (char *)pp )+1;
+ pp1 += strlen( (char *)pp1 )+1;
+ while ( *pp ) {
+ while ( *pp ) {
+ GetSegInxEP( *pp, &segInx, &segEP );
+ if ( segIndexMap(segInx) > 0 ) {
+ c = segIndexMap(segInx);
+ if ( *pp<0 )
+ c = -c;
+ *pp1++ = c;
+ }
+ pp++;
+ }
+ *pp1++ = '\0';
+ pp++;
+ }
+ *pp1++ = '\0';
+ pp++;
+ }
+ *pp1++ = '\0';
+
+ /*
+ * 6. Reorigin segments
+ */
+ GetSegBounds( zero, 0, tempSegs_da.cnt, &tempSegs(0), &orig, &size );
+ orig.x = -orig.x;
+ orig.y = -orig.y;
+ MoveSegs( tempSegs_da.cnt, &tempSegs(0), orig );
+ epPos.x += orig.x;
+ epPos.y += orig.y;
+ cp = strchr( xx->title, '\t' );
+ if ( cp ) {
+ if ( strncmp( cp+1, "Split ", 6 ) != 0 ) {
+ memcpy( message, xx->title, cp-xx->title+1 );
+ strcpy( message+(cp-xx->title+1), "Split " );
+ strcat( message, cp+1 );
+ } else {
+ strcpy( message, xx->title );
+ }
+ } else {
+ sprintf( message, "Split %s", xx->title );
+ }
+
+ /*
+ * 7. Convert trailing segments to new tracks
+ */
+ path = splitTurnoutPath;
+ if ( segProcDataSplit.split.length[s1] < minLength )
+ path += splitTurnoutDir;
+ while ( path[0] ) {
+ GetSegInxEP( path[0], &segInx, &segEP );
+ s0 = (path[0] > 0) != (splitTurnoutDir > 0);
+ if ( segInx0 != segInx ) {
+ newSeg = xx->segs[segInx];
+ } else {
+ newSeg = segProcDataSplit.split.newSeg[s1];
+ }
+ MoveSegs( 1, &newSeg, xx->orig );
+ RotateSegs( 1, &newSeg, xx->orig, xx->angle );
+ SegProc( SEGPROC_NEWTRACK, &newSeg, &segProcDataNewTrack );
+ if ( *leftover == NULL ) {
+ *ep0 = segProcDataNewTrack.newTrack.ep[s0];
+ *leftover = trk2 = segProcDataNewTrack.newTrack.trk;
+ ep2 = 1-*ep0;
+ } else {
+ epN = segProcDataNewTrack.newTrack.ep[s0];
+ ConnectTracks( trk2, ep2, segProcDataNewTrack.newTrack.trk, epN );
+ trk2 = segProcDataNewTrack.newTrack.trk;
+ ep2 = 1-epN;
+ }
+ path += splitTurnoutDir;
+ }
+
+ /*
+ * 8. Replace segments, paths, and endPt in original turnout
+ */
+ xx->split = TRUE;
+ Rotate( &orig, zero, xx->angle );
+ xx->orig.x -= orig.x;
+ xx->orig.y -= orig.y;
+ xx->segCnt = tempSegs_da.cnt;
+ xx->segs = (trkSeg_p)memdup( &tempSegs(0), tempSegs_da.cnt * sizeof tempSegs(0) );
+ CloneFilledDraw( xx->segCnt, xx->segs, TRUE );
+ xx->pathLen = pp1-(PATHPTR_T)&newPath(0);
+ xx->pathCurr = xx->paths = memdup( &newPath(0), xx->pathLen );
+ epAngle = NormalizeAngle( xx->angle+epAngle );
+ epPos.x += xx->orig.x;
+ epPos.y += xx->orig.y;
+ Rotate( &epPos, xx->orig, xx->angle );
+ SetTrkEndPoint( trk, ep, epPos, epAngle );
+ ComputeCompoundBoundingBox( trk );
+
+ return TRUE;
+}
+
+
+static BOOL_T CheckTraverseTurnout(
+ track_p trk,
+ coOrd pos )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ coOrd pos1;
+#ifdef LATER
+ int inx, foundInx = 0;
+ DIST_T d, foundD;
+#endif
+ DIST_T d;
+ PATHPTR_T pathCurr;
+ int segInx;
+ EPINX_T segEP;
+
+LOG( log_traverseTurnout, 1, ( "CheckTraverseTurnout( T%d, [%0.3f %0.3f])\n", GetTrkIndex(trk), pos.x, pos.y ) )
+ Rotate( &pos, xx->orig, -xx->angle );
+ pos.x -= xx->orig.x;
+ pos.y -= xx->orig.y;
+LOG( log_traverseTurnout, 1, ( "After rotation = [%0.3f %0.3f])\n", pos.x, pos.y ) )
+
+#ifdef LATER
+ for ( inx=0; inx<xx->segCnt; inx++ ) {
+ switch ( xx->segs[inx].type ) {
+ case SEG_STRTRK:
+ case SEG_CRVTRK:
+ pos1 = GetSegEndPt( &xx->segs[inx], 0, FALSE, NULL );
+ d = FindDistance( pos, pos1 );
+ if ( foundInx == 0 || d < foundD ) {
+ foundInx = inx+1;
+ foundD = d;
+ }
+ pos1 = GetSegEndPt( &xx->segs[inx], 1, FALSE, NULL );
+ d = FindDistance( pos, pos1 );
+ if ( foundInx == 0 || d < foundD ) {
+ foundInx = -(inx+1);
+ foundD = d;
+ }
+ break;
+ }
+ }
+ if ( foundInx == 0 )
+ return FALSE;
+#endif
+ for ( pathCurr = xx->pathCurr+strlen((char*)xx->pathCurr)+1; pathCurr[0] || pathCurr[1]; pathCurr++ ) {
+LOG( log_traverseTurnout, 1, ( "P[%d] = %d ", pathCurr-xx->paths, pathCurr[0] ) )
+ if ( pathCurr[-1] == 0 ) {
+ GetSegInxEP( pathCurr[0], &segInx, &segEP );
+ pos1 = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL );
+ d = FindDistance( pos, pos1 );
+LOG( log_traverseTurnout, 1, ( "d=%0.3f\n", d ) )
+ if ( d < connectDistance )
+ return TRUE;
+ }
+ if ( pathCurr[1] == 0 ) {
+ GetSegInxEP( pathCurr[0], &segInx, &segEP );
+ pos1 = GetSegEndPt( &xx->segs[segInx], 1-segEP, FALSE, NULL );
+ d = FindDistance( pos, pos1 );
+LOG( log_traverseTurnout, 1, ( "d=%0.3f\n", d ) )
+ if ( d < connectDistance )
+ return TRUE;
+ }
+ }
+LOG( log_traverseTurnout, 1, ( " not found\n" ) )
+ return FALSE;
+}
+
+
+static BOOL_T TraverseTurnout(
+ traverseTrack_p trvTrk,
+ DIST_T * distR )
+{
+ track_p trk = trvTrk->trk;
+ struct extraData * xx = GetTrkExtraData(trk);
+ coOrd pos0, pos1, pos2;
+ DIST_T d, dist;
+ PATHPTR_T path, pathCurr;
+ BOOL_T backwards=FALSE;
+ trkSeg_p segPtr;
+ EPINX_T ep, epCnt, ep2;
+ int segInx;
+ EPINX_T segEP;
+ segProcData_t segProcData;
+
+ d = 10000;
+ pos0 = trvTrk->pos;
+ Rotate( &pos0, xx->orig, -xx->angle );
+ pos0.x -= xx->orig.x;
+ pos0.y -= xx->orig.y;
+ dist = *distR;
+LOG( log_traverseTurnout, 1, ( "TraverseTurnout( T%d, [%0.3f %0.3f] [%0.3f %0.3f], A%0.3f, D%0.3f\n", GetTrkIndex(trk), trvTrk->pos.x, trvTrk->pos.y, pos0.x, pos0.y, trvTrk->angle, *distR ) )
+ pathCurr = 0;
+ for ( path = xx->pathCurr+strlen((char*)xx->pathCurr)+1; path[0] || path[1]; path++ ) {
+ if ( path[0] == 0 )
+ continue;
+ GetSegInxEP( path[0], &segInx, &segEP );
+ segPtr = xx->segs+segInx;
+#ifdef LATER
+ for ( inx = 0; inx<xx->segCnt; inx++ ) {
+ segPtr = xx->segs+inx;
+#endif
+ segProcData.distance.pos1 = pos0;
+ SegProc( SEGPROC_DISTANCE, segPtr, &segProcData );
+ if ( segProcData.distance.dd < d ) {
+ d = segProcData.distance.dd;
+ pos2 = segProcData.distance.pos1;
+ pathCurr = path;
+ }
+ }
+ if ( d > 10 || pathCurr == 0 ) {
+ ErrorMessage( "traverseTurnout: Not near: %0.3f", d );
+ return FALSE;
+ }
+LOG( log_traverseTurnout, 1, ( " PC=%d ", pathCurr[0] ) )
+ GetSegInxEP( pathCurr[0], &segInx, &segEP );
+ segPtr = xx->segs+segInx;
+#ifdef LATER
+ for ( pathCurr = xx->pathCurr+strlen((char*)xx->pathCurr)+1; pathCurr[0] || pathCurr[1]; pathCurr++ ) {
+ if ( pathCurr[0] == 0 )
+ continue;
+ if ( Abs(pathCurr[0])-1 == currInx )
+ break;
+ }
+ if ( pathCurr[0] == 0 ) {
+ fprintf( stderr, "Open turnout [%d]\n", currInx );
+ return FALSE;
+ }
+ segPtr = xx->segs+currInx;
+#endif
+ segProcData.traverse1.pos = pos2;
+ segProcData.traverse1.angle = xx->angle-trvTrk->angle;
+ SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData );
+ dist += segProcData.traverse1.dist;
+ backwards = segProcData.traverse1.backwards;
+ if ( segEP ) backwards = !backwards;
+LOG( log_traverseTurnout, 2, ( " B%d D%0.3f\n", backwards, dist ) )
+
+ while ( *pathCurr ) {
+ GetSegInxEP( pathCurr[0], &segInx, &segEP );
+ segPtr = xx->segs+segInx;
+ segProcData.traverse2.segDir = (backwards?1-segEP:segEP);
+ segProcData.traverse2.dist = dist;
+ SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData );
+ if ( segProcData.traverse2.dist <= 0 ) {
+ *distR = 0;
+ REORIGIN( trvTrk->pos, segProcData.traverse2.pos, xx->angle, xx->orig );
+ trvTrk->angle = NormalizeAngle( xx->angle+segProcData.traverse2.angle );
+ return TRUE;
+ }
+ dist = segProcData.traverse2.dist;
+ pathCurr += (backwards?-1:1);
+LOG( log_traverseTurnout, 1, ( " D%0.3f\n", dist ) )
+ }
+
+ pathCurr += (backwards?1:-1);
+ pos1 = MapPathPos( xx, pathCurr[0], (backwards?0:1) );
+ *distR = dist;
+ epCnt = GetTrkEndPtCnt(trk);
+ ep = 0;
+ dist = FindDistance( pos1, GetTrkEndPos(trk,0) );
+ for ( ep2=1; ep2<epCnt; ep2++ ) {
+ d = FindDistance( pos1, GetTrkEndPos(trk,ep2) );
+ if ( d < dist ) {
+ dist = d;
+ ep = ep2;
+ }
+ }
+ if ( dist > connectDistance ) {
+ trk = NULL;
+ trvTrk->pos = pos1;
+ } else {
+ trvTrk->pos = GetTrkEndPos( trk, ep );
+ trvTrk->angle = GetTrkEndAngle( trk, ep );
+ trk = GetTrkEndTrk( trk, ep );
+ }
+ dist = FindDistance( trvTrk->pos, pos1 );
+LOG( log_traverseTurnout, 1, ( " -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) )
+ trvTrk->trk = trk;
+ return TRUE;
+}
+
+
+static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos )
+{
+ struct extraData *xx;
+ static EPINX_T ep;
+ DIST_T d;
+
+ xx = GetTrkExtraData(trk);
+ if ( xx->special == TOadjustable ) {
+ switch ( action ) {
+ case C_DOWN:
+ ep = PickUnconnectedEndPoint( pos, trk );
+ if (ep == -1)
+ return C_ERROR;
+ UndrawNewTrack( trk );
+ tempSegs(0).type = SEG_STRTRK;
+ tempSegs(0).width = 0;
+ tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, 1-ep );
+ tempSegs_da.cnt = 1;
+ InfoMessage( _("Drag to change track length") );
+
+ case C_MOVE:
+ d = FindDistance( tempSegs(0).u.l.pos[0], pos );
+ if ( d < xx->u.adjustable.minD )
+ d = xx->u.adjustable.minD;
+ else if ( d > xx->u.adjustable.maxD )
+ d = xx->u.adjustable.maxD;
+ Translate( &tempSegs(0).u.l.pos[1], tempSegs(0).u.l.pos[0], GetTrkEndAngle( trk, ep ), d );
+ tempSegs_da.cnt = 1;
+ if (action == C_MOVE)
+ InfoMessage( _("Length=%s"), FormatDistance( d ) );
+ return C_CONTINUE;
+
+ case C_UP:
+ d = FindDistance( tempSegs(0).u.l.pos[0],tempSegs(0).u.l.pos[1] );
+ ChangeAdjustableEndPt( trk, ep, d );
+ return C_TERMINATE;
+
+ default:
+ ;
+ }
+ }
+ return ExtendStraightFromOrig( trk, action, pos );
+}
+
+
+static BOOL_T GetParamsTurnout( int inx, track_p trk, coOrd pos, trackParams_t * params )
+{
+ params->type = curveTypeStraight;
+ params->ep = PickUnconnectedEndPoint( pos, trk );
+ if (params->ep == -1)
+ return FALSE;
+ params->lineOrig = GetTrkEndPos(trk,params->ep);
+ params->lineEnd = params->lineOrig;
+ params->len = 0.0;
+ params->angle = GetTrkEndAngle(trk,params->ep);
+ params->arcR = 0.0;
+ return TRUE;
+}
+
+
+static BOOL_T MoveEndPtTurnout( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 )
+{
+ ANGLE_T angle0;
+ DIST_T d;
+ track_p trk1;
+
+ angle0 = GetTrkEndAngle(*trk,*ep);
+ d = FindDistance( GetTrkEndPos(*trk,*ep), pos);
+ if (d0 > 0.0) {
+ d -= d0;
+ if (d < 0.0) {
+ ErrorMessage( MSG_MOVED_BEFORE_END_TURNOUT );
+ return FALSE;
+ }
+ Translate( &pos, pos, angle0+180, d0 );
+ }
+ if (d > minLength) {
+ trk1 = NewStraightTrack( GetTrkEndPos(*trk,*ep), pos );
+ CopyAttributes( *trk, trk1 );
+ ConnectTracks( *trk, *ep, trk1, 0 );
+ *trk = trk1;
+ *ep = 1;
+ DrawNewTrack( *trk );
+ }
+ return TRUE;
+}
+
+
+static BOOL_T QueryTurnout( track_p trk, int query )
+{
+ switch ( query ) {
+ case Q_IGNORE_EASEMENT_ON_EXTEND:
+ case Q_DRAWENDPTV_1:
+ case Q_CAN_GROUP:
+ case Q_ISTRACK:
+ case Q_NOT_PLACE_FROGPOINTS:
+ case Q_HAS_DESC:
+ case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK:
+ return TRUE;
+ case Q_CAN_PARALLEL:
+ if( GetTrkEndPtCnt( trk ) == 2 && fabs( GetTrkEndAngle( trk, 0 ) - GetTrkEndAngle( trk, 1 )) == 180.0 )
+ return TRUE;
+ else
+ return FALSE;
+ case Q_CAN_NEXT_POSITION:
+ return ( GetTrkEndPtCnt(trk) > 2 );
+ default:
+ return FALSE;
+ }
+}
+
+
+EXPORT int doDrawTurnoutPosition = 1;
+static wIndex_t drawTurnoutPositionWidth=3;
+static void DrawTurnoutPositionIndicator(
+ track_p trk,
+ wDrawColor color )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ PATHPTR_T path = xx->pathCurr;
+ coOrd pos0, pos1;
+
+ if ( xx->pathCurr == xx->paths ) {
+ for ( path=xx->pathCurr+strlen((char *)xx->pathCurr); path[0] || path[1]; path++ );
+ if ( path[2] == 0 )
+ return;
+ }
+ for ( path=xx->pathCurr+strlen((char *)xx->pathCurr); path[0] || path[1]; path++ ) {
+ if ( path[0] == 0 ) {
+ pos0 = MapPathPos( xx, path[1], 0 );
+ } else if ( path[1] == 0 ) {
+ pos1 = MapPathPos( xx, path[0], 1 );
+ DrawLine( &mainD, pos0, pos1, drawTurnoutPositionWidth, color );
+ }
+ }
+}
+
+
+EXPORT void AdvanceTurnoutPositionIndicator(
+ track_p trk,
+ coOrd pos,
+ coOrd *posR,
+ ANGLE_T *angleR )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ PATHPTR_T path;
+ traverseTrack_t trvtrk;
+ DIST_T dist;
+
+ if ( GetTrkType(trk) != T_TURNOUT )
+ AbortProg( "nextTurnoutPosition" );
+
+ DrawTurnoutPositionIndicator( trk, wDrawColorWhite );
+ path = xx->pathCurr;
+ path += strlen((char *)path)+1;
+ while ( path[0] || path[1] )
+ path++;
+ path += 2;
+ if ( *path == 0 )
+ path = xx->paths;
+ xx->pathCurr = path;
+ DrawTurnoutPositionIndicator( trk, selectedColor );
+ if ( angleR == NULL || posR == NULL )
+ return;
+ trvtrk.trk = trk;
+ trvtrk.length = 0;
+ trvtrk.dist = 0;
+ trvtrk.pos = *posR;
+ trvtrk.angle = *angleR;
+ dist = 0;
+ if ( !TraverseTurnout( &trvtrk, &dist ) )
+ return;
+ if ( NormalizeAngle( trvtrk.angle-*angleR+90.0 ) > 180 )
+ trvtrk.angle = NormalizeAngle( trvtrk.angle+180.0 );
+ *posR = trvtrk.pos;
+ *angleR = trvtrk.angle;
+}
+
+/**
+ * Create a parallel track for a turnout.
+ *
+ *
+ * \param trk IN existing track
+ * \param pos IN ??
+ * \param sep IN distance between existing and new track
+ * \param newTrk OUT new track piece
+ * \param p0R OUT starting point of new piece
+ * \param p1R OUT ending point of new piece
+ * \return always TRUE
+ */
+
+static BOOL_T MakeParallelTurnout(
+ track_p trk,
+ coOrd pos,
+ DIST_T sep,
+ track_p * newTrk,
+ coOrd * p0R,
+ coOrd * p1R )
+{
+ ANGLE_T angle = GetTrkEndAngle(trk,1);
+ struct extraData *xx, *yy;
+ coOrd *endPts;
+ trkEndPt_p endPt;
+ int i;
+ int option;
+ DIST_T d;
+
+ if ( NormalizeAngle( FindAngle( GetTrkEndPos(trk,0), pos ) - GetTrkEndAngle(trk,1) ) < 180.0 )
+ angle += 90;
+ else
+ angle -= 90;
+
+ /*
+ * get all endpoints of current piece and translate them for the new piece
+ */
+ endPts = MyMalloc( GetTrkEndPtCnt( trk ) * sizeof( coOrd ));
+ for( i = 0; i < GetTrkEndPtCnt( trk ); i++) {
+ Translate( &(endPts[ i ]), GetTrkEndPos( trk, i ), angle, sep );
+ }
+
+ /*
+ * get information about the current piece and copy data
+ */
+
+ if( newTrk ) {
+ endPt = MyMalloc( GetTrkEndPtCnt( trk ) * sizeof( trkEndPt_t ));
+ endPt[ 0 ].pos = endPts[ 0 ];
+ endPt[ 0 ].angle = GetTrkEndAngle( trk, 0 );
+ endPt[ 1 ].pos = endPts[ 1 ];
+ endPt[ 1 ].angle = GetTrkEndAngle( trk, 1 );
+
+ yy = GetTrkExtraData(trk);
+
+ *newTrk = NewCompound( T_TURNOUT, 0, endPt[ 0 ].pos, endPt[ 0 ].angle + 90.0, yy->title, 2, endPt, yy->pathLen, (char *)yy->paths, yy->segCnt, yy->segs );
+ xx = GetTrkExtraData(*newTrk);
+ xx->customInfo = yy->customInfo;
+
+ /* if (connection((int)curTurnoutEp).trk) {
+ CopyAttributes( connection((int)curTurnoutEp).trk, newTrk );
+ SetTrkScale( newTrk, curScaleInx );
+ } */
+ xx->special = yy->special;
+ xx->u = yy->u;
+
+ SetDescriptionOrig( *newTrk );
+ xx->descriptionOff = zero;
+ xx->descriptionSize = zero;
+
+ SetTrkElev(*newTrk, GetTrkElevMode(trk), GetTrkElev(trk));
+ GetTrkEndElev( trk, 0, &option, &d );
+ SetTrkEndElev( *newTrk, 0, option, d, NULL );
+ GetTrkEndElev( trk, 1, &option, &d );
+ SetTrkEndElev( *newTrk, 1, option, d, NULL );
+
+ MyFree( endPt );
+ } else {
+ /* draw some temporary track while command is in process */
+ tempSegs(0).color = wDrawColorBlack;
+ tempSegs(0).width = 0;
+ tempSegs_da.cnt = 1;
+ tempSegs(0).type = SEG_STRTRK;
+ tempSegs(0).u.l.pos[0] = endPts[ 0 ];
+ tempSegs(0).u.l.pos[1] = endPts[ 1 ];
+ }
+
+ if ( p0R ) *p0R = endPts[ 0 ];
+ if ( p1R ) *p1R = endPts[ 1 ];
+
+ MyFree( endPts );
+ return TRUE;
+}
+
+static trackCmd_t turnoutCmds = {
+ N_("TURNOUT "),
+ DrawTurnout,
+ DistanceCompound,
+ DescribeCompound,
+ DeleteCompound,
+ WriteCompound,
+ ReadTurnout,
+ MoveCompound,
+ RotateCompound,
+ RescaleCompound,
+ NULL,
+ GetAngleTurnout,
+ SplitTurnout,
+ TraverseTurnout,
+ EnumerateCompound,
+ NULL, /*redraw*/
+ NULL, /*trim*/
+ NULL, /*merge*/
+ ModifyTurnout,
+ NULL, /* getLength */
+ GetParamsTurnout,
+ MoveEndPtTurnout,
+ QueryTurnout,
+ UngroupCompound,
+ FlipCompound,
+ DrawTurnoutPositionIndicator,
+ AdvanceTurnoutPositionIndicator,
+ CheckTraverseTurnout,
+ MakeParallelTurnout };
+
+
+#ifdef TURNOUTCMD
+/*****************************************
+ *
+ * Turnout Dialog
+ *
+ */
+
+static coOrd maxTurnoutDim;
+
+static void AddTurnout( void );
+
+
+static wWin_p turnoutW;
+
+
+static void RescaleTurnout( void )
+{
+ DIST_T xscale, yscale;
+ wPos_t ww, hh;
+ DIST_T w, h;
+ wDrawGetSize( turnoutD.d, &ww, &hh );
+ w = ww/turnoutD.dpi;
+ h = hh/turnoutD.dpi;
+ xscale = maxTurnoutDim.x/w;
+ yscale = maxTurnoutDim.y/h;
+ turnoutD.scale = max(xscale,yscale);
+ if (turnoutD.scale == 0.0)
+ turnoutD.scale = 1.0;
+ turnoutD.size.x = w*turnoutD.scale;
+ turnoutD.size.y = h*turnoutD.scale;
+ return;
+}
+
+
+static void TurnoutChange( long changes )
+{
+ static char * lastScaleName = NULL;
+ if (turnoutW == NULL)
+ return;
+ wListSetIndex( turnoutListL, 0 );
+ if ( (!wWinIsVisible(turnoutW)) ||
+ ( ((changes&CHANGE_SCALE) == 0 || lastScaleName == curScaleName) &&
+ (changes&CHANGE_PARAMS) == 0 ) )
+ return;
+ lastScaleName = curScaleName;
+ curTurnout = NULL;
+ curTurnoutEp = 0;
+ wControlShow( (wControl_p)turnoutListL, FALSE );
+ wListClear( turnoutListL );
+ maxTurnoutDim.x = maxTurnoutDim.y = 0.0;
+ if (turnoutInfo_da.cnt <= 0)
+ return;
+ curTurnout = TurnoutAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, curScaleInx, turnoutListL, &maxTurnoutDim, -1 );
+ wListSetIndex( turnoutListL, 0 );
+ wControlShow( (wControl_p)turnoutListL, TRUE );
+ if (curTurnout == NULL) {
+ wDrawClear( turnoutD.d );
+ return;
+ }
+ turnoutD.orig.x = -trackGauge;
+ turnoutD.orig.y = -trackGauge;
+ maxTurnoutDim.x += 2*trackGauge;
+ maxTurnoutDim.y += 2*trackGauge;
+ /*RescaleTurnout();*/
+ RedrawTurnout();
+ return;
+}
+
+static void RedrawTurnout()
+{
+ coOrd p, s;
+ RescaleTurnout();
+LOG( log_turnout, 2, ( "SelTurnout(%s)\n", (curTurnout?curTurnout->title:"<NULL>") ) )
+
+ wDrawClear( turnoutD.d );
+ if (curTurnout == NULL) {
+ return;
+ }
+ turnoutD.orig.x = curTurnout->orig.x - trackGauge;
+ turnoutD.orig.y = (curTurnout->size.y + curTurnout->orig.y) - turnoutD.size.y + trackGauge;
+ DrawSegs( &turnoutD, zero, 0.0, curTurnout->segs, curTurnout->segCnt,
+ trackGauge, wDrawColorBlack );
+ curTurnoutEp = 0;
+ p.x = curTurnout->endPt[0].pos.x - trackGauge;
+ p.y = curTurnout->endPt[0].pos.y - trackGauge;
+ s.x = s.y = trackGauge*2.0 /*+ turnoutD.minSize*/;
+ DrawHilight( &turnoutD, p, s );
+}
+
+
+static void TurnoutOk( void )
+{
+ AddTurnout();
+ Reset();
+}
+
+
+static void TurnoutDlgUpdate(
+ paramGroup_p pg,
+ int inx,
+ void * valueP )
+{
+ turnoutInfo_t * to;
+ if ( inx != I_LIST ) return;
+ to = (turnoutInfo_t*)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP );
+ AddTurnout();
+ curTurnout = to;
+ RedrawTurnout();
+/* ParamDialogOkActive( &turnoutPG, FALSE ); */
+}
+
+
+static wIndex_t TOpickEndPoint(
+ coOrd p,
+ turnoutInfo_t *to )
+{
+ wIndex_t inx, i;
+ DIST_T d, dd;
+ coOrd posI;
+
+ d = FindDistance( p, to->endPt[0].pos );
+ inx = 0;
+ for ( i=1; i<to->endCnt; i++ ) {
+ posI = to->endPt[i].pos;
+ if ((dd=FindDistance(p, posI)) < d) {
+ d = dd;
+ inx = i;
+ }
+ }
+ return inx;
+}
+
+
+static void HilightEndPt( void )
+{
+ coOrd p, s;
+ p.x = curTurnout->endPt[(int)curTurnoutEp].pos.x - trackGauge;
+ p.y = curTurnout->endPt[(int)curTurnoutEp].pos.y - trackGauge;
+ s.x = s.y = trackGauge*2.0 /*+ turnoutD.minSize*/;
+ DrawHilight( &turnoutD, p, s );
+}
+
+
+static void SelTurnoutEndPt(
+ wIndex_t action,
+ coOrd pos )
+{
+ if (action != C_DOWN) return;
+
+ HilightEndPt();
+ curTurnoutEp = TOpickEndPoint( pos, curTurnout );
+ HilightEndPt();
+LOG( log_turnout, 3, (" selected (action=%d) %ld\n", action, curTurnoutEp ) )
+}
+#endif
+
+/****************************************
+ *
+ * GRAPHICS COMMANDS
+ *
+ */
+
+/*
+ * STATE INFO
+ */
+static struct {
+ int state;
+ coOrd pos;
+ coOrd place;
+ track_p trk;
+ ANGLE_T angle;
+ coOrd rot0, rot1;
+ } Dto;
+
+
+typedef struct {
+ DIST_T off;
+ ANGLE_T angle;
+ EPINX_T ep;
+ } vector_t;
+
+static void PlaceTurnoutTrial(
+ track_p *trkR,
+ coOrd *posR,
+ ANGLE_T *angle1R,
+ ANGLE_T *angle2R,
+ int *connCntR,
+ DIST_T *maxDR,
+ vector_t *v )
+{
+ coOrd pos = *posR;
+ ANGLE_T aa;
+ ANGLE_T angle;
+ EPINX_T ep0, ep1;
+ track_p trk, trk1;
+ coOrd epPos, conPos, posI;
+ ANGLE_T epAngle;
+ int i, connCnt = 0;
+ DIST_T d, maxD = 0;
+
+ if ( (*trkR = trk = OnTrack( &pos, FALSE, TRUE )) != NULL &&
+ !QueryTrack(trk,Q_CANNOT_PLACE_TURNOUT) &&
+ (ep0 = PickEndPoint( pos, trk )) >= 0 &&
+ ! ( GetTrkType(trk) == T_TURNOUT &&
+ (trk1=GetTrkEndTrk(trk,ep0)) &&
+ GetTrkType(trk1) == T_TURNOUT) &&
+ ! GetLayerFrozen(GetTrkLayer(trk)) ) {
+ epPos = GetTrkEndPos( trk, ep0 );
+ d = FindDistance( pos, epPos );
+ if (d <= minLength)
+ pos = epPos;
+ if ( GetTrkType(trk) == T_TURNOUT ) {
+ ep0 = ep1 = PickEndPoint( pos, trk );
+ angle = GetTrkEndAngle( trk, ep0 );
+ } else {
+ angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 );
+ }
+ angle = NormalizeAngle( angle + 180.0 );
+ if ( NormalizeAngle( FindAngle( pos, *posR ) - angle ) < 180.0 && ep0 != ep1 )
+ angle = NormalizeAngle( angle + 180 );
+ *angle2R = angle;
+ epPos = curTurnout->endPt[(int)curTurnoutEp].pos;
+ *angle1R = angle = NormalizeAngle( angle - curTurnout->endPt[(int)curTurnoutEp].angle );
+ Rotate( &epPos, zero, angle );
+ pos.x -= epPos.x;
+ pos.y -= epPos.y;
+ *posR = pos;
+LOG( log_turnout, 3, ( "placeTurnout T%d (%0.3f %0.3f) A%0.3f\n",
+ GetTrkIndex(trk), pos.x, pos.y, angle ) )
+ /*InfoMessage( "Turnout(%d): Angle=%0.3f", GetTrkIndex(trk), angle );*/
+
+ for (i=0;i<curTurnout->endCnt;i++) {
+ posI = curTurnout->endPt[i].pos;
+ epPos = AddCoOrd( pos, posI, angle );
+ epAngle = NormalizeAngle( curTurnout->endPt[i].angle + angle );
+ conPos = epPos;
+ if ((trk = OnTrack(&conPos, FALSE, TRUE)) != NULL &&
+ !GetLayerFrozen(GetTrkLayer(trk))) {
+ v->off = FindDistance( epPos, conPos );
+ v->angle = FindAngle( epPos, conPos );
+ if ( GetTrkType(trk) == T_TURNOUT ) {
+ ep0 = ep1 = PickEndPoint( conPos, trk );
+ aa = GetTrkEndAngle( trk, ep0 );
+ } else {
+ aa = GetAngleAtPoint( trk, conPos, &ep0, &ep1 );
+ }
+ v->ep = i;
+ aa = NormalizeAngle( aa - epAngle + connectAngle/2.0 );
+ if ( IsClose(v->off) &&
+ ( aa<connectAngle || ( aa>180.0 && aa<180.0+connectAngle ) ) &&
+ ! ( GetTrkType(trk) == T_TURNOUT &&
+ (trk1=GetTrkEndTrk(trk,ep0)) &&
+ GetTrkType(trk1) == T_TURNOUT ) ) {
+ if (v->off > maxD)
+ maxD = v->off;
+ connCnt++;
+ v++;
+ }
+ }
+ }
+ }
+ *connCntR = connCnt;
+ *maxDR = maxD;
+}
+
+
+static void PlaceTurnout(
+ coOrd pos )
+{
+ coOrd p, pos1, pos2;
+ track_p trk1, trk2;
+ ANGLE_T a, a1, a2, a3;
+ int i, connCnt1, connCnt2;
+ DIST_T d, maxD1, maxD2, sina;
+ vector_t *V, * maxV;
+
+ static dynArr_t vector_da;
+#define vector(N) DYNARR_N( vector_t, vector_da, N )
+
+ pos1 = Dto.place = Dto.pos = pos;
+ if (curTurnoutEp >= (long)curTurnout->endCnt)
+ curTurnoutEp = 0;
+ DYNARR_SET( vector_t, vector_da, curTurnout->endCnt );
+ PlaceTurnoutTrial( &trk1, &pos1, &a1, &a2, &connCnt1, &maxD1, &vector(0) );
+ if (connCnt1 > 0) {
+ Dto.pos = pos1;
+ Dto.trk = trk1;
+ Dto.angle = a1;
+ if ( (MyGetKeyState()&WKEY_SHIFT)==0 && connCnt1 > 1 && maxD1 >= 0.001 ) {
+ maxV = &vector(0);
+ for ( i=1; i<connCnt1; i++ ) {
+ V = &vector(i);
+ if ( V->off > maxV->off ) {
+ maxV = V;
+ }
+ }
+ a3 = NormalizeAngle( Dto.angle + curTurnout->endPt[maxV->ep].angle );
+ a = NormalizeAngle( a2 - a3 );
+ sina = sin(D2R(a));
+ if (fabs(sina) > 0.01) {
+ d = maxV->off/sina;
+ if (NormalizeAngle( maxV->angle - a3) > 180)
+ d = -d;
+ Translate( &pos2, pos, a2, d );
+ PlaceTurnoutTrial( &trk2, &pos2, &a2, &a, &connCnt2, &maxD2, &vector(0) );
+ if ( connCnt2 >= connCnt1 && maxD2 < maxD1 ) {
+ Dto.pos = pos2;
+ Dto.trk = trk2;
+ Dto.angle = a2;
+ maxD1 = maxD2;
+ connCnt1 = connCnt2;
+ }
+ }
+ }
+ }
+ if ( connCnt1 > 0 ) {
+ FormatCompoundTitle( listLabels, curTurnout->title );
+ InfoMessage( _("%d connections, max distance %0.3f (%s)"),
+ connCnt1, PutDim(maxD1), message );
+ } else {
+ Dto.trk = NULL;
+ FormatCompoundTitle( listLabels, curTurnout->title );
+ InfoMessage( _("0 connections (%s)"), message );
+ p = curTurnout->endPt[(int)curTurnoutEp].pos;
+ Rotate( &p, zero, Dto.angle );
+ Dto.pos.x = pos.x - p.x;
+ Dto.pos.y = pos.y - p.y;
+ }
+}
+
+static void AddTurnout( void )
+{
+ track_p newTrk;
+ track_p trk, trk1;
+ struct extraData *xx;
+ coOrd epPos;
+ DIST_T d;
+ ANGLE_T a, aa;
+ EPINX_T ep0, ep1, epx, epy;
+ wIndex_t i,j;
+ wIndex_t titleLen;
+ typedef struct {
+ track_p trk;
+ EPINX_T ep;
+ } junk_t;
+ static dynArr_t connection_da;
+ static dynArr_t leftover_da;
+#define connection(N) DYNARR_N( junk_t, connection_da, N )
+#define leftover(N) DYNARR_N( junk_t, leftover_da, N )
+ BOOL_T visible;
+ BOOL_T noConnections;
+ coOrd p0, p1;
+
+ if (Dto.state == 0)
+ return;
+
+ if (curTurnout->segCnt < 1 || curTurnout->endCnt < 1) {
+ AbortProg( "addTurnout: bad cnt" );
+ }
+
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack );
+ UndoStart( _("Place New Turnout"), "addTurnout" );
+ titleLen = strlen( curTurnout->title );
+#ifdef LATER
+ newTrk = NewTrack( 0, T_TURNOUT, curTurnout->endCnt, sizeof (*xx) + 1 );
+ xx = GetTrkExtraData(newTrk);
+ xx->orig = Dto.pos;
+ xx->angle = Dto.angle;
+ xx->customInfo = curTurnout->customInfo;
+ xx->segs = MyMalloc( (curTurnout->segCnt)*sizeof curTurnout->segs[0] );
+#endif
+
+ DYNARR_SET( trkEndPt_t, tempEndPts_da, curTurnout->endCnt );
+ DYNARR_SET( junk_t, connection_da, curTurnout->endCnt );
+ DYNARR_SET( junk_t, leftover_da, curTurnout->endCnt );
+
+ for (i=0; i<curTurnout->endCnt; i++ ) {
+ coOrd posI;
+ posI = curTurnout->endPt[i].pos;
+ tempEndPts(i).pos = AddCoOrd( Dto.pos, posI, Dto.angle );
+ tempEndPts(i).angle = NormalizeAngle( curTurnout->endPt[i].angle + Dto.angle );
+ }
+
+ AuditTracks( "addTurnout begin" );
+
+ for (i=0;i<curTurnout->endCnt;i++) {
+ AuditTracks( "addTurnout [%d]", i );
+ connection(i).trk = leftover(i).trk = NULL;
+ /* connect each endPt ... */
+ epPos = tempEndPts(i).pos;
+ if ((trk = OnTrack(&epPos, FALSE, TRUE)) != NULL &&
+ (!GetLayerFrozen(GetTrkLayer(trk))) &&
+ (!QueryTrack(trk,Q_CANNOT_PLACE_TURNOUT)) ) {
+LOG( log_turnout, 1, ( "ep[%d] on T%d @(%0.3f %0.3f)\n",
+ i, GetTrkIndex(trk), epPos.x, epPos.y ) )
+ d = FindDistance( tempEndPts(i).pos, epPos );
+ if ( GetTrkType(trk) == T_TURNOUT ) {
+ ep0 = ep1 = PickEndPoint( epPos, trk );
+ a = GetTrkEndAngle( trk, ep0 );
+ } else {
+ a = GetAngleAtPoint( trk, epPos, &ep0, &ep1 );
+ }
+ aa = NormalizeAngle( a - tempEndPts(i).angle + connectAngle/2.0 );
+ if ( IsClose(d) &&
+ ( (ep0!=ep1 && aa<connectAngle) ||
+ ( aa>180.0 && aa<180.0+connectAngle ) ) &&
+ ! ( GetTrkType(trk) == T_TURNOUT &&
+ (trk1=GetTrkEndTrk(trk,ep0)) &&
+ GetTrkType(trk1) == T_TURNOUT ) ) {
+ /* ... if they are close to a track and line up */
+ if (aa<connectAngle) {
+ epx = ep1;
+ epy = ep0;
+ } else {
+ epx = ep0;
+ epy = ep1;
+ }
+LOG( log_turnout, 1, ( " Attach! epx=%d\n", epx ) )
+ if ( epx != epy &&
+ (d=FindDistance(GetTrkEndPos(trk,epy), epPos)) < minLength &&
+ (trk1=GetTrkEndTrk(trk,epy)) != NULL ) {
+ epx = GetEndPtConnectedToMe( trk1, trk );
+ trk = trk1;
+ }
+ /* split the track at the intersection point */
+ AuditTracks( "addTurnout [%d] before splitTrack", i );
+ if (SplitTrack( trk, epPos, epx, &leftover(i).trk, TRUE )) {
+ AuditTracks( "addTurnout [%d], after splitTrack", i );
+ /* remember so we can fix up connection later */
+ connection(i).trk = trk;
+ connection(i).ep = epx;
+ if (leftover(i).trk != NULL) {
+ leftover(i).ep = PickEndPoint( epPos, leftover(i).trk );
+ /* did we already split this track? */
+ for (j=0;j<i;j++) {
+ if ( leftover(j).trk == leftover(i).trk ) {
+ leftover(i).trk = NULL;
+ break;
+ }
+ if ( leftover(j).trk == connection(i).trk ) {
+ /* yes. Remove the leftover piece */
+LOG( log_turnout, 1, ( " deleting leftover T%d\n",
+ GetTrkIndex(leftover(i).trk) ) )
+ leftover(j).trk = NULL;
+ AuditTracks( "addTurnout [%d] before delete", i );
+ DeleteTrack( leftover(i).trk, FALSE );
+ AuditTracks( "addTurnout [%d] before delete", i );
+ leftover(i).trk = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ AuditTracks( "addTurnout after loop" );
+
+ /*
+ * copy data */
+ newTrk = NewCompound( T_TURNOUT, 0, Dto.pos, Dto.angle, curTurnout->title, tempEndPts_da.cnt, &tempEndPts(0), curTurnout->pathLen, (char *)curTurnout->paths, curTurnout->segCnt, curTurnout->segs );
+ xx = GetTrkExtraData(newTrk);
+ xx->customInfo = curTurnout->customInfo;
+ if (connection((int)curTurnoutEp).trk) {
+ CopyAttributes( connection((int)curTurnoutEp).trk, newTrk );
+ SetTrkScale( newTrk, curScaleInx );
+ }
+ xx->special = curTurnout->special;
+ xx->u = curTurnout->u;
+#ifdef LATER
+ xx->segCnt = curTurnout->segCnt;
+ memcpy( xx->segs, curTurnout->segs, xx->segCnt * sizeof *(trkSeg_p)0 );
+ xx->title = curTurnout->title;
+ xx->paths = xx->pathCurr = curTurnout->paths;
+ xx->pathLen = curTurnout->pathLen;
+#endif
+
+ /* Make the connections */
+#ifdef LATER
+ for (i=0; i<curTurnout->endCnt; i++)
+ SetTrkEndPoint( newTrk, i, tempEndPts(i).pos, tempEndPts(i).angle );
+#endif
+ visible = FALSE;
+ noConnections = TRUE;
+ AuditTracks( "addTurnout T%d before connection", GetTrkIndex(newTrk) );
+ for (i=0;i<curTurnout->endCnt;i++) {
+ if ( connection(i).trk != NULL ) {
+ p0 = GetTrkEndPos( newTrk, i );
+ p1 = GetTrkEndPos( connection(i).trk, connection(i).ep );
+ d = FindDistance( p0, p1 );
+ if ( d < connectDistance ) {
+ noConnections = FALSE;
+ trk1 = connection(i).trk;
+ ep0 = connection(i).ep;
+ DrawEndPt( &mainD, trk1, ep0, wDrawColorWhite );
+ ConnectTracks( newTrk, i, trk1, ep0 );
+ visible |= GetTrkVisible(trk1);
+ DrawEndPt( &mainD, trk1, ep0, wDrawColorBlack );
+ }
+ }
+ }
+ if (noConnections)
+ visible = TRUE;
+ SetTrkVisible( newTrk, visible);
+#ifdef LATER
+ SetTrkScale( newTrk, curScaleInx );
+ ComputeCompoundBoundingBox( newTrk );
+#endif
+
+ AuditTracks( "addTurnout T%d before dealing with leftovers", GetTrkIndex(newTrk) );
+ /* deal with the leftovers */
+ for (i=0;i<curTurnout->endCnt;i++) {
+ if ( (trk=leftover(i).trk) != NULL && !IsTrackDeleted(trk) ) {
+ /* move endPt beyond the turnout */
+ /* it it is short then delete it */
+ coOrd off;
+ DIST_T maxX;
+ track_p lt = leftover(i).trk;
+ EPINX_T ep, le = leftover(i).ep;
+ coOrd pos;
+ maxX = 0.0;
+ a = NormalizeAngle( GetTrkEndAngle(lt,le) + 180.0 );
+ for (ep=0; ep<curTurnout->endCnt; ep++) {
+ FindPos( &off, NULL, GetTrkEndPos(newTrk,ep), GetTrkEndPos(lt,le), a, 100000.0 );
+ if (off.x > maxX)
+ maxX = off.x;
+ }
+ maxX += trackGauge;
+ pos = Dto.pos;
+ AuditTracks( "addTurnout T%d[%d] before trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le );
+ TrimTrack( lt, le, maxX );
+ AuditTracks( "addTurnout T%d[%d] after trimming L%d[%d]", GetTrkIndex(newTrk), i, GetTrkIndex(lt), le );
+ }
+ }
+
+ SetDescriptionOrig( newTrk );
+ xx->descriptionOff = zero;
+ xx->descriptionSize = zero;
+
+ DrawNewTrack( newTrk );
+
+ AuditTracks( "addTurnout T%d returns", GetTrkIndex(newTrk) );
+ UndoEnd();
+ Dto.state = 0;
+ Dto.trk = NULL;
+ Dto.angle = 0.0;
+}
+
+
+static void TurnoutRotate( void * pangle )
+{
+ ANGLE_T angle = (ANGLE_T)(long)pangle;
+ if (Dto.state == 1)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack );
+ else
+ Dto.pos = cmdMenuPos;
+ Rotate( &Dto.pos, cmdMenuPos, angle );
+ Dto.angle += angle;
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack );
+ Dto.state = 1;
+}
+
+/**
+ * Process the mouse events for laying track.
+ *
+ * \param action IN event type
+ * \param pos IN mouse position
+ * \return next state
+ */
+
+EXPORT STATUS_T CmdTurnoutAction(
+ wAction_t action,
+ coOrd pos )
+{
+ ANGLE_T angle;
+ static BOOL_T validAngle;
+ static ANGLE_T baseAngle;
+ static coOrd origPos;
+#ifdef NEWROTATE
+ static ANGLE_T origAngle;
+#endif
+ switch (action & 0xFF) {
+
+ case C_START:
+ Dto.state = 0;
+ Dto.trk = NULL;
+ Dto.angle = 0.0;
+ return C_CONTINUE;
+
+ case C_DOWN:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ if (Dto.state == 1) {
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ }
+ PlaceTurnout( pos );
+ Dto.state = 1;
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ if ( curTurnoutEp >= (long)curTurnout->endCnt )
+ curTurnoutEp = 0;
+ if (Dto.state == 1) {
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ } else {
+ Dto.state = 1;
+ }
+ PlaceTurnout( pos );
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ return C_CONTINUE;
+
+ case C_UP:
+ InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") );
+ return C_CONTINUE;
+
+ case C_RDOWN:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ if (Dto.state == 1)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ else
+ Dto.pos = pos;
+ Dto.rot0 = Dto.rot1 = pos;
+ DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack );
+ Dto.state = 1;
+ origPos = Dto.pos;
+#ifdef NEWROTATE
+ origAngle = Dto.angle;
+#else
+ Rotate( &origPos, Dto.rot0, -(Dto.angle + curTurnout->endPt[(int)curTurnoutEp].angle) );
+#endif
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ validAngle = FALSE;
+ return C_CONTINUE;
+
+ case C_RMOVE:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack );
+ Dto.rot1 = pos;
+ if ( FindDistance(Dto.rot0, Dto.rot1) > 0.1*mainD.scale ) {
+ angle = FindAngle( Dto.rot0, Dto.rot1 );
+ if (!validAngle) {
+ baseAngle = angle/* - Dto.angle*/;
+ validAngle = TRUE;
+ }
+ Dto.pos = origPos;
+#ifdef NEWROTATE
+ angle -= baseAngle;
+ Dto.angle = NormalizeAngle( origAngle + angle );
+#else
+ angle += 180.0;
+ Dto.angle = angle - curTurnout->endPt[(int)curTurnoutEp].angle;
+#endif
+ Rotate( &Dto.pos, Dto.rot0, angle );
+ }
+ FormatCompoundTitle( listLabels, curTurnout->title );
+ InfoMessage( _("Angle = %0.3f (%s)"), PutAngle( NormalizeAngle(Dto.angle + 90.0) ), message );
+ DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack );
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ return C_CONTINUE;
+
+ case C_RUP:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ DrawLine( &tempD, Dto.rot0, Dto.rot1, 0, wDrawColorBlack );
+ InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") );
+ return C_CONTINUE;
+
+ case C_LCLICK:
+ if ( curTurnout == NULL ) return C_CONTINUE;
+ if ( MyGetKeyState() & WKEY_SHIFT ) {
+ if (Dto.state == 1)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ angle = curTurnout->endPt[(int)curTurnoutEp].angle;
+ curTurnoutEp++;
+ if (curTurnoutEp >= (long)curTurnout->endCnt)
+ curTurnoutEp = 0;
+ if (Dto.trk == NULL)
+ Dto.angle = NormalizeAngle( Dto.angle + (angle - curTurnout->endPt[(int)curTurnoutEp].angle ) );
+ PlaceTurnout( Dto.place );
+ if (Dto.state == 1)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ } else {
+ CmdTurnoutAction( C_DOWN, pos );
+ CmdTurnoutAction( C_UP, pos );
+ }
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ if (Dto.state)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ if (Dto.state)
+ DrawSegs( &tempD, Dto.pos, Dto.angle,
+ curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlue );
+ Dto.state = 0;
+ Dto.trk = NULL;
+ /*wHide( newTurn.reg.win );*/
+ return C_TERMINATE;
+
+ case C_TEXT:
+ if ((action>>8) != ' ')
+ return C_CONTINUE;
+ case C_OK:
+ AddTurnout();
+ return C_TERMINATE;
+
+ case C_FINISH:
+ if (Dto.state != 0 && Dto.trk != NULL)
+ CmdTurnoutAction( C_OK, pos );
+ else
+ CmdTurnoutAction( C_CANCEL, pos );
+ return C_TERMINATE;
+
+ case C_CMDMENU:
+ if ( turnoutPopupM == NULL ) {
+ turnoutPopupM = MenuRegister( "Turnout Rotate" );
+ AddRotateMenu( turnoutPopupM, TurnoutRotate );
+ }
+ wMenuPopupShow( turnoutPopupM );
+ return C_CONTINUE;
+
+ default:
+ return C_CONTINUE;
+ }
+}
+
+
+#ifdef TURNOUTCMD
+static STATUS_T CmdTurnout(
+ wAction_t action,
+ coOrd pos )
+{
+ wIndex_t turnoutIndex;
+ turnoutInfo_t * turnoutPtr;
+
+ switch (action & 0xFF) {
+
+ case C_START:
+ if (turnoutW == NULL) {
+/* turnoutW = ParamCreateDialog( &turnoutPG, MakeWindowTitle("Turnout"), "Ok", , (paramActionCancelProc)Reset, TRUE, NULL, F_RESIZE|F_RECALLSIZE, TurnoutDlgUpdate ); */
+ turnoutW = ParamCreateDialog( &turnoutPG, MakeWindowTitle(_("Turnout")), _("Close"), (paramActionOkProc)TurnoutOk, NULL, TRUE, NULL, F_RESIZE|F_RECALLSIZE|PD_F_ALT_CANCELLABEL, TurnoutDlgUpdate );
+ InitNewTurn( turnoutNewM );
+ }
+/* ParamDialogOkActive( &turnoutPG, FALSE ); */
+ turnoutIndex = wListGetIndex( turnoutListL );
+ turnoutPtr = curTurnout;
+ wShow( turnoutW );
+ TurnoutChange( CHANGE_PARAMS|CHANGE_SCALE );
+ if (curTurnout == NULL) {
+ NoticeMessage2( 0, MSG_TURNOUT_NO_TURNOUT, _("Ok"), NULL );
+ return C_TERMINATE;
+ }
+ if (turnoutIndex > 0 && turnoutPtr) {
+ curTurnout = turnoutPtr;
+ wListSetIndex( turnoutListL, turnoutIndex );
+ RedrawTurnout();
+ }
+ InfoMessage( _("Pick turnout and active End Point, then place on the layout"));
+ ParamLoadControls( &turnoutPG );
+ ParamGroupRecord( &turnoutPG );
+ return CmdTurnoutAction( action, pos );
+
+ case C_DOWN:
+ case C_RDOWN:
+ ParamDialogOkActive( &turnoutPG, TRUE );
+ if (hideTurnoutWindow)
+ wHide( turnoutW );
+ case C_MOVE:
+ case C_RMOVE:
+ return CmdTurnoutAction( action, pos );
+
+ case C_UP:
+ case C_RUP:
+ if (hideTurnoutWindow)
+ wShow( turnoutW );
+ InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") );
+ return CmdTurnoutAction( action, pos );
+
+ case C_LCLICK:
+ HilightEndPt();
+ CmdTurnoutAction( action, pos );
+ HilightEndPt();
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ wHide( turnoutW );
+ return CmdTurnoutAction( action, pos );
+ case C_TEXT:
+ CmdTurnoutAction( action, pos );
+ return C_CONTINUE;
+ case C_OK:
+ case C_FINISH:
+ case C_CMDMENU:
+ case C_REDRAW:
+ return CmdTurnoutAction( action, pos );
+
+ default:
+ return C_CONTINUE;
+ }
+}
+
+#endif
+
+/**
+ * Event procedure for the hotbar.
+ *
+ * \param op IN requested function
+ * \param data IN pointer to info on selected element
+ * \param d IN
+ * \param origP IN
+ * \return
+ */
+
+static char * CmdTurnoutHotBarProc(
+ hotBarProc_e op,
+ void * data,
+ drawCmd_p d,
+ coOrd * origP )
+{
+ turnoutInfo_t * to = (turnoutInfo_t*)data;
+ switch ( op ) {
+ case HB_SELECT: /* new element is selected */
+ CmdTurnoutAction( C_FINISH, zero ); /* finish current operation */
+ curTurnout = to;
+ DoCommandB( (void*)(intptr_t)turnoutHotBarCmdInx ); /* continue with new turnut / structure */
+ return NULL;
+ case HB_LISTTITLE:
+ FormatCompoundTitle( listLabels, to->title );
+ if (message[0] == '\0')
+ FormatCompoundTitle( listLabels|LABEL_DESCR, to->title );
+ return message;
+ case HB_BARTITLE:
+ FormatCompoundTitle( hotBarLabels<<1, to->title );
+ return message;
+ case HB_FULLTITLE:
+ return to->title;
+ case HB_DRAW:
+ DrawSegs( d, *origP, 0.0, to->segs, to->segCnt, trackGauge, wDrawColorBlack );
+ return NULL;
+ }
+ return NULL;
+}
+
+
+EXPORT void AddHotBarTurnouts( void )
+{
+ wIndex_t inx;
+ turnoutInfo_t * to;
+ for ( inx=0; inx < turnoutInfo_da.cnt; inx ++ ) {
+ to = turnoutInfo(inx);
+ if ( !( IsParamValid(to->paramFileIndex) &&
+ to->segCnt > 0 &&
+ CompatibleScale( TRUE, to->scaleInx, curScaleInx ) ) )
+ continue;
+ AddHotBarElement( to->contentsLabel, to->size, to->orig, TRUE, to->barScale, to, CmdTurnoutHotBarProc );
+ }
+}
+
+/**
+ * Handle mouse events for laying track when initiated from hotbar.
+ *
+ * \param action IN mouse event type
+ * \param pos IN mouse position
+ * \return next state of operation
+ */
+
+static STATUS_T CmdTurnoutHotBar(
+ wAction_t action,
+ coOrd pos )
+{
+
+ switch (action & 0xFF) {
+
+ case C_START:
+ TurnoutChange( CHANGE_PARAMS|CHANGE_SCALE );
+ if (curTurnout == NULL) {
+ NoticeMessage2( 0, MSG_TURNOUT_NO_TURNOUT, _("Ok"), NULL );
+ return C_TERMINATE;
+ }
+ FormatCompoundTitle( listLabels|LABEL_DESCR, curTurnout->title );
+ InfoMessage( _("Place %s and draw into position"), message );
+ ParamLoadControls( &turnoutPG );
+ ParamGroupRecord( &turnoutPG );
+ return CmdTurnoutAction( action, pos );
+
+ case C_UP:
+ case C_RUP:
+ InfoMessage( _("Left drag to move, right drag to rotate, press Space or Return to fix track in place or Esc to cancel") );
+ return CmdTurnoutAction( action, pos );
+
+ case C_TEXT:
+ if ((action>>8) != ' ')
+ return C_CONTINUE;
+ case C_OK:
+ CmdTurnoutAction( action, pos );
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ HotBarCancel();
+ default:
+ return CmdTurnoutAction( action, pos );
+ }
+}
+
+#ifdef TURNOUTCMD
+#include "bitmaps/turnout.xpm"
+
+
+EXPORT void InitCmdTurnout( wMenu_p menu )
+{
+ AddMenuButton( menu, CmdTurnout, "cmdTurnout", _("Turnout"), wIconCreatePixMap(turnout_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, ACCL_TURNOUT, NULL );
+ turnoutHotBarCmdInx = AddMenuButton( menu, CmdTurnoutHotBar, "cmdTurnoutHotBar", "", NULL, LEVEL0_50, IC_STICKY|IC_LCLICK|IC_CMDMENU|IC_POPUP2, 0, NULL );
+ RegisterChangeNotification( TurnoutChange );
+ ParamRegister( &turnoutPG );
+ log_turnout = LogFindIndex( "turnout" );
+ log_traverseTurnout = LogFindIndex( "traverseTurnout" );
+}
+#endif
+
+EXPORT void InitTrkTurnout( void )
+{
+ T_TURNOUT = InitObject( &turnoutCmds );
+
+ /*InitDebug( "Turnout", &debugTurnout );*/
+ AddParam( N_("TURNOUT "), ReadTurnoutParam );
+}
+
+#ifdef TEST
+
+wDrawable_t turnoutD;
+
+void wListAddValue( wList_p bl, char * val, wIcon_p, void * listData, void * itemData )
+{
+}
+
+void wListClear( wList_p bl )
+{
+}
+
+void wDrawSetScale( wDrawable_p d )
+{
+ d->scale = 1.0;
+}
+
+void wDrawClear( wDrawable_p d )
+{
+}
+
+void GetTrkCurveCenter( track_p t, coOrd *pos, DIST_T *radius )
+{
+}
+
+#ifdef NOTRACK_C
+
+track_p NewTrack( wIndex_t index, TRKTYP_T type, EPINX_T endCnt, SIZE_T extraSize )
+{
+ return NULL;
+}
+
+track_p OnTrack( coOrd *pos )
+{
+ return NULL;
+}
+
+void ErrorMessage( char * msg, ... )
+{
+ lprintf( "ERROR : %s\n", msg );
+}
+
+void DeleteTrack( track_p t )
+{
+}
+
+void ConnectTracks( track_p t0, EPINX_T ep0, track_p t1, EPINX_T ep1 )
+{
+}
+#endif
+
+main( INT_T argc, char * argv[] )
+{
+ FILE * f;
+ char line[STR_SIZE];
+ wIndex_t lineCnt = 0;
+
+ /*debugTurnout = 3;*/
+ if ((f = fopen("turnout.params", "r" )) == NULL ) {
+ Perror( "turnout.params" );
+ Exit(1);
+ }
+ while ( fgets( line, sizeof line, f ) != NULL ) {
+ lineCnt++;
+ ReadTurnoutParam( &lineCnt );
+ }
+}
+#endif