public void Render(Graphics g, Train train) { var style = new TrainStyle(train, attrs); if (!style.CalcedShow) { return; } var ardps = train.GetArrDeps(); var dir = GetTrainDirection(train); using (var pen = new Pen((Color)style.CalcedColor, style.CalcedWidth) { DashStyle = ds.ParseDashstyle(style.CalcedLineStyle) }) using (var brush = new SolidBrush((Color)style.CalcedColor)) { List <PointF> points = new List <PointF>(); bool hadFirstArrival = false, hadLastDeparture = false, isFirst = true; var stas = dir ? Enumerable.Reverse(stations) : stations; //TODO: Those helpers are ugly in this part of the code... // Render helpers float GetTimeY(TimeSpan time) => margin.Top + ((time - startTime).GetMinutes() * attrs.HeightPerHour / 60f); PointF?GetGutterPoint(bool arrival, StationX sx, TimeSpan time) { if (time == default) { return(null); } var x = arrival ^ dir ? sx.Left : sx.Right; return(new PointF(margin.Left + x, GetTimeY(time))); } PointF?GetInternalPoint(StationX sx, TimeSpan time, string track) { if (time == default || track == null || !sx.TrackOffsets.TryGetValue(track, out float x)) { return(null); } return(new PointF(margin.Left + x, GetTimeY(time))); } void MaybeAddPoint(PointF?point) { if (point.HasValue) { points.Add(point.Value); } } foreach (var sta in stas) { if (!ardps.ContainsKey(sta)) { continue; } var ardp = ardps[sta]; if (!ardp.HasMinOneTimeSet) { continue; } MaybeAddPoint(GetGutterPoint(true, stationOffsets[sta], ardp.Arrival)); MaybeAddPoint(GetInternalPoint(stationOffsets[sta], ardp.Arrival, ardp.ArrivalTrack)); foreach (var shunt in ardp.ShuntMoves) { MaybeAddPoint(GetInternalPoint(stationOffsets[sta], shunt.Time, shunt.SourceTrack)); MaybeAddPoint(GetInternalPoint(stationOffsets[sta], shunt.Time, shunt.TargetTrack)); } MaybeAddPoint(GetInternalPoint(stationOffsets[sta], ardp.Departure, ardp.DepartureTrack)); MaybeAddPoint(GetGutterPoint(false, stationOffsets[sta], ardp.Departure)); hadLastDeparture = ardp.Departure != default; if (isFirst) { hadFirstArrival = ardp.Arrival != default; } isFirst = false; } // Halbe Linien bei Abfahrten / Ankünften ohne Gegenstelle 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)); } // Verbindung zum Folgezug var transition = tt.GetTransition(train); if (transition != null && !hadLastDeparture && attrs.StationLines != StationLineStyle.None) { var lastStaOfFirst = GetSortedStations(train)?.LastOrDefault(); 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); // Bei Transitions 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 : -bezierOffsetT); p.AddBezier(points[i], points[i] - bezierOffset, control2, points[i + 1]); break; } } else { p.AddLine(points[i], points[i + 1]); // Normale Zuglinie } 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 = new[] { points[i].Y, points[i + 1].Y }; float[] xs = new[] { 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; float angle = CalcAngle(ys, xs, train); g.SaveTransform(); g.TranslateTransform(tx, ty); g.RotateTransform(-angle); g.DrawText(trainFont, brush, -(size.Width / 2), -(size.Height / 2), train.TName); g.RestoreTransform(); } g.DrawPath(pen, p); } } }
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); }
public Dictionary <Station, StationX> Render(Graphics g, Margins margin, float width, float height, bool drawHeader) { var stationFont = (Font)attrs.StationFont; // Reminder: Do not dispose, will be disposed with MFont instance! var firstStation = stations.First(); var lastStation = stations.Last(); var stationOffsets = new Dictionary <Station, StationX>(); var allTrackCount = stations.Select(s => s.Tracks.Count).Sum(); var stasWithTracks = stations.Count(s => s.Tracks.Any()); var allTrackWidth = (stasWithTracks + allTrackCount) * StationX.IndividualTrackOffset; var emSize = g.MeasureString(stationFont, "M").Width; StationX lastPos = null; foreach (var sta in stations) { var style = new StationStyle(sta, attrs); var kil = sta.Positions.GetPosition(route) - firstStation.Positions.GetPosition(route); var length = lastStation.Positions.GetPosition(route) - firstStation.Positions.GetPosition(route); if (!kil.HasValue || !length.HasValue) { throw new Exception("Unerwarteter Fehler beim Rendern der Route!"); } StationX posX; if (!attrs.MultiTrack) { posX = new StationX(sta, kil.Value, ((kil / length) * (width - margin.Right - margin.Left)).Value); } else { var availWidth = width - margin.Right - margin.Left - allTrackWidth; var lastKil = lastPos?.CurKilometer ?? 0f; var lastRight = lastPos?.Right ?? 0f; var leftOffset = (((kil / length) - (lastKil / length)) * availWidth).Value; posX = new StationX(sta, kil.Value, lastRight + leftOffset, true); } lastPos = posX; stationOffsets.Add(sta, posX); if (!style.CalcedShow) { continue; } using (var pen = new Pen((Color)style.CalcedColor, style.CalcedWidth) { DashStyle = ds.ParseDashstyle(style.CalcedLineStyle) }) using (var brush = new SolidBrush((Color)style.CalcedColor)) { if (!attrs.MultiTrack) { // Linie (Single-Track-Mode) g.DrawLine(pen, margin.Left + posX.Center, margin.Top - 5, margin.Left + posX.Center, height - margin.Bottom); } else { // Linie (Multi-Track-Mode) g.DrawLine(pen, margin.Left + posX.Left, margin.Top - 5, margin.Left + posX.Left, height - margin.Bottom); foreach (var trackX in posX.TrackOffsets) { g.DrawLine(pen, margin.Left + trackX.Value, margin.Top - 5, margin.Left + trackX.Value, height - margin.Bottom); } g.DrawLine(pen, margin.Left + posX.Right, margin.Top - 5, margin.Left + posX.Right, height - margin.Bottom); } if (!drawHeader) { continue; } // Stationsnamen if (attrs.DrawHeader) { var display = sta.ToString(attrs.DisplayKilometre, route); var size = g.MeasureString(stationFont, display); var addOffset = attrs.MultiTrack ? emSize + 3 : 0; if (attrs.StationVertical) { g.SaveTransform(); g.TranslateTransform(margin.Left + posX.Center + (size.Height / 2), margin.Top - 8 - addOffset - size.Width); g.RotateTransform(90); g.DrawText(stationFont, brush, 0, 0, display); g.RestoreTransform(); } else { g.DrawText(stationFont, brush, margin.Left + posX.Center - (size.Width / 2), margin.Top - size.Height - addOffset - 5, display); } if (attrs.MultiTrack) { foreach (var track in posX.TrackOffsets) { var trackSize = g.MeasureString(stationFont, track.Key); g.DrawText(stationFont, brush, margin.Left + track.Value - (trackSize.Width / 2), margin.Top - trackSize.Height - 5, track.Key); } } } }// Disposing Pens and Brushes } return(stationOffsets); }
public Dictionary <Station, StationRenderProps> Render(Graphics g, Margins margin, float width, float height, bool drawHeader, bool exportColor) { var stationFont = (Font)attrs.StationFont; // Reminder: Do not dispose, will be disposed with MFont instance! var stationOffsets = new Dictionary <Station, StationRenderProps>(); var raw = path.GetRawPath().ToList(); var allTrackCount = raw.Select(s => s.Tracks.Count).Sum(); var stasWithTracks = raw.Count(s => s.Tracks.Any()); var allTrackWidth = (stasWithTracks + allTrackCount) * StationRenderProps.IndividualTrackOffset; var verticalTrackOffset = GetTrackOffset(g, stationFont) + TOP_GAP; float length = 0f; PathEntry lastpe = null; foreach (var sta in path.PathEntries) { var er = path.GetEntryRoute(sta.Station); if (er != Timetable.UNASSIGNED_ROUTE_ID) { length += (sta !.Station.Positions.GetPosition(er) - lastpe !.Station.Positions.GetPosition(er)) !.Value; } lastpe = sta; } StationRenderProps lastPos = null; lastpe = null; float kil = 0f; foreach (var sta in path.PathEntries) { var style = new StationStyle(sta.Station, attrs); var er = path.GetEntryRoute(sta.Station); if (er != Timetable.UNASSIGNED_ROUTE_ID) { kil += (sta !.Station.Positions.GetPosition(er) - lastpe !.Station.Positions.GetPosition(er)) !.Value; } lastpe = sta; StationRenderProps posX; if (!attrs.MultiTrack) { posX = new StationRenderProps(sta.Station, kil, ((kil / length) * (width - margin.Right - margin.Left))); } else { var availWidth = width - margin.Right - margin.Left - allTrackWidth; var lastKil = lastPos?.CurKilometer ?? 0f; var lastRight = lastPos?.Right ?? 0f; var leftOffset = (((kil / length) - (lastKil / length)) * availWidth); posX = new StationRenderProps(sta.Station, kil, lastRight + leftOffset, true); } lastPos = posX; stationOffsets.Add(sta.Station, posX); if (!style.CalcedShow) { continue; } 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)); if (!attrs.MultiTrack) { // Linie (Single-Track-Mode) g.DrawLine(pen, margin.Left + posX.Center, margin.Top - TOP_GAP, margin.Left + posX.Center, height - margin.Bottom); } else { // Linie (Multi-Track-Mode) g.DrawLine(pen, margin.Left + posX.Left, margin.Top - TOP_GAP, margin.Left + posX.Left, height - margin.Bottom); foreach (var trackX in posX.TrackOffsets) { g.DrawLine(pen, margin.Left + trackX.Value, margin.Top - TOP_GAP, margin.Left + trackX.Value, height - margin.Bottom); } g.DrawLine(pen, margin.Left + posX.Right, margin.Top - TOP_GAP, margin.Left + posX.Right, height - margin.Bottom); } if (!drawHeader) { continue; } // Stationsnamen if (attrs.DrawHeader) { var display = StationDisplay(sta); var size = g.MeasureString(stationFont, display); if (attrs.StationVertical) { var matrix = g.Transform.Clone(); g.TranslateTransform(margin.Left + posX.Center + (size.Height / 2), margin.Top - 8 - verticalTrackOffset - size.Width); g.RotateTransform(90); g.DrawText(stationFont, brush, 0, 0, display); g.Transform = matrix; } else { g.DrawText(stationFont, brush, margin.Left + posX.Center - (size.Width / 2), margin.Top - size.Height - verticalTrackOffset - TOP_GAP, display); } if (attrs.MultiTrack) { foreach (var track in posX.TrackOffsets) { var trackSize = g.MeasureString(stationFont, track.Key); if (attrs.StationVertical) { var matrix = g.Transform.Clone(); g.TranslateTransform(margin.Left + track.Value + (trackSize.Height / 2), margin.Top - 8 - trackSize.Width); g.RotateTransform(90); g.DrawText(stationFont, brush, 0, 0, track.Key); g.Transform = matrix; } else { g.DrawText(stationFont, brush, margin.Left + track.Value - (trackSize.Width / 2), margin.Top - trackSize.Height - TOP_GAP, track.Key); } } } } } return(stationOffsets); }