From 6d4a91f82cc241a3f32e767014944945d77a7d79 Mon Sep 17 00:00:00 2001 From: dementive <87823030+dementive@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:12:12 -0400 Subject: [PATCH 1/5] Cylinder stock rendering --- .changeset/cylinder-stock.md | 5 + examples/react-viewer/src/main.tsx | 160 +++++++++----- examples/react-viewer/tests/toolbar.spec.ts | 18 ++ packages/viewer/README.md | 33 ++- packages/viewer/src/index.ts | 18 +- packages/viewer/src/render/stock.ts | 182 ++++++++++++++-- packages/viewer/src/stock.tsx | 45 +++- packages/viewer/tests/stock.test.ts | 222 +++++++++++++++++++- 8 files changed, 605 insertions(+), 78 deletions(-) create mode 100644 .changeset/cylinder-stock.md diff --git a/.changeset/cylinder-stock.md b/.changeset/cylinder-stock.md new file mode 100644 index 0000000..993af04 --- /dev/null +++ b/.changeset/cylinder-stock.md @@ -0,0 +1,5 @@ +--- +'@toolpath/viewer': minor +--- + +Add `CylinderStock` for round stock along any axis, `fixedCylinderStock` for a fixed-cylinder preview placed by the same position rule as fixed-box stock, and `OrientedBoxStock` for a block squared to a frame of its own, such as a plan's setup-squared stock box. Stock outlines are now built only when shown. diff --git a/examples/react-viewer/src/main.tsx b/examples/react-viewer/src/main.tsx index 9264763..371318f 100644 --- a/examples/react-viewer/src/main.tsx +++ b/examples/react-viewer/src/main.tsx @@ -6,7 +6,9 @@ import { Axes, Banana, BoxStock, + CylinderStock, fixedBoxStockBounds, + fixedCylinderStock, directionHighlights, directionLabel, directionColor, @@ -215,7 +217,11 @@ const App = () => { const [showDirections, setShowDirections] = useState(false) const [focus, setFocus] = useState(false) const [wireframe, setWireframe] = useState(false) + const [stockShape, setStockShape] = useState<'box' | 'cylinder'>( + params.get('stockShape') === 'cylinder' ? 'cylinder' : 'box', + ) const [stockDimensions, setStockDimensions] = useState({ x: 25.908, y: 25.908, z: 25.908 }) + const [stockCylinder, setStockCylinder] = useState({ diameter: 38.1, length: 25.908 }) const [stockPosition, setStockPosition] = useState('model_centered') const [stockPositionOffset, setStockPositionOffset] = useState(0) const stockSize = useMemo( @@ -228,6 +234,10 @@ const App = () => { ).getSize(new THREE.Vector3()), [part.geometry, stockDimensions, stockPosition, stockPositionOffset], ) + const cylinderFigure = useMemo( + () => fixedCylinderStock(part.geometry, stockCylinder, stockPosition, stockPositionOffset), + [part.geometry, stockCylinder, stockPosition, stockPositionOffset], + ) const highlights = useMemo( () => (showDirections ? directionHighlights(part.model, direction) : []), [direction, part.model, showDirections], @@ -346,57 +356,106 @@ const App = () => {

{part.hint}

Stock:{' '} - {stockSize - .toArray() - .map((value) => value.toFixed(2)) - .join(' × ')}{' '} - mm (X × Y × Z) + {stockShape === 'cylinder' + ? `⌀${cylinderFigure.diameter.toFixed(2)} × ${cylinderFigure.length.toFixed(2)} mm (diameter × length)` + : `${stockSize + .toArray() + .map((value) => value.toFixed(2)) + .join(' × ')} mm (X × Y × Z)`}

- - + {stockShape === 'cylinder' ? ( + <> + + + + ) : null} + {stockShape === 'box' ? ( + <> + + + + + ) : null} - {stockShape === 'cylinder' ? ( + {stockSource === 'geometry' ? ( + + ) : null} + {stockSource === 'glb' ? ( + + ) : null} + {stockSource === 'geometry' && stockShape === 'cylinder' ? ( <> ) : null} - {stockShape === 'box' ? ( + {stockSource === 'geometry' && stockShape === 'box' ? ( <> ) : null} - - {stockPosition !== 'model_centered' ? ( + {stockSource === 'geometry' ? ( + + ) : null} + {stockSource === 'geometry' && stockPosition !== 'model_centered' ? ( ) : null}

- Fixed stock shape, dimensions and position. Values are millimetres. + {stockSource === 'glb' + ? 'Synthetic initial stock encoded by the services GLB writer, in part coordinates.' + : 'Fixed stock shape, dimensions and position. Values are millimetres.'}