Refactor code structure for improved readability and maintainability
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 CNCKitchen (Stefan Elitz)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,62 @@
|
||||
# STL Texturizer
|
||||
|
||||
A browser-based tool for applying surface displacement textures to STL files — no installation required.
|
||||
|
||||
Load any `.stl` file, pick a texture, tune the parameters, and export a new displaced STL ready for slicing.
|
||||
|
||||
## Features
|
||||
|
||||
- **Texture presets** — 12 built-in seamless textures (basket, brick, bubble, crystal, hexagon, knitting, knurling, leather, weave, wood, noise, and more)
|
||||
- **Custom textures** — upload your own image as a displacement map
|
||||
- **Projection modes** — Triplanar, Cubic (Box), Cylindrical, Spherical, Planar XY/XZ/YZ
|
||||
- **UV transform** — independent U/V scale, offset, and rotation controls
|
||||
- **Displacement amplitude** — fine-tune depth from subtle grain to deep relief
|
||||
- **Surface mask** — skip horizontal top/bottom faces to keep flat surfaces clean
|
||||
- **Surface exclusions / inclusions** — paint individual faces with a brush or bucket-fill to exclude or exclusively include them from displacement
|
||||
- **Live preview** — real-time textured 3D preview with orbit/pan/zoom controls
|
||||
- **Mesh subdivision** — auto-subdivides coarse geometry before displacement for smoother results
|
||||
- **Export** — downloads a new binary STL with displacement baked in
|
||||
- **Light / Dark theme** — persisted per browser
|
||||
- **Multilingual** — English and German UI
|
||||
|
||||
## Usage
|
||||
|
||||
1. Open `index.html` in a modern browser (Chrome, Edge, Firefox, Safari).
|
||||
2. Drop an STL file onto the viewport or click **Load STL…**.
|
||||
3. Select a texture preset from the sidebar (or upload a custom image).
|
||||
4. Adjust projection mode, UV scale, offset, rotation, and amplitude.
|
||||
5. Optionally mask or exclude surfaces with the brush/fill tools.
|
||||
6. Click **Export STL** to download the displaced mesh.
|
||||
|
||||
> **Note:** All processing runs entirely in the browser — no data is uploaded to any server.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
index.html # Main entry point
|
||||
style.css # Styles (light/dark theme)
|
||||
textures/ # Built-in JPG displacement map images
|
||||
js/
|
||||
main.js # App bootstrap & UI wiring
|
||||
viewer.js # Three.js scene / camera / controls
|
||||
stlLoader.js # Binary & ASCII STL parser
|
||||
presetTextures.js # Built-in texture presets + custom upload
|
||||
previewMaterial.js# Three.js material for live preview
|
||||
mapping.js # UV projection logic
|
||||
displacement.js # Vertex displacement baking
|
||||
subdivision.js # Mesh subdivision
|
||||
decimation.js # QEM mesh decimation
|
||||
exclusion.js # Face exclusion / inclusion painting
|
||||
exporter.js # Binary STL export
|
||||
i18n.js # Translations (EN / DE)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Loaded via CDN — no build step needed:
|
||||
|
||||
- [Three.js](https://threejs.org/) v0.170.0
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE).
|
||||
@@ -9,7 +9,7 @@
|
||||
// Apply saved theme before first paint to avoid flash
|
||||
(function() {
|
||||
const t = localStorage.getItem('stlt-theme');
|
||||
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
|
||||
if (t !== 'dark') document.documentElement.setAttribute('data-theme', 'light');
|
||||
})();
|
||||
// Apply saved language before first paint to avoid flash
|
||||
(function() {
|
||||
|
||||
@@ -102,8 +102,8 @@ export const TRANSLATIONS = {
|
||||
'progress.subdividing': 'Subdividing mesh\u2026',
|
||||
'progress.applyingDisplacement': 'Applying displacement to {n} triangles\u2026',
|
||||
'progress.displacingVertices': 'Displacing vertices\u2026',
|
||||
'progress.decimatingTo': 'Decimating {from} \u2192 {to} triangles\u2026',
|
||||
'progress.decimating': 'Decimating: {cur} \u2192 {to} triangles',
|
||||
'progress.decimatingTo': 'Simplifying {from} \u2192 {to} triangles\u2026',
|
||||
'progress.decimating': 'Simplifying: {cur} \u2192 {to} triangles',
|
||||
'progress.writingStl': 'Writing STL\u2026',
|
||||
'progress.done': 'Done!',
|
||||
'progress.processing': 'Processing\u2026',
|
||||
@@ -212,7 +212,7 @@ export const TRANSLATIONS = {
|
||||
|
||||
// Export section
|
||||
'sections.export': 'Export \u24d8',
|
||||
'tooltips.export': 'Kleinere Kantenl\u00e4nge = feineres Verschiebungsdetail. Die Ausgabe wird dann auf das Dreieckslimit dezimiert.',
|
||||
'tooltips.export': 'Kleinere Kantenl\u00e4nge = mehr Texturdetails. Die Ausgabe wird dann auf das Dreieckslimit vereinfacht.',
|
||||
'labels.resolution': 'Aufl\u00f6sung',
|
||||
'tooltips.resolution': 'Kanten l\u00e4nger als dieser Wert werden beim Export unterteilt',
|
||||
'labels.outputTriangles': 'Max Dreiecke',
|
||||
@@ -221,11 +221,11 @@ export const TRANSLATIONS = {
|
||||
'ui.exportStl': 'STL exportieren',
|
||||
|
||||
// Export progress stages
|
||||
'progress.subdividing': 'Netz wird unterteilt\u2026',
|
||||
'progress.applyingDisplacement': 'Verschiebung auf {n} Dreiecke anwenden\u2026',
|
||||
'progress.displacingVertices': 'Vertices werden verschoben\u2026',
|
||||
'progress.decimatingTo': '{from} \u2192 {to} Dreiecke dezimieren\u2026',
|
||||
'progress.decimating': 'Dezimieren: {cur} \u2192 {to} Dreiecke',
|
||||
'progress.subdividing': 'Netz wird verfeinert\u2026',
|
||||
'progress.applyingDisplacement': 'Textur auf {n} Dreiecke anwenden\u2026',
|
||||
'progress.displacingVertices': 'Punkte werden verschoben\u2026',
|
||||
'progress.decimatingTo': '{from} \u2192 {to} Dreiecke vereinfachen\u2026',
|
||||
'progress.decimating': 'Vereinfachen: {cur} \u2192 {to} Dreiecke',
|
||||
'progress.writingStl': 'STL schreiben\u2026',
|
||||
'progress.done': 'Fertig!',
|
||||
'progress.processing': 'Verarbeitung\u2026',
|
||||
|
||||
@@ -14,18 +14,18 @@ function makeCanvas(size = SIZE) {
|
||||
// ── Image-based presets ───────────────────────────────────────────────────────
|
||||
|
||||
const IMAGE_PRESETS = [
|
||||
{ name: 'Basket', url: 'basket.jpg' },
|
||||
{ name: 'Brick', url: 'brick.jpg' },
|
||||
{ name: 'Bubble', url: 'bubble.jpg' },
|
||||
{ name: 'Crystal', url: 'crystal.jpg' },
|
||||
{ name: 'Hexagon', url: 'hexagon.jpg' },
|
||||
{ name: 'Knitting', url: 'knitting.jpg' },
|
||||
{ name: 'Knurling', url: 'knurling.jpg' },
|
||||
{ name: 'Leather', url: 'leather.jpg' },
|
||||
{ name: 'Leather 2', url: 'leather2.jpg' },
|
||||
{ name: 'Weave', url: 'weave.jpg' },
|
||||
{ name: 'Wood', url: 'wood.jpg' },
|
||||
{ name: 'Noise', url: 'noise.jpg' },
|
||||
{ name: 'Basket', url: 'textures/basket.jpg' },
|
||||
{ name: 'Brick', url: 'textures/brick.jpg' },
|
||||
{ name: 'Bubble', url: 'textures/bubble.jpg' },
|
||||
{ name: 'Crystal', url: 'textures/crystal.jpg' },
|
||||
{ name: 'Hexagon', url: 'textures/hexagon.jpg' },
|
||||
{ name: 'Knitting', url: 'textures/knitting.jpg' },
|
||||
{ name: 'Knurling', url: 'textures/knurling.jpg' },
|
||||
{ name: 'Leather', url: 'textures/leather.jpg' },
|
||||
{ name: 'Leather 2', url: 'textures/leather2.jpg' },
|
||||
{ name: 'Weave', url: 'textures/weave.jpg' },
|
||||
{ name: 'Wood', url: 'textures/wood.jpg' },
|
||||
{ name: 'Noise', url: 'textures/noise.jpg' },
|
||||
];
|
||||
|
||||
function loadImagePreset({ name, url }) {
|
||||
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 512 KiB After Width: | Height: | Size: 512 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 175 KiB |