/// <summary> /// Converts a LineString in world coordinates to a Skia path /// </summary> /// <param name="coordinates">List of points in Mapsui world coordinates</param> /// <param name="viewport">Viewport implementation</param> /// <param name="clipRect">Rectangle to clip to. All lines outside aren't drawn.</param> /// <returns></returns> public static SKPath ToSkiaPath(this LineString lineString, IReadOnlyViewport viewport, SKRect clipRect) { var coordinates = lineString.Coordinates; // First convert List<Points> to screen coordinates var vertices = ClippingFunctions.WorldToScreen(viewport, coordinates); var path = new SKPath(); var lastPoint = SKPoint.Empty; for (var i = 1; i < vertices.Count; i++) { // Check each part of LineString, if it is inside or intersects the clipping rectangle var intersect = ClippingFunctions.LiangBarskyClip(vertices[i - 1], vertices[i], clipRect, out var intersectionPoint1, out var intersectionPoint2); if (intersect != ClippingFunctions.Intersection.CompleteOutside) { // If the last point isn't the same as actual starting point ... if (lastPoint.IsEmpty || !lastPoint.Equals(intersectionPoint1)) { // ... than move to this point path.MoveTo(intersectionPoint1); } // Draw line path.LineTo(intersectionPoint2); // Save last end point for later use lastPoint = intersectionPoint2; } } return(path); }
/// <summary> /// Converts a Polygon into a SKPath, that is clipped to clipRect, where exterior is bigger than interior /// </summary> /// <param name="polygon">Polygon to convert</param> /// <param name="viewport">Viewport implementation</param> /// <param name="clipRect">Rectangle to clip to. All lines outside aren't drawn.</param> /// <param name="strokeWidth">StrokeWidth for inflating clipRect</param> /// <returns></returns> public static SKPath ToSkiaPath(this Polygon polygon, IReadOnlyViewport viewport, SKRect clipRect, float strokeWidth) { // Reduce exterior ring to parts, that are visible in clipping rectangle // Inflate clipRect, so that we could be sure, nothing of stroke is visible on screen var exterior = ClippingFunctions.ReducePointsToClipRect(polygon.ExteriorRing?.Coordinates, viewport, SKRect.Inflate(clipRect, strokeWidth * 2, strokeWidth * 2)); // Create path for exterior and interior parts var path = new SKPath(); if (exterior.Count == 0) { return(path); } // Draw exterior path path.MoveTo(exterior[0]); for (var i = 1; i < exterior.Count; i++) { path.LineTo(exterior[i]); } // Close exterior path path.Close(); foreach (var interiorRing in polygon.InteriorRings) { // note: For Skia inner rings need to be clockwise and outer rings // need to be counter clockwise (if this is the other way around it also // seems to work) // this is not a requirement of the OGC polygon. // Reduce interior ring to parts, that are visible in clipping rectangle var interior = ClippingFunctions.ReducePointsToClipRect(interiorRing.Coordinates, viewport, SKRect.Inflate(clipRect, strokeWidth, strokeWidth)); if (interior.Count == 0) { continue; } // Draw interior paths path.MoveTo(interior[0]); for (var i = 1; i < interior.Count; i++) { path.LineTo(interior[i]); } } // Close interior paths path.Close(); return(path); }