mirror of
https://github.com/CNCKitchen/stlTexturizer.git
synced 2026-04-07 22:11:32 +00:00
fix: spatial index, decimation overflow, input validation, accessibility
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.
This commit is contained in:
@@ -3,6 +3,8 @@ import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
|
||||
import { unzipSync } from 'fflate';
|
||||
import * as THREE from 'three';
|
||||
|
||||
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
const stlLoader = new STLLoader();
|
||||
const objLoader = new OBJLoader();
|
||||
|
||||
@@ -12,6 +14,11 @@ const objLoader = new OBJLoader();
|
||||
* The geometry is translated so its bounding-box centre is at the world origin.
|
||||
*/
|
||||
export function loadSTLFile(file) {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return Promise.reject(new Error(
|
||||
'File too large (' + Math.round(file.size / 1024 / 1024) + ' MB). Maximum supported: ' + (MAX_FILE_SIZE / 1024 / 1024) + ' MB.'
|
||||
));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
@@ -73,6 +80,11 @@ export function getTriangleCount(geometry) {
|
||||
* Returns { geometry, bounds }.
|
||||
*/
|
||||
export function loadOBJFile(file) {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return Promise.reject(new Error(
|
||||
'File too large (' + Math.round(file.size / 1024 / 1024) + ' MB). Maximum supported: ' + (MAX_FILE_SIZE / 1024 / 1024) + ' MB.'
|
||||
));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
@@ -99,6 +111,11 @@ export function loadOBJFile(file) {
|
||||
* Returns { geometry, bounds }.
|
||||
*/
|
||||
export function load3MFFile(file) {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return Promise.reject(new Error(
|
||||
'File too large (' + Math.round(file.size / 1024 / 1024) + ' MB). Maximum supported: ' + (MAX_FILE_SIZE / 1024 / 1024) + ' MB.'
|
||||
));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
@@ -170,6 +187,13 @@ function parse3MF(data) {
|
||||
triangles[i * 3 + 2] = parseInt(triEls[i].getAttribute('v3'), 10);
|
||||
}
|
||||
|
||||
const vertCount = vertEls.length;
|
||||
for (let i = 0; i < triangles.length; i++) {
|
||||
if (triangles[i] < 0 || triangles[i] >= vertCount || isNaN(triangles[i])) {
|
||||
throw new Error('Invalid triangle index in 3MF file');
|
||||
}
|
||||
}
|
||||
|
||||
// Normalise path for lookup (strip leading slash, use forward slashes)
|
||||
const normPath = path.replace(/^\//, '').replace(/\\/g, '/');
|
||||
objectMap.set(normPath + '#' + id, { vertices, triangles });
|
||||
|
||||
Reference in New Issue
Block a user