-
Notifications
You must be signed in to change notification settings - Fork 1
OEP1: Generalized extrusion module
Issue: https://github.com/openscad/openscad/issues/114
- Make extrusion handling more general and flexible
- Handle transformations along the extrusion
- Allow extrusion along a path
- Make use of functions to describe the behavior
This lists the API shown in the examples. That needs to be amended to be more flexible regarding the specification of transformations. The working name for the module is loft()
, but it could just as well be extrude()
or any other suitable names.
loft(<parameters>) {
<2d child nodes>
}
Two options:
- f(t) = [X-Coordinate(t), Y-Coordinate(t), Z-Coordinate(t)] The given function will be called with a single argument ranging from 0.0-1.0. The function should return a 3D coordinate.
- list of coordinates
This produces points in 3d space to be used as anchor points for the shape.
If a list of coordinates was given for path, this might be redundant.
Three alternatives:
- f(t) = Rotation(t)
- f(t) = [X-Rotation(t), Y-Rotation(t), Z-Rotation(t)]
- List of rotations
Same as twist in today's linear_extrude(), but can be given as a function similar to the path parameter. The function will be called with a single argument from 0.0-1.0 and return a value in degrees rotation of the base 2D shape around the origin. The alternative form would specify a 3D transform as replacement for the default behavior which is to orient the shape to point in the direction of the next point in the path.
Two variants:
- f(t) = Scale(t)
- f(t) = [X-Scale(t), Y-Scale(t)]
- List of scale factors
Same as scale in today's linear_extrude(), but can be given as a function similar to the path parameter. The function will be called with a single argument from 0.0-1.0 and return a value describing a scale factor of the base 2D shape. If the function returns a 2D vector, the X- and Y- axes will be scaled separately.
- f(t) = [X-Offset(t), Y-Offset(t)]
- List of offsets
The function will be called with a single argument from 0.0-1.0 and return a 2D vector describing an offset of the base 2D shape from its origin.
http://piratepad.net/LLU3j8QIJh
Look at e.g. Mathematica's ParametricPlot3D function for inspiration.
The examples show functions specified as strings. This assumes that functions are looked up in the scope where loft() is instantiated. This way of binding functions cause some challenges wrt. caching of nodes, as we can no longer look at the expanded representation of a module instantiation and deduct how to render it.
Another option is to allow binding a function reference to a parameter value, or more generally, to a variable. This would also make it possible to pass functions as parameter to modules. To do this properly requires some thinking. The main issue right now appears to be that functions, modules and variables live in separate namespaces. This causes ambiguous lookup when a name could resolve both to a function and to a parameter holding a function reference. Perhaps we need to deprecate the possibility of functions, modules and variables having the same name.
I have done a quick study of around 1000 packages collected from Thingiverse with the script in statistics-scripts
. Check.hs
can be found here,
$ ghc Check.hs
$ ./Check files/* > results
$ grep "^duplicates" results | wc -l
27
$ wc -l results
954
That is to say that only 27 scripts out of the 1000 tested have identifiers that occur in more than one namespace (in all cases the overlap was between the module and variable namespaces). This seems like an acceptable breakage.
Add a transformation() type node? That could even specify the range, or variable?
transformation(function = "func3", variable = "t", range = [0:0.1:1]);
Require the shape to be the first node, all other nodes are transformation nodes?
loft() {
union() {
... 2d child nodes ...
}
transformation(function = "func1");
transformation(function = "func2");
transformation(function = "func3");
}
Put the transformation into the tree?
loft() {
transformation(function = "func1") {
transformation(function = "func2") {
... 2d child nodes ...
}
}
}
Other ideas?...
- Add a matrix parameter (or matrix2D and matrix3D) which can take functions as matrix elements.
- Allow less mathematical ways of building extrusions by specifying a list of shapes, points and transforms to describe a turtle-graphics-like environment.
The examples below show the functions as parameters to the loft() module. This is not very flexible and also does not allow to give a specific order for transformations.
- Always assume f(t) and sweep t between 0 and 1?
- Introduce some kind of generator function to produce a list of points / values?
Function based transformations essentially extend the existing linear_extrude() by giving more control about the transformations. E.g. scale and twist are not fixed/linear across the whole extrusion but can vary according to the result of a function evaluation.
function func5(t) = 2 * PI * t * t * t;
function func6(t) = 3 - 2 * t;
loft(rotate = "func5", scale_x = "func6", scale_y = "func6", slices = 100, height = 100)
square(size = [10, 10], center = true);
By extruding a shape along a path, quite complex models can be created with very little code.
function path(t) = [
(t / 1.5 + 0.5) * 100 * cos(6 * 360 * t),
(t / 1.5 + 0.5) * 100 * sin(6 * 360 * t),
200 * (1 - t)
];
loft(path = "path", slices = 800, height = 100) {
translate([-10, -6, 0]) square([3, 6]);
square([20, 2], center = true);
translate([7, -6, 0]) square([3, 6]);
}
With extrusion along a path together with a scaling function, it's easily possible to model something based on the teardrop formula by Paul Bourke (http://paulbourke.net/geometry/teardrop/).
function drop(t) = 100 * 0.5 * (1 - cos(180 * t)) * sin(180 * t) + 1;
function path(t) = [0, 0, 80 + 80 * cos(180 * t)];
function rotate(t) = PI * pow((1 - t), 3);
loft(path="path", scale_x = "drop", scale_y="drop", rotate="rotate", slices=100) circle(r = 1, $fn = 12);