- Adds scroll-wheel precision adjustment on all numeric input fields
- Extracts shared applyLinkedValue in linkSlider for DRY event handling
- Adds data-wheel-decimals attribute for per-input precision override
- Trims trailing .0 in formatM() for cleaner triangle count display
- Resolves conflict: boundary-falloff moved to Masking section on main
- Split monolithic i18n.js into per-language files under js/i18n/
- Lazy-load translations via dynamic import() with caching
- Add French (fr) language support
- Add error handling in _loadLang with English fallback
- Remove duplicated lang.name from per-language files (registry is single source of truth)
- Add dev-time key validation (warns on localhost when keys are missing vs en.js)
- Add missing alerts.fileTooLarge key from main to all language files
- Await async initLang() in main.js (supported by type=module)
- Added a language dropdown selector to replace the previous button-based language toggle.
- Integrated boundary falloff settings into the preview material, allowing for smoother transitions between masked and unmasked areas.
- Updated shaders to utilize boundary falloff attributes for improved visual fidelity in bump-only previews.
- Refactored related CSS styles for the new dropdown and adjusted layout for better usability.
- Introduced new functions for computing boundary attributes and managing edge data textures.
Integrates PR #1 (Add Boundary Falloff / Live Preview) with additional
fixes and improvements:
- Boundary falloff with configurable distance slider
- Live preview with per-vertex and per-fragment falloff computation
- Smooth view-dependent shading for all mask types (exclude, include-only, angle)
- Mask-type-dependent falloff tinting (orange for user masks, grey for angle masks)
- Italian translations for Boundary Falloff
- Performance: skip falloff computation during active masking
- Fix: boundary vertices start at falloff factor 0
- Fix: trailing comma syntax errors in i18n.js
- Remove flat-shaded MeshLambertMaterial overlay that was covering the
custom shader output on user-masked faces (root cause of static shading)
- Pass smooth interpolated normal (vSmoothNormal) to fragment shader and
blend toward it on masked faces so they get smooth view-dependent
lighting instead of flat per-face shading
- Brighten user-mask color to warm orange (0.85, 0.40, 0.15) for better
visibility of lighting variation on masked surfaces
- Shader now handles all mask visualization consistently: exclude mode
(orange), include-only mode (orange for unselected), and angle mask
(grey) all receive identical smooth shading
- Track whether each boundary is from user masking or angle masking
via a new boundaryMaskTypeAttr vertex attribute (0=user, 1=angle)
- Pass vUserMask and vMaskType varyings to the fragment shader
- Use consistent teal base color for all surfaces so lighting is
uniform across masked and textured areas (fixes dark halo artifact)
- Tint the falloff gradient warm red-orange near user-painted masks
and neutral grey near angle-masked boundaries
Boundary vertices (shared between masked and unmasked faces) were
skipped during falloff computation, keeping their falloffAttr at 1.0.
This created a ring of full-intensity texture at the mask border before
the falloff gradient started. Now these vertices correctly get factor 0
(distance to boundary = 0), matching the export behavior.
Replace fixed-interval yields (every 4096 iterations) with time-based
yields (every ~100ms of wall time). In foreground tabs this means ~10
yields per second instead of ~50-200, with identical UI responsiveness.
In background tabs where setTimeout(0) is throttled to ~1s, this
reduces overhead from ~200 wasted seconds to ~10 — the export runs
nearly as fast in the background as in the foreground.
Addresses #2 (background tab resource allocation).
Round 2 of performance and correctness improvements:
- Spatial grid index for brush painting: forEachTriInSphere now queries
only nearby grid cells instead of scanning all triangles. ~5.7x faster
for brush operations on 68k+ tri meshes.
- Decimation overflow fix: hasLinkViolation used a fixed 0x200000
multiplier for vertex-pair keys, overflowing at >2M vertices.
Now uses dynamic multiplier based on actual vertex count.
- Decimation determinant threshold: solveQ used absolute 1e-10 which
fails for large coordinates. Now relative to matrix element magnitude.
- 3MF triangle index validation: bounds-check all parsed indices against
vertex count, throw clear error on corrupt files instead of silent NaN.
- File size limit: reject files >500 MB before loading into memory,
prevents browser tab crash on oversized files.
- Accessibility: preset swatches now keyboard-navigable (role=button,
tabindex=0, Enter/Space to select). Modal dialogs trap focus and
close on Escape.
- Ctrl+click straight line tool: click to set start point, Ctrl+click
to paint a straight line between points. Ctrl+hover shows preview.
- Precision masking available for radius brush mode.
- Spatial grid rebuilt when entering/leaving precision mode.
Write binary STL directly from BufferGeometry typed arrays using
Uint8Array.set() bulk copies. Eliminates the Three.js STLExporter
overhead: no Mesh/Material creation, no identity matrix multiplication,
no redundant normal recomputation, no per-float DataView calls.
- SoAHeap: hole method for bubbleUp/sinkDown (half the typed-array
writes per heap level vs the old swap approach).
- Seed dedup: Number keys instead of BigInt when vertex count < 94M
(10-50x faster per key). BigInt fallback for extreme meshes.
- addCreaseQuadrics: encode face pairs as negative numbers instead
of allocating small arrays per edge.
- Guard 3 (checkFlipped): single branch for corner index instead of
9 ternary operations per face.
- Yield interval kept at 4096 with setTimeout(0) for reliable UI updates.
- Render loop now only calls renderer.render() when the scene actually
changed (needsRender flag + requestRender export). Idle GPU usage
drops to near zero.
- Disabled shadow map (no receiver in scene, wasted a full render pass).
- Reuse overlay materials instead of creating new ones every paint frame.
- Dispose CanvasTexture in getEffectiveMapEntry (VRAM leak on every
slider change).
- Dispose axes/dimension geometry on model reload.
- Reuse Vector3/Quaternion temp objects in pointer/touch/wheel handlers
instead of allocating ~10 objects per mouse event.
- RAF-batch mousemove for hover/cursor, keep paint events immediate.
- Reuse faceMask buffer attribute when size matches.
- Cache getEffectiveMapEntry result (skip canvas tiling+blur when
texture and smoothing haven't changed).
- addSmoothNormals: same dedup+flat-array approach as displacement.
Displacement, subdivision and exclusion all used template-string keys
for vertex dedup and edge lookup maps. Replaced with a single numeric
dedup pass + flat typed arrays (Float64Array / Uint8Array), cutting
displacement time by ~2.5x and subdivision by ~2.4x on a 68k tri STL.
- displacement.js: vertex dedup → flat arrays for smooth normals,
zone areas, masked fractions, displacement cache, excluded set
- subdivision.js: numeric edge keys (a*maxV+b) instead of template strings
- exclusion.js: numeric edge keys, BFS queue.shift() → index pointer,
adjacency Map → Array, TypedArray.set() for overlay copy
- mapping.js: Math.pow(x,4) → x²·x², cos/sin cached per computeUV call,
applyTransform signature changed to accept precomputed cos/sin
- Added stripes.png and stripes_02.png with metadata from Adobe Photoshop.
- Included two new woodgrain textures (woodgrain_02.jpg and woodgrain_03.jpg) for enhanced visual variety.