Table of Contents

Results that change on upgrade

None of the following breaks a build. All of them change what a simulation produces, so a byte-for-byte comparison against 3.1.175 output will differ — usually because 3.1.175 was wrong.

Silent wrong geometry, now fixed. Each of these produced a plausible-looking simulation of the wrong thing.

  • The kinematic pivot anchor. The pivot-transform chain entry was built as K(0)·K(abc)⁻¹, folding the whole machine-zero forward kinematic — its linear part included — into the pre-pivot anchor. That linear part encodes each axis' motion sense and tool/workpiece-side ownership, so it mirrored the program components of every workpiece-side linear axis before the IK ran. On a table-side chain whose machine-zero linear part is diag(1,1,-1), every program Z was mirrored and a near-180° swing amplified it into metres of machine-Z error, floating the toolpath above the workpiece. The anchor is now the translation alone, matching what HardNc has always kept. Machines whose linear axes all ride the tool side are unaffected.
  • G68 2D coordinate rotation was a silent no-op. TiltTransformUtil judged the active mode from the current block but always took the matrix from the previous one, so every G68 activation past the first block had its freshly authored rotation overwritten with an identity that then propagated. G68 rotation did nothing at all while the block's term still read G68, and the simulation machined the unrotated pattern. Re-running an existing G68 program now gives different — correct — geometry.
  • A blank line reset G90/G91. A piece with no parsing section — a blank line, a comment-only line, %, an O-number — left the next block's single-step lookback empty, and it fell back to the G90 default. A program that was incremental throughout silently flipped to absolute mid-file. Such pieces now carry the positioning section forward like every other modal syntax.
  • A Siemens D offset with no $TC_DP row resolved to zero length, in silence. A whole TRAORI program machined one tool length low. The read point now falls back to the generic tool-number-keyed height and emits SiemensToolOffset--TcdpRowMissing.
  • G43.4 with an unresolvable H word activated RTCP with a zero-length tool. It now reports Comp-ToolHeight--001 as a warning and keeps processing the block. An omitted H is no longer a silent zero: it takes the equipped tool's own offset row first (HardNc parity), then a non-zero modal id, and only then warns Comp-ToolHeight--NoToolForOmittedH (plain G43) or Comp-ToolHeight--NoToolForOmittedHRtcp (G43.4). A vacant table row warns Comp-ToolHeight--RowMissing instead of compensating a zero length.
  • G43.4 survived M02 / M30 and a rotary-only G53 still coupled through RTCP. A sequence that ended one program with M02 and started the next with a rotary rapid — without a G49 — kept tool-centre-point control on, pinned the tip, and dragged machine XYZ through the stock. On SoftNc the program-end block is a reset edge: tool-length compensation (G43 / G44 / G43.4, Siemens TRAORI, Heidenhain M128), tilt (G68 / G68.2 / CYCLE800 / PLANE / TRANS), G41/G42 and G81–G89 cancel on the next block, written as explicit cancel sections so modal carry cannot copy the previous state. The M02 block itself still runs under the modal state it executed with, so a retract on the same line as M30 still carries tool length. A rotary-only G53 under active RTCP now holds the linear axes at the previous machine position and stays McLinear, which is what G53 does on a control (no compensation). HardNc still carries G43.4 across M02 — the two engines are deliberately divergent on that edge; 3.2's default is SoftNc. A tool change that leaves G43.4 Hm alive for a different tool warns Comp-ToolHeight--RtcpOutlivesToolChange on the change block.
  • Fanuc G54.1 Pn (also written G54 Pn) was not resolved. Additional work coordinate systems now resolve against the extended table; a selected row nobody has entered reports Coord-WorkOffset--AdditionalZero and runs on the machine origin. G59.1G59.9 have a provider on the ISO presets. See Work Coordinates.
  • Heidenhain DIN/ISO arc centres. I / J / K are absolute circle centres on Heidenhain — the ISO face of the klartext CC pole — not start-to-centre offsets. Reading them incrementally turned arcs into near-full phantom circles. Fixed in all three engines: the HardNc reader (IsIjkAbsolute), the SoftNc reader (IsIjkAbsolute) and the optimizer's write-back. G91 is the exception: an incremental block switches the words back to start-to-centre offsets, so an arc programmed under G91 is unaffected by this change. Also delivered on the 3.1.175.x service line.
  • Feed per tooth, MRR and cutting forces follow the equipped tool's tip, not the controller's F word. The physics used to read the commanded CL feedrate. It now reads the step's real tip feedrate (ActualTipFeedrate_mmds, client key ActualTipFeedrate_mmdmin): the tip's displacement relative to the workpiece over the step duration. On XYZ moves, CL files and RTCP with the equipped tool's own offset the two agree (the recomputation moves forces by at most about 1e-4 relative, from the TimeSpan-quantised step duration); they part under RTCP with a tool-length offset that does not describe the equipped tool, on rotary-axis-limited simultaneous five-axis blocks (the tip is slower than F, up to a few percent), and wherever the commanded value was stale.
  • A feed block that does not move lost its F word. A block that repeats the current position (X.. Y.. Z.. F1600. right after the same point at F800., a common CAM habit) returned on zero distance before emitting anything, so every following block showed and cut at the old feed. Its ActFeedrate now lands on a zero-length linear, arc or polar block alike. The same stale commanded value is one of the cases the tip-feedrate split above parts on.
  • A rotary-bearing act mixed tip travel with the commanded CL feed resolution. On a pure RTCP rapid swing the CL point stands still, the commanded feed is ~0, and dividing the equipped tool's tip sweep by that resolution saturated the step count (the play appeared to hang on one line). A rotary-bearing act now splits by posture change (each rotary-axis delta over the rotary resolution) and by spindle cycles when the spindle turns — never by tip travel over the CL-feed resolution. Step counts of existing five-axis projects therefore shift on non-RTCP rotary blocks; XYZ-only programs are unaffected.
  • A K0 word on a HardNc G02/G03 saturated the turn count. Under the default G17 plane a written-but-zero plane-normal word divided the axial travel by zero, the additional-turn count saturated to int.MaxValue, and one arc block became a spiral act of roughly 302 simulated years — the play appeared to hang on a single NC line. The reading now falls back to the closed-circle rule for a zero pace, matching the guard the SoftNc side already had.
  • Indexed-rotary programs folded the pivot into plain moves. A block with no active G68.2 or G43.4 no longer folds the kinematic pivot transform into a plain XYZ move, which is what a real controller does with a table-side rotary program.
  • The radius-compensation arc transient cache landed on the wrong block. Any non-motion line between the corner and the arc orphaned the cache, so the arc lost its leading linear bridge and began at the corner intersection instead of on the offset arc.
  • Cutter radius compensation on G18 / G19 ran out of the plane. Both engines built the perpendicular as Z × axis whatever plane was selected, so under G18 or G19 the whole offset went one radius along the plane normal (a G41 region in the ZX plane was displaced in Y), every arc of the region reported Arc-EndpointOffRadius, and the RadiusComp--Interference gate could not fire there. Both engines now take the perpendicular, the in-plane test for blocks without movement and the interference gate from the selected plane: the offset lies in the plane, left/right is judged looking down the plane's positive normal (the same convention that orients G02/G03), and the diagnostics behave as on G17. G17 programs are unaffected.
  • The G68.2 tool-axis IK fallback probed the mirrored tool axis. Both normal-only fallbacks in IsoG68p2TiltSyntax read the transposed third column instead of the third row. On the no-hint path this only skewed a warning gate, but the explicit A/B/C path seeds its hint blend from that solve, so a machine with fewer than three rotary axes composed a mirrored tilted plane.
  • A Fanuc WHILE forward jump bound to the wrong END. Sequential loops idiomatically reuse DO 1, so a second WHILE's falsy-condition exit bound to the first loop's END 1 and redirected execution to a point before the second WHILE — an unbounded loop the iteration watchdog cannot see, because it only ticks on END reverse jumps. The jump now uses the anchored label scan the Siemens loop family already used.
  • Heidenhain G28 is MIRROR IMAGE, not a reference-point return. On the Heidenhain preset the shared pipeline had been reading it as the Fanuc reference return, so a G28 X block minted a phantom rapid to home while the mirror silently vanished. It is now simulated as a program-to-MC transform, ReferenceReturnSyntax leaves the Heidenhain logic list, and klartext CYCL DEF 8 records the same mirror statement so one program mirrors identically in either dialect. HardNc keeps the Fanuc reading, so the two engines are deliberately divergent on Heidenhain G28 files and any parity comparison must account for it.
  • MathUtil.Convert_inchdmin_To_mmds returned mm/min, not mm/s. The body multiplied by 25.4 and never divided by 60, so a caller trusting the name got a 60× feedrate; the sibling Convert_mmdmin_To_mmds divides as its name demands. No shipped path called it — the CL/APT feedrate route converts inches-per-minute to mm/min itself — so this changes nothing inside the product, but a caller who had compensated for the old behaviour must remove that compensation.
  • GetRByZ(List<PairZr>) never interpolated. The list overload looked up its ceiling node with the floor lookup, so floor equalled ceiling and the method degenerated into a step function. For a sharp cone whose inner-beam Z–R list has no node between apex and rim, that pinned the inner radius to 0 across the whole cone face and produced NaN flute vertices — the root cause of the transparent cone-tip flute that 3.1.180 addressed at the display layer. It affects every consumer of radius-by-Z interpolation, force geometry included.

Numerical results move even where nothing was renamed.

  • Five-axis IK is roughly 1000× tighter. The rotary solver behind XyzabcSolver now runs coarse→polish: the coarse stage keeps the original dot residual so solve success and failure semantics are unchanged, then a [dot, cross, rr-per-axis] system polishes. The dot criterion is quadratically blind to angle error — an envelope of about 1.4e-3 rad, and the resulting tip error is that envelope times the tool length — so adding the cross term restores quadratic convergence and drops the envelope to about 1e-6 rad. Failed solves no longer pollute the implicit seed, and the measure-zero perfect-saddle case is escaped by a deterministic retry offset instead of by leftover seed pollution.
  • ActMcXyzLinearContour steps by euclidean tip travel instead of the largest per-axis component, so LinearResolution_mm now caps actual tool-tip travel per step and a diagonal move produces up to √3× more steps at the same setting.
  • The SoftNc pipeline is the default engine.
  • Executed SyntaxPieces freeze to UTF-8 (below), so their JsonObject is a fresh read-only snapshot per call rather than a retained live graph.
  • SyntaxPiece.SentenceIndex becomes a session-global execution-order counter and is no longer contiguous per file.
  • Repeated NC diagnostics fold into per-run summaries at the run boundaries.
  • HardNc tool changes fire on M06 / Heidenhain TOOL CALL rather than on a changed T word — a bare T is magazine pre-selection, and a same-tool M06 still runs the changer cycle — and unset HardNcEnv tooling defaults become the three-axis shape (XYZ = NaN, NaN, 0; ABC all NaN). The old defaults swung all three rotary axes home on every M06, which no post expects.