
1234

This post was updated on .
Johan_s_examples.scadI have an object with seperate fingers. What I do is to sweep the fingers separately and then join them with the base of the object. I wonder if it is possible to sweep them in one.
In the example below it works a little but there is a connection between the two fingers
use <naca4.scad>
use <naca_sweep.scad>
sweep (gen_double_cylinder());
function gen_double_cylinder() =
[ for (h=[1,2,3])
for (height = (h==1)? [0:1:10]:
(h==2)? [15:5:200]:
[201:1:210])
let (R = 10*(210height/2)/210)
let (BB = (height<10)? sqrt(R*Rabs(Rheight)*abs(Rheight)):
(height>200)? sqrt(R*Rabs(200height)*abs(200height)):
R)
let (N = round(60R+BB))
let (LM = (height<100)? R: (height<150)? R*(50(height100))/50:0)
let (cyl = vec3D(doublecircle(BB,LM,60)))
T_(0,0,height,cyl)];
function doublecircle(R,LM,N) =
[ for (w = [0:round(360/N):719])
let (RR=10)
let (wlm = asin(LM/10))
(w<wlm)? [R*cos(wlm),R*sin(wlm)]:
(w<90)? [R*cos(w),R*sin(w)]:
(w<180)? [R*cos(w),R*sin(w)]:
(w<270)? [R*cos(w),R*sin(w)]:
(w<360wlm)? [R*cos(w),R*sin(w)]:
(w<360+wlm)? [R*cos(wlm),R*sin(wlm)]:
(w<450)? [2*RRR*cos(w),R*sin(w)]:
(w<540)? [2*RRR*cos(w),R*sin(w)]:
(w<630)? [2*RRR*cos(w),R*sin(w)]:
(w<720wlm)? [2*RRR*cos(w),R*sin(w)]:
[2*RRcos(wlm),+R*sin(wlm)]
];


This isn't specified. Sweep operates over a series of simple polygons. It could be specified to also work with vector of a series of polygons (each being a vector of vectors itself) or a series of a vector of polygons, but I don't see much use for this.
But it is easy to achieve by writing your own wrapper function that will decompose such a combined data structure, call sweep() for each component, and union the result.
If OpenSCAD had data structures such a packing would be more explicit, but operating with vectors of vectors of vectors of vectors data is prone to misinterpretation, besides all the multiple selfintersection perils.
To keep track of such monster structures, it might be a good idea to introduce some explicit typing scheme with OpenScad, otherwise you easily get lost, when tracking errors. At least in the case you mention, it might make a lot of sense. Here an example, how the constructors could look:
function point_xyz(x, y, z) = ["point3", [x, y, z]];
function point_v3(v3) = ["point3", [v[0], v[1], v[2]]]; // better explicit instead of just v3
function polygon(P) = ["polygon3", P]; // might also do an isSimple()test
With this, the next step is to define type aware affine operations for this objects. Not a big deal, but an approach that is somehow idiosyncratic.


I1ve been trying a different approach, namely to generate polyhedron data by functions. This means to separate the data generation from the polyhedron call. In sweeping, for example, all computations of vertices and faces are done by a function that generates a pair of vertices,faces. A very simple module show_data() receives this pair and pass the parameters to the polyhedron primitive.
This approach has shown to be versatile. I recently wrote a code to build triangular Bezier patches. To preview the patch surface I just wrote a function to generate the polyhedron data for a triangular patch, a specific and simple code, and send the data to the same module show_data. Another function converts simple polygons to polyhedron data. And another converts meshes (bidimensional matrix of points) to polyhedron data. Finally I have extended the show_data module to receive a list of polyhedron data, consolidate all of them in one polyhedron data to feed the polyhedron call once. This approach might be used to deal with fingers. Generate the polyhedron data of each finger individually and send everything to show_data() or any specific and simple substitute.
_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


This post was updated on .
The picture above shows the object that I want to make.
It is the right part of a saxophone key guard called the "engelsflugel"; a part of a famous Keilwerth saxophone.
What I do is to make 5 objects the base of the wing and the four points of the wing.
Each part has its own data structure and sweep operation.
By the way. Is it possible to decrease the number of points in a the serie of polygons in one sweep operation? For instance when near the point of the object there are no so many points needed.
@ronaldo
I am interested in your approach, but I am sorry my theoretical knowledge is limited so I can not understand your suggestion without an example.


Ronaldo wrote
I1ve been trying a different approach, namely to generate polyhedron data
by functions. This means to separate the data generation from the
polyhedron call. In sweeping, for example, all computations of vertices and
faces are done by a function that generates a pair of vertices,faces. A
very simple module show_data() receives this pair and pass the parameters
to the polyhedron primitive.
This is exactly what the sweep() from Naca_sweep.scad Johan is referring at does.
It is also the skin() approach.


Rudolf,
I reread your code of sweep and I think I am going a step further. Your module sweep receives polygonal sections, all of the same length, and builds the polyhedron wrapping them including caps. It is a very specialized module. In my approach, the preparation of the polyhedron data is done externally to the module. To make it clear here is a possible code of the module:
// Builds a polyhedron based on a list of polyhedron data
// polys  a list of polyhedron data, that is polys[i] = [ vert_list, face_lists ]
module make_polyhedron(polys, convexity = 10) {
vertlist = [for(p=polys, pt=p[0]) pt]; // collect all verts from polyhedra
vertlen = [for(p=polys) p[0] ];
acclen = acc_len(vertlen);
facets = [ for(i=[0:len(polys)1], f=polys[i][1] ) [ for(v=f) acclen[i]+v ] ];
polyhedron(
points = vertlist,
faces = facets,
convexity = convexity
);
function _accum_sum(l, offs=0, res=[]) =
len(res) == len(l) ?
res :
_accum_sum(l, offs+l[len(res)], concat(res, [ offs+l[len(res)] ] ));
function acc_len( f ) =
concat([0], _accum_sum([ for(fi=f) len(fi) ]));
}
As you see, make_polyhedron is simpler than sweep and basically unifies polyhedron data and send it to polyhedron primitive.
As part of this approach, each kind of object requires a specialized function to generate polyhedron data for it. For instance,
// generates polyhedron data for a mesh
// a mesh is a rectangular matrix of 3D points
function mesh2polyhedron(mesh, inv=false) =
let( n = len(mesh) != 0 ? len(mesh) : 0,
m = n==0 ? 0 : len(mesh[0]) != 0 ? len(mesh[0]) : 0 ,
l = n*m)
l > 0 ?
let( range = inv ? [len(mesh)1: 1: 0] : [0:len(mesh)1],
vertices = l == 0 ? [] : [ for(i=range) for(pt=mesh[i]) pt ],
tris = concat( [ for(i=[0:n2],j=[0:m2]) [ i*m+j, i*m+j+1, (i+1)*m+j ] ] ,
[ for(i=[0:n2],j=[0:m2]) [ i*m+j+1, (i+1)*m+j+1, (i+1)*m+j ] ] ) )
[ vertices, tris ]:
[] ;
// generates polyhedron data for a closed polygonal face
function polygon2polyhedron(polygon, inv=false) =
let( vertices = polygon,
range = inv ? [len(polygon)1: 1: 0] : [0:len(polygon)1],
facets = [[for(i=range) i ]] )
[ vertices, facets ];
Now, the application code just call the appropriate functions with pieces of the polyhedron surface, concatenate all of them and send to make_polyhedron.
The flexibility of the approach is evident when a new kind of object (like a Bezier triangular patch, or a sweep or loft) is created and just one function is needed to be coded to include it in the system. If the cap of sweep is optional, the main code may build a special cap made of Bezier patches before sending all to make_polyhedron. And so, fingers are also possible.


This post was updated on .
You are right, it is obvious that you can have a more general interface for calling polyhedron(). skin() for instance accepts polygons with different vertex numbers  and uses some automatism to deal with that. Of course you also can prepare a polyhedron call by just wrapping all the needed information about faces and vertices into a common structure and unwrap that again before doing the call.
But isn't that more an interface rather than an abstraction that uses some regularization to reduce the amount of information (and preparation) to be passed? The former is how I understand your approach  at least from the code you show. Knowing your stuff a bit, I suppose there is some more magic behind, e.g. where your beziers come into play.
What do I mean by magic? When I designed sweep() my primary aim was to get a tool for generalized extrusions that allows for refinement by using interpolation schemes (applicable by the polygon generator AND the path generator). The secondary aim was to have all "knitting" be done by the function. The price for this "magic" was some structural constraints, like
1. all polygons must be simple and have an equal number of points  which skin() doesn't require
2. each two subsequent polygons get connected with a fixed scheme: nth vertex to nth vertex
3. polygons must describe a nonselfintersecting extrusion path
From a topological point of view, this is of course not even the tip of the iceberg, but it is a milestone away from what linear_extrude() can do. You are welcome to define new schemes that allow e.g. to define extrusions with furcations and anastomoses. But: As long as I have to compose the full points list AND full faces list on my own, I don't see much progress. It is the (hidden) magic that turns an interface into a new concept.


Rudolf,
I agree with you. My observations about how to connect parts in one module to render a polyhedron is a low level technique. My point is that it is a very flexible one and it allows many different uses, and that is an important property in low level stages. I expect that this will become apparent in the following.
Yes, I have a dream! I want to render organic models in OpenSCAD.
When I started to write my Bezier library in OpenSCAD I had no idea how to integrated it. Soon I realized that in a noninteractive environment like OpenSCAD, the Bezier control points are as hard to work with as the definition of faces of a complex polyhedron. So I needed to address those two issues.
At first, I worked in modules to help me to visualize and debug Bezier curves and tensor product surfaces. Those are nonmanifold geometries. To build a model to be rendered I needed a module to integrate a list of surface patch meshes with a list of faces in one polyhedron complying the manifold topology: an output module. This task was simplified by one key observation: the list of vertices of a polyhedron may have many incarnations of the same vertex coordinates and two different faces will be connected even when they refer to different incarnations of the same vertex. CGAL seems to collapse all vertices with same coordinates and connect faces based on vertex coordinates, similar to STL processing. This observation simplified a lot the task of integrating many surfaces in one polyhedron: each face may be processed individually.
The first issue, how to deal with the definition of lots of control points of a model, is bit harder to solve. After the implementation of spline interpolation methods I started to explore loft techniques to connect "automatically" individual surface patches. The following image depict one of those experiment.
The model in the back plane shows an exploded view of the middle one. It is composed by 5 parts: two rounded caps, two splines surfaces interpolating the bluemarked points and a cubic loft surface connecting them. All surfaces are C2 and all surfacesurface meetings are G1, geometrically differentiable. When all the meshes of those 5 patches are integrated in one polyhedron we have one acceptable CGAL manifold. It can boolean operated. No, not yet! As you see in this thrown together image, some faces are wrongly oriented. To repair this you can either change the surface definitions accordingly or, easier, just mark them to be reverted by the output module.
This is the current state of my "magic", as you call it. The only data to build this model were the 24 bluemarked points and some few floats that control the shape of the lofts. The first plane model, for instance, used the same definitions of the middle one except for the higher "scales" of the tangents at the caps border. To build this kind of model you don't need to understand fully Bezier curves and surface theory.
But my goal is a bit further. I want to integrate triangular Bezier patches and create shapes with genus. After all, the model above is nothing more than a generalized sweep, skin or loft with rounded caps. I have no tools yet to create a model with genus. If to be able to build models with genus I need to model furcations and anastomoses and, therewith, fingers and hands!
And this is the "magic" I am pursuing now.


Ronaldo, I expected something like this and I am looking forward to see your patchwork approach implemented as fullgrown concept, which will be a long way to go, especially when you want address different genera. It was also my first impression about the use of Beziers in OpenSCAD that you will easily get lost without having at least a minimum of (preferable GUI) interactivity.
I like your idea,
CGAL seems to collapse all vertices with same coordinates and connect faces based on vertex coordinates, similar to STL processing. This observation simplified a lot the task of integrating many surfaces in one polyhedron: each face may be processed individually.
but as far as I know, this is not documented and might change in future. Usually it is not a good idea, to build new cities on such grounding. Maybe one from the dev team can say more about this.
But back to your approach. I have the impression that it might be a good idea to connect the simpler sweep() approach with your stuff: Using sweep() with all its restrictions and advantages (planar polgons, easy transformations, description, and meshing) for the G0/G1 parts of a design, and to model just the transition zones, where furcations actually happen, with your pathwork design. In this case, your generator's input could be one or more (consecutive) planar polygons, already transformed into 3D, for each branch. The first polygon defines the border (meeting zone) with all its vertices, the others slopes (also of higher order)  all polygons together form your boundary condition, that can be transferred into Bezier parameters. The desired output would be the e.g. C2 continued mesh. You can view this as an isolated problem, since it is perfectly allowed to union polyhedra (even it is slow right now).
I have sketched this approach once in an other thread, but never found time (and enough reason) to go deeper into the sloppy modelling of the transition zone.


Parkinbot wrote
But back to your approach. I have the impression that it might be a good idea to connect the simpler sweep() approach with your stuff: Using sweep() with all its restrictions and advantages (planar polgons, easy transformations, description, and meshing) for the G0/G1 parts of a design, and to model just the transition zones, where furcations actually happen, with your pathwork design. In this case, your generator's input could be one or more (consecutive) planar polygons, already transformed into 3D, for each branch. The first polygon defines the border (meeting zone) with all its vertices, the others slopes (also of higher order)  all polygons together form your boundary condition, that can be transferred into Bezier parameters. The desired output would be the e.g. C2 continued mesh. You can view this as an isolated problem, since it is perfectly allowed to union polyhedra (even it is slow right now).
I have sketched this approach once in an other thread, but never found time (and enough reason) to go deeper into the sloppy modelling of the transition zone.
I think I missed the discussion you have referred: I have never seen before the image of your furcation. Very clever illustration.
To integrate sweep/skin in my approach was already in my plans. If you look at my version of Linde's sweep code, you will see that the computation of vertices and faces of the sweep is done by a function that outputs the pair [ <vertex list>, <face list> ] good enough to send to a polyhedron, what is done by a separate module. Besides, I sketched loft functions to connect two closed arcs that works even when they have different len(). It is a kind of C1 or C2 sweep (like the one I have shown in my last image). In another experiment I had generalized the skin to smoothly wrap a sequence of 2D closed arcs. Everything done with spline interpolation.
So, I have the most basic tools to make "tubes". Now I am facing the hard part: to model furcations. One of the problems is how to easily specify the topology so that a suitable geometry could be created. I see two possible specification approaches: define the geometry of the nodes or connectors and then how to connect them by tubes; or, define the geometry of the tubes (sweeps) and then the topology of the nodes or connectors. Either way, I need ways of define hexagonal (or octogonal, etc) smooth surfaces. And for that the triangular Bezier patches will be handy.


Ronaldo wrote
One of the problems is how to easily specify the topology so that a suitable geometry could be created.
Here we are. Find a maximal information reduced representation capable to describe everything you want in a intuitive way. Feed your magic with e.g. some small matrix and let it create a whole world.


Ronaldo wrote
The only data to build this model were the 24 bluemarked points and some few floats that control the shape of the lofts. The first plane model, for instance, used the same definitions of the middle one except for the higher "scales" of the tangents at the caps border. To build this kind of model you don't need to understand fully Bezier curves and surface theory.
@ Ronaldo, trying to understand your approach here. Since there are only 24 points of data, which defines the 2nd and the 4th sections, how was the 3rd section (the middle one) generated ?


@ Ronaldo, another question: the make_polyhedron. When you connect multiple polyhedral, do they have to have the same # of points on both of the connecting surfaces ?


Another good approach would be to finally implement the DoWhatImean() function.


runsun wrote
@ Ronaldo, another question: the make_polyhedron. When you connect multiple polyhedral, do they have to have the same # of points on both of the connecting surfaces ?
Good question. They must have the same points with the same coordinates. If one of them has a sequence of points along the border edge of the other, it doesn't work (possibly because of numerical differences).


runsun wrote
@ Ronaldo, trying to understand your approach here. Since there are only 24 points of data, which defines the 2nd and the 4th sections, how was the 3rd section (the middle one) generated ?
It is simpler than it sounds. I apply a loft function that interpolates (position and derivatives) the border of the 2nd surface and the border of the 4th. This function operates on the meshes and compute discrete derivatives of the meshes. If I have the value and derivative in two points I may apply Hermite interpolation. That is what is done at each pair of points from border 1 and border 2.
// loft to surfaces with continuity G1
// requires that the borders to be lofted have same refinement
// h1 and h2 are derivative scales
function _loftSurfacesG1(s1,s2,h1=1,h2=1) =
len(s1[0]) == len(s2[0]) ?
let( n = $rnu, n1 = len(s1), n2 = len(s2),
c1 = concat(s1[n11], cl1 ? [s1[n11][0]]: []), // edge curve of s1
b1 = concat(s1[n12], cl1 ? [s1[n12][0]]: []), // near edge curve of s1
c2 = concat(s2[0], cl2 ? [s2[0][0]] : []),// edge curve of s2
b2 = concat(s2[1], cl2 ? [s2[1][0]] : []), // near edge curve of s2
d1 = h1*(c1b1)*n1, // derivative at edge of s1
d2 = h2*(b2c2)*n2, // derivative at edge of s2
s = concat([c1],_H1_2B(c1, c2, d1, d2)) )
Bezier_curve(s,$rn=n) :
["incompatible surface borders"];
In the code, _H1_2B is a conversion from Hermite data to Bezier degree 3 data.
Things are a bit harder if the two meshes to interpolate have distinct number of border points. Some regularization is needed. So, the two mesh borders are resampled accordingly.


Ronaldo wrote
runsun wrote
@ Ronaldo, another question: the make_polyhedron. When you connect multiple polyhedral, do they have to have the same # of points on both of the connecting surfaces ?
Good question. They must have the same points with the same coordinates. If one of them has a sequence of points along the border edge of the other, it doesn't work (possibly because of numerical differences).
That is not right. I have to investigate it a little more. The following image shows the resampling I did in lofting two differently refined surfaces.
The resampling process takes some points inside the border edge of the meshes to loft. And the polyhedron joining them is a manifold.


Ronaldo wrote
Ronaldo wrote
runsun wrote
@ Ronaldo, another question: the make_polyhedron. When you connect multiple polyhedral, do they have to have the same # of points on both of the connecting surfaces ?
Good question. They must have the same points with the same coordinates. If one of them has a sequence of points along the border edge of the other, it doesn't work (possibly because of numerical differences).
That is not right. I have to investigate it a little more. The following image shows the resampling I did in lofting two differently refined surfaces.
Well, I have rechecked this. It seems that the vertices of meeting parts should be exactly the same. Try this code with show edges:
// tetrahedron with a missin face
p = [ [0,0,0], [10,0,0], [0,10,0], [0,0,10] ];
f = [ [0,1,2], [0,3,1], [0,2,3] ];
// additional faces to close it
n=3;
degface = false;
ps = [for(i=[0:n+1]) p[1]*i/(n+1) + p[2]*(n+1i)/(n+1) ];
fs = [for(i=[1:n+1]) [ 3, 3+i, 4+i ] ];
df = n>0 && degface ? [for(i=[n+2:1:1]) 3+i ] : [];
// the full tetrahedron
difference(){
polyhedron(concat(p,ps),concat(f,fs,[df]));
cube(3,center=true);
translate([10,0,0]) cube(3,center=true);
}
Forget df for while. It is an empty list.
When n=0, ps will have 2 points equal to some points of list p and fs will be the missing face. The difference will be a manifold. The render is fine.
When n>0, the missing face is subdivided in n parts. ps will have n+2 points and fs will have n+1 faces whose union is the missing face. The difference is no more a manifold, the render generates a warning and doesn't displays some of the difference faces.
Now, a revealing trick: when degface=true, df is a degenerated face, all vertices on a line. If this face is included in the polyhedron face list, all gets right, CGAL builds a manifold from the polyhedron. This suggests that CGAL expects a correct topological specification from polyhedron even if some faces are degenerated. I think the same kind of consistence should be expected in STL files.
I had not tested with F6 my last loft model. It doesn't work either. I will have to review my loft for this case creating a transition.

1234
