Skip to content

Commit 455ca38

Browse files
authored
Merge pull request #9 from vycdev/develop
Auto Paint Update
2 parents 5a25c2a + 776e8ee commit 455ca38

41 files changed

Lines changed: 6953 additions & 1418 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/settings.local.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(npm run build:*)",
5+
"Bash(npm run lint:*)"
6+
]
7+
}
8+
}

‎CLAUDE.md‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What is Kromacut
6+
7+
Kromacut is a browser-based tool for converting images into stacked, color-layered 3D prints (lithophanes). Users upload an image, reduce it to a small color palette via quantization algorithms, configure per-color layer heights and ordering, preview in 3D, and export to STL or 3MF for multi-material printing.
8+
9+
Key domain concepts:
10+
- **Transmission Distance (TD):** Models how light transmits through thin filament layers; used by the auto-paint algorithm to simulate multi-filament lithophane effects via Beer-Lambert law optical simulation.
11+
- **Greedy meshing:** Maximal rectangle algorithm generates optimized 3D geometry with separate wall generation to prevent T-junctions.
12+
- **Quantization algorithms:** Posterize, median-cut, K-means, octree, and Wu methods for color reduction.
13+
- **Dedithering:** Median-filter-like smoothing pass that replaces isolated dithered pixels with their most frequent neighbor color; runs as a pre-quantization step.
14+
- **Filament profiles:** Reusable auto-paint filament configurations persisted to localStorage, importable/exportable as `.kapp` JSON files.
15+
16+
## Commands
17+
18+
```bash
19+
npm run dev # Start Vite dev server
20+
npm run build # TypeScript check + Vite production build
21+
npm run lint # ESLint with zero warnings policy (--max-warnings=0)
22+
npm run lint:fix # ESLint with auto-fix
23+
npm run format # Prettier format all src files
24+
npm run preview # Preview production build locally
25+
```
26+
27+
No test framework is configured.
28+
29+
## Architecture
30+
31+
**Stack:** React 19 + TypeScript + Vite 7 + Three.js + Tailwind CSS v4 + Shadcn/Radix UI
32+
33+
**Path alias:** `@/*` maps to `./src/*`
34+
35+
### Code organization
36+
37+
- `src/types/index.ts` — Shared TypeScript types (`Swatch`, `Filament`, `ThreeDControlsStateShape`). Canonical location to avoid circular imports between lib modules and components.
38+
- `src/components/` — React UI components. `App.tsx` is the root, holds top-level state.
39+
- `ThreeDControls.tsx` — Orchestrator for 3D print settings; delegates to extracted sub-components and hooks.
40+
- `FilamentRow.tsx` — Individual filament row with color picker, TD input, and auto-estimate.
41+
- `PrintSettingsCard.tsx` — Print settings Card (pixel size, layer height, first layer height).
42+
- `PrintInstructions.tsx` — Print instructions Card (recommended settings, swap plan, copy button).
43+
- `AutoPaintTab.tsx` — Auto-paint tab content (profiles, filament list, max height, transition zones).
44+
- `src/components/ui/` — Shadcn/Radix primitive components (buttons, sliders, popovers, etc.).
45+
- `src/hooks/` — Custom hooks that encapsulate business logic and state management:
46+
- `useSwatches` — Async image histogram computation with cancellation
47+
- `useQuantize` — Color quantization algorithm dispatch
48+
- `useThreeScene` — Three.js scene setup, camera, controls, render loop
49+
- `useAppHandlers` — STL/3MF export orchestration, image download
50+
- `useImageHistory` — Undo/redo stack
51+
- `useDropzone` — Drag-and-drop file upload
52+
- `useHorizontalSplit` — Draggable horizontal splitter state via CSS custom properties
53+
- `useFilaments` — Filament CRUD state (add, remove, update)
54+
- `useProfileManager` — Profile save/load/delete/import/export for auto-paint filament configurations
55+
- `useColorSlicing` — Color order and per-color slice height reconciliation with layer-height snapping
56+
- `useSwapPlan` — Swap plan computation (manual and auto-paint modes) and clipboard copy
57+
- `useProcessingState` — Processing overlay state (quantizing, dedithering, progress, label)
58+
- `useBuildWarning` — Build warning logic, image dimension tracking, 3D state and rebuild signal
59+
- `src/lib/` — Pure algorithmic logic (no React dependencies):
60+
- `algorithms.ts` — All quantization algorithms (K-means, median-cut, octree, Wu)
61+
- `meshing.ts` — Greedy mesh generation for 3D geometry
62+
- `autoPaint.ts` — Auto-paint layer stacking algorithm using TD and Beer-Lambert law
63+
- `exportStl.ts` — Binary STL file generation
64+
- `export3mf.ts` — 3MF multi-material export (uses JSZip)
65+
- `applyAdjustments.ts` — Image adjustment filters (exposure, contrast, saturation, etc.)
66+
- `color.ts` — RGB/HSL/Lab color space conversions
67+
- `colorUtils.ts` — Shared color utilities: `hexLuminance()` and `estimateTDFromColor()`
68+
- `profileManager.ts` — CRUD + localStorage persistence for auto-paint filament profiles; import/export as `.kapp` files
69+
- `printSettingsStorage.ts` — Print settings localStorage persistence (layer height, first layer height, pixel size)
70+
- `slicerDefaults.ts` — Default slicer metadata (layer height, infill, nozzle diameter) embedded in 3MF exports
71+
- `logger.ts` — Environment-aware logger; `debug` is a no-op in production
72+
- `compose-refs.ts` — Utility to compose multiple React refs into a single callback ref
73+
- `utils.ts` — Shadcn `cn()` helper (clsx + tailwind-merge)
74+
- `src/data/palettes.ts` — Predefined color palettes
75+
76+
### Data flow
77+
78+
State lives in `App.tsx` and flows down through props. The pipeline is:
79+
80+
1. Image upload → `useDropzone` / `useImageHistory`
81+
2. Adjustments applied → `applyAdjustments.ts` on offscreen canvases
82+
3. Dedithering (optional) → `DeditherPanel` runs a smoothing pass on quantized output
83+
4. Quantization → `useQuantize` dispatches to algorithm in `algorithms.ts`
84+
5. Swatch management → `useSwatches` computes histogram, user reorders via dnd-kit
85+
6. 3D preview → `useThreeScene` builds geometry via `meshing.ts`, renders with Three.js
86+
7. Export → `useAppHandlers` calls `exportStl.ts` or `export3mf.ts`
87+
88+
Canvas rendering uses dual offscreen canvases (`originalCanvasRef`, `processedCanvasRef`) for non-destructive adjustment processing. `CanvasPreview` and the Three.js scene expose imperative handles via `useImperativeHandle`.
89+
90+
### Key UI patterns
91+
92+
- **Processing overlay:** Managed by `useProcessingState` hook; unified progress indicator (`processingActive`, `processingLabel`, `processingProgress`) gates on quantization or dedithering and displays a progress bar.
93+
- **`adjustmentsEpoch` counter:** Forces `AdjustmentsPanel` to remount and reset sliders after baking adjustments into the image.
94+
- **Build warning dialog:** Managed by `useBuildWarning` hook; an `AlertDialog` warns before building 3D geometry when layer count exceeds 64 or pixel count exceeds 2.5M.
95+
- **`ResizableSplitter` layout:** The main 2-pane layout uses a percentage-based draggable splitter (30% default, 20–50% range).
96+
- **Auto-paint persistence:** `threeDState` (filaments, `paintMode: 'manual' | 'autopaint'`) is persisted to localStorage under `kromacut.autopaint.v1` with legacy migration.
97+
- **3MF export enrichment:** Exports embed `layerHeight`, `firstLayerHeight`, auto-paint filament colors, and slicer defaults from `slicerDefaults.ts`.
98+
99+
### Rendering details
100+
101+
- Three.js BufferGeometry with per-face (non-indexed) triangles so each color slice is a solid block.
102+
- `CanvasTexture` with `NearestFilter` and disabled mipmaps for crisp pixel mapping.
103+
- STL/3MF export uses chunked processing with `setTimeout(_, 0)` to keep the UI responsive.
104+
105+
## Code style
106+
107+
- **Prettier:** 4-space indentation, single quotes, trailing commas (es5), semicolons, 100-char print width.
108+
- **ESLint:** Zero warnings policy. `no-console` warns (allows `warn`/`error`). Unused vars warn (underscore-prefixed args ignored).
109+
- **Naming:** PascalCase for components/types, camelCase for hooks/functions, UPPER_SNAKE_CASE for constants.
110+
- **Hooks-first architecture:** No class components. Business logic extracted into custom hooks. Event handlers stabilized with `useCallback`.
111+
- **TypeScript strict mode** enabled. Explicit interfaces for component props and shared types.
112+

‎README.md‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,65 @@ 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+
## Auto-paint (Preview)
66+
67+
Auto-paint is an automated layer-generation mode that replaces the manual palette/swatch workflow. Instead of reducing an image to a fixed number of colors and manually tuning per-color slice heights, you define a set of **filaments** (each with a color and a Transmission Distance) and let the algorithm compute the optimal layer stack automatically.
68+
69+
### Core concepts
70+
71+
- **Filaments**: Each filament has a hex color and a **Transmission Distance (TD)** value (in mm). TD describes how translucent the filament is — at a thickness equal to TD, only ~10% of light passes through. Dark/opaque filaments have low TD (e.g. 0.5 mm); light/translucent filaments have high TD (e.g. 6+ mm). When you add a filament without specifying a TD, Kromacut estimates one from the color's luminance and saturation.
72+
- **Beer-Lambert optical blending**: The algorithm simulates how light transmits through stacked filament layers using the Beer-Lambert law: `transmission = 10^(-thickness / TD)`. This physically models the color you see when printing thin semi-transparent layers on top of each other.
73+
- **Transition zones**: Each filament in the stack needs enough vertical space to visually transition from the color below it to its own pure color. The algorithm simulates adding layers one at a time until the blended color converges (DeltaE < 2.3 — the "just noticeable difference" threshold) or opacity exceeds 85%. The result is a set of transition zones, each with a start height, end height, and the filament used.
74+
- **Luminance-to-height mapping**: Once the transition zones are computed, each pixel's brightness is mapped to a target height in the model. Dark pixels get the minimum height (base layer only), bright pixels get the full height (all layers), and mid-tones fall proportionally in between. This produces the characteristic lithophane-style relief where image brightness = model thickness.
75+
76+
### How it works (step by step)
77+
78+
1. **Define filaments** — Add your filament colors and Transmission Distances in the Auto-paint tab. Use the color picker and TD input for each filament row.
79+
2. **Filament ordering** — By default filaments are sorted by luminance (darkest on the bottom, lightest on top). With **Enhanced color matching** enabled, the algorithm evaluates orderings to find the one that best reproduces the image's color palette.
80+
3. **Transition zone calculation** — For each consecutive pair of filaments, the algorithm simulates Beer-Lambert blending layer-by-layer until the color converges. The first (darkest) filament gets a foundation zone thick enough to be ~95% opaque (`TD × 1.3`).
81+
4. **Height compression** — If the ideal height exceeds a user-set **Max Height**, all zones are uniformly compressed. The UI shows which zones are compressed and by how much.
82+
5. **Virtual swatch generation** — The transition zones are sampled at each layer-height increment to produce a sequence of blended colors (virtual swatches). These drive the 3D preview and the height map, where each pixel maps to the layer whose blended color best matches.
83+
84+
### Options
85+
86+
| Option | Description |
87+
|---|---|
88+
| **Max Height** | Constrains the total model height (mm). When set below the auto-calculated ideal, zones are uniformly compressed. Leave blank or click `Auto` for the physics-derived default. |
89+
| **Enhanced color matching** | Optimizes filament ordering for best color reproduction rather than simple luminance sorting. For ≤6 filaments, all permutations of all non-empty subsets are evaluated exhaustively. For >6, a greedy heuristic builds the sequence one filament at a time. Scoring considers weighted DeltaE accuracy, height spread, layer count, and transition waste. |
90+
| **Allow repeated filament swaps** | (Requires Enhanced color matching) Allows a filament to appear more than once in the stack. This creates intermediate blended colors — for example, a thin white layer over red produces pink. The algorithm greedily inserts up to 4 extra swaps, each at the position that best improves the score. |
91+
| **Height dithering** | (Requires Enhanced color matching) Applies block-aware Floyd-Steinberg error diffusion to the quantized height map. Instead of sharp stair-steps between layer heights, dithering produces a stippled gradient that simulates intermediate heights, resulting in smoother tonal transitions in the print. Edge pixels between different heights are protected from dithering to avoid staircase artifacts. |
92+
| **Dither line width** | (Requires Height dithering) Controls the minimum dot size for the dither pattern in mm. This should roughly match your printer's line/nozzle width so dither dots are actually printable. Default: `0.42 mm`. |
93+
94+
### Filament profiles
95+
96+
Auto-paint configurations (filament lists) can be saved, loaded, and shared as **profiles**:
97+
98+
- **Save / Save New** — Persist the current filament set under a name. Overwrite an existing profile or create a new one.
99+
- **Load** — Select a saved profile from the dropdown to restore its filaments.
100+
- **Export** — Download the current filaments as a `.kapp` file (JSON) to share with others.
101+
- **Import** — Load a `.kapp` or `.json` profile file from disk.
102+
- **Delete** — Remove a saved profile.
103+
104+
Profiles are stored in browser `localStorage` and persist across sessions.
105+
106+
### Transition zones panel
107+
108+
When auto-paint is active and filaments are defined, the UI displays a **Transition Zones** panel showing:
109+
110+
- Each zone's filament color swatch and hex code.
111+
- The start and end height of each zone (in mm) and its thickness (Δ).
112+
- A **compressed** badge on zones that have been reduced below their ideal thickness due to a Max Height constraint.
113+
- Total model height and total number of physical layers.
114+
115+
### Quick start
116+
117+
1. Load an image into Kromacut.
118+
2. Switch to the **Auto-paint** tab (inside the 3D controls panel).
119+
3. Click **Add Filament** and configure each filament's color and TD to match your real filament stock.
120+
4. (Optional) Enable **Enhanced color matching** for better results with complex images.
121+
5. The 3D preview updates automatically. Adjust **Max Height** if the model is too tall.
122+
6. Export the model via **Download STL** or **Download 3MF** and follow the printed swap plan.
123+
65124
## 3MF Export (Preview)
66125

67126
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.

0 commit comments

Comments
 (0)