Skip to content

OEP1: Generalized extrusion module

Marius Kintel edited this page Jun 11, 2014 · 38 revisions

Goals

  • Make extrusions more general and flexible
  • Handle transformations along the extrusion
  • Allow extrusion along a path
  • Make use of functions to describe the paths

Status and References

Status as of 2014-05-26

  • Different example/prototype code exists
  • Open discussion about how to fit the functionality into the language
  • Discussions indicate that this should be split into sweep() and loft(), where the primary difference is that loft() offers morphing between different 2D shapes
  • Some user-space experiments: https://github.com/openscad/list-comprehension-demos

Github

Issue: https://github.com/openscad/openscad/issues/114

Mailinglist

API

This attempts to formalize an API based on discussions so far.

sweep(<path>) <2d shape>

This version sweeps the given 2D shape across the path. Different types of paths have been suggested:

Discretized space curves

In this case, a path is a list of 2D or 3D coordinates describing a curve in space. The user is thus responsible for discretizing the curve.

This is likely to be the easier way for users to describe paths. The downside is that we need to calculate reference frames along the path. As there are many ways of doing this, we should offer a reasonable default, and perhaps additional parameters for typical use-cases such as:

  • Modified Frenet
  • Torsion minimizing
  • Lock one axis (to a direction or a point in space)
  • Inertial/glide One other aspect is the possibility to interpolate the path using splines or subdivision functions.

Discretized transformation path

In this case, a path is a list of 3x4 or 4x4 transformation matrices describing a curve in space including its reference frame. This is a generalization of the above and would pass the responsibility to define reference frames to the user. To make this more manageable, we should supply a user-space library for building such frames.

As with space curves, interpolating between transformations could be useful. If we want to e.g. replace today's linear_extrude with a user-space implementation using sweep, this would be necessary (but could perhaps be done in user-space as well).

Function

In this case, the curve is defined by a passed function reference. The function is parametrizing the path using a parameter from 0-1. The return value of the function is either a 2D/3D vector or a 3x3/4x4 transformation matrix (see above).

Open questions:

  • How do we decide on discretization of the function? -- We could make use of $fa, $fs, and/or offer an explicit slices parameter. The slices could be located either uniformly along the path or dependent on local curvature.
  • This requires us to extend the OpenSCAD language with function values. We could postpone this variant to a later point in time.
  • How do we handle self-intersecting extrusions? Ideally, we should guarantee a manifold result, but we might need to postpone this guarantee and let the user be responsible for this (by e.g. splitting up a sweep into multiple non-intersecting sub-sweeps and let CGAL do the union job).

loft() <oriented 2d shapes>

loft is also called skin in the discussions.

The idea of loft is that it successively morphs one shape into the next. To be able to discretize shape or movement formulas, it's possible to use for loops with loft, e.g.:

loft() for(i=[0:10]) translate([0,0,i]) square(10*i);

Points for discussion:

  • For certain applications, the profiles being lofted must be nonplanar. polygon() with 3D points could possibly be used for this.
  • How to associate boundary points between two profiles. Some kind of tension minimization may be best. Steiner points could be added to reduce triangulation artifacts.
  • We could potentially offer different kinds of transitions between profiles. While a linear loft between two profiles would leave the quantization entirely to the user, intermediary slices could be generated using constraints or parameters for each loft node. Such constraints could for example define a smooth or hard transition (continuous or discontinuous surface gradients), rounding/fillets, etc.

Old Ideas

http://piratepad.net/LLU3j8QIJh

Look at e.g. Mathematica's ParametricPlot3D function for inspiration.

How to describe the 2D shapes

2D shapes can be described in two ways:

  • Use OpenSCAD's 2D subsystem to create a 2D shape using a tree of CSG operators
  • Describe 2D shapes using lists (e.g. through list comprehensions, concat and other functions)

The first way is likely to be more logic to average users, while the second might be used my more mathematically minded people. We'd likely have to support both ways. As lists of vertices can be fed directly to the polygon() module, supporting the first explicitly should be sufficient.

How to bind the function

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.

Impact of merging namespaces

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.

How to make the API general

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.

How to integrate the functions

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.

How to call the functions

  • Always assume f(t) and sweep t between 0 and 1?
  • Introduce some kind of generator function to produce a list of points / values?

Examples of models that could be generated by this type of functionality

Extrusion with function based transformations

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);

loft-3

Extrusion along a path

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]);
}

loft-4

Combining things together: Teardrop

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);

loft-5