style: apply CSharpier formatting to all C# sources

Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.

Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).

Formatting is now enforceable: dotnet csharpier check . passes.
This commit is contained in:
aj
2026-09-20 16:41:50 -04:00
parent 8e6fa677fb
commit aec0523062
476 changed files with 16592 additions and 7800 deletions
@@ -129,14 +129,28 @@ public sealed class CincinnatiFeatureWriter
var sb = new StringBuilder();
// Kerf compensation on first cutting move (skip for etch)
if (!ctx.IsEtch && !kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
if (
!ctx.IsEtch
&& !kerfEmitted
&& _config.KerfCompensation == KerfMode.ControllerSide
)
{
sb.Append(_config.DefaultKerfSide == KerfSide.Left ? "G41 " : "G42 ");
kerfEmitted = true;
}
var xCoord = FormatCoordWithVars(linear.EndPoint.X + offset.X, "X", linear.VariableRefs, ctx);
var yCoord = FormatCoordWithVars(linear.EndPoint.Y + offset.Y, "Y", linear.VariableRefs, ctx);
var xCoord = FormatCoordWithVars(
linear.EndPoint.X + offset.X,
"X",
linear.VariableRefs,
ctx
);
var yCoord = FormatCoordWithVars(
linear.EndPoint.Y + offset.Y,
"Y",
linear.VariableRefs,
ctx
);
sb.Append($"G1 X{xCoord} Y{yCoord}");
// Feedrate — etch always uses process feedrate
@@ -155,7 +169,11 @@ public sealed class CincinnatiFeatureWriter
var sb = new StringBuilder();
// Kerf compensation on first cutting move (skip for etch)
if (!ctx.IsEtch && !kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
if (
!ctx.IsEtch
&& !kerfEmitted
&& _config.KerfCompensation == KerfMode.ControllerSide
)
{
sb.Append(_config.DefaultKerfSide == KerfSide.Left ? "G41 " : "G42 ");
kerfEmitted = true;
@@ -163,8 +181,18 @@ public sealed class CincinnatiFeatureWriter
// G2 = CW, G3 = CCW
var gCode = arc.Rotation == RotationType.CW ? "G2" : "G3";
var xCoord = FormatCoordWithVars(arc.EndPoint.X + offset.X, "X", arc.VariableRefs, ctx);
var yCoord = FormatCoordWithVars(arc.EndPoint.Y + offset.Y, "Y", arc.VariableRefs, ctx);
var xCoord = FormatCoordWithVars(
arc.EndPoint.X + offset.X,
"X",
arc.VariableRefs,
ctx
);
var yCoord = FormatCoordWithVars(
arc.EndPoint.Y + offset.Y,
"Y",
arc.VariableRefs,
ctx
);
sb.Append($"{gCode} X{xCoord} Y{yCoord}");
// Convert absolute center to incremental I/J
@@ -175,8 +203,7 @@ public sealed class CincinnatiFeatureWriter
// Feedrate — etch always uses process feedrate, cut uses layer/radius-based
var radius = currentPos.DistanceTo(arc.CenterPoint);
var isFullCircle = IsFullCircle(currentPos, arc.EndPoint);
var feedVar = ctx.IsEtch ? "#148"
: GetArcFeedrate(arc.Layer, radius, isFullCircle);
var feedVar = ctx.IsEtch ? "#148" : GetArcFeedrate(arc.Layer, radius, isFullCircle);
if (feedVar != lastFeedVar)
{
sb.Append($" F{feedVar}");
@@ -211,14 +238,20 @@ public sealed class CincinnatiFeatureWriter
/// the sheet width/length variables.
/// Inline variables fall through to literal formatting.
/// </summary>
private string FormatCoordWithVars(double value, string axis,
Dictionary<string, string> variableRefs, FeatureContext ctx)
private string FormatCoordWithVars(
double value,
string axis,
Dictionary<string, string> variableRefs,
FeatureContext ctx
)
{
// User-defined variable references take priority
if (variableRefs != null
if (
variableRefs != null
&& variableRefs.TryGetValue(axis, out var varName)
&& ctx.UserVariableMapping != null
&& ctx.UserVariableMapping.TryGetValue((ctx.DrawingId, varName), out var varNum))
&& ctx.UserVariableMapping.TryGetValue((ctx.DrawingId, varName), out var varNum)
)
{
return $"#{varNum}";
}
@@ -268,7 +301,12 @@ public sealed class CincinnatiFeatureWriter
return Vector.Zero;
}
private void WriteRapidToPierce(TextWriter writer, FeatureContext ctx, Vector piercePoint, Vector offset)
private void WriteRapidToPierce(
TextWriter writer,
FeatureContext ctx,
Vector piercePoint,
Vector offset
)
{
var sb = new StringBuilder();
@@ -311,15 +349,18 @@ public sealed class CincinnatiFeatureWriter
{
LayerType.Leadin => "#126",
LayerType.Leadout => "#129",
_ => "#148"
_ => "#148",
};
}
private string GetArcFeedrate(LayerType layer, double radius, bool isFullCircle)
{
if (layer == LayerType.Leadin) return "#127";
if (layer == LayerType.Leadout) return "#129";
if (isFullCircle) return "[#148*#128]";
if (layer == LayerType.Leadin)
return "#127";
if (layer == LayerType.Leadout)
return "#129";
if (isFullCircle)
return "[#148*#128]";
return GetArcCutFeedrate(radius);
}
@@ -19,8 +19,10 @@ public sealed class CincinnatiPartSubprogramWriter
private readonly CoordinateFormatter _fmt;
private readonly Dictionary<int, int> _holeSubprograms;
public CincinnatiPartSubprogramWriter(CincinnatiPostConfig config,
Dictionary<int, int> holeSubprograms = null)
public CincinnatiPartSubprogramWriter(
CincinnatiPostConfig config,
Dictionary<int, int> holeSubprograms = null
)
{
_config = config;
_featureWriter = new CincinnatiFeatureWriter(config);
@@ -32,8 +34,15 @@ public sealed class CincinnatiPartSubprogramWriter
/// Writes a complete part sub-program for the given normalized program.
/// The program coordinates must already be normalized to origin (0,0).
/// </summary>
public void Write(TextWriter w, Program normalizedProgram, string drawingName,
int subNumber, string cutLibrary, string etchLibrary, double sheetDiagonal)
public void Write(
TextWriter w,
Program normalizedProgram,
string drawingName,
int subNumber,
string cutLibrary,
string etchLibrary,
double sheetDiagonal
)
{
var allFeatures = FeatureUtils.SplitByRapids(normalizedProgram.Codes);
if (allFeatures.Count == 0)
@@ -58,9 +67,7 @@ public sealed class CincinnatiPartSubprogramWriter
continue;
}
var featureNumber = i == 0
? _config.FeatureLineNumberStart
: 1000 + i + 1;
var featureNumber = i == 0 ? _config.FeatureLineNumberStart : 1000 + i + 1;
var cutDistance = FeatureUtils.ComputeCutDistance(codes);
var ctx = new FeatureContext
@@ -75,7 +82,7 @@ public sealed class CincinnatiPartSubprogramWriter
IsEtch = isEtch,
LibraryFile = isEtch ? etchLibrary : cutLibrary,
CutDistance = cutDistance,
SheetDiagonal = sheetDiagonal
SheetDiagonal = sheetDiagonal,
};
_featureWriter.Write(w, ctx);
@@ -84,15 +91,20 @@ public sealed class CincinnatiPartSubprogramWriter
w.WriteLine($"M99 (END OF {drawingName})");
}
private void WriteHoleSubprogramCall(TextWriter w, SubProgramCall call,
int featureIndex, bool isLastFeature)
private void WriteHoleSubprogramCall(
TextWriter w,
SubProgramCall call,
int featureIndex,
bool isLastFeature
)
{
var postSubNum = _holeSubprograms != null && _holeSubprograms.TryGetValue(call.Id, out var num)
? num : call.Id;
var postSubNum =
_holeSubprograms != null && _holeSubprograms.TryGetValue(call.Id, out var num)
? num
: call.Id;
var featureNumber = featureIndex == 0
? _config.FeatureLineNumberStart
: 1000 + featureIndex + 1;
var featureNumber =
featureIndex == 0 ? _config.FeatureLineNumberStart : 1000 + featureIndex + 1;
var sb = new StringBuilder();
if (_config.UseLineNumbers)
@@ -138,8 +150,10 @@ public sealed class CincinnatiPartSubprogramWriter
/// Scans all plates and builds a mapping of unique part geometries to sub-program numbers,
/// along with their normalized programs for writing.
/// </summary>
internal static (Dictionary<(int, long), int> mapping, List<(int subNum, string name, Program program)> entries)
BuildRegistry(IEnumerable<Plate> plates, int startNumber)
internal static (
Dictionary<(int, long), int> mapping,
List<(int subNum, string name, Program program)> entries
) BuildRegistry(IEnumerable<Plate> plates, int startNumber)
{
var mapping = new Dictionary<(int, long), int>();
var entries = new List<(int, string, Program)>();
@@ -149,7 +163,8 @@ public sealed class CincinnatiPartSubprogramWriter
{
foreach (var part in plate.Parts)
{
if (part.BaseDrawing.IsCutOff) continue;
if (part.BaseDrawing.IsCutOff)
continue;
var key = SubprogramKey(part);
if (!mapping.ContainsKey(key))
{
@@ -180,8 +195,10 @@ public sealed class CincinnatiPartSubprogramWriter
/// Scans all parts across all plates and builds a nest-level registry of unique
/// hole sub-programs. Deduplicates by comparing sub-program code content.
/// </summary>
internal static (Dictionary<int, int> modelToPostMapping, List<(int subNum, Program program)> entries)
BuildHoleRegistry(IEnumerable<Plate> plates, int startNumber)
internal static (
Dictionary<int, int> modelToPostMapping,
List<(int subNum, Program program)> entries
) BuildHoleRegistry(IEnumerable<Plate> plates, int startNumber)
{
var mapping = new Dictionary<int, int>();
var entries = new List<(int, Program)>();
@@ -192,11 +209,14 @@ public sealed class CincinnatiPartSubprogramWriter
{
foreach (var part in plate.Parts)
{
if (part.BaseDrawing.IsCutOff) continue;
if (part.BaseDrawing.IsCutOff)
continue;
foreach (var code in part.Program.Codes)
{
if (code is not SubProgramCall call) continue;
if (mapping.ContainsKey(call.Id)) continue;
if (code is not SubProgramCall call)
continue;
if (mapping.ContainsKey(call.Id))
continue;
var canonical = ProgramToCanonical(call.Program);
if (contentIndex.TryGetValue(canonical, out var existingNum))
@@ -226,7 +246,9 @@ public sealed class CincinnatiPartSubprogramWriter
if (code is LinearMove lm)
sb.Append($"L{lm.EndPoint.X:F6},{lm.EndPoint.Y:F6},{(int)lm.Layer}");
else if (code is ArcMove am)
sb.Append($"A{am.EndPoint.X:F6},{am.EndPoint.Y:F6},{am.CenterPoint.X:F6},{am.CenterPoint.Y:F6},{(int)am.Rotation},{(int)am.Layer}");
sb.Append(
$"A{am.EndPoint.X:F6},{am.EndPoint.Y:F6},{am.CenterPoint.X:F6},{am.CenterPoint.Y:F6},{(int)am.Rotation},{(int)am.Layer}"
);
else if (code is RapidMove rm)
sb.Append($"R{rm.EndPoint.X:F6},{rm.EndPoint.Y:F6}");
}
@@ -17,7 +17,7 @@ namespace OpenNest.Posts.Cincinnati
G91,
/// <summary>Use machine coordinate system.</summary>
G53
G53,
}
/// <summary>
@@ -29,7 +29,7 @@ namespace OpenNest.Posts.Cincinnati
LibraryFile,
/// <summary>Explicitly define G89 parameters in the program.</summary>
Explicit
Explicit,
}
/// <summary>
@@ -41,7 +41,7 @@ namespace OpenNest.Posts.Cincinnati
ControllerSide,
/// <summary>Pre-applied to part geometry during post-processing.</summary>
PreApplied
PreApplied,
}
/// <summary>
@@ -53,7 +53,7 @@ namespace OpenNest.Posts.Cincinnati
Left,
/// <summary>Kerf applied to the right side of the cut.</summary>
Right
Right,
}
/// <summary>
@@ -71,7 +71,7 @@ namespace OpenNest.Posts.Cincinnati
Auto,
/// <summary>Do not use M47.</summary>
None
None,
}
/// <summary>
@@ -86,7 +86,7 @@ namespace OpenNest.Posts.Cincinnati
EndOfSheet,
/// <summary>Pallet exchange at start and end of sheet.</summary>
StartAndEnd
StartAndEnd,
}
/// <summary>
@@ -132,7 +132,9 @@ namespace OpenNest.Posts.Cincinnati
[Category("2. Subprograms")]
[DisplayName("Use Part Subprograms")]
[Description("Use M98 sub-programs for part geometry. Reduces output size for repeated parts.")]
[Description(
"Use M98 sub-programs for part geometry. Reduces output size for repeated parts."
)]
public bool UsePartSubprograms { get; set; } = false;
[Category("2. Subprograms")]
@@ -247,13 +249,31 @@ namespace OpenNest.Posts.Cincinnati
[Category("9. Feedrates")]
[DisplayName("Arc Feedrate Ranges")]
[Description("Radius-based arc feedrate ranges. Matched from smallest to largest MaxRadius.")]
public List<ArcFeedrateRange> ArcFeedrateRanges { get; set; } = new()
{
new() { MaxRadius = 0.125, FeedratePercent = 0.25, VariableNumber = 123 },
new() { MaxRadius = 0.750, FeedratePercent = 0.50, VariableNumber = 124 },
new() { MaxRadius = 4.500, FeedratePercent = 0.80, VariableNumber = 125 }
};
[Description(
"Radius-based arc feedrate ranges. Matched from smallest to largest MaxRadius."
)]
public List<ArcFeedrateRange> ArcFeedrateRanges { get; set; } =
new()
{
new()
{
MaxRadius = 0.125,
FeedratePercent = 0.25,
VariableNumber = 123,
},
new()
{
MaxRadius = 0.750,
FeedratePercent = 0.50,
VariableNumber = 124,
},
new()
{
MaxRadius = 4.500,
FeedratePercent = 0.80,
VariableNumber = 125,
},
};
[Category("A. Variables")]
[DisplayName("User Variable Start")]
@@ -272,7 +292,9 @@ namespace OpenNest.Posts.Cincinnati
[Category("B. Libraries")]
[DisplayName("Material Libraries")]
[Description("Material-to-library mapping for cut operations. Maps (material, thickness, gas) to a G89 library file.")]
[Description(
"Material-to-library mapping for cut operations. Maps (material, thickness, gas) to a G89 library file."
)]
public List<MaterialLibraryEntry> MaterialLibraries { get; set; } = new();
[Category("B. Libraries")]
@@ -282,7 +304,9 @@ namespace OpenNest.Posts.Cincinnati
[Category("B. Libraries")]
[DisplayName("Selected Library")]
[Description("Overrides Material/Thickness/Gas auto-resolution. Pick an existing entry from Material Libraries, or leave blank to auto-resolve.")]
[Description(
"Overrides Material/Thickness/Gas auto-resolution. Pick an existing entry from Material Libraries, or leave blank to auto-resolve."
)]
[TypeConverter(typeof(MaterialLibraryNameConverter))]
public string SelectedLibrary { get; set; } = "";
@@ -292,10 +316,13 @@ namespace OpenNest.Posts.Cincinnati
return "";
return MaterialLibraries
.Where(e => string.Equals(e.Material, materialName, StringComparison.OrdinalIgnoreCase))
.OrderBy(e => System.Math.Abs(e.Thickness - thickness))
.Select(e => e.Library)
.FirstOrDefault() ?? "";
.Where(e =>
string.Equals(e.Material, materialName, StringComparison.OrdinalIgnoreCase)
)
.OrderBy(e => System.Math.Abs(e.Thickness - thickness))
.Select(e => e.Library)
.FirstOrDefault()
?? "";
}
}
@@ -325,7 +352,7 @@ namespace OpenNest.Posts.Cincinnati
Percentages,
/// <summary>Radius-range-based variables: F #varNum based on radius range.</summary>
Variables
Variables,
}
/// <summary>
@@ -9,12 +9,15 @@ using OpenNest.CNC;
namespace OpenNest.Posts.Cincinnati
{
public sealed class CincinnatiPostProcessor : IConfigurablePostProcessor, IPostProcessorNestAware, IMaterialProvidingPostProcessor
public sealed class CincinnatiPostProcessor
: IConfigurablePostProcessor,
IPostProcessorNestAware,
IMaterialProvidingPostProcessor
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
Converters = { new JsonStringEnumConverter() }
Converters = { new JsonStringEnumConverter() },
};
public string Name => "Cincinnati CL-707";
@@ -30,8 +33,8 @@ namespace OpenNest.Posts.Cincinnati
if (Config?.MaterialLibraries == null)
return System.Array.Empty<string>();
return Config.MaterialLibraries
.Select(e => e.Material)
return Config
.MaterialLibraries.Select(e => e.Material)
.Where(s => !string.IsNullOrWhiteSpace(s));
}
@@ -83,9 +86,7 @@ namespace OpenNest.Posts.Cincinnati
var vars = CreateVariableManager();
// 2. Filter to non-empty plates
var plates = nest.Plates
.Where(p => p.Parts.Count > 0)
.ToList();
var plates = nest.Plates.Where(p => p.Parts.Count > 0).ToList();
// 3. Register user variables from drawing programs
var userVarMapping = RegisterUserVariables(vars, plates);
@@ -97,36 +98,55 @@ namespace OpenNest.Posts.Cincinnati
// Resolve cut library from nest material/thickness for preamble
var firstPlate = plates.FirstOrDefault();
var initialCutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
var initialCutLibrary = resolver.ResolveCutLibrary(
nest.Material?.Name ?? "",
nest.Thickness,
gas
);
// 5. Build part sub-program registry (if enabled)
Dictionary<(int, long), int> partSubprograms = null;
List<(int subNum, string name, Program program)> subprogramEntries = null;
if (Config.UsePartSubprograms)
(partSubprograms, subprogramEntries) = CincinnatiPartSubprogramWriter.BuildRegistry(plates, Config.PartSubprogramStart);
(partSubprograms, subprogramEntries) = CincinnatiPartSubprogramWriter.BuildRegistry(
plates,
Config.PartSubprogramStart
);
// 5b. Build hole sub-program registry (SubProgramCalls across all parts)
var holeStartNumber = Config.PartSubprogramStart
+ (subprogramEntries?.Count ?? 0);
var (holeMapping, holeEntries) = CincinnatiPartSubprogramWriter.BuildHoleRegistry(plates, holeStartNumber);
var holeStartNumber = Config.PartSubprogramStart + (subprogramEntries?.Count ?? 0);
var (holeMapping, holeEntries) = CincinnatiPartSubprogramWriter.BuildHoleRegistry(
plates,
holeStartNumber
);
// 6. Create writers
var preamble = new CincinnatiPreambleWriter(Config);
var sheetWriter = new CincinnatiSheetWriter(Config, vars,
holeMapping.Count > 0 ? holeMapping : null);
var sheetWriter = new CincinnatiSheetWriter(
Config,
vars,
holeMapping.Count > 0 ? holeMapping : null
);
// 7. Build material description from nest
var material = nest.Material;
var materialDesc = material != null
? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}"
: "";
var materialDesc =
material != null
? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}"
: "";
// 8. Write to stream
using var writer = new StreamWriter(outputStream, Encoding.UTF8, 1024, leaveOpen: true);
// Main program
preamble.WriteMainProgram(writer, nest.Name ?? "NEST", materialDesc, plates, initialCutLibrary);
preamble.WriteMainProgram(
writer,
nest.Name ?? "NEST",
materialDesc,
plates,
initialCutLibrary
);
// Variable declaration subprogram
preamble.WriteVariableDeclaration(writer, vars);
@@ -137,25 +157,50 @@ namespace OpenNest.Posts.Cincinnati
var plate = plates[i];
var layoutIndex = i + 1;
var subNumber = Config.SheetSubprogramStart + i;
var cutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
sheetWriter.Write(writer, plate, nest.Name ?? "NEST", layoutIndex, subNumber,
cutLibrary, etchLibrary, partSubprograms, userVarMapping);
var cutLibrary = resolver.ResolveCutLibrary(
nest.Material?.Name ?? "",
nest.Thickness,
gas
);
sheetWriter.Write(
writer,
plate,
nest.Name ?? "NEST",
layoutIndex,
subNumber,
cutLibrary,
etchLibrary,
partSubprograms,
userVarMapping
);
}
// Part sub-programs (if enabled)
if (subprogramEntries != null)
{
var partSubWriter = new CincinnatiPartSubprogramWriter(Config,
holeMapping.Count > 0 ? holeMapping : null);
var sheetDiagonal = firstPlate != null
? System.Math.Sqrt(firstPlate.Size.Width * firstPlate.Size.Width
+ firstPlate.Size.Length * firstPlate.Size.Length)
: 100.0;
var partSubWriter = new CincinnatiPartSubprogramWriter(
Config,
holeMapping.Count > 0 ? holeMapping : null
);
var sheetDiagonal =
firstPlate != null
? System.Math.Sqrt(
firstPlate.Size.Width * firstPlate.Size.Width
+ firstPlate.Size.Length * firstPlate.Size.Length
)
: 100.0;
foreach (var (subNum, name, pgm) in subprogramEntries)
{
partSubWriter.Write(writer, pgm, name, subNum,
initialCutLibrary, etchLibrary, sheetDiagonal);
partSubWriter.Write(
writer,
pgm,
name,
subNum,
initialCutLibrary,
etchLibrary,
sheetDiagonal
);
}
}
@@ -163,16 +208,26 @@ namespace OpenNest.Posts.Cincinnati
if (holeEntries.Count > 0)
{
var holeSubWriter = new CincinnatiPartSubprogramWriter(Config);
var sheetDiagonal = firstPlate != null
? System.Math.Sqrt(firstPlate.Size.Width * firstPlate.Size.Width
+ firstPlate.Size.Length * firstPlate.Size.Length)
: 100.0;
var sheetDiagonal =
firstPlate != null
? System.Math.Sqrt(
firstPlate.Size.Width * firstPlate.Size.Width
+ firstPlate.Size.Length * firstPlate.Size.Length
)
: 100.0;
foreach (var (subNum, pgm) in holeEntries)
{
CincinnatiPartSubprogramWriter.EnsureLeadingRapid(pgm);
holeSubWriter.Write(writer, pgm, "HOLE", subNum,
initialCutLibrary, etchLibrary, sheetDiagonal);
holeSubWriter.Write(
writer,
pgm,
"HOLE",
subNum,
initialCutLibrary,
etchLibrary,
sheetDiagonal
);
}
}
@@ -186,13 +241,17 @@ namespace OpenNest.Posts.Cincinnati
}
private Dictionary<(int drawingId, string varName), int> RegisterUserVariables(
ProgramVariableManager vars, List<Plate> plates)
ProgramVariableManager vars,
List<Plate> plates
)
{
var mapping = new Dictionary<(int drawingId, string varName), int>();
var nextNumber = Config.UserVariableStart;
// Track global variables by name so they share a single number
var globalNumbers = new Dictionary<string, int>(System.StringComparer.OrdinalIgnoreCase);
var globalNumbers = new Dictionary<string, int>(
System.StringComparer.OrdinalIgnoreCase
);
// Collect unique drawings from all plates
var seenDrawings = new HashSet<int>();
@@ -285,19 +344,31 @@ namespace OpenNest.Posts.Cincinnati
private ProgramVariableManager CreateVariableManager()
{
var vars = new ProgramVariableManager();
vars.GetOrCreate("ProcessFeedrate", 148); // Set by G89, no expression
vars.GetOrCreate("ProcessFeedrate", 148); // Set by G89, no expression
vars.GetOrCreate("LeadInFeedrate", 126, $"[#148*{Config.LeadInFeedratePercent}]");
vars.GetOrCreate("LeadInArcLine2Feedrate", 127, $"[#148*{Config.LeadInArcLine2FeedratePercent}]");
vars.GetOrCreate("CircleFeedrate", 128, Config.CircleFeedrateMultiplier.ToString("0.#"));
vars.GetOrCreate(
"LeadInArcLine2Feedrate",
127,
$"[#148*{Config.LeadInArcLine2FeedratePercent}]"
);
vars.GetOrCreate(
"CircleFeedrate",
128,
Config.CircleFeedrateMultiplier.ToString("0.#")
);
vars.GetOrCreate("LeadOutFeedrate", 129, $"[#148*{Config.LeadOutFeedratePercent}]");
if (Config.ArcFeedrate == ArcFeedrateMode.Variables)
{
foreach (var range in Config.ArcFeedrateRanges)
{
var name = $"ArcFeedR{range.MaxRadius.ToString("0.###", System.Globalization.CultureInfo.InvariantCulture)}";
vars.GetOrCreate(name, range.VariableNumber,
$"[#148*{range.FeedratePercent.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture)}]");
var name =
$"ArcFeedR{range.MaxRadius.ToString("0.###", System.Globalization.CultureInfo.InvariantCulture)}";
vars.GetOrCreate(
name,
range.VariableNumber,
$"[#148*{range.FeedratePercent.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture)}]"
);
}
}
@@ -23,12 +23,24 @@ public sealed class CincinnatiPreambleWriter
/// Writes the main program header block.
/// </summary>
/// <param name="initialLibrary">Resolved G89 library file for the initial process setup.</param>
public void WriteMainProgram(TextWriter w, string nestName, string materialDescription,
List<Plate> plates, string initialLibrary)
public void WriteMainProgram(
TextWriter w,
string nestName,
string materialDescription,
List<Plate> plates,
string initialLibrary
)
{
w.WriteLine(CoordinateFormatter.Comment($"NEST {nestName}"));
w.WriteLine(CoordinateFormatter.Comment($"CONFIGURATION - {_config.ConfigurationName}"));
w.WriteLine(CoordinateFormatter.Comment(DateTime.Now.ToString("MM-dd-yyyy hh:mm:ss tt", System.Globalization.CultureInfo.InvariantCulture)));
w.WriteLine(
CoordinateFormatter.Comment(
DateTime.Now.ToString(
"MM-dd-yyyy hh:mm:ss tt",
System.Globalization.CultureInfo.InvariantCulture
)
)
);
if (!string.IsNullOrEmpty(materialDescription))
w.WriteLine(CoordinateFormatter.Comment($"Material = {materialDescription}"));
@@ -45,7 +57,10 @@ public sealed class CincinnatiPreambleWriter
w.WriteLine("M42");
if (_config.ProcessParameterMode == G89Mode.LibraryFile && !string.IsNullOrEmpty(initialLibrary))
if (
_config.ProcessParameterMode == G89Mode.LibraryFile
&& !string.IsNullOrEmpty(initialLibrary)
)
w.WriteLine($"G89 P{initialLibrary}");
w.WriteLine($"M98 P{_config.VariableDeclarationSubprogram} (Variable Declaration)");
@@ -61,9 +76,8 @@ public sealed class CincinnatiPreambleWriter
var subNum = _config.SheetSubprogramStart + i;
var qty = System.Math.Max(plates[i].Quantity, 1);
var lParam = qty > 1 ? $" L{qty}" : "";
var sheetLabel = qty > 1
? $"LAYOUT {layoutNumber} - {qty} SHEETS"
: $"LAYOUT {layoutNumber}";
var sheetLabel =
qty > 1 ? $"LAYOUT {layoutNumber} - {qty} SHEETS" : $"LAYOUT {layoutNumber}";
w.WriteLine($"N{layoutNumber} M98 P{subNum}{lParam} ({sheetLabel})");
}
@@ -19,8 +19,11 @@ public sealed class CincinnatiSheetWriter
private readonly CincinnatiFeatureWriter _featureWriter;
private readonly Dictionary<int, int> _holeSubprograms;
public CincinnatiSheetWriter(CincinnatiPostConfig config, ProgramVariableManager vars,
Dictionary<int, int> holeSubprograms = null)
public CincinnatiSheetWriter(
CincinnatiPostConfig config,
ProgramVariableManager vars,
Dictionary<int, int> holeSubprograms = null
)
{
_config = config;
_vars = vars;
@@ -38,10 +41,17 @@ public sealed class CincinnatiSheetWriter
/// Optional mapping of (drawingId, rotationKey) to sub-program number.
/// When provided, non-cutoff parts are emitted as M98 calls instead of inline features.
/// </param>
public void Write(TextWriter w, Plate plate, string nestName, int layoutIndex, int subNumber,
string cutLibrary, string etchLibrary,
public void Write(
TextWriter w,
Plate plate,
string nestName,
int layoutIndex,
int subNumber,
string cutLibrary,
string etchLibrary,
Dictionary<(int, long), int> partSubprograms = null,
Dictionary<(int drawingId, string varName), int> userVarMapping = null)
Dictionary<(int drawingId, string varName), int> userVarMapping = null
)
{
if (plate.Parts.Count == 0)
return;
@@ -59,8 +69,12 @@ public sealed class CincinnatiSheetWriter
w.WriteLine($"( Layout {layoutIndex} )");
w.WriteLine($"( SHEET NAME = {_fmt.FormatCoord(width)} X {_fmt.FormatCoord(length)} )");
w.WriteLine($"( Total parts on sheet = {partCount} )");
w.WriteLine($"#{_config.SheetWidthVariable}={_fmt.FormatCoord(width)} (SHEET WIDTH FOR CUTOFFS)");
w.WriteLine($"#{_config.SheetLengthVariable}={_fmt.FormatCoord(length)} (SHEET LENGTH FOR CUTOFFS)");
w.WriteLine(
$"#{_config.SheetWidthVariable}={_fmt.FormatCoord(width)} (SHEET WIDTH FOR CUTOFFS)"
);
w.WriteLine(
$"#{_config.SheetLengthVariable}={_fmt.FormatCoord(length)} (SHEET LENGTH FOR CUTOFFS)"
);
// 2. Coordinate setup
w.WriteLine("M42");
@@ -76,23 +90,40 @@ public sealed class CincinnatiSheetWriter
w.WriteLine("GOTO1( Goto Feature )");
// 3. Order parts: non-cutoff sorted by Bottom then Left, cutoffs last
var nonCutoffParts = plate.Parts
.Where(p => !p.BaseDrawing.IsCutOff)
var nonCutoffParts = plate
.Parts.Where(p => !p.BaseDrawing.IsCutOff)
.OrderBy(p => p.Bottom)
.ThenBy(p => p.Left)
.ToList();
var cutoffParts = plate.Parts
.Where(p => p.BaseDrawing.IsCutOff)
.ToList();
var cutoffParts = plate.Parts.Where(p => p.BaseDrawing.IsCutOff).ToList();
var allParts = nonCutoffParts.Concat(cutoffParts).ToList();
// 4. Emit parts
if (partSubprograms != null)
WritePartsWithSubprograms(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, width, length, partSubprograms, userVarMapping);
WritePartsWithSubprograms(
w,
allParts,
cutLibrary,
etchLibrary,
sheetDiagonal,
width,
length,
partSubprograms,
userVarMapping
);
else
WritePartsInline(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, width, length, userVarMapping);
WritePartsInline(
w,
allParts,
cutLibrary,
etchLibrary,
sheetDiagonal,
width,
length,
userVarMapping
);
// 5. Footer
w.WriteLine("M42");
@@ -101,11 +132,17 @@ public sealed class CincinnatiSheetWriter
w.WriteLine($"M99 (END OF {nestName}.{layoutIndex:D3})");
}
private void WritePartsWithSubprograms(TextWriter w, List<Part> allParts,
string cutLibrary, string etchLibrary, double sheetDiagonal,
double plateWidth, double plateLength,
private void WritePartsWithSubprograms(
TextWriter w,
List<Part> allParts,
string cutLibrary,
string etchLibrary,
double sheetDiagonal,
double plateWidth,
double plateLength,
Dictionary<(int, long), int> partSubprograms,
Dictionary<(int drawingId, string varName), int> userVarMapping)
Dictionary<(int drawingId, string varName), int> userVarMapping
)
{
var lastPartName = "";
var featureIndex = 0;
@@ -124,8 +161,15 @@ public sealed class CincinnatiSheetWriter
if (hasSubprogram)
{
WriteSubprogramCall(w, part, subNum, featureIndex, partName,
isSafetyHeadraise, isLastPart);
WriteSubprogramCall(
w,
part,
subNum,
featureIndex,
partName,
isSafetyHeadraise,
isLastPart
);
featureIndex++;
}
else
@@ -146,9 +190,10 @@ public sealed class CincinnatiSheetWriter
continue;
}
var featureNumber = featureIndex == 0
? _config.FeatureLineNumberStart
: 1000 + featureIndex + 1;
var featureNumber =
featureIndex == 0
? _config.FeatureLineNumberStart
: 1000 + featureIndex + 1;
var cutDistance = FeatureUtils.ComputeCutDistance(codes);
@@ -170,7 +215,7 @@ public sealed class CincinnatiSheetWriter
DrawingId = part.BaseDrawing.Id,
IsCutOff = part.BaseDrawing.IsCutOff,
PlateWidth = plateWidth,
PlateLength = plateLength
PlateLength = plateLength,
};
_featureWriter.Write(w, ctx);
@@ -182,17 +227,23 @@ public sealed class CincinnatiSheetWriter
}
}
private void WriteSubprogramCall(TextWriter w, Part part, int subNum,
int featureIndex, string partName, bool isSafetyHeadraise, bool isLastPart)
private void WriteSubprogramCall(
TextWriter w,
Part part,
int subNum,
int featureIndex,
string partName,
bool isSafetyHeadraise,
bool isLastPart
)
{
// Safety headraise before rapid to new part
if (isSafetyHeadraise && _config.SafetyHeadraiseDistance.HasValue)
w.WriteLine($"M47 P{_config.SafetyHeadraiseDistance.Value} (Safety Headraise)");
// Rapid to part position (bounding box lower-left)
var featureNumber = featureIndex == 0
? _config.FeatureLineNumberStart
: 1000 + featureIndex + 1;
var featureNumber =
featureIndex == 0 ? _config.FeatureLineNumberStart : 1000 + featureIndex + 1;
var sb = new StringBuilder();
if (_config.UseLineNumbers)
@@ -217,14 +268,20 @@ public sealed class CincinnatiSheetWriter
w.WriteLine("M47");
}
private void WriteHoleSubprogramCall(TextWriter w, SubProgramCall call, int featureIndex, bool isLastFeature)
private void WriteHoleSubprogramCall(
TextWriter w,
SubProgramCall call,
int featureIndex,
bool isLastFeature
)
{
var postSubNum = _holeSubprograms != null && _holeSubprograms.TryGetValue(call.Id, out var num)
? num : call.Id;
var postSubNum =
_holeSubprograms != null && _holeSubprograms.TryGetValue(call.Id, out var num)
? num
: call.Id;
var featureNumber = featureIndex == 0
? _config.FeatureLineNumberStart
: 1000 + featureIndex + 1;
var featureNumber =
featureIndex == 0 ? _config.FeatureLineNumberStart : 1000 + featureIndex + 1;
// Shift the local origin to the hole center via G52 (manual §1.52).
// G52 does not move the nozzle, so the sub-program's first rapid
@@ -247,10 +304,16 @@ public sealed class CincinnatiSheetWriter
w.WriteLine("M47");
}
private void WritePartsInline(TextWriter w, List<Part> allParts,
string cutLibrary, string etchLibrary, double sheetDiagonal,
double plateWidth, double plateLength,
Dictionary<(int drawingId, string varName), int> userVarMapping)
private void WritePartsInline(
TextWriter w,
List<Part> allParts,
string cutLibrary,
string etchLibrary,
double sheetDiagonal,
double plateWidth,
double plateLength,
Dictionary<(int drawingId, string varName), int> userVarMapping
)
{
// Split and classify features, ordering etch before cut per part
var features = new List<(Part part, List<ICode> codes, bool isEtch)>();
@@ -279,9 +342,7 @@ public sealed class CincinnatiSheetWriter
continue;
}
var featureNumber = i == 0
? _config.FeatureLineNumberStart
: 1000 + i + 1;
var featureNumber = i == 0 ? _config.FeatureLineNumberStart : 1000 + i + 1;
var cutDistance = FeatureUtils.ComputeCutDistance(codes);
@@ -303,12 +364,11 @@ public sealed class CincinnatiSheetWriter
DrawingId = part.BaseDrawing.Id,
IsCutOff = part.BaseDrawing.IsCutOff,
PlateWidth = plateWidth,
PlateLength = plateLength
PlateLength = plateLength,
};
_featureWriter.Write(w, ctx);
lastPartName = partName;
}
}
}
@@ -13,7 +13,8 @@ namespace OpenNest.Posts.Cincinnati
public string FormatCoord(double value)
{
return System.Math.Round(value, _accuracy)
return System
.Math.Round(value, _accuracy)
.ToString(_format, System.Globalization.CultureInfo.InvariantCulture);
}
+15 -7
View File
@@ -52,7 +52,9 @@ public static class FeatureUtils
/// <summary>
/// Classifies features as etch or cut and orders etch features before cut features.
/// </summary>
public static List<(List<ICode> codes, bool isEtch)> ClassifyAndOrder(List<List<ICode>> features)
public static List<(List<ICode> codes, bool isEtch)> ClassifyAndOrder(
List<List<ICode>> features
)
{
var result = new List<(List<ICode>, bool)>();
var etch = new List<List<ICode>>();
@@ -157,27 +159,33 @@ public static class FeatureUtils
return 0.0;
// Full circle: start ≈ end
if (Tolerance.IsEqualTo(startPos.X, arc.EndPoint.X)
&& Tolerance.IsEqualTo(startPos.Y, arc.EndPoint.Y))
if (
Tolerance.IsEqualTo(startPos.X, arc.EndPoint.X)
&& Tolerance.IsEqualTo(startPos.Y, arc.EndPoint.Y)
)
return 2.0 * System.Math.PI * radius;
var startAngle = System.Math.Atan2(
startPos.Y - arc.CenterPoint.Y,
startPos.X - arc.CenterPoint.X);
startPos.X - arc.CenterPoint.X
);
var endAngle = System.Math.Atan2(
arc.EndPoint.Y - arc.CenterPoint.Y,
arc.EndPoint.X - arc.CenterPoint.X);
arc.EndPoint.X - arc.CenterPoint.X
);
double sweep;
if (arc.Rotation == RotationType.CW)
{
sweep = startAngle - endAngle;
if (sweep <= 0) sweep += 2.0 * System.Math.PI;
if (sweep <= 0)
sweep += 2.0 * System.Math.PI;
}
else
{
sweep = endAngle - startAngle;
if (sweep <= 0) sweep += 2.0 * System.Math.PI;
if (sweep <= 0)
sweep += 2.0 * System.Math.PI;
}
return radius * sweep;
@@ -18,11 +18,13 @@ namespace OpenNest.Posts.Cincinnati
if (config?.MaterialLibraries != null)
{
names.AddRange(config.MaterialLibraries
.Select(e => e.Library)
.Where(s => !string.IsNullOrWhiteSpace(s))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase));
names.AddRange(
config
.MaterialLibraries.Select(e => e.Library)
.Where(s => !string.IsNullOrWhiteSpace(s))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase)
);
}
return new StandardValuesCollection(names);
@@ -25,9 +25,10 @@ public sealed class MaterialLibraryResolver
return EnsureLibExtension(_selectedLibrary);
var entry = _materialLibraries.FirstOrDefault(e =>
string.Equals(e.Material, materialName, StringComparison.OrdinalIgnoreCase) &&
System.Math.Abs(e.Thickness - thickness) <= ThicknessTolerance &&
string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase));
string.Equals(e.Material, materialName, StringComparison.OrdinalIgnoreCase)
&& System.Math.Abs(e.Thickness - thickness) <= ThicknessTolerance
&& string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase)
);
return EnsureLibExtension(entry?.Library ?? "");
}
@@ -35,7 +36,8 @@ public sealed class MaterialLibraryResolver
public string ResolveEtchLibrary(string gas)
{
var entry = _etchLibraries.FirstOrDefault(e =>
string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase));
string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase)
);
return EnsureLibExtension(entry?.Library ?? "");
}
+4 -2
View File
@@ -8,8 +8,10 @@ namespace OpenNest.Posts.Cincinnati
public string Classify(double contourLength, double sheetDiagonal)
{
var ratio = contourLength / sheetDiagonal;
if (ratio >= FastThreshold) return "FAST";
if (ratio <= SlowThreshold) return "SLOW";
if (ratio >= FastThreshold)
return "FAST";
if (ratio <= SlowThreshold)
return "SLOW";
return "MEDIUM";
}