43 lines
1.3 KiB
Dart
43 lines
1.3 KiB
Dart
import '../models/timeline_entry.dart';
|
|
import '../models/projected_entry.dart';
|
|
import 'time_scale_service.dart';
|
|
|
|
class TimelineProjectionService {
|
|
const TimelineProjectionService();
|
|
|
|
Map<String, List<ProjectedEntry>> project({
|
|
required Iterable<TimelineEntry> entries,
|
|
required DateTime domainStart,
|
|
required DateTime domainEnd,
|
|
}) {
|
|
final byGroup = <String, List<ProjectedEntry>>{};
|
|
for (final e in entries) {
|
|
if (e.overlaps(domainStart, domainEnd)) {
|
|
final startX = TimeScaleService.mapTimeToPosition(
|
|
e.start.isBefore(domainStart) ? domainStart : e.start,
|
|
domainStart,
|
|
domainEnd,
|
|
).clamp(0.0, 1.0);
|
|
final endX = TimeScaleService.mapTimeToPosition(
|
|
e.end.isAfter(domainEnd) ? domainEnd : e.end,
|
|
domainStart,
|
|
domainEnd,
|
|
).clamp(0.0, 1.0);
|
|
|
|
final pe = ProjectedEntry(entry: e, startX: startX, endX: endX);
|
|
(byGroup[e.groupId] ??= <ProjectedEntry>[]).add(pe);
|
|
}
|
|
}
|
|
|
|
// Keep original order stable by lane then startX
|
|
for (final list in byGroup.values) {
|
|
list.sort((a, b) {
|
|
final laneCmp = a.entry.lane.compareTo(b.entry.lane);
|
|
if (laneCmp != 0) return laneCmp;
|
|
return a.startX.compareTo(b.startX);
|
|
});
|
|
}
|
|
return byGroup;
|
|
}
|
|
}
|