diff --git a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs index c767109..fb1efe4 100644 --- a/PamhagenSysCtrl/Helpers/PamhagenGraph.cs +++ b/PamhagenSysCtrl/Helpers/PamhagenGraph.cs @@ -431,13 +431,13 @@ namespace PamhagenSysCtrl.Helpers { } } - public void ColorPipesAdjacientToPath(Path path, Brush? color1, Brush? color2) { + public void ColorPipesAdjacientToPath(Path path, Brush? color1, bool flowArrows, Brush? color2) { var color = color1; var flowPathOffset = 0.0; HashSet visited = []; foreach (var (edge, node) in path.Hops) { edge.Highlight = color1; - edge.Flow = color1 == PamhagenBrushes.Green ? (edge.End == node, flowPathOffset) : null; + edge.Flow = flowArrows ? (edge.End == node, flowPathOffset) : null; if (edge.Flow != null) flowPathOffset += edge.FlowLength; visited.Add(edge); if (node is Pump) color = color2; diff --git a/PamhagenSysCtrl/Helpers/Pipeline/FlowArrow.cs b/PamhagenSysCtrl/Helpers/Pipeline/FlowArrow.cs index 4d1ed07..f271262 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/FlowArrow.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/FlowArrow.cs @@ -7,10 +7,10 @@ using System.Windows.Shapes; namespace PamhagenSysCtrl.Helpers.Pipeline { internal sealed class FlowArrow { - private const double Speed = 45; - private const double Spacing = 40; - private const double HalfLength = 5; - private const double HalfWidth = 3.5; + public const double SPEED = 20; + public const double SPACING = 15; + public const double LENGTH = 7; + public const double WIDTH = 7; private static readonly Stopwatch Clock = Stopwatch.StartNew(); @@ -51,17 +51,8 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { return; } - var points = flow.Value.TowardEnd - ? _path - : [.. _path.Reverse()]; - var length = GetLength(points[0], points[1]) + GetLength(points[1], points[2]); - if (length < 16) { - Stop(); - return; - } - - _points = points; - _length = length; + _points = flow.Value.TowardEnd ? _path : [.. _path.Reverse()]; + _length = GetLength(_points[0], _points[1]) + GetLength(_points[1], _points[2]); _pathOffset = flow.Value.PathOffset; if (!_isActive) { CompositionTarget.Rendering += OnRendering; @@ -76,13 +67,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } private void Render() { - var distance = PositiveModulo(Clock.Elapsed.TotalSeconds * Speed - _pathOffset, Spacing); + var distance = ((Clock.Elapsed.TotalSeconds * SPEED - _pathOffset) % SPACING + SPACING) % SPACING; var arrowIndex = 0; while (distance <= _length) { var arrow = GetArrow(arrowIndex++); SetPosition(arrow, distance); arrow.Visibility = Visibility.Visible; - distance += Spacing; + distance += SPACING; } while (arrowIndex < _layer.Children.Count) { _layer.Children[arrowIndex++].Visibility = Visibility.Collapsed; @@ -95,12 +86,15 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } var arrow = new Polygon() { - Fill = Brushes.Black, + Fill = new SolidColorBrush(Color.FromArgb(0x60, 0, 0, 0)), IsHitTestVisible = false, Points = [ - new Point(HalfLength, 0), - new Point(-HalfLength, HalfWidth), - new Point(-HalfLength, -HalfWidth), + new(-LENGTH / 2, -WIDTH / 2), + new(0, -WIDTH / 2), + new(LENGTH / 2, 0), + new(0, WIDTH / 2), + new(-LENGTH / 2, WIDTH / 2), + new(0, 0), ], RenderTransform = new MatrixTransform(Matrix.Identity), }; @@ -109,29 +103,51 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { } private void SetPosition(Polygon arrow, double distance) { - var firstLength = GetLength(_points[0], _points[1]); - var secondLength = GetLength(_points[1], _points[2]); - Point start; - Point end; - double segmentDistance; - if ((firstLength > 0 && distance <= firstLength) || secondLength == 0) { - start = _points[0]; - end = _points[1]; - segmentDistance = distance; + const double blendDistance = WIDTH / 2; + double firstLength = GetLength(_points[0], _points[1]); + double blendStartDistance = firstLength - blendDistance; + double blendEndDistance = firstLength + blendDistance; + + Vector v1 = Normalize(_points[0], _points[1]); + Vector v2 = Normalize(_points[1], _points[2]); + Point blendStart = _points[1] - v1 * blendDistance; + Point blendEnd = _points[1] + v2 * blendDistance; + + Point position; + Vector direction; + if (distance < blendStartDistance) { + position = _points[0] + v1 * distance; + direction = v1; + } else if (distance <= blendEndDistance) { + double t = (distance - blendStartDistance) / (blendEndDistance - blendStartDistance); + double u = 1.0 - t; + + position = new Point( + u * u * blendStart.X + + 2 * u * t * _points[1].X + + t * t * blendEnd.X, + + u * u * blendStart.Y + + 2 * u * t * _points[1].Y + + t * t * blendEnd.Y); + + direction = new Vector( + 2 * u * (_points[1].X - blendStart.X) + + 2 * t * (blendEnd.X - _points[1].X), + + 2 * u * (_points[1].Y - blendStart.Y) + + 2 * t * (blendEnd.Y - _points[1].Y)); + + direction.Normalize(); } else { - start = _points[1]; - end = _points[2]; - segmentDistance = distance - firstLength; + double d = distance - firstLength; + position = _points[1] + v2 * d; + direction = v2; } - var segmentLength = GetLength(start, end); - if (segmentLength == 0) return; - var ux = (end.X - start.X) / segmentLength; - var uy = (end.Y - start.Y) / segmentLength; - var x = start.X + ux * segmentDistance; - var y = start.Y + uy * segmentDistance; - ((MatrixTransform)arrow.RenderTransform).Matrix = new Matrix( - ux, uy, -uy, ux, x, y); + double ux = direction.X; + double uy = direction.Y; + (arrow.RenderTransform as MatrixTransform)?.Matrix = new Matrix(ux, uy, -uy, ux, position.X, position.Y); } private void Stop() { @@ -142,14 +158,16 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { _layer.Visibility = Visibility.Collapsed; } - private static double PositiveModulo(double value, double modulus) { - return (value % modulus + modulus) % modulus; - } - private static double GetLength(Point start, Point end) { var dx = end.X - start.X; var dy = end.Y - start.Y; return Math.Sqrt(dx * dx + dy * dy); } + + private static Vector Normalize(Point p1, Point p2) { + Vector v = p2 - p1; + if (v.Length > 0) v.Normalize(); + return v; + } } } diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs b/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs index ba4c07c..031d489 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Inlet.cs @@ -83,7 +83,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { public void Update() { _inner1?.Stroke = Highlight ?? Brushes.DarkGray; _inner2?.Stroke = Highlight ?? Brushes.DarkGray; - _flowArrow?.Update(Highlight == PamhagenBrushes.Green ? Flow : null); + _flowArrow?.Update(Flow); } } } diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Outlet.cs b/PamhagenSysCtrl/Helpers/Pipeline/Outlet.cs index 4450984..0aaa13b 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Outlet.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Outlet.cs @@ -49,7 +49,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { Y2 = y2, StrokeThickness = 10, Stroke = Brushes.Black, - StrokeStartLineCap = PenLineCap.Flat, + StrokeStartLineCap = PenLineCap.Round, StrokeEndLineCap = PenLineCap.Round, }; _inner1 = new Line() { @@ -59,7 +59,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { Y2 = y2, StrokeThickness = 6, Stroke = Brushes.DarkGray, - StrokeStartLineCap = PenLineCap.Flat, + StrokeStartLineCap = PenLineCap.Round, StrokeEndLineCap = PenLineCap.Round, }; _outer2 = new Line() { @@ -79,22 +79,22 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { Y2 = y3, StrokeThickness = 6, Stroke = Brushes.DarkGray, - StrokeStartLineCap = PenLineCap.Square, - StrokeEndLineCap = PenLineCap.Square, + StrokeStartLineCap = PenLineCap.Round, + StrokeEndLineCap = PenLineCap.Round, }; canvas.Children.Add(_outer1); canvas.Children.Add(_outer2); + canvas.Children.Add(_hopper); canvas.Children.Add(_inner1); canvas.Children.Add(_inner2); _flowArrow = FlowArrow.Create(canvas, _inner1!, _inner2!); - canvas.Children.Add(_hopper); } public void Update() { _inner1?.Stroke = Highlight ?? Brushes.DarkGray; _inner2?.Stroke = Highlight ?? Brushes.DarkGray; _hopper?.Fill = Highlight ?? Brushes.DarkGray; - _flowArrow?.Update(Highlight == PamhagenBrushes.Green ? Flow : null); + _flowArrow?.Update(Flow); } } } diff --git a/PamhagenSysCtrl/Helpers/Pipeline/Pipe.cs b/PamhagenSysCtrl/Helpers/Pipeline/Pipe.cs index 20fffa9..318f230 100644 --- a/PamhagenSysCtrl/Helpers/Pipeline/Pipe.cs +++ b/PamhagenSysCtrl/Helpers/Pipeline/Pipe.cs @@ -54,7 +54,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline { public void Update() { _inner?.Stroke = Highlight ?? Brushes.DarkGray; - _flowArrow?.Update(Highlight == PamhagenBrushes.Green ? Flow : null); + _flowArrow?.Update(Flow); } } } diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs index 9c66998..b522be0 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs @@ -76,7 +76,7 @@ namespace PamhagenSysCtrl.Windows { e.Flow = null; } foreach (var p in Graph.SelectedPaths) { - Graph.ColorPipesAdjacientToPath(p, PamhagenBrushes.Green, PamhagenBrushes.DimOrange); + Graph.ColorPipesAdjacientToPath(p, PamhagenBrushes.Green, true, PamhagenBrushes.DimOrange); } var hover = Graph.GetHover(pos.X, pos.Y); if (_lastSource != null && (hover.Count > 0 || _path.Count > 0)) { @@ -87,17 +87,16 @@ namespace PamhagenSysCtrl.Windows { if (points.Count >= 2) { var res = Graph.GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked)); if (res is Path path) { - Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, PamhagenBrushes.DimOrange); + Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, false, PamhagenBrushes.DimOrange); } } } else if (_lastSource == null && hover.Count > 0 && Graph.SelectedPaths.Any(p => p.Start == hover.First())) { var path = Graph.SelectedPaths.First(p => p.Start == hover.First()); - Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Red, null); + Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Red, true, null); } if (_lastSource != null) { foreach (var edge in _lastSource.Outputs) { edge.Highlight = PamhagenBrushes.Green; - edge.Flow = (true, 0); } } Graph.Update(hover, down);