Skip to content

Derive per-path line height and extrusion width for Cura gcode - #459

Closed
sophiedeziel wants to merge 4 commits into
interpreter-line-width-heightfrom
cura-derived-dimensions
Closed

sophiedeziel wants to merge 4 commits into
interpreter-line-width-heightfrom
cura-derived-dimensions

Conversation

@sophiedeziel

@sophiedeziel sophiedeziel commented Sep 7, 2026 •

Copy link
Copy Markdown
Collaborator

Derives per-path line height and extrusion width for Cura-sliced files, which announce no ;WIDTH:/;HEIGHT: comments — heights come from the Z steps between extrusion moves, widths from conservation of volume (ΔE × filament cross-section / (length × height), rectangular deposit matching what ExtrusionGeometry renders).

Stacked on #458.

Five first-layer lines printed at 1.2 / 0.9 / 0.6 / 0.4 / 0.25 mm, same camera in both shots:

Before: every line the same 0.6 mm After: derived widths
Five parallel first-layer lines, all rendered at an identical 0.6 mm width because the file announces no dimensions The same five lines with derived widths, tapering from a fat 1.2 mm bead at the bottom to a hairline 0.25 mm one at the top

Heights come out of the Z steps, so the tower's three tiers stack visibly differently — 0.3 mm at the bottom, 0.2 mm in the middle, 0.1 mm on top:

Close-up of the tower wall: four fat beads in the bottom tier, eight medium ones in the middle, about twenty hairline beads in the top tier

Design decisions:

  • Derivation only activates when the detected slicer's dialect has no dimension comments (Cura, minus the volumetric UltiGCode flavor); Prusa-family files keep their exact comment values, unrecognized files render exactly as before.
  • Derived values count as the path's own dimensions, so they beat the global fallback with the same precedence and path-breaking behavior as comment-announced ones.
  • New M82/M83 handlers plus State.trackE give real absolute/relative E accounting (closes Support M82/M83 (absolute/relative extrusion mode) #419); z-hops, ironing, spiralize, G92 E resets and adaptive layer heights are all handled, with implausible results clamped away.
  • Filament diameter comes from the header ;FLAVOR: line (Griffin = Ultimaker 2.85 mm, else 1.75 mm). Cura's material_diameter sits in the end-of-file ;SETTING_3 blob — using it would make streamed and one-shot parses disagree, so it is deliberately not read.
  • Derived widths are quantized to 0.01 mm with a 2% tolerance so E-rounding noise cannot shatter the model into micro-paths; streamed vs one-shot equivalence is covered by a new Cura fixture in the ingestion harness, and synthetic round-trip tests recover programmed dimensions through the inverted volumetric formula.

Assisted by Claude Code - Fable 5

@sophiedeziel sophiedeziel added enhancement Improvement to an existing feature parser Related to G-code parsing labels Sep 7, 2026
sophiedeziel added a commit that referenced this pull request Sep 7, 2026
- State.trackE applies a move's E parameter to the extruder position and
  returns the extruded filament length: the parameter itself in relative
  mode, the difference with the tracked position in absolute mode. G92 E
  resets already set state.e directly and compose naturally with both.
- New M82/M83 handlers switch State.relativeExtrusion; the default is
  absolute, matching the Marlin firmware default. Only the E accounting
  consults the mode: whether a move extrudes is still classified from the
  raw E parameter, so no existing rendering changes.
- linearMove tracks E on every command, including zero-length
  retract/prime moves, and resolves the move's physical endpoint before
  touching the state (groundwork for deriving dimensions from the segment).
  arcMove keeps the extruder position in sync too, so a linear move after
  an arc measures its extruded length from the right position.
- SlicerMetadataParser gains derivesExtrusionDimensions and
  parseFilamentDiameter, both defaulting to no-ops; dialects that announce
  exact ;WIDTH:/;HEIGHT: comments are unaffected.
- CuraMetadataParser opts in: Cura emits no dimension comments at all, so
  per-path width and height can only be reconstructed from the moves. The
  volumetric UltiGCode flavor is excluded (its E values are mm3 of material,
  not mm of filament).
- The filament diameter is read from the header ;FLAVOR: line only: Griffin
  is exclusive to Ultimaker's 2.85mm machines. Cura's own material_diameter
  setting sits in the ;SETTING_3 blob at the end of the file, after every
  move it would apply to; using it would reach a one-shot parse before any
  move executes but a streamed parse only after every move already ran,
  breaking streamed/one-shot equivalence, so it is deliberately not used.
- Parser.parseGCode forwards both as metadata.deriveExtrusionDimensions and
  metadata.filamentDiameter, evaluated once on the chunk that identified the
  slicer, like detectSlicerName.
- Job.deriveMoveDimensions runs on every extruding linear move when the
  metadata asked for derivation, before continuePath — so a derived change
  breaks the path exactly like a ;WIDTH:/;HEIGHT: comment would, and the
  derived values take the same render-time precedence over the global
  fallback (they are the path's own values).
- Layer height is the Z step between consecutive extrusion moves; the first
  move's Z stands in for the first layer. Comparing extrusion Zs only makes
  z-hop travels invisible; a zero step (ironing) or a step outside
  0.01-1.0mm (spiralize's micro-climb, sequential objects) keeps the
  current height while re-anchoring the reference Z.
- Width comes from conservation of volume with a rectangular deposit
  cross-section — width = deltaE x filament cross-section / (length x
  height) — matching the box profile ExtrusionGeometry extrudes. The
  filament cross-section comes from metadata.filamentDiameter (1.75mm when
  unannounced). Results are quantized to 0.01mm and held within a 2%
  relative tolerance of the current width so E-value rounding noise cannot
  shatter the model into micro-paths; implausible results (outside
  0.1-2.0mm, or over a segment shorter than 0.05mm) are discarded so the
  path falls back to the global setting.
- All derivation state (extruder position, last extrusion Z, current
  derived dimensions) lives on Job/State, which persist across streamed
  chunks: the ingestion-equivalence harness gains a Cura fixture whose
  layer change and width shift land mid-chunk in every chunked mode.
- Round-trip tests generate Cura-style gcode from known target dimensions
  via the inverted volumetric formula and assert the derived per-path
  values recover them, covering adaptive layer heights, z-hops, G92 E
  resets, relative extrusion (M83), ironing and spiralize.
Security review finding: coordinates and E values that are individually
finite (and so survive the parser's param validation) can overflow the
derivation's intermediate arithmetic — Math.hypot to Infinity, or NaN via
Inf - Inf — and Infinity/Infinity makes the width NaN, which a plain
less-than range check waves through and latches into the state, breaking
the path on every extruding move after it. Guard the length with
Number.isFinite and write the width plausibility check in inclusive form
(matching the height check) so NaN is rejected.
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 388e983):

https://gcode-preview--pr459-cura-derived-dimensi-w10oi6dl.web.app

(expires Wed, 07 Oct 2026 06:51:15 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 59bd114ae4847b32c2bba0b68620b9069a3e3531

@sophiedeziel

Copy link
Copy Markdown
Collaborator Author

Superseded by #462: the M82/M83 extrusion-mode work is split out into its own PR (#461), and this branch now stacks on top of it. GitHub does not allow retargeting the base of a PR that is part of a stack, hence the new PR rather than an edit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement to an existing feature parser Related to G-code parsing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support M82/M83 (absolute/relative extrusion mode)

1 participant