how to compile a JsCad script from string instead of file?



  • Hello everyone! I've been enjoying JsCad, but have found myself unable to move forward in my project that uses it.

    First some background:

    For my own learning I've been working on a project where I can write JsCad code in half of a window and display its output in the other half of the same window. I was using the @jscad/openjscad npm module which was working well, but I wanted it to respond faster when drawing the same solid multiple times.

    Therefore my primary goal is to compile This caused me to run across https://www.npmjs.com/package/@jscad/vtree which looked like it would provide some speed ups. It wasn't entirely clear to me how to make use of this module with my setup. I came across the @jscad/desktop module (https://www.npmjs.com/package/@jscad/desktop) which described how to use the @jscad/vtree module in a way that didn't match my intended implementation.

    I'm compiling the output from strings, not files using jscad.compile(script, params). I noticed on this forum that V2 is now available on npm, so I've tried using it as well, as it appears I can enable the use of vtree. However I cannot figure out how I can compile a script that is contained within a string in the manner that I was before. I believe the @jscad/core's code-evaluation.rebuildGeometryCli.js is the intended method, but I am not certain if I am going down the correct path with this as I cannot get that to work either.

    My primary issue that I want to address is speed. I've been testing with the following script:

    function main () {
    	return getLogos();
    }
    
    function getLogos() {
    	let size = 1;
    	let space = 0.65;
    
    	let arr = [];
    
    	for(let i = 0; i < size; i++) {
    		for(let j = 0; j < size; j++) {
    			for(let k = 0; k < size; k++) {
    				arr.push(getLogo().translate([i * space, j * space, k * space]));
    			}
    		}
    	}
    
    	return union(arr);
    }
    
    function getLogo() {
    	return union(
    		difference(
    			cube({size: 3, center: true}),
    			sphere({r: 2, center: true})
    		),
    			intersection(
    			sphere({r: 1.3, center: true}),
    			cube({size: 2.1, center: true})
    		)
    	).scale(0.2).translate([0, 0.3, 0]);
    }
    

    As I increase the size variable in the getLogos() function, it takes exponentially longer to compile something that I can draw.

    My questions are:
    Am I correct in my interpretation that the @jscad/vtree module would help with this when used correctly?
    Given that I want to load jscad scripts from strings and not files, how can I do this with the @jscad/vtree module used correctly in both V1 and V2?

    Thank you!



  • @Robert-Olson small update:

    managed to add reload functionality into :
    http://3d.hrg.hr/jscad/three/threejscad.html

    just drop a script on to the screen, edit in your fav editor and see results.

    tested only in chrome, to se errors use browsers console F12

    it is a rough prototype with ton of scrappy code, but works relatively ok.
    modeling is in main thread, so if you create an infinite loop, you will have to kill the tab.

    nevertheless if you do start using it, let me know how it works for you , or if it breaks



  • @Robert-Olson I have a working renderer that was made for the same performance reasons

    http://3d.hrg.hr/jscad/three/threejscad.html?uri=model.logos.js

    you can try out sliders to see how fast it renders

    the first performance gain is that modeling and render are in same thread.
    I have moved const logo = getLogo() outside of main function so on parameter change it can be reused
    0106b4cf-7ae1-435d-9558-50fe4f32bafb-image.png

    you will notice model gen: {few}ms as it only generates translated wrappers and not doing subtract and other operations for each entity
    but when you move slider past 10 (1000+objects) it will be visually slower as it takes time to push the model data to GPU.

    const jscad = require('@jscad/modeling')
    const { sphere, cube } = jscad.primitives
    const { translate, scale } = jscad.transforms
    const { union, subtract, intersect } = jscad.booleans
    
    const logo = getLogo()
    function main ({// @jscad-params
    	size = 6,// {type:'slider', live:1}
    	space = 6,// {type:'slider', live:1}
    }) {
    
    	let arr = [];
    	for(let i = 0; i < size; i++) {
    		for(let j = 0; j < size; j++) {
    			for(let k = 0; k < size; k++) {
    				arr.push(translate([i * space, j * space, k * space],logo));
    			}
    		}
    	}
    
    	return arr
    }
    
    function getLogo() {
    	return union(
    		subtract(
    			cube({size: 3}),
    			sphere({radius: 2})
    		),
    		translate([0, 0.3, 0], scale([0.2,0.2,0.2],intersect(
    			sphere({radius: 1.3}),
    			cube({size: 2.1})
    		)))
    	);
    }
    
    module.exports = {main}
    

    There is still space for performance improvements and if you would like to discuss in more detail you can joins openjscad discord channel,
    https://discord.gg/rky2TMNH (not much talk there currenty, so I am eager to get ppl there to chat)

    I am in the process of allowing that page drag an drop for working with files like on openjscad.xyz (most likely only for single file scripts, not folders)



  • It has been a while since I first asked this question as I got preoccupied with work for my day job. However, I am now able to devote time to my hobby project again and I just wanted to say thank you to everyone who responded. 🙂 All of you input showed me how I could write the script I had to be more efficient. I've changed it to create the object and store it in a variable for reuse outside of the loops which has been a huge speed improvement.

    I am still curious if I could be pointed in the right direction for the @jscad/core equivalent to the jscad.compile(script, params) function that I was using from @jscad/openjscad.

    Thanks in advance!



  • @hrgdavor nice reply.

    that was my first thought after looking at the example from @Robert-Olson

    transforms (translation, rotation, scale, center, etc) are very fast, as only the matrix part of the geometry is modified. so, create a 'part' once and translate many times.

    here's a example from one of my designs.

    const makeRow = (h, p) => {
        let i = 0
        let x = 0
        let y = 0
        let c = circle({radius: p.case_b_v_h, segments: p.segments})
        let holes = [c]
        for (i = 1; i < h; i++) {
            y = y + (p.case_b_v_h * 2) + p.case_b_v_g
            holes.push(translate([x, y], c))
        }
        return translate([0, -(y / 2)], holes)
    }
    


  • Well, I doubt you can avoid exponential slowdown as size increase increases number of objects exponentially.

    you could possibly gain some performance if you store the object in a variable outside of the loops,

    var logo = getLogo();
    

    then inside the loop

    arr.push(logo.translate([i * space, j * space, k * space]));
    

    I am not 100% sure if it will work or help with perf, but try it anyway 🙂


Log in to reply