jscad / OpenJSCAD.org

JSCAD is an open source set of modular, browser and command line tools for creating parametric 2D and 3D designs with JavaScript code. It provides a quick, precise and reproducible method for generating 3D models, and is especially useful for 3D printing applications.
https://openjscad.xyz/
MIT License
2.58k stars 505 forks source link

@jscad/stl-deserializer in the browser? #1332

Closed tsdexter closed 3 months ago

tsdexter commented 3 months ago

Can the stl-deserializer be used in the browser? There is only a node example and the code below is not working (I have tried with multiple STLs exported from jscad and other sources)

Expected Behavior

geometry is logged in the console

Actual Behavior

Uncaught (in promise) Error: three or more points are required
    at Object.polyhedron (chunk-JDTWA6TY.js?v=0f31f1e5:518:15)
    at toPolyhedron (@jscad_stl-deserializer.js?v=0f31f1e5:4617:25)
    at elementFormatterObject (@jscad_stl-deserializer.js?v=0f31f1e5:4335:84)
    at deserializeBinarySTL (@jscad_stl-deserializer.js?v=0f31f1e5:4523:15)
    at Object.deserialize (@jscad_stl-deserializer.js?v=0f31f1e5:4340:38)

Steps to Reproduce the Problem

use the below file handler on a file input and open the jscad logo jscad.stl exported from jscad.app

reproduction: https://codesandbox.io/p/sandbox/jscad-react-js-demo-forked-hfgxh8

download and unzip attached file and then choose the unzipped jscad.stl in the file picker: jscad.zip

function handleFilesSelect(e) {
        const { acceptedFiles, fileRejections } = e.detail;
        files.accepted = [...files.accepted, ...acceptedFiles];
        files.rejected = [...files.rejected, ...fileRejections];
        console.log('files', files);
        //create rawData arraybuffer
        const rawData = new Uint8Array(files.accepted[0].data);
        const geometry = stlDeserializer.deserialize(
            { output: 'geometry', filename: files.accepted[0].name },
            rawData
        );
        console.log('geometry', geometry);
    }

Specifications

z3dev commented 3 months ago

@tsdexter just a simple question... is this a custom website? Or just want to get the contents of an STL file via the JSCAD website?

tsdexter commented 3 months ago

@z3dev it's a custom site, I want to get the geometry from the STL (or 3mf and other formats with deserializers) opened via <input type="file" /> and display it in my custom viewer that already handles jscad data (ie: models: Array<Geometry>).

tsdexter commented 3 months ago

@z3dev I would also need to load/import them via browser fetch (to my cloud file storage) in the frontend - which I assume (maybe naively) that once I get the file input working, fetching should also work pretty much the same way.

tsdexter commented 3 months ago

debugging it, I see that when using script output, the points, faces, and colors are all empty arrays, hence the error. Still trying to track down why they are empty

//
  // producer: JSCAD STL Deserializer 2.1.23
  // date: Mon Apr 01 2024 10:30:53 GMT-0400 (Eastern Daylight Time)
  // source: jscad.stl
  // objects: 1
  //
  const {primitives} = require('@jscad/modeling')
//
// solid 1 : 0 points, 0 faces, 0 colors
//
const solid1 = () => {
  const points = [
  ]
  const faces = [
  ]
  const colors = [
  ]
  return primitives.polyhedron({points, faces, colors, orientation: 'inside'})
}

const main = () => {
 return [solid1()]
}

module.exports = {main}
tsdexter commented 3 months ago

@z3dev I added a reproduction to the issue (I'm not using the jscad-react package, was just a quick setup in codesandbox and does reproduce there as well)

tsdexter commented 3 months ago

@z3dev nevermind, I figured it out. Just have to read the file in as a string, instead of raw.

function readFileAsText(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (event) => resolve(event.target?.result as string);
        reader.onerror = (error) => reject(error);
        reader.readAsText(file);
    });
}

async function handleFilesSelect(e) {
        const { acceptedFiles, fileRejections } = e.detail;
        files.accepted = [...files.accepted, ...acceptedFiles];
        files.rejected = [...files.rejected, ...fileRejections];
        const fileText = await readFileAsText(files.accepted[0]);
        const geometry = stlDeserializer.deserialize(
            { output: 'geometry', filename: files.accepted[0].name },
            fileText
        );
        console.log({geometry}); // working
    }
tsdexter commented 3 months ago

@z3dev working now, but found other issue with certain STLs now - do you think this is unavoidable due to bad STLs or is stl-deserializer broken? #1333