Skip to content

Loading…

Functions that can query a given 3D shape and provide size or location info #301

Open
filipmu opened this Issue · 64 comments
@filipmu

All openscad functions take parameters as input and produce shapes. It would be useful to have functions that take shapes as input and produce parameters. This would be useful for aligning shapes. Some examples are max/min dimensions on each axis, center of gravity, etc. There are a number of uses of this feature: 1-since all shapes in openscad are polyhedrons, spheres and cylinders do not render exactly as specified. So there is no way to exactly align shapes based only on input parameters. 2-To align two objects created by separate modules, the user needs to know the inner workings of each module to calculate how to align them. 3- Application of complex transformations like Minkowski and Convex Hull or many CSG combinations create shapes with very complex boundaries that can't be easily calculated.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@ivoknutsel

What would the syntax for these functions and their use look like ?

@GilesBathgate

For now It doesn't really matter how they look like until someone can come up with a sensible way to implement it.

The problem is this:

To find the boundaries of objects you have to first evaluate the script into its CSG result. At this point you then have to feed those boundary values back into the script and evaluate the CSG result again. If the value from the first evaluation effects the result of the boundary in the second evaluation you will have to feed those boundary values back into the script and evaluate the CSG again, and of course this process would go in forever in an infinite loop.

At least that's the fundamental problem with it the way I see it. Perhaps I've misunderstood.

Regards,
Giles

@ivoknutsel

I have more or less the same problem, i create geometries in nested modules. In the root i need to position the geometries relative to each other based on the length or another function of those geometries.

As i understand OpenSCAD, geometries are always leaves in a model tree and the tree and all variables are calculated compile time. There is no way to assign a geometry or the result of a module to a variable, modules don't return values but can only (with "child()") modify or disable branches in the model tree.

I am an OpenSCAD novice and may be completely wrong.

@nophead
@TakeItAndRun
@nophead
@GilesBathgate

Rapcad implements a bounds module. It just echos the bounds of its children to the console, which you can then copy back into the script. Its still hardcoded numbers but I think its better than nothing. Still can't get my head around how it would feed the value back into the script without re-evaluation.

On the other hand as nophead points out a stl_dim() function (equivalent to dxf_dim()) would be nice.

Regards,
Giles

@nophead
@kintel
openscad member

If we handled imports similar to how we handle use and include, this would work.
Imported objects would then become resources which can be used by the script.
Naturally, the script then cannot be evaluated before files are loaded, but that's decent enough I'd think.

RapCad actually started on doing this by introducing a new import syntax: import <myfile.stl> as myfile.

Ideally, myfile would in this case become a variable which could be queried, e.g. myfile.bounds (not sure what else would be interesting to query for).

Also, there already is a similar hack in OpenSCAD: We have have dxfdim() function, which reads dimensions from a DXF file and makes it available to the script. I really don't like that function, but it's there :/

@kintel
openscad member

Related to this, the new resize() module might provide a workflow for some of the cases where a query would be the obvious solution.

See https://github.com/openscad/openscad/blob/master/testdata/scad/features/resize-2d-tests.scad and https://github.com/openscad/openscad/blob/master/testdata/scad/features/resize-tests.scad

@NateTG

... At this point you then have to feed those boundary values back into the script and evaluate the CSG result
again.

My understanding is that variables can only have their value set once in OpenSCAD, so if you could assign a mesh to a variable, and then operate on the variable, you should never get in trouble. Since you can't alter the underlying variable after getting its dimensions.

@nophead
@kintel
openscad member

Assignments are collected over the entire scope and evaluated first (but in individual order of each variable's last assignment). I don't see how this would cause problems.
However, to be able to assign meshes to variables, we'd need to be able to evaluate the corresponding geometry inline with the parser, which is a major architecture change.

@ablapo

Here is my idea. Just make a rough estimate, that would be sufficient for most things.
Do you think it is possible to use the hull() function on an imported STL-file and remember that hull-data for later function calls. And is it possible to get some info from that hull data?
hull_import (hulldata, "3D_Object.stl");

length= outbound_length ( angle [x,y,z], hulldata); -> gives the length to boarder from the center (0,0,0)
vertex=outbound_vertex ( angle [x,y,z], hulldata); -> gives the 3d-vertex of the hitting face of the hull

With that tools you could arrange elements around a stl-file. Make Masks from 3D scanned humans. Make cloth around scanned bodies.

Maybe split that into two parts, if its too difficult.
1. Step: generate a .dat -file with a hull data from a stl.
2. Step: use that .dat file to get the important values

@GilesBathgate

Can we add a FAQ to the main OpenSCAD website? this question/idea keeps being asked/suggested.

@kintel
openscad member

Yes, we should start a FAQ

@blobule

I just did an interesting experiment on the issue of "measuring"...

We all known that openscad parse the code first, builds a csg tree, the evaluate the csg tree to obtain the final geometry. Because of that, any measure on the geometry is only available long after the parsing is done.

However, nothing prevents a partial evaluation of the csg tree during parsing... Here what I did to achieve this:

The command "render()" now does the csg rendering of its subtree (its childs) immediately during parsing, and it computes the bounding box of the resulting polyset geometry.
To make this information available to the code, it simply defines variables ($xmin, $xmax, ...) in the context of its parsing parent. This means that this will work:

render() sphere(r=10);
echo("bbox is ",$xmin,$xmax,$ymin,$ymax,$zmin,$zmax);

Of course, since this is done during parsing, those information are only available to what follows render() in the code.
Because there is a cache system for geometry, this "early evaluation" for render does not seem to impact performance.

Here is an example adding walls around an object to illustrate the bounding box:

bbox-a

The code looks like this:

module openbbox(ep=3,sc=0.7) {
    render() child(0); // draw and measure the child
    echo("bbox X is ",$xmin,$xmax);
    echo("bbox Y is ",$ymin,$ymax);
    echo("bbox Z is ",$zmin,$zmax);
    color("blue",0.2) union() {
        translate([$xmin-ep/2,0,0]) scale([1,sc,sc]) cube([ep,$ymax-$ymin,$zmax-$zmin],center=true);
        translate([$xmax+ep/2,0,0]) scale([1,sc,sc]) cube([ep,$ymax-$ymin,$zmax-$zmin],center=true);
        translate([0,$ymin-ep/2,0]) scale([sc,1,sc]) cube([$xmax-$xmin,ep,$zmax-$zmin],center=true);
        translate([0,$ymax+ep/2,0]) scale([sc,1,sc]) cube([$xmax-$xmin,ep,$zmax-$zmin],center=true);
        translate([0,0,$zmin-ep/2]) scale([sc,sc,1]) cube([$xmax-$xmin,$ymax-$ymin,ep],center=true);
        translate([0,0,$zmax+ep/2]) scale([sc,sc,1]) cube([$xmax-$xmin,$ymax-$ymin,ep],center=true);
    }
}
openbbox() rotate(rands(-90,90,3,1234)) cylinder(r=10,h=30,center=true);

This means that once an object is created, it is easy to place other objects relative to it. What about measuring geometry before using it, so we can place the object according to its size? What we need is a way to "render but don' t use the resulting geometry" . I ended up using '%', which seems to do exactly that:

%render() sphere(r=10);

Here is an example where I stack objects according to their measured sizes:

bbox-b

The code (a little ugly, sorry) :

module randcylinder(seed=1234) { rotate(rands(-90,90,3,seed)) cylinder(r=10,h=30,center=true); }
module base(h=5) { cube([30,30,h],center=true); }

// stack the second child so its resting on top of second child
module stack() {
        render() child(0); // the "base"
        // use the zmax of this object as a reference
        assign(alt=$zmax) {
                %render() child(1); // do not keep this geometry
                translate([0,0,-$zmin+alt]) child(1); // show the child at the right place
        }
}

stack() {
        stack() {
                stack() {
                        stack() {
                                stack() {
                                        stack() {
                                                base(h=5);
                                                randcylinder(100);
                                        }
                                        base(h=10);
                                }
                                randcylinder(101);
                        }
                        base(h=2);
                }
                randcylinder(102);
        }
        base(h=4);
}

Before I submit this, I would like to know what people think of this approach to measuring. Also, my changes are too much of a "hack" at this point and need a bit of work to integrate nicely.

@TakeItAndRun
@nophead
@blobule

Indeed, it does break the assignment rule.

How about, instead of changing render(), we define a function similar to assign(), say assignbbox(), which will define in a local context the bounding box variables (xmin,xmax,...) according to the immediate rendering of its first child?

assignbbox() {
     sphere(r=5); // first child, used to compute bbox
     // from here, any geometry can use xmin,xmax,...
     // The bounding box info is only valid inside the assignbbox
}

Would this work?

In general, maybe we could have a "probe()" command which would create a local context just like assign, but were all defined variables would come from analyzing the immediate rendering of the first child. The parameters of probe() would determine what we wish to compute:

probe(boundingbox=true) {
    geometryToProbe();
    geometryUsingTheComputedBoundingBox();
}

As mentioned in previous posts, we could think of other options, like finding the intersection of the geometry with one axis, or an extreme point in some direction, well, any computation on the first child would be assigned as local variables for the following childs.

@MichaelAtOz

While I like the possibilities, calling render() defeats the concept of OpenSCAD.
It uses OpenCSG (F5) for fast preview & CGAL (F6) for proper geometry.
Using render() defeats F5's benefits. (but does allow additional functions)
... two minds...
I assume OpenCSG can't do a bounding box??

@kintel
openscad member

The OpenSCAD architecture is based around evaluating the source code into a concrete CSG tree before attempting to render. The render() module is really a workaround for rendering artifacts and performance issues and will hopefully eventually become less and less needed.

This suggestion attempts to bring compiling and rendering closer together in order to create some feedback loop opportunities. It's worth a discussion, but I feel that if we were to go in this direction we should take a more careful look at why/if we should continue to keep compiling and rendering as separate as we do today, rather than patching it up to fix symptoms.

@blobule

I like the simplicity we get by separating compiling and rendering. This approach puts an emphasis on building an object using known components, which is a great way to approach CSG modelling, in my opinion.

However, occasionally, a design will require feedback information about one of its components. Right now, such design can' t be done on openscad. The workaround is to render the component and externally analyze it, then re-compile the design, which is tedious.

By adding a command such as "probe_bounding_box() { }", you force the designer to explicitly ask for this information, and thus clearly state that this should be used only when really needed. This is ideal in my opinion since automatically computing the bounding box of all components (i.e. rendering while compiling) would hurt performance and change the approach of openscad to CSG modelling.

So basically I consider the "separate compile/render" model very good, but an occasional "on-demand component information" when the design needs it would add a lot of power.

@nophead
@MichaelAtOz

you never need to query the geometry unless it is imported as an STL or DXF

I found resize() handy in those situations, make the STL fit to your spec. Won't help for all.

@MichaelAtOz

Bigger picture.
I had though that a capability to produce multiple parts (exports e.g. STLs) from one scad program would be handy, something akin to Thingiverse Customizer 'Part' (which I believe just runs the .scad multiple times in batch mode).
So if there was something like

export(file=str("myfile",part,".stl")
    render() // maybe this should just be implicitly done as part of export
        my_object(part);

Program logic (for/if etc) can then handle an assembly, and if combined with Marius idea from above

import <myfile.stl> as myfile
myfile would in this case become a variable which could be queried, e.g. myfile.bounds

Could then be used to iteratively build upon previous 'part's, automate a build layout based on size etc.

@blobule

It is true that, in theory, the declarative approach of openscad implies that everything can be computed "in advance", before the shape is actually rendered. This is what makes the "fast preview" (F5) possible and useful.

If we had to render() everything with CGAL before getting a preview, that would defeat the original idea of a "fast preview" using openCSG.

However, even simple shapes can be very hard to compute "by hand" just to get a boundary point. As an example, consider this model I just made:

probe6b

The "pistons" use the bounding box of the green shape to select their position. The shape is the projection of a cone, and even if it is simple, there is no obvious (i.e. easy to derive) equation to get the boundary points.
In this case, rendering the shape while parsing is quite cheap, so there is no problem with the fast preview.
In my opinion, the code is much simpler to write and more "natural" than with lots of equations.

Here is the code:

module forme(c=true) {
    projection(cut=c) {
        rotate([45,0,0]) cylinder(r1=20,r2=0,h=40,center=true,$fn=64);
    }
}

module cam(t=0) {
    rotate([t*360,0,0]) translate([-10,0,0]) rotate([0,90,0]) linear_extrude(height=20) forme(false);
}

module piston(t=0.5) {
    probe() {
                // check along the z-axis by intersecting with a cube
        % render() intersection() {
            child(0);
            translate([0,0,0]) cube([0.001,0.001,80],center=true);
        }
                // variables xmin,ymin,zmin,xmax,ymax,zmax are available for the following nodes
        echo(zmin);
        translate([0,0,-40+zmin]) {
            cylinder(r1=5,r2=0,h=40);   
            translate([0,0,-10]) cylinder(r=30,h=10);
        }
    }
    %color("red",0.2) translate([0,0,-32-48]) cylinder(r=31,h=32);
}

color("limegreen") cam($t);
color("limegreen") rotate([$t*360,0,0]) rotate([0,90,0]) cylinder(r=3,h=100,center=true,$fn=6);
rotate([-120,0,0]) piston($t,120)  rotate([120,0,0])  cam($t);
rotate([0,0,0])       piston($t,0)       rotate([0,0,0])       cam($t);
rotate([120,0,0])  piston($t,-120) rotate([-120,0,0]) cam($t);

The probe() function I coded is similar to assign(), but simply performs a CGAL evaluation of its first child, then defines variables related to the geometry (like xmin,xmax,...) which are only available to the following children inside probe() (in the local context). This way, there is no violation of assignment rules.

So this is my latest attempt at tackling the querying of shapes.

@MichaelAtOz

Nice model!

@GilesBathgate

@MichaelAtOz Sure its a nice model, the fact that he has implemented a probe() module is much more impressive. I like the idea, it solves a lot of problems (relating to recursion) that I couldn't get my head around when considering this before.

@blobule

(following a comment in issue #586 ) The code for probe() is on github, in the branch "probe" of my fork of openscad ( https://github.com/blobule/openscad ). Feel free to check it out. It is not fully tested (not ready for a pull request I assume), but it already works well.

Quick documentation of the module:

probe() {
      some_geometry; // this is the reference geometry
      some_other_geometry; // this and following can use information about reference geometry
}

probe() will render the first child with CGAL, gather some information about the geometry, define in a local context some variables holding this information, and then evaluate the remaining children with this context.
The variables defined are related to the bounding box of the first child:
xmin, xmax, ymin, ymax, zmin, zmax, and also center=[(xmax+xmin)/2,...] and boxsize=[xmax-xmin,...]

Note that the first child of probe() is discarded after it is computed, to make it easy to use objects for the only purpose of aligning things.

Examples:

probe() {
    cylinder(r=10,h=40);
    echo(center,boxsize);
    #translate(center) cube(boxsize,center=true);
}
////////////////////////////////
// place child(0) on top of child(1)
module stack() {
    probe() {
        children(1);
        translate([0,0,zmax]) probe() {
            children(0);
            translate([0,0,-zmin]) children(0);
        }
    }
    children(1);
}
stack() {
    rotate([10,20,30]) cylinder(r=10,h=40,center=true);
    cube([30,30,5],center=true);
}
///////////////////////////////
// simple resize module
module setsize(sz=[10,10,10]) {
    probe() {
        children(0);
        echo(center,boxsize);
        translate(center)
        multmatrix(m = [ 
            [1/boxsize[0], 0, 0, 0],
            [0, 1/boxsize[1], 0,0],
            [0, 0, 1/boxsize[2], 0],
            [0, 0, 0,  1]
            ])
        scale(sz)
        translate(-center)
        children(0);
    }
}
setsize([5,10,15]) sphere(r=4);
setsize([5,10,25]) translate([10,20,30]) sphere(r=4);
//////////////////////////
// to move an object's center at the origine
module centerize() {
    probe() {
        children(0);
        echo(center,boxsize);
        translate(-center) children(0);
    }
}
centerize() translate([10,20,30]) sphere(r=10);
@nophead
@blobule

yes, the cache is on the "rendering" side of things, so the cache is working in this case.

@obobo2
@blobule

As I understand, the internal representation is always polygonal.

This is why the bounding box of a sphere(r=2,$fn=16) is +- 3.696 while for the sphere(r=2,$fn=128) it is +-3.999.

@lordofhyphens

#1155 might be a useful step towards a solution, especially if you allow for iterative compilation.

@blobule blobule referenced this issue
Open

Probe #1388

@MatthiasWM

RFC: The probe() solution seems to have too many side effects and non-obvious variable names. I would like to suggest the following interface:

The union()/etc. functions could have optional arguments that work in a "call by reference" way, for example:

module someGeometry() { ... }

// render the bounding box of two random geometries
my_bbox = [ [0,0,0], [0,0,0]];
// store the bounding box data in my_bbox as an array of two vectors
union(query_boundingbox=my_bbox) {
  someGeometry();
  someOtherGeometry()
}
%translate (my_bbox[0]) cube(my_bbox[1]);

Positives:

  • no change in syntax
  • caching works
  • union() call can provide many more attributes
  • query many bounding boxes without collision of variable names

Negatives:

  • query_boundingbox=my_bbox my be surprising; users may expect the left side to be the one that receives a value; we could reverse the order (not logical) or add a syntax, i.e. boundingbox->my_bbox
  • union() returns a geometry that will be rendered; we need an additional method 'query()' that performs a 'union()' call without returning any geometry
@tcurdt

Indeed query_boundingbox=my_bbox is surprising and not quite so obvious.
I have no idea about the internals but I assume something like this is not an option?

bbox = union() {
  ..
}
@MatthiasWM

I have tried myBBox = bbox() { cube(1) }, but it would require a change in syntax, possibly failing existing scripts. Also, you would have to introduce new calls for bounding boxes, position, orientation, scale, and what ever other queries we will eventually com up with.

A few alternatives that are syntax compatible, or at least not entirely disruptive:

myBBox = [ [0, 0, 0], [0, 0, 0] ]; // not needed, just illustrating the returned format
union(return_bbox=myBBox) { ... } // hint by parameter name, 100% compatible
union(myBBox=query_bbox) { ... } // more obvious data flow direction, but contrary to syntax conv.
union(bbox=>myBBox) { ... } // additional syntax needed, obvious, not breaking things
union() (bbox=*myBBox) { ... } // C++ call by reference syntax 
union() (bbox=&myBBox) { ... } // PHP call by reference syntax 
union() (out myBBox=bbox) { ... } // C# call by reference syntax 

In a perfect world, there would be no differentiation between variables/values and geometry. A geometry would just be another value type. It is possible to get a somewhat compatible syntax, but I am unsure if this would work for all existing scripts. Example:

x = [90, 0, 0]; // x is a vector
y = rotate(x) cube([10]); // y is a geometry
z = bbox(y); // z is an array of vectors
z = y.bbox(); // alternative syntax
render() {
  y; // draw the cube
  translate(z[0]) sphere(); // draw a sphere at the lowest corner of the bounding box
  translate(z[1]) sphere(); // and at the highest corner
}
myBBox = bbox( scale([1, 3, 4]) sphere(r=2) ); // another sample
@tcurdt

I am not sure I get why "it would require a change in syntax, possibly failing existing scripts" as it should be forward (just not backwards) compatible - or can you assign subtrees like a = union() already?

If you want to avoid all kinds of new calls why not just

props = properties( union(...) );
props.bounds
props.position
etc.
@MatthiasWM

In OpenSCAD, if I understand the lex syntax right, values and geometries are syntactically different things (and so are functions and modules). You can't write a function that takes a geometry/module as an argument. Functions can not have children either. So a = func( cube() ); is not possible. union() etc. are different in that they can take geometry as children, but again not as arguments, and they can only return a geometry, not a value.

@tcurdt

The first thing should not be a problem as this looks OK, too

props = properties(){
  union(...) 
}

props.bounds
props.position

but if they can only return geometries - that's a bummer.
Question is how easy that would be to change.

@TakeItAndRun
@FluxIX

I'm not convinced you couldn't return a value and use it. The compilation process is still hierarchical and thus the compilation order could be arranged so as to process the dependent items first, then use the computed values in the depending items.

An obvious limitation of this approach is you cannot dynamic alter the values, and you have to check for dependency cycles.

@MatthiasWM

FluxIX is right, and it has been done in this thread and also here:
#1388

The CSG is created during compilation. By running branches of the already existing (though incomplete) CSG, the values are calculated and assigned to the variable. This is completely transparent to the compiler and the user. Even the caches are used as expected.

The limitation that you can't change variables still applies. So this code:

a = 0;
union (bbox=>a) { cube(1); }
echo(a);
union (bbox=>a) { cube(2); }

would print [[0,0,0],[2,2,2]] because echo prints the last assigned value of a.

@tcurdt

...which is another reason why I'd prefer a syntax like

props = properties(){
  ...
}
props.bounds

For one it would make the (one-off) assignment clearer - plus it clarifies the scope of where the information is available.

@MatthiasWM

@tcurdt, yes, that would be nicer. Problem here is that you need to calculate all possible properties and put them inside the props variable. Potentially, some properties may take quite a while to calculate.

I will look into modifying the syntax to allow for modules to return values, and maybe even a few steps beyond. If that works, all suggested syntaxes would be possible!

@tcurdt

@MatthiasWM nice!

Modifying the syntax to allow return values sounds great. Not sure how easy it is one impl side - but wrapping those properties so they get calculated on first use should possible (in theory).

@TakeItAndRun
@MatthiasWM

RFC: so I think I have solved it.

openscad_query

Short version: I added a new kind of function named "Query". Queries are different from functions by allowing geometry children. Any kind of query can be implemented. Queries have easy access to all children's geometry if needed.

Long version:
This implementation closes the bridge from geometry back to numeric values. The code is based partially on blobule's contribution to "Probe #1388". The syntax has been expanded carefully to allow for logical and obvious code. All the buffering should work transparently to the user. There are no hidden variable names. New Queries can be added easily at "C" source code level.

Here are some syntax examples:

echo( boundingbox() cube() );
myBoundingBox = boundingbox() {
    translate([10, 10, 5]) sphere();
    cube([5, 6, 7]);
} ;
translate(myBoundingBox[0]) cube(myBoundingBox[1]);

module something() {
    import("something.stl");
}
// return two vectors, center and size, instead of minimum and maximium
echo( boundingbox(format="center,size") something() );

Any suggestions?

EDIT: removed boundingbox(render=...) as it made no obvious sense

@MatthiasWM

PS: git source in MatthiasWM/matt_converge

@nophead
@tcurdt

Nice! That looks pretty good!

But didn't you say "[we] would have to introduce new calls for bounding boxes, position, orientation, scale, and what ever other queries we will eventually com up with"?

So shouldn't that better be a properties query?

@MatthiasWM

@nophead: WARNING: Ignoring unknown variable 'myBoundingBox'. :boundingbox() is evaluated before myBoundingBox is set.

But:

myBoundingBox = [[-2, -2, -2], [5, 6, 7]];
yourBoundingBox = boundingbox() {
    cube([myBoundingBox[1][0] * 2, myBoundingBox[1][1] * 2, myBoundingBox[1][2] * 2]);
};
echo( myBoundingBox );
echo( yourBoundingBox );
----
ECHO: [[-2, -2, -2], [5, 6, 7]]
ECHO: [[0, 0, 0], [10, 12, 14]]
@MatthiasWM

@tcurdt: Yep, that's what I said. It's not consistent though, and we would still require different implementations for properties(boundingbox), properties(center), etc. , so nothing gained. You were right. Only slight disadvantage is some namespace pollution.

@MichaelAtOz

What's the difference boundingbox() v's boundingbox(render=true) ?

@MatthiasWM

@MichaelAtOz it's from the original boundingbox code, something about a preferred renderer, and only here to illustrate that boundingbox() can take parameters. I will throw that out of the example.

@MichaelAtOz

So just to confirm. boundingcox() et al. effectively do a render() to generate the full geometry with CGAL, hence do not have the fast preview that F5 gives (for that child/ren). Given that CGAL caching applies.

@MatthiasWM

@MichaelAtOz queries will usually need to know the geometry in detail. So, yes, they need to calculate the geometry for the given branch. Additional caching is probably needed to avoid recalculating bounding boxes and possibly other, much more complex calculations. F5-rendering is pixel-based and not sufficient.

I have just started to understand the OpenSCAD source code and I am not involved enough to understand caching at this point. Help would be greatly appreciated.

@MichaelAtOz

I was just clarifying, and stating it for the wider audience. One of the great benefits of OpenSCAD is the fast preview, and the not so good is the (while accurate) lengthy render.

While obviously, needing access to geometry data will require rendering, bringing this into the main stream will detract from the fast preview, particularly for complex objects. It is of course up to the coder.

@Yona-Appletree

I've been looking forward to this feature for a while, and would like to chime in with some real-world use-cases and ideas on keeping the implementation simple.

First, I agree with the concern about having many specialized function for properties for geometry. It seems to me that the main goal here is to bridge the geometry space of OpenSCAD with the function/value space.

Why not keep the language changes to a minimum by using render() function to allow obtaining the geometry for a tree, using MatthiasWM's extension to allow geometry with function syntax.

For instance:
geo = render() cube([20,10,20); // geo now contains an array with each separate polyhedron (or polygon, for 2d geometry), each compatible with the polyhedron/polygon call: [[points, faces]]

Now we can compute the bounding box, center of mass, etc. in user-space easily enough with recursive functions.

This would also allow some very neat hybrid functionality where one could iterate the points or faces and then constructively add geometry to them, or use them with any of the existing polyhedron libraries. In particular, this would be very useful for implementing functions like fillet() and chamfer(), both in 2d and 3d space. Other uses include more complex variants on the above piston / cam exmaple, rendering text along a path (or face), all in native OpenSCAD-code.

Currently, we have polyhedron() which takes function/variable data and moves it into the geometry space. Using render() to go the other way is simple, elegant and provides symmetry with minimum complexity. What do you guys think?

@Neon22
@MatthiasWM

Sounds good for most cases IMHO. For large geometries, I can see compile time and memory use exploding. I would love to have a binary plugin interface... .

@Yona-Appletree
@MatthiasWM

Ah, but it is all in the patch and it is working. The calculations for the bounding box need fixing (use the provided boundingbox() function?), but the logic for returning geometry is there. And it's the same an=mount of change that the "render() call would need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.