@@ -1022,6 +1022,7 @@ public abstract class CVCWithFrames : CabViewControl
1022
1022
public bool MouseControl ;
1023
1023
public int Orientation ;
1024
1024
public int Direction ;
1025
+ public bool Reversed { get ; protected set ; } = false ;
1025
1026
1026
1027
public List < double > Values
1027
1028
{
@@ -1037,8 +1038,8 @@ public class CVCDiscrete : CVCWithFrames
1037
1038
public List < int > Positions = new List < int > ( ) ;
1038
1039
1039
1040
private int _ValuesRead ;
1040
- private int numPositions ;
1041
- private bool canFill = true ;
1041
+ private int NumPositions ;
1042
+ private bool CanFill = true ;
1042
1043
1043
1044
public struct NewScreenData
1044
1045
{
@@ -1091,7 +1092,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1091
1092
stf . MustMatch ( "(" ) ;
1092
1093
// If Positions are not filled before by Values
1093
1094
bool shouldFill = ( Positions . Count == 0 ) ;
1094
- numPositions = stf . ReadInt ( null ) ; // Number of Positions
1095
+ NumPositions = stf . ReadInt ( null ) ; // Number of Positions
1095
1096
1096
1097
var minPosition = 0 ;
1097
1098
var positionsRead = 0 ;
@@ -1130,22 +1131,33 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1130
1131
Positions [ i ] = i ;
1131
1132
}
1132
1133
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
+ }
1134
1143
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 ;
1136
1147
else
1137
1148
{
1138
1149
for ( var iPos = 1 ; iPos <= Positions . Count - 1 ; iPos ++ )
1139
1150
{
1140
- if ( Positions [ iPos ] > Positions [ iPos - 1 ] ) continue ;
1141
- canFill = false ;
1151
+ if ( Positions [ iPos ] > Positions [ iPos - 1 ] )
1152
+ continue ;
1153
+ CanFill = false ;
1142
1154
break ;
1143
1155
}
1144
1156
}
1145
1157
1146
1158
// This is a protection against GP40 locomotives that erroneously have positions pointing beyond frame count limit.
1147
1159
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 )
1149
1161
{
1150
1162
STFException . TraceInformation ( stf , "Some NumPositions entries refer to non-exisiting frames, trying to renumber" ) ;
1151
1163
Positions [ Positions . Count - 1 ] = FramesCount - 1 ;
@@ -1170,7 +1182,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1170
1182
}
1171
1183
// Avoid later repositioning, put every value to its Position
1172
1184
// But before resize Values if needed
1173
- if ( numValues != numPositions )
1185
+ if ( numValues != NumPositions )
1174
1186
{
1175
1187
while ( Values . Count <= Positions [ _ValuesRead ] )
1176
1188
{
@@ -1232,7 +1244,7 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1232
1244
1233
1245
// Only shuffle data in following cases
1234
1246
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 ) )
1236
1248
{
1237
1249
1238
1250
// Fixup Positions and Values collections first
@@ -1270,42 +1282,52 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1270
1282
Positions . Add ( 0 ) ;
1271
1283
// We will need the FramesCount later!
1272
1284
// We use Positions only here
1273
- Positions . Add ( FramesCount ) ;
1285
+ Positions . Add ( FramesCount - 1 ) ;
1274
1286
1275
1287
// Fill empty Values
1276
- for ( int i = 0 ; i < FramesCount ; i ++ )
1288
+ for ( int i = 0 ; i < ( FramesCount - 1 ) ; i ++ )
1277
1289
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 ) ;
1281
1300
}
1282
1301
else if ( Values . Count == 2 && Values [ 0 ] == 0 && Values [ 1 ] < MaxValue && Positions [ 0 ] == 0 && Positions [ 1 ] == 1 && Values . Count < FramesCount )
1283
1302
{
1284
1303
//This if clause covers among others following cases:
1285
1304
// 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 ) ;
1293
1313
// 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 ) ;
1297
1320
}
1298
-
1299
1321
else
1300
1322
{
1301
1323
//This if clause covers among others following cases:
1302
1324
// 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 )
1309
1331
//
1310
1332
// Fill empty Values
1311
1333
int iValues = 1 ;
@@ -1321,11 +1343,6 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1321
1343
}
1322
1344
iValues ++ ;
1323
1345
}
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 ) ;
1329
1346
}
1330
1347
1331
1348
// OK, we have a valid size of Positions and Values
@@ -1351,9 +1368,6 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1351
1368
p = Positions [ i ] ;
1352
1369
}
1353
1370
}
1354
-
1355
- // Don't need the MaxValue added before, remove it
1356
- Values . RemoveAt ( Values . Count - 1 ) ;
1357
1371
}
1358
1372
}
1359
1373
@@ -1378,13 +1392,21 @@ public CVCDiscrete(STFReader stf, string basepath, DiscreteStates discreteState)
1378
1392
break ;
1379
1393
}
1380
1394
}
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
+
1388
1410
} // End of Constructor
1389
1411
1390
1412
protected void ParseNewScreen ( STFReader stf )
@@ -1432,13 +1454,12 @@ public CVCMultiStateDisplay(STFReader stf, string basepath)
1432
1454
stf . ParseBlock ( new STFReader . TokenProcessor [ ] {
1433
1455
new STFReader . TokenProcessor ( "style" , ( ) => { MSStyles . Add ( ParseNumStyle ( stf ) ) ;
1434
1456
} ) ,
1435
- new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadFloatBlock ( STFReader . UNITS . None , null ) )
1457
+ new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadDoubleBlock ( null ) )
1436
1458
; } ) ,
1437
1459
} ) ; } ) ,
1438
1460
} ) ;
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 ( ) ;
1442
1463
} ) ,
1443
1464
new STFReader . TokenProcessor ( "ortsdisplay" , ( ) => { ParseDisplay ( stf ) ; } ) ,
1444
1465
new STFReader . TokenProcessor ( "ortsscreenpage" , ( ) => { ParseScreen ( stf ) ; } ) ,
@@ -1447,6 +1468,16 @@ public CVCMultiStateDisplay(STFReader stf, string basepath)
1447
1468
new STFReader . TokenProcessor ( "ortsunitsscalefactor" , ( ) => { UnitsScale = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
1448
1469
new STFReader . TokenProcessor ( "ortsunitsoffset" , ( ) => { UnitsOffset = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
1449
1470
} ) ;
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 ( ) ;
1450
1481
}
1451
1482
protected int ParseNumStyle ( STFReader stf )
1452
1483
{
@@ -1485,13 +1516,12 @@ public CVCAnimatedDisplay(STFReader stf, string basepath)
1485
1516
stf . ParseBlock ( new STFReader . TokenProcessor [ ] {
1486
1517
new STFReader . TokenProcessor ( "style" , ( ) => { MSStyles . Add ( ParseNumStyle ( stf ) ) ;
1487
1518
} ) ,
1488
- new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadFloatBlock ( STFReader . UNITS . None , null ) )
1519
+ new STFReader . TokenProcessor ( "switchval" , ( ) => { Values . Add ( stf . ReadDoubleBlock ( null ) )
1489
1520
; } ) ,
1490
1521
} ) ; } ) ,
1491
1522
} ) ;
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 ( ) ;
1495
1525
} ) ,
1496
1526
new STFReader . TokenProcessor ( "ortsdisplay" , ( ) => { ParseDisplay ( stf ) ; } ) ,
1497
1527
new STFReader . TokenProcessor ( "ortsscreenpage" , ( ) => { ParseScreen ( stf ) ; } ) ,
@@ -1500,6 +1530,16 @@ public CVCAnimatedDisplay(STFReader stf, string basepath)
1500
1530
new STFReader . TokenProcessor ( "ortsunitsscalefactor" , ( ) => { UnitsScale = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
1501
1531
new STFReader . TokenProcessor ( "ortsunitsoffset" , ( ) => { UnitsOffset = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ; } ) ,
1502
1532
} ) ;
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 ( ) ;
1503
1543
}
1504
1544
protected int ParseNumStyle ( STFReader stf )
1505
1545
{
0 commit comments