diff --git a/MapControl/Avalonia/MapPolypoint.Avalonia.cs b/MapControl/Avalonia/MapPolypoint.Avalonia.cs index 33a883a8..48be8a38 100644 --- a/MapControl/Avalonia/MapPolypoint.Avalonia.cs +++ b/MapControl/Avalonia/MapPolypoint.Avalonia.cs @@ -51,39 +51,31 @@ namespace MapControl protected void UpdateData(IEnumerable locations, bool closed) { - var pathFigures = new PathFigures(); + var pathFigure = new PathFigure(); - if (ParentMap != null && locations != null) + if (ParentMap != null && locations?.Count() >= 2) { - var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + var longitudeOffset = GetLongitudeOffset(Location ?? locations.First()); - AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); + AddPolylinePoints(pathFigure, locations, longitudeOffset, closed); } - ((PathGeometry)Data).Figures = pathFigures; + ((PathGeometry)Data).Figures = new PathFigures { pathFigure }; - InvalidateGeometry(); + InvalidateGeometry(); // ignores an empty PathGeometry.Figures collection } - private void AddPolylinePoints(PathFigures pathFigures, IEnumerable locations, double longitudeOffset, bool closed) + private void AddPolylinePoints(PathFigure pathFigure, IEnumerable locations, double longitudeOffset, bool closed) { - if (locations.Count() >= 2) - { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); - var figure = new PathFigure - { - StartPoint = points.First(), - IsClosed = closed, - IsFilled = true - }; - - figure.Segments.Add(new PolyLineSegment(points.Skip(1))); - pathFigures.Add(figure); - } + pathFigure.StartPoint = points.First(); + pathFigure.Segments.Add(new PolyLineSegment(points.Skip(1))); + pathFigure.IsClosed = closed; + pathFigure.IsFilled = true; } } } diff --git a/MapControl/Shared/Intersections.cs b/MapControl/Shared/Intersections.cs deleted file mode 100644 index e6f14d76..00000000 --- a/MapControl/Shared/Intersections.cs +++ /dev/null @@ -1,103 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// Copyright © 2024 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using System; -#if WPF -using System.Windows; -#endif - -namespace MapControl -{ - public static class Intersections - { - /// - /// Returns the intersection point of two line segments given by (p1,p2) and (p3,p4), - /// or null if no intersection exists. See https://stackoverflow.com/a/1968345. - /// - public static Point? GetIntersection(Point p1, Point p2, Point p3, Point p4) - { - var x12 = p2.X - p1.X; - var y12 = p2.Y - p1.Y; - var x34 = p4.X - p3.X; - var y34 = p4.Y - p3.Y; - var x13 = p3.X - p1.X; - var y13 = p3.Y - p1.Y; - - var d = x12 * y34 - x34 * y12; - var s = (x13 * y12 - y13 * x12) / d; - var t = (x13 * y34 - y13 * x34) / d; - - if (s >= 0d && s <= 1d && t >= 0d && t <= 1d) - { - return new Point(p1.X + t * x12, p1.Y + t * y12); - } - - return null; - } - - /// - /// Calculates the potential intersections of a line segment given by (p1,p2) with a rectangle. - /// Updates either p1, p2, or both with any found intersection and returns a value that indicates - /// whether the segment intersects or lies inside the rectangle. - /// - public static bool GetIntersections(ref Point p1, ref Point p2, Rect rect) - { - if (rect.Contains(p1) && rect.Contains(p2)) - { - return true; - } - - var topLeft = new Point(rect.X, rect.Y); - var topRight = new Point(rect.X + rect.Width, rect.Y); - var bottomLeft = new Point(rect.X, rect.Y + rect.Height); - var bottomRight = new Point(rect.X + rect.Width, rect.Y + rect.Height); - var numIntersections = 0; - - if (GetIntersection(ref p1, ref p2, topLeft, bottomLeft, p => p.X <= rect.X)) // left edge - { - numIntersections++; - } - - if (GetIntersection(ref p1, ref p2, topLeft, topRight, p => p.Y <= rect.Y)) // top edge - { - numIntersections++; - } - - if (numIntersections < 2 && - GetIntersection(ref p1, ref p2, topRight, bottomRight, p => p.X >= rect.X + rect.Width)) // right edge - { - numIntersections++; - } - - if (numIntersections < 2 && - GetIntersection(ref p1, ref p2, bottomLeft, bottomRight, p => p.Y >= rect.Y + rect.Height)) // bottom edge - { - numIntersections++; - } - - return numIntersections > 0; - } - - private static bool GetIntersection(ref Point p1, ref Point p2, Point p3, Point p4, Predicate predicate) - { - var intersection = GetIntersection(p1, p2, p3, p4); - - if (!intersection.HasValue) - { - return false; - } - - if (predicate(p1)) - { - p1 = intersection.Value; - } - else - { - p2 = intersection.Value; - } - - return true; - } - } -} diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index bfee0381..edf6650e 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -89,9 +89,6 @@ ImageLoader.cs - - Intersections.cs - Location.cs diff --git a/MapControl/WPF/MapPolypoint.WPF.cs b/MapControl/WPF/MapPolypoint.WPF.cs index 51f2ebc3..56963393 100644 --- a/MapControl/WPF/MapPolypoint.WPF.cs +++ b/MapControl/WPF/MapPolypoint.WPF.cs @@ -60,9 +60,9 @@ namespace MapControl { using (var context = ((StreamGeometry)Data).Open()) { - if (ParentMap != null && locations != null) + if (ParentMap != null && locations?.Count() >= 2) { - var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + var longitudeOffset = GetLongitudeOffset(Location ?? locations.First()); AddPolylinePoints(context, locations, longitudeOffset, closed); } @@ -77,7 +77,7 @@ namespace MapControl { var longitudeOffset = GetLongitudeOffset(Location); - foreach (var polygon in polygons) + foreach (var polygon in polygons.Where(p => p?.Count() >= 2)) { AddPolylinePoints(context, polygon, longitudeOffset, true); } @@ -87,16 +87,13 @@ namespace MapControl private void AddPolylinePoints(StreamGeometryContext context, IEnumerable locations, double longitudeOffset, bool closed) { - if (locations.Count() >= 2) - { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); - context.BeginFigure(points.First(), true, closed); - context.PolyLineTo(points.Skip(1).ToList(), true, true); - } + context.BeginFigure(points.First(), true, closed); + context.PolyLineTo(points.Skip(1).ToList(), true, true); } } } diff --git a/MapControl/WinUI/MapPolypoint.WinUI.cs b/MapControl/WinUI/MapPolypoint.WinUI.cs index 3020b128..e62974cf 100644 --- a/MapControl/WinUI/MapPolypoint.WinUI.cs +++ b/MapControl/WinUI/MapPolypoint.WinUI.cs @@ -2,6 +2,7 @@ // Copyright © 2024 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -61,9 +62,9 @@ namespace MapControl var pathFigures = ((PathGeometry)Data).Figures; pathFigures.Clear(); - if (ParentMap != null && locations != null) + if (ParentMap != null && locations?.Count() >= 2) { - var longitudeOffset = GetLongitudeOffset(Location ?? locations.FirstOrDefault()); + var longitudeOffset = GetLongitudeOffset(Location ?? locations.First()); AddPolylinePoints(pathFigures, locations, longitudeOffset, closed); } @@ -71,77 +72,157 @@ namespace MapControl private void AddPolylinePoints(PathFigureCollection pathFigures, IEnumerable locations, double longitudeOffset, bool closed) { - if (locations.Count() >= 2) + var points = locations + .Select(location => LocationToView(location, longitudeOffset)) + .Where(point => point.HasValue) + .Select(point => point.Value); + + if (closed) { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); - - if (closed) + var figure = new PathFigure { - var figure = new PathFigure - { - StartPoint = points.First(), - IsClosed = closed, - IsFilled = true - }; + StartPoint = points.First(), + IsClosed = true, + IsFilled = true + }; - var polyline = new PolyLineSegment(); + var polyline = new PolyLineSegment(); - foreach (var point in points.Skip(1)) - { - polyline.Points.Add(point); - } - - figure.Segments.Add(polyline); - pathFigures.Add(figure); + foreach (var point in points.Skip(1)) + { + polyline.Points.Add(point); } - else - { - var pointList = points.ToList(); - if (closed) + figure.Segments.Add(polyline); + pathFigures.Add(figure); + } + else + { + PathFigure figure = null; + PolyLineSegment polyline = null; + var viewport = new Rect(0, 0, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height); + var pointList = points.ToList(); + + for (int i = 1; i < pointList.Count; i++) + { + var p1 = pointList[i - 1]; + var p2 = pointList[i]; + var inside = GetIntersections(ref p1, ref p2, viewport); + + if (inside) { - pointList.Add(pointList[0]); + if (figure == null) + { + figure = new PathFigure + { + StartPoint = p1, + IsClosed = false, + IsFilled = true + }; + + polyline = new PolyLineSegment(); + figure.Segments.Add(polyline); + pathFigures.Add(figure); + } + + polyline.Points.Add(p2); } - var viewport = new Rect(0, 0, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height); - PathFigure figure = null; - PolyLineSegment polyline = null; - - for (int i = 1; i < pointList.Count; i++) + if (!inside || p2 != pointList[i]) { - var p1 = pointList[i - 1]; - var p2 = pointList[i]; - var inside = Intersections.GetIntersections(ref p1, ref p2, viewport); - - if (inside) - { - if (figure == null) - { - figure = new PathFigure - { - StartPoint = p1, - IsClosed = false, - IsFilled = true - }; - - polyline = new PolyLineSegment(); - figure.Segments.Add(polyline); - pathFigures.Add(figure); - } - - polyline.Points.Add(p2); - } - - if (!inside || p2 != pointList[i]) - { - figure = null; - } + figure = null; } } } } + + /// + /// Calculates the potential intersections of a line segment given by (p1,p2) with a rectangle. + /// Updates either p1, p2, or both with any found intersection and returns a value that indicates + /// whether the segment intersects or lies inside the rectangle. + /// + private static bool GetIntersections(ref Point p1, ref Point p2, Rect rect) + { + if (rect.Contains(p1) && rect.Contains(p2)) + { + return true; + } + + var topLeft = new Point(rect.X, rect.Y); + var topRight = new Point(rect.X + rect.Width, rect.Y); + var bottomLeft = new Point(rect.X, rect.Y + rect.Height); + var bottomRight = new Point(rect.X + rect.Width, rect.Y + rect.Height); + var numIntersections = 0; + + if (GetIntersection(ref p1, ref p2, topLeft, bottomLeft, p => p.X <= rect.X)) // left edge + { + numIntersections++; + } + + if (GetIntersection(ref p1, ref p2, topLeft, topRight, p => p.Y <= rect.Y)) // top edge + { + numIntersections++; + } + + if (numIntersections < 2 && + GetIntersection(ref p1, ref p2, topRight, bottomRight, p => p.X >= rect.X + rect.Width)) // right edge + { + numIntersections++; + } + + if (numIntersections < 2 && + GetIntersection(ref p1, ref p2, bottomLeft, bottomRight, p => p.Y >= rect.Y + rect.Height)) // bottom edge + { + numIntersections++; + } + + return numIntersections > 0; + } + + private static bool GetIntersection(ref Point p1, ref Point p2, Point p3, Point p4, Predicate predicate) + { + var intersection = GetIntersection(p1, p2, p3, p4); + + if (!intersection.HasValue) + { + return false; + } + + if (predicate(p1)) + { + p1 = intersection.Value; + } + else + { + p2 = intersection.Value; + } + + return true; + } + + /// + /// Returns the intersection point of two line segments given by (p1,p2) and (p3,p4), + /// or null if no intersection exists. See https://stackoverflow.com/a/1968345. + /// + private static Point? GetIntersection(Point p1, Point p2, Point p3, Point p4) + { + var x12 = p2.X - p1.X; + var y12 = p2.Y - p1.Y; + var x34 = p4.X - p3.X; + var y34 = p4.Y - p3.Y; + var x13 = p3.X - p1.X; + var y13 = p3.Y - p1.Y; + + var d = x12 * y34 - x34 * y12; + var s = (x13 * y12 - y13 * x12) / d; + var t = (x13 * y34 - y13 * x34) / d; + + if (s >= 0d && s <= 1d && t >= 0d && t <= 1d) + { + return new Point(p1.X + t * x12, p1.Y + t * y12); + } + + return null; + } } }