summaryrefslogtreecommitdiff
path: root/app/bin/trkendpt.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2024-11-14 19:35:45 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2024-11-14 19:35:45 +0100
commitdf5520aa2dae5b3ce7abf8733dcdd152898af163 (patch)
tree00d3047bfb14f682bfb5a21010c731ed649bfed7 /app/bin/trkendpt.c
parentdf247efec654e512242e4f4f1b0212034f9e01fe (diff)
parentec3c0f6f6e7153fa797dc57a0e95779cbc63a23b (diff)
Merge branch 'release/debian/1_5.3.0GA-1'debian/1_5.3.0GA-1
Diffstat (limited to 'app/bin/trkendpt.c')
-rw-r--r--app/bin/trkendpt.c648
1 files changed, 648 insertions, 0 deletions
diff --git a/app/bin/trkendpt.c b/app/bin/trkendpt.c
new file mode 100644
index 0000000..a9ca40b
--- /dev/null
+++ b/app/bin/trkendpt.c
@@ -0,0 +1,648 @@
+/*
+ * $Header: $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2022 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "common.h"
+#include "common.h"
+#include "common-ui.h"
+#include "fileio.h"
+#include "param.h"
+#include "track.h"
+#include "trkendpt.h"
+#include "trkendptx.h"
+#include "draw.h"
+
+
+/************************************************************************
+ * Basic Access
+ */
+
+EXPORT CSIZE_T EndPtSize(
+ EPINX_T epCnt )
+{
+ return epCnt * sizeof *(trkEndPt_p)NULL;
+}
+
+
+EXPORT coOrd GetEndPtPos( trkEndPt_p epp )
+{
+ return epp->pos;
+}
+
+
+EXPORT ANGLE_T GetEndPtAngle( trkEndPt_p epp )
+{
+ return epp->angle;
+}
+
+
+EXPORT track_p GetEndPtTrack( trkEndPt_p epp )
+{
+ return epp->track;
+}
+
+
+EXPORT void SetEndPt( trkEndPt_p epp, coOrd pos, ANGLE_T angle )
+{
+ epp->pos = pos;
+ epp->angle = angle;
+}
+
+
+EXPORT EPINX_T GetEndPtEndPt( trkEndPt_p epp )
+{
+ return (EPINX_T)(epp->index);
+}
+
+
+EXPORT TRKINX_T GetEndPtIndex( trkEndPt_p epp )
+{
+ return (TRKINX_T)(epp->index);
+}
+
+
+EXPORT void SetEndPtTrack( trkEndPt_p epp, track_p trk )
+{
+ epp->track = trk;
+ epp->index = -1;
+}
+
+
+EXPORT void SetEndPtEndPt( trkEndPt_p epp, EPINX_T ep )
+{
+ epp->index = ep;
+}
+
+
+EXPORT trkEndPt_p EndPtIndex( trkEndPt_p epp, EPINX_T ep )
+{
+ return epp+ep;
+}
+
+
+/************************************************************************
+ * Temp End Points
+ */
+
+static dynArr_t tempEndPts_da;
+
+EXPORT void TempEndPtsReset( void )
+{
+ DYNARR_RESET( trkEndPt_t, tempEndPts_da );
+}
+
+
+EXPORT void TempEndPtsSet( EPINX_T ep )
+{
+ DYNARR_SET( trkEndPt_t, tempEndPts_da, ep );
+ memset( &DYNARR_N(trkEndPt_t,tempEndPts_da,0), 0,
+ ep * sizeof *(trkEndPt_p)NULL );
+}
+
+
+EXPORT EPINX_T TempEndPtsCount( void )
+{
+ return tempEndPts_da.cnt;
+}
+
+
+EXPORT trkEndPt_p TempEndPt( EPINX_T ep )
+{
+ CHECK( ep < tempEndPts_da.cnt );
+ return &DYNARR_N( trkEndPt_t, tempEndPts_da, ep );
+}
+
+
+#ifndef MKTURNOUT
+
+EXPORT trkEndPt_p TempEndPtsAppend( void )
+{
+ DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 );
+ trkEndPt_p epp = &DYNARR_LAST( trkEndPt_t, tempEndPts_da );
+ memset( epp, 0, sizeof *epp );
+ return epp;
+}
+
+
+EXPORT void SwapEndPts(
+ trkEndPt_p epp,
+ EPINX_T ep0,
+ EPINX_T ep1 )
+{
+ trkEndPt_t tempEP;
+ tempEP = epp[ep0];
+ epp[ep0] = epp[ep1];
+ epp[ep1] = tempEP;
+}
+
+
+/************************************************************************
+ * Track level access
+ */
+
+EXPORT void SetTrkEndPoint( track_p trk, EPINX_T ep, coOrd pos, ANGLE_T angle )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ // check setTrkEndPoint: endPt is not connected
+ CHECK( GetEndPtTrack( GetTrkEndPt( trk, ep ) ) == NULL );
+ SetEndPt( GetTrkEndPt( trk, ep ), pos, angle );
+}
+
+
+EXPORT void SetTrkEndPointSilent( track_p trk, EPINX_T ep, coOrd pos,
+ ANGLE_T angle )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ SetEndPt( GetTrkEndPt( trk, ep ), pos, angle );
+}
+
+
+EXPORT coOrd GetTrkEndPos( track_p trk, EPINX_T ep )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ return GetTrkEndPt(trk,ep)->pos;
+}
+
+
+EXPORT ANGLE_T GetTrkEndAngle( track_p trk, EPINX_T ep )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ return GetTrkEndPt(trk,ep)->angle;
+}
+
+
+EXPORT track_p GetTrkEndTrk( track_p trk, EPINX_T ep )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ return GetTrkEndPt(trk,ep)->track;
+}
+
+
+EXPORT long GetTrkEndOption( track_p trk, EPINX_T ep )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ return GetTrkEndPt(trk,ep)->option;
+}
+
+
+EXPORT long SetTrkEndOption( track_p trk, EPINX_T ep, long option )
+{
+ CHECK( ep < GetTrkEndPtCnt(trk) );
+ return GetTrkEndPt(trk,ep)->option = option;
+}
+
+
+/************************************************************************
+ * Elevations
+ */
+
+EXPORT void SetTrkEndElev( track_p trk, EPINX_T ep, int option, DIST_T height,
+ char * station )
+{
+ track_p trk1;
+ EPINX_T ep1;
+ trkEndPt_p epp = GetTrkEndPt( trk, ep );
+ epp->elev.option = option;
+ epp->elev.cacheSet = FALSE;
+ if (EndPtIsDefinedElev(trk,ep)) {
+ epp->elev.u.height = height;
+ } else if (EndPtIsStationElev(trk,ep)) {
+ if (station == NULL) {
+ station = "";
+ }
+ epp->elev.u.name = MyStrdup(station);
+ }
+ if ( (trk1=GetTrkEndTrk(trk, ep)) != NULL ) {
+ ep1 = GetEndPtConnectedToMe( trk1, trk );
+ if (ep1 >= 0) {
+ trkEndPt_p epp1 = GetTrkEndPt( trk1, ep1 );
+ epp1->elev.option = option;
+ epp1->elev.u.height = height;
+ if (EndPtIsDefinedElev(trk1,ep1)) {
+ epp1->elev.u.height = height;
+ } else if (EndPtIsStationElev(trk,ep)) {
+ epp1->elev.u.name = MyStrdup(station);
+ }
+ }
+ }
+}
+
+
+EXPORT void GetTrkEndElev( track_p trk, EPINX_T e, int *option, DIST_T *height )
+{
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ *option = epp->elev.option;
+ *height = epp->elev.u.height;
+}
+
+
+EXPORT int GetTrkEndElevUnmaskedMode( track_p trk, EPINX_T e )
+{
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ return epp->elev.option;
+}
+
+
+EXPORT int GetTrkEndElevMode( track_p trk, EPINX_T e )
+{
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ return epp->elev.option&ELEV_MASK;
+}
+
+
+EXPORT DIST_T GetTrkEndElevHeight( track_p trk, EPINX_T e )
+{
+ CHECK( EndPtIsDefinedElev(trk,e) );
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ return epp->elev.u.height;
+}
+
+
+EXPORT void ClrEndPtElevCache(
+ EPINX_T epCnt,
+ trkEndPt_p epPts )
+{
+ for ( EPINX_T ep = 0; ep < epCnt; ep++ ) {
+ epPts[ep].elev.cacheSet = FALSE;
+ }
+}
+
+
+static BOOL_T bCacheElev = TRUE;
+
+EXPORT BOOL_T GetTrkEndElevCachedHeight (track_p trk, EPINX_T e,
+ DIST_T * height, DIST_T * grade)
+{
+ if ( ! bCacheElev ) {
+ return FALSE;
+ }
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ if (epp->elev.cacheSet) {
+ *height = epp->elev.cachedElev;
+ *grade = epp->elev.cachedGrade;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+EXPORT void SetTrkEndElevCachedHeight ( track_p trk, EPINX_T e, DIST_T height,
+ DIST_T grade)
+{
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ epp->elev.cachedElev = height;
+ epp->elev.cachedGrade = grade;
+ epp->elev.cacheSet = TRUE;
+}
+
+
+EXPORT char * GetTrkEndElevStation( track_p trk, EPINX_T e )
+{
+ CHECK( EndPtIsStationElev(trk,e) );
+ trkEndPt_p epp = GetTrkEndPt( trk, e );
+ if ( epp->elev.u.name == NULL ) {
+ return "";
+ } else {
+ return epp->elev.u.name;
+ }
+}
+
+
+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_BACKGROUND;
+ BOOL_T gradeOk = TRUE;
+ char *elevStr;
+
+ if ((labelEnable&LABELENABLE_ENDPT_ELEV)==0) {
+ return;
+ }
+ elev = &GetTrkEndPt(trk,ep)->elev;
+ 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, FALSE ) ) {
+ 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%%", round(fabs(grade*100.0)*10)/10 );
+ elevStr = message;
+ a = GetTrkEndAngle( trk, ep );
+ style = BOX_ARROW_BACKGROUND;
+ if (grade <= -0.001) {
+ a = NormalizeAngle( a+180.0 );
+ } else if ( grade < 0.001 ) {
+ style = BOX_BOX_BACKGROUND;
+ }
+ 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;
+ }
+ coOrd startLine = pp, endLine = pp;
+ pp.x += elev->doff.x;
+ pp.y += elev->doff.y;
+ if (color==drawColorPreviewSelected) {
+ Translate(&endLine,pp,FindAngle(pp,startLine),descriptionFontSize/d->dpi);
+ DrawLine( d, startLine, endLine, 0, color );
+ }
+ DrawBoxedString( style, d, pp, elevStr, fp, (wFontSize_t)descriptionFontSize,
+ color, a );
+
+}
+
+
+/************************************************************************
+ * Descriptions
+ */
+
+EXPORT DIST_T EndPtDescriptionDistance(
+ coOrd pos,
+ track_p trk,
+ EPINX_T ep,
+ coOrd *dpos,
+ BOOL_T show_hidden,
+ BOOL_T * hidden)
+{
+ elev_t *e;
+ coOrd pos1;
+ track_p trk1;
+ *dpos = pos;
+ if (hidden) { *hidden = FALSE; }
+ e = &GetTrkEndPt(trk,ep)->elev;
+ if ((e->option&ELEV_MASK)==ELEV_NONE) {
+ return DIST_INF;
+ }
+ if (((e->option&ELEV_VISIBLE)==0) && !show_hidden) {
+ return DIST_INF;
+ }
+ if ((trk1=GetTrkEndTrk(trk,ep)) && GetTrkIndex(trk1)<GetTrkIndex(trk)) {
+ return DIST_INF;
+ }
+ if ((e->option&ELEV_VISIBLE)==0) { //Hidden - disregard offset
+ if (hidden) { *hidden = TRUE; }
+ return FindDistance( GetTrkEndPos(trk,ep), pos );
+ }
+ /*REORIGIN( pos1, e->doff, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) );*/
+ pos1 = GetTrkEndPos(trk,ep);
+ coOrd tpos = pos1;
+ pos1.x += e->doff.x;
+ pos1.y += e->doff.y;
+ *dpos = pos1;
+ if (hidden) { *hidden = !(e->option&ELEV_VISIBLE); }
+ if (FindDistance(tpos,pos)<FindDistance( pos1, pos )) {
+ return FindDistance(tpos,pos);
+ }
+ return FindDistance( pos1, pos );
+}
+
+
+EXPORT STATUS_T EndPtDescriptionMove(
+ track_p trk,
+ EPINX_T ep,
+ wAction_t action,
+ coOrd pos )
+{
+ static coOrd p0;
+// static coOrd p1;
+ elev_t *e;
+ track_p trk1;
+
+ e = &GetTrkEndPt(trk,ep)->elev;
+ switch (action) {
+ case C_DOWN:
+ p0 = GetTrkEndPos(trk,ep);
+// p1 = pos;
+ e->option |= ELEV_VISIBLE; //Make sure we make visible
+ DrawEndElev( &mainD, trk, ep, wDrawColorWhite );
+ /*no break*/
+ case C_MOVE:
+ case C_UP:
+// p1 = pos;
+ e->doff.x = (pos.x-p0.x);
+ e->doff.y = (pos.y-p0.y);
+ if ((trk1=GetTrkEndTrk(trk,ep))) {
+ EPINX_T ep1 = GetEndPtConnectedToMe(trk1,trk);
+ trkEndPt_p epp = GetTrkEndPt( trk1, ep1 );
+// e1 = &trk1->endPt[GetEndPtConnectedToMe(trk1,trk)].elev;
+ epp->elev.doff = e->doff;
+ }
+ if ( action == C_UP ) {
+ wDrawColor color = GetTrkColor( trk, &mainD );
+ DrawEndElev( &mainD, trk, ep, color );
+ }
+ return action==C_UP?C_TERMINATE:C_CONTINUE;
+
+ case C_REDRAW:
+ DrawEndElev( &tempD, trk, ep, drawColorPreviewSelected );
+ break;
+ }
+ return C_CONTINUE;
+}
+
+
+/************************************************************************
+ * Read/Write End Points
+ */
+
+EXPORT BOOL_T GetEndPtArg(
+ char * cp,
+ char type,
+ BOOL_T bImprovedEnds )
+{
+ long option;
+ long option2;
+ trkEndPt_p e = TempEndPtsAppend();
+ FLOAT_T ignoreFloat;
+
+ if (type == SEG_CONEP) {
+ if ( !GetArgs( cp, "dc", &e->index, &cp ) ) {
+ /*??*/return FALSE;
+ }
+ } else {
+ e->index = -1;
+ }
+ if ( !GetArgs( cp, "pfc",
+ &e->pos, &e->angle, &cp) ) {
+ /*??*/return FALSE;
+ }
+ e->elev.option = 0;
+ e->elev.u.height = 0.0;
+ e->elev.doff = zero;
+ e->option = 0;
+ if (bImprovedEnds) { //E4 and T4
+ if (!GetArgs( cp, "lpc", &option, &e->elev.doff, &cp )) {
+ /*??*/return FALSE;
+ }
+ switch (option&ELEV_MASK) {
+ case ELEV_STATION:
+ GetArgs( cp, "qc", &e->elev.u.name, &cp);
+ break;
+ default:
+ GetArgs( cp, "fc", &e->elev.u.height, &cp); //First height
+ }
+ DIST_T height2;
+ if (!GetArgs( cp, "flLlc", &height2, &option2, &e->elev.option, &e->option,
+ &cp ) ) {
+ return FALSE;
+ }
+ if (option2) { e->elev.option |= ELEV_VISIBLE; }
+ GetArgs(cp, "fc", &ignoreFloat, &cp);
+ return TRUE;
+ }
+ if ( cp != NULL ) {
+ if (paramVersion < 7) {
+ GetArgs( cp, "dfp", &e->elev.option, &e->elev.u.height, &e->elev.doff, &cp );
+ /*??*/return TRUE;
+ }
+ GetArgs( cp, "lpc", &option, &e->elev.doff, &cp );
+ e->option = option >> 8;
+ e->elev.option = (int)(option&0xFF);
+ if ( (e->elev.option&ELEV_MASK) != ELEV_NONE ) {
+ switch (e->elev.option&ELEV_MASK) {
+ case ELEV_DEF:
+ GetArgs( cp, "fc", &e->elev.u.height, &cp );
+ break;
+ case ELEV_STATION:
+ GetArgs( cp, "qc", &e->elev.u.name, &cp );
+ /*??*/break;
+ default:
+ ;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+EXPORT BOOL_T bWriteEndPtDirectIndex = FALSE;
+EXPORT BOOL_T bWriteEndPtExporting = FALSE;
+
+
+EXPORT BOOL_T WriteEndPt( FILE * f, track_cp trk, EPINX_T ep )
+{
+ trkEndPt_p endPt = GetTrkEndPt(trk,ep);
+ BOOL_T rc = TRUE;
+ long option;
+
+ CHECK ( endPt );
+ if (bWriteEndPtDirectIndex && endPt->index > 0) {
+ rc &= fprintf( f, "\tT4 %ld ", endPt->index )>0;
+ } else if (endPt->track == NULL ||
+ ( bWriteEndPtExporting && !GetTrkSelected(endPt->track) ) ) {
+ rc &= fprintf( f, "\tE4 " )>0;
+ } else {
+ rc &= fprintf( f, "\tT4 %d ", GetTrkIndex(endPt->track) )>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;
+ 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, " 0.0 ")>0;
+ }
+ } else {
+ rc &= fprintf( f, " 0 0.0 0.0 0.0 ")>0;
+ }
+ if ((endPt->elev.option&ELEV_MASK) == ELEV_DEF) {
+ rc &= fprintf( f, "%0.6f ",endPt->elev.u.height)>0;
+ } else {
+ rc &= fprintf( f, "0.0 ")>0;
+ }
+ long elevVisible = (endPt->elev.option&ELEV_VISIBLE)?1:0;
+ long elevType = endPt->elev.option&ELEV_MASK;
+ long gapType = endPt->option;
+ rc &= fprintf( f, "%ld %ld %ld ", elevVisible, elevType, gapType)>0;
+ rc &= fprintf( f, "%0.6f ", GetTrkElev( trk ) )>0;
+ rc &= fprintf( f, "\n" )>0;
+ return rc;
+}
+
+
+/************************************************************************
+* Read/Write End Points
+*/
+
+wBool_t CompareEndPt(
+ char * cp,
+ track_p trk1,
+ track_p trk2,
+ EPINX_T ep )
+{
+ trkEndPt_p epp1 = GetTrkEndPt( trk1, ep );
+ trkEndPt_p epp2 = GetTrkEndPt( trk2, ep );
+ REGRESS_CHECK_POS( "Pos", epp1, epp2, pos )
+ REGRESS_CHECK_ANGLE( "Angle", epp1, epp2, angle )
+ // Actual EP connection
+ int inx1 = -1;
+ if ( epp1->track ) {
+ inx1 = GetTrkIndex( epp1->track );
+ }
+ // Expected EP connection. endPt[inx].track is not resolved
+ int inx2 = epp2->index;
+ if ( inx1 != inx2 ) {
+ sprintf( cp, "Index: Actual` %d, Expected %d\n", inx1, inx2 );
+ return FALSE;
+ }
+ REGRESS_CHECK_INT( "Option", epp1, epp2, option )
+ return TRUE;
+}
+#endif