/// <summary> /// http://alienryderflex.com/polygon_fill/ /// </summary> /// <typeparam name="T"></typeparam> /// <param name="polygon"></param> /// <param name="fillVolume"></param> /// <param name="dimX"></param> /// <param name="dimY"></param> /// <param name="dimZ"></param> /// <param name="sliceZ"></param> /// <param name="fillValue"></param> /// <returns></returns> private static VoxelCounts FillPolygonAndCount( PointF[] polygon, ushort[] fillVolume, ushort fillValue, Volume2D <byte> countVolume, byte foregroundId) { var bounds = GetBoundingBox(polygon); const float epsilon = 0.01f; var length = polygon.Length; var nodeIntersections = new IntersectionXPoint[length * 2]; var nodeX = new float[length]; var polygonX = new float[length]; var polygonY = new float[length]; for (var index = 0; index < length; index++) { var point = polygon[index]; polygonX[index] = point.X; polygonY[index] = point.Y; } var voxelCounts = new VoxelCounts(); // Loop through the rows of the image. for (int y = 0; y < countVolume.DimY; y++) { float yPlusEpsilon = y + epsilon; float yMinusEpsilon = y - epsilon; if ((yPlusEpsilon < bounds.Top && yMinusEpsilon < bounds.Top) || (yPlusEpsilon > bounds.Bottom && yMinusEpsilon > bounds.Bottom)) { continue; } // Build a list of nodes, sorted int nodesBoth = FindIntersections(polygonX, polygonY, nodeIntersections, y, yPlusEpsilon, yMinusEpsilon); // Merge int nodes = MergeIntersections(nodeIntersections, nodeX, nodesBoth); // Fill the pixels between node pairs. voxelCounts += FillNodePairsAndCount( fillVolume, fillValue, epsilon, nodeX, y, nodes, countVolume, foregroundId); } return(voxelCounts); }
/// <summary> /// Fills all points that fall inside of a given polygon, and at the same time, /// aggregate statistics on what values are present at the filled pixel /// positions in a "count volume" that has the same size as the fill volume. /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="polygon"></param> /// <param name="fillVolume"></param> /// <param name="dimX"></param> /// <param name="dimY"></param> /// <param name="dimZ"></param> /// <param name="sliceZ"></param> /// <param name="fillValue"></param> /// <param name="countVolume"></param> /// <param name="foregroundId"></param> /// <returns></returns> public static VoxelCounts FillPolygonAndCount( PointInt[] polygon, ushort[] fillVolume, ushort fillValue, Volume2D <byte> countVolume, byte foregroundId) { polygon = polygon ?? throw new ArgumentNullException(nameof(polygon)); fillVolume = fillVolume ?? throw new ArgumentNullException(nameof(fillVolume)); countVolume = countVolume ?? throw new ArgumentNullException(nameof(countVolume)); if (polygon.Length == 0) { throw new ArgumentOutOfRangeException(nameof(polygon), "The polygon does not contain any points."); } if (fillVolume.Length != countVolume.Length) { throw new ArgumentException("The fill and the count volume must have the same size.", nameof(fillVolume)); } var pointsAsFloat = polygon.Select(point => new PointF(point.X, point.Y)).ToArray(); uint foregroundCount = 0; uint otherCount = 0; var countArray = countVolume.Array; var dimX = countVolume.DimX; foreach (var point in polygon) { // Manually computing index, rather than relying on GetIndex, brings substantial speedup. var index = point.X + dimX * point.Y; if (fillVolume[index] != fillValue) { fillVolume[index] = fillValue; if (countArray[index] == foregroundId) { foregroundCount++; } else { otherCount++; } } } var voxelCountsAtPoints = new VoxelCounts(foregroundCount, otherCount); var voxelCountsInside = FillPolygonAndCount( pointsAsFloat, fillVolume, fillValue, countVolume, foregroundId); return(voxelCountsAtPoints + voxelCountsInside); }
/// <summary> /// Creates a new instance of the class. /// </summary> /// <param name="points"></param> /// <param name="voxelCounts"></param> /// <param name="insideOfPolygon"></param> /// <param name="isBackground"></param> /// <param name="startPointMinimumY"></param> public PolygonPoints( PointInt[] points, VoxelCounts voxelCounts, ushort insideOfPolygon, bool isInside, PointInt startPointMinimumY) { Points = points ?? throw new ArgumentNullException(nameof(points)); VoxelCounts = voxelCounts ?? throw new ArgumentNullException(nameof(voxelCounts)); InsideOfPolygon = insideOfPolygon; IsInnerContour = isInside; StartPointMinimumY = startPointMinimumY; }