diff --git a/OpenNest.Gpu/OpenNest.Gpu.csproj b/OpenNest.Gpu/OpenNest.Gpu.csproj new file mode 100644 index 0000000..ba271a1 --- /dev/null +++ b/OpenNest.Gpu/OpenNest.Gpu.csproj @@ -0,0 +1,15 @@ + + + net8.0-windows + OpenNest.Gpu + OpenNest.Gpu + + + + + + + + + + diff --git a/OpenNest.Gpu/PartBitmap.cs b/OpenNest.Gpu/PartBitmap.cs new file mode 100644 index 0000000..b141280 --- /dev/null +++ b/OpenNest.Gpu/PartBitmap.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenNest.Converters; +using OpenNest.Geometry; +using OpenNest.Math; + +namespace OpenNest.Gpu +{ + public class PartBitmap + { + public int[] Cells { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public double CellSize { get; set; } + public double OriginX { get; set; } + public double OriginY { get; set; } + + public const double DefaultCellSize = 0.05; + + public static PartBitmap FromDrawing(Drawing drawing, double cellSize = DefaultCellSize, double spacingDilation = 0) + { + var polygons = GetClosedPolygons(drawing); + return Rasterize(polygons, cellSize, spacingDilation); + } + + public static PartBitmap FromDrawingRotated(Drawing drawing, double rotation, double cellSize = DefaultCellSize, double spacingDilation = 0) + { + var polygons = GetClosedPolygons(drawing); + + if (!rotation.IsEqualTo(0)) + { + foreach (var poly in polygons) + poly.Rotate(rotation); + } + + return Rasterize(polygons, cellSize, spacingDilation); + } + + private static PartBitmap Rasterize(List polygons, double cellSize, double spacingDilation) + { + if (polygons.Count == 0) + return new PartBitmap { Cells = Array.Empty(), Width = 0, Height = 0, CellSize = cellSize }; + + var minX = double.MaxValue; + var minY = double.MaxValue; + var maxX = double.MinValue; + var maxY = double.MinValue; + + foreach (var poly in polygons) + { + poly.UpdateBounds(); + var bb = poly.BoundingBox; + if (bb.Left < minX) minX = bb.Left; + if (bb.Bottom < minY) minY = bb.Bottom; + if (bb.Right > maxX) maxX = bb.Right; + if (bb.Top > maxY) maxY = bb.Top; + } + + minX -= spacingDilation; + minY -= spacingDilation; + maxX += spacingDilation; + maxY += spacingDilation; + + var width = (int)System.Math.Ceiling((maxX - minX) / cellSize); + var height = (int)System.Math.Ceiling((maxY - minY) / cellSize); + + if (width <= 0 || height <= 0) + return new PartBitmap { Cells = Array.Empty(), Width = 0, Height = 0, CellSize = cellSize }; + + var cells = new int[width * height]; + + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + var px = minX + (x + 0.5) * cellSize; + var py = minY + (y + 0.5) * cellSize; + var pt = new Vector(px, py); + + foreach (var poly in polygons) + { + if (poly.ContainsPoint(pt)) + { + cells[y * width + x] = 1; + break; + } + } + } + } + + var dilationCells = (int)System.Math.Ceiling(spacingDilation / cellSize); + + if (dilationCells > 0) + Dilate(cells, width, height, dilationCells); + + return new PartBitmap + { + Cells = cells, + Width = width, + Height = height, + CellSize = cellSize, + OriginX = minX, + OriginY = minY + }; + } + + private static List GetClosedPolygons(Drawing drawing) + { + var entities = ConvertProgram.ToGeometry(drawing.Program) + .Where(e => e.Layer != SpecialLayers.Rapid); + var shapes = Helper.GetShapes(entities); + + var polygons = new List(); + + foreach (var shape in shapes) + { + if (!shape.IsClosed()) + continue; + + var polygon = shape.ToPolygonWithTolerance(0.05); + polygon.Close(); + polygons.Add(polygon); + } + + return polygons; + } + + private static void Dilate(int[] cells, int width, int height, int radius) + { + var source = (int[])cells.Clone(); + + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + if (source[y * width + x] != 1) + continue; + + for (var dy = -radius; dy <= radius; dy++) + { + for (var dx = -radius; dx <= radius; dx++) + { + var nx = x + dx; + var ny = y + dy; + + if (nx >= 0 && nx < width && ny >= 0 && ny < height) + cells[ny * width + nx] = 1; + } + } + } + } + } + } +} diff --git a/OpenNest.sln b/OpenNest.sln index d26a394..251ebd6 100644 --- a/OpenNest.sln +++ b/OpenNest.sln @@ -9,24 +9,66 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Core", "OpenNest.C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Engine", "OpenNest.Engine\OpenNest.Engine.csproj", "{0083B9CC-54AD-4085-A30D-56BC6834B71A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Gpu", "OpenNest.Gpu\OpenNest.Gpu.csproj", "{1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|x64.Build.0 = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|x86.Build.0 = Debug|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.Build.0 = Release|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|x64.ActiveCfg = Release|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|x64.Build.0 = Release|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|x86.ActiveCfg = Release|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|x86.Build.0 = Release|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|x64.Build.0 = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|x86.Build.0 = Debug|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.Build.0 = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|x64.ActiveCfg = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|x64.Build.0 = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|x86.ActiveCfg = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|x86.Build.0 = Release|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|x64.ActiveCfg = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|x64.Build.0 = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|x86.ActiveCfg = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|x86.Build.0 = Debug|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.ActiveCfg = Release|Any CPU {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.Build.0 = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|x64.ActiveCfg = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|x64.Build.0 = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|x86.ActiveCfg = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|x86.Build.0 = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|x64.Build.0 = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Debug|x86.Build.0 = Debug|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|Any CPU.Build.0 = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|x64.ActiveCfg = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|x64.Build.0 = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|x86.ActiveCfg = Release|Any CPU + {1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE