diff --git a/.gitignore b/.gitignore index bee8a64..b54041d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__ +release \ No newline at end of file diff --git a/README.md b/README.md index d072e0d..5ceb325 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,50 @@ # 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 -- **Custom Node Editor**: A dedicated node tree type for slicing workflows. -- **Fast Geometry Processing**: Uses NumPy for vectorized mesh operations and Shapely for robust 2D geometry manipulation. -- **G-code Generation**: Direct conversion of 2D contours (toolpaths) into G-code. -- **Live Preview**: Debug nodes to visualize generated toolpaths directly in the Blender viewport. -- **Extensible**: Modular architecture allowing for easy addition of new nodes and processing logic. +- **Custom Node Editor**: A dedicated node tree type (`NodeSlicer Tree`) for slicing workflows. +- **Fast Geometry Processing**: Vectorized mesh operations using NumPy and robust 2D geometry manipulation via Pyclipper. +- **G-code Lifecycle**: Direct conversion of 2D contours (toolpaths) into G-code, with support for loading, joining, and exporting G-code. +- **Advanced Slicing**: + - **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 ### Requirements -- Blender 4.2 or newer (uses the new Extension system). -- Dependencies (bundled or installed via `uv`): - - `numpy` - - `shapely` - - `pyclipper` +- Blender 3.6 or newer. +- Dependencies: `numpy`, `pyclipper`. (Included in the `wheels/` directory or managed via `uv`). -### As a Blender Extension -1. Zip the repository contents (excluding `.venv`, `__pycache__`, etc.). -2. In Blender, go to `Edit > Preferences > Get Extensions`. -3. Click the arrow in the top right and select `Install from Disk`. -4. Select your zip file. +### Setup +1. Download or clone this repository. +2. In Blender, install as an addon by pointing to the directory or zip. +3. Ensure the dependencies are available in your Blender Python environment. -## Quick Start +## Workflow -1. Open the **Node Editor**. -2. Switch the tree type to **NodeSlicer Tree**. -3. Create a new tree. -4. Add an **Object Input** node and select a mesh object. -5. Connect it to a **Get Mesh** node. -6. Connect the mesh to **Slice Shells**. -7. Connect the slices to **Slices to Paths**, then **Paths to Gcode**. -8. Use a **Gcode To File** node to export your result. +1. **Initialize**: Open the **Node Editor** and switch the tree type to **NodeSlicer Tree**. +2. **Import**: Use a **Get Mesh** node to bring a Blender object into the slicing engine. +3. **Slice**: Use **Slice Shells** to generate layer boundaries. +4. **Process**: + * Add perimeters with **Buffer Shells**. + * Add internal structure with the **Infill** node. + * Combine or modify paths using **Join Paths** or **Join Slices**. +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 -- [Project Overview](doc/README.md) -- [Module Reference](doc/MODULES.md) +For detailed information on nodes and modules, see: +- [User Guide](doc/USER_GUIDE.md) - **Start here!** - [Node & Socket Reference](doc/NODES.md) +- [Module Architecture](doc/MODULES.md) +- [Project Overview](doc/README.md) ## 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. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..c36da19 --- /dev/null +++ b/build.sh @@ -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" diff --git a/doc/MODULES.md b/doc/MODULES.md index 36020ea..d448735 100644 --- a/doc/MODULES.md +++ b/doc/MODULES.md @@ -10,42 +10,40 @@ This file provides a mapping of modules to their primary responsibilities and ke - **Sockets**: Definitions for all `SLICESOCK_*` classes. - **Nodes**: Definitions for all `SLICENODE_*` classes. - **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. - `_follow_reroute()`: Handles Blender's Reroute nodes during evaluation. - - **Error Handling**: `NodeException` for user-facing evaluation errors. - `registry.py`: - **Auto-Registration**: Utility to track classes decorated with `@register_class`. - - **Resource Management**: Logic for dynamic icon and timer registration. - `blender_manifest.toml`: - Metadata for the Blender Extension platform. - - Lists required wheels (Shapely, PyClipper) for different platforms. - `pyproject.toml`: - Standard Python project configuration. - - Lists development and runtime dependencies. ## `/core` (Geometry & Slicing) - `slice.py`: - `slice_at_range()`: Top-level slicing function that iterates through Z-heights. - `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. - - `segments_to_slice()`: Utility to convert raw intersection segments into closed loops using Pyclipper and the Even-Odd fill rule. + - `Pather`: Class that converts toolpaths into G-code strings. + - `segments_to_slice()`: Utility to convert raw intersection segments into closed loops using Pyclipper. - `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`: - - `crc32_array()`: High-performance checksum generation for NumPy arrays, used for internal caching and data validation. + - Geometry utility functions. ## `/infra` (Blender Integration) - `blender_mesh_capture.py`: - - `SlicingObject`: Represents a single mesh as a NumPy triangle array. Computes bounding boxes and checksums. - - `SlicingCollection` / `SlicingGroup`: Hierarchical wrappers for managing multiple meshes and their metadata (e.g., Wipe Tower location). - - `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. + - `SlicingObject`: Represents a single mesh as a NumPy triangle array. + - `capture_mesh_edges()`: Extracts edges from a Blender mesh for path-based workflows. -- `blender_bridge.py`: - - **Hierarchy Utilities**: Helpers like `get_all_children()` for traversing Blender's object tree. +- `blender_slice_nodes_generic.py`: + - Base classes for NodeSlicer nodes (`SliceNode`) and sockets (`SliceNodeSocket`). + - Core evaluation logic (`resolve_input`). diff --git a/doc/NODES.md b/doc/NODES.md index 07ec03a..093db53 100644 --- a/doc/NODES.md +++ b/doc/NODES.md @@ -8,23 +8,24 @@ | `SLICESOCK_Float` | `float` | Python float | Numeric value | | `SLICESOCK_Int` | `int` | Python int | Integer 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_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 | ## Detailed Data Types ### SlicingObject (`SLICESOCK_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. - `[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). -### Paths (`SLICESOCK_Paths`) -A `list[np.ndarray]` (each `(N, 3)`) containing toolpaths or slice results. -- **Content**: Can be open or closed segments. When generated from `Slice Shells`, they are guaranteed to be closed loops forming valid manifold boundaries. +### SlicePaths (`SLICESOCK_Paths`) +A custom list subclass `SlicePaths(list[np.ndarray])` containing toolpaths. +- **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. - **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. - **Format**: `G0` for travels, `G1` for extrusions. - **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 -### Input Nodes -- `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. +All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** category. -### Processing Nodes -- `SLICENODE_GetMesh`: Converts a Blender Object into internal `SlicingObject` format. -- `SLICENODE_SliceShells`: Slices a `SlicingObject` into 2D `Paths` at a given layer height. -- `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. -- `SLICENODE_GridInfill`: Generates a grid of lines clipped by boundary paths. -- `SLICENODE_PathsToGcode`: Converts `Paths` into G-code blocks. +### Data & Input +- **Object**: Blender object picker. +- **Float**: Single float input. +- **Integer**: Single integer input. +- **String**: Single string/path input. +- **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) -- `SLICENODE_JoinMesh`: Merges multiple `SlicingObject` instances into one. -- `SLICENODE_JoinPaths`: Merges multiple paths. -- `SLICENODE_JoinGcode`: Concatenates multiple G-code strings with newline separators. +### Geometry Processing +- **Get Mesh**: Converts a Blender Object into internal `SlicingObject` format. +- **Get Paths**: Extracts existing edges from a mesh as paths. +- **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) -- `SLICENODE_GcodeToFile`: Writes G-code string to a file on disk. -- `SLICENODE_GcodeToMesh`: Decodes G-code and creates a Blender mesh (Edges) for visualization/debugging. -- `SLICENODE_LoadGcode`: Reads a G-code file from disk into a string. -- `SLICENODE_Test`: Triggers evaluation for debugging purposes. +### Utility & Joiners +- **Join Mesh**: Merges multiple `SlicingObject` instances into one. +- **Join Paths**: Combines multiple `SlicePaths`. Supports Sequential or Interleaved modes. +- **Join Gcode**: Concatenates multiple G-code strings. +- **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. diff --git a/doc/USER_GUIDE.md b/doc/USER_GUIDE.md new file mode 100644 index 0000000..a9dde3b --- /dev/null +++ b/doc/USER_GUIDE.md @@ -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.