style: apply CSharpier formatting to files merged from arc-tangency branch
This commit is contained in:
@@ -15,18 +15,23 @@ namespace OpenNest.Posts.GravographIS
|
||||
public int FeedMmPerSec { get; set; } = 10;
|
||||
|
||||
[DisplayName("Depth (inches)")]
|
||||
[Description("Programmed Z plunge (DZ). Note: the spring-floated spindle means this does not set actual cut depth — tool protrusion does.")]
|
||||
[Description(
|
||||
"Programmed Z plunge (DZ). Note: the spring-floated spindle means this does not set actual cut depth — tool protrusion does."
|
||||
)]
|
||||
public double Depth { get; set; } = 0.25;
|
||||
|
||||
[DisplayName("Pause Before")]
|
||||
[Description("Stop the spindle and prompt the operator before this pass begins, so the tool can be swapped/adjusted.")]
|
||||
[Description(
|
||||
"Stop the spindle and prompt the operator before this pass begins, so the tool can be swapped/adjusted."
|
||||
)]
|
||||
public bool PauseBefore { get; set; }
|
||||
|
||||
[DisplayName("Pause Message")]
|
||||
[Description("Message shown on the controller during the pause.")]
|
||||
public string PauseMessage { get; set; } = "";
|
||||
|
||||
public override string ToString() => $"{FeedMmPerSec} mm/s, {Depth:0.###}\"" + (PauseBefore ? ", pause" : "");
|
||||
public override string ToString() =>
|
||||
$"{FeedMmPerSec} mm/s, {Depth:0.###}\"" + (PauseBefore ? ", pause" : "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,24 +45,28 @@ namespace OpenNest.Posts.GravographIS
|
||||
[Category("Engrave (Scribe)")]
|
||||
[DisplayName("Engrave")]
|
||||
[Description("Parameters for engrave/scribe geometry (text).")]
|
||||
public LayerCutConfig Engrave { get; set; } = new LayerCutConfig
|
||||
{
|
||||
FeedMmPerSec = 10,
|
||||
Depth = 0.25,
|
||||
PauseBefore = false,
|
||||
PauseMessage = "",
|
||||
};
|
||||
public LayerCutConfig Engrave { get; set; } =
|
||||
new LayerCutConfig
|
||||
{
|
||||
FeedMmPerSec = 10,
|
||||
Depth = 0.25,
|
||||
PauseBefore = false,
|
||||
PauseMessage = "",
|
||||
};
|
||||
|
||||
[Category("Cut")]
|
||||
[DisplayName("Cut")]
|
||||
[Description("Parameters for cut geometry (outlines). Pauses for a tool change by default.")]
|
||||
public LayerCutConfig Cut { get; set; } = new LayerCutConfig
|
||||
{
|
||||
FeedMmPerSec = 3,
|
||||
Depth = 0.25,
|
||||
PauseBefore = true,
|
||||
PauseMessage = "Change tool",
|
||||
};
|
||||
[Description(
|
||||
"Parameters for cut geometry (outlines). Pauses for a tool change by default."
|
||||
)]
|
||||
public LayerCutConfig Cut { get; set; } =
|
||||
new LayerCutConfig
|
||||
{
|
||||
FeedMmPerSec = 3,
|
||||
Depth = 0.25,
|
||||
PauseBefore = true,
|
||||
PauseMessage = "Change tool",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cut config a polyline of the given layer should use, or null
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenNest.Posts.GravographIS
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
};
|
||||
|
||||
public string Name => "Gravograph IS8000";
|
||||
@@ -48,7 +48,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
var json = File.ReadAllText(configPath);
|
||||
Config = JsonSerializer.Deserialize<GravographISPostConfig>(json, JsonOptions)
|
||||
Config =
|
||||
JsonSerializer.Deserialize<GravographISPostConfig>(json, JsonOptions)
|
||||
?? new GravographISPostConfig();
|
||||
}
|
||||
else
|
||||
@@ -103,14 +104,16 @@ namespace OpenNest.Posts.GravographIS
|
||||
/// </summary>
|
||||
public IReadOnlyList<GravographPass> BuildPasses(IEnumerable<LayeredPolyline> polylines)
|
||||
{
|
||||
if (polylines == null) throw new ArgumentNullException(nameof(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;
|
||||
if (poly == null)
|
||||
continue;
|
||||
var block = Config.ConfigFor(poly.Layer);
|
||||
if (block == null)
|
||||
continue; // non-cutting (Display) geometry
|
||||
|
||||
@@ -48,16 +48,99 @@ namespace OpenNest.Posts.GravographIS
|
||||
// fixed return-to-home block.
|
||||
private static readonly byte[] PreambleTemplate = new byte[]
|
||||
{
|
||||
0x21, 0x41, 0x53, 0x20, 0x33, 0x38, 0x3b, 0x01, 0x90, 0x01,
|
||||
0xf4, 0x01, 0x90, 0x01, 0xf4, 0x01, 0x90, 0x01, 0xf4, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x09, 0x00, 0x00, 0x03, 0xe8, 0x05, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0x32, 0x44, 0x00,
|
||||
0x00, 0xff, 0xfd, 0x4d, 0x43, 0x00, 0x01, 0xff, 0xfd, 0x4f,
|
||||
0x55, 0xff, 0xfb, 0xff, 0xfd, 0x4f, 0x55, 0xff, 0xfa, 0xff,
|
||||
0xfd, 0x50, 0x5a, 0x00, 0x00, 0xff, 0xfd, 0x56, 0x53, 0x00,
|
||||
0x23, 0xff, 0xfd, 0x56, 0x5a, 0x00, 0x23, 0xff, 0xfd, 0x44,
|
||||
0x5a, 0x01, 0xfc,
|
||||
0x21,
|
||||
0x41,
|
||||
0x53,
|
||||
0x20,
|
||||
0x33,
|
||||
0x38,
|
||||
0x3b,
|
||||
0x01,
|
||||
0x90,
|
||||
0x01,
|
||||
0xf4,
|
||||
0x01,
|
||||
0x90,
|
||||
0x01,
|
||||
0xf4,
|
||||
0x01,
|
||||
0x90,
|
||||
0x01,
|
||||
0xf4,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0xe8,
|
||||
0x05,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x32,
|
||||
0x44,
|
||||
0x00,
|
||||
0x00,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4d,
|
||||
0x43,
|
||||
0x00,
|
||||
0x01,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4f,
|
||||
0x55,
|
||||
0xff,
|
||||
0xfb,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4f,
|
||||
0x55,
|
||||
0xff,
|
||||
0xfa,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x50,
|
||||
0x5a,
|
||||
0x00,
|
||||
0x00,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x56,
|
||||
0x53,
|
||||
0x00,
|
||||
0x23,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x56,
|
||||
0x5a,
|
||||
0x00,
|
||||
0x23,
|
||||
0xff,
|
||||
0xfd,
|
||||
0x44,
|
||||
0x5a,
|
||||
0x01,
|
||||
0xfc,
|
||||
};
|
||||
|
||||
// Stripped 36-byte postamble: lift, aux off, motor off, operator beep,
|
||||
@@ -73,11 +156,36 @@ namespace OpenNest.Posts.GravographIS
|
||||
// lift + PU travel to the operator-set origin before these final commands.
|
||||
private static readonly byte[] EndJobBytes = new byte[]
|
||||
{
|
||||
0xff, 0xfd, 0x4f, 0x55, 0xff, 0xfa, // OU 0xFFFA aux off
|
||||
0xff, 0xfd, 0x4f, 0x55, 0xff, 0xfb, // OU 0xFFFB aux off
|
||||
0xff, 0xfd, 0x4d, 0x43, 0x00, 0x00, // MC 0x0000 motor off
|
||||
0xff, 0xfd, 0x4f, 0x50, 0x00, 0x00, // OP 0x0000 operator beep
|
||||
0xff, 0xfd, 0x4a, 0x46, 0x00, 0x00, // JF 0x0000 job finish
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4f,
|
||||
0x55,
|
||||
0xff,
|
||||
0xfa, // OU 0xFFFA aux off
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4f,
|
||||
0x55,
|
||||
0xff,
|
||||
0xfb, // OU 0xFFFB aux off
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4d,
|
||||
0x43,
|
||||
0x00,
|
||||
0x00, // MC 0x0000 motor off
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4f,
|
||||
0x50,
|
||||
0x00,
|
||||
0x00, // OP 0x0000 operator beep
|
||||
0xff,
|
||||
0xfd,
|
||||
0x4a,
|
||||
0x46,
|
||||
0x00,
|
||||
0x00, // JF 0x0000 job finish
|
||||
};
|
||||
|
||||
// 80 steps/mm × 25.4 mm/in
|
||||
@@ -86,9 +194,7 @@ namespace OpenNest.Posts.GravographIS
|
||||
public GravographISWriterOptions Options { get; }
|
||||
|
||||
public GravographISWriter()
|
||||
: this(new GravographISWriterOptions())
|
||||
{
|
||||
}
|
||||
: this(new GravographISWriterOptions()) { }
|
||||
|
||||
public GravographISWriter(GravographISWriterOptions options)
|
||||
{
|
||||
@@ -103,21 +209,25 @@ namespace OpenNest.Posts.GravographIS
|
||||
/// </summary>
|
||||
public void Write(IEnumerable<IReadOnlyList<Vector>> polylines, Stream output)
|
||||
{
|
||||
if (polylines == null) throw new ArgumentNullException(nameof(polylines));
|
||||
if (polylines == null)
|
||||
throw new ArgumentNullException(nameof(polylines));
|
||||
|
||||
// A single pass at the configured feed/depth — byte-identical to the
|
||||
// original single-group output (no transitions, no pause).
|
||||
Write(new[]
|
||||
{
|
||||
new GravographPass
|
||||
Write(
|
||||
new[]
|
||||
{
|
||||
Polylines = polylines,
|
||||
FeedMmPerSec = Options.FeedMmPerSec,
|
||||
DepthInches = Options.DepthInches,
|
||||
PauseBefore = false,
|
||||
PauseMessage = "",
|
||||
new GravographPass
|
||||
{
|
||||
Polylines = polylines,
|
||||
FeedMmPerSec = Options.FeedMmPerSec,
|
||||
DepthInches = Options.DepthInches,
|
||||
PauseBefore = false,
|
||||
PauseMessage = "",
|
||||
},
|
||||
},
|
||||
}, output);
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -128,8 +238,10 @@ namespace OpenNest.Posts.GravographIS
|
||||
/// </summary>
|
||||
public void Write(IReadOnlyList<GravographPass> passes, Stream output)
|
||||
{
|
||||
if (passes == null) throw new ArgumentNullException(nameof(passes));
|
||||
if (output == null) throw new ArgumentNullException(nameof(output));
|
||||
if (passes == null)
|
||||
throw new ArgumentNullException(nameof(passes));
|
||||
if (output == null)
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
|
||||
var firstFeed = passes.Count > 0 ? passes[0].FeedMmPerSec : Options.FeedMmPerSec;
|
||||
var firstDepth = passes.Count > 0 ? passes[0].DepthInches : Options.DepthInches;
|
||||
@@ -146,10 +258,16 @@ namespace OpenNest.Posts.GravographIS
|
||||
// catch bad records before they ship to the engraver.
|
||||
var headX = 0;
|
||||
var headY = 0;
|
||||
var envelopeXSteps = (int)System.Math.Round(Options.WorkEnvelopeXMm * StepsPerMm,
|
||||
MidpointRounding.AwayFromZero);
|
||||
var envelopeYSteps = (int)System.Math.Round(Options.WorkEnvelopeYMm * StepsPerMm,
|
||||
MidpointRounding.AwayFromZero);
|
||||
var envelopeXSteps = (int)
|
||||
System.Math.Round(
|
||||
Options.WorkEnvelopeXMm * StepsPerMm,
|
||||
MidpointRounding.AwayFromZero
|
||||
);
|
||||
var envelopeYSteps = (int)
|
||||
System.Math.Round(
|
||||
Options.WorkEnvelopeYMm * StepsPerMm,
|
||||
MidpointRounding.AwayFromZero
|
||||
);
|
||||
|
||||
var firstPolyline = true;
|
||||
var polyIndex = 0;
|
||||
@@ -167,9 +285,18 @@ namespace OpenNest.Posts.GravographIS
|
||||
// Park: lift Z, then rapid (pen-up) back to the operator origin so
|
||||
// the head is clear of the work while the tool is swapped.
|
||||
WriteLiftOnly(output);
|
||||
WriteTravel(output, (byte)'P', (byte)'U',
|
||||
checked(-headX), checked(-headY),
|
||||
ref headX, ref headY, envelopeXSteps, envelopeYSteps, polyIndex);
|
||||
WriteTravel(
|
||||
output,
|
||||
(byte)'P',
|
||||
(byte)'U',
|
||||
checked(-headX),
|
||||
checked(-headY),
|
||||
ref headX,
|
||||
ref headY,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex
|
||||
);
|
||||
WritePauseCore(output, pass.PauseMessage);
|
||||
}
|
||||
|
||||
@@ -182,12 +309,18 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
if (pass.DepthInches != currentDepth)
|
||||
{
|
||||
WriteCommand(output, (byte)'D', (byte)'Z', DepthInStepsAsInt16(pass.DepthInches));
|
||||
WriteCommand(
|
||||
output,
|
||||
(byte)'D',
|
||||
(byte)'Z',
|
||||
DepthInStepsAsInt16(pass.DepthInches)
|
||||
);
|
||||
currentDepth = pass.DepthInches;
|
||||
}
|
||||
}
|
||||
|
||||
if (pass.Polylines == null) continue;
|
||||
if (pass.Polylines == null)
|
||||
continue;
|
||||
|
||||
foreach (var poly in pass.Polylines)
|
||||
{
|
||||
@@ -195,31 +328,62 @@ namespace OpenNest.Posts.GravographIS
|
||||
if (poly == null || poly.Count < 2)
|
||||
continue;
|
||||
|
||||
WritePolyline(output, poly, ref firstPolyline, ref headX, ref headY,
|
||||
envelopeXSteps, envelopeYSteps, polyIndex);
|
||||
WritePolyline(
|
||||
output,
|
||||
poly,
|
||||
ref firstPolyline,
|
||||
ref headX,
|
||||
ref headY,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLiftOnly(output);
|
||||
if (Options.ReturnToOriginAtEnd && !firstPolyline)
|
||||
{
|
||||
WriteTravel(output, (byte)'P', (byte)'U',
|
||||
checked(-headX), checked(-headY),
|
||||
ref headX, ref headY, envelopeXSteps, envelopeYSteps, polyIndex);
|
||||
WriteTravel(
|
||||
output,
|
||||
(byte)'P',
|
||||
(byte)'U',
|
||||
checked(-headX),
|
||||
checked(-headY),
|
||||
ref headX,
|
||||
ref headY,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex
|
||||
);
|
||||
}
|
||||
output.Write(EndJobBytes, 0, EndJobBytes.Length);
|
||||
}
|
||||
|
||||
private void WritePolyline(Stream output, IReadOnlyList<Vector> poly,
|
||||
ref bool firstPolyline, ref int headX, ref int headY,
|
||||
int envelopeXSteps, int envelopeYSteps, int polyIndex)
|
||||
private void WritePolyline(
|
||||
Stream output,
|
||||
IReadOnlyList<Vector> poly,
|
||||
ref bool firstPolyline,
|
||||
ref int headX,
|
||||
ref int headY,
|
||||
int envelopeXSteps,
|
||||
int envelopeYSteps,
|
||||
int polyIndex
|
||||
)
|
||||
{
|
||||
var (startX, startY) = ToWire(poly[0]);
|
||||
WriteTravel(output,
|
||||
WriteTravel(
|
||||
output,
|
||||
firstPolyline ? (byte)'D' : (byte)'P',
|
||||
firstPolyline ? (byte)'R' : (byte)'U',
|
||||
checked(startX - headX), checked(startY - headY),
|
||||
ref headX, ref headY, envelopeXSteps, envelopeYSteps, polyIndex);
|
||||
checked(startX - headX),
|
||||
checked(startY - headY),
|
||||
ref headX,
|
||||
ref headY,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex
|
||||
);
|
||||
|
||||
// PD command + single records-follow flag, then one record per segment.
|
||||
output.WriteByte(0xFF);
|
||||
@@ -236,8 +400,15 @@ namespace OpenNest.Posts.GravographIS
|
||||
var (cx, cy) = ToWire(poly[i]);
|
||||
var dx = checked(cx - prevX);
|
||||
var dy = checked(cy - prevY);
|
||||
EnsureEnvelope(headX + dx, headY + dy, envelopeXSteps, envelopeYSteps,
|
||||
polyIndex, segment: i, isTravel: false);
|
||||
EnsureEnvelope(
|
||||
headX + dx,
|
||||
headY + dy,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex,
|
||||
segment: i,
|
||||
isTravel: false
|
||||
);
|
||||
WriteRecord(output, dx, dy);
|
||||
prevX = cx;
|
||||
prevY = cy;
|
||||
@@ -267,7 +438,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
// space-padded to a whole number of packets so widths stay 2 bytes.
|
||||
private static void WriteMessagePackets(Stream s, string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message)) return;
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var chars = Encoding.ASCII.GetBytes(message);
|
||||
for (var i = 0; i < chars.Length; i += 2)
|
||||
@@ -295,11 +467,18 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
private const double StepsPerMm = 80.0;
|
||||
|
||||
private void EnsureEnvelope(int wireX, int wireY,
|
||||
int envXSteps, int envYSteps,
|
||||
int polyIndex, int segment, bool isTravel)
|
||||
private void EnsureEnvelope(
|
||||
int wireX,
|
||||
int wireY,
|
||||
int envXSteps,
|
||||
int envYSteps,
|
||||
int polyIndex,
|
||||
int segment,
|
||||
bool isTravel
|
||||
)
|
||||
{
|
||||
if (!Options.EnvelopeGuardEnabled) return;
|
||||
if (!Options.EnvelopeGuardEnabled)
|
||||
return;
|
||||
|
||||
// Wire frame: X is identity to input; Y is negated. With the operator
|
||||
// origin set at the upper-left of the work envelope and an OpenNest
|
||||
@@ -313,16 +492,21 @@ namespace OpenNest.Posts.GravographIS
|
||||
var inputY = -wireY / (double)StepsPerInch;
|
||||
var kind = isTravel ? "pen-up travel" : "cut segment";
|
||||
throw new InvalidOperationException(
|
||||
$"Polyline {polyIndex} {kind} (segment {segment}) would place the head at " +
|
||||
$"({inputX:F3}\", {inputY:F3}\"), outside the {Options.WorkEnvelopeXMm}×{Options.WorkEnvelopeYMm} mm " +
|
||||
$"work envelope from upper-left origin. Refusing to emit the record.");
|
||||
$"Polyline {polyIndex} {kind} (segment {segment}) would place the head at "
|
||||
+ $"({inputX:F3}\", {inputY:F3}\"), outside the {Options.WorkEnvelopeXMm}×{Options.WorkEnvelopeYMm} mm "
|
||||
+ $"work envelope from upper-left origin. Refusing to emit the record."
|
||||
);
|
||||
}
|
||||
|
||||
private static short DepthInStepsAsInt16(double depthInches)
|
||||
{
|
||||
var steps = (long)System.Math.Round(depthInches * StepsPerInch, MidpointRounding.AwayFromZero);
|
||||
var steps = (long)
|
||||
System.Math.Round(depthInches * StepsPerInch, MidpointRounding.AwayFromZero);
|
||||
if (steps < short.MinValue || steps > short.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(depthInches), $"Depth {depthInches} in. → {steps} steps overflows int16.");
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(depthInches),
|
||||
$"Depth {depthInches} in. → {steps} steps overflows int16."
|
||||
);
|
||||
return (short)steps;
|
||||
}
|
||||
|
||||
@@ -335,10 +519,18 @@ namespace OpenNest.Posts.GravographIS
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
private void WriteTravel(Stream s, byte c0, byte c1, int dx, int dy,
|
||||
ref int headX, ref int headY,
|
||||
int envelopeXSteps, int envelopeYSteps,
|
||||
int polyIndex)
|
||||
private void WriteTravel(
|
||||
Stream s,
|
||||
byte c0,
|
||||
byte c1,
|
||||
int dx,
|
||||
int dy,
|
||||
ref int headX,
|
||||
ref int headY,
|
||||
int envelopeXSteps,
|
||||
int envelopeYSteps,
|
||||
int polyIndex
|
||||
)
|
||||
{
|
||||
if (dx == 0 && dy == 0)
|
||||
return;
|
||||
@@ -352,20 +544,31 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
var chunks = System.Math.Max(
|
||||
(int)System.Math.Ceiling(System.Math.Abs(dx) / (double)short.MaxValue),
|
||||
(int)System.Math.Ceiling(System.Math.Abs(dy) / (double)short.MaxValue));
|
||||
if (chunks < 1) chunks = 1;
|
||||
(int)System.Math.Ceiling(System.Math.Abs(dy) / (double)short.MaxValue)
|
||||
);
|
||||
if (chunks < 1)
|
||||
chunks = 1;
|
||||
|
||||
var emittedX = 0;
|
||||
var emittedY = 0;
|
||||
for (var i = 1; i <= chunks; i++)
|
||||
{
|
||||
var targetX = (int)System.Math.Round(dx * (i / (double)chunks), MidpointRounding.AwayFromZero);
|
||||
var targetY = (int)System.Math.Round(dy * (i / (double)chunks), MidpointRounding.AwayFromZero);
|
||||
var targetX = (int)
|
||||
System.Math.Round(dx * (i / (double)chunks), MidpointRounding.AwayFromZero);
|
||||
var targetY = (int)
|
||||
System.Math.Round(dy * (i / (double)chunks), MidpointRounding.AwayFromZero);
|
||||
var chunkX = checked(targetX - emittedX);
|
||||
var chunkY = checked(targetY - emittedY);
|
||||
|
||||
EnsureEnvelope(headX + chunkX, headY + chunkY, envelopeXSteps, envelopeYSteps,
|
||||
polyIndex, segment: 0, isTravel: true);
|
||||
EnsureEnvelope(
|
||||
headX + chunkX,
|
||||
headY + chunkY,
|
||||
envelopeXSteps,
|
||||
envelopeYSteps,
|
||||
polyIndex,
|
||||
segment: 0,
|
||||
isTravel: true
|
||||
);
|
||||
WriteRecord(s, chunkX, chunkY);
|
||||
|
||||
emittedX = targetX;
|
||||
@@ -399,11 +602,16 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
private static void WriteRecord(Stream s, int dx, int dy)
|
||||
{
|
||||
if (dx < short.MinValue || dx > short.MaxValue ||
|
||||
dy < short.MinValue || dy > short.MaxValue)
|
||||
if (
|
||||
dx < short.MinValue
|
||||
|| dx > short.MaxValue
|
||||
|| dy < short.MinValue
|
||||
|| dy > short.MaxValue
|
||||
)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Move delta ({dx}, {dy}) steps overflows signed int16 — split moves upstream.");
|
||||
$"Move delta ({dx}, {dy}) steps overflows signed int16 — split moves upstream."
|
||||
);
|
||||
}
|
||||
|
||||
int word1;
|
||||
@@ -423,11 +631,15 @@ namespace OpenNest.Posts.GravographIS
|
||||
else
|
||||
{
|
||||
var maxAbs = System.Math.Max(absDx, absDy);
|
||||
word1 = (int)System.Math.Round(16384.0 * maxAbs / len, MidpointRounding.AwayFromZero);
|
||||
word1 = (int)
|
||||
System.Math.Round(16384.0 * maxAbs / len, MidpointRounding.AwayFromZero);
|
||||
param = (int)System.Math.Round(len / 22.4, MidpointRounding.AwayFromZero);
|
||||
if (param < 1) param = 1;
|
||||
if (param > 180) param = 180;
|
||||
if (word1 > 16384) word1 = 16384;
|
||||
if (param < 1)
|
||||
param = 1;
|
||||
if (param > 180)
|
||||
param = 180;
|
||||
if (word1 > 16384)
|
||||
word1 = 16384;
|
||||
}
|
||||
|
||||
WriteBigEndianInt16(s, (short)word1);
|
||||
@@ -448,8 +660,12 @@ namespace OpenNest.Posts.GravographIS
|
||||
{
|
||||
for (int i = 0; i <= buffer.Length - 6; i++)
|
||||
{
|
||||
if (buffer[i] == 0xFF && buffer[i + 1] == 0xFD &&
|
||||
buffer[i + 2] == c0 && buffer[i + 3] == c1)
|
||||
if (
|
||||
buffer[i] == 0xFF
|
||||
&& buffer[i + 1] == 0xFD
|
||||
&& buffer[i + 2] == c0
|
||||
&& buffer[i + 3] == c1
|
||||
)
|
||||
{
|
||||
buffer[i + 4] = (byte)((value >> 8) & 0xFF);
|
||||
buffer[i + 5] = (byte)(value & 0xFF);
|
||||
@@ -458,7 +674,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Command '{(char)c0}{(char)c1}' not found in preamble template.");
|
||||
$"Command '{(char)c0}{(char)c1}' not found in preamble template."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
/// </summary>
|
||||
public List<LayeredPolyline> ExtractLayered(Nest nest)
|
||||
{
|
||||
if (nest == null) throw new ArgumentNullException(nameof(nest));
|
||||
if (nest == null)
|
||||
throw new ArgumentNullException(nameof(nest));
|
||||
|
||||
var result = new List<LayeredPolyline>();
|
||||
|
||||
@@ -91,7 +92,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
private void ExtractPart(Part part, List<LayeredPolyline> sink)
|
||||
{
|
||||
var program = part.Program;
|
||||
if (program == null) return;
|
||||
if (program == null)
|
||||
return;
|
||||
|
||||
// The walk below treats Motion.EndPoint as absolute. Convert a working
|
||||
// copy to absolute mode so G91 programs (the form OpenNest's UI writes)
|
||||
@@ -123,7 +125,13 @@ namespace OpenNest.Posts.GravographIS
|
||||
|
||||
case LinearMove linear:
|
||||
{
|
||||
StartOrSplit(sink, ref current, ref currentLayer, linear.Layer, pos + offset);
|
||||
StartOrSplit(
|
||||
sink,
|
||||
ref current,
|
||||
ref currentLayer,
|
||||
linear.Layer,
|
||||
pos + offset
|
||||
);
|
||||
var end = linear.EndPoint;
|
||||
current.Add(end + offset);
|
||||
pos = end;
|
||||
@@ -147,8 +155,13 @@ namespace OpenNest.Posts.GravographIS
|
||||
// seeded at `seed` (the current pen position). When the layer changes
|
||||
// mid-chain the previous polyline is flushed and a new one begins at the
|
||||
// shared seam vertex so engrave and cut passes stay geometrically continuous.
|
||||
private static void StartOrSplit(List<LayeredPolyline> sink, ref List<Vector> current,
|
||||
ref LayerType currentLayer, LayerType moveLayer, Vector seed)
|
||||
private static void StartOrSplit(
|
||||
List<LayeredPolyline> sink,
|
||||
ref List<Vector> current,
|
||||
ref LayerType currentLayer,
|
||||
LayerType moveLayer,
|
||||
Vector seed
|
||||
)
|
||||
{
|
||||
if (current == null)
|
||||
{
|
||||
@@ -163,7 +176,11 @@ namespace OpenNest.Posts.GravographIS
|
||||
}
|
||||
}
|
||||
|
||||
private static void FlushCurrent(List<LayeredPolyline> sink, ref List<Vector> current, LayerType layer)
|
||||
private static void FlushCurrent(
|
||||
List<LayeredPolyline> sink,
|
||||
ref List<Vector> current,
|
||||
LayerType layer
|
||||
)
|
||||
{
|
||||
if (current != null && current.Count >= 2)
|
||||
sink.Add(new LayeredPolyline(current, layer));
|
||||
@@ -175,8 +192,13 @@ namespace OpenNest.Posts.GravographIS
|
||||
// (G-code I/J in this codebase are stored as the absolute center), arc.EndPoint
|
||||
// is absolute end. The starting point is assumed to already be in the polyline;
|
||||
// intermediate samples and the endpoint are appended.
|
||||
private static void TessellateArc(Vector start, ArcMove arc, Vector offset,
|
||||
double chordTol, List<Vector> sink)
|
||||
private static void TessellateArc(
|
||||
Vector start,
|
||||
ArcMove arc,
|
||||
Vector offset,
|
||||
double chordTol,
|
||||
List<Vector> sink
|
||||
)
|
||||
{
|
||||
var c = arc.CenterPoint;
|
||||
var r = c.DistanceTo(start);
|
||||
@@ -193,18 +215,22 @@ namespace OpenNest.Posts.GravographIS
|
||||
if (arc.Rotation == RotationType.CW)
|
||||
{
|
||||
sweep = a0 - a1;
|
||||
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
||||
if (sweep <= 0)
|
||||
sweep += 2 * System.Math.PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
sweep = a1 - a0;
|
||||
if (sweep <= 0) sweep += 2 * System.Math.PI;
|
||||
if (sweep <= 0)
|
||||
sweep += 2 * System.Math.PI;
|
||||
}
|
||||
|
||||
// Treat a near-zero sweep with coincident start/end as a full circle.
|
||||
if (sweep < 1e-9 &&
|
||||
System.Math.Abs(start.X - arc.EndPoint.X) < 1e-9 &&
|
||||
System.Math.Abs(start.Y - arc.EndPoint.Y) < 1e-9)
|
||||
if (
|
||||
sweep < 1e-9
|
||||
&& System.Math.Abs(start.X - arc.EndPoint.X) < 1e-9
|
||||
&& System.Math.Abs(start.Y - arc.EndPoint.Y) < 1e-9
|
||||
)
|
||||
{
|
||||
sweep = 2 * System.Math.PI;
|
||||
}
|
||||
@@ -215,7 +241,8 @@ namespace OpenNest.Posts.GravographIS
|
||||
maxAngleStep = System.Math.PI / 32;
|
||||
|
||||
var steps = (int)System.Math.Ceiling(sweep / maxAngleStep);
|
||||
if (steps < 1) steps = 1;
|
||||
if (steps < 1)
|
||||
steps = 1;
|
||||
|
||||
var direction = arc.Rotation == RotationType.CW ? -1.0 : 1.0;
|
||||
for (int i = 1; i < steps; i++)
|
||||
|
||||
Reference in New Issue
Block a user