fix: improve arc-tangency fitting and add layered engrave/cut passes for GravographIS
GeometrySimplifier/ArcFit now fit arcs that pass exactly through run endpoints while balancing tangency error between trusted and estimated directions, fixing arcs that previously bulged or broke tangent continuity at fillet/compound-curve junctions. GravographIS post processor gains per-layer (engrave/cut) tool passes via a new GravographISPostConfig, so ENGRAVE/ETCH-tagged geometry runs as a separate scribe pass with its own feed/depth and an operator pause before the cut pass (spring-floated spindle needs a tool swap). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Posts.GravographIS
|
||||
{
|
||||
/// <summary>
|
||||
/// IPostProcessor implementation for the Gravograph IS8000. <see cref="Post(Nest, Stream)"/>
|
||||
/// writes the binary HPGL bytes. For serial streaming, use <see cref="Stream(Nest, string, Handshake, CancellationToken)"/>.
|
||||
///
|
||||
/// Geometry is split by <see cref="OpenNest.CNC.LayerType"/> into an engrave pass
|
||||
/// (Scribe) and a cut pass (Cut), each with its own feed/depth from <see cref="Config"/>.
|
||||
/// The cut pass pauses by default so the operator can swap/adjust the tool.
|
||||
/// </summary>
|
||||
public sealed class GravographISPostProcessor : IPostProcessor
|
||||
public sealed class GravographISPostProcessor : IConfigurablePostProcessor
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
};
|
||||
|
||||
public string Name => "Gravograph IS8000";
|
||||
public string Author => "OpenNest";
|
||||
public string Description => "Gravograph IS8000 mechanical engraver (binary HPGL over serial)";
|
||||
@@ -23,14 +37,53 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
public bool AllowReverse { get; set; } = true;
|
||||
|
||||
public GravographISPostConfig Config { get; }
|
||||
|
||||
object IConfigurablePostProcessor.Config => Config;
|
||||
|
||||
public GravographISPostProcessor()
|
||||
{
|
||||
var configPath = GetConfigPath();
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
var json = File.ReadAllText(configPath);
|
||||
Config = JsonSerializer.Deserialize<GravographISPostConfig>(json, JsonOptions)
|
||||
?? new GravographISPostConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
Config = new GravographISPostConfig();
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public GravographISPostProcessor(GravographISPostConfig config)
|
||||
{
|
||||
Config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
public void SaveConfig()
|
||||
{
|
||||
var configPath = GetConfigPath();
|
||||
var json = JsonSerializer.Serialize(Config, JsonOptions);
|
||||
File.WriteAllText(configPath, json);
|
||||
}
|
||||
|
||||
private static string GetConfigPath()
|
||||
{
|
||||
var assemblyPath = typeof(GravographISPostProcessor).Assembly.Location;
|
||||
var dir = Path.GetDirectoryName(assemblyPath);
|
||||
var name = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
return Path.Combine(dir, name + ".json");
|
||||
}
|
||||
|
||||
public void Post(Nest nest, Stream outputStream)
|
||||
{
|
||||
if (nest == null) throw new ArgumentNullException(nameof(nest));
|
||||
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
|
||||
|
||||
var polylines = Extractor.Extract(nest);
|
||||
var prepared = PolylinePrePass.Prepare(polylines, StitchTolerance, AllowReverse);
|
||||
new GravographISWriter(WriterOptions).Write(prepared, outputStream);
|
||||
var passes = BuildPasses(Extractor.ExtractLayered(nest));
|
||||
new GravographISWriter(WriterOptions).Write(passes, outputStream);
|
||||
}
|
||||
|
||||
public void Post(Nest nest, string outputFile)
|
||||
@@ -39,6 +92,52 @@ namespace OpenNest.Posts.GravographIS
|
||||
Post(nest, fs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Groups layer-tagged polylines into ordered tool passes: engrave (Scribe)
|
||||
/// first, then cut. Each group is stitch/reverse-optimized independently.
|
||||
/// Geometry whose layer maps to no config (Display) is skipped. When only one
|
||||
/// group is present, a single pass is returned (and so the writer emits no pause).
|
||||
/// </summary>
|
||||
public IReadOnlyList<GravographPass> BuildPasses(IEnumerable<LayeredPolyline> polylines)
|
||||
{
|
||||
if (polylines == null) throw new ArgumentNullException(nameof(polylines));
|
||||
|
||||
var engrave = new List<IReadOnlyList<Vector>>();
|
||||
var cut = new List<IReadOnlyList<Vector>>();
|
||||
|
||||
foreach (var poly in polylines)
|
||||
{
|
||||
if (poly == null) continue;
|
||||
var block = Config.ConfigFor(poly.Layer);
|
||||
if (block == null)
|
||||
continue; // non-cutting (Display) geometry
|
||||
if (ReferenceEquals(block, Config.Engrave))
|
||||
engrave.Add(poly.Points);
|
||||
else
|
||||
cut.Add(poly.Points);
|
||||
}
|
||||
|
||||
var passes = new List<GravographPass>();
|
||||
if (engrave.Count > 0)
|
||||
passes.Add(MakePass(Config.Engrave, engrave));
|
||||
if (cut.Count > 0)
|
||||
passes.Add(MakePass(Config.Cut, cut));
|
||||
|
||||
return passes;
|
||||
}
|
||||
|
||||
private GravographPass MakePass(LayerCutConfig block, List<IReadOnlyList<Vector>> polylines)
|
||||
{
|
||||
return new GravographPass
|
||||
{
|
||||
Polylines = PolylinePrePass.Prepare(polylines, StitchTolerance, AllowReverse),
|
||||
FeedMmPerSec = block.FeedMmPerSec,
|
||||
DepthInches = block.Depth,
|
||||
PauseBefore = block.PauseBefore,
|
||||
PauseMessage = block.PauseMessage ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buffers the encoded job in memory, then streams it to the named COM port.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user