Preserve all STL assembly faces
This commit is contained in:
@@ -18,6 +18,9 @@ to zoom. A cached STL copy is stored locally under the ignored `runtime/`
|
|||||||
directory so the model can be restored on the next launch.
|
directory so the model can be restored on the next launch.
|
||||||
STEP/STP CAD solids must first be tessellated/exported as STL because the
|
STEP/STP CAD solids must first be tessellated/exported as STL because the
|
||||||
embedded renderer intentionally does not include a full CAD kernel.
|
embedded renderer intentionally does not include a full CAD kernel.
|
||||||
|
Binary and ASCII STL faces are preserved in full; large assemblies are not
|
||||||
|
rendered by skipping triangles. Imports are limited to 30 MB to keep startup
|
||||||
|
and interactive orbiting responsive.
|
||||||
|
|
||||||
## Safety first
|
## Safety first
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
let vertices = null, frame = 0, yaw = 0, pitch = 0, zoom = 1;
|
let vertices = null, frame = 0, yaw = 0, pitch = 0, zoom = 1;
|
||||||
let dragging = false, lastX = 0, lastY = 0;
|
let dragging = false, lastX = 0, lastY = 0;
|
||||||
const renderScale = .62;
|
// Still below native resolution, but high enough for clean assembly contours.
|
||||||
|
const renderScale = .82;
|
||||||
|
|
||||||
function parseStl(buffer) {
|
function parseStl(buffer) {
|
||||||
const view = new DataView(buffer);
|
const view = new DataView(buffer);
|
||||||
@@ -12,8 +13,7 @@
|
|||||||
const binaryCount = buffer.byteLength >= 84 ? view.getUint32(80, true) : 0;
|
const binaryCount = buffer.byteLength >= 84 ? view.getUint32(80, true) : 0;
|
||||||
const isBinary = binaryCount > 0 && 84 + binaryCount * 50 <= buffer.byteLength;
|
const isBinary = binaryCount > 0 && 84 + binaryCount * 50 <= buffer.byteLength;
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
const stride = Math.max(1, Math.ceil(binaryCount / 14000));
|
for (let t = 0; t < binaryCount; t++) {
|
||||||
for (let t = 0; t < binaryCount; t += stride) {
|
|
||||||
const offset = 84 + t * 50 + 12;
|
const offset = 84 + t * 50 + 12;
|
||||||
for (let v = 0; v < 3; v++) {
|
for (let v = 0; v < 3; v++) {
|
||||||
raw.push(view.getFloat32(offset + v * 12, true));
|
raw.push(view.getFloat32(offset + v * 12, true));
|
||||||
@@ -25,8 +25,7 @@
|
|||||||
const text = new TextDecoder().decode(buffer);
|
const text = new TextDecoder().decode(buffer);
|
||||||
const matches = [...text.matchAll(/vertex\s+([-+\deE.]+)\s+([-+\deE.]+)\s+([-+\deE.]+)/gi)];
|
const matches = [...text.matchAll(/vertex\s+([-+\deE.]+)\s+([-+\deE.]+)\s+([-+\deE.]+)/gi)];
|
||||||
if (matches.length < 3) throw new Error('This file does not contain a readable STL mesh');
|
if (matches.length < 3) throw new Error('This file does not contain a readable STL mesh');
|
||||||
const stride = Math.max(1, Math.ceil(matches.length / 42000));
|
for (let i = 0; i + 2 < matches.length; i += 3) {
|
||||||
for (let i = 0; i + 2 < matches.length; i += 3 * stride) {
|
|
||||||
for (let j = 0; j < 3; j++) raw.push(+matches[i+j][1], +matches[i+j][2], +matches[i+j][3]);
|
for (let j = 0; j < 3; j++) raw.push(+matches[i+j][1], +matches[i+j][2], +matches[i+j][3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,16 +52,19 @@
|
|||||||
return [rx,y*cosP-rz*sinP];
|
return [rx,y*cosP-rz*sinP];
|
||||||
};
|
};
|
||||||
maskCtx.fillStyle='#fff';
|
maskCtx.fillStyle='#fff';
|
||||||
|
maskCtx.beginPath();
|
||||||
for(let i=0;i<vertices.length;i+=9){
|
for(let i=0;i<vertices.length;i+=9){
|
||||||
const a=project(vertices[i],vertices[i+1],vertices[i+2]);
|
const a=project(vertices[i],vertices[i+1],vertices[i+2]);
|
||||||
const b=project(vertices[i+3],vertices[i+4],vertices[i+5]);
|
const b=project(vertices[i+3],vertices[i+4],vertices[i+5]);
|
||||||
const c=project(vertices[i+6],vertices[i+7],vertices[i+8]);
|
const c=project(vertices[i+6],vertices[i+7],vertices[i+8]);
|
||||||
maskCtx.beginPath();
|
|
||||||
maskCtx.moveTo(cx+a[0]*size,cy-a[1]*size);
|
maskCtx.moveTo(cx+a[0]*size,cy-a[1]*size);
|
||||||
maskCtx.lineTo(cx+b[0]*size,cy-b[1]*size);
|
maskCtx.lineTo(cx+b[0]*size,cy-b[1]*size);
|
||||||
maskCtx.lineTo(cx+c[0]*size,cy-c[1]*size);
|
maskCtx.lineTo(cx+c[0]*size,cy-c[1]*size);
|
||||||
maskCtx.closePath();maskCtx.fill();
|
maskCtx.closePath();
|
||||||
}
|
}
|
||||||
|
// Rasterize the union once. Filling faces individually creates faint
|
||||||
|
// antialiased seams that resemble a wireframe on dense assemblies.
|
||||||
|
maskCtx.fill();
|
||||||
// A filtered copy creates only the exterior contour; triangle boundaries
|
// A filtered copy creates only the exterior contour; triangle boundaries
|
||||||
// never enter the final image because the source is a single solid mask.
|
// never enter the final image because the source is a single solid mask.
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|||||||
Reference in New Issue
Block a user