/** \file cturnout.c * 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 #include #include #include #include "ccurve.h" #include "tbezier.h" #include "cjoin.h" #include "compound.h" #include "cstraigh.h" #include "cundo.h" #include "custom.h" #include "fileio.h" #include "i18n.h" #include "layout.h" #include "messages.h" #include "param.h" #include "track.h" #include "utility.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 ); CopyPoly(to->segs,segCnt); FixUpBezierSegs(to->segs,to->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 = 0; 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; inxsegTrkLast; 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;inx1) 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;inxhandlaid = 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; inxsegCnt; 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->scaleorig, 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; ifuncs->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; segCntsegCnt && 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, NULL, NULL, NULL, NULL ); 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; segCntsegCnt && 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= 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; segCntsegCnt && 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; segInxsegCnt; 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; segInxsegCnt; segInx++ ) { if ( segIndexMap(segInx) > 0 ) segIndexMap(segInx) = ++posCnt; } DYNARR_SET( trkSeg_t, tempSegs_da, posCnt ); for ( segInx=posCnt=0; segInxsegCnt; 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; inxsegCnt; 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; 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; 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; segProcData.traverse1.pos = pos2; segProcData.traverse1.angle = -xx->angle+trvTrk->angle; SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData ); dist += segProcData.traverse1.dist; //Get ready for Traverse2 - copy all Traverse1 first BOOL_T backwards = segProcData.traverse1.backwards; BOOL_T segs_backwards = segProcData.traverse1.segs_backwards; BOOL_T neg = segProcData.traverse1.negative; int BezSegInx = segProcData.traverse1.BezSegInx; // Backwards means universally we going towards EP=0 on this segment. // But the overall direction we are going can have two types of reversal, // a curve that is flipped is negative (the end points are reversed) which Traverse1 handles, // and a path can also be reversed (negative path number) and will have segEP = 1 BOOL_T turnout_backwards = backwards; if (segEP) turnout_backwards = !turnout_backwards; //direction modified if path reversed LOG( log_traverseTurnout, 2, ( " SI%d TB%d SP%d B%d SB%d N%d BSI%d D%0.3f\n", segInx, turnout_backwards, segEP, backwards, segs_backwards, neg, BezSegInx, dist ) ) while ( *pathCurr ) { //Set up Traverse2 GetSegInxEP( pathCurr[0], &segInx, &segEP ); segPtr = xx->segs+segInx; segProcData.traverse2.segDir = backwards; segProcData.traverse2.dist = dist; segProcData.traverse2.BezSegInx = BezSegInx; segProcData.traverse2.segs_backwards = segs_backwards; 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 ); LOG( log_traverseTurnout, 2, ( " -> [%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; //Remainder after segment pathCurr += (turnout_backwards?-1:1); //Use master direction for turnout //Redrive Traverse 1 for each segment for Bezier - to pick up backwards elements if (pathCurr[0] == '\0') continue; // //Set up Traverse1 - copy all of Traverse2 values first GetSegInxEP( pathCurr[0], &segInx, &segEP ); segPtr = xx->segs+segInx; ANGLE_T angle = segProcData.traverse2.angle; coOrd pos = segProcData.traverse2.pos; LOG( log_traverseTurnout, 1, ( " Loop2-1 SI%d SP%d [%0.3f %0.3f] A%0.3f D%0.3f\n", segInx, segEP, pos.x, pos.y, angle, dist ) ) segProcData.traverse1.pos = pos; segProcData.traverse1.angle = angle; SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData ); // dist += segProcData.traverse1.dist; //Add distance from end to pos (could be zero or whole length if backwards) backwards = segProcData.traverse1.backwards; segs_backwards = segProcData.traverse1.segs_backwards; neg = segProcData.traverse1.negative; BezSegInx = segProcData.traverse1.BezSegInx; LOG( log_traverseTurnout, 1, ( " Loop1-2 B%d SB%d N%d BSI%d D%0.3f\n", backwards, segs_backwards, neg, BezSegInx, dist ) ) } pathCurr += (turnout_backwards?1:-1); pos1 = MapPathPos( xx, pathCurr[0], (turnout_backwards?0:1) ); *distR = dist; epCnt = GetTrkEndPtCnt(trk); ep = 0; dist = FindDistance( pos1, GetTrkEndPos(trk,0) ); for ( ep2=1; ep2 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; //TODO should check if last segment is actually straight if (inx == PARAMS_CORNU || inx == PARAMS_BEZIER) { params->arcR = 0.0; params->arcP = zero; params->ep = PickEndPoint(pos,trk); //Nearest if (params->ep>=0) { params->angle = GetTrkEndAngle(trk,params->ep); params->track_angle = params->angle + params->ep?0:180; } else { params->angle = params-> track_angle = 0; return FALSE; } return TRUE; } params->ep = PickUnconnectedEndPointSilent( 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: case Q_CAN_EXTEND: return TRUE; case Q_MODIFY_CAN_SPLIT: if (GetTrkEndPtCnt(trk) <= 2) { // allow splitting of simple track und buffers return TRUE ; } else { return FALSE; } 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 ); case Q_CORNU_CAN_MODIFY: return FALSE; 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, GetLayoutCurScale(), 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:"") ) ) 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; iendCnt; 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;iendCnt;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) && ( aa180.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++; } } } } else { trk = NULL; *trkR = NULL; } *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; ioff > 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 ); 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; iendCnt; 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;iendCnt;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 && aa180.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 (aatitle, 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, GetLayoutCurScale()); } xx->special = curTurnout->special; xx->u = curTurnout->u; /* Make the connections */ visible = FALSE; noConnections = TRUE; AuditTracks( "addTurnout T%d before connection", GetTrkIndex(newTrk) ); for (i=0;iendCnt;i++) { if ( connection(i).trk != NULL ) { p0 = GetTrkEndPos( newTrk, i ); p1 = GetTrkEndPos( connection(i).trk, connection(i).ep ); ANGLE_T a0 = GetTrkEndAngle( newTrk, i); ANGLE_T a1 = GetTrkEndAngle( connection(i).trk, connection(i).ep ); ANGLE_T a = NormalizeAngle(a1-a0+180); 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;iendCnt;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; ependCnt; 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, GetLayoutCurScale()) ) ) 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