Skip to content

Commit f57f504

Browse files
committed
Merge branch 'develop'
1 parent 7d65d3e commit f57f504

19 files changed

Lines changed: 1360 additions & 799 deletions

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Another minimal test you can try yourself in the app header: the Transmission Di
3434
- Per-color slice heights and a configurable base slice height.
3535
- Reorder colors with drag-and-drop to control stack order (darkest → lightest default ordering).
3636
- Live 2D preview and a 3D stacked preview rendered with three.js.
37-
- 3D model export to binary STL (suitable for slicers).
37+
- 3D model export to binary STL or 3MF (Preview) suitable for multi-material slicers.
3838
- Plain-text 3D print instructions that describe layer heights and exact layers where filament swaps are required.
3939
- Copy-to-clipboard button for the print instructions (produces a clean, copyable plain-text plan).
4040

@@ -52,7 +52,7 @@ Another minimal test you can try yourself in the app header: the Transmission Di
5252
- Adjust quantization settings to reduce to the desired number of colors.
5353
- Tweak or replace swatches using the color pickers in the Swatches panel.
5454
- Open the 3D panel to configure per-color slice heights, base slice height, pixel size, and the color order.
55-
- When ready, click `Download STL` (in the preview-actions bar when in 3D mode) to export a binary STL file suitable for slicing.
55+
- When ready, click `Download STL` or `Download 3MF` (in the preview-actions bar when in 3D mode) to export your model.
5656
- Use the `Copy` button in the 3D controls to copy a plain-text print plan that lists layer heights and swap layers (hex codes are followed by friendly color names where available).
5757

5858
## 3D / printing specifics and tips
@@ -62,6 +62,12 @@ Another minimal test you can try yourself in the app header: the Transmission Di
6262
- Layer height used to compute the exact layer numbers at which color swaps happen in the plain-text plan.
6363
- Per-color slice heights are snapped/multiplied to sensible values relative to `layerHeight` when the swatches change or are initialized.
6464

65+
## 3MF Export (Preview)
66+
67+
Kromacut now supports exporting directly to `.3mf` format. This file format preserves color information by splitting the model into separate objects for each color, automatically assigned to different extruders/filaments.
68+
69+
**Disclaimer:** This feature is currently in **PREVIEW**. While Kromacut preserves colors, the rest of the settings in the slicer profile should be manually adjusted. Please report any issues or weird behaviors you encounter on the GitHub Issues page.
70+
6571
## Transmission Distance (TD) — what it is and how to use it here
6672

6773
Transmission Distance (TD) is the concept HueForge uses to produce perceptual intermediate shades by stacking translucent filament layers: instead of relying purely on opaque color pigments, TD models how light transmits through thin layers of filament and how stacking different colors (and varying thickness) produces new perceived colors. HueForge does a lot of this work automatically for you (generating intermediate shades and mapping them to layer swaps). For a full conceptual description see the HueForge blog: https://shop.thehueforge.com/blogs/news/what-is-hueforge

package-lock.json

Lines changed: 136 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
"@dnd-kit/sortable": "^10.0.0",
1818
"@dnd-kit/utilities": "^3.2.2",
1919
"@radix-ui/react-label": "^2.1.7",
20+
"@radix-ui/react-popover": "^1.1.15",
2021
"@radix-ui/react-select": "^2.2.6",
2122
"@radix-ui/react-separator": "^1.1.7",
2223
"@radix-ui/react-slider": "^1.3.6",
2324
"@radix-ui/react-slot": "^1.2.3",
2425
"@radix-ui/react-tabs": "^1.1.13",
26+
"@types/jszip": "^3.4.0",
2527
"class-variance-authority": "^0.7.1",
2628
"clsx": "^2.1.1",
29+
"jszip": "^3.10.1",
2730
"lucide-react": "^0.546.0",
2831
"react": "^19.1.1",
2932
"react-colorful": "^5.6.1",

src/App.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import Header from './components/Header';
1818
import ModeTabs from './components/ModeTabs';
1919
import PreviewActions from './components/PreviewActions';
2020
import { useDropzone } from './hooks/useDropzone';
21-
import { exportMeshToStlBlob } from './lib/exportStl';
21+
import { exportObjectToStlBlob } from './lib/exportStl';
22+
import { exportObjectTo3MFBlob } from './lib/export3mf';
2223
import { useAppHandlers } from './hooks/useAppHandlers';
2324
import ResizableSplitter from './components/ResizableSplitter';
2425
import { ControlsPanel } from './components/ControlsPanel';
@@ -133,15 +134,20 @@ function App(): React.ReactElement | null {
133134
const layoutRef = useRef<HTMLDivElement | null>(null);
134135

135136
const dropzone = useDropzone({ enabled: mode === '2d', onFile: handleFiles });
136-
const { onExportImage, onExportStl, onSwatchApply, onSwatchDelete } = useAppHandlers({
137+
const { onExportImage, onExportStl, onExport3MF, onSwatchApply, onSwatchDelete } = useAppHandlers({
137138
canvasPreviewRef,
138139
imageSrc,
139140
invalidate,
140141
setImage,
141142
setExportingSTL,
142143
setExportProgress,
143144
exportingSTL,
144-
exportMeshToStlBlob,
145+
exportObjectToStlBlob,
146+
exportObjectTo3MFBlob: (obj) =>
147+
exportObjectTo3MFBlob(obj, {
148+
layerHeight: threeDState.layerHeight,
149+
firstLayerHeight: threeDState.slicerFirstLayerHeight,
150+
}),
145151
applyQuantize,
146152
swatches,
147153
});
@@ -298,6 +304,7 @@ function App(): React.ReactElement | null {
298304
imageSrc={imageSrc}
299305
baseSliceHeight={0}
300306
layerHeight={threeDState.layerHeight}
307+
slicerFirstLayerHeight={threeDState.slicerFirstLayerHeight}
301308
colorSliceHeights={threeDState.colorSliceHeights}
302309
colorOrder={threeDState.colorOrder}
303310
swatches={threeDState.filteredSwatches}
@@ -333,6 +340,7 @@ function App(): React.ReactElement | null {
333340
onClear={clear}
334341
onExportImage={onExportImage}
335342
onExportStl={onExportStl}
343+
onExport3MF={onExport3MF}
336344
/>
337345
</div>
338346
</main>

src/components/ControlsPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const ControlsPanel: React.FC<Props> = ({
5757
<Select
5858
value={selectedPalette}
5959
onValueChange={(paletteId) => {
60-
const palette = PALETTES.find((p: any) => p.id === paletteId);
60+
const palette = PALETTES.find((p) => p.id === paletteId);
6161
if (palette) {
6262
onPaletteSelect(paletteId, palette.size);
6363
}
@@ -67,7 +67,7 @@ export const ControlsPanel: React.FC<Props> = ({
6767
<SelectValue placeholder="Select a palette" />
6868
</SelectTrigger>
6969
<SelectContent className="max-h-48 overflow-y-auto">
70-
{PALETTES.map((p: any) => (
70+
{PALETTES.map((p) => (
7171
<SelectItem key={p.id} value={p.id}>
7272
<div className="flex items-center gap-2">
7373
<span>

0 commit comments

Comments
 (0)