mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 14:51:06 +00:00
Both change emitted G-code, which is why they were kept out of the extraction commit. Both are wrong only where the machine mapping is non-identity, which is the definition of each bug. 1. Suppress lifts commanded through an unknown position. _travel_to_z() emits full XYZ whenever the mapping must emit every axis, because the mapping can make machine Z depend on logical X/Y, and it builds that point from m_pos. At print start, and after any custom G-code that invalidates position, m_pos.xy is the uninitialised origin; mapping (0, 0, z) through a non-identity remap produces a real but wrong machine point -- for a reverse mapping, build_vol_max, i.e. the far corner of the bed. The subsequent full-XYZ move corrects the position, but the lift has already commanded a rapid across the whole bed at travel speed. Belt kinematics already guarded this; the Cartesian path did not. The guard is now applied at all three lift sites through must_skip_lift_now(), not just the one the extraction covered: travel_to_xyz()'s pending-lift branch, lazy_lift(spiral_vase=true), and eager_lift(). The latter two also needed the state fix -- both recorded m_lifted = target_lift regardless, so suppressing only the emission would leave a later unlift() descending from a height that was never commanded. 2. Never emit a G2/G3 arc a mapping cannot represent. extrude_arc_to_xy() emitted G2/G3 with logical X/Y and I/J and never consulted the mapping. There is no general fix by transforming the arc: a permutation moves it out of the XY plane that I/J describes, a negation reverses handedness, and the belt shear maps a circle to an ellipse that G2/G3 cannot express at all. So supports_arc_moves() gates generation through the existing GCode::should_disable_arc_fitting() hook, and BeltGCode's special-case override is deleted -- belt now gets the same behaviour from the general rule instead of its own exception. supports_arc_moves() is m_remap_x == 0 && m_remap_y == 1, not !has_axis_remap(): an arc emits only X/Y/I/J, so a mapping that merely negates or reverses Z leaves every emitted word untouched and keeps its arcs. The fallback for an unrepresentable arc tessellates it into linear segments at a 0.005mm chord tolerance rather than substituting a single chord, and splits dE proportionally across the segments. The capability check is hoisted above every extrusion mutation: an earlier form ran it after filament()->extrude(dE) and so extruded 2*dE on the fallback path. Known limits of that fallback, since it is worth stating rather than discovering: emitted relative E is conserved only to per-segment rounding (a radius-5 semicircle with dE=1.5 emits 1.50012 across 36 segments); the 0.005mm bound is a logical-frame bound, about 0.00855mm in machine space under a 45-degree belt shear; unequal endpoint radii and non-finite inputs are unchecked. Ordinary export takes the original polyline when the mapping rejects arcs, so this path is a fallback rather than the normal route. Known gap, not claimed fixed: classic wipe towers have their own enable_arc_fitting and their own G2/G3 emitter in GCode/WipeTower.cpp, which should_disable_arc_fitting() does not govern. Belt printers are barred from classic wipe towers; a remapped Cartesian printer is not. Tests in tests/fff_print/test_gcodewriter.cpp: reverse-X remap with unknown and with known position plus an identity control; eager_lift emitting nothing and recording nothing; the arc-capability matrix including the Z-only cases; and the tessellated fallback. E accounting is asserted through used_filament() rather than E(), which resets per line in relative-E mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011jgzj1sf53KMLPweZ8yeUQ