From eef77a6f4c535883d07d7ab0114a5681e6c29ccd Mon Sep 17 00:00:00 2001 From: mitchellhansen Date: Sun, 26 Apr 2026 22:20:15 -0700 Subject: [PATCH] feat: Hull/Fill nodes freely addable+deletable; remove Fill view tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NodeGraph: isFixed now only applies to Source — Hull and Fill are fully deletable, supporting arbitrary multi-pass branching (multiple Hull/Fill chains from the same detection graph). addNode() handles Hull (defaultHullParams) and Fill (defaultFillParams). Toolbar gains teal '+ Hull' and purple '+ Fill' buttons alongside the existing Kernel/Combine buttons. App.jsx: Remove 'fill' from VIEW_MODES — fill output is visible in the detection graph's Fill node thumbnail and in the G-code view. Remove the dead case 'fill': branch from the viewport refresh switch. Co-Authored-By: Claude Sonnet 4.6 --- src-frontend/src/App.jsx | 6 +----- src-frontend/src/components/NodeGraph.jsx | 25 ++++++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src-frontend/src/App.jsx b/src-frontend/src/App.jsx index baf444dd..7dc63615 100644 --- a/src-frontend/src/App.jsx +++ b/src-frontend/src/App.jsx @@ -8,7 +8,7 @@ import { defaultPass, defaultGcodeConfig, PAPER_SIZES } from './store.js' import * as tauri from './hooks/useTauri.js' import { useFps } from './hooks/useFps.js' -const VIEW_MODES = ['source', 'detection', 'contours', 'fill', 'gcode'] +const VIEW_MODES = ['source', 'detection', 'contours', 'gcode'] export default function App() { const [image, setImage] = useState(null) @@ -101,10 +101,6 @@ export default function App() { setDisplayB64(null) } break - case 'fill': - // Fill uses the same full-res canvas rendering as gcode (via strokes prop). - setDisplayB64(null) - break case 'gcode': if (passes.some(p => p.strokeCount > 0)) { try { diff --git a/src-frontend/src/components/NodeGraph.jsx b/src-frontend/src/components/NodeGraph.jsx index 29950959..147e9508 100644 --- a/src-frontend/src/components/NodeGraph.jsx +++ b/src-frontend/src/components/NodeGraph.jsx @@ -189,9 +189,10 @@ export default function NodeGraph({ graph, onChange, nodePreviews, sourceImageB6 const x = (r.width / 2 - p.x) / z - NODE_W / 2 const y = (r.height / 2 - p.y) / z - 60 const id = newNodeId(kind) - const node = kind === 'Kernel' - ? { id, kind, x, y, ...defaultKernelProps() } - : { id, kind, x, y, blend_mode: 'Average', inputCount: 2 } + const node = kind === 'Kernel' ? { id, kind, x, y, ...defaultKernelProps() } + : kind === 'Hull' ? { id, kind, x, y, ...defaultHullParams() } + : kind === 'Fill' ? { id, kind, x, y, ...defaultFillParams() } + : { id, kind, x, y, blend_mode: 'Average', inputCount: 2 } const g = graphRef.current onChangeRef.current({ ...g, nodes: [...g.nodes, node] }) } @@ -222,7 +223,7 @@ export default function NodeGraph({ graph, onChange, nodePreviews, sourceImageB6 // ── Node rendering ───────────────────────────────────────────────────────── function renderNode(node) { - const isFixed = node.kind === 'Source' || node.kind === 'Hull' || node.kind === 'Fill' + const isFixed = node.kind === 'Source' const inputCnt = node.kind === 'Combine' ? (node.inputCount ?? 2) : (node.kind === 'Kernel' || node.kind === 'Output' || node.kind === 'Hull' || node.kind === 'Fill') ? 1 : 0 const hasOut = node.kind !== 'Output' && node.kind !== 'Hull' && node.kind !== 'Fill' @@ -440,12 +441,16 @@ export default function NodeGraph({ graph, onChange, nodePreviews, sourceImageB6 > {/* Toolbar */}
- - + {[ + ['Kernel', '#374151', '#94a3b8'], + ['Combine', '#374151', '#94a3b8'], + ['Hull', '#0d9488', '#5eead4'], + ['Fill', '#7c3aed', '#c4b5fd'], + ].map(([kind, border, color]) => ( + + ))} scroll=zoom · drag=pan · click wire=delete