Skip to content

Commit

Permalink
feat: added visualization for two track with single track equipment. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rawi-coding committed Jan 31, 2025
1 parent a5852db commit 031d0ea
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,9 @@ void main() {
of: segment1CABStop, matching: find.byKey(TrackEquipmentCellBody.extendedSpeedReversingPossibleKey));
expect(segment1CABStopTrackEquipment, findsOneWidget);

// check two tracks with single track equipment on Gilly-Bursinel
_checkTrackEquipmentOnServicePoint('Gilly-Bursinel', TrackEquipmentCellBody.twoTracksWithSingleTrackEquipmentKey);

// check ConventionalSpeedReversingImpossible from Morges to Onnens-Bonvillars
await tester.dragUntilVisible(find.text('Onnens-Bonvillars'), scrollableFinder, const Offset(0, -50));
final segment2CABStart = findDASTableRowByText('12.5').first;
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'dart:math';

import 'package:das_client/app/pages/journey/train_journey/widgets/table/config/track_equipment_render_data.dart';
import 'package:das_client/app/widgets/table/das_table_theme.dart';
import 'package:das_client/model/journey/track_equipment_segment.dart';
import 'package:flutter/material.dart';

class TrackEquipmentCellBody extends StatelessWidget {
static const Key conventionalExtendedSpeedBorderKey = Key('conventional_extended_speed_border_key');
static const Key twoTracksWithSingleTrackEquipmentKey = Key('two_tracks_with_single_track_equipment_key');
static const Key extendedSpeedReversingPossibleKey = Key('extended_speed_reversing_possible_key');
static const Key extendedSpeedReversingImpossibleKey = Key('extended_speed_reversing_impossible_key');
static const Key conventionalSpeedReversingImpossible = Key('conventional_speed_reversing_impossible_key');
Expand Down Expand Up @@ -33,6 +36,8 @@ class TrackEquipmentCellBody extends StatelessWidget {
_extSpeedReversingImpossible(context, height),
if (trackEquipmentType == TrackEquipmentType.etcsL2ConvSpeedReversingImpossible)
_convSpeedReversingImpossible(context, height),
if (trackEquipmentType == TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment)
_twoTracksWithSingleTrackEquipment(context, height),
],
);
},
Expand Down Expand Up @@ -109,16 +114,41 @@ class TrackEquipmentCellBody extends StatelessWidget {
);
}

/// Calculation of bottom is used to draw over table border if necessary
Widget _twoTracksWithSingleTrackEquipment(BuildContext context, double height) {
final width = 3.0;
final borderWidth = 1.0;
return Positioned(
key: twoTracksWithSingleTrackEquipmentKey,
top: _calculateTop(height),
bottom: _calculateBottom(context, height),
left: 1.0,
child: CustomPaint(
key: TrackEquipmentCellBody.conventionalSpeedReversingImpossible,
painter: _CumulativeDashedLinePainter(
cumulativeHeight: renderData.cumulativeHeight,
dashHeights: [7.0],
dashSpace: 5.0,
width: width,
borderWidth: borderWidth,
),
child: SizedBox(height: double.infinity, width: width + borderWidth * 2),
),
);
}

/// calculation of bottom is used to draw over table border and handle start or end if necessary
double _calculateBottom(BuildContext context, double height) {
if (renderData.isCABEnd) return height / 2;
if (renderData.isStart && renderData.isEnd) return height * 0.25;
if (renderData.isEnd) return height * 0.5;

final tableBorder = DASTableTheme.of(context)?.data.tableBorder;
return -(tableBorder?.horizontalInside.width ?? 0);
}

/// calculation of top is used to draw over table border and handle start or end if necessary
double _calculateTop(double height) {
if (renderData.isCABStart) return height / 2;
if (renderData.isStart && renderData.isEnd) return height * 0.25;
if (renderData.isStart) return height * 0.5;

return renderData.isConventionalExtendedSpeedBorder ? conventionalExtendedSpeedBorderSpace : 0;
}
Expand Down Expand Up @@ -152,12 +182,14 @@ class _CumulativeDashedLinePainter extends CustomPainter {
this.dashHeights = const [4.0],
this.dashSpace = 4.0,
this.width = 3.0,
this.borderWidth,
}) : assert(dashHeights.isNotEmpty);

final double cumulativeHeight;
final List<double> dashHeights;
final double dashSpace;
final double width;
final double? borderWidth;

@override
void paint(Canvas canvas, Size size) {
Expand All @@ -171,19 +203,37 @@ class _CumulativeDashedLinePainter extends CustomPainter {

int dashIndex = 0;
double startY = -offsetInPattern;
double endY = 0;
while (startY < size.height) {
dashIndex = dashIndex % dashHeights.length;
final endY = startY + dashHeights[dashIndex];
endY = startY + dashHeights[dashIndex];
if (endY > 0) {
canvas.drawLine(
Offset(size.width / 2, startY),
Offset(size.width / 2, endY),
paint,
);
canvas.drawLine(Offset(size.width * 0.5, startY), Offset(size.width * 0.5, endY), paint);
}
startY = endY + dashSpace;
dashIndex++;
}

// adds border on left/right of dashed line
if (borderWidth != null) {
_drawBorder(canvas, size, endY);
}
}

void _drawBorder(Canvas canvas, Size size, double patternEndY) {
final borderPaint = Paint()
..color = Colors.black
..strokeWidth = borderWidth!;

final endY = max(size.height, patternEndY);

// Draw left border line
final leftDx = (size.width - width - borderWidth!) * 0.5;
canvas.drawLine(Offset(leftDx, 0), Offset(leftDx, endY), borderPaint);

// Draw right border line
final rightDx = (size.width + width + borderWidth!) * 0.5;
canvas.drawLine(Offset(rightDx, 0), Offset(rightDx, endY), borderPaint);
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,71 @@ import 'package:das_client/model/journey/track_equipment_segment.dart';
class TrackEquipmentRenderData {
const TrackEquipmentRenderData({
this.cumulativeHeight = 0.0,
this.isCABStart = false,
this.isCABEnd = false,
this.isStart = false,
this.isEnd = false,
this.isConventionalExtendedSpeedBorder = false,
this.trackEquipmentType,
});

final double cumulativeHeight;
final bool isCABStart;
final bool isCABEnd;
final bool isStart;
final bool isEnd;
final bool isConventionalExtendedSpeedBorder;
final TrackEquipmentType? trackEquipmentType;

static TrackEquipmentRenderData? from(List<BaseData> rowData, Metadata metadata, int index) {
final data = rowData[index];
final nonStandardTrackEquipmentSegments = metadata.nonStandardTrackEquipmentSegments;
final trackEquipment = nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull;
if (trackEquipment == null || !trackEquipment.isEtcsL2Segment) return null;
final matchingSegment = nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull;
if (matchingSegment == null) return null;

return TrackEquipmentRenderData(
trackEquipmentType: trackEquipment.type,
cumulativeHeight: _calculateTrackEquipmentCumulativeHeight(rowData, metadata, trackEquipment, index),
trackEquipmentType: matchingSegment.type,
cumulativeHeight: _calculateTrackEquipmentCumulativeHeight(rowData, metadata, matchingSegment, index),
isConventionalExtendedSpeedBorder: _isConventionalExtendedSpeedBorder(rowData, metadata, index),
isCABStart: data is CABSignaling ? data.isStart : false,
isCABEnd: data is CABSignaling ? data.isEnd : false,
isStart: _isStart(data, matchingSegment, rowData),
isEnd: _isEnd(data, matchingSegment, rowData),
);
}

/// calculates the cumulative height of the track equipment "line" of previous rows with the same type as given [trackEquipment].
/// checks if current data is the end of the [NonStandardTrackEquipmentSegment] for the given rowData
static bool _isEnd(BaseData data, NonStandardTrackEquipmentSegment segment, List<BaseData> rowData) {
if (data is CABSignaling && data.isEnd) {
return true;
} else if (segment.isEtcsL2Segment) {
// ETCS level 2 end is marked by CABSignaling
return false;
}

return rowData.inNonStandardTrackEquipmentSegment(segment).last == data;
}

/// checks if current data is the start of the [NonStandardTrackEquipmentSegment] for the given rowData
static bool _isStart(BaseData data, NonStandardTrackEquipmentSegment segment, List<BaseData> rowData) {
if (data is CABSignaling && data.isStart) {
return true;
} else if (segment.isEtcsL2Segment) {
// ETCS level 2 start is marked by CABSignaling
return false;
}

return rowData.inNonStandardTrackEquipmentSegment(segment).first == data;
}

/// calculates the cumulative height of the track equipment "line" of previous rows with the same type as given [segment].
static double _calculateTrackEquipmentCumulativeHeight(
List<BaseData> rowData, Metadata metadata, NonStandardTrackEquipmentSegment trackEquipment, int index) {
List<BaseData> rowData, Metadata metadata, NonStandardTrackEquipmentSegment segment, int index) {
var cumulativeHeight = 0.0;
var searchIndex = index - 1;
while (searchIndex >= 0) {
final data = rowData[searchIndex];
final testTrackEquipment = metadata.nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull;
final segment = metadata.nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull;

if (testTrackEquipment == null || testTrackEquipment.type != trackEquipment.type) {
if (segment == null || segment.type != segment.type) {
break;
}

cumulativeHeight += _rowHeight(data);
cumulativeHeight += _rowHeight(data, segment, rowData);

// if is conventional extended speed border, reduce by it's height as it is not part of the dashed line.
if (_isConventionalExtendedSpeedBorder(rowData, metadata, searchIndex)) {
Expand All @@ -64,15 +88,19 @@ class TrackEquipmentRenderData {
}

/// returns height of track equipment "line" for given row
static double _rowHeight(BaseData data) {
static double _rowHeight(BaseData data, NonStandardTrackEquipmentSegment segment, List<BaseData> rowData) {
late double rowHeight;
switch (data.type) {
case Datatype.servicePoint:
return ServicePointRow.rowHeight;
case Datatype.cabSignaling:
return (data as CABSignaling).isStart ? BaseRowBuilder.rowHeight / 2 : BaseRowBuilder.rowHeight;
rowHeight = ServicePointRow.rowHeight;
break;
default:
return BaseRowBuilder.rowHeight;
rowHeight = BaseRowBuilder.rowHeight;
}

final isStartOrEnd = _isStart(data, segment, rowData) || _isEnd(data, segment, rowData);

return isStartOrEnd ? rowHeight / 2 : rowHeight;
}

/// checks if between current and previous track equipment is a border between extended and conventional speed.
Expand Down
14 changes: 13 additions & 1 deletion das_client/lib/model/journey/track_equipment_segment.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:das_client/model/journey/base_data.dart';
import 'package:das_client/util/comparators.dart';

/// Represents a segment with non standard track equipment. Standard is bidirectional ETCS L1LS.
Expand Down Expand Up @@ -28,7 +29,9 @@ class NonStandardTrackEquipmentSegment implements Comparable {

/// checks if the given order is part of this segment.
bool appliesToOrder(int order) {
if (startOrder != null && endOrder != null) {
if(startOrder == null && endOrder == null) {
return true; // applies for whole journey
} else if (startOrder != null && endOrder != null) {
return startOrder! <= order && order <= endOrder!;
} else if (startOrder != null) {
return startOrder! <= order;
Expand All @@ -54,6 +57,7 @@ class NonStandardTrackEquipmentSegment implements Comparable {

enum TrackEquipmentType {
etcsL1ls2TracksWithSingleTrackEquipment,
etcsL1lsSingleTrackNoBlock,
etcsL2ConvSpeedReversingImpossible,
etcsL2ExtSpeedReversingPossible,
etcsL2ExtSpeedReversingImpossible;
Expand All @@ -74,6 +78,14 @@ enum TrackEquipmentType {

// extensions

extension BaseDataListSegmentExtension on Iterable<BaseData> {
Iterable<BaseData> inNonStandardTrackEquipmentSegment(NonStandardTrackEquipmentSegment segment) {
final dataInsideSegment = where((data) => segment.appliesToOrder(data.order)).toList();
dataInsideSegment.sort();
return dataInsideSegment;
}
}

extension NonStandardTrackEquipmentSegmentsExtension on Iterable<NonStandardTrackEquipmentSegment> {
Iterable<NonStandardTrackEquipmentSegment> appliesToOrder(int order) =>
where((segment) => segment.appliesToOrder(order));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ enum SferaTrackEquipmentType implements XmlEnum {
xmlValue: 'ETCS-L1LS-2TracksWithSingleTrackEquipment',
trackEquipmentType: TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment,
),
etcsL1lsSingleTrackNoBlock(
xmlValue: 'ETCS-L1LS-singleTrackNoBlock',
trackEquipmentType: TrackEquipmentType.etcsL1lsSingleTrackNoBlock,
),
etcsL2ConvSpeedReversingImpossible(
xmlValue: 'ETCS-L2-convSpeedReversingImpossible',
trackEquipmentType: TrackEquipmentType.etcsL2ConvSpeedReversingImpossible,
Expand Down
Loading

0 comments on commit 031d0ea

Please sign in to comment.