/// <inheritdoc /> public Dictionary <Station, ArrDep> GetArrDepsUnsorted() { return(baseTrain .GetArrDepsUnsorted() .ToDictionary( kvp => kvp.Key, kvp => link.ProcessArrDep(kvp.Value, countingIndex))); }
public TrainPathData(Timetable tt, ITrain train) : base(tt) { var path = train.GetPath().ToArray(); var arrDeps = train.GetArrDepsUnsorted(); Entries = Init(path, (s, r) => { arrDeps.TryGetValue(s, out var ardp); return(new TrainPathEntry(s, ardp, r)); }); PathEntries = Entries.OfType <TrainPathEntry>().ToArray(); }
private IEnumerable <Station> GetSortedStations(ITrain train) { var path = train.GetPath(); var arrdeps = train.GetArrDepsUnsorted(); foreach (var sta in path) { if (arrdeps[sta].HasMinOneTimeSet) { yield return(sta); } } }
/// <summary> /// Get the direction the train passes through this station. /// </summary> /// <remarks>This direction may differ from train to train (at the same station) and from station to station (with the same train).</remarks> /// <returns>If null, the train does not pass through this station.</returns> public TrainDirection?GetTrainDirectionAtStation(ITrain train, Station sta) { var stasAfter = GetStationsInDir(TrainDirection.ti, sta); var stasBefore = GetStationsInDir(TrainDirection.ta, sta); var path = train.GetPath(); if (!path.Contains(sta)) { return(null); } var ardeps = train.GetArrDepsUnsorted(); var isStopping = ardeps[sta].HasMinOneTimeSet; Station?nextStopStation = path.Where(s => stasAfter.Contains(s)).FirstOrDefault(s => ardeps[s].HasMinOneTimeSet); Station?lastStopStation = null; if (nextStopStation == null || !isStopping) { lastStopStation = path.Where(s => stasBefore.Contains(s)).FirstOrDefault(s => ardeps[s].HasMinOneTimeSet); } // We have a stop at this station, use time difference between first/last and current. if (isStopping) { if (nextStopStation == null) { if (lastStopStation == null) { return(null); } var lastStopTime = ardeps[lastStopStation].LastSetTime; return(lastStopTime < ardeps[sta].FirstSetTime ? TrainDirection.ti : TrainDirection.ta); } var nextStopTime = ardeps[nextStopStation].FirstSetTime; return(nextStopTime > ardeps[sta].LastSetTime ? TrainDirection.ti : TrainDirection.ta); } if (lastStopStation == null || nextStopStation == null) { return(null); // We are (proven!) not running over this station. } // Passthrough, use difference between first and last return(ardeps[nextStopStation].FirstSetTime > ardeps[lastStopStation].LastSetTime ? TrainDirection.ti : TrainDirection.ta); }
public void Render(Graphics g, ITrain train, bool exportColor) { var style = new TrainStyle(train, attrs); if (!style.CalcedShow) { return; } var ardps = train.GetArrDepsUnsorted(); var dir = GetTrainDirection(train); using var pen = new Pen(style.CalcedColor.ToSD(exportColor), style.CalcedWidth) { DashPattern = ds.ParseDashstyle(style.CalcedLineStyle) }; using var brush = new SolidBrush(style.CalcedColor.ToSD(exportColor)); List <PointF> points = new List <PointF>(); bool hadFirstArrival = false, hadLastDeparture = false, isFirst = true; var stas = dir ? Enumerable.Reverse(stations) : stations; int trainTravelsRouteCount = 0; foreach (var sta in stas) { if (!ardps.ContainsKey(sta)) { continue; } var ardp = ardps[sta]; trainTravelsRouteCount++; if (!ardp.HasMinOneTimeSet) { continue; } MaybeAddPoint(points, GetGutterPoint(true, dir, stationOffsets[sta], ardp.Arrival)); MaybeAddPoint(points, GetInternalPoint(stationOffsets[sta], ardp.Arrival, ardp.ArrivalTrack)); foreach (var shunt in ardp.ShuntMoves) { MaybeAddPoint(points, GetInternalPoint(stationOffsets[sta], shunt.Time, shunt.SourceTrack)); MaybeAddPoint(points, GetInternalPoint(stationOffsets[sta], shunt.Time, shunt.TargetTrack)); } MaybeAddPoint(points, GetInternalPoint(stationOffsets[sta], ardp.Departure, ardp.DepartureTrack)); MaybeAddPoint(points, GetGutterPoint(false, dir, stationOffsets[sta], ardp.Departure)); hadLastDeparture = ardp.Departure != default; if (isFirst) { hadFirstArrival = ardp.Arrival != default; } isFirst = false; } // Halbe Linien bei Abfahrten / Ankünften ohne Gegenstelle if (attrs.DrawNetworkTrains) { var hly = !dir ? 20 : -20; if (hadLastDeparture) { points.Add(points.Last() + new Size(50, hly)); } if (hadFirstArrival) { points.Insert(0, points.First() - new Size(50, hly)); } } else if (trainTravelsRouteCount <= 1) { return; // This train has only one station on this route and we don't draw network trains. } if (points.Count == 0) { return; // This train is not travelling on this route } // Transition to the next train; filtered by days and station. var lastStaOfFirst = GetSortedStations(train)?.LastOrDefault(); var transition = tt.GetTransition(train, renderDays, lastStaOfFirst); if (transition != null && !hadLastDeparture && attrs.StationLines != StationLineStyle.None && transition.Days.IsIntersecting(renderDays)) { var firstStaOfNext = GetSortedStations(transition)?.FirstOrDefault(); if (lastStaOfFirst == firstStaOfNext) { var departure = transition.GetArrDep(firstStaOfNext).Departure; points.Add(new PointF(points.Last().X, GetTimeY(departure))); } } using var p = new GraphicsPath(); for (int i = 0; i < points.Count; i += 1) { if (points.Count <= i + 1) { continue; } var isStationLine = (int)points[i].X == (int)points[i + 1].X; if (isStationLine) { var preX = i > 0 ? points[i - 1].X : 0; var postX = i < points.Count - 2 ? points[i + 2].X : 0; var x = points[i].X; var isTransition = isStationLine && (points.Count == i + 2 || Math.Sign(preX - x) == Math.Sign(postX - x)); float bezierFactor = !isTransition ? ((preX < postX) ? -1 : 1) : // preX < postX --> TrainDirection.ti Math.Sign(preX - x); // Transition if (isTransition) { bezierFactor *= 0.5f; } var bezierOffset = new SizeF(bezierFactor * 14, (points[i + 1].Y - points[i].Y) / -4.0f); var bezierOffsetT = new SizeF(bezierOffset.Width, -bezierOffset.Height); switch (attrs.StationLines) { case StationLineStyle.None: p.MoveTo(points[i + 1]); break; case StationLineStyle.Normal: p.AddLine(points[i], points[i + 1]); break; case StationLineStyle.Cubic: var control2 = points[i + 1] + (!isTransition ? bezierOffset : (SizeF.Empty - bezierOffsetT)); p.AddBezier(points[i], points[i] - bezierOffset, control2, points[i + 1]); break; } } else { p.AddLine(points[i], points[i + 1]); // Normal line between stations } if (points[i].X == points[i + 1].X || points[i].Y == points[i + 1].Y) { continue; } // Zugnummern zeichnen var trainFont = (Font)attrs.TrainFont; var size = g.MeasureString(trainFont, train.TName); float[] ys = { points[i].Y, points[i + 1].Y }; float[] xs = { points[i].X, points[i + 1].X }; float ty = ys.Min() + (ys.Max() - ys.Min()) / 2 - (size.Height / 2); float tx = xs.Min() + (xs.Max() - xs.Min()) / 2; if (g.Clip.IsVisible(new PointF(tx, ty))) // translated drawing does not respect clip (Idk why) { float angle = CalcAngle(ys, xs, train); var matrix = g.Transform.Clone(); g.TranslateTransform(tx, ty); g.RotateTransform(-angle); g.DrawText(trainFont, brush, -(size.Width / 2), -(size.Height / 2), train.TName); g.Transform = matrix; } } g.DrawPath(pen, p); }