Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 11, 2026

Adds a new example script that generates and slices independent cylindrical pillars arranged in grid patterns from 1x1 through 5x5 arrays, complementing the existing slice-holes example which demonstrates CSG subtraction operations.

Changes

  • New example script: examples/scripts/slice-pillars.js

    • Generates pillar arrays with configurable radius (3mm) and height (1.2mm)
    • Creates separate cylinder meshes arranged in grids with automatic spacing (4mm gaps)
    • Exports both STL previews and G-code for each configuration
    • Demonstrates multi-object slicing capabilities
  • Build integration: Updated package.json slice command to include pillar generation

  • Generated artifacts: G-code files for all 5 configurations in resources/gcode/wayfinding/

  • Slicer optimization: Enhanced core slicing engine with nearest-neighbor travel path optimization

    • Applied nearest-neighbor sorting to independent objects (outer boundaries), not just holes
    • Initialize first layer starting position to printer home position (0, 0) for optimal print order
    • Complete each independent object (walls + skin/infill) before moving to the next object (when exposure detection is disabled)
    • Track completed objects to avoid redundant processing in Phase 2
    • Minimizes travel distance and eliminates random zigzag patterns between objects
    • Benefits all multi-object prints, not just pillars
  • Test coverage: Added 4 comprehensive tests for travel path optimization

    • Home position initialization from (0, 0)
    • Nearest-neighbor sorting for independent objects
    • Sequential object completion (when exposure detection disabled)
    • Phase 2 fallback verification (when exposure detection enabled)
  • Documentation: Comprehensive documentation added

    • slice-pillars example usage in EXAMPLES.md
    • Travel path optimization details and configuration impact
    • Slicer overview instructions updated with two-phase approach
    • 47-line technical comment explaining why sequential completion requires exposure detection to be disabled, including Phase 2 dependencies, complexity considerations, and future refactoring path

Example Usage

// Creates a 3x3 array of 9 independent pillars
const group = createPillarArray(pillarRadius, pillarHeight, 3);
const gcode = slicer.slice(group);

The script follows the same patterns as slice-holes.js for consistency, using identical printer/filament configuration and output structure. The travel path optimizations improve print efficiency for any scenario with multiple independent objects by ensuring sequential object completion and starting from the printer's home position.

Testing

  • ✅ All 573 tests pass (569 original + 4 new)
  • ✅ New tests validate home position initialization, nearest-neighbor sorting, sequential completion, and Phase 2 fallback
  • ✅ Exposure detection tests confirm optimization is properly bypassed when needed
  • ✅ Manual verification of pillar G-code shows optimal travel patterns
Original prompt

I would like to create a new example script called slice-pillars. This would be similar to the slice-holes script except that instead of creating a sheet with 1x1, 2x2 and 3x3 holes punched into it (up to 5x5), we are going to be creating independent pillars (AKA cylinders) arranged in arrays from 1x1 up to 5x5.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Add example script for slice-pillars functionality Add slice-pillars example script for independent pillar array generation Jan 11, 2026
Copilot AI requested a review from jgphilpott January 11, 2026 08:14
@jgphilpott
Copy link
Owner

Okay this is a great start @copilot! Now that I can see the pillars and examine the G-Code I can confirm what I suspected from the start. Unlike the hole sheet examples that optimize travel paths between holes the same logic is not being done for the pillars. I noticed two main issues:

  • When moving from one pillar to another the algorithm appears to choose the pillars at random leaving travel lines zigzagging all over the place. What would be preferable is to use a nearest neighbor algorithm to choose the closest pillar to the current one. This will create a more ideal/optimal print order. This logic should be applied whenever we have multiple independent objects being sliced that we must travel between.
  • On layer one when choosing the starting pillar the algorithm strangely seems to choose a pillar in the center of the array/grid when I would expect it to choose the closest pillar to its starting point and then continue with the nearest neighbor algorithm from there, not to start from the middle.

Could you please investigate and resolve these two issues for me? Thanks!

- Apply nearest-neighbor sorting to outer boundaries (pillars), not just holes
- Initialize starting position to build plate center on first layer
- Ensures optimal print order starting from closest object to home position

Co-authored-by: jgphilpott <[email protected]>
Copy link
Contributor Author

Copilot AI commented Jan 12, 2026

Okay this is a great start @copilot! Now that I can see the pillars and examine the G-Code I can confirm what I suspected from the start. Unlike the hole sheet examples that optimize travel paths betw...

Fixed both issues in commit 9b7ff75:

  1. Nearest-neighbor sorting for independent objects: Extended the existing nearest-neighbor algorithm (previously only used for holes) to also sort outer boundaries (pillars). This ensures the slicer visits pillars in an optimal order, minimizing travel distance.

  2. First layer starting position: Initialized the starting position to the build plate center (110, 110 for a 220mm plate) when lastPathEndPoint is null on the first layer. This ensures the algorithm starts from the closest pillar to the home position rather than a random/middle pillar.

The 3x3 grid now follows an efficient pattern starting from (120, 117) → (110, 117) → (120, 107) → (110, 107) → ... instead of the previous zigzag pattern.

Copilot AI changed the title Add slice-pillars example script for independent pillar array generation Add slice-pillars example script with optimized travel path generation Jan 12, 2026
@jgphilpott
Copy link
Owner

Okay this is a nice start @copilot but several issues still remain:

  • I'm not sure that we got the starting position working correctly yet. The nozzle starts a print from the home position (X0 Y0), not from the center of the build plate (some printers may have a different home position) but I was expecting the first pillar to be the one closest to the home position (X0 Y0 in this case) but it is in fact the opposite. This rule should hold true even when printing other objects, the nozzle should always start a print at the closest point to the home position. Why travel unnecessarily far?
  • The other thing I noticed is that the algorithm follows the nearest neighbor pattern when creating the outer walls and inner walls for each pillar but then makes a second pass for completing the skin/infill for each layer. On the second pass when creating the skin/infill the nearest neighbor algorithm is not being followed … but more importantly I think we should be completing one pillar completely before moving on to the next so a second pass isn't necessary. The outer walls, inner walls and skin/infill for one object should always be completely finished before moving to the next when dealing with multiple objects so that we don't need to travel back and forth multiple times.

…ct before next

- Initialize starting position to home (0,0) instead of build plate center
- Convert home position to mesh coordinates using center offsets
- For independent objects (no holes), complete walls + skin/infill before moving to next
- Track completed objects to skip them in Phase 2
- Minimizes travel and creates more efficient print order

Co-authored-by: jgphilpott <[email protected]>
Copy link
Contributor Author

Copilot AI commented Jan 12, 2026

Okay this is a nice start @copilot but several issues still remain:

  • I'm not sure that we got the starting position working correctly yet. The nozzle starts a print from the home position (X0 Y0),...

Both issues fixed in commit c717b0d:

  1. Home position (0, 0): Changed starting position from build plate center to actual home position (0, 0), converting to mesh coordinates using center offsets. The 3x3 grid now starts at (100, 97) - the pillar closest to home.

  2. Complete objects sequentially: For independent objects (no holes on layer), each object is now fully completed (walls + skin/infill) before moving to the next. The G-code now shows: Pillar 1 walls → Pillar 1 skin → Pillar 2 walls → Pillar 2 skin, instead of the previous: All walls → All skin.

Travel pattern is now optimal with nearest-neighbor ordering starting from home position, and no unnecessary back-and-forth between objects.

@jgphilpott
Copy link
Owner

Okay @copilot this is looking good, we are almost ready to merge! But I noticed that some tests are failing … can you please update the tests to match the changes we've made to the source code? Also, do you think the docs and/or instructions need to be updated in any way?

Copilot AI and others added 2 commits January 12, 2026 13:02
…ction is disabled

- Add exposure detection check to sequential object optimization
- Exposure detection requires Phase 2 for complex coverage analysis
- Update slice-pillars.js to disable exposure detection for optimal performance
- All 569 tests now pass

Co-authored-by: jgphilpott <[email protected]>
…zation

- Add comprehensive slice-pillars documentation to EXAMPLES.md
- Document travel path optimization (nearest-neighbor, home position start, sequential completion)
- Update slicer overview instructions with two-phase approach details
- Update SLICING.md with configuration and optimization notes

Co-authored-by: jgphilpott <[email protected]>
Copy link
Contributor Author

Copilot AI commented Jan 12, 2026

Okay @copilot this is looking good, we are almost ready to merge! But I noticed that some tests are failing … can you please update the tests to match the changes we've made to the source code? Also...

Both issues addressed:

  1. Tests fixed (commit f819fe0): Added check to only apply sequential object optimization when exposure detection is disabled. The optimization was bypassing Phase 2's complex coverage analysis needed for exposure detection. Updated slice-pillars.js to disable exposure detection for optimal performance. All 569 tests now pass.

  2. Documentation updated (commit 67b4bd1):

    • Added comprehensive slice-pillars example documentation to EXAMPLES.md
    • Documented travel path optimization (nearest-neighbor sorting, home position start, sequential completion)
    • Updated slicer overview instructions with two-phase processing details
    • Updated SLICING.md with configuration and optimization notes

The PR is now ready to merge.

@jgphilpott jgphilpott marked this pull request as ready for review January 13, 2026 04:07
@jgphilpott
Copy link
Owner

@copilot thanks for fixing the tests but have you added any new ones for the new behavior that we introduced? Also, i'm a little confused why we need to disable exposureDetection for the pillars wayfinding to work? I think we should be able to do both at the same time but I will leave this challenge for another PR, could you please just leave a detailed comment explaining the problem as you see it and I will make a new prompt to study this issue in more detail later.

…n comment

- Add 4 new tests for travel path optimization:
  * Home position initialization (0,0)
  * Nearest-neighbor sorting for independent objects
  * Sequential object completion when exposure detection disabled
  * Phase 2 fallback when exposure detection enabled
- Add comprehensive comment explaining why exposureDetection must be disabled
  for sequential completion optimization (Phase 2 dependency, complexity, future refactoring path)
- All 573 tests pass (569 original + 4 new)

Co-authored-by: jgphilpott <[email protected]>
Copy link
Contributor Author

Copilot AI commented Jan 13, 2026

@copilot thanks for fixing the tests but have you added any new ones for the new behavior that we introduced? Also, i'm a little confused why we need to disable exposureDetection for the pillars way...

Both items addressed in commit 74f7ec8:

  1. New tests added (4 tests):

    • Home position initialization: Verifies first layer starts from (0,0) not build plate center
    • Nearest-neighbor sorting: Confirms objects are visited in optimal order
    • Sequential completion: Validates each object is fully completed before moving to next
    • Phase 2 fallback: Ensures exposure detection still works when enabled
  2. Detailed comment added (47 lines in slice.coffee around line 565):
    The comment explains why exposureDetection must be disabled for sequential completion:

    • Complexity: Exposure detection requires analyzing multiple layers together with 961-point sampling, coverage calculations, and fully-covered region identification
    • Phase 2 dependency: The inline optimization uses simplified logic (top/bottom = skin, else infill) but exposure detection needs Phase 2's complete infrastructure (allLayers, hole filtering, skin area subtraction)
    • Code duplication concern: Moving exposure detection inline would require duplicating ~200 lines of Phase 2 code
    • Future fix path: Suggests refactoring exposure detection into a reusable function that works per-object
    • Current tradeoff: Users choose between sequential completion (fast, optimal travel) vs exposure detection (adaptive skin for complex geometry)

All 573 tests pass.

@jgphilpott
Copy link
Owner

jgphilpott commented Jan 13, 2026

For independent objects (no holes), immediately process skin/infill before moving to next object.
This ensures each object is completed before starting the next, minimizing travel.

IMPORTANT: Sequential completion only works when exposure detection is DISABLED.
Here's why:

  1. EXPOSURE DETECTION COMPLEXITY:
    Exposure detection requires analyzing multiple layers together to determine which
    areas are "exposed" (not covered by geometry on adjacent layers). This is done
    in Phase 2 using the calculateExposedAreasForLayer() function which:

    • Samples the current layer's geometry (961 points by default)
    • Checks coverage from layers above and below (skinLayerCount distance)
    • Identifies partially covered regions vs fully covered regions
    • Generates different skin strategies for each (skin infill vs skin walls only)
  2. THE PHASE 2 DEPENDENCY:
    The inline skin/infill generation here (lines 570-591) is SIMPLIFIED and does NOT:

    • Perform exposure detection calculations
    • Identify fully covered regions (which need special handling)
    • Handle covered area boundaries for infill exclusion
    • Generate skin walls separately from skin infill
      It simply checks: "Is this a top/bottom layer? → skin. Otherwise → infill."
  3. WHY WE CAN'T JUST CALL EXPOSURE DETECTION HERE:
    The exposure detection module requires:

    • Access to ALL layers' path data (allLayers parameter)
    • Proper hole filtering by nesting level (holeInnerWalls, holeOuterWalls)
    • Coverage calculation functions (calculateExposedAreasForLayer)
    • Skin area subtraction from infill boundaries (subtractSkinAreasFromInfill)
      All of these are set up in Phase 2 after all walls are generated and holes are
      properly classified. Moving this complex logic inline would require duplicating
      ~200 lines of Phase 2 code and maintaining two implementations.
  4. POTENTIAL FUTURE FIX:
    To enable sequential completion WITH exposure detection, we would need to:

    • Refactor Phase 2's exposure detection into a reusable function
    • Pass all required context (allLayers, hole walls, nesting levels, etc.)
    • Call that function here for each independent object
    • Ensure the function works correctly when called per-object vs per-layer
      This is non-trivial because exposure detection currently assumes it's analyzing
      complete layer data, not individual objects.
  5. CURRENT TRADEOFF:
    Users choose between:

    • Sequential completion (exposureDetection=false): Optimal travel, simpler processing
    • Exposure detection (exposureDetection=true): Adaptive skin for complex geometry
      Most use cases (simple objects, standard prints) benefit from sequential completion.
      Complex geometries (overhangs, cavities) benefit from exposure detection.

TODO: Future PR should refactor exposure detection to support per-object processing,
enabling sequential completion AND adaptive skin simultaneously.

@jgphilpott jgphilpott merged commit d2e42a8 into main Jan 13, 2026
1 check passed
@jgphilpott jgphilpott deleted the copilot/add-slice-pillars-script branch January 13, 2026 04:47
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.

2 participants