@@ -1022,6 +1022,7 @@ public abstract class CVCWithFrames : CabViewControl
10221022 public bool MouseControl ;
10231023 public int Orientation ;
10241024 public int Direction ;
1025+ public bool Reversed { get ; protected set ; } = false ;
10251026
10261027 public List < double > Values
10271028 {
@@ -1037,8 +1038,8 @@ public class CVCDiscrete : CVCWithFrames
10371038 public List < int > Positions = new List < int > ( ) ;
10381039
10391040 private int _ValuesRead ;
1040- private int numPositions ;
1041- private bool canFill = true ;
1041+ private int NumPositions ;
1042+ private bool CanFill = true ;
10421043
10431044 public struct NewScreenData
10441045 {
@@ -1091,7 +1092,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
10911092 stf . MustMatch ( "(" ) ;
10921093 // If Positions are not filled before by Values
10931094 bool shouldFill = ( Positions . Count == 0 ) ;
1094- numPositions = stf . ReadInt ( null ) ; // Number of Positions
1095+ NumPositions = stf . ReadInt ( null ) ; // Number of Positions
10951096
10961097 var minPosition = 0 ;
10971098 var positionsRead = 0 ;
@@ -1130,22 +1131,33 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
11301131 Positions [ i ] = i ;
11311132 }
11321133
1133- // Check if eligible for filling
1134+ // Possible that positions were defined in reverse, eg: 3DTrains Surfliner trains
1135+ // Ensure positions are sorted from least to greatest before proceeding
1136+ if ( Positions . Count > 0 && Positions [ 0 ] > Positions [ Positions . Count - 1 ] )
1137+ {
1138+ Reversed ^= true ;
1139+ // Recalculate positions in reverse
1140+ for ( int i = 0 ; i < Positions . Count ; i ++ )
1141+ Positions [ i ] = ( FramesCount - 1 ) - Positions [ i ] ;
1142+ }
11341143
1135- if ( Positions . Count > 1 && Positions [ 0 ] != 0 ) canFill = false ;
1144+ // Check if eligible for filling
1145+ if ( Positions . Count > 1 && Positions [ 0 ] != 0 )
1146+ CanFill = false ;
11361147 else
11371148 {
11381149 for ( var iPos = 1 ; iPos <= Positions . Count - 1 ; iPos ++ )
11391150 {
1140- if ( Positions [ iPos ] > Positions [ iPos - 1 ] ) continue ;
1141- canFill = false ;
1151+ if ( Positions [ iPos ] > Positions [ iPos - 1 ] )
1152+ continue ;
1153+ CanFill = false ;
11421154 break ;
11431155 }
11441156 }
11451157
11461158 // This is a protection against GP40 locomotives that erroneously have positions pointing beyond frame count limit.
11471159
1148- if ( Positions . Count > 1 && canFill && Positions . Count < FramesCount && Positions [ Positions . Count - 1 ] >= FramesCount && Positions [ 0 ] == 0 )
1160+ if ( Positions . Count > 1 && CanFill && Positions . Count < FramesCount && Positions [ Positions . Count - 1 ] >= FramesCount && Positions [ 0 ] == 0 )
11491161 {
11501162 STFException . TraceInformation ( stf , "Some NumPositions entries refer to non-exisiting frames, trying to renumber" ) ;
11511163 Positions [ Positions . Count - 1 ] = FramesCount - 1 ;
@@ -1170,7 +1182,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
11701182 }
11711183 // Avoid later repositioning, put every value to its Position
11721184 // But before resize Values if needed
1173- if ( numValues != numPositions )
1185+ if ( numValues != NumPositions )
11741186 {
11751187 while ( Values . Count <= Positions [ _ValuesRead ] )
11761188 {
@@ -1232,7 +1244,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
12321244
12331245 // Only shuffle data in following cases
12341246
1235- if ( Values . Count != Positions . Count || ( Values . Count < FramesCount & canFill ) || ( Values . Count > 0 && Values [ 0 ] == Values [ Values . Count - 1 ] && Values [ 0 ] == 0 ) )
1247+ if ( Values . Count != Positions . Count || ( Values . Count < FramesCount & CanFill ) || ( Values . Count > 0 && Values [ 0 ] == Values [ Values . Count - 1 ] && Values [ 0 ] == 0 ) )
12361248 {
12371249
12381250 // Fixup Positions and Values collections first
@@ -1270,42 +1282,52 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
12701282 Positions . Add ( 0 ) ;
12711283 // We will need the FramesCount later!
12721284 // We use Positions only here
1273- Positions . Add ( FramesCount ) ;
1285+ Positions . Add ( FramesCount - 1 ) ;
12741286
12751287 // Fill empty Values
1276- for ( int i = 0 ; i < FramesCount ; i ++ )
1288+ for ( int i = 0 ; i < ( FramesCount - 1 ) ; i ++ )
12771289 Values . Add ( 0 ) ;
1278- Values [ 0 ] = MinValue ;
1279-
1280- Values . Add ( MaxValue ) ;
1290+ // Offset for min and max values to achieve equal frame spacing
1291+ double offset = 1.0 / ( 2.0 * FramesCount ) ;
1292+ // Some dummy controls will have only one frame
1293+ if ( Values . Count > 0 )
1294+ Values [ 0 ] = MinValue + offset ;
1295+ else
1296+ Values . Add ( MinValue + offset ) ;
1297+
1298+ // Add maximum value to the end
1299+ Values . Add ( MaxValue - offset ) ;
12811300 }
12821301 else if ( Values . Count == 2 && Values [ 0 ] == 0 && Values [ 1 ] < MaxValue && Positions [ 0 ] == 0 && Positions [ 1 ] == 1 && Values . Count < FramesCount )
12831302 {
12841303 //This if clause covers among others following cases:
12851304 // Case 1 (e.g. engine brake lever of gp38):
1286- //NumFrames ( 18 2 9 )
1287- //NumPositions ( 2 0 1 )
1288- //NumValues ( 2 0 0.3 )
1289- //Orientation ( 0 )
1290- //DirIncrease ( 0 )
1291- //ScaleRange ( 0 1 )
1292- Positions . Add ( FramesCount ) ;
1305+ //NumFrames ( 18 2 9 )
1306+ //NumPositions ( 2 0 1 )
1307+ //NumValues ( 2 0 0.3 )
1308+ //Orientation ( 0 )
1309+ //DirIncrease ( 0 )
1310+ //ScaleRange ( 0 1 )
1311+ // Add missing positions
1312+ Positions . Add ( FramesCount - 1 ) ;
12931313 // Fill empty Values
1294- for ( int i = Values . Count ; i < FramesCount ; i ++ )
1295- Values . Add ( Values [ 1 ] ) ;
1296- Values . Add ( MaxValue ) ;
1314+ for ( int i = Values . Count ; i < ( FramesCount - 1 ) ; i ++ )
1315+ Values . Add ( 0 ) ;
1316+ // Offset for min and max values to achieve equal frame spacing
1317+ double offset = 1.0 / ( 2.0 * FramesCount ) ;
1318+ // Add maximum value to the end
1319+ Values . Add ( MaxValue - offset ) ;
12971320 }
1298-
12991321 else
13001322 {
13011323 //This if clause covers among others following cases:
13021324 // Case 1 (e.g. train brake lever of Acela):
1303- //NumFrames ( 12 4 3 )
1304- //NumPositions ( 5 0 1 9 10 11 )
1305- //NumValues ( 5 0 0.2 0.85 0.9 0.95 )
1306- //Orientation ( 1 )
1307- //DirIncrease ( 1 )
1308- //ScaleRange ( 0 1 )
1325+ //NumFrames ( 12 4 3 )
1326+ //NumPositions ( 5 0 1 9 10 11 )
1327+ //NumValues ( 5 0 0.2 0.85 0.9 0.95 )
1328+ //Orientation ( 1 )
1329+ //DirIncrease ( 1 )
1330+ //ScaleRange ( 0 1 )
13091331 //
13101332 // Fill empty Values
13111333 int iValues = 1 ;
@@ -1321,11 +1343,6 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
13211343 }
13221344 iValues ++ ;
13231345 }
1324-
1325- // Add the maximums to the end, the Value will be removed
1326- // We use Positions only here
1327- if ( Values . Count > 0 && Values [ 0 ] <= Values [ Values . Count - 1 ] ) Values . Add ( MaxValue ) ;
1328- else if ( Values . Count > 0 && Values [ 0 ] > Values [ Values . Count - 1 ] ) Values . Add ( MinValue ) ;
13291346 }
13301347
13311348 // OK, we have a valid size of Positions and Values
@@ -1351,9 +1368,6 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
13511368 p = Positions [ i ] ;
13521369 }
13531370 }
1354-
1355- // Don't need the MaxValue added before, remove it
1356- Values . RemoveAt ( Values . Count - 1 ) ;
13571371 }
13581372 }
13591373
@@ -1378,13 +1392,21 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
13781392 break ;
13791393 }
13801394 }
1381- // catch (Exception error)
1382- // {
1383- // if (error is STFException) // Parsing error, so pass it on
1384- // throw;
1385- // else // Unexpected error, so provide a hint
1386- // throw new STFException(stf, "Problem with NumPositions/NumValues/NumFrames/ScaleRange");
1387- // } // End of Need check the Values collection for validity
1395+ // catch (Exception error)
1396+ // {
1397+ // if (error is STFException) // Parsing error, so pass it on
1398+ // throw;
1399+ // else // Unexpected error, so provide a hint
1400+ // throw new STFException(stf, "Problem with NumPositions/NumValues/NumFrames/ScaleRange");
1401+ // } // End of Need check the Values collection for validity
1402+
1403+ // Ensure resulting set of values has the correct format (sorted least to greatest) and resort
1404+ // Assume values have been entered in reverse order if final value is less than initial value
1405+ if ( Values . Count > 0 && Values [ 0 ] > Values [ Values . Count - 1 ] )
1406+ Reversed ^= true ;
1407+ // Force sort values from least to greatest
1408+ Values . Sort ( ) ;
1409+
13881410 } // End of Constructor
13891411
13901412 protected void ParseNewScreen ( STFReader stf )
@@ -1432,13 +1454,12 @@ public CVCMultiStateDisplay(STFReader stf, string basepath)
14321454 stf . ParseBlock ( new STFReader . TokenProcessor [ ] {
14331455 new STFReader . TokenProcessor ( "style" , ( ) => { MSStyles . Add ( ParseNumStyle ( stf ) ) ;
14341456 } ) ,
1435- new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadFloatBlock ( STFReader . UNITS . None , null ) )
1457+ new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadDoubleBlock ( null ) )
14361458 ; } ) ,
14371459 } ) ; } ) ,
14381460 } ) ;
1439- if ( Values . Count > 0 ) MaxValue = Values . Last ( ) ;
1440- for ( int i = Values . Count ; i < FramesCount ; i ++ )
1441- Values . Add ( - 10000 ) ;
1461+ if ( Values . Count > 0 )
1462+ MaxValue = Values . Max ( ) ;
14421463 } ) ,
14431464 new STFReader . TokenProcessor ( "ortsdisplay" , ( ) => { ParseDisplay ( stf ) ; } ) ,
14441465 new STFReader . TokenProcessor ( "ortsscreenpage" , ( ) => { ParseScreen ( stf ) ; } ) ,
@@ -1447,6 +1468,16 @@ public CVCMultiStateDisplay(STFReader stf, string basepath)
14471468 new STFReader . TokenProcessor ( "ortsunitsscalefactor" , ( ) => { UnitsScale = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
14481469 new STFReader . TokenProcessor ( "ortsunitsoffset" , ( ) => { UnitsOffset = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
14491470 } ) ;
1471+
1472+ // Ensure resulting set of values has the correct format (sorted least to greatest) and resort
1473+ // Assume values have been entered in reverse order if final value is less than initial value
1474+ if ( Values . Count > 0 && Values [ 0 ] > Values [ Values . Count - 1 ] )
1475+ Reversed ^= true ;
1476+ // Fill in missing values
1477+ for ( int i = Values . Count ; i < FramesCount ; i ++ )
1478+ Values . Add ( Values [ Values . Count - 1 ] ) ;
1479+ // Force sort values from least to greatest
1480+ Values . Sort ( ) ;
14501481 }
14511482 protected int ParseNumStyle ( STFReader stf )
14521483 {
@@ -1485,13 +1516,12 @@ public CVCAnimatedDisplay(STFReader stf, string basepath)
14851516 stf . ParseBlock ( new STFReader . TokenProcessor [ ] {
14861517 new STFReader . TokenProcessor ( "style" , ( ) => { MSStyles . Add ( ParseNumStyle ( stf ) ) ;
14871518 } ) ,
1488- new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadFloatBlock ( STFReader . UNITS . None , null ) )
1519+ new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadDoubleBlock ( null ) )
14891520 ; } ) ,
14901521 } ) ; } ) ,
14911522 } ) ;
1492- if ( Values . Count > 0 ) MaxValue = Values . Last ( ) ;
1493- for ( int i = Values . Count ; i < FramesCount ; i ++ )
1494- Values . Add ( - 10000 ) ;
1523+ if ( Values . Count > 0 )
1524+ MaxValue = Values . Max ( ) ;
14951525 } ) ,
14961526 new STFReader . TokenProcessor ( "ortsdisplay" , ( ) => { ParseDisplay ( stf ) ; } ) ,
14971527 new STFReader . TokenProcessor ( "ortsscreenpage" , ( ) => { ParseScreen ( stf ) ; } ) ,
@@ -1500,6 +1530,16 @@ public CVCAnimatedDisplay(STFReader stf, string basepath)
15001530 new STFReader . TokenProcessor ( "ortsunitsscalefactor" , ( ) => { UnitsScale = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
15011531 new STFReader . TokenProcessor ( "ortsunitsoffset" , ( ) => { UnitsOffset = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
15021532 } ) ;
1533+
1534+ // Ensure resulting set of values has the correct format (sorted least to greatest) and resort
1535+ // Assume values have been entered in reverse order if final value is less than initial value
1536+ if ( Values . Count > 0 && Values [ 0 ] > Values [ Values . Count - 1 ] )
1537+ Reversed ^= true ;
1538+ // Fill in missing values
1539+ for ( int i = Values . Count ; i < FramesCount ; i ++ )
1540+ Values . Add ( Values [ Values . Count - 1 ] ) ;
1541+ // Force sort values from least to greatest
1542+ Values . Sort ( ) ;
15031543 }
15041544 protected int ParseNumStyle ( STFReader stf )
15051545 {
0 commit comments