Merge branch 'chore/csharpier-sweep'

# Conflicts:
#	OpenNest.Core/Geometry/ArcFit.cs
#	OpenNest.Core/Geometry/GeometrySimplifier.cs
#	OpenNest.Posts.GravographIS/GravographISWriter.cs
#	OpenNest.Posts.GravographIS/NestPolylineExtractor.cs
This commit is contained in:
aj
2026-09-20 16:53:52 -04:00
473 changed files with 16171 additions and 7660 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
using ACadSharp;
using OpenNest.Bending;
using System.Collections.Generic;
using System.Linq;
using ACadSharp;
using OpenNest.Bending;
namespace OpenNest.IO.Bending
{
+1 -1
View File
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using ACadSharp;
using OpenNest.Bending;
using System.Collections.Generic;
namespace OpenNest.IO.Bending
{
+68 -29
View File
@@ -1,12 +1,12 @@
using ACadSharp;
using ACadSharp.Entities;
using OpenNest.Bending;
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using ACadSharp;
using ACadSharp.Entities;
using OpenNest.Bending;
using OpenNest.Geometry;
namespace OpenNest.IO.Bending
{
@@ -18,15 +18,18 @@ namespace OpenNest.IO.Bending
private static readonly Regex BendNoteRegex = new Regex(
@"(?<direction>UP|DOWN|DN)\s+(?<angle>\d+(\.\d+)?)[^A-Z\d]*R\s*(?<radius>\d+(\.\d+)?)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
RegexOptions.Compiled | RegexOptions.IgnoreCase
);
private static readonly Regex MTextFormatRegex = new Regex(
@"\\[fHCTQWASpOoLlKk][^;]*;|\\P|[{}]|%%[dDpPcC]",
RegexOptions.Compiled);
RegexOptions.Compiled
);
private static readonly Regex UnicodeEscapeRegex = new Regex(
@"\\U\+([0-9A-Fa-f]{4})",
RegexOptions.Compiled);
RegexOptions.Compiled
);
public List<Bend> DetectBends(CadDocument document)
{
@@ -47,7 +50,7 @@ namespace OpenNest.IO.Bending
{
StartPoint = start,
EndPoint = end,
Direction = BendDirection.Unknown
Direction = BendDirection.Unknown,
};
var note = FindClosestBendNote(line, bendNotes);
@@ -101,7 +104,12 @@ namespace OpenNest.IO.Bending
}
}
private static bool AreCollinear(Bend a, Bend b, double angleTolerance, double distanceTolerance)
private static bool AreCollinear(
Bend a,
Bend b,
double angleTolerance,
double distanceTolerance
)
{
var angleA = a.StartPoint.AngleTo(a.EndPoint);
var angleB = b.StartPoint.AngleTo(b.EndPoint);
@@ -114,7 +122,8 @@ namespace OpenNest.IO.Bending
// Perpendicular distance from midpoint of A to the infinite line through B
var midA = new Vector(
(a.StartPoint.X + a.EndPoint.X) / 2.0,
(a.StartPoint.Y + a.EndPoint.Y) / 2.0);
(a.StartPoint.Y + a.EndPoint.Y) / 2.0
);
var dx = b.EndPoint.X - b.StartPoint.X;
var dy = b.EndPoint.Y - b.StartPoint.Y;
@@ -133,18 +142,22 @@ namespace OpenNest.IO.Bending
private List<ACadSharp.Entities.Line> FindBendLines(CadDocument document)
{
return document.Entities
.OfType<ACadSharp.Entities.Line>()
.Where(l => (l.Layer?.Name == "BEND" || l.Layer?.Name == "0")
&& (l.LineType?.Name?.Contains("CENTER") == true
|| l.LineType?.Name == "CENTERX2"))
return document
.Entities.OfType<ACadSharp.Entities.Line>()
.Where(l =>
(l.Layer?.Name == "BEND" || l.Layer?.Name == "0")
&& (
l.LineType?.Name?.Contains("CENTER") == true
|| l.LineType?.Name == "CENTERX2"
)
)
.ToList();
}
private List<MText> FindBendNotes(CadDocument document)
{
return document.Entities
.OfType<MText>()
return document
.Entities.OfType<MText>()
.Where(t => GetBendDirection(t.Value) != BendDirection.Unknown)
.ToList();
}
@@ -172,10 +185,24 @@ namespace OpenNest.IO.Bending
if (match.Success)
{
if (double.TryParse(match.Groups["radius"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var radius))
if (
double.TryParse(
match.Groups["radius"].Value,
NumberStyles.Any,
CultureInfo.InvariantCulture,
out var radius
)
)
bend.Radius = radius;
if (double.TryParse(match.Groups["angle"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var angle))
if (
double.TryParse(
match.Groups["angle"].Value,
NumberStyles.Any,
CultureInfo.InvariantCulture,
out var angle
)
)
bend.Angle = angle;
}
}
@@ -186,17 +213,27 @@ namespace OpenNest.IO.Bending
return text;
// Convert \U+XXXX DXF unicode escapes to actual characters
var result = UnicodeEscapeRegex.Replace(text, m =>
{
var codePoint = int.Parse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
return char.ConvertFromUtf32(codePoint);
});
var result = UnicodeEscapeRegex.Replace(
text,
m =>
{
var codePoint = int.Parse(
m.Groups[1].Value,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture
);
return char.ConvertFromUtf32(codePoint);
}
);
// Replace known DXF special characters
result = result
.Replace("%%d", "°").Replace("%%D", "°")
.Replace("%%p", "±").Replace("%%P", "±")
.Replace("%%c", "⌀").Replace("%%C", "⌀");
.Replace("%%d", "°")
.Replace("%%D", "°")
.Replace("%%p", "±")
.Replace("%%P", "±")
.Replace("%%c", "⌀")
.Replace("%%C", "⌀");
// Strip MText formatting codes and braces
result = MTextFormatRegex.Replace(result, " ");
@@ -207,7 +244,8 @@ namespace OpenNest.IO.Bending
private MText FindClosestBendNote(ACadSharp.Entities.Line bendLine, List<MText> notes)
{
if (notes.Count == 0) return null;
if (notes.Count == 0)
return null;
MText closest = null;
var closestDist = double.MaxValue;
@@ -223,7 +261,8 @@ namespace OpenNest.IO.Bending
var dist = notePos.DistanceTo(perpPoint);
var maxAcceptable = note.Height * 2.0;
if (dist > maxAcceptable) continue;
if (dist > maxAcceptable)
continue;
if (dist < closestDist)
{
+6 -4
View File
@@ -62,8 +62,10 @@ namespace OpenNest.IO.Bom
var lookupName = item.FileName;
if (lookupName.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)
|| lookupName.EndsWith(".dwg", StringComparison.OrdinalIgnoreCase))
if (
lookupName.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)
|| lookupName.EndsWith(".dwg", StringComparison.OrdinalIgnoreCase)
)
lookupName = Path.GetFileNameWithoutExtension(lookupName);
if (!folderExists)
@@ -86,13 +88,13 @@ namespace OpenNest.IO.Bom
.GroupBy(p => new
{
Material = (p.Item.Material ?? "").ToUpperInvariant(),
Thickness = p.Item.Thickness.Value
Thickness = p.Item.Thickness.Value,
})
.Select(g => new MaterialGroup
{
Material = g.First().Item.Material ?? "",
Thickness = g.Key.Thickness,
Parts = g.ToList()
Parts = g.ToList(),
})
.OrderBy(g => g.Material)
.ThenBy(g => g.Thickness)
+6 -3
View File
@@ -1,8 +1,8 @@
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ClosedXML.Excel;
namespace OpenNest.IO.Bom
{
@@ -20,7 +20,9 @@ namespace OpenNest.IO.Bom
private IXLWorksheet GetPartsWorksheet()
{
if (!workbook.TryGetWorksheet("Parts", out var worksheet))
throw new InvalidOperationException("BOM file does not contain a 'Parts' worksheet.");
throw new InvalidOperationException(
"BOM file does not contain a 'Parts' worksheet."
);
return worksheet;
}
@@ -41,7 +43,8 @@ namespace OpenNest.IO.Bom
for (var columnIndex = 1; columnIndex <= lastColumn; columnIndex++)
{
var cell = worksheet.Cell(1, columnIndex);
if (cell.IsEmpty()) continue;
if (cell.IsEmpty())
continue;
var excelColumnName = cell.GetString().ToUpper();
var isMatch = classColumnNames.Any(n => n == excelColumnName);
+12 -6
View File
@@ -6,17 +6,23 @@ namespace OpenNest.IO.Bom
{
public static int? ToIntOrNull(this IXLCell cell)
{
if (cell.IsEmpty()) return null;
if (cell.DataType == XLDataType.Number) return (int)cell.GetDouble();
if (int.TryParse(cell.GetString(), out var i)) return i;
if (cell.IsEmpty())
return null;
if (cell.DataType == XLDataType.Number)
return (int)cell.GetDouble();
if (int.TryParse(cell.GetString(), out var i))
return i;
return null;
}
public static double? ToDoubleOrNull(this IXLCell cell)
{
if (cell.IsEmpty()) return null;
if (cell.DataType == XLDataType.Number) return cell.GetDouble();
if (double.TryParse(cell.GetString(), out var result)) return result;
if (cell.IsEmpty())
return null;
if (cell.DataType == XLDataType.Number)
return cell.GetDouble();
if (double.TryParse(cell.GetString(), out var result))
return result;
return null;
}
}
+2 -1
View File
@@ -24,7 +24,8 @@ namespace OpenNest.IO
/// </summary>
public List<Bend> Bends { get; set; } = new List<Bend>();
public List<Bending.BendRepairReport> BendRepairReports { get; set; } = new List<Bending.BendRepairReport>();
public List<Bending.BendRepairReport> BendRepairReports { get; set; } =
new List<Bending.BendRepairReport>();
/// <summary>
/// Bounding box of <see cref="Entities"/> at import time. May be stale
+47 -22
View File
@@ -26,8 +26,10 @@ namespace OpenNest.IO
var dxf = Dxf.Import(path, preserveRepairMarks: options.BendRepair != null);
var cleanup = options.BendRepair == null ? dxf.Entities : dxf.Entities
.Where(e => !IsRepairMark(e)).ToList();
var cleanup =
options.BendRepair == null
? dxf.Entities
: dxf.Entities.Where(e => !IsRepairMark(e)).ToList();
RemoveDuplicateArcs(cleanup);
RemoveZeroSweepArcs(cleanup);
if (options.BendRepair != null)
@@ -36,11 +38,13 @@ namespace OpenNest.IO
var bends = new List<Bend>();
if (options.DetectBends && dxf.Document != null)
{
bends = options.BendDetectorName == null
? BendDetectorRegistry.AutoDetect(dxf.Document)
: BendDetectorRegistry.GetByName(options.BendDetectorName)
?.DetectBends(dxf.Document)
?? new List<Bend>();
bends =
options.BendDetectorName == null
? BendDetectorRegistry.AutoDetect(dxf.Document)
: BendDetectorRegistry
.GetByName(options.BendDetectorName)
?.DetectBends(dxf.Document)
?? new List<Bend>();
}
var repairReports = new List<BendRepairReport>();
@@ -50,11 +54,23 @@ namespace OpenNest.IO
{
// Unitless DXFs require the explicit caller declaration. Never override a conflicting header.
var headerUnits = (int)(dxf.Document?.Header.InsUnits ?? 0);
var requestedUnits = options.BendRepair.DrawingUnits == BendRepairUnits.Inches ? 1 : 4;
var requestedUnits =
options.BendRepair.DrawingUnits == BendRepairUnits.Inches ? 1 : 4;
if (headerUnits != 0 && headerUnits != requestedUnits)
repairReports = bends.Select((b, i) => new BendRepairReport(i, "Skipped",
"DXF insertion units conflict with the declared repair units or are unsupported.",
b.StartPoint, b.EndPoint, b.StartPoint, b.EndPoint)).ToList();
repairReports = bends
.Select(
(b, i) =>
new BendRepairReport(
i,
"Skipped",
"DXF insertion units conflict with the declared repair units or are unsupported.",
b.StartPoint,
b.EndPoint,
b.StartPoint,
b.EndPoint
)
)
.ToList();
else
repairReports = BendRepair.Apply(dxf.Entities, bends, options.BendRepair);
}
@@ -85,7 +101,8 @@ namespace OpenNest.IO
result.Bends,
options.Quantity,
options.Customer,
editedProgram: null);
editedProgram: null
);
}
/// <summary>
@@ -119,7 +136,8 @@ namespace OpenNest.IO
IEnumerable<Bend> bends,
int quantity,
string customer,
OpenNest.CNC.Program editedProgram)
OpenNest.CNC.Program editedProgram
)
{
var visible = entities as IList<Entity> ?? new List<Entity>(entities);
var bendList = bends as IList<Bend> ?? new List<Bend>(bends);
@@ -128,7 +146,11 @@ namespace OpenNest.IO
var pgm = ConvertGeometry.ToProgram(normalized);
var offset = Vector.Zero;
if (pgm != null && pgm.Codes.Count > 0 && pgm[0].Type == OpenNest.CNC.CodeType.RapidMove)
if (
pgm != null
&& pgm.Codes.Count > 0
&& pgm[0].Type == OpenNest.CNC.CodeType.RapidMove
)
{
var rapid = (OpenNest.CNC.RapidMove)pgm[0];
offset = rapid.EndPoint;
@@ -147,16 +169,18 @@ namespace OpenNest.IO
drawing.Program = editedProgram ?? pgm;
var bendSources = new HashSet<Entity>(
bendList.Where(b => b.SourceEntity != null).Select(b => b.SourceEntity));
bendList.Where(b => b.SourceEntity != null).Select(b => b.SourceEntity)
);
drawing.SourceEntities = result.Entities
.Where(e => !bendSources.Contains(e))
.ToList();
drawing.SourceEntities = result.Entities.Where(e => !bendSources.Contains(e)).ToList();
drawing.SuppressedEntityIds = new HashSet<System.Guid>(
drawing.SourceEntities
.Where(e => !(e.Layer != null && e.Layer.IsVisible && e.IsVisible))
.Select(e => e.Id));
drawing
.SourceEntities.Where(e =>
!(e.Layer != null && e.Layer.IsVisible && e.IsVisible)
)
.Select(e => e.Id)
);
return drawing;
}
@@ -168,7 +192,8 @@ namespace OpenNest.IO
internal static void RemoveZeroSweepArcs(List<Entity> entities)
{
entities.RemoveAll(e =>
e is Arc arc && arc.StartAngle.IsEqualTo(arc.EndAngle, Tolerance.ChainTolerance));
e is Arc arc && arc.StartAngle.IsEqualTo(arc.EndAngle, Tolerance.ChainTolerance)
);
}
internal static void RemoveDuplicateArcs(List<Entity> entities)
+64 -19
View File
@@ -42,7 +42,12 @@ namespace OpenNest.IO
return width;
}
public List<Entity> RenderText(string text, double height, Vector position, Layer layer = null)
public List<Entity> RenderText(
string text,
double height,
Vector position,
Layer layer = null
)
{
var scale = height / CapHeight;
var entities = new List<Entity>();
@@ -97,7 +102,8 @@ namespace OpenNest.IO
while (i + 5 < data.Length)
{
var charCode = data[i] | (data[i + 1] << 8);
var offset = data[i + 2] | (data[i + 3] << 8) | (data[i + 4] << 16) | (data[i + 5] << 24);
var offset =
data[i + 2] | (data[i + 3] << 8) | (data[i + 4] << 16) | (data[i + 5] << 24);
if (charCode < 0x20 || offset == 0 || offset >= data.Length)
break;
@@ -110,9 +116,10 @@ namespace OpenNest.IO
{
var (charCode, offset) = charTable[c];
var nextOffset = c + 1 < charTable.Count
? FindNextOffset(charTable, offset, data.Length)
: data.Length;
var nextOffset =
c + 1 < charTable.Count
? FindNextOffset(charTable, offset, data.Length)
: data.Length;
var glyph = ParseGlyph(data, offset, nextOffset);
if (glyph != null)
@@ -134,7 +141,11 @@ namespace OpenNest.IO
return font;
}
private static int FindNextOffset(List<(int charCode, int offset)> table, int currentOffset, int fileLength)
private static int FindNextOffset(
List<(int charCode, int offset)> table,
int currentOffset,
int fileLength
)
{
var best = fileLength;
foreach (var (_, off) in table)
@@ -199,7 +210,8 @@ namespace OpenNest.IO
private static int ReadBE16(byte[] data, int offset)
{
var val = (data[offset] << 8) | data[offset + 1];
if (val > 32767) val -= 65536;
if (val > 32767)
val -= 65536;
return val;
}
}
@@ -234,19 +246,26 @@ namespace OpenNest.IO
private const int ArcSamples = 16;
public List<Entity> ToEntities(double scale, double offsetX, double offsetY, Layer layer = null)
public List<Entity> ToEntities(
double scale,
double offsetX,
double offsetY,
Layer layer = null
)
{
var entities = new List<Entity>();
layer ??= Layer.Default;
foreach (var stroke in Strokes)
{
if (stroke.Count < 2) continue;
if (stroke.Count < 2)
continue;
var segments = BuildSegments(stroke);
foreach (var seg in segments)
{
if (seg.Points.Count < 2) continue;
if (seg.Points.Count < 2)
continue;
var scaled = new List<Vector>(seg.Points.Count);
foreach (var pt in seg.Points)
@@ -324,14 +343,23 @@ namespace OpenNest.IO
public bool HasCurves;
}
private static void SampleCircularArc(List<Vector> output, Vector p0, Vector pMid, Vector p1, int samples)
private static void SampleCircularArc(
List<Vector> output,
Vector p0,
Vector pMid,
Vector p1,
int samples
)
{
if (output.Count == 0 || output[^1].DistanceTo(p0) > 0.01)
output.Add(p0);
double ax = p0.X, ay = p0.Y;
double bx = pMid.X, by = pMid.Y;
double cx = p1.X, cy = p1.Y;
double ax = p0.X,
ay = p0.Y;
double bx = pMid.X,
by = pMid.Y;
double cx = p1.X,
cy = p1.Y;
var d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
@@ -342,8 +370,18 @@ namespace OpenNest.IO
return;
}
var ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
var uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
var ux =
(
(ax * ax + ay * ay) * (by - cy)
+ (bx * bx + by * by) * (cy - ay)
+ (cx * cx + cy * cy) * (ay - by)
) / d;
var uy =
(
(ax * ax + ay * ay) * (cx - bx)
+ (bx * bx + by * by) * (ax - cx)
+ (cx * cx + cy * cy) * (bx - ax)
) / d;
var radius = System.Math.Sqrt((ax - ux) * (ax - ux) + (ay - uy) * (ay - uy));
var a0 = System.Math.Atan2(ay - uy, ax - ux);
@@ -351,10 +389,12 @@ namespace OpenNest.IO
var a1 = System.Math.Atan2(cy - uy, cx - ux);
var ccwSweep = a1 - a0;
while (ccwSweep <= 0) ccwSweep += 2 * System.Math.PI;
while (ccwSweep <= 0)
ccwSweep += 2 * System.Math.PI;
var midRel = am - a0;
while (midRel < 0) midRel += 2 * System.Math.PI;
while (midRel < 0)
midRel += 2 * System.Math.PI;
var sweep = midRel < ccwSweep ? ccwSweep : ccwSweep - 2 * System.Math.PI;
@@ -362,7 +402,12 @@ namespace OpenNest.IO
{
var t = (double)i / samples;
var angle = a0 + sweep * t;
output.Add(new Vector(ux + radius * System.Math.Cos(angle), uy + radius * System.Math.Sin(angle)));
output.Add(
new Vector(
ux + radius * System.Math.Cos(angle),
uy + radius * System.Math.Sin(angle)
)
);
}
}
}
+35 -26
View File
@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ACadSharp;
using ACadSharp.IO;
using CSMath;
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace OpenNest.IO
{
@@ -35,14 +35,12 @@ namespace OpenNest.IO
if (preserveRepairMarks)
{
// Keep source marks separate: optimization could merge two ticks or unrelated scribing.
entities.AddRange(ConvertEntities(doc, name => !IsRepairMarkLayer(name), optimize: false));
entities.AddRange(
ConvertEntities(doc, name => !IsRepairMarkLayer(name), optimize: false)
);
}
return new DxfImportResult
{
Entities = entities,
Document = doc
};
return new DxfImportResult { Entities = entities, Document = doc };
}
public static List<Entity> GetGeometry(string path)
@@ -167,7 +165,11 @@ namespace OpenNest.IO
}
}
private static List<Entity> ConvertEntities(CadDocument doc, Func<string, bool> layerFilter = null, bool optimize = true)
private static List<Entity> ConvertEntities(
CadDocument doc,
Func<string, bool> layerFilter = null,
bool optimize = true
)
{
var entities = new List<Entity>();
var lines = new List<Line>();
@@ -197,8 +199,10 @@ namespace OpenNest.IO
case ACadSharp.Entities.Spline spline:
foreach (var e in spline.ToOpenNest())
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
if (e is Line l)
lines.Add(l);
else if (e is Arc a)
arcs.Add(a);
}
break;
@@ -213,8 +217,10 @@ namespace OpenNest.IO
case ACadSharp.Entities.Ellipse ellipse:
foreach (var e in ellipse.ToOpenNest())
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
if (e is Line l)
lines.Add(l);
else if (e is Arc a)
arcs.Add(a);
}
break;
}
@@ -276,14 +282,17 @@ namespace OpenNest.IO
{
StartPoint = start,
EndPoint = end,
Layer = layer
Layer = layer,
};
Document.Entities.Add(ln);
}
public void AddPlateOutline(Plate plate)
{
XYZ pt1, pt2, pt3, pt4;
XYZ pt1,
pt2,
pt3,
pt4;
switch (plate.Quadrant)
{
@@ -324,7 +333,11 @@ namespace OpenNest.IO
AddLine(pt3, pt4, PlateLayer);
AddLine(pt4, pt1, PlateLayer);
var m1 = new XYZ(pt1.X + plate.EdgeSpacing.Left, pt1.Y + plate.EdgeSpacing.Bottom, 0);
var m1 = new XYZ(
pt1.X + plate.EdgeSpacing.Left,
pt1.Y + plate.EdgeSpacing.Bottom,
0
);
var m2 = new XYZ(m1.X, pt2.Y - plate.EdgeSpacing.Top, 0);
var m3 = new XYZ(pt3.X - plate.EdgeSpacing.Right, m2.Y, 0);
var m4 = new XYZ(m3.X, m1.Y, 0);
@@ -399,13 +412,9 @@ namespace OpenNest.IO
center = new XYZ(center.X + CurPos.X, center.Y + CurPos.Y, 0);
}
var startAngle = System.Math.Atan2(
CurPos.Y - center.Y,
CurPos.X - center.X);
var startAngle = System.Math.Atan2(CurPos.Y - center.Y, CurPos.X - center.X);
var endAngle = System.Math.Atan2(
endpt.Y - center.Y,
endpt.X - center.X);
var endAngle = System.Math.Atan2(endpt.Y - center.Y, endpt.X - center.X);
if (arc.Rotation == RotationType.CW)
Generic.Swap(ref startAngle, ref endAngle);
@@ -420,7 +429,7 @@ namespace OpenNest.IO
{
Center = center,
Radius = radius,
Layer = CutLayer
Layer = CutLayer,
};
Document.Entities.Add(circle);
}
@@ -432,7 +441,7 @@ namespace OpenNest.IO
Radius = radius,
StartAngle = startAngle,
EndAngle = endAngle,
Layer = CutLayer
Layer = CutLayer,
};
Document.Entities.Add(acadArc);
}
+1 -1
View File
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using ACadSharp;
using OpenNest.Geometry;
using System.Collections.Generic;
namespace OpenNest.IO
{
+14 -11
View File
@@ -1,7 +1,7 @@
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using OpenNest.Geometry;
using static OpenNest.IO.NestFormat;
namespace OpenNest.IO
@@ -13,7 +13,7 @@ namespace OpenNest.IO
return new EntitySetDto
{
Entities = entities.Select(ToEntityDto).ToList(),
Suppressed = suppressed.Select(id => id.ToString()).ToList()
Suppressed = suppressed.Select(id => id.ToString()).ToList(),
};
}
@@ -39,7 +39,7 @@ namespace OpenNest.IO
X1 = line.StartPoint.X,
Y1 = line.StartPoint.Y,
X2 = line.EndPoint.X,
Y2 = line.EndPoint.Y
Y2 = line.EndPoint.Y,
};
case EntityType.Arc:
@@ -55,7 +55,7 @@ namespace OpenNest.IO
R = arc.Radius,
StartAngle = arc.StartAngle,
EndAngle = arc.EndAngle,
Reversed = arc.IsReversed
Reversed = arc.IsReversed,
};
case EntityType.Circle:
@@ -69,11 +69,13 @@ namespace OpenNest.IO
CX = circle.Center.X,
CY = circle.Center.Y,
R = circle.Radius,
Rotation = circle.Rotation == RotationType.CW ? "CW" : "CCW"
Rotation = circle.Rotation == RotationType.CW ? "CW" : "CCW",
};
default:
throw new NotSupportedException($"Entity type {entity.Type} is not supported for serialization.");
throw new NotSupportedException(
$"Entity type {entity.Type} is not supported for serialization."
);
}
}
@@ -84,9 +86,7 @@ namespace OpenNest.IO
switch (dto.Type)
{
case "line":
entity = new Line(
new Vector(dto.X1, dto.Y1),
new Vector(dto.X2, dto.Y2));
entity = new Line(new Vector(dto.X1, dto.Y1), new Vector(dto.X2, dto.Y2));
break;
case "arc":
@@ -95,7 +95,8 @@ namespace OpenNest.IO
dto.R,
dto.StartAngle,
dto.EndAngle,
dto.Reversed);
dto.Reversed
);
break;
case "circle":
@@ -105,7 +106,9 @@ namespace OpenNest.IO
break;
default:
throw new NotSupportedException($"Entity type '{dto.Type}' is not supported for deserialization.");
throw new NotSupportedException(
$"Entity type '{dto.Type}' is not supported for deserialization."
);
}
entity.Id = Guid.Parse(dto.Id);
+73 -44
View File
@@ -1,10 +1,10 @@
using ACadSharp.Entities;
using CSMath;
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using ACadSharp.Entities;
using CSMath;
using OpenNest.Geometry;
namespace OpenNest.IO
{
@@ -23,11 +23,14 @@ namespace OpenNest.IO
public static Geometry.Arc ToOpenNest(this ACadSharp.Entities.Arc arc)
{
var result = new Geometry.Arc(
arc.Center.X, arc.Center.Y, arc.Radius,
arc.Center.X,
arc.Center.Y,
arc.Radius,
arc.StartAngle,
arc.EndAngle)
arc.EndAngle
)
{
Layer = arc.Layer.ToOpenNest()
Layer = arc.Layer.ToOpenNest(),
};
result.ApplyDxfProperties(arc);
return result;
@@ -35,11 +38,9 @@ namespace OpenNest.IO
public static Geometry.Circle ToOpenNest(this ACadSharp.Entities.Circle circle)
{
var result = new Geometry.Circle(
circle.Center.X, circle.Center.Y,
circle.Radius)
var result = new Geometry.Circle(circle.Center.X, circle.Center.Y, circle.Radius)
{
Layer = circle.Layer.ToOpenNest()
Layer = circle.Layer.ToOpenNest(),
};
result.ApplyDxfProperties(circle);
return result;
@@ -48,10 +49,13 @@ namespace OpenNest.IO
public static Geometry.Line ToOpenNest(this ACadSharp.Entities.Line line)
{
var result = new Geometry.Line(
line.StartPoint.X, line.StartPoint.Y,
line.EndPoint.X, line.EndPoint.Y)
line.StartPoint.X,
line.StartPoint.Y,
line.EndPoint.X,
line.EndPoint.Y
)
{
Layer = line.Layer.ToOpenNest()
Layer = line.Layer.ToOpenNest(),
};
result.ApplyDxfProperties(line);
return result;
@@ -115,25 +119,30 @@ namespace OpenNest.IO
{
var nextPoint = polyline.Vertices[i].Location.ToOpenNest();
lines.Add(new Geometry.Line(lastPoint, nextPoint)
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName
});
lines.Add(
new Geometry.Line(lastPoint, nextPoint)
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName,
}
);
lastPoint = nextPoint;
}
var isClosed = (polyline.Flags & PolylineFlags.ClosedPolylineOrClosedPolygonMeshInM) != 0;
var isClosed =
(polyline.Flags & PolylineFlags.ClosedPolylineOrClosedPolygonMeshInM) != 0;
if (isClosed)
lines.Add(new Geometry.Line(lastPoint, polyline.Vertices[0].Location.ToOpenNest())
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName
});
lines.Add(
new Geometry.Line(lastPoint, polyline.Vertices[0].Location.ToOpenNest())
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName,
}
);
return lines;
}
@@ -154,12 +163,14 @@ namespace OpenNest.IO
{
var nextPoint = polyline.Vertices[i].ToOpenNest();
lines.Add(new Geometry.Line(lastPoint, nextPoint)
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName
});
lines.Add(
new Geometry.Line(lastPoint, nextPoint)
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName,
}
);
lastPoint = nextPoint;
}
@@ -167,17 +178,22 @@ namespace OpenNest.IO
var isClosed = (polyline.Flags & LwPolylineFlags.Closed) != 0;
if (isClosed)
lines.Add(new Geometry.Line(lastPoint, polyline.Vertices[0].ToOpenNest())
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName
});
lines.Add(
new Geometry.Line(lastPoint, polyline.Vertices[0].ToOpenNest())
{
Layer = layer,
Color = color,
LineTypeName = lineTypeName,
}
);
return lines;
}
public static List<Geometry.Entity> ToOpenNest(this ACadSharp.Entities.Ellipse ellipse, double tolerance = 0.001)
public static List<Geometry.Entity> ToOpenNest(
this ACadSharp.Entities.Ellipse ellipse,
double tolerance = 0.001
)
{
var center = new Vector(ellipse.Center.X, ellipse.Center.Y);
var majorAxis = new Vector(ellipse.MajorAxisEndPoint.X, ellipse.MajorAxisEndPoint.Y);
@@ -201,8 +217,15 @@ namespace OpenNest.IO
var color = ellipse.ResolveColor();
var lineTypeName = ellipse.ResolveLineTypeName();
var entities = EllipseConverter.Convert(center, semiMajor, semiMinor, rotation,
startParam, endParam, tolerance);
var entities = EllipseConverter.Convert(
center,
semiMajor,
semiMinor,
rotation,
startParam,
endParam,
tolerance
);
foreach (var entity in entities)
{
@@ -220,7 +243,7 @@ namespace OpenNest.IO
{
Color = Color.FromArgb(layer.Color.R, layer.Color.G, layer.Color.B),
IsVisible = layer.IsOn,
LineTypeName = layer.LineType?.Name
LineTypeName = layer.LineType?.Name,
};
}
@@ -238,13 +261,19 @@ namespace OpenNest.IO
{
var lt = entity.LineType;
if (lt == null || string.Equals(lt.Name, "ByLayer", System.StringComparison.OrdinalIgnoreCase))
if (
lt == null
|| string.Equals(lt.Name, "ByLayer", System.StringComparison.OrdinalIgnoreCase)
)
return entity.Layer.LineType?.Name ?? "Continuous";
return lt.Name;
}
public static void ApplyDxfProperties(this Geometry.Entity target, ACadSharp.Entities.Entity source)
public static void ApplyDxfProperties(
this Geometry.Entity target,
ACadSharp.Entities.Entity source
)
{
target.Color = source.ResolveColor();
target.LineTypeName = source.ResolveLineTypeName();
+1 -1
View File
@@ -11,7 +11,7 @@ namespace OpenNest.IO
public static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
WriteIndented = true,
};
public record NestDto
+102 -57
View File
@@ -1,7 +1,3 @@
using OpenNest.Bending;
using OpenNest.CNC;
using OpenNest.Engine.BestFit;
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Drawing;
@@ -9,6 +5,10 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json;
using OpenNest.Bending;
using OpenNest.CNC;
using OpenNest.Engine.BestFit;
using OpenNest.Geometry;
using static OpenNest.IO.NestFormat;
namespace OpenNest.IO
@@ -49,7 +49,8 @@ namespace OpenNest.IO
private string ReadEntry(string name)
{
var entry = zipArchive.GetEntry(name)
var entry =
zipArchive.GetEntry(name)
?? throw new InvalidDataException($"Nest file is missing required entry '{name}'.");
using var entryStream = entry.Open();
using var reader = new StreamReader(entryStream);
@@ -62,7 +63,8 @@ namespace OpenNest.IO
for (var i = 1; i <= count; i++)
{
var entry = zipArchive.GetEntry($"programs/program-{i}");
if (entry == null) continue;
if (entry == null)
continue;
using var entryStream = entry.Open();
var memStream = new MemoryStream();
@@ -120,7 +122,10 @@ namespace OpenNest.IO
// Wire up SubProgramCall.Program references
foreach (var code in parent.Codes)
{
if (code is SubProgramCall call && parent.SubPrograms.TryGetValue(call.Id, out var sub))
if (
code is SubProgramCall call
&& parent.SubPrograms.TryGetValue(call.Id, out var sub)
)
call.Program = sub;
}
}
@@ -133,13 +138,16 @@ namespace OpenNest.IO
return reader.Read();
}
private Dictionary<int, (List<Entity> entities, HashSet<Guid> suppressed)> ReadEntitySets(int count)
private Dictionary<int, (List<Entity> entities, HashSet<Guid> suppressed)> ReadEntitySets(
int count
)
{
var result = new Dictionary<int, (List<Entity>, HashSet<Guid>)>();
for (var i = 1; i <= count; i++)
{
var entry = zipArchive.GetEntry($"entities/entities-{i}");
if (entry == null) continue;
if (entry == null)
continue;
using var entryStream = entry.Open();
using var reader = new StreamReader(entryStream);
@@ -150,8 +158,11 @@ namespace OpenNest.IO
return result;
}
private Dictionary<int, Drawing> BuildDrawings(NestDto dto, Dictionary<int, Program> programs,
Dictionary<int, (List<Entity> entities, HashSet<Guid> suppressed)> entitySets)
private Dictionary<int, Drawing> BuildDrawings(
NestDto dto,
Dictionary<int, Program> programs,
Dictionary<int, (List<Entity> entities, HashSet<Guid> suppressed)> entitySets
)
{
var map = new Dictionary<int, Drawing>();
foreach (var d in dto.Drawings)
@@ -165,7 +176,11 @@ namespace OpenNest.IO
drawing.Constraints.StartAngle = d.Constraints.StartAngle;
drawing.Constraints.EndAngle = d.Constraints.EndAngle;
drawing.Constraints.Allow180Equivalent = d.Constraints.Allow180Equivalent;
drawing.Material = new Material(d.Material.Name, d.Material.Grade, d.Material.Density);
drawing.Material = new Material(
d.Material.Name,
d.Material.Grade,
d.Material.Density
);
drawing.Source.Path = d.Source.Path;
drawing.Source.Offset = new Vector(d.Source.Offset.X, d.Source.Offset.Y);
@@ -173,16 +188,23 @@ namespace OpenNest.IO
{
foreach (var b in d.Bends)
{
drawing.Bends.Add(new Bend
{
StartPoint = new Vector(b.StartX, b.StartY),
EndPoint = new Vector(b.EndX, b.EndY),
Direction = Enum.TryParse<BendDirection>(b.Direction, true, out var dir)
? dir : BendDirection.Unknown,
Angle = b.Angle,
Radius = b.Radius,
NoteText = b.NoteText
});
drawing.Bends.Add(
new Bend
{
StartPoint = new Vector(b.StartX, b.StartY),
EndPoint = new Vector(b.EndX, b.EndY),
Direction = Enum.TryParse<BendDirection>(
b.Direction,
true,
out var dir
)
? dir
: BendDirection.Unknown,
Angle = b.Angle,
Radius = b.Radius,
NoteText = b.NoteText,
}
);
}
}
@@ -205,14 +227,16 @@ namespace OpenNest.IO
foreach (var kvp in drawingMap)
{
var entry = zipArchive.GetEntry($"bestfits/bestfit-{kvp.Key}");
if (entry == null) continue;
if (entry == null)
continue;
using var entryStream = entry.Open();
using var reader = new StreamReader(entryStream);
var json = reader.ReadToEnd();
var sets = JsonSerializer.Deserialize<List<BestFitSetDto>>(json, JsonOptions);
if (sets == null) continue;
if (sets == null)
continue;
PopulateBestFitSets(kvp.Value, sets);
}
@@ -222,29 +246,37 @@ namespace OpenNest.IO
{
foreach (var set in sets)
{
var results = set.Results.Select(r => new BestFitResult
{
Candidate = new PairCandidate
var results = set
.Results.Select(r => new BestFitResult
{
Drawing = drawing,
Part1Rotation = r.Part1Rotation,
Part2Rotation = r.Part2Rotation,
Part2Offset = new Vector(r.Part2OffsetX, r.Part2OffsetY),
StrategyIndex = r.StrategyType,
TestNumber = r.TestNumber,
Spacing = r.CandidateSpacing
},
RotatedArea = r.RotatedArea,
BoundingWidth = r.BoundingWidth,
BoundingHeight = r.BoundingHeight,
OptimalRotation = r.OptimalRotation,
Keep = r.Keep,
Reason = r.Reason,
TrueArea = r.TrueArea,
HullAngles = r.HullAngles
}).ToList();
Candidate = new PairCandidate
{
Drawing = drawing,
Part1Rotation = r.Part1Rotation,
Part2Rotation = r.Part2Rotation,
Part2Offset = new Vector(r.Part2OffsetX, r.Part2OffsetY),
StrategyIndex = r.StrategyType,
TestNumber = r.TestNumber,
Spacing = r.CandidateSpacing,
},
RotatedArea = r.RotatedArea,
BoundingWidth = r.BoundingWidth,
BoundingHeight = r.BoundingHeight,
OptimalRotation = r.OptimalRotation,
Keep = r.Keep,
Reason = r.Reason,
TrueArea = r.TrueArea,
HullAngles = r.HullAngles,
})
.ToList();
BestFitCache.Populate(drawing, set.PlateWidth, set.PlateHeight, set.Spacing, results);
BestFitCache.Populate(
drawing,
set.PlateWidth,
set.PlateHeight,
set.Spacing,
results
);
}
}
@@ -273,18 +305,25 @@ namespace OpenNest.IO
nest.PlateDefaults.Size = new OpenNest.Geometry.Size(pd.Size.Width, pd.Size.Length);
nest.PlateDefaults.Quadrant = pd.Quadrant;
nest.PlateDefaults.PartSpacing = pd.PartSpacing;
nest.PlateDefaults.EdgeSpacing = new Spacing(pd.EdgeSpacing.Left, pd.EdgeSpacing.Bottom, pd.EdgeSpacing.Right, pd.EdgeSpacing.Top);
nest.PlateDefaults.EdgeSpacing = new Spacing(
pd.EdgeSpacing.Left,
pd.EdgeSpacing.Bottom,
pd.EdgeSpacing.Right,
pd.EdgeSpacing.Top
);
// Plate optimizer settings
nest.SalvageRate = dto.SalvageRate;
if (dto.PlateOptions != null)
{
nest.PlateOptions = dto.PlateOptions.Select(o => new PlateOption
{
Width = o.Width,
Length = o.Length,
Cost = o.Cost,
}).ToList();
nest.PlateOptions = dto
.PlateOptions.Select(o => new PlateOption
{
Width = o.Width,
Length = o.Length,
Cost = o.Cost,
})
.ToList();
}
// Drawings
@@ -299,7 +338,12 @@ namespace OpenNest.IO
plate.Quadrant = p.Quadrant;
plate.Quantity = p.Quantity;
plate.PartSpacing = p.PartSpacing;
plate.EdgeSpacing = new Spacing(p.EdgeSpacing.Left, p.EdgeSpacing.Bottom, p.EdgeSpacing.Right, p.EdgeSpacing.Top);
plate.EdgeSpacing = new Spacing(
p.EdgeSpacing.Left,
p.EdgeSpacing.Bottom,
p.EdgeSpacing.Right,
p.EdgeSpacing.Top
);
plate.GrainAngle = p.GrainAngle;
foreach (var partDto in p.Parts)
@@ -318,13 +362,14 @@ namespace OpenNest.IO
{
foreach (var cutoffDto in p.CutOffs)
{
var axis = cutoffDto.Axis?.ToLowerInvariant() == "horizontal"
? CutOffAxis.Horizontal
: CutOffAxis.Vertical;
var axis =
cutoffDto.Axis?.ToLowerInvariant() == "horizontal"
? CutOffAxis.Horizontal
: CutOffAxis.Vertical;
var cutoff = new CutOff(new Vector(cutoffDto.X, cutoffDto.Y), axis)
{
StartLimit = cutoffDto.StartLimit,
EndLimit = cutoffDto.EndLimit
EndLimit = cutoffDto.EndLimit,
};
plate.CutOffs.Add(cutoff);
}
+215 -161
View File
@@ -1,6 +1,3 @@
using OpenNest.Bending;
using OpenNest.CNC;
using OpenNest.Engine.BestFit;
using System;
using System.Collections.Generic;
using System.IO;
@@ -8,6 +5,9 @@ using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.Json;
using OpenNest.Bending;
using OpenNest.CNC;
using OpenNest.Engine.BestFit;
using static OpenNest.IO.NestFormat;
namespace OpenNest.IO
@@ -85,17 +85,20 @@ namespace OpenNest.IO
{
Name = nest.Material.Name ?? "",
Grade = nest.Material.Grade ?? "",
Density = nest.Material.Density
Density = nest.Material.Density,
},
PlateDefaults = BuildPlateDefaultsDto(),
Drawings = BuildDrawingDtos(),
Plates = BuildPlateDtos(),
PlateOptions = nest.PlateOptions?.Select(o => new PlateOptionDto
{
Width = o.Width,
Length = o.Length,
Cost = o.Cost,
}).ToList() ?? new(),
PlateOptions =
nest.PlateOptions?.Select(o => new PlateOptionDto
{
Width = o.Width,
Length = o.Length,
Cost = o.Cost,
})
.ToList()
?? new(),
SalvageRate = nest.SalvageRate,
};
}
@@ -113,15 +116,15 @@ namespace OpenNest.IO
{
Name = nest.Material.Name ?? "",
Grade = nest.Material.Grade ?? "",
Density = nest.Material.Density
Density = nest.Material.Density,
},
EdgeSpacing = new SpacingDto
{
Left = pd.EdgeSpacing.Left,
Top = pd.EdgeSpacing.Top,
Right = pd.EdgeSpacing.Right,
Bottom = pd.EdgeSpacing.Bottom
}
Bottom = pd.EdgeSpacing.Bottom,
},
};
}
@@ -131,44 +134,55 @@ namespace OpenNest.IO
foreach (var kvp in drawingDict.OrderBy(k => k.Key))
{
var d = kvp.Value;
list.Add(new DrawingDto
{
Id = kvp.Key,
Name = d.Name ?? "",
Customer = d.Customer ?? "",
Color = new ColorDto { A = d.Color.A, R = d.Color.R, G = d.Color.G, B = d.Color.B },
Quantity = new QuantityDto { Required = d.Quantity.Required },
Priority = d.Priority,
Constraints = new ConstraintsDto
list.Add(
new DrawingDto
{
StepAngle = d.Constraints.StepAngle,
StartAngle = d.Constraints.StartAngle,
EndAngle = d.Constraints.EndAngle,
Allow180Equivalent = d.Constraints.Allow180Equivalent
},
Material = new MaterialDto
{
Name = d.Material.Name ?? "",
Grade = d.Material.Grade ?? "",
Density = d.Material.Density
},
Source = new SourceDto
{
Path = d.Source.Path ?? "",
Offset = new OffsetDto { X = d.Source.Offset.X, Y = d.Source.Offset.Y }
},
Bends = d.Bends?.Select(b => new BendDto
{
StartX = b.StartPoint.X,
StartY = b.StartPoint.Y,
EndX = b.EndPoint.X,
EndY = b.EndPoint.Y,
Direction = b.Direction.ToString(),
Angle = b.Angle,
Radius = b.Radius,
NoteText = b.NoteText ?? ""
}).ToList() ?? new List<BendDto>()
});
Id = kvp.Key,
Name = d.Name ?? "",
Customer = d.Customer ?? "",
Color = new ColorDto
{
A = d.Color.A,
R = d.Color.R,
G = d.Color.G,
B = d.Color.B,
},
Quantity = new QuantityDto { Required = d.Quantity.Required },
Priority = d.Priority,
Constraints = new ConstraintsDto
{
StepAngle = d.Constraints.StepAngle,
StartAngle = d.Constraints.StartAngle,
EndAngle = d.Constraints.EndAngle,
Allow180Equivalent = d.Constraints.Allow180Equivalent,
},
Material = new MaterialDto
{
Name = d.Material.Name ?? "",
Grade = d.Material.Grade ?? "",
Density = d.Material.Density,
},
Source = new SourceDto
{
Path = d.Source.Path ?? "",
Offset = new OffsetDto { X = d.Source.Offset.X, Y = d.Source.Offset.Y },
},
Bends =
d.Bends?.Select(b => new BendDto
{
StartX = b.StartPoint.X,
StartY = b.StartPoint.Y,
EndX = b.EndPoint.X,
EndY = b.EndPoint.Y,
Direction = b.Direction.ToString(),
Angle = b.Angle,
Radius = b.Radius,
NoteText = b.NoteText ?? "",
})
.ToList()
?? new List<BendDto>(),
}
);
}
return list;
}
@@ -181,56 +195,67 @@ namespace OpenNest.IO
{
var plate = nest.Plates[i];
if (plate.Parts.Count(p => !p.BaseDrawing.IsCutOff) == 0 && plate.CutOffs.Count == 0)
if (
plate.Parts.Count(p => !p.BaseDrawing.IsCutOff) == 0
&& plate.CutOffs.Count == 0
)
continue;
id++;
var parts = new List<PartDto>();
foreach (var part in plate.Parts.Where(p => !p.BaseDrawing.IsCutOff))
{
var match = drawingDict.Where(dwg => dwg.Value == part.BaseDrawing).FirstOrDefault();
parts.Add(new PartDto
{
DrawingId = match.Key,
X = part.Location.X,
Y = part.Location.Y,
Rotation = part.Rotation,
HasManualLeadIns = part.HasManualLeadIns,
LeadInsLocked = part.LeadInsLocked
});
var match = drawingDict
.Where(dwg => dwg.Value == part.BaseDrawing)
.FirstOrDefault();
parts.Add(
new PartDto
{
DrawingId = match.Key,
X = part.Location.X,
Y = part.Location.Y,
Rotation = part.Rotation,
HasManualLeadIns = part.HasManualLeadIns,
LeadInsLocked = part.LeadInsLocked,
}
);
}
var cutoffs = new List<CutOffDto>();
foreach (var cutoff in plate.CutOffs)
{
cutoffs.Add(new CutOffDto
{
X = cutoff.Position.X,
Y = cutoff.Position.Y,
Axis = cutoff.Axis == CutOffAxis.Vertical ? "vertical" : "horizontal",
StartLimit = cutoff.StartLimit,
EndLimit = cutoff.EndLimit
});
cutoffs.Add(
new CutOffDto
{
X = cutoff.Position.X,
Y = cutoff.Position.Y,
Axis = cutoff.Axis == CutOffAxis.Vertical ? "vertical" : "horizontal",
StartLimit = cutoff.StartLimit,
EndLimit = cutoff.EndLimit,
}
);
}
list.Add(new PlateDto
{
Id = id,
Size = new SizeDto { Width = plate.Size.Width, Length = plate.Size.Length },
Quadrant = plate.Quadrant,
Quantity = plate.Quantity,
PartSpacing = plate.PartSpacing,
EdgeSpacing = new SpacingDto
list.Add(
new PlateDto
{
Left = plate.EdgeSpacing.Left,
Top = plate.EdgeSpacing.Top,
Right = plate.EdgeSpacing.Right,
Bottom = plate.EdgeSpacing.Bottom
},
Parts = parts,
CutOffs = cutoffs,
GrainAngle = plate.GrainAngle
});
Id = id,
Size = new SizeDto { Width = plate.Size.Width, Length = plate.Size.Length },
Quadrant = plate.Quadrant,
Quantity = plate.Quantity,
PartSpacing = plate.PartSpacing,
EdgeSpacing = new SpacingDto
{
Left = plate.EdgeSpacing.Left,
Top = plate.EdgeSpacing.Top,
Right = plate.EdgeSpacing.Right,
Bottom = plate.EdgeSpacing.Bottom,
},
Parts = parts,
CutOffs = cutoffs,
GrainAngle = plate.GrainAngle,
}
);
}
return list;
}
@@ -247,11 +272,13 @@ namespace OpenNest.IO
foreach (var kvp in allBestFits)
{
if (!plateSizes.Contains((kvp.Key.PlateWidth, kvp.Key.PlateHeight, kvp.Key.Spacing)))
if (
!plateSizes.Contains((kvp.Key.PlateWidth, kvp.Key.PlateHeight, kvp.Key.Spacing))
)
continue;
var results = kvp.Value
.Where(r => r.Keep)
var results = kvp
.Value.Where(r => r.Keep)
.Select(r => new BestFitResultDto
{
Part1Rotation = r.Candidate.Part1Rotation,
@@ -268,16 +295,19 @@ namespace OpenNest.IO
Keep = r.Keep,
Reason = r.Reason ?? "",
TrueArea = r.TrueArea,
HullAngles = r.HullAngles ?? new List<double>()
}).ToList();
HullAngles = r.HullAngles ?? new List<double>(),
})
.ToList();
sets.Add(new BestFitSetDto
{
PlateWidth = kvp.Key.PlateWidth,
PlateHeight = kvp.Key.PlateHeight,
Spacing = kvp.Key.Spacing,
Results = results
});
sets.Add(
new BestFitSetDto
{
PlateWidth = kvp.Key.PlateWidth,
PlateHeight = kvp.Key.PlateHeight,
Spacing = kvp.Key.Spacing,
Results = results,
}
);
}
return sets;
@@ -319,7 +349,11 @@ namespace OpenNest.IO
}
}
private void WriteSubPrograms(ZipArchive zipArchive, int drawingId, Dictionary<int, Program> subPrograms)
private void WriteSubPrograms(
ZipArchive zipArchive,
int drawingId,
Dictionary<int, Program> subPrograms
)
{
var entry = zipArchive.CreateEntry($"programs/program-{drawingId}-subs");
using var entryStream = entry.Open();
@@ -345,7 +379,10 @@ namespace OpenNest.IO
if (drawing.SourceEntities == null || drawing.SourceEntities.Count == 0)
continue;
var dto = EntitySerializer.ToDto(drawing.SourceEntities, drawing.SuppressedEntityIds);
var dto = EntitySerializer.ToDto(
drawing.SourceEntities,
drawing.SuppressedEntityIds
);
var json = JsonSerializer.Serialize(dto, JsonOptions);
var entry = zipArchive.CreateEntry($"entities/entities-{kvp.Key}");
@@ -365,8 +402,10 @@ namespace OpenNest.IO
foreach (var v in program.Variables.Values)
{
var line = $"{v.Name} = {v.Expression}";
if (v.Inline) line += " inline";
if (v.Global) line += " global";
if (v.Inline)
line += " inline";
if (v.Global)
line += " global";
writer.WriteLine(line);
}
@@ -381,7 +420,11 @@ namespace OpenNest.IO
stream.Position = 0;
}
private string FormatCoord(double value, string axis, Dictionary<string, string> variableRefs)
private string FormatCoord(
double value,
string axis,
Dictionary<string, string> variableRefs
)
{
if (variableRefs != null && variableRefs.TryGetValue(axis, out var varName))
return $"${varName}";
@@ -393,89 +436,100 @@ namespace OpenNest.IO
switch (code.Type)
{
case CodeType.ArcMove:
{
var sb = new StringBuilder();
var arcMove = (ArcMove)code;
var refs = arcMove.VariableRefs;
{
var sb = new StringBuilder();
var arcMove = (ArcMove)code;
var refs = arcMove.VariableRefs;
var x = FormatCoord(arcMove.EndPoint.X, "X", refs);
var y = FormatCoord(arcMove.EndPoint.Y, "Y", refs);
var i = FormatCoord(arcMove.CenterPoint.X, "I", refs);
var j = FormatCoord(arcMove.CenterPoint.Y, "J", refs);
var x = FormatCoord(arcMove.EndPoint.X, "X", refs);
var y = FormatCoord(arcMove.EndPoint.Y, "Y", refs);
var i = FormatCoord(arcMove.CenterPoint.X, "I", refs);
var j = FormatCoord(arcMove.CenterPoint.Y, "J", refs);
sb.Append(arcMove.Rotation == RotationType.CW
sb.Append(
arcMove.Rotation == RotationType.CW
? $"G02X{x}Y{y}I{i}J{j}"
: $"G03X{x}Y{y}I{i}J{j}");
: $"G03X{x}Y{y}I{i}J{j}"
);
if (arcMove.Layer != LayerType.Cut)
sb.Append(GetLayerString(arcMove.Layer));
if (arcMove.Layer != LayerType.Cut)
sb.Append(GetLayerString(arcMove.Layer));
if (arcMove.Suppressed)
sb.Append(":SUPPRESSED");
if (arcMove.Suppressed)
sb.Append(":SUPPRESSED");
return sb.ToString();
}
return sb.ToString();
}
case CodeType.Comment:
{
var comment = (Comment)code;
return ":" + comment.Value;
}
{
var comment = (Comment)code;
return ":" + comment.Value;
}
case CodeType.LinearMove:
{
var sb = new StringBuilder();
var linearMove = (LinearMove)code;
var refs = linearMove.VariableRefs;
{
var sb = new StringBuilder();
var linearMove = (LinearMove)code;
var refs = linearMove.VariableRefs;
sb.Append($"G01X{FormatCoord(linearMove.EndPoint.X, "X", refs)}Y{FormatCoord(linearMove.EndPoint.Y, "Y", refs)}");
sb.Append(
$"G01X{FormatCoord(linearMove.EndPoint.X, "X", refs)}Y{FormatCoord(linearMove.EndPoint.Y, "Y", refs)}"
);
if (linearMove.Layer != LayerType.Cut)
sb.Append(GetLayerString(linearMove.Layer));
if (linearMove.Layer != LayerType.Cut)
sb.Append(GetLayerString(linearMove.Layer));
if (linearMove.Suppressed)
sb.Append(":SUPPRESSED");
if (linearMove.Suppressed)
sb.Append(":SUPPRESSED");
return sb.ToString();
}
return sb.ToString();
}
case CodeType.RapidMove:
{
var rapidMove = (RapidMove)code;
var refs = rapidMove.VariableRefs;
{
var rapidMove = (RapidMove)code;
var refs = rapidMove.VariableRefs;
return $"G00X{FormatCoord(rapidMove.EndPoint.X, "X", refs)}Y{FormatCoord(rapidMove.EndPoint.Y, "Y", refs)}";
}
return $"G00X{FormatCoord(rapidMove.EndPoint.X, "X", refs)}Y{FormatCoord(rapidMove.EndPoint.Y, "Y", refs)}";
}
case CodeType.SetFeedrate:
{
var setFeedrate = (Feedrate)code;
if (setFeedrate.VariableRef != null)
return $"F${setFeedrate.VariableRef}";
return "F" + setFeedrate.Value;
}
{
var setFeedrate = (Feedrate)code;
if (setFeedrate.VariableRef != null)
return $"F${setFeedrate.VariableRef}";
return "F" + setFeedrate.Value;
}
case CodeType.SetKerf:
{
var setKerf = (Kerf)code;
switch (setKerf.Value)
{
var setKerf = (Kerf)code;
switch (setKerf.Value)
{
case KerfType.None: return "G40";
case KerfType.Left: return "G41";
case KerfType.Right: return "G42";
}
break;
case KerfType.None:
return "G40";
case KerfType.Left:
return "G41";
case KerfType.Right:
return "G42";
}
break;
}
case CodeType.SubProgramCall:
{
var subProgramCall = (SubProgramCall)code;
var x = System.Math.Round(subProgramCall.Offset.X, OutputPrecision).ToString(CoordinateFormat);
var y = System.Math.Round(subProgramCall.Offset.Y, OutputPrecision).ToString(CoordinateFormat);
return $"G65P{subProgramCall.Id}X{x}Y{y}";
}
{
var subProgramCall = (SubProgramCall)code;
var x = System
.Math.Round(subProgramCall.Offset.X, OutputPrecision)
.ToString(CoordinateFormat);
var y = System
.Math.Round(subProgramCall.Offset.Y, OutputPrecision)
.ToString(CoordinateFormat);
return $"G65P{subProgramCall.Id}X{x}Y{y}";
}
}
return string.Empty;
+161 -87
View File
@@ -1,12 +1,12 @@
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
namespace OpenNest.IO
{
@@ -31,15 +31,25 @@ namespace OpenNest.IO
{
// First pass: read all lines, collect variable definitions
var allLines = new List<string>();
var variableDefs = new Dictionary<string, (string expression, bool inline, bool global)>(
StringComparer.OrdinalIgnoreCase);
var variableDefs = new Dictionary<
string,
(string expression, bool inline, bool global)
>(StringComparer.OrdinalIgnoreCase);
var codeLines = new List<string>();
string line;
while ((line = reader.ReadLine()) != null)
{
allLines.Add(line);
if (TryParseVariableDefinition(line, out var name, out var expression, out var isInline, out var isGlobal))
if (
TryParseVariableDefinition(
line,
out var name,
out var expression,
out var isInline,
out var isGlobal
)
)
variableDefs[name] = (expression, isInline, isGlobal);
else
codeLines.Add(line);
@@ -54,7 +64,13 @@ namespace OpenNest.IO
var name = kvp.Key;
var (expression, isInline, isGlobal) = kvp.Value;
var value = resolvedVariables[name];
program.Variables[name] = new VariableDefinition(name, expression, value, isInline, isGlobal);
program.Variables[name] = new VariableDefinition(
name,
expression,
value,
isInline,
isGlobal
);
}
// Second pass: parse G-code lines with variable substitution
@@ -78,7 +94,10 @@ namespace OpenNest.IO
{
// Read the maximal variable name (letters, digits, underscores)
var start = i + 1;
while (start < line.Length && (char.IsLetterOrDigit(line[start]) || line[start] == '_'))
while (
start < line.Length
&& (char.IsLetterOrDigit(line[start]) || line[start] == '_')
)
start++;
var maxName = line.Substring(i + 1, start - i - 1);
@@ -89,8 +108,9 @@ namespace OpenNest.IO
while (nameLen > 0)
{
var candidate = maxName.Substring(0, nameLen);
lookupKey = resolvedVariables.Keys
.FirstOrDefault(k => string.Equals(k, candidate, StringComparison.OrdinalIgnoreCase));
lookupKey = resolvedVariables.Keys.FirstOrDefault(k =>
string.Equals(k, candidate, StringComparison.OrdinalIgnoreCase)
);
if (lookupKey != null)
break;
nameLen--;
@@ -98,7 +118,8 @@ namespace OpenNest.IO
if (lookupKey != null)
{
code.Value = resolvedVariables[lookupKey].ToString(CultureInfo.InvariantCulture);
code.Value = resolvedVariables[lookupKey]
.ToString(CultureInfo.InvariantCulture);
code.VariableRef = lookupKey;
i += nameLen; // advance past the matched variable name
}
@@ -211,7 +232,8 @@ namespace OpenNest.IO
double y = 0;
var layer = LayerType.Cut;
var suppressed = false;
string xRef = null, yRef = null;
string xRef = null,
yRef = null;
while (section == CodeSection.Line)
{
@@ -235,36 +257,36 @@ namespace OpenNest.IO
break;
case ':':
{
var tags = code.Value.Trim().ToUpper().Split(':');
foreach (var tag in tags)
{
var tags = code.Value.Trim().ToUpper().Split(':');
foreach (var tag in tags)
switch (tag)
{
switch (tag)
{
case "DISPLAY":
layer = LayerType.Display;
break;
case "DISPLAY":
layer = LayerType.Display;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
case "SUPPRESSED":
suppressed = true;
break;
}
case "SUPPRESSED":
suppressed = true;
break;
}
break;
}
break;
}
default:
section = CodeSection.Unknown;
@@ -277,7 +299,14 @@ namespace OpenNest.IO
if (isRapid)
program.Codes.Add(new RapidMove(x, y) { VariableRefs = refs });
else
program.Codes.Add(new LinearMove(x, y) { Layer = layer, Suppressed = suppressed, VariableRefs = refs });
program.Codes.Add(
new LinearMove(x, y)
{
Layer = layer,
Suppressed = suppressed,
VariableRefs = refs,
}
);
}
private void ReadArc(RotationType rotation)
@@ -288,7 +317,10 @@ namespace OpenNest.IO
double j = 0;
var layer = LayerType.Cut;
var suppressed = false;
string xRef = null, yRef = null, iRef = null, jRef = null;
string xRef = null,
yRef = null,
iRef = null,
jRef = null;
while (section == CodeSection.Arc)
{
@@ -323,51 +355,58 @@ namespace OpenNest.IO
break;
case ':':
{
var tags = code.Value.Trim().ToUpper().Split(':');
foreach (var tag in tags)
{
var tags = code.Value.Trim().ToUpper().Split(':');
foreach (var tag in tags)
switch (tag)
{
switch (tag)
{
case "DISPLAY":
layer = LayerType.Display;
break;
case "DISPLAY":
layer = LayerType.Display;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADIN":
layer = LayerType.Leadin;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "LEADOUT":
layer = LayerType.Leadout;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
case "SCRIBE":
layer = LayerType.Scribe;
break;
case "SUPPRESSED":
suppressed = true;
break;
}
case "SUPPRESSED":
suppressed = true;
break;
}
break;
}
break;
}
default:
section = CodeSection.Unknown;
break;
}
}
program.Codes.Add(new ArcMove()
{
EndPoint = new Vector(x, y),
CenterPoint = new Vector(i, j),
Rotation = rotation,
Layer = layer,
Suppressed = suppressed,
VariableRefs = BuildVariableRefs(("X", xRef), ("Y", yRef), ("I", iRef), ("J", jRef))
});
program.Codes.Add(
new ArcMove()
{
EndPoint = new Vector(x, y),
CenterPoint = new Vector(i, j),
Rotation = rotation,
Layer = layer,
Suppressed = suppressed,
VariableRefs = BuildVariableRefs(
("X", xRef),
("Y", yRef),
("I", iRef),
("J", jRef)
),
}
);
}
private void ReadSubProgram()
@@ -411,12 +450,14 @@ namespace OpenNest.IO
}
}
program.Codes.Add(new SubProgramCall
{
Id = p,
Rotation = r,
Offset = new Geometry.Vector(x, y)
});
program.Codes.Add(
new SubProgramCall
{
Id = p,
Rotation = r,
Offset = new Geometry.Vector(x, y),
}
);
}
private Code GetNextCode()
@@ -446,8 +487,13 @@ namespace OpenNest.IO
return block[codeIndex];
}
private static bool TryParseVariableDefinition(string line, out string name, out string expression,
out bool isInline, out bool isGlobal)
private static bool TryParseVariableDefinition(
string line,
out string name,
out string expression,
out bool isInline,
out bool isGlobal
)
{
name = null;
expression = null;
@@ -467,7 +513,22 @@ namespace OpenNest.IO
if (trimmed.Length >= 2 && char.IsDigit(trimmed[1]))
{
var upper = char.ToUpper(firstChar);
if (upper is 'G' or 'M' or 'N' or 'F' or 'X' or 'Y' or 'I' or 'J' or 'T' or 'S' or 'O' or 'P' or 'R')
if (
upper
is 'G'
or 'M'
or 'N'
or 'F'
or 'X'
or 'Y'
or 'I'
or 'J'
or 'T'
or 'S'
or 'O'
or 'P'
or 'R'
)
return false;
}
@@ -515,8 +576,10 @@ namespace OpenNest.IO
for (var i = flagStart; i < words.Length; i++)
{
var word = words[i].ToLowerInvariant();
if (word == "inline") isInline = true;
else if (word == "global") isGlobal = true;
if (word == "inline")
isInline = true;
else if (word == "global")
isGlobal = true;
}
name = rawName;
@@ -524,13 +587,16 @@ namespace OpenNest.IO
}
private static Dictionary<string, double> ResolveVariables(
Dictionary<string, (string expression, bool inline, bool global)> variableDefs)
Dictionary<string, (string expression, bool inline, bool global)> variableDefs
)
{
if (variableDefs.Count == 0)
return new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
// Build dependency graph
var dependencies = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
var dependencies = new Dictionary<string, List<string>>(
StringComparer.OrdinalIgnoreCase
);
foreach (var kvp in variableDefs)
{
var deps = new List<string>();
@@ -540,12 +606,16 @@ namespace OpenNest.IO
if (expr[i] == '$')
{
var start = i + 1;
while (start < expr.Length && (char.IsLetterOrDigit(expr[start]) || expr[start] == '_'))
while (
start < expr.Length
&& (char.IsLetterOrDigit(expr[start]) || expr[start] == '_')
)
start++;
var refName = expr.Substring(i + 1, start - i - 1);
// Find the canonical name (case-insensitive match)
var canonical = variableDefs.Keys
.FirstOrDefault(k => string.Equals(k, refName, StringComparison.OrdinalIgnoreCase));
var canonical = variableDefs.Keys.FirstOrDefault(k =>
string.Equals(k, refName, StringComparison.OrdinalIgnoreCase)
);
if (canonical != null)
deps.Add(canonical);
i = start - 1;
@@ -600,12 +670,16 @@ namespace OpenNest.IO
}
if (order.Count != variableDefs.Count)
throw new InvalidOperationException("Circular dependency detected among variables.");
throw new InvalidOperationException(
"Circular dependency detected among variables."
);
return resolved;
}
private static Dictionary<string, string> BuildVariableRefs(params (string axis, string varRef)[] refs)
private static Dictionary<string, string> BuildVariableRefs(
params (string axis, string varRef)[] refs
)
{
Dictionary<string, string> result = null;
foreach (var (axis, varRef) in refs)
@@ -671,7 +745,7 @@ namespace OpenNest.IO
Unknown,
Arc,
Line,
SubProgram
SubProgram,
}
}
}
+70 -46
View File
@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.IO;
using ACadSharp;
using ACadSharp.Entities;
using ACadSharp.IO;
@@ -6,9 +8,6 @@ using CSMath;
using OpenNest.Bending;
using OpenNest.Converters;
using OpenNest.Geometry;
using System.Collections.Generic;
using System.IO;
// Disambiguate Entity — both ACadSharp.Entities and OpenNest.Geometry define it
using GeoEntity = OpenNest.Geometry.Entity;
@@ -50,13 +49,21 @@ namespace OpenNest.IO
writer.Write();
}
private static void WriteProgramEntities(CadDocument doc, CNC.Program program, ACadSharp.Tables.Layer layer)
private static void WriteProgramEntities(
CadDocument doc,
CNC.Program program,
ACadSharp.Tables.Layer layer
)
{
var geometry = ConvertProgram.ToGeometry(program);
WriteGeometryEntities(doc, geometry, layer);
}
private static void WriteGeometryEntities(CadDocument doc, List<GeoEntity> geometry, ACadSharp.Tables.Layer layer)
private static void WriteGeometryEntities(
CadDocument doc,
List<GeoEntity> geometry,
ACadSharp.Tables.Layer layer
)
{
foreach (var entity in geometry)
{
@@ -67,12 +74,14 @@ namespace OpenNest.IO
switch (entity)
{
case OpenNest.Geometry.Line line:
doc.Entities.Add(new ACadSharp.Entities.Line
{
StartPoint = new XYZ(line.StartPoint.X, line.StartPoint.Y, 0),
EndPoint = new XYZ(line.EndPoint.X, line.EndPoint.Y, 0),
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Line
{
StartPoint = new XYZ(line.StartPoint.X, line.StartPoint.Y, 0),
EndPoint = new XYZ(line.EndPoint.X, line.EndPoint.Y, 0),
Layer = layer,
}
);
break;
case OpenNest.Geometry.Arc arc:
@@ -81,23 +90,27 @@ namespace OpenNest.IO
if (arc.IsReversed)
OpenNest.Math.Generic.Swap(ref startAngle, ref endAngle);
doc.Entities.Add(new ACadSharp.Entities.Arc
{
Center = new XYZ(arc.Center.X, arc.Center.Y, 0),
Radius = arc.Radius,
StartAngle = startAngle,
EndAngle = endAngle,
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Arc
{
Center = new XYZ(arc.Center.X, arc.Center.Y, 0),
Radius = arc.Radius,
StartAngle = startAngle,
EndAngle = endAngle,
Layer = layer,
}
);
break;
case OpenNest.Geometry.Circle circle:
doc.Entities.Add(new ACadSharp.Entities.Circle
{
Center = new XYZ(circle.Center.X, circle.Center.Y, 0),
Radius = circle.Radius,
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Circle
{
Center = new XYZ(circle.Center.X, circle.Center.Y, 0),
Radius = circle.Radius,
Layer = layer,
}
);
break;
case OpenNest.Geometry.Shape shape:
@@ -107,14 +120,19 @@ namespace OpenNest.IO
}
}
private static void WriteBendLine(CadDocument doc, Bend bend, ACadSharp.Tables.Layer layer, LineType lineType)
private static void WriteBendLine(
CadDocument doc,
Bend bend,
ACadSharp.Tables.Layer layer,
LineType lineType
)
{
var line = new ACadSharp.Entities.Line
{
StartPoint = new XYZ(bend.StartPoint.X, bend.StartPoint.Y, 0),
EndPoint = new XYZ(bend.EndPoint.X, bend.EndPoint.Y, 0),
Layer = layer,
LineType = lineType
LineType = lineType,
};
doc.Entities.Add(line);
@@ -128,7 +146,7 @@ namespace OpenNest.IO
InsertPoint = new XYZ(midX, midY + 0.5, 0),
Value = bend.NoteText,
Height = 0.1,
Layer = layer
Layer = layer,
};
doc.Entities.Add(mtext);
}
@@ -145,12 +163,14 @@ namespace OpenNest.IO
if (length < EtchLength * 3.0)
{
doc.Entities.Add(new ACadSharp.Entities.Line
{
StartPoint = new XYZ(start.X, start.Y, 0),
EndPoint = new XYZ(end.X, end.Y, 0),
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Line
{
StartPoint = new XYZ(start.X, start.Y, 0),
EndPoint = new XYZ(end.X, end.Y, 0),
Layer = layer,
}
);
}
else
{
@@ -158,19 +178,23 @@ namespace OpenNest.IO
var dx = System.Math.Cos(angle) * EtchLength;
var dy = System.Math.Sin(angle) * EtchLength;
doc.Entities.Add(new ACadSharp.Entities.Line
{
StartPoint = new XYZ(start.X, start.Y, 0),
EndPoint = new XYZ(start.X + dx, start.Y + dy, 0),
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Line
{
StartPoint = new XYZ(start.X, start.Y, 0),
EndPoint = new XYZ(start.X + dx, start.Y + dy, 0),
Layer = layer,
}
);
doc.Entities.Add(new ACadSharp.Entities.Line
{
StartPoint = new XYZ(end.X, end.Y, 0),
EndPoint = new XYZ(end.X - dx, end.Y - dy, 0),
Layer = layer
});
doc.Entities.Add(
new ACadSharp.Entities.Line
{
StartPoint = new XYZ(end.X, end.Y, 0),
EndPoint = new XYZ(end.X - dx, end.Y - dy, 0),
Layer = layer,
}
);
}
}
}