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 absent H stays silent — re-activating G43.4 on the modal offset id is legitimate input.
  • 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. One such stale case is fixed alongside: a feed block that repeats the current position (X.. Y.. Z.. F1600. right after the same point at F800.) lost its F word, so every following block showed and cut at the old feed; its ActFeedrate now lands.
  • 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.
  • 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.