summaryrefslogtreecommitdiff
path: root/app/bin/track.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2016-12-28 16:52:56 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2016-12-28 16:52:56 +0100
commit7b358424ebad9349421acd533c2fa1cbf6cf3e3e (patch)
tree686678532eefed525c242fd214d0cfb2914726c5 /app/bin/track.c
Initial import of xtrkcad version 1:4.0.2-2
Diffstat (limited to 'app/bin/track.c')
-rw-r--r--app/bin/track.c2932
1 files changed, 2932 insertions, 0 deletions
diff --git a/app/bin/track.c b/app/bin/track.c
new file mode 100644
index 0000000..30ea186
--- /dev/null
+++ b/app/bin/track.c
@@ -0,0 +1,2932 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/track.c,v 1.7 2009-07-05 15:11:02 m_fischer Exp $
+ */
+
+/* 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 <time.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <math.h>
+#include "track.h"
+#include "ccurve.h"
+#include "cstraigh.h"
+#include "cjoin.h"
+#include "compound.h"
+#include "i18n.h"
+#include "draw.h"
+
+#ifndef TRACKDEP
+#ifndef FASTTRACK
+#include "trackx.h"
+#endif
+#endif
+
+#ifndef WINDOWS
+#include <errno.h>
+#else
+// starting from Visual Studio 2015 round is in the runtime library, fake otherwise
+#if ( _MSC_VER < 1900 )
+#define round(x) floor((x)+0.5)
+#endif
+#endif
+
+static int log_track = 0;
+static int log_endPt = 0;
+static int log_readTracks = 0;
+
+/*****************************************************************************
+ *
+ * VARIABLES
+ *
+ */
+
+#define DRAW_TUNNEL_NONE (0)
+
+#define CLOSETOTHEEDGE (10) /**< minimum distance between paste position and edge of window */
+
+EXPORT wIndex_t trackCount;
+
+EXPORT long drawEndPtV = 2;
+
+EXPORT long centerDrawMode = FALSE; /**< flag to control drawing of circle centers */
+
+static BOOL_T exportingTracks = FALSE;
+
+EXPORT signed char * pathPtr;
+EXPORT int pathCnt = 0;
+EXPORT int pathMax = 0;
+
+static dynArr_t trackCmds_da;
+#define trackCmds(N) DYNARR_N( trackCmd_t*, trackCmds_da, N )
+
+EXPORT BOOL_T useCurrentLayer = FALSE;
+
+EXPORT LAYER_T curTrackLayer;
+
+EXPORT coOrd descriptionOff;
+
+EXPORT DIST_T roadbedWidth = 0.0;
+EXPORT DIST_T roadbedLineWidth = 3.0/75.0;
+
+EXPORT DIST_T minTrackRadius;
+EXPORT DIST_T maxTrackGrade = 5.0;
+
+static int suspendElevUpdates = FALSE;
+
+static track_p * importTrack;
+
+EXPORT BOOL_T onTrackInSplit;
+
+static BOOL_T inDrawTracks;
+
+#ifndef TRACKDEP
+
+/*****************************************************************************
+ *
+ *
+ *
+ */
+
+
+EXPORT void DescribeTrack( track_cp trk, char * str, CSIZE_T len )
+{
+ trackCmds( GetTrkType(trk) )->describe ( trk, str, len );
+ /*epCnt = GetTrkEndPtCnt(trk);
+ if (debugTrack >= 2)
+ for (ep=0; epCnt; ep++)
+ PrintEndPt( logFile, trk, ep );???*/
+}
+
+
+EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd pos )
+{
+ return trackCmds( GetTrkType(trk) )->distance( trk, &pos );
+}
+
+/**
+ * Check whether the track passed as parameter is close to an existing piece. Track
+ * pieces that aren't visible (in a tunnel or on an invisble layer) can be ignored,
+ * depending on flag. If there is a track closeby, the passed track is moved to that
+ * position. This implements the snap feature.
+ *
+ * \param fp IN/OUT the old and the new position
+ * \param complain IN show error message if there is no other piece of track
+ * \param track IN
+ * \param ignoreHidden IN decide whether hidden track is ignored or not
+ * \return NULL if there is no track, pointer to track otherwise
+ */
+
+EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignoreHidden )
+{
+ track_p trk;
+ DIST_T distance, closestDistance = 1000000;
+ track_p closestTrack = NULL;
+ coOrd p, closestPos, q0, q1;
+
+ q0 = q1 = * fp;
+ q0.x -= 1.0;
+ q1.x += 1.0;
+ q0.y -= 1.0;
+ q1.y += 1.0;
+ TRK_ITERATE( trk ) {
+ if ( track && !IsTrack(trk) )
+ continue;
+ if (trk->hi.x < q0.x ||
+ trk->lo.x > q1.x ||
+ trk->hi.y < q0.y ||
+ trk->lo.y > q1.y )
+ continue;
+ if ( ignoreHidden ) {
+ if ( (!GetTrkVisible(trk)) && drawTunnel == DRAW_TUNNEL_NONE)
+ continue;
+ if ( !GetLayerVisible( GetTrkLayer( trk ) ) )
+ continue;
+ }
+ p = *fp;
+ distance = trackCmds( GetTrkType(trk) )->distance( trk, &p );
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestTrack = trk;
+ closestPos = p;
+ }
+ }
+ if (closestTrack && (closestDistance <= mainD.scale*0.25 || closestDistance <= trackGauge*2.0) ) {
+ *fp = closestPos;
+ return closestTrack;
+ }
+ if (complain) {
+ ErrorMessage( MSG_PT_IS_NOT_TRK, FormatDistance(fp->x), FormatDistance(fp->y) );
+ }
+ return NULL;
+}
+
+/**
+ * Check whether the track passed as parameter is close to an existing piece. Track
+ * pieces that aren't visible (in a tunnel or on an invisble layer) are ignored,
+ * This function is basically a wrapper function to OnTrack2().
+ */
+
+
+EXPORT track_p OnTrack( coOrd * fp, BOOL_T complain, BOOL_T track )
+{
+ return OnTrack2( fp, complain, track, TRUE );
+}
+
+
+EXPORT BOOL_T CheckTrackLayer( track_p trk )
+{
+ if (GetLayerFrozen( GetTrkLayer( trk ) ) ) {
+ ErrorMessage( MSG_CANT_MODIFY_FROZEN_TRK );
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+/******************************************************************************
+ *
+ * PARTS LIST
+ *
+ */
+
+
+EXPORT void EnumerateTracks( void )
+{
+ track_p trk;
+ TRKINX_T inx;
+
+ enumerateMaxDescLen = strlen("Description");
+
+ TRK_ITERATE( trk ) {
+ /*
+ * process track piece if none are selected (list all) or if it is one of the
+ * selected pieces (list only selected )
+ */
+ if ((!selectedTrackCount || GetTrkSelected(trk)) && trackCmds(trk->type)->enumerate != NULL)
+ trackCmds(trk->type)->enumerate( trk );
+ }
+
+ EnumerateStart();
+
+ for (inx=1; inx<trackCmds_da.cnt; inx++)
+ if (trackCmds(inx)->enumerate != NULL)
+ trackCmds(inx)->enumerate( NULL );
+
+ EnumerateEnd();
+ Reset();
+}
+
+/*****************************************************************************
+ *
+ * NOTRACK
+ *
+ */
+
+static void AbortNoTrack( void )
+{
+ AbortProg( "No Track Op called" );
+}
+
+static trackCmd_t notrackCmds = {
+ "NOTRACK",
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack,
+ (void*)AbortNoTrack };
+
+EXPORT TRKTYP_T InitObject( trackCmd_t * cmds )
+{
+ DYNARR_APPEND( trackCmd_t*, trackCmds_da, 10 );
+ trackCmds(trackCmds_da.cnt-1) = cmds;
+ return trackCmds_da.cnt-1;
+}
+
+
+EXPORT TRKTYP_T T_NOTRACK = -1;
+
+EXPORT void InitTrkTrack( void )
+{
+ T_NOTRACK = InitObject( &notrackCmds );
+ log_track = LogFindIndex( "track" );
+ log_endPt = LogFindIndex( "endPt" );
+ log_readTracks = LogFindIndex( "readTracks" );
+}
+
+/*****************************************************************************
+ *
+ * TRACK FIELD ACCESS
+ *
+ */
+
+
+#ifndef FASTTRACK
+
+EXPORT TRKINX_T GetTrkIndex( track_p trk )
+{
+ return trk->index;
+}
+
+EXPORT TRKTYP_T GetTrkType( track_p trk )
+{
+ ASSERT( trk->type != T_NOTRACK && !IsTrackDeleted(trk) );
+ return trk->type;
+}
+
+EXPORT SCALEINX_T GetTrkScale( track_p trk )
+{
+ return (SCALEINX_T)trk->scale;
+}
+
+EXPORT void SetTrkScale( track_p trk, SCALEINX_T si )
+{
+ trk->scale = (char)si;
+}
+
+EXPORT LAYER_T GetTrkLayer( track_p trk )
+{
+ return trk->layer;
+}
+
+EXPORT void SetBoundingBox( track_p trk, coOrd hi, coOrd lo )
+{
+ trk->hi.x = (float)hi.x;
+ trk->hi.y = (float)hi.y;
+ trk->lo.x = (float)lo.x;
+ trk->lo.y = (float)lo.y;
+}
+
+
+EXPORT void GetBoundingBox( track_p trk, coOrd *hi, coOrd *lo )
+{
+ hi->x = (POS_T)trk->hi.x;
+ hi->y = (POS_T)trk->hi.y;
+ lo->x = (POS_T)trk->lo.x;
+ lo->y = (POS_T)trk->lo.y;
+}
+
+EXPORT EPINX_T GetTrkEndPtCnt( track_cp trk )
+{
+ return trk->endCnt;
+}
+
+EXPORT struct extraData * GetTrkExtraData( track_cp trk )
+{
+ return trk->extraData;
+}
+
+EXPORT void SetTrkEndPoint( track_p trk, EPINX_T ep, coOrd pos, ANGLE_T angle )
+{
+ if (trk->endPt[ep].track != NULL) {
+ AbortProg( "setTrkEndPoint: endPt is connected" );
+ }
+ trk->endPt[ep].pos = pos;
+ trk->endPt[ep].angle = angle;
+}
+
+EXPORT coOrd GetTrkEndPos( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].pos;
+}
+
+EXPORT ANGLE_T GetTrkEndAngle( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].angle;
+}
+
+EXPORT track_p GetTrkEndTrk( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].track;
+}
+
+EXPORT long GetTrkEndOption( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].option;
+}
+
+EXPORT long SetTrkEndOption( track_p trk, EPINX_T e, long option )
+{
+ return trk->endPt[e].option = option;
+}
+
+EXPORT int GetTrkWidth( track_p trk )
+{
+ return (int)trk->width;
+}
+
+EXPORT void SetTrkWidth( track_p trk, int width )
+{
+ trk->width = (unsigned int)width;
+}
+
+EXPORT int GetTrkBits( track_p trk )
+{
+ return trk->bits;
+}
+
+EXPORT int SetTrkBits( track_p trk, int bits )
+{
+ int oldBits = trk->bits;
+ trk->bits |= bits;
+ return oldBits;
+}
+
+EXPORT int ClrTrkBits( track_p trk, int bits )
+{
+ int oldBits = trk->bits;
+ trk->bits &= ~bits;
+ return oldBits;
+}
+
+EXPORT BOOL_T IsTrackDeleted( track_p trk )
+{
+ return trk->deleted;
+}
+#endif
+
+EXPORT void SetTrkEndElev( track_p trk, EPINX_T ep, int option, DIST_T height, char * station )
+{
+ track_p trk1;
+ EPINX_T ep1;
+ trk->endPt[ep].elev.option = option;
+ if (EndPtIsDefinedElev(trk,ep)) {
+ trk->endPt[ep].elev.u.height = height;
+ } else if (EndPtIsStationElev(trk,ep)) {
+ if (station == NULL)
+ station = "";
+ trk->endPt[ep].elev.u.name = MyStrdup(station);
+ }
+ if ( (trk1=GetTrkEndTrk(trk, ep)) != NULL ) {
+ ep1 = GetEndPtConnectedToMe( trk1, trk );
+ if (ep1 >= 0) {
+ trk1->endPt[ep1].elev.option = option;
+ trk1->endPt[ep1].elev.u.height = height;
+ if (EndPtIsDefinedElev(trk1,ep1))
+ trk1->endPt[ep1].elev.u.height = height;
+ else if (EndPtIsStationElev(trk,ep))
+ trk1->endPt[ep1].elev.u.name = MyStrdup(station);
+ }
+ }
+}
+
+
+EXPORT void GetTrkEndElev( track_p trk, EPINX_T e, int *option, DIST_T *height )
+{
+ *option = trk->endPt[e].elev.option;
+ *height = trk->endPt[e].elev.u.height;
+}
+
+
+EXPORT int GetTrkEndElevUnmaskedMode( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].elev.option;
+}
+
+
+EXPORT int GetTrkEndElevMode( track_p trk, EPINX_T e )
+{
+ return trk->endPt[e].elev.option&ELEV_MASK;
+}
+
+
+EXPORT DIST_T GetTrkEndElevHeight( track_p trk, EPINX_T e )
+{
+ ASSERT( EndPtIsDefinedElev(trk,e) );
+ return trk->endPt[e].elev.u.height;
+}
+
+
+EXPORT char * GetTrkEndElevStation( track_p trk, EPINX_T e )
+{
+ ASSERT( EndPtIsStationElev(trk,e) );
+ if ( trk->endPt[e].elev.u.name == NULL )
+ return "";
+ else
+ return trk->endPt[e].elev.u.name;
+}
+
+
+EXPORT void SetTrkEndPtCnt( track_p trk, EPINX_T cnt )
+{
+ EPINX_T oldCnt = trk->endCnt;
+ trk->endCnt = cnt;
+ if ((trk->endPt = MyRealloc( trk->endPt, trk->endCnt * sizeof trk->endPt[0] )) == NULL) {
+ AbortProg("setTrkEndPtCnt: No memory" );
+ }
+ if (oldCnt < cnt)
+ memset( &trk->endPt[oldCnt], 0, (cnt-oldCnt) * sizeof *trk->endPt );
+}
+
+
+EXPORT void SetTrkLayer( track_p trk, int layer )
+{
+ if (useCurrentLayer)
+ trk->layer = (LAYER_T)curLayer;
+ else
+ trk->layer = (LAYER_T)layer;
+}
+
+
+
+EXPORT int ClrAllTrkBits( int bits )
+{
+ track_p trk;
+ int cnt;
+ cnt = 0;
+ TRK_ITERATE( trk ) {
+ if (trk->bits&bits)
+ cnt++;
+ trk->bits &= ~bits;
+ }
+ return cnt;
+}
+
+
+
+EXPORT void SetTrkElev( track_p trk, int mode, DIST_T elev )
+{
+ SetTrkBits( trk, TB_ELEVPATH );
+ trk->elev = elev;
+ trk->elevMode = mode;
+}
+
+
+EXPORT int GetTrkElevMode( track_p trk )
+{
+ return trk->elevMode;
+}
+
+EXPORT DIST_T GetTrkElev( track_p trk )
+{
+ return trk->elev;
+}
+
+
+EXPORT void ClearElevPath( void )
+{
+ track_p trk;
+ TRK_ITERATE( trk ) {
+ ClrTrkBits( trk, TB_ELEVPATH );
+ trk->elev = 0.0;
+ }
+}
+
+
+EXPORT BOOL_T GetTrkOnElevPath( track_p trk, DIST_T * elev )
+{
+ if (trk->bits&TB_ELEVPATH) {
+ if ( elev ) *elev = trk->elev;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+EXPORT void CopyAttributes( track_p src, track_p dst )
+{
+ SetTrkScale( dst, GetTrkScale( src ) );
+ dst->bits = (dst->bits&TB_HIDEDESC) | (src->bits&~TB_HIDEDESC);
+ SetTrkWidth( dst, GetTrkWidth( src ) );
+ dst->layer = GetTrkLayer( src );
+}
+
+/*****************************************************************************
+ *
+ * ENDPOINTS
+ *
+ */
+
+
+EXPORT BOOL_T WriteEndPt( FILE * f, track_cp trk, EPINX_T ep )
+{
+ trkEndPt_p endPt = &trk->endPt[ep];
+ BOOL_T rc = TRUE;
+ long option;
+
+ assert ( endPt != NULL );
+ if (endPt->track == NULL ||
+ ( exportingTracks && !GetTrkSelected(endPt->track) ) ) {
+ rc &= fprintf( f, "\tE " )>0;
+ } else {
+ rc &= fprintf( f, "\tT %d ", endPt->track->index )>0;
+ }
+ rc &= fprintf( f, "%0.6f %0.6f %0.6f", endPt->pos.x, endPt->pos.y, endPt->angle )>0;
+ option = (endPt->option<<8) | (endPt->elev.option&0xFF);
+ if ( option != 0 ) {
+ rc &= fprintf( f, " %ld %0.6f %0.6f", option, endPt->elev.doff.x, endPt->elev.doff.y )>0;
+ if ( (endPt->elev.option&ELEV_MASK) != ELEV_NONE ) {
+ switch ( endPt->elev.option&ELEV_MASK ) {
+ case ELEV_DEF:
+ rc &= fprintf( f, " %0.6f", endPt->elev.u.height )>0;
+ break;
+ case ELEV_STATION:
+ rc &= fprintf( f, " \"%s\"", PutTitle( endPt->elev.u.name ) )>0;
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ rc &= fprintf( f, "\n" )>0;
+ return rc;
+}
+
+
+EXPORT EPINX_T PickEndPoint( coOrd p, track_cp trk )
+{
+ EPINX_T inx, i;
+ DIST_T d, dd;
+ coOrd pos;
+ if (trk->endCnt <= 0)
+ return -1;
+ if ( onTrackInSplit && trk->endCnt > 2 )
+ return TurnoutPickEndPt( p, trk );
+ d = FindDistance( p, trk->endPt[0].pos );
+ inx = 0;
+ for ( i=1; i<trk->endCnt; i++ ) {
+ pos = trk->endPt[i].pos;
+ dd=FindDistance(p, pos);
+ if (dd < d) {
+ d = dd;
+ inx = i;
+ }
+ }
+ return inx;
+}
+
+
+EXPORT EPINX_T PickUnconnectedEndPoint( coOrd p, track_cp trk )
+{
+ EPINX_T inx, i;
+ DIST_T d=0, dd;
+ coOrd pos;
+ inx = -1;
+
+ for ( i=0; i<trk->endCnt; i++ ) {
+ if (trk->endPt[i].track == NULL) {
+ pos = trk->endPt[i].pos;
+ dd=FindDistance(p, pos);
+ if (inx == -1 || dd <= d) {
+ d = dd;
+ inx = i;
+ }
+ }
+ }
+
+ if (inx == -1)
+ ErrorMessage( MSG_NO_UNCONN_EP );
+ return inx;
+}
+
+
+EXPORT EPINX_T GetEndPtConnectedToMe( track_p trk, track_p me )
+{
+ EPINX_T ep;
+ for (ep=0; ep<trk->endCnt; ep++)
+ if (trk->endPt[ep].track == me)
+ return ep;
+ return -1;
+}
+
+
+EXPORT void SetEndPts( track_p trk, EPINX_T cnt )
+{
+ EPINX_T inx;
+
+LOG1( log_readTracks, ( "SetEndPts( T%d, %d )\n", trk->index, cnt ) )
+ if (cnt > 0 && tempEndPts_da.cnt != cnt) {
+ InputError( "Incorrect number of End Points for track, read %d, expected %d.\n", FALSE, tempEndPts_da.cnt, cnt );
+ return;
+ }
+ if (tempEndPts_da.cnt) {
+ trk->endPt = (trkEndPt_p)MyMalloc( tempEndPts_da.cnt * sizeof *trk->endPt );
+ } else {
+ trk->endPt = NULL;
+ }
+ for ( inx=0; inx<tempEndPts_da.cnt; inx++ ) {
+ trk->endPt[inx].index = tempEndPts(inx).index;
+ trk->endPt[inx].pos = tempEndPts(inx).pos;
+ trk->endPt[inx].angle = tempEndPts(inx).angle;
+ trk->endPt[inx].elev = tempEndPts(inx).elev;
+ trk->endPt[inx].option = tempEndPts(inx).option;
+ }
+ trk->endCnt = tempEndPts_da.cnt;
+}
+
+
+EXPORT void MoveTrack( track_p trk, coOrd orig )
+{
+ EPINX_T ep;
+ for (ep=0; ep<trk->endCnt; ep++) {
+ trk->endPt[ep].pos.x += orig.x;
+ trk->endPt[ep].pos.y += orig.y;
+ }
+ trackCmds( trk->type )->move( trk, orig );
+}
+
+
+EXPORT void RotateTrack( track_p trk, coOrd orig, ANGLE_T angle )
+{
+ EPINX_T ep;
+ for (ep=0; ep<trk->endCnt; ep++) {
+ Rotate( &trk->endPt[ep].pos, orig, angle );
+ trk->endPt[ep].angle = NormalizeAngle( trk->endPt[ep].angle + angle );
+ }
+ trackCmds( trk->type )->rotate( trk, orig, angle );
+}
+
+
+EXPORT void RescaleTrack( track_p trk, FLOAT_T ratio, coOrd shift )
+{
+ EPINX_T ep;
+ if ( trackCmds( trk->type )->rotate == NULL )
+ return;
+ for (ep=0; ep<trk->endCnt; ep++) {
+ trk->endPt[ep].pos.x *= ratio;
+ trk->endPt[ep].pos.y *= ratio;
+ }
+ trackCmds( trk->type )->rescale( trk, ratio );
+ MoveTrack( trk, shift );
+}
+
+
+EXPORT void FlipPoint(
+ coOrd * pos,
+ coOrd orig,
+ ANGLE_T angle )
+{
+ Rotate( pos, orig, -angle );
+ pos->x = 2*orig.x - pos->x;
+ Rotate( pos, orig, angle );
+}
+
+
+EXPORT void FlipTrack(
+ track_p trk,
+ coOrd orig,
+ ANGLE_T angle )
+{
+ EPINX_T ep;
+ trkEndPt_t endPt;
+
+ for ( ep=0; ep<trk->endCnt; ep++ ) {
+ FlipPoint( &trk->endPt[ep].pos, orig, angle );
+ trk->endPt[ep].angle = NormalizeAngle( 2*angle - trk->endPt[ep].angle );
+ }
+ if ( trackCmds(trk->type)->flip )
+ trackCmds(trk->type)->flip( trk, orig, angle );
+ if ( QueryTrack( trk, Q_FLIP_ENDPTS ) ) {
+ endPt = trk->endPt[0];
+ trk->endPt[0] = trk->endPt[1];
+ trk->endPt[1] = endPt;
+ }
+}
+
+
+EXPORT EPINX_T GetNextTrk(
+ track_p trk1,
+ EPINX_T ep1,
+ track_p *Rtrk,
+ EPINX_T *Rep,
+ int mode )
+{
+ EPINX_T ep, epCnt = GetTrkEndPtCnt(trk1), epRet=-1;
+ track_p trk;
+
+ *Rtrk = NULL;
+ *Rep = 0;
+ for (ep=0; ep<epCnt; ep++) {
+ if (ep==ep1)
+ continue;
+ trk = GetTrkEndTrk( trk1, ep );
+ if (trk==NULL) {
+#ifdef LATER
+ if (isElev)
+ epRet = ep;
+#endif
+ continue;
+ }
+ if ( (mode&GNTignoreIgnore) &&
+ ((trk1->endPt[ep].elev.option&ELEV_MASK)==ELEV_IGNORE))
+ continue;
+ if (*Rtrk != NULL)
+ return -1;
+ *Rtrk = trk;
+ *Rep = GetEndPtConnectedToMe( trk, trk1 );
+ epRet = ep;
+ }
+ return epRet;
+}
+
+EXPORT BOOL_T MakeParallelTrack(
+ track_p trk,
+ coOrd pos,
+ DIST_T dist,
+ track_p * newTrkR,
+ coOrd * p0R,
+ coOrd * p1R )
+{
+ if ( trackCmds(trk->type)->makeParallel )
+ return trackCmds(trk->type)->makeParallel( trk, pos, dist, newTrkR, p0R, p1R );
+ return FALSE;
+}
+
+
+/*****************************************************************************
+ *
+ * LIST MANAGEMENT
+ *
+ */
+
+
+
+EXPORT track_p to_first = NULL;
+
+EXPORT TRKINX_T max_index = 0;
+EXPORT track_p * to_last = &to_first;
+
+static struct {
+ track_p first;
+ track_p *last;
+ wIndex_t count;
+ wIndex_t changed;
+ TRKINX_T max_index;
+ } savedTrackState;
+
+
+EXPORT void RenumberTracks( void )
+{
+ track_p trk;
+ max_index = 0;
+ for (trk=to_first; trk!=NULL; trk=trk->next) {
+ trk->index = ++max_index;
+ }
+}
+
+
+EXPORT track_p NewTrack( TRKINX_T index, TRKTYP_T type, EPINX_T endCnt, CSIZE_T extraSize )
+{
+ track_p trk;
+ EPINX_T ep;
+ trk = (track_p ) MyMalloc( sizeof *trk );
+ *to_last = trk;
+ to_last = &trk->next;
+ trk->next = NULL;
+ if (index<=0) {
+ index = ++max_index;
+ } else if (max_index < index) {
+ max_index = index;
+ }
+LOG( log_track, 1, ( "NewTrack( T%d, t%d, E%d, X%ld)\n", index, type, endCnt, extraSize ) )
+ trk->index = index;
+ trk->type = type;
+ trk->layer = curLayer;
+ trk->scale = (char)curScaleInx;
+ trk->bits = TB_VISIBLE;
+ trk->elevMode = ELEV_ALONE;
+ trk->elev = 0;
+ trk->endCnt = endCnt;
+ trk->hi.x = trk->hi.y = trk->lo.x = trk->lo.y = (float)0.0;
+ if (endCnt) {
+ trk->endPt = (trkEndPt_p)MyMalloc( endCnt * sizeof *trk->endPt );
+ for ( ep = 0; ep < endCnt; ep++ )
+ trk->endPt[ep].index = -1;
+ } else
+ trk->endPt = NULL;
+ if (extraSize) {
+ trk->extraData = MyMalloc( extraSize );
+ } else
+ trk->extraData = NULL;
+ trk->extraSize = extraSize;
+ UndoNew( trk );
+ trackCount++;
+ InfoCount( trackCount );
+ return trk;
+}
+
+
+EXPORT void FreeTrack( track_p trk )
+{
+ trackCmds(trk->type)->delete( trk );
+ if (trk->endPt)
+ MyFree(trk->endPt);
+ if (trk->extraData)
+ MyFree(trk->extraData);
+ MyFree(trk);
+}
+
+
+EXPORT void ClearTracks( void )
+{
+ track_p curr, next;
+ UndoClear();
+ ClearNote();
+ for (curr = to_first; curr; curr=next) {
+ next = curr->next;
+ FreeTrack( curr );
+ }
+ to_first = NULL;
+ to_last = &to_first;
+ max_index = 0;
+ changed = 0;
+ trackCount = 0;
+ ClearCars();
+ InfoCount( trackCount );
+}
+
+
+EXPORT track_p FindTrack( TRKINX_T index )
+{
+ track_p trk;
+ TRK_ITERATE(trk) {
+ if (trk->index == index) return trk;
+ }
+ return NULL;
+}
+
+
+EXPORT void ResolveIndex( void )
+{
+ track_p trk;
+ EPINX_T ep;
+ TRK_ITERATE(trk)
+ for (ep=0; ep<trk->endCnt; ep++)
+ if (trk->endPt[ep].index >= 0) {
+ trk->endPt[ep].track = FindTrack( trk->endPt[ep].index );
+ if (trk->endPt[ep].track == NULL) {
+ NoticeMessage( MSG_RESOLV_INDEX_BAD_TRK, _("Continue"), NULL, trk->index, ep, trk->endPt[ep].index );
+ }
+ }
+ AuditTracks( "readTracks" );
+}
+
+
+EXPORT BOOL_T DeleteTrack( track_p trk, BOOL_T all )
+{
+ EPINX_T i, ep2;
+ track_p trk2;
+LOG( log_track, 4, ( "DeleteTrack(T%d)\n", GetTrkIndex(trk) ) )
+ if (all) {
+ if (!QueryTrack(trk,Q_CANNOT_BE_ON_END)) {
+ for (i=0;i<trk->endCnt;i++) {
+ if ((trk2=trk->endPt[i].track) != NULL) {
+ if (QueryTrack(trk2,Q_CANNOT_BE_ON_END)) {
+ DeleteTrack( trk2, FALSE );
+ }
+ }
+ }
+ }
+ }
+ UndrawNewTrack( trk );
+ for (i=0;i<trk->endCnt;i++) {
+ if ((trk2=trk->endPt[i].track) != NULL) {
+ ep2 = GetEndPtConnectedToMe( trk2, trk );
+ /*UndrawNewTrack( trk2 );*/
+ DrawEndPt( &mainD, trk2, ep2, wDrawColorWhite );
+ DisconnectTracks( trk2, ep2, trk, i );
+ /*DrawNewTrack( trk2 );*/
+ if (!QueryTrack(trk2,Q_DONT_DRAW_ENDPOINT))
+ DrawEndPt( &mainD, trk2, ep2, wDrawColorBlack );
+ if ( QueryTrack(trk,Q_CANNOT_BE_ON_END) )
+ UndoJoint( trk2, ep2, trk, i );
+ ClrTrkElev( trk2 );
+ }
+ }
+ UndoDelete( trk );
+ MainRedraw();
+ trackCount--;
+ AuditTracks( "deleteTrack T%d", trk->index);
+ InfoCount( trackCount );
+ return TRUE;
+}
+
+EXPORT void SaveTrackState( void )
+{
+ savedTrackState.first = to_first;
+ savedTrackState.last = to_last;
+ savedTrackState.count = trackCount;
+ savedTrackState.changed = changed;
+ savedTrackState.max_index = max_index;
+ to_first = NULL;
+ to_last = &to_first;
+ trackCount = 0;
+ changed = 0;
+ max_index = 0;
+ SaveCarState();
+ InfoCount( trackCount );
+}
+
+EXPORT void RestoreTrackState( void )
+{
+ to_first = savedTrackState.first;
+ to_last = savedTrackState.last;
+ trackCount = savedTrackState.count;
+ changed = savedTrackState.changed;
+ max_index = savedTrackState.max_index;
+ RestoreCarState();
+ InfoCount( trackCount );
+}
+
+
+BOOL_T TrackIterate( track_p * trk )
+{
+ track_p trk1;
+ if (!*trk)
+ trk1 = to_first;
+ else
+ trk1 = (*trk)->next;
+ while (trk1 && IsTrackDeleted(trk1))
+ trk1 = trk1->next;
+ *trk = trk1;
+ return trk1 != NULL;
+}
+
+/*****************************************************************************
+ *
+ * ABOVE / BELOW
+ *
+ */
+
+static void ExciseSelectedTracks( track_p * pxtrk, track_p * pltrk )
+{
+ track_p trk, *ptrk;
+ for (ptrk=&to_first; *ptrk!=NULL; ) {
+ trk = *ptrk;
+ if (IsTrackDeleted(trk) || !GetTrkSelected(trk)) {
+ ptrk = &(*ptrk)->next;
+ continue;
+ }
+ UndoModify( *ptrk );
+ UndoModify( trk );
+ *ptrk = trk->next;
+ *pltrk = *pxtrk = trk;
+ pxtrk = &trk->next;
+ trk->next = NULL;
+ }
+ to_last = ptrk;
+}
+
+
+EXPORT void SelectAbove( void )
+{
+ track_p xtrk, ltrk;
+ if (selectedTrackCount<=0) {
+ ErrorMessage( MSG_NO_SELECTED_TRK );
+ return;
+ }
+ UndoStart( _("Move Objects Above"), "above" );
+ xtrk = NULL;
+ ExciseSelectedTracks( &xtrk, &ltrk );
+ if (xtrk) {
+ *to_last = xtrk;
+ to_last = &ltrk->next;
+ }
+ UndoEnd();
+ DrawSelectedTracks( &mainD );
+}
+
+
+EXPORT void SelectBelow( void )
+{
+ track_p xtrk, ltrk, trk;
+ coOrd lo, hi, lowest, highest;
+ if (selectedTrackCount<=0) {
+ ErrorMessage( MSG_NO_SELECTED_TRK );
+ return;
+ }
+ UndoStart( _("Mode Objects Below"), "below" );
+ xtrk = NULL;
+ ExciseSelectedTracks( &xtrk, &ltrk );
+ if (xtrk) {
+ for ( trk=xtrk; trk; trk=trk->next ) {
+ if (trk==xtrk) {
+ GetBoundingBox( trk, &highest, &lowest );
+ } else {
+ GetBoundingBox( trk, &hi, &lo );
+ if (highest.x < hi.x)
+ highest.x = hi.x;
+ if (highest.y < hi.y)
+ highest.y = hi.y;
+ if (lowest.x > lo.x)
+ lowest.x = lo.x;
+ if (lowest.y > lo.y)
+ lowest.y = lo.y;
+ }
+ ClrTrkBits( trk, TB_SELECTED );
+ }
+ ltrk->next = to_first;
+ to_first = xtrk;
+ highest.x -= lowest.x;
+ highest.y -= lowest.y;
+ DrawTracks( &mainD, 0.0, lowest, highest );
+ }
+ UndoEnd();
+}
+
+
+#include "bitmaps/above.xpm"
+#include "bitmaps/below.xpm"
+
+EXPORT void InitCmdAboveBelow( void )
+{
+ wIcon_p bm_p;
+ bm_p = wIconCreatePixMap( above_xpm );
+ AddToolbarButton( "cmdAbove", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectAbove, NULL );
+ bm_p = wIconCreatePixMap( below_xpm );
+ AddToolbarButton( "cmdBelow", bm_p, IC_SELECTED, (addButtonCallBack_t)SelectBelow, NULL );
+}
+
+/*****************************************************************************
+ *
+ * INPUT / OUTPUT
+ *
+ */
+
+
+static int bsearchRead = 0;
+static trackCmd_t **sortedCmds = NULL;
+static int CompareCmds( const void * a, const void * b )
+{
+ return strcmp( (*(trackCmd_t**)a)->name, (*(trackCmd_t**)b)->name );
+}
+
+EXPORT BOOL_T ReadTrack( char * line )
+{
+ TRKINX_T inx, lo, hi;
+ int cmp;
+if (bsearchRead) {
+ if (sortedCmds == NULL) {
+ sortedCmds = (trackCmd_t**)MyMalloc( (trackCmds_da.cnt-1) * sizeof *(trackCmd_t*)0 );
+ for (inx=1; inx<trackCmds_da.cnt; inx++)
+ sortedCmds[inx-1] = trackCmds(inx);
+ qsort( sortedCmds, trackCmds_da.cnt-1, sizeof *(trackCmd_t**)0, CompareCmds );
+ }
+
+ lo = 0;
+ hi = trackCmds_da.cnt-2;
+ do {
+ inx = (lo+hi)/2;
+ cmp = strncmp( line, sortedCmds[inx]->name, strlen(sortedCmds[inx]->name) );
+ if (cmp == 0) {
+ sortedCmds[inx]->read(line);
+ return TRUE;
+ } else if (cmp < 0) {
+ hi = inx-1;
+ } else {
+ lo = inx+1;
+ }
+ } while ( lo <= hi );
+} else {
+ for (inx=1; inx<trackCmds_da.cnt; inx++) {
+ if (strncmp( line, trackCmds(inx)->name, strlen(trackCmds(inx)->name) ) == 0 ) {
+ trackCmds(inx)->read( line );
+ return TRUE;
+ }
+ }
+}
+ if (strncmp( paramLine, "TABLEEDGE ", 10 ) == 0)
+ return ReadTableEdge( paramLine+10 );
+ if (strncmp( paramLine, "TEXT ", 5 ) == 0)
+ return ReadText( paramLine+5 );
+ return FALSE;
+}
+
+
+EXPORT BOOL_T WriteTracks( FILE * f )
+{
+ track_p trk;
+ BOOL_T rc = TRUE;
+ RenumberTracks();
+ TRK_ITERATE( trk ) {
+ rc &= trackCmds(GetTrkType(trk))->write( trk, f );
+ }
+ rc &= WriteCars( f );
+ return rc;
+}
+
+
+
+EXPORT void ImportStart( void )
+{
+ importTrack = to_last;
+}
+
+
+EXPORT void ImportEnd( void )
+{
+ track_p to_firstOld;
+ wIndex_t trackCountOld;
+ track_p trk;
+ coOrd pos;
+ wPos_t x, y;
+ wPos_t ww, hh;
+ double ymax = 0.0;
+
+ // get the current mouse position
+ GetMousePosition( &x, &y );
+ mainD.Pix2CoOrd( &mainD, x, y, &pos );
+
+ // get the size of the drawing area
+ wDrawGetSize( mainD.d, &ww, &hh );
+
+ // in case the pointer is close to the edge or above the drawing area
+ // recalculate the destination position so the pasted part remains visible
+ if( abs( y - hh ) < CLOSETOTHEEDGE ) {
+ for ( trk=*importTrack; trk; trk=trk->next ) {
+ if (!IsTrackDeleted(trk) && trk->hi.y > ymax ) {
+ ymax = trk->hi.y;
+ }
+ }
+ pos.y -= ymax;
+ }
+
+ to_firstOld = to_first;
+ to_first = *importTrack;
+ trackCountOld = trackCount;
+ ResolveIndex();
+ to_first = to_firstOld;
+ RenumberTracks();
+ DrawMapBoundingBox( FALSE );
+
+ // move the imported track into place
+ for ( trk=*importTrack; trk; trk=trk->next ) if (!IsTrackDeleted(trk)) {
+ MoveTrack( trk, pos );// mainD.orig );
+ trk->bits |= TB_SELECTED;
+ DrawTrack( trk, &mainD, wDrawColorBlack );
+ }
+ DrawMapBoundingBox( TRUE );
+ importTrack = NULL;
+ trackCount = trackCountOld;
+ InfoCount( trackCount );
+}
+
+
+EXPORT BOOL_T ExportTracks( FILE * f )
+{
+ track_p trk;
+ coOrd xlat, orig;
+
+ exportingTracks = TRUE;
+ orig = mapD.size;
+ max_index = 0;
+ TRK_ITERATE(trk) {
+ if ( GetTrkSelected(trk) ) {
+ if (trk->lo.x < orig.x)
+ orig.x = trk->lo.x;
+ if (trk->lo.y < orig.y)
+ orig.y = trk->lo.y;
+ trk->index = ++max_index;
+ }
+ }
+ orig.x -= trackGauge;
+ orig.y -= trackGauge;
+ xlat.x = - orig.x;
+ xlat.y = - orig.y;
+ TRK_ITERATE( trk ) {
+ if ( GetTrkSelected(trk) ) {
+ MoveTrack( trk, xlat );
+ trackCmds(GetTrkType(trk))->write( trk, f );
+ MoveTrack( trk, orig );
+ }
+ }
+ RenumberTracks();
+ exportingTracks = FALSE;
+ return TRUE;
+}
+
+/*******************************************************************************
+ *
+ * AUDIT
+ *
+ */
+
+
+#define SET_BIT( set, bit ) set[bit>>3] |= (1<<(bit&7))
+#define BIT_SET( set, bit ) (set[bit>>3] & (1<<(bit&7)))
+
+static FILE * auditFile = NULL;
+static BOOL_T auditStop = TRUE;
+static int auditCount = 0;
+static int auditIgnore = FALSE;
+
+static void AuditDebug( void )
+{
+}
+
+static void AuditPrint( char * msg )
+{
+ time_t clock;
+ if (auditFile == NULL) {
+ sprintf( message, "%s%s%s", workingDir, FILE_SEP_CHAR, sAuditF );
+ auditFile = fopen( message, "a+" );
+ if (auditFile == NULL) {
+ NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Audit"), message, strerror(errno) );
+ auditIgnore = TRUE;
+ return;
+ }
+ time(&clock);
+ fprintf(auditFile,"\n#==== TRACK AUDIT FAILED\n#==== %s", ctime(&clock) );
+ fprintf(auditFile,"#==== %s\n\n", msg );
+ auditCount = 0;
+ auditIgnore = FALSE;
+ }
+ fprintf(auditFile, "# " );
+ fprintf(auditFile, "%s", msg );
+ if (auditIgnore)
+ return;
+ NoticeMessage( MSG_AUDIT_PRINT_MSG, _("Ok"), NULL, msg );
+ if (++auditCount>10) {
+ if (NoticeMessage( MSG_AUDIT_PRINT_IGNORE, _("Yes"), _("No") ) )
+ auditIgnore = TRUE;
+ auditCount = 0;
+ }
+}
+
+
+EXPORT void CheckTrackLength( track_cp trk )
+{
+ DIST_T dist;
+
+ if (trackCmds(trk->type)->getLength) {
+ dist = trackCmds(trk->type)->getLength( trk );
+ } else {
+ ErrorMessage( MSG_CTL_UNK_TYPE, trk->type );
+ return;
+ }
+
+ if ( dist < minLength ) {
+ ErrorMessage( MSG_CTL_SHORT_TRK, dist );
+ }
+}
+
+
+EXPORT void AuditTracks( char * event, ... )
+{
+ va_list ap;
+ static char used[4096];
+ wIndex_t i,j;
+ track_p trk, tn;
+ BOOL_T (*auditCmd)( track_p, char * );
+ char msg[STR_SIZE], *msgp;
+
+ va_start( ap, event );
+ vsprintf( msg, event, ap );
+ va_end( ap );
+ msgp = msg+strlen(msg);
+ *msgp++ = '\n';
+
+ trackCount = 0;
+ for (i=0;i<sizeof used;i++) {
+ used[i] = 0;
+ }
+ if (*to_last) {
+ sprintf( msgp, "*to_last is not NULL (%lx)", (long)*to_last );
+ AuditPrint( msg );
+ }
+ TRK_ITERATE( trk ) {
+ trackCount++;
+ if (trk->type == T_NOTRACK) {
+ sprintf( msgp, "T%d: type is NOTRACK", trk->index );
+ AuditPrint( msg );
+ continue;
+ }
+ if (trk->index > max_index) {
+ sprintf( msgp, "T%d: index bigger than max %d\n", trk->index, max_index );
+ AuditPrint( msg );
+ }
+ if ((auditCmd = trackCmds( trk->type )->audit) != NULL) {
+ if (!auditCmd( trk, msgp ))
+ AuditPrint( msg );
+ }
+ if (trk->index < 8*sizeof used) {
+ if (BIT_SET(used,trk->index)) {
+ sprintf( msgp, "T%d: index used again\n", trk->index );
+ AuditPrint( msg );
+ }
+ SET_BIT(used, trk->index);
+ }
+ for (i=0; i<trk->endCnt; i++) {
+ if ( (tn = trk->endPt[i].track) != NULL ) {
+ if (IsTrackDeleted(trk)) {
+ sprintf( msgp, "T%d[%d]: T%d is deleted\n", trk->index, i, tn->index );
+ AuditPrint( msg );
+ trk->endPt[i].track = NULL;
+ } else {
+ for (j=0;j<tn->endCnt;j++)
+ if (tn->endPt[j].track == trk)
+ goto nextEndPt;
+ sprintf( msgp, "T%d[%d]: T%d doesn\'t point back\n", trk->index, i, tn->index );
+ AuditPrint( msg );
+ trk->endPt[i].track = NULL;
+ }
+ }
+nextEndPt:;
+ }
+ if (!trk->next) {
+ if (to_last != &trk->next) {
+ sprintf( msgp, "last track (T%d @ %lx) is not to_last (%lx)\n",
+ trk->index, (long)trk, (long)to_last );
+ AuditPrint( msg );
+ }
+ }
+ }
+ InfoCount( trackCount );
+ if (auditFile != NULL) {
+ if (auditStop)
+ if (NoticeMessage( MSG_AUDIT_WRITE_FILE, _("Yes"), _("No"))) {
+ fprintf( auditFile, "# before undo\n" );
+ WriteTracks(auditFile);
+ Rdump( auditFile );
+ if (strcmp("undoUndo",event)==0) {
+ fprintf( auditFile, "# failure in undo\n" );
+ } else if (UndoUndo()) {
+ fprintf( auditFile, "# after undo\n" );
+ WriteTracks(auditFile);
+ Rdump( auditFile );
+ } else {
+ fprintf( auditFile, "# undo stack is empty\n" );
+ }
+ }
+ if (NoticeMessage( MSG_AUDIT_ABORT, _("Yes"), _("No"))) {
+ AuditDebug();
+ exit(1);
+ }
+ fclose(auditFile);
+ auditFile = NULL;
+ }
+}
+
+
+EXPORT void ComputeRectBoundingBox( track_p trk, coOrd p0, coOrd p1 )
+{
+ trk->lo.x = (float)min(p0.x, p1.x);
+ trk->lo.y = (float)min(p0.y, p1.y);
+ trk->hi.x = (float)max(p0.x, p1.x);
+ trk->hi.y = (float)max(p0.y, p1.y);
+}
+
+
+EXPORT void ComputeBoundingBox( track_p trk )
+{
+ EPINX_T i;
+
+ if (trk->endCnt <= 0)
+ AbortProg("computeBoundingBox - endCnt<=0");
+
+ trk->hi.x = trk->lo.x = (float)trk->endPt[0].pos.x;
+ trk->hi.y = trk->lo.y = (float)trk->endPt[0].pos.y;
+ for ( i=1; i<trk->endCnt; i++ ) {
+ if (trk->endPt[i].pos.x > trk->hi.x)
+ trk->hi.x = (float)trk->endPt[i].pos.x;
+ if (trk->endPt[i].pos.y > trk->hi.y)
+ trk->hi.y = (float)trk->endPt[i].pos.y;
+ if (trk->endPt[i].pos.x < trk->lo.x)
+ trk->lo.x = (float)trk->endPt[i].pos.x;
+ if (trk->endPt[i].pos.y < trk->lo.y)
+ trk->lo.y = (float)trk->endPt[i].pos.y;
+ }
+}
+
+
+
+EXPORT DIST_T EndPtDescriptionDistance(
+ coOrd pos,
+ track_p trk,
+ EPINX_T ep )
+{
+ elev_t *e;
+ coOrd pos1;
+ track_p trk1;
+ e = &trk->endPt[ep].elev;
+ if ((e->option&ELEV_MASK)==ELEV_NONE ||
+ (e->option&ELEV_VISIBLE)==0 )
+ return 100000;
+ if ((trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk))
+ return 100000;
+ /*REORIGIN( pos1, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/
+ pos1 = GetTrkEndPos(trk,ep);
+ pos1.x += e->doff.x;
+ pos1.y += e->doff.y;
+ return FindDistance( pos1, pos );
+}
+
+
+EXPORT STATUS_T EndPtDescriptionMove(
+ track_p trk,
+ EPINX_T ep,
+ wAction_t action,
+ coOrd pos )
+{
+ static coOrd p0, p1;
+ elev_t *e, *e1;
+ wDrawColor color;
+ track_p trk1;
+
+ e = &trk->endPt[ep].elev;
+ switch (action) {
+ case C_DOWN:
+ p0 = GetTrkEndPos(trk,ep);
+ /*REORIGIN( p0, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/
+
+ case C_MOVE:
+ case C_UP:
+ if (action != C_DOWN)
+ DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
+ color = GetTrkColor( trk, &mainD );
+ DrawEndElev( &tempD, trk, ep, color );
+ p1 = pos;
+ e->doff.x = (pos.x-p0.x);
+ e->doff.y = (pos.y-p0.y);
+ if ((trk1=GetTrkEndTrk(trk,ep))) {
+ e1 = &trk1->endPt[GetEndPtConnectedToMe(trk1,trk)].elev;
+ e1->doff = e->doff;
+ }
+ DrawEndElev( &tempD, trk, ep, color );
+ if (action != C_UP)
+ DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
+ MainRedraw();
+ return action==C_UP?C_TERMINATE:C_CONTINUE;
+
+ case C_REDRAW:
+ DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
+ break;
+ }
+ return C_CONTINUE;
+}
+
+
+/*****************************************************************************
+ *
+ * TRACK SPLICING ETC
+ *
+ */
+
+
+static DIST_T distanceEpsilon = 0.0;
+static ANGLE_T angleEpsilon = 0.0;
+
+EXPORT void LoosenTracks( void )
+{
+ track_p trk, trk1;
+ EPINX_T ep0, ep1;
+ ANGLE_T angle0, angle1;
+ coOrd pos0, pos1;
+ DIST_T d;
+ ANGLE_T a;
+ int count;
+
+ count = 0;
+ TRK_ITERATE(trk) {
+ for (ep0=0; ep0<trk->endCnt; ep0++) {
+ trk1 = GetTrkEndTrk( trk, ep0 );
+ if (trk1 == NULL)
+ continue;
+ ASSERT( !IsTrackDeleted(trk1) );
+ ep1 = GetEndPtConnectedToMe( trk1, trk );
+ if (ep1 < 0)
+ continue;
+ pos0 = GetTrkEndPos( trk, ep0 );
+ pos1 = GetTrkEndPos( trk1, ep1 );
+ angle0 = GetTrkEndAngle( trk, ep0 );
+ angle1 = GetTrkEndAngle( trk1, ep1 );
+ d = FindDistance( pos0, pos1 );
+ a = NormalizeAngle( 180+angle0-angle1+angleEpsilon );
+ if (d > distanceEpsilon || a > angleEpsilon*2.0) {
+ DisconnectTracks( trk, ep0, trk1, ep1 );
+ count++;
+ InfoMessage( _("%d Track(s) loosened"), count );
+ }
+ }
+ }
+ if (count)
+ MainRedraw();
+ else
+ InfoMessage(_("No tracks loosened"));
+}
+
+EXPORT void ConnectTracks( track_p trk0, EPINX_T inx0, track_p trk1, EPINX_T inx1 )
+{
+ DIST_T d;
+ ANGLE_T a;
+ coOrd pos0, pos1;
+
+ if ( !IsTrack(trk0) ) {
+ NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk0), GetTrkIndex(trk1) );
+ return;
+ }
+ if ( !IsTrack(trk1) ) {
+ NoticeMessage( _("Connecting a non-track(%d) to (%d)"), _("Continue"), NULL, GetTrkIndex(trk1), GetTrkIndex(trk0) );
+ return;
+ }
+ pos0 = trk0->endPt[inx0].pos;
+ pos1 = trk1->endPt[inx1].pos;
+LOG( log_track, 3, ( "ConnectTracks( T%d[%d] @ [%0.3f, %0.3f] = T%d[%d] @ [%0.3f %0.3f]\n", trk0->index, inx0, pos0.x, pos0.y, trk1->index, inx1, pos1.x, pos1.y ) )
+ d = FindDistance( pos0, pos1 );
+ a = NormalizeAngle( trk0->endPt[inx0].angle -
+ trk1->endPt[inx1].angle + (180.0+connectAngle/2.0) );
+ if (d > connectDistance || a > connectAngle || logTable(log_endPt).level>=1) {
+#ifndef WINDOWS
+ LogPrintf( "connectTracks: T%d[%d] T%d[%d] d=%0.3f a=%0.3f\n %d ",
+ trk0->index, inx0, trk1->index, inx1, d, a, trk0->index );
+ /*PrintEndPt( logFile, trk0, 0 );
+ PrintEndPt( logFile, trk0, 1 );???*/
+ LogPrintf( "\n %d ", trk1->index );
+ /*PrintEndPt( logFile, trk1, 0 );
+ PrintEndPt( logFile, trk1, 1 );???*/
+ LogPrintf("\n");
+#endif
+ NoticeMessage( MSG_CONNECT_TRK, _("Continue"), NULL, trk0->index, inx0, trk1->index, inx1, d, a );
+ }
+ UndoModify( trk0 );
+ UndoModify( trk1 );
+ if (!suspendElevUpdates)
+ SetTrkElevModes( TRUE, trk0, inx0, trk1, inx1 );
+ trk0->endPt[inx0].track = trk1;
+ trk1->endPt[inx1].track = trk0;
+ AuditTracks( "connectTracks T%d[%d], T%d[%d]", trk0->index, inx0, trk1->index, inx1 );
+}
+
+
+EXPORT void DisconnectTracks( track_p trk1, EPINX_T ep1, track_p trk2, EPINX_T ep2 )
+{
+ if (trk1->endPt[ep1].track != trk2 ||
+ trk2->endPt[ep2].track != trk1 )
+ AbortProg("disconnectTracks: tracks not connected" );
+ UndoModify( trk1 );
+ UndoModify( trk2 );
+ trk1->endPt[ep1].track = NULL;
+ trk2->endPt[ep2].track = NULL;
+ if (!suspendElevUpdates)
+ SetTrkElevModes( FALSE, trk1, ep1, trk2, ep2 );
+}
+
+
+EXPORT BOOL_T ConnectAbuttingTracks(
+ track_p trk0,
+ EPINX_T ep0,
+ track_p trk1,
+ EPINX_T ep1 )
+{
+ DIST_T d;
+ ANGLE_T a;
+ d = FindDistance( GetTrkEndPos(trk0,ep0),
+ GetTrkEndPos(trk1,ep1 ) );
+ a = NormalizeAngle( GetTrkEndAngle(trk0,ep0) -
+ GetTrkEndAngle(trk1,ep1) +
+ (180.0+connectAngle/2.0) );
+ if ( a < connectAngle &&
+ d < connectDistance ) {
+ UndoStart( _("Join Abutting Tracks"), "ConnectAbuttingTracks( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 );
+ DrawEndPt( &mainD, trk0, ep0, wDrawColorWhite );
+ DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite );
+ ConnectTracks( trk0, ep0,
+ trk1, ep1 );
+ DrawEndPt( &mainD, trk0, ep0, wDrawColorBlack );
+ DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack );
+ UndoEnd();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+EXPORT ANGLE_T GetAngleAtPoint( track_p trk, coOrd pos, EPINX_T *ep0, EPINX_T *ep1 )
+{
+ ANGLE_T (*getAngleCmd)( track_p, coOrd, EPINX_T *, EPINX_T * );
+
+ if ((getAngleCmd = trackCmds(trk->type)->getAngle) != NULL)
+ return getAngleCmd( trk, pos, ep0, ep1 );
+ else {
+ NoticeMessage( MSG_GAAP_BAD_TYPE, _("Continue"), NULL, trk->type, trk->index );
+ return 0;
+ }
+}
+
+
+EXPORT BOOL_T SplitTrack( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, BOOL_T disconnect )
+{
+ DIST_T d;
+ track_p trk0, trk2, trkl;
+ EPINX_T epl, ep0, ep1, ep2=-1, epCnt;
+ BOOL_T rc;
+ BOOL_T (*splitCmd)( track_p, coOrd, EPINX_T, track_p *, EPINX_T *, EPINX_T * );
+ coOrd pos0;
+
+ trk0 = trk;
+ epl = ep;
+ epCnt = GetTrkEndPtCnt(trk);
+ *leftover = NULL;
+LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos.x, pos.y ) )
+
+ if ((splitCmd = trackCmds(trk->type)->split) == NULL) {
+ ErrorMessage( MSG_CANT_SPLIT_TRK, trackCmds(trk->type)->name );
+ return FALSE;
+ }
+ UndrawNewTrack( trk );
+ UndoModify( trk );
+ pos0 = trk->endPt[ep].pos;
+ if ((d = FindDistance( pos0, pos )) <= minLength) {
+ /* easy: just disconnect */
+ if ((trk2=trk->endPt[ep].track) != NULL) {
+ UndrawNewTrack( trk2 );
+ ep2 = GetEndPtConnectedToMe( trk2, trk );
+ if (ep2 < 0)
+ return FALSE;
+ DisconnectTracks( trk, ep, trk2, ep2 );
+ LOG( log_track, 2, ( " at endPt with T%d[%d]\n", trk2->index, ep2 ) )
+ DrawNewTrack( trk2 );
+ } else {
+ LOG( log_track, 2, ( " at endPt (no connection)\n") )
+ }
+ *leftover = trk2;
+ DrawNewTrack( trk );
+
+#ifdef LATER
+ } else if ( IsTurnout(trk) ) {
+ ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") );
+ return FALSE;
+#endif
+
+ } else if ( epCnt == 2 &&
+ (d = FindDistance( trk->endPt[1-ep].pos, pos )) <= minLength) {
+ /* easy: just disconnect */
+ if ((trk2=trk->endPt[1-ep].track) != NULL) {
+ UndrawNewTrack( trk2 );
+ ep2 = GetEndPtConnectedToMe( trk2, trk );
+ if (ep2 < 0)
+ return FALSE;
+ DisconnectTracks( trk, 1-ep, trk2, ep2 );
+ LOG( log_track, 2, ( " at endPt with T%d[%d]\n", trk2->index, ep2 ) )
+ DrawNewTrack( trk2 );
+#ifdef LATER
+ *trk = trk2;
+ *ep = ep1;
+ *leftover = trk;
+#endif
+ } else {
+#ifdef LATER
+ *trk = NULL;
+#endif
+ LOG( log_track, 2, ( " at endPt (no connection)\n") )
+ }
+ DrawNewTrack( trk );
+
+ } else {
+ /* TODO circle's don't have ep's */
+ trk2 = GetTrkEndTrk( trk, ep );
+ if ( !disconnect )
+ suspendElevUpdates = TRUE;
+ if (trk2 != NULL) {
+ ep2 = GetEndPtConnectedToMe( trk2, trk );
+ DisconnectTracks( trk, ep, trk2, ep2 );
+ }
+ rc = splitCmd( trk, pos, ep, leftover, &epl, &ep1 );
+ if (!rc) {
+ if ( trk2 != NULL )
+ ConnectTracks( trk, ep, trk2, ep2 );
+ suspendElevUpdates = FALSE;
+ DrawNewTrack( trk );
+ return FALSE;
+ }
+ ClrTrkElev( trk );
+ if (*leftover) {
+ trkl = *leftover;
+ ep0 = epl;
+ if ( !disconnect )
+ ConnectTracks( trk, ep, trkl, ep0 );
+ ep0 = 1-ep0;
+ while ( 1 ) {
+ CopyAttributes( trk, trkl );
+ ClrTrkElev( trkl );
+ trk0 = GetTrkEndTrk(trkl,ep0);
+ if ( trk0 == NULL )
+ break;
+ ep0 = 1-GetEndPtConnectedToMe(trk0,trkl);
+ trkl = trk0;
+ }
+ if (trk2)
+ ConnectTracks( trkl, ep0, trk2, ep2 );
+ LOG( log_track, 2, ( " midTrack (leftover = T%d)\n", (trkl)->index ) )
+ }
+ suspendElevUpdates = FALSE;
+ DrawNewTrack( trk );
+ if (*leftover) {
+ trkl = *leftover;
+ ep0 = 1-epl;
+ while ( 1 ) {
+ DrawNewTrack( trkl );
+ trk0 = GetTrkEndTrk(trkl,ep0);
+ if ( trk0 == NULL || trk0 == trk2 )
+ break;
+ ep0 = 1-GetEndPtConnectedToMe(trk0,trkl);
+ trkl = trk0;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+EXPORT BOOL_T TraverseTrack(
+ traverseTrack_p trvTrk,
+ DIST_T * distR )
+{
+ track_p oldTrk;
+ EPINX_T ep;
+
+ while ( *distR > 0.0 && trvTrk->trk ) {
+ if ( trackCmds((trvTrk->trk)->type)->traverse == NULL )
+ return FALSE;
+ oldTrk = trvTrk->trk;
+ if ( !trackCmds((trvTrk->trk)->type)->traverse( trvTrk, distR ) )
+ return FALSE;
+ if ( *distR <= 0.0 )
+ return TRUE;
+ if ( !trvTrk->trk )
+ return FALSE;
+ ep = GetEndPtConnectedToMe( trvTrk->trk, oldTrk );
+ if ( ep != -1 ) {
+ trvTrk->pos = GetTrkEndPos( trvTrk->trk, ep );
+ trvTrk->angle = NormalizeAngle( GetTrkEndAngle( trvTrk->trk, ep ) + 180.0 );
+ }
+ if ( trackCmds((trvTrk->trk)->type)->checkTraverse &&
+ !trackCmds((trvTrk->trk)->type)->checkTraverse( trvTrk->trk, trvTrk->pos ) )
+ return FALSE;
+ trvTrk->length = -1;
+ trvTrk->dist = 0.0;
+ }
+ return TRUE;
+}
+
+
+EXPORT BOOL_T RemoveTrack( track_p * trk, EPINX_T * ep, DIST_T *dist )
+{
+ DIST_T dist1;
+ track_p trk1;
+ EPINX_T ep1=-1;
+ while ( *dist > 0.0 ) {
+ if (trackCmds((*trk)->type)->getLength == NULL)
+ return FALSE;
+ if (GetTrkEndPtCnt(*trk) != 2)
+ return FALSE;
+ dist1 = trackCmds((*trk)->type)->getLength(*trk);
+ if ( dist1 > *dist )
+ break;
+ *dist -= dist1;
+ trk1 = GetTrkEndTrk( *trk, 1-*ep );
+ if (trk1)
+ ep1 = GetEndPtConnectedToMe( trk1, *trk );
+ DeleteTrack( *trk, FALSE );
+ if (!trk1)
+ return FALSE;
+ *trk = trk1;
+ *ep = ep1;
+ }
+ dist1 = *dist;
+ *dist = 0.0;
+ return TrimTrack( *trk, *ep, dist1 );
+}
+
+
+EXPORT BOOL_T TrimTrack( track_p trk, EPINX_T ep, DIST_T dist )
+{
+ if (trackCmds(trk->type)->trim)
+ return trackCmds(trk->type)->trim( trk, ep, dist );
+ else
+ return FALSE;
+}
+
+
+EXPORT BOOL_T MergeTracks( track_p trk0, EPINX_T ep0, track_p trk1, EPINX_T ep1 )
+{
+ if (trk0->type == trk1->type &&
+ trackCmds(trk0->type)->merge)
+ return trackCmds(trk0->type)->merge( trk0, ep0, trk1, ep1 );
+ else
+ return FALSE;
+}
+
+
+EXPORT STATUS_T ExtendStraightFromOrig( track_p trk, wAction_t action, coOrd pos )
+{
+ static EPINX_T ep;
+ static BOOL_T valid;
+ DIST_T d;
+ track_p trk1;
+
+ switch ( action ) {
+ case C_DOWN:
+ ep = PickUnconnectedEndPoint( pos, trk );
+ if ( ep == -1 )
+ return C_ERROR;
+ tempSegs(0).type = SEG_STRTRK;
+ tempSegs(0).width = 0;
+ tempSegs(0).u.l.pos[0] = GetTrkEndPos( trk, ep );
+ InfoMessage( _("Drag to change track length") );
+
+ case C_MOVE:
+ d = FindDistance( tempSegs(0).u.l.pos[0], pos );
+ valid = TRUE;
+ if ( d <= minLength ) {
+ if (action == C_MOVE)
+ ErrorMessage( MSG_TRK_TOO_SHORT, _("Connecting "), PutDim(fabs(minLength-d)) );
+ valid = FALSE;
+ return C_CONTINUE;
+ }
+ 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( _("Straight: Length=%s Angle=%0.3f"),
+ FormatDistance( d ), PutAngle( GetTrkEndAngle( trk, ep ) ) );
+ return C_CONTINUE;
+
+ case C_UP:
+ if (!valid)
+ return C_TERMINATE;
+ UndrawNewTrack( trk );
+ trk1 = NewStraightTrack( tempSegs(0).u.l.pos[0], tempSegs(0).u.l.pos[1] );
+ CopyAttributes( trk, trk1 );
+ ConnectTracks( trk, ep, trk1, 0 );
+ DrawNewTrack( trk );
+ DrawNewTrack( trk1 );
+ return C_TERMINATE;
+
+ default:
+ ;
+ }
+ return C_ERROR;
+}
+
+
+EXPORT STATUS_T ModifyTrack( track_p trk, wAction_t action, coOrd pos )
+{
+ if ( trackCmds(trk->type)->modify ) {
+ ClrTrkElev( trk );
+ return trackCmds(trk->type)->modify( trk, action, pos );
+ } else {
+ return C_TERMINATE;
+ }
+}
+
+
+EXPORT BOOL_T GetTrackParams( int inx, track_p trk, coOrd pos, trackParams_t * params )
+{
+ if ( trackCmds(trk->type)->getTrackParams ) {
+ return trackCmds(trk->type)->getTrackParams( inx, trk, pos, params );
+ } else {
+ ASSERT( FALSE ); /* CHECKME */
+#ifdef LATER
+ switch ( inx ) {
+ case PARAMS_1ST_JOIN:
+ case PARAMS_2ND_JOIN:
+ ErrorMessage( MSG_JOIN_TRK, (inx==PARAMS_1ST_JOIN?_("First"):_("Second")) );
+ break;
+ case PARAMS_EXTEND:
+ ErrorMessage( MSG_CANT_EXTEND );
+ break;
+ case PARAMS_PARALLEL:
+ ErrorMessage( MSG_INV_TRK_PARALLEL );
+ break;
+ default:
+ ErrorMessage( MSG_INVALID_TRK );
+ }
+#endif
+ return FALSE;
+ }
+}
+
+
+EXPORT BOOL_T MoveEndPt( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d )
+{
+ if ( trackCmds((*trk)->type)->moveEndPt ) {
+ return trackCmds((*trk)->type)->moveEndPt( trk, ep, pos, d );
+ } else {
+ ErrorMessage( MSG_MEP_INV_TRK, GetTrkType(*trk) );
+ return FALSE;
+ }
+}
+
+
+EXPORT BOOL_T QueryTrack( track_p trk, int query )
+{
+ if ( trackCmds(trk->type)->query ) {
+ return trackCmds(trk->type)->query( trk, query );
+ } else {
+ return FALSE;
+ }
+}
+
+
+EXPORT BOOL_T IsTrack( track_p trk )
+{
+ return ( trk && QueryTrack( trk, Q_ISTRACK ) );
+}
+
+
+EXPORT void UngroupTrack( track_p trk )
+{
+ if ( trackCmds(trk->type)->ungroup ) {
+ trackCmds(trk->type)->ungroup( trk );
+ }
+}
+
+
+EXPORT char * GetTrkTypeName( track_p trk )
+{
+ return trackCmds(trk->type)->name;
+}
+
+
+EXPORT DIST_T GetFlexLength( track_p trk0, EPINX_T ep, coOrd * pos )
+{
+ track_p trk = trk0, trk1;
+ EPINX_T ep1;
+ DIST_T d, dd;
+
+ d = 0.0;
+ while(1) {
+ trk1 = GetTrkEndTrk( trk, ep );
+ if (trk1 == NULL)
+ break;
+ if (trk1 == trk0)
+ break;
+ ep1 = GetEndPtConnectedToMe( trk1, trk );
+ if (ep1 < 0 || ep1 > 1)
+ break;
+ if (trackCmds(trk1->type)->getLength == NULL)
+ break;
+ dd = trackCmds(trk1->type)->getLength(trk1);
+ if (dd <= 0.0)
+ break;
+ d += dd;
+ trk = trk1;
+ ep = 1-ep1;
+ if (d>1000000.0)
+ break;
+ }
+ *pos = GetTrkEndPos( trk, ep );
+ return d;
+}
+
+
+EXPORT DIST_T GetTrkLength( track_p trk, EPINX_T ep0, EPINX_T ep1 )
+{
+ coOrd pos0, pos1;
+ DIST_T d;
+ if (ep0 == ep1)
+ return 0.0;
+ else if (trackCmds(trk->type)->getLength != NULL) {
+ d = trackCmds(trk->type)->getLength(trk);
+ if (ep1==-1)
+ d /= 2.0;
+ return d;
+ } else {
+ pos0 = GetTrkEndPos(trk,ep0);
+ if (ep1==-1) {
+ pos1.x = (trk->hi.x+trk->lo.x)/2.0;
+ pos1.y = (trk->hi.y+trk->lo.y)/2.0;
+ } else {
+ pos1 = GetTrkEndPos(trk,ep1);
+ }
+ pos1.x -= pos0.x;
+ pos1.y -= pos0.y;
+ Rotate( &pos1, zero, -GetTrkEndAngle(trk,ep0) );
+ return fabs(pos1.y);
+ }
+}
+#endif
+/*#define DRAW_TUNNEL_NONE (0)*/
+#define DRAW_TUNNEL_DASH (1)
+#define DRAW_TUNNEL_SOLID (2)
+EXPORT long drawTunnel = DRAW_TUNNEL_DASH;
+
+/******************************************************************************
+ *
+ * SIMPLE DRAWING
+ *
+ */
+
+EXPORT long tieDrawMode = TIEDRAWMODE_SOLID;
+EXPORT wDrawColor tieColor;
+
+EXPORT void DrawTie(
+ drawCmd_p d,
+ coOrd pos,
+ ANGLE_T angle,
+ DIST_T length,
+ DIST_T width,
+ wDrawColor color,
+ BOOL_T solid )
+{
+ coOrd p[4], lo, hi;
+
+ length /= 2;
+ width /= 2;
+ lo = hi = pos;
+ lo.x -= length;
+ lo.y -= length;
+ hi.x += length;
+ hi.y += length;
+ angle += 90;
+ Translate( &p[0], pos, angle, length );
+ Translate( &p[1], p[0], angle+90, width );
+ Translate( &p[0], p[0], angle-90, width );
+ Translate( &p[2], pos, angle+180, length );
+ Translate( &p[3], p[2], angle-90, width );
+ Translate( &p[2], p[2], angle+90, width );
+#ifdef LATER
+ lo = hi = p[0];
+ for ( i=1; i<4; i++ ) {
+ if ( p[i].x < lo.x ) lo.x = p[i].x;
+ if ( p[i].y < lo.y ) lo.y = p[i].y;
+ if ( p[i].x > hi.x ) hi.x = p[i].x;
+ if ( p[i].y > hi.y ) hi.y = p[i].y;
+ }
+#endif
+ if ( d == &mainD ) {
+ lo.x -= RBORDER/mainD.dpi*mainD.scale;
+ lo.y -= TBORDER/mainD.dpi*mainD.scale;
+ hi.x += LBORDER/mainD.dpi*mainD.scale;
+ hi.y += BBORDER/mainD.dpi*mainD.scale;
+ if ( OFF_D( d->orig, d->size, lo, hi ) )
+ return;
+ }
+ if ( solid ) {
+ DrawFillPoly( d, 4, p, color );
+ } else {
+ DrawLine( d, p[0], p[1], 0, color );
+ DrawLine( d, p[1], p[2], 0, color );
+ DrawLine( d, p[2], p[3], 0, color );
+ DrawLine( d, p[3], p[0], 0, color );
+ }
+}
+
+
+EXPORT void DrawCurvedTies(
+ drawCmd_p d,
+ track_p trk,
+ coOrd p,
+ DIST_T r,
+ ANGLE_T a0,
+ ANGLE_T a1,
+ wDrawColor color )
+{
+ tieData_p td = GetScaleTieData(GetTrkScale(trk));
+ DIST_T len;
+ ANGLE_T ang, dang;
+ coOrd pos;
+ int cnt;
+
+ if ( (d->funcs->options&wDrawOptTemp) != 0 )
+ return;
+ if ( trk == NULL )
+ return;
+ if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID )
+ return;
+ if (color == wDrawColorBlack)
+ color = tieColor;
+ len = 2*M_PI*r*a1/360.0;
+ cnt = (int)(len/td->spacing);
+ if ( len-td->spacing*cnt-td->width > (td->spacing-td->width)/2 )
+ cnt++;
+ if ( cnt != 0 ) {
+ dang = a1/cnt;
+ for ( ang=a0+dang/2; cnt; cnt--,ang+=dang ) {
+ PointOnCircle( &pos, p, r, ang );
+ DrawTie( d, pos, ang+90, td->length, td->width, color, tieDrawMode==TIEDRAWMODE_SOLID );
+ }
+ }
+}
+
+
+EXPORT void DrawCurvedTrack(
+ drawCmd_p d,
+ coOrd p,
+ DIST_T r,
+ ANGLE_T a0,
+ ANGLE_T a1,
+ coOrd p0,
+ coOrd p1,
+ track_p trk,
+ DIST_T trackGauge,
+ wDrawColor color,
+ long options )
+{
+ DIST_T scale2rail;
+ wDrawWidth width=0;
+ trkSeg_p segPtr;
+
+ if ( (d->options&DC_SEGTRACK) ) {
+ DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
+ segPtr = &tempSegs(tempSegs_da.cnt-1);
+ segPtr->type = SEG_CRVTRK;
+ segPtr->width = 0;
+ segPtr->color = wDrawColorBlack;
+ segPtr->u.c.center = p;
+ segPtr->u.c.a0 = a0;
+ segPtr->u.c.a1 = a1;
+ segPtr->u.c.radius = r;
+ return;
+ }
+
+ scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
+ if (options&DTS_THICK2)
+ width = 2;
+ if (options&DTS_THICK3)
+ width = 3;
+#ifdef WINDOWS
+ width *= (wDrawWidth)(d->dpi/mainD.dpi);
+#else
+ if (d->options&DC_PRINT)
+ width *= 300/75;
+#endif
+
+LOG( log_track, 4, ( "DST( (%0.3f %0.3f) R%0.3f A%0.3f..%0.3f)\n",
+ p.x, p.y, r, a0, a1 ) )
+ if ( (options&DTS_TIES) != 0 && trk &&
+ tieDrawMode!=TIEDRAWMODE_NONE &&
+ d!=&mapD &&
+ (d->options&DC_TIES)!=0 &&
+ d->scale<scale2rail/2 )
+ DrawCurvedTies( d, trk, p, r, a0, a1, color );
+ if (color == wDrawColorBlack)
+ color = normalColor;
+ if ( d->scale >= scale2rail ) {
+ DrawArc( d, p, r, a0, a1, ((d->scale<32) && centerDrawMode && !(options&DTS_NOCENTER)) ? 1 : 0, width, color );
+ } else if (d->options & DC_QUICK) {
+ DrawArc( d, p, r, a0, a1, ((d->scale<32) && centerDrawMode && !(options&DTS_NOCENTER)) ? 1 : 0, 0, color );
+ } else {
+ if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) {
+ long options = d->options;
+ d->options |= DC_DASH;
+ DrawArc( d, p, r, a0, a1, 0, 0, color );
+ d->options = options;
+ }
+ DrawArc( d, p, r+trackGauge/2.0, a0, a1, 0, width, color );
+ DrawArc( d, p, r-trackGauge/2.0, a0, a1, (centerDrawMode && !(options&DTS_NOCENTER) ? 1: 0), width, color );
+ if ( (d->options&DC_PRINT) && roadbedWidth > trackGauge && d->scale <= scale2rail/2 ) {
+ wDrawWidth rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5);
+ if ( options&DTS_RIGHT ) {
+ DrawArc( d, p, r+roadbedWidth/2.0, a0, a1, 0, rbw, color );
+ }
+ if ( options&DTS_LEFT ) {
+ DrawArc( d, p, r-roadbedWidth/2.0, a0, a1, 0, rbw, color );
+ }
+ }
+ }
+}
+
+
+EXPORT void DrawStraightTies(
+ drawCmd_p d,
+ track_p trk,
+ coOrd p0,
+ coOrd p1,
+ wDrawColor color )
+{
+ tieData_p td = GetScaleTieData(GetTrkScale(trk));
+ DIST_T tieOff0=0.0, tieOff1=0.0;
+ DIST_T len, dlen;
+ coOrd pos;
+ int cnt;
+ ANGLE_T angle;
+
+ if ( (d->funcs->options&wDrawOptTemp) != 0 )
+ return;
+ if ( trk == NULL )
+ return;
+ if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID )
+ return;
+ if ( color == wDrawColorBlack )
+ color = tieColor;
+ td = GetScaleTieData( GetTrkScale(trk) );
+ len = FindDistance( p0, p1 );
+ len -= tieOff0+tieOff1;
+ angle = FindAngle( p0, p1 );
+ cnt = (int)(len/td->spacing);
+ if ( len-td->spacing*cnt-td->width > (td->spacing-td->width)/2 )
+ cnt++;
+ if ( cnt != 0 ) {
+ dlen = len/cnt;
+ for ( len=dlen/2; cnt; cnt--,len+=dlen ) {
+ Translate( &pos, p0, angle, len );
+ DrawTie( d, pos, angle, td->length, td->width, color, tieDrawMode==TIEDRAWMODE_SOLID );
+ }
+ }
+}
+
+
+EXPORT void DrawStraightTrack(
+ drawCmd_p d,
+ coOrd p0,
+ coOrd p1,
+ ANGLE_T angle,
+ track_p trk,
+ DIST_T trackGauge,
+ wDrawColor color,
+ long options )
+{
+ coOrd pp0, pp1;
+ DIST_T scale2rail;
+ wDrawWidth width=0;
+ trkSeg_p segPtr;
+
+ if ( (d->options&DC_SEGTRACK) ) {
+ DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
+ segPtr = &tempSegs(tempSegs_da.cnt-1);
+ segPtr->type = SEG_STRTRK;
+ segPtr->width = 0;
+ segPtr->color = wDrawColorBlack;
+ segPtr->u.l.pos[0] = p0;
+ segPtr->u.l.pos[1] = p1;
+ segPtr->u.l.angle = angle;
+ segPtr->u.l.option = 0;
+ return;
+ }
+
+ scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
+
+ if (options&DTS_THICK2)
+ width = 2;
+ if (options&DTS_THICK3)
+ width = 3;
+#ifdef WINDOWS
+ width *= (wDrawWidth)(d->dpi/mainD.dpi);
+#else
+ if (d->options&DC_PRINT)
+ width *= 300/75;
+#endif
+LOG( log_track, 4, ( "DST( (%0.3f %0.3f) .. (%0.3f..%0.3f)\n",
+ p0.x, p0.y, p1.x, p1.y ) )
+ if ( (options&DTS_TIES) != 0 && trk &&
+ tieDrawMode!=TIEDRAWMODE_NONE &&
+ d!=&mapD &&
+ (d->options&DC_TIES)!=0 &&
+ d->scale<scale2rail/2 )
+ DrawStraightTies( d, trk, p0, p1, color );
+ if (color == wDrawColorBlack)
+ color = normalColor;
+ if ( d->scale >= scale2rail ) {
+ DrawLine( d, p0, p1, width, color );
+ } else if (d->options&DC_QUICK) {
+ DrawLine( d, p0, p1, 0, color );
+ } else {
+ if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) {
+ long options = d->options;
+ d->options |= DC_DASH;
+ DrawLine( d, p0, p1, 0, color );
+ d->options = options;
+ }
+ Translate( &pp0, p0, angle+90, trackGauge/2.0 );
+ Translate( &pp1, p1, angle+90, trackGauge/2.0 );
+ DrawLine( d, pp0, pp1, width, color );
+ Translate( &pp0, p0, angle-90, trackGauge/2.0 );
+ Translate( &pp1, p1, angle-90, trackGauge/2.0 );
+ DrawLine( d, pp0, pp1, width, color );
+ if ( (d->options&DC_PRINT) && roadbedWidth > trackGauge && d->scale <= scale2rail/2.0) {
+ wDrawWidth rbw = (wDrawWidth)floor(roadbedLineWidth*(d->dpi/d->scale)+0.5);
+ if ( options&DTS_RIGHT ) {
+ Translate( &pp0, p0, angle+90, roadbedWidth/2.0 );
+ Translate( &pp1, p1, angle+90, roadbedWidth/2.0 );
+ DrawLine( d, pp0, pp1, rbw, color );
+ }
+ if ( options&DTS_LEFT ) {
+ Translate( &pp0, p0, angle-90, roadbedWidth/2.0 );
+ Translate( &pp1, p1, angle-90, roadbedWidth/2.0 );
+ DrawLine( d, pp0, pp1, rbw, color );
+ }
+ }
+ }
+}
+
+
+EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d )
+{
+ DIST_T len, elev0, elev1;
+ ANGLE_T grade = 0.0;
+
+ if ( IsTrack( trk ) && GetTrkEndPtCnt(trk) == 2 ) {
+ len = GetTrkLength( trk, 0, 1 );
+ if (len>0.1) {
+ ComputeElev( trk, 0, FALSE, &elev0, NULL );
+ ComputeElev( trk, 1, FALSE, &elev1, NULL );
+ grade = fabs( (elev1-elev0)/len )*100.0;
+ }
+ }
+ if ( (d->options&(DC_GROUP)) == 0 ) {
+ if ( grade > maxTrackGrade )
+ return exceptionColor;
+ if ( QueryTrack( trk, Q_EXCEPTION ) )
+ return exceptionColor;
+ }
+ if ( (d->options&(DC_PRINT|DC_GROUP)) == 0 ) {
+ if (GetTrkBits(trk)&TB_PROFILEPATH)
+ return profilePathColor;
+ if ((d->options&DC_PRINT)==0 && GetTrkSelected(trk))
+ return selectedColor;
+ }
+ if ( (d->options&(DC_GROUP)) == 0 ) {
+ if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) )
+ return GetLayerColor((LAYER_T)curTrackLayer);
+ }
+ return wDrawColorBlack;
+}
+
+
+EXPORT void DrawTrack( track_cp trk, drawCmd_p d, wDrawColor color )
+{
+ DIST_T scale2rail;
+ TRKTYP_T trkTyp;
+
+ trkTyp = GetTrkType(trk);
+ curTrackLayer = GetTrkLayer(trk);
+ if (d != &mapD ) {
+ if ( (!GetTrkVisible(trk)) ) {
+ if ( drawTunnel==DRAW_TUNNEL_NONE )
+ return;
+ if ( drawTunnel==DRAW_TUNNEL_DASH )
+ d->options |= DC_DASH;
+ }
+ if (color == wDrawColorBlack) {
+ color = GetTrkColor( trk, d );
+ }
+ }
+ if (d == &mapD && !GetLayerOnMap(curTrackLayer))
+ return;
+ if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) &&
+ d != &mapD && color == wDrawColorBlack )
+ color = GetLayerColor((LAYER_T)curTrackLayer);
+ scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
+ if ( (!inDrawTracks) &&
+ tieDrawMode!=TIEDRAWMODE_NONE &&
+ d != &mapD &&
+ d->scale<scale2rail/2 &&
+ QueryTrack(trk, Q_ISTRACK) &&
+ (GetTrkVisible(trk) || drawTunnel==DRAW_TUNNEL_SOLID) ) {
+ d->options |= DC_TIES;
+ }
+ trackCmds(trkTyp)->draw( trk, d, color );
+ if ( (!inDrawTracks) ) {
+ d->options &= ~DC_TIES;
+ }
+ d->options &= ~DC_DASH;
+
+ DrawTrackElev( trk, d, color!=wDrawColorWhite );
+}
+
+
+static void DrawATrack( track_cp trk, wDrawColor color )
+{
+ DrawMapBoundingBox( FALSE );
+ DrawTrack( trk, &mapD, color );
+ DrawTrack( trk, &mainD, color );
+ DrawMapBoundingBox( TRUE );
+}
+
+
+EXPORT void DrawNewTrack( track_cp t )
+{
+ DrawATrack( t, wDrawColorBlack );
+}
+
+EXPORT void UndrawNewTrack( track_cp t )
+{
+ DrawATrack( t, wDrawColorWhite );
+}
+
+EXPORT int doDrawPositionIndicator = 1;
+EXPORT void DrawPositionIndicators( void )
+{
+ track_p trk;
+ coOrd hi, lo;
+ if ( !doDrawPositionIndicator )
+ return;
+ TRK_ITERATE( trk ) {
+ if ( trackCmds(trk->type)->drawPositionIndicator ) {
+ if ( drawTunnel==DRAW_TUNNEL_NONE && (!GetTrkVisible(trk)) )
+ continue;
+ GetBoundingBox( trk, &hi, &lo );
+ if ( OFF_MAIND( lo, hi ) )
+ continue;
+ if (!GetLayerVisible( GetTrkLayer(trk) ) )
+ continue;
+ trackCmds(trk->type)->drawPositionIndicator( trk, selectedColor );
+ }
+ }
+}
+
+
+EXPORT void AdvancePositionIndicator(
+ track_p trk,
+ coOrd pos,
+ coOrd * posR,
+ ANGLE_T * angleR )
+{
+ if ( trackCmds(trk->type)->advancePositionIndicator )
+ trackCmds(trk->type)->advancePositionIndicator( trk, pos, posR, angleR );
+}
+/*****************************************************************************
+ *
+ * BASIC DRAWING
+ *
+ */
+
+static void DrawUnconnectedEndPt( drawCmd_p d, coOrd p, ANGLE_T a, DIST_T trackGauge, wDrawColor color )
+{
+ coOrd p0, p1;
+ Translate( &p0, p, a, trackGauge );
+ Translate( &p1, p, a-180.0, trackGauge );
+ DrawLine( d, p0, p1, 0, color );
+ if (d->scale < 8) {
+ Translate( &p, p, a+90.0, 0.2 );
+ Translate( &p0, p, a, trackGauge );
+ Translate( &p1, p, a-180.0, trackGauge );
+ DrawLine( d, p0, p1, 0, color );
+ }
+}
+
+
+EXPORT void DrawEndElev( drawCmd_p d, track_p trk, EPINX_T ep, wDrawColor color )
+{
+ coOrd pp;
+ wFont_p fp;
+ elev_t * elev;
+ track_p trk1;
+ DIST_T elev0, grade;
+ ANGLE_T a=0;
+ int style = BOX_BOX;
+ BOOL_T gradeOk = TRUE;
+ char *elevStr;
+
+ if ((labelEnable&LABELENABLE_ENDPT_ELEV)==0)
+ return;
+ elev = &trk->endPt[ep].elev; /* TRACKDEP */
+ if ( (elev->option&ELEV_MASK)==ELEV_NONE ||
+ (elev->option&ELEV_VISIBLE)==0 )
+ return;
+ if ( (trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk) )
+ return;
+
+ fp = wStandardFont( F_HELV, FALSE, FALSE );
+ pp = GetTrkEndPos( trk, ep );
+ switch ((elev->option&ELEV_MASK)) {
+ case ELEV_COMP:
+ case ELEV_GRADE:
+ if ( color == wDrawColorWhite ) {
+ elev0 = grade = elev->u.height;
+ } else if ( !ComputeElev( trk, ep, FALSE, &elev0, &grade ) ) {
+ elev0 = grade = 0;
+ gradeOk = FALSE;
+ }
+ if ((elev->option&ELEV_MASK)==ELEV_COMP) {
+ elevStr = FormatDistance(elev0);
+ elev->u.height = elev0;
+ } else if (gradeOk) {
+ sprintf( message, "%0.1f%%", fabs(grade*100.0) );
+ elevStr = message;
+ a = GetTrkEndAngle( trk, ep );
+ style = BOX_ARROW;
+ if (grade <= -0.001)
+ a = NormalizeAngle( a+180.0 );
+ else if ( grade < 0.001 )
+ style = BOX_BOX;
+ elev->u.height = grade;
+ } else {
+ elevStr = "????%%";
+ }
+ break;
+ case ELEV_DEF:
+ elevStr = FormatDistance( elev->u.height);
+ break;
+ case ELEV_STATION:
+ elevStr = elev->u.name;
+ break;
+ default:
+ return;
+ }
+ pp.x += elev->doff.x;
+ pp.y += elev->doff.y;
+ DrawBoxedString( style, d, pp, elevStr, fp, (wFontSize_t)descriptionFontSize, color, a );
+}
+
+/**
+ * Draw track endpoints. The correct track endpoint (connected, unconnected etc.)
+ * is drawn to the track. In case the endpoint is on the transition into a
+ * tunnel, a tunnel portal is drawn.
+ *
+ * \param d IN drawing functions to use (depends on print, draw to screen etc.)
+ * \param trk IN track for which endpoints are drawn
+ * \param ep IN index of endpoint to draw
+ * \param color IN color to use
+ */
+
+EXPORT void DrawEndPt(
+ drawCmd_p d,
+ track_p trk,
+ EPINX_T ep,
+ wDrawColor color )
+{
+ coOrd p, pp;
+ ANGLE_T a;
+ track_p trk1;
+ coOrd p0, p1, p2;
+ BOOL_T sepBoundary;
+ DIST_T trackGauge;
+ wDrawWidth width;
+ wDrawWidth width2;
+
+ // line width for the tunnel portal, make sure it is rounded correctly
+ width2 = (wDrawWidth)round((2.0 * d->dpi)/75.0);
+ if (d->funcs->options&wDrawOptTemp)
+ return;
+ if ( trk && QueryTrack( trk, Q_NODRAWENDPT ) )
+ return;
+
+ if (trk == NULL || ep < 0)
+ return;
+
+ if (color == wDrawColorBlack)
+ color = normalColor;
+
+ if (labelScale >= d->scale)
+ DrawEndElev( d, trk, ep, color );
+
+ if ( d->scale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) )
+ return;
+
+ trk1 = GetTrkEndTrk(trk,ep);
+ pp = p = GetTrkEndPos( trk, ep );
+ a = GetTrkEndAngle( trk, ep ) + 90.0;
+
+ trackGauge = GetTrkGauge(trk);
+ if (trk1 == NULL) {
+ DrawUnconnectedEndPt( d, p, a, trackGauge, color );
+ return;
+ }
+
+ sepBoundary = FALSE;
+ if ((d->options&DC_PRINT)==0 && importTrack == NULL && GetTrkSelected(trk) && (!GetTrkSelected(trk1))) {
+ DIST_T len;
+ len = trackGauge*2.0;
+ if (len < 0.10*d->scale)
+ len = 0.10*d->scale;
+ Translate( &p0, p, a+45, len );
+ Translate( &p1, p, a+225, len );
+ DrawLine( &tempD, p0, p1, 0, selectedColor );
+ Translate( &p0, p, a-45, len );
+ Translate( &p1, p, a-225, len );
+ DrawLine( &tempD, p0, p1, 0, selectedColor );
+ sepBoundary = TRUE;
+ } else if ((d->options&DC_PRINT)==0 && importTrack == NULL && (!GetTrkSelected(trk)) && GetTrkSelected(trk1)) {
+ sepBoundary = TRUE;
+ }
+
+ // is the endpoint a transition into a tunnel?
+ if (GetTrkVisible(trk) && (!GetTrkVisible(trk1))) {
+ // yes, draw tunnel portal
+ Translate( &p0, p, a, trackGauge );
+ Translate( &p1, p, a+180, trackGauge );
+ DrawLine( d, p0, p1, width2, color );
+ Translate( &p2, p0, a+45, trackGauge/2.0 );
+ DrawLine( d, p0, p2, width2, color );
+ Translate( &p2, p1, a+135, trackGauge/2.0 );
+ DrawLine( d, p1, p2, width2, color );
+ if ( d == &mainD ) {
+ width = (wDrawWidth)ceil(trackGauge*d->dpi/2.0/d->scale);
+ if ( width > 1 ) {
+ if ( (GetTrkEndOption(trk,ep)&EPOPT_GAPPED) != 0 ) {
+ Translate( &p0, p, a, trackGauge );
+ DrawLine( d, p0, p, width, color );
+ }
+ trk1 = GetTrkEndTrk(trk,ep);
+ if ( trk1 ) {
+ ep = GetEndPtConnectedToMe( trk1, trk );
+ if ( (GetTrkEndOption(trk1,ep)&EPOPT_GAPPED) != 0 ) {
+ Translate( &p0, p, a+180.0, trackGauge );
+ DrawLine( d, p0, p, width, color );
+ }
+ }
+ }
+ }
+ } else if ((!GetTrkVisible(trk)) && GetTrkVisible(trk1)) {
+ ;
+ } else if ( GetLayerVisible( GetTrkLayer( trk ) ) && !GetLayerVisible( GetTrkLayer( trk1 ) ) ) {
+ a -= 90.0;
+ Translate( &p, p, a, trackGauge/2.0 );
+ Translate( &p0, p, a-135.0, trackGauge*2.0 );
+ DrawLine( d, p0, p, width2, color );
+ Translate( &p0, p, a+135.0, trackGauge*2.0 );
+ DrawLine( d, p0, p, width2, color );
+ } else if ( !GetLayerVisible( GetTrkLayer( trk ) ) && GetLayerVisible( GetTrkLayer( trk1 ) ) ) {
+ ;
+ } else if ( sepBoundary ) {
+ ;
+ } else if ( (drawEndPtV == 1 && (QueryTrack(trk,Q_DRAWENDPTV_1) || QueryTrack(trk1,Q_DRAWENDPTV_1)) ) ||
+ (drawEndPtV == 2) ) {
+ Translate( &p0, p, a, trackGauge );
+ width = 0;
+ if ( d != &mapD && d != &tempD && (GetTrkEndOption(trk,ep)&EPOPT_GAPPED) != 0 )
+ width = (wDrawWidth)ceil(trackGauge*d->dpi/2.0/d->scale);
+ DrawLine( d, p0, p, width, color );
+ } else {
+ ;
+ }
+}
+
+
+EXPORT void DrawEndPt2(
+ drawCmd_p d,
+ track_p trk,
+ EPINX_T ep,
+ wDrawColor color )
+{
+ track_p trk1;
+ EPINX_T ep1;
+ DrawEndPt( d, trk, ep, color );
+ trk1 = GetTrkEndTrk( trk, ep );
+ if (trk1) {
+ ep1 = GetEndPtConnectedToMe( trk1, trk );
+ if (ep1>=0)
+ DrawEndPt( d, trk1, ep1, color );
+ }
+}
+
+EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size )
+{
+ track_cp trk;
+ TRKINX_T inx;
+ wIndex_t count;
+ coOrd lo, hi;
+ BOOL_T doSelectRecount = FALSE;
+
+ inDrawTracks = TRUE;
+ count = 0;
+ InfoCount( 0 );
+ count = 0;
+ d->options |= DC_TIES;
+ TRK_ITERATE( trk ) {
+ if ( (d->options&DC_PRINT) != 0 &&
+ wPrintQuit() ) {
+ inDrawTracks = FALSE;
+ return;
+ }
+ if ( GetTrkSelected(trk) &&
+ ( (!GetLayerVisible(GetTrkLayer(trk))) ||
+ (drawTunnel==0 && !GetTrkVisible(trk)) ) ) {
+ ClrTrkBits( trk, TB_SELECTED );
+ doSelectRecount = TRUE;
+ }
+ GetBoundingBox( trk, &hi, &lo );
+ if ( OFF_D( orig, size, lo, hi ) ||
+ (d != &mapD && !GetLayerVisible( GetTrkLayer(trk) ) ) ||
+ (d == &mapD && !GetLayerOnMap( GetTrkLayer(trk) ) ) )
+ continue;
+ DrawTrack( trk, d, wDrawColorBlack );
+ count++;
+ if (count%10 == 0)
+ InfoCount( count );
+ }
+ d->options &= ~DC_TIES;
+
+ if (d == &mainD) {
+ for (inx=1; inx<trackCmds_da.cnt; inx++)
+ if (trackCmds(inx)->redraw != NULL)
+ trackCmds(inx)->redraw();
+ }
+ InfoCount( trackCount );
+ inDrawTracks = FALSE;
+ if ( doSelectRecount )
+ SelectRecount();
+}
+
+
+EXPORT void RedrawLayer( LAYER_T l, BOOL_T draw )
+{
+ MainRedraw();
+#ifdef LATER
+ track_cp trk;
+ track_cp trk1;
+ EPINX_T ep;
+ wIndex_t count;
+ coOrd hi, lo;
+
+ count = 0;
+ InfoCount( 0 );
+ TRK_ITERATE( trk ) {
+ if (GetTrkLayer(trk) != l)
+ continue;
+ GetBoundingBox( trk, &hi, &lo );
+ if ( !OFF_MAIND( lo, hi ) ) {
+ if ( GetLayerVisible( l ) ) {
+ DrawTrack( trk, &mainD, draw?wDrawColorBlack:wDrawColorWhite );
+ }
+ for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) {
+ trk1 = GetTrkEndTrk( trk, ep );
+ if ( trk1 && GetTrkLayer(trk1) != l && GetLayerVisible(GetTrkLayer(trk1)) ) {
+ DrawEndPt( &mainD, trk1, GetEndPtConnectedToMe( trk1, trk ),
+ draw?wDrawColorBlack:wDrawColorWhite );
+ }
+ }
+ count++;
+ if (count%10 == 0)
+ InfoCount( count );
+ }
+ if (draw)
+ ClrTrkBits( trk, TB_SELECTED );
+ }
+ InfoCount( trackCount );
+ SelectRecount();
+#endif
+}
+
+
+EXPORT void DrawSelectedTracks( drawCmd_p d )
+{
+ track_cp trk;
+ wIndex_t count;
+
+ count = 0;
+ InfoCount( 0 );
+
+ TRK_ITERATE( trk ) {
+ if ( GetTrkSelected( trk ) ) {
+ DrawTrack( trk, d, wDrawColorBlack );
+ count++;
+ if (count%10 == 0)
+ InfoCount( count );
+ }
+ }
+ InfoCount( trackCount );
+ SelectRecount();
+}
+
+
+EXPORT void HilightElevations( BOOL_T hilight )
+{
+ static long lastRedraw = -1;
+ static BOOL_T lastHilight = FALSE;
+ track_p trk, trk1;
+ EPINX_T ep;
+ int mode;
+ DIST_T elev;
+ coOrd pos;
+ DIST_T radius;
+
+ if (currRedraw > lastRedraw) {
+ lastRedraw = currRedraw;
+ lastHilight = FALSE;
+ }
+ if (lastHilight == hilight)
+ return;
+ radius = 0.05*mainD.scale;
+ if ( radius < trackGauge/2.0 )
+ radius = trackGauge/2.0;
+ TRK_ITERATE( trk ) {
+ for (ep=0;ep<GetTrkEndPtCnt(trk);ep++) {
+ GetTrkEndElev( trk, ep, &mode, &elev ); /* TRACKDEP */
+ if ((mode&ELEV_MASK)==ELEV_DEF || (mode&ELEV_MASK)==ELEV_IGNORE) {
+ if ((trk1=GetTrkEndTrk(trk,ep)) != NULL &&
+ GetTrkIndex(trk1) < GetTrkIndex(trk))
+ continue;
+ if (drawTunnel == DRAW_TUNNEL_NONE && (!GetTrkVisible(trk)) && (trk1==NULL||!GetTrkVisible(trk1)) )
+ continue;
+ if ((!GetLayerVisible(GetTrkLayer(trk))) &&
+ (trk1==NULL||!GetLayerVisible(GetTrkLayer(trk1))))
+ continue;
+ pos = GetTrkEndPos(trk,ep);
+ if ( !OFF_MAIND( pos, pos ) ) {
+ DrawFillCircle( &tempD, pos, radius,
+ ((mode&ELEV_MASK)==ELEV_DEF?elevColorDefined:elevColorIgnore) );
+ }
+ }
+ }
+ }
+ lastHilight = hilight;
+}
+
+
+EXPORT void HilightSelectedEndPt( BOOL_T show, track_p trk, EPINX_T ep )
+{
+ static BOOL_T lastShow = FALSE;
+ static long lastRedraw = -1;
+ coOrd pos;
+ if (trk == NULL)
+ return;
+ if (currRedraw > lastRedraw) {
+ lastRedraw = currRedraw;
+ lastShow = FALSE;
+ }
+ if (lastShow != show) {
+ pos = GetTrkEndPos( trk, ep );
+ DrawFillCircle( &tempD, pos, 0.10*mainD.scale, selectedColor );
+ lastShow = show;
+ }
+}
+
+
+EXPORT void LabelLengths( drawCmd_p d, track_p trk, wDrawColor color )
+{
+ wFont_p fp;
+ wFontSize_t fs;
+ EPINX_T i;
+ coOrd p0, p1;
+ DIST_T dist;
+ char * msg;
+ coOrd textsize;
+
+ if ((labelEnable&LABELENABLE_LENGTHS)==0)
+ return;
+ fp = wStandardFont( F_HELV, FALSE, FALSE );
+ fs = (float)descriptionFontSize/d->scale;
+ for (i=0; i<GetTrkEndPtCnt(trk); i++) {
+ p0 = GetTrkEndPos( trk, i );
+ dist = GetFlexLength( trk, i, &p1 );
+ if (dist < 0.1)
+ continue;
+ if (dist < 3.0) {
+ p0.x = (p0.x+p1.x)/2.0;
+ p0.y = (p0.y+p1.y)/2.0;
+ } else {
+ Translate( &p0, p0, GetTrkEndAngle(trk,i), 0.25*d->scale );
+ }
+ msg = FormatDistance(dist);
+ DrawTextSize( &mainD, msg, fp, fs, TRUE, &textsize );
+ p0.x -= textsize.x/2.0;
+ p0.y -= textsize.y/2.0;
+ DrawString( d, p0, 0.0, msg, fp, fs*d->scale, color );
+ }
+}