Skip to content

Pylon Injection V1 (WIP — libvgcode viewer integration in progress) - #6

Draft
jacobblitzer wants to merge 12 commits into
feature/custom-infillfrom
feature/pylon-injection
Draft

Pylon Injection V1 (WIP — libvgcode viewer integration in progress)#6
jacobblitzer wants to merge 12 commits into
feature/custom-infillfrom
feature/pylon-injection

Conversation

@jacobblitzer

Copy link
Copy Markdown
Owner

Pylon Injection — Work In Progress

Status: Active development. In flux. Not ready for review or merge.

What This Does

VoxelFill-style cylindrical pylon voids carved out of sparse infill and filled by periodic bottom-up extrusion events at layer boundaries. Creates reinforced structural columns embedded inside 3D prints.

V1 Scope (implemented)

  • Config keys, ModelVolume::is_pylon(), per-layer footprint cache
  • Subtract pylon footprints from sparse infill
  • CustomGCode::Type::PylonInject event type + descriptor
  • Scheduler with brick-bond, floor safety, support exclusion
  • Tool ordering routing with optional filament change
  • G-code emitter with state-coherent injection block
  • GUI menu entry: right-click → Add Pylon (cylinder)
  • Catch2 test for event emission and E bookkeeping
  • V1 docs (recon, impl summary, touchpoints)

WIP (this commit)

  • libvgcode viewer integration for pylon injection event visualization
  • PylonDebugSnapshot GUI component (522 lines, new)
  • 3DScene / GCodeViewer / GUI_ObjectList / MainFrame / Tab wiring
  • Tests not yet updated for new viewer/debug code

Safety

pylon_max_descent_depth = 0 (default) disables all injection — safe failure mode. Prevents nozzle crashes on unconfigured printers.

Build Status

Builds clean on Windows Release (VS 2022). CI not yet verified — this is the first push of this branch.

Draft PR to track work. Will mark ready for review when the libvgcode viewer integration and tests are complete.

jacobblitzer and others added 12 commits May 10, 2026 13:55
Adds the config surface for V1 of pylon injection. Per-region keys live
on PrintRegionConfig so they can be overridden via a pylon ModelVolume's
config. Global keys (filament selection, max nozzle descent) live on
PrintConfig.

No code reads these yet — wired up in subsequent commits. Defaults are
chosen so the feature is a no-op until the user opts in:
- pylon_enabled defaults false
- pylon_max_descent_depth defaults 0 (safety: disables all injection)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ons()

is_pylon() recognizes a ModelVolume as a pylon when it's NEGATIVE_VOLUME
typed AND has pylon_enabled=true on its config — graceful degrade in
older slicers (negative volumes carve as normal, injection step skipped).

has_pylons() is the early-exit gate for the pylon pipeline steps so
the cost is zero for prints with no pylons.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PrintObject::compute_pylon_footprints() walks (pylon volume × instance)
pairs, approximates each as a vertical cylinder from its transformed
AABB (radius = max(dx, dy) / 2, center XY = bbox center, Z extent =
bbox Z), and stamps the resulting 32-segment polygon into every layer
whose print_z is inside the Z extent — pre-intersected with
Layer::lslices so the footprint is guaranteed to be inside the model.

Called from PrintObject::infill() before the parallel make_fills loop
so the cache is hot for Hook B subtraction in the next commit.

Early-exits zero-cost when has_pylons() is false.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hook B from the recon. Inside Layer::make_fills, for each SurfaceFill,
gate on internal-and-not-bridge-and-not-solid (i.e. sparse stInternal
only, leaving stTop/stBottom/stInternalSolid/*Bridge alone so they cap
and seal the pylon). Subtract the per-layer pylon footprint from
surface_fill.expolygons before pattern instantiation; skip the pattern
entirely if the void consumed the region.

Top/bottom and solid surfaces are deliberately left unmodified so the
existing shell logic produces the planar caps that contain the
injected plug.

Note: CLI slice-and-inspect verification skipped per branch's "do not
run the slicer" constraint. Will be exercised by the Catch2 test in
Task 10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…descriptor

CustomGCode::PylonInject is the new layer-boundary event type for pylon
injection. Inserted before the Unknown sentinel to preserve string-keyed
serialisation; added to the JSON read map.

emit_custom_gcode_per_print_z gets a defensive early-return for
PylonInject so its JSON payload is never written to G-code as text —
the real emitter lands in Task 8.

PylonInjection::Event is the serialised descriptor that rides inside
CustomGCode::Item::extra. to_item / from_item are the round-trip
helpers the scheduler (Task 6) and emitter (Task 8) will use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lusion

Adds posSchedulePylonInjection step (between posContouring and
posSupportMaterial in the per-object pipeline). PrintObject::schedule
_pylon_injections() walks (pylon volume × instance) pairs, approximates
each as a vertical cylinder from its world AABB, runs three safety
gates (max-descent enabled, SUPPORT_ENFORCER overlap, floor below),
emits Z-spaced events using pylon_max_descent_depth as the step-aligned
event height with brick-bond 2-coloring stagger, snaps each event to
the nearest layer print_z, computes dE from cylinder volume ×
filament e_per_mm3, and writes batched CustomGCode::Items into
Model::plates_custom_gcodes[curr_plate_index].

Two deviations from the recon report (logged in summary):

  1) Storage. Recon claimed Model::custom_gcode_per_print_z; actual
     storage is the per-plate map plates_custom_gcodes (BBS multi-plate).
     PrintBase gains a non-const model() accessor for this.

  2) Per-layer multiplicity. assign_custom_gcodes consumes at most one
     CustomGCode::Item per layer. Scheduler now batches all events at
     the same snapped print_z into one Item; PylonInjection gains
     to_item_batch / from_item_batch helpers.

V1 limitation logged at runtime: auto-generated overhang support is
warned about but not geometrically intersected against pylon footprints.
Only user-declared SUPPORT_ENFORCER volumes are checked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ament change

Adds LayerTools::pylon_filament(print_config, region) — resolves the
global pylon_injection_filament; falls back to solid_infill_filament
when unset, mirroring Jake's existing per-feature filament fork.

Existing assign_custom_gcodes path is unmodified: PylonInject Items
pass through the `pause_or_custom_gcode` branch (line 1561), get
assigned to LayerTools::custom_gcode, and surface to the emitter.

V1 limitation (documented): pylon-driven filament changes happen at
the emitter without participation in wipe-tower partitioning. If
pylon_injection_filament differs from the layer's active filament,
expect an unpurged tool change at each pylon event. This avoids the
deep ToolOrdering::fill_wipe_tower_partitions restructuring flagged
in the spec; revisit if/when V2 wants clean wipe-tower interaction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the defensive PylonInject early-return in
emit_custom_gcode_per_print_z with the real synthesizer. For each
event in the batched JSON payload:

  ;PYLON_INJECT_START id=… x=… y=… z_bottom=… z_top=… dE=… filament=…
  [optional set_extruder if event.filament_id differs from current]
  retract
  G1 X Y                       ; descent speed
  G1 Z<z_bottom>               ; descent speed
  unretract
  G1 X Y Z<+step> E<dE_seg>    ; extrude speed (looped to z_top)
  retract
  G4 P<dwell_ms>
  [optional set_extruder back to prior filament]
  ;PYLON_INJECT_END

All moves go through GCodeWriter so its cached position, retract
state, and feedrate stay coherent — the next normal extrusion picks
up from a retracted, repositioned state and travels naturally.

dE is distributed linearly across the ascent (dE * dz / total_dz per
segment) so total dispensed equals the scheduler's pi*r^2*h volume.

V1 limitation: tool change happens via set_extruder() without wipe-
tower trip (consistent with Task 7's wipe-tower decision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an "Add Pylon (cylinder)" entry to the object's add-volume context
menu. The handler delegates to ObjectList::load_generic_subobject to
create a default cylinder primitive as a NEGATIVE_VOLUME, then sets
pylon_enabled=true on the new volume's config so the slicer's
schedule_pylon_injections() step picks it up.

All pylon_* PrintRegionConfig keys surface automatically in the
existing modifier-volume settings panel because
region_config_from_model_volume() merges ModelVolume.config into the
region's effective config. No new panel needed for V1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests/fff_print/test_pylon_injection.cpp with four scenarios:

  1) Pylon volume + max_descent_depth > 0 → paired markers in output.
  2) max_descent_depth == 0 → no markers (safe failure mode).
  3) No pylon volume → no markers (early-exit gate).
  4) Declared dE in each START marker matches pi*r^2*h * e_per_mm3
     to within 1e-3 relative tolerance (validates scheduler arithmetic).

All test cases use the [.] tag so they're hidden by default (slicing
is expensive); run explicitly via "[PylonInjection]". Tests are
single-threaded and avoid Catch2 pitfalls per tests/CLAUDE.md
(WithinRel matcher, no Approx, no shared section names in loops).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-facing guide for V1: setup workflow, config-key table, safety
notes on pylon_max_descent_depth, brief "how it works" sketch with
file pointers, G-code marker contract, V1 limitations, and 3MF
backward-compat behaviour.

Lives at repo root (no doc/ directory in this fork; matches the
recon report's location).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…napshot, touchpoints doc

Work-in-progress checkpoint of the pylon-injection feature. Not complete,
in active development. Capturing current state before context switch.

Highlights since last commit (dce4d13):
- libvgcode: extend Types/Viewer/Settings/ViewerImpl for pylon injection
  event visualization in the G-code viewer
- GUI: 3DScene, GCodeViewer, GUI_ObjectList, MainFrame, Tab wiring for
  pylon debug snapshot and viewer integration
- PylonDebugSnapshot: new GUI component (522 lines) for visual debugging
  of pylon injection events
- PrintObjectSlice, PrintApply, Model, ExtrusionEntity: plumbing updates
- Recon, impl summary, and touchpoints docs added
- AGENTS.md updated

Builds clean on Windows Release (VS 2022). Tests not yet updated for
new viewer/debug code. Known incomplete areas documented in
pylon-injection-impl-summary.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant