feat: add OpenNest.Gpu project with PartBitmap rasterizer
Introduces the OpenNest.Gpu class library with ILGPU dependencies and a PartBitmap class that rasterizes Drawing closed shapes into integer grids for GPU-based overlap testing. Supports rotation and spacing dilation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<RootNamespace>OpenNest.Gpu</RootNamespace>
|
||||||
|
<AssemblyName>OpenNest.Gpu</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenNest.Core\OpenNest.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenNest.Engine\OpenNest.Engine.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ILGPU" Version="1.5.1" />
|
||||||
|
<PackageReference Include="ILGPU.Algorithms" Version="1.5.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -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<Polygon> polygons, double cellSize, double spacingDilation)
|
||||||
|
{
|
||||||
|
if (polygons.Count == 0)
|
||||||
|
return new PartBitmap { Cells = Array.Empty<int>(), 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<int>(), 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<Polygon> GetClosedPolygons(Drawing drawing)
|
||||||
|
{
|
||||||
|
var entities = ConvertProgram.ToGeometry(drawing.Program)
|
||||||
|
.Where(e => e.Layer != SpecialLayers.Rapid);
|
||||||
|
var shapes = Helper.GetShapes(entities);
|
||||||
|
|
||||||
|
var polygons = new List<Polygon>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,24 +9,66 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Core", "OpenNest.C
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Engine", "OpenNest.Engine\OpenNest.Engine.csproj", "{0083B9CC-54AD-4085-A30D-56BC6834B71A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Engine", "OpenNest.Engine\OpenNest.Engine.csproj", "{0083B9CC-54AD-4085-A30D-56BC6834B71A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Gpu", "OpenNest.Gpu\OpenNest.Gpu.csproj", "{1F0DD58E-9E83-4F78-A9D9-0557C0B2D96F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{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|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.ActiveCfg = Release|Any CPU
|
||||||
{1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
|
||||||
{5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
|
||||||
{0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user