This commit is contained in:
Nicolas Predella 2026-05-06 21:09:46 +03:00
parent b736f1821a
commit 35996fb956
6 changed files with 174 additions and 69 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
__pycache__ __pycache__
release

View file

@ -1,47 +1,50 @@
# NodeSlicer # NodeSlicer
NodeSlicer is a high-performance Blender extension that provides a custom node-based interface for mesh slicing and G-code generation. By bypassing traditional Blender modifiers and using NumPy and Shapely for geometry processing, it offers a flexible and extensible workflow for 3D printing and procedural geometry tasks. NodeSlicer is a high-performance Blender extension that provides a custom node-based interface for mesh slicing and G-code generation. By using NumPy for geometry processing and Pyclipper for robust 2D polygon operations, it offers a flexible and extensible workflow for 3D printing, CNC toolpathing, and procedural geometry tasks.
## Key Features ## Key Features
- **Custom Node Editor**: A dedicated node tree type for slicing workflows. - **Custom Node Editor**: A dedicated node tree type (`NodeSlicer Tree`) for slicing workflows.
- **Fast Geometry Processing**: Uses NumPy for vectorized mesh operations and Shapely for robust 2D geometry manipulation. - **Fast Geometry Processing**: Vectorized mesh operations using NumPy and robust 2D geometry manipulation via Pyclipper.
- **G-code Generation**: Direct conversion of 2D contours (toolpaths) into G-code. - **G-code Lifecycle**: Direct conversion of 2D contours (toolpaths) into G-code, with support for loading, joining, and exporting G-code.
- **Live Preview**: Debug nodes to visualize generated toolpaths directly in the Blender viewport. - **Advanced Slicing**:
- **Extensible**: Modular architecture allowing for easy addition of new nodes and processing logic. - **Shell Slicing**: Accurate Z-layer extraction.
- **Buffered Offsetting**: Layer-independent offsetting with support for Euler rotations (tilted/non-planar offsetting).
- **Consistent Infill**: Cross-hatch grid infill that maintains alignment across layers and supports degree-based rotation.
- **Boolean Slices**: Union, Intersection, and Difference operations on slice boundaries.
- **Visual Preview**: Built-in G-code to Mesh conversion with automated Geometry Nodes integration ("Nonplanar Slicing Preview") for high-quality viewport feedback.
## Installation ## Installation
### Requirements ### Requirements
- Blender 4.2 or newer (uses the new Extension system). - Blender 3.6 or newer.
- Dependencies (bundled or installed via `uv`): - Dependencies: `numpy`, `pyclipper`. (Included in the `wheels/` directory or managed via `uv`).
- `numpy`
- `shapely`
- `pyclipper`
### As a Blender Extension ### Setup
1. Zip the repository contents (excluding `.venv`, `__pycache__`, etc.). 1. Download or clone this repository.
2. In Blender, go to `Edit > Preferences > Get Extensions`. 2. In Blender, install as an addon by pointing to the directory or zip.
3. Click the arrow in the top right and select `Install from Disk`. 3. Ensure the dependencies are available in your Blender Python environment.
4. Select your zip file.
## Quick Start ## Workflow
1. Open the **Node Editor**. 1. **Initialize**: Open the **Node Editor** and switch the tree type to **NodeSlicer Tree**.
2. Switch the tree type to **NodeSlicer Tree**. 2. **Import**: Use a **Get Mesh** node to bring a Blender object into the slicing engine.
3. Create a new tree. 3. **Slice**: Use **Slice Shells** to generate layer boundaries.
4. Add an **Object Input** node and select a mesh object. 4. **Process**:
5. Connect it to a **Get Mesh** node. * Add perimeters with **Buffer Shells**.
6. Connect the mesh to **Slice Shells**. * Add internal structure with the **Infill** node.
7. Connect the slices to **Slices to Paths**, then **Paths to Gcode**. * Combine or modify paths using **Join Paths** or **Join Slices**.
8. Use a **Gcode To File** node to export your result. 5. **Generate**: Convert paths to G-code using **Paths to Gcode**.
6. **Export & Preview**: Use **Gcode To File** to save the result, and **Gcode to Mesh** to visualize the final toolpaths in the viewport.
## Documentation ## Documentation
- [Project Overview](doc/README.md) For detailed information on nodes and modules, see:
- [Module Reference](doc/MODULES.md) - [User Guide](doc/USER_GUIDE.md) - **Start here!**
- [Node & Socket Reference](doc/NODES.md) - [Node & Socket Reference](doc/NODES.md)
- [Module Architecture](doc/MODULES.md)
- [Project Overview](doc/README.md)
## License ## License
This project is licensed under the GPL-3.0-or-later license. See `blender_manifest.toml` for details. This project is licensed under the GPL-3.0-or-later license.

21
build.sh Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
TMPDIR="$(mktemp -d)"
cleanup() { rm -rf "$TMPDIR"; }
trap cleanup EXIT
mapfile -t FILES < <(git ls-files -co --exclude-standard \
| grep -v -xE '(\.gitignore|build\.sh)')
tar -cf - "${FILES[@]}" | tar -xf - -C "$TMPDIR"
cd "$TMPDIR"
/home/nicolas/Applications/blender-5.1.0-linux-x64/blender \
--command extension build --split-platforms \
--output-filepath "$SCRIPT_DIR/release/NodeSlicer_0.0.1.zip"
cd "$SCRIPT_DIR"

View file

@ -10,42 +10,40 @@ This file provides a mapping of modules to their primary responsibilities and ke
- **Sockets**: Definitions for all `SLICESOCK_*` classes. - **Sockets**: Definitions for all `SLICESOCK_*` classes.
- **Nodes**: Definitions for all `SLICENODE_*` classes. - **Nodes**: Definitions for all `SLICENODE_*` classes.
- **Evaluation Engine**: - **Evaluation Engine**:
- `_resolve_input()`: Recursively evaluates linked sockets. - `resolve_input()`: Recursively evaluates linked sockets.
- `_resolve_output()`: Computes node outputs with a caching system (`_EvalCache`) to avoid redundant calculations. - `_resolve_output()`: Computes node outputs with a caching system (`_EvalCache`) to avoid redundant calculations.
- `_follow_reroute()`: Handles Blender's Reroute nodes during evaluation. - `_follow_reroute()`: Handles Blender's Reroute nodes during evaluation.
- **Error Handling**: `NodeException` for user-facing evaluation errors.
- `registry.py`: - `registry.py`:
- **Auto-Registration**: Utility to track classes decorated with `@register_class`. - **Auto-Registration**: Utility to track classes decorated with `@register_class`.
- **Resource Management**: Logic for dynamic icon and timer registration.
- `blender_manifest.toml`: - `blender_manifest.toml`:
- Metadata for the Blender Extension platform. - Metadata for the Blender Extension platform.
- Lists required wheels (Shapely, PyClipper) for different platforms.
- `pyproject.toml`: - `pyproject.toml`:
- Standard Python project configuration. - Standard Python project configuration.
- Lists development and runtime dependencies.
## `/core` (Geometry & Slicing) ## `/core` (Geometry & Slicing)
- `slice.py`: - `slice.py`:
- `slice_at_range()`: Top-level slicing function that iterates through Z-heights. - `slice_at_range()`: Top-level slicing function that iterates through Z-heights.
- `slice_at_z()`: Vectorized plane-mesh intersection using NumPy. - `slice_at_z()`: Vectorized plane-mesh intersection using NumPy.
- `Pather`: Class that converts toolpaths into G-code strings. Handles layer change comments and extrusion calculations. - `Pather`: Class that converts toolpaths into G-code strings.
- `segments_to_slice()`: Utility to convert raw intersection segments into closed loops using Pyclipper and the Even-Odd fill rule. - `segments_to_slice()`: Utility to convert raw intersection segments into closed loops using Pyclipper.
- `link_segments()`: Logic for joining loose segments into continuous paths. - `link_segments()`: Logic for joining loose segments into continuous paths.
- `buffer_slice()`: Offset utility using PyclipperOffset. Groups paths by Z-height to ensure independent buffering of layers. - `buffer_slice()`: Offset utility using PyclipperOffset. Supports rotated offsetting.
- `infill_slice()`: Generates rotated grid infill patterns, ensuring layer-to-layer consistency.
- `boolean_slice()`: Performs 2D boolean operations (Union, Intersection, etc.) on slice sets.
- `geometry.py`: - `geometry.py`:
- `crc32_array()`: High-performance checksum generation for NumPy arrays, used for internal caching and data validation. - Geometry utility functions.
## `/infra` (Blender Integration) ## `/infra` (Blender Integration)
- `blender_mesh_capture.py`: - `blender_mesh_capture.py`:
- `SlicingObject`: Represents a single mesh as a NumPy triangle array. Computes bounding boxes and checksums. - `SlicingObject`: Represents a single mesh as a NumPy triangle array.
- `SlicingCollection` / `SlicingGroup`: Hierarchical wrappers for managing multiple meshes and their metadata (e.g., Wipe Tower location). - `capture_mesh_edges()`: Extracts edges from a Blender mesh for path-based workflows.
- `objects_to_tris()`: Performance-critical function that extracts mesh data from Blender objects into NumPy arrays using `foreach_get`. Handles world-space transformation and unit scaling.
- `blender_bridge.py`: - `blender_slice_nodes_generic.py`:
- **Hierarchy Utilities**: Helpers like `get_all_children()` for traversing Blender's object tree. - Base classes for NodeSlicer nodes (`SliceNode`) and sockets (`SliceNodeSocket`).
- Core evaluation logic (`resolve_input`).

View file

@ -8,23 +8,24 @@
| `SLICESOCK_Float` | `float` | Python float | Numeric value | | `SLICESOCK_Float` | `float` | Python float | Numeric value |
| `SLICESOCK_Int` | `int` | Python int | Integer value | | `SLICESOCK_Int` | `int` | Python int | Integer value |
| `SLICESOCK_String` | `str` | Python string | Text/Path value | | `SLICESOCK_String` | `str` | Python string | Text/Path value |
| `SLICESOCK_Vector` | `tuple` | Python tuple (3) | XYZ or Euler rotation value |
| `SLICESOCK_Mesh` | `SlicingObject` | NumPy NDArray (Triangles) | Internal mesh data (Transformed/Scaled) | | `SLICESOCK_Mesh` | `SlicingObject` | NumPy NDArray (Triangles) | Internal mesh data (Transformed/Scaled) |
| `SLICESOCK_Paths` | `list[np.ndarray]` | List of paths (N, 3) | Generic toolpaths or slice boundaries | | `SLICESOCK_Paths` | `SlicePaths` (list) | List of paths (N, 3) + groups | Generic toolpaths or slice boundaries |
| `SLICESOCK_Gcode` | `str` | Multi-line string | Generated G-code text | | `SLICESOCK_Gcode` | `str` | Multi-line string | Generated G-code text |
## Detailed Data Types ## Detailed Data Types
### SlicingObject (`SLICESOCK_Mesh`) ### SlicingObject (`SLICESOCK_Mesh`)
The `SlicingObject` class wraps a high-performance NumPy representation of a Blender mesh. The `SlicingObject` class wraps a high-performance NumPy representation of a Blender mesh.
- **`.mesh`**: A NumPy `ndarray` of type `float64` with shape `(N, 4, 3)`. - **`.mesh`**: A NumPy `ndarray` of type `float64` with shape `(N, 3, 3)`.
- `N`: Number of triangles. - `N`: Number of triangles.
- `[i, 0:3, :]`: The three vertices of the $i$-th triangle. - `[i, 0:3, :]`: The three vertices of the $i$-th triangle.
- `[i, 3, :]`: The face normal of the $i$-th triangle.
- **Coordinates**: Transformed to world space and scaled by the Blender scene unit scale (multiplied by 1000 for mm-standard G-code). - **Coordinates**: Transformed to world space and scaled by the Blender scene unit scale (multiplied by 1000 for mm-standard G-code).
### Paths (`SLICESOCK_Paths`) ### SlicePaths (`SLICESOCK_Paths`)
A `list[np.ndarray]` (each `(N, 3)`) containing toolpaths or slice results. A custom list subclass `SlicePaths(list[np.ndarray])` containing toolpaths.
- **Content**: Can be open or closed segments. When generated from `Slice Shells`, they are guaranteed to be closed loops forming valid manifold boundaries. - **Content**: Each `np.ndarray` has shape `(N, 3)`.
- **Groups**: Maintains a `.groups` list of integers mapping each path to a layer or logical group.
- **Z-Awareness**: Coordinates are stored as 3D `(X, Y, Z)` to maintain the correct layer height. - **Z-Awareness**: Coordinates are stored as 3D `(X, Y, Z)` to maintain the correct layer height.
- **Topology**: Generated and cleaned using `pyclipper`. - **Topology**: Generated and cleaned using `pyclipper`.
@ -32,30 +33,38 @@ A `list[np.ndarray]` (each `(N, 3)`) containing toolpaths or slice results.
A standard Python string containing formatted G-code. A standard Python string containing formatted G-code.
- **Format**: `G0` for travels, `G1` for extrusions. - **Format**: `G0` for travels, `G1` for extrusions.
- **Extrusion**: Calculated as absolute `E` values based on Euclidean distance and a configurable `EXTRUSION_RATE`. - **Extrusion**: Calculated as absolute `E` values based on Euclidean distance and a configurable `EXTRUSION_RATE`.
- **Metadata**: Includes `;OFFSET_X=` and `;OFFSET_Y=` comments to allow reconstruction of original coordinates.
## Nodes ## Nodes
### Input Nodes All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** category.
- `SLICENODE_InputFloat`: Single float input.
- `SLICENODE_InputInt`: Single integer input.
- `SLICENODE_InputString`: Single string/path input.
- `SLICENODE_InputObject`: Blender object picker.
- `SLICENODE_GcodeBlock`: Manual text area for entering raw G-code.
### Processing Nodes ### Data & Input
- `SLICENODE_GetMesh`: Converts a Blender Object into internal `SlicingObject` format. - **Object**: Blender object picker.
- `SLICENODE_SliceShells`: Slices a `SlicingObject` into 2D `Paths` at a given layer height. - **Float**: Single float input.
- `SLICENODE_BufferShells`: Offsets/buffers 2D `Paths` using a "Distance" parameter. Buffers each Z-layer independently to maintain 3D structure and uses winding rules to correctly handle nested holes. - **Integer**: Single integer input.
- `SLICENODE_GridInfill`: Generates a grid of lines clipped by boundary paths. - **String**: Single string/path input.
- `SLICENODE_PathsToGcode`: Converts `Paths` into G-code blocks. - **Rotation**: Input for Euler rotation vectors.
- **Printer Intrinsics**: Configures bed size and extrusion rates.
- **Gcode Block**: Manual text area for entering raw G-code.
### Joiner Nodes (Variable Inputs) ### Geometry Processing
- `SLICENODE_JoinMesh`: Merges multiple `SlicingObject` instances into one. - **Get Mesh**: Converts a Blender Object into internal `SlicingObject` format.
- `SLICENODE_JoinPaths`: Merges multiple paths. - **Get Paths**: Extracts existing edges from a mesh as paths.
- `SLICENODE_JoinGcode`: Concatenates multiple G-code strings with newline separators. - **Slice Shells**: Slices a `SlicingObject` into 2D `SlicePaths` at a given layer height.
- **Buffer Shells**: Offsets/buffers 2D `SlicePaths`. Supports rotated offsetting and multiple miter types.
- **Infill**: Generates a cross-hatch grid of lines clipped by boundary paths. Supports rotation in degrees.
- **Boolean Paths**: Performs 2D boolean operations (`UNION`, `INTERSECTION`, `DIFFERENCE`, `XOR`) on multiple slice sets.
### Action Nodes (Executable) ### Utility & Joiners
- `SLICENODE_GcodeToFile`: Writes G-code string to a file on disk. - **Join Mesh**: Merges multiple `SlicingObject` instances into one.
- `SLICENODE_GcodeToMesh`: Decodes G-code and creates a Blender mesh (Edges) for visualization/debugging. - **Join Paths**: Combines multiple `SlicePaths`. Supports Sequential or Interleaved modes.
- `SLICENODE_LoadGcode`: Reads a G-code file from disk into a string. - **Join Gcode**: Concatenates multiple G-code strings.
- `SLICENODE_Test`: Triggers evaluation for debugging purposes. - **Integer Math**: Simple integer operations (Add, Multiply, etc.).
### Action & I/O
- **Paths to Gcode**: Converts `SlicePaths` into G-code blocks.
- **Load Gcode**: Reads a G-code file from disk.
- **Gcode to File**: Writes G-code string to a file on disk.
- **Gcode to Mesh**: Decodes G-code and creates a Blender mesh ("Debug" object) for visualization.
- **Test**: Triggers evaluation for debugging purposes.

73
doc/USER_GUIDE.md Normal file
View file

@ -0,0 +1,73 @@
# NodeSlicer User Guide
This guide will walk you through finding the NodeSlicer interface and creating your first slicing workflow.
## 1. Finding NodeSlicer
NodeSlicer lives in its own dedicated Node Tree type. To access it:
1. Open any **Node Editor** area in Blender (usually found in the Shading or Geometry Nodes workspaces).
2. In the header, click the **Tree Type** icon (next to the "New" button).
3. Select **NodeSlicer Tree** from the list.
4. Click **New** to create a new slicing project.
## 2. Basic Usage: "The Quick Slice"
Follow these steps to generate G-code for a simple object and preview it:
### Step 1: Input your Object
* Press `Shift+A` to open the Add menu.
* Look for the **SliceNodes** sub-menu at the bottom.
* Select **Object** to add an object picker.
* Use the eyedropper to select the mesh you want to slice.
* From the same menu, add a **Get Mesh** node and connect the Object output to it. This converts the Blender object into a format the slicer understands.
### Step 2: Slice into Layers
* Add a **Slice Shells** node from the **SliceNodes** menu.
* Connect the Mesh output from "Get Mesh" to the Mesh input of "Slice Shells".
* Set your **Layer Height** (e.g., 0.2 mm).
### Step 3: Create Paths and G-code
* Add a **Paths to Gcode** node.
* Connect the Paths output of "Slice Shells" to the Paths input of "Paths to Gcode".
* This node generates the raw G-code text.
### Step 4: Preview the Result
* Add a **Gcode to Mesh** node.
* Connect the Gcode output to it.
* Click the **Update Debug Mesh** button on the node.
* A new object named **Debug** will appear in your scene, showing the toolpaths.
> **Pro Tip**: If your file contains a Geometry Nodes group called "Nonplanar Slicing Preview", it will be automatically assigned to the Debug object for a high-quality visualization.
---
## 3. Adding Perimeters and Infill
A single slice is just an outline. To make a 3D print, you need wall thickness and internal structure.
### Buffering (Walls)
* Use the **Buffer Shells** node between "Slice Shells" and "Paths to Gcode".
* Set a negative **Distance** (e.g., -0.4 mm) to create an inner perimeter.
* You can chain multiple Buffer nodes to create multiple walls.
### Infill (Internal Structure)
* Add an **Infill** node.
* Connect your slice paths to its input.
* **Density**: Controls how close the grid lines are.
* **Rotation**: Rotates the grid (in degrees). The grid is calculated globally, so it stays perfectly aligned across layers.
---
## 4. Exporting to a File
Once you are happy with the preview:
1. Add a **Gcode to File** node.
2. Connect your G-code string to it.
3. Type a **Filename** (e.g., `C:/temp/my_slice.gcode` or `/tmp/test.gcode`).
4. Click **Generate File**.
## 5. Tips for Success
* **Scale**: The slicer treats 1 Blender unit as 1 meter, but scales everything to **mm** for the G-code output.
* **Orientation**: The slicer currently assumes your object is oriented correctly for printing (Z is up).
* **Performance**: For very complex meshes, the "Get Mesh" and "Slice Shells" nodes might take a moment to compute. The result is cached, so changing settings further down the chain (like Infill density) will be much faster.