discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Sweeping polygon over irregular shape?

KT
Kevin Toppenberg
Mon, Dec 3, 2018 8:20 PM

Marko,

A few years ago I was studying up on 3D engines and learned about quats,
but haven't thought about them since.  I seem to recall they are a method
of achieving a transformation and avoids gimble-lock that can occur with
standard XYZ transformation matrices.  Is there built in functionality for
quats or did you write a library for yourself?

Thanks for the code.  I will keep this as a reference.

Kevin

On Mon, Dec 3, 2018 at 3:16 PM berkenb Marko.Kl.Berkenbusch@gmail.com
wrote:

Your problem appears to be solved, but for what it's worth, I wrote a
'loft'
function a while ago that sweeps the interpolation between two
two-dimensional profiles along a three-dimensional path. It is very similar
to the other 'sweep' libraries mentioned (except mine is poorly documented
and the code probably unreadable, sorry ;-)). The transformations along the
path are computed using quaternions and the usual restrictions apply
(profiles have to be singly-connected and have the same number of points in
them, no self-intersections).
Maybe it is useful to somebody...
Marko

function flatten(vec) = [for (v=vec) for(e=v) e];
function Q_im(q) = [q[1], q[2], q[3]];
function Q_conj(q) = [q[0], -q[1], -q[2], -q[3]];
function Q_mult(q,p) =
[(q[0]*p[0]-q[1]*p[1]-q[2]*p[2]-q[3]*p[3]),(q[1]*p[0]+q[0]*p[1]+q[2]*p[3]-q[3]*p[2]),(q[2]*p[0]+q[0]*p[2]-q[1]*p[3]+q[3]*p[1]),(q[3]*p[0]+q[0]*p[3]+q[1]*p[2]-q[2]*p[1])];

function rotQ(q, a, n) = Q_mult(flatten([cos(a/2),n*sin(a/2)]),q);
function poly_rotQ(list, q) = [for (v=list)
Q_im(Q_mult(q,Q_mult([0,v.x,v.y,v.z],Q_conj(q))))];
function poly_rot2d(list, a) = [for (x=list) [cos(a)*x[0]+sin(a)*x[1],
-sin(a)x[0]+cos(a)x[1]]];
function poly_translate(list, d) = [for (v=list) v+d];
function interp_lists(l1, w1, l2, w2) = [for (i=[0:len(l1)-1])
w1
l1[i]+w2
l2[i]];

function poly_loft_faces (N_z, N_x, closed=false) = flatten([
(closed ? ([for (i=[0:N_x-1]) [(N_z-1)*N_x+i, (N_z-1)*N_x+(i+1)%N_x,
i],
for (i=[0:N_x-1]) [(i+1)%N_x, i, (N_z-1)N_x+(i+1)%N_x]])
: concat([[for (i=[0:N_x-1]) N_x-1-i]], [[for (i=[0:N_x-1])
(N_z-1)N_x+i]])), // caps
for (i=[0:N_z-2],j=[0:N_x-1]) [[(i+1)N_x+j, iN_x+j,
i
N_x+((j+1)%N_x)],[i
N_x+((j+1)%N_x), (i+1)*N_x+((j+1)%N_x),
(i+1)*N_x+j]]]);

// extrude a cross section linearly interpolated between cross sections cr1
and cr2 along path 'path',
// with optional tangential twist linearly increasing along path
module loft (path, cr1, cr2, twist=0) {
p = flatten([path, [2path[len(path)-1]-path[len(path)-2]]]);
pts = flatten([
for (i=1, d=p[1]-p[0], u=cross([0,0,1], d), un=norm(u), dn=norm(d),
a=asin(un/dn),
q=un>0?rotQ([1,0,0,0],a,u/un) : [1,0,0,0], n=d/dn, cr=cr1;
i<len(p);
d=p[i]-path[i-1], u=cross(n, d), un=norm(u), dn=norm(d),
a=asin(un/dn),
n=d/dn,q=un>0?rotQ(q,a,u/un):q,
cr=interp_lists(cr1,1-(i-1)/(len(p)-1),cr2,(i-1)/(len(p)-1)), i=i+1)
poly_translate(poly_rotQ(twist!=0?[for(v=poly_rot2d([for (v=cr)
[v.x,v.y,0]],i
twist/(len(p)-1))) [v.x,v.y,0]]:[for (v=cr) [v.x,v.y,0]],
q),
p[i-1])
]);
fcs = poly_loft_faces(len(path), len(cr1));
polyhedron(pts, fcs, convexity=8);
}

pH = [[-1, 1], [-0.8,1], [-0.8, 0.1], [0.8, 0.1], [0.8, 1], [1, 1],
[1, -1], [0.8, -1], [0.8, -0.1], [-0.8, -0.1], [-0.8, -1], [-1, -1]];
pH2 = [for (v=pH) 2v];
phelix = [for (i=[0:6:3
360]) 5*[cos(i), sin(i), i/360]];

loft(phelix, pH, pH2, -170);

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Marko, A few years ago I was studying up on 3D engines and learned about quats, but haven't thought about them since. I seem to recall they are a method of achieving a transformation and avoids gimble-lock that can occur with standard XYZ transformation matrices. Is there built in functionality for quats or did you write a library for yourself? Thanks for the code. I will keep this as a reference. Kevin On Mon, Dec 3, 2018 at 3:16 PM berkenb <Marko.Kl.Berkenbusch@gmail.com> wrote: > Your problem appears to be solved, but for what it's worth, I wrote a > 'loft' > function a while ago that sweeps the interpolation between two > two-dimensional profiles along a three-dimensional path. It is very similar > to the other 'sweep' libraries mentioned (except mine is poorly documented > and the code probably unreadable, sorry ;-)). The transformations along the > path are computed using quaternions and the usual restrictions apply > (profiles have to be singly-connected and have the same number of points in > them, no self-intersections). > Maybe it is useful to somebody... > Marko > > function flatten(vec) = [for (v=vec) for(e=v) e]; > function Q_im(q) = [q[1], q[2], q[3]]; > function Q_conj(q) = [q[0], -q[1], -q[2], -q[3]]; > function Q_mult(q,p) = > [(q[0]*p[0]-q[1]*p[1]-q[2]*p[2]-q[3]*p[3]),(q[1]*p[0]+q[0]*p[1]+q[2]*p[3]-q[3]*p[2]),(q[2]*p[0]+q[0]*p[2]-q[1]*p[3]+q[3]*p[1]),(q[3]*p[0]+q[0]*p[3]+q[1]*p[2]-q[2]*p[1])]; > > function rotQ(q, a, n) = Q_mult(flatten([cos(a/2),n*sin(a/2)]),q); > function poly_rotQ(list, q) = [for (v=list) > Q_im(Q_mult(q,Q_mult([0,v.x,v.y,v.z],Q_conj(q))))]; > function poly_rot2d(list, a) = [for (x=list) [cos(a)*x[0]+sin(a)*x[1], > -sin(a)*x[0]+cos(a)*x[1]]]; > function poly_translate(list, d) = [for (v=list) v+d]; > function interp_lists(l1, w1, l2, w2) = [for (i=[0:len(l1)-1]) > w1*l1[i]+w2*l2[i]]; > > > function poly_loft_faces (N_z, N_x, closed=false) = flatten([ > (closed ? ([for (i=[0:N_x-1]) [(N_z-1)*N_x+i, (N_z-1)*N_x+(i+1)%N_x, > i], > for (i=[0:N_x-1]) [(i+1)%N_x, i, (N_z-1)*N_x+(i+1)%N_x]]) > : concat([[for (i=[0:N_x-1]) N_x-1-i]], [[for (i=[0:N_x-1]) > (N_z-1)*N_x+i]])), // caps > for (i=[0:N_z-2],j=[0:N_x-1]) [[(i+1)*N_x+j, i*N_x+j, > i*N_x+((j+1)%N_x)],[i*N_x+((j+1)%N_x), (i+1)*N_x+((j+1)%N_x), > (i+1)*N_x+j]]]); > > > // extrude a cross section linearly interpolated between cross sections cr1 > and cr2 along path 'path', > // with optional tangential twist linearly increasing along path > module loft (path, cr1, cr2, twist=0) { > p = flatten([path, [2*path[len(path)-1]-path[len(path)-2]]]); > pts = flatten([ > for (i=1, d=p[1]-p[0], u=cross([0,0,1], d), un=norm(u), dn=norm(d), > a=asin(un/dn), > q=un>0?rotQ([1,0,0,0],a,u/un) : [1,0,0,0], n=d/dn, cr=cr1; > i<len(p); > d=p[i]-path[i-1], u=cross(n, d), un=norm(u), dn=norm(d), > a=asin(un/dn), > n=d/dn,q=un>0?rotQ(q,a,u/un):q, > cr=interp_lists(cr1,1-(i-1)/(len(p)-1),cr2,(i-1)/(len(p)-1)), i=i+1) > poly_translate(poly_rotQ(twist!=0?[for(v=poly_rot2d([for (v=cr) > [v.x,v.y,0]],i*twist/(len(p)-1))) [v.x,v.y,0]]:[for (v=cr) [v.x,v.y,0]], > q), > p[i-1]) > ]); > fcs = poly_loft_faces(len(path), len(cr1)); > polyhedron(pts, fcs, convexity=8); > } > > pH = [[-1, 1], [-0.8,1], [-0.8, 0.1], [0.8, 0.1], [0.8, 1], [1, 1], > [1, -1], [0.8, -1], [0.8, -0.1], [-0.8, -0.1], [-0.8, -1], [-1, -1]]; > pH2 = [for (v=pH) 2*v]; > phelix = [for (i=[0:6:3*360]) 5*[cos(i), sin(i), i/360]]; > > loft(phelix, pH, pH2, -170); > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
B
berkenb
Mon, Dec 3, 2018 8:42 PM

Kevin,

As far as I know, there are no quaternion functions built into the language;
however, they are quite simple to write, in the code I posted there are just
four of them, the three starting with "Q_" and "rotQ". The main reason I use
them is that I seem to be able to better remember them than rotation
matrices.

Marko

--
Sent from: http://forum.openscad.org/

Kevin, As far as I know, there are no quaternion functions built into the language; however, they are quite simple to write, in the code I posted there are just four of them, the three starting with "Q_" and "rotQ". The main reason I use them is that I seem to be able to better remember them than rotation matrices. Marko -- Sent from: http://forum.openscad.org/
KT
Kevin Toppenberg
Mon, Dec 3, 2018 10:41 PM

Marko,

So I understand how one can translate the vertices of a polygon in 3D
space.  But I am confused about how to make it into a 3D object.  Take for
example a square that is going to be swept through space to make a square
tube.  How does one figure out how to arrange the points in the resulting
faces?

[image: open_scad-1.png]

In the example, as the square is swept from P_init to P_final, there should
a face using points 1, 1b, 3, 3b.  And another using 1b, 1c, 3b, 3c.  That
should be OK to figure out.  But doesn't the ordering of the vertices have
to be in a counter-clockwise rotation so that the normal faces outward, and
thus has the proper orientation?  Using all those points to build a polygon
seem daunting.

Kevin T

On Mon, Dec 3, 2018 at 3:43 PM berkenb Marko.Kl.Berkenbusch@gmail.com
wrote:

Kevin,

As far as I know, there are no quaternion functions built into the
language;
however, they are quite simple to write, in the code I posted there are
just
four of them, the three starting with "Q_" and "rotQ". The main reason I
use
them is that I seem to be able to better remember them than rotation
matrices.

Marko

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Marko, So I understand how one can translate the vertices of a polygon in 3D space. But I am confused about how to make it into a 3D object. Take for example a square that is going to be swept through space to make a square tube. How does one figure out how to arrange the points in the resulting faces? [image: open_scad-1.png] In the example, as the square is swept from P_init to P_final, there should a face using points 1, 1b, 3, 3b. And another using 1b, 1c, 3b, 3c. That should be OK to figure out. But doesn't the ordering of the vertices have to be in a counter-clockwise rotation so that the normal faces outward, and thus has the proper orientation? Using all those points to build a polygon seem daunting. Kevin T On Mon, Dec 3, 2018 at 3:43 PM berkenb <Marko.Kl.Berkenbusch@gmail.com> wrote: > Kevin, > > As far as I know, there are no quaternion functions built into the > language; > however, they are quite simple to write, in the code I posted there are > just > four of them, the three starting with "Q_" and "rotQ". The main reason I > use > them is that I seem to be able to better remember them than rotation > matrices. > > Marko > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
B
berkenb
Mon, Dec 3, 2018 11:01 PM

Kevin,

To answer your second question first: you are correct, the ordering of the
polygon points has to conform to the OpenSCAD convention. I should have
mentioned that my function assumes that the points describing the cross
sections (second and third argument to the 'loft()' call)  have to be
correctly ordered for the resulting auto-generated triangles to be oriented
correctly. This is less tricky than it sounds, though - if the order is
wrong (can be seen by rendering in 'Thrown together' mode, for example) the
order simply has to be reversed. This is a manual step, the loft function
will gladly produce an invalid polyhedron.
Also, figuring out the triangulation of the swept walls is actually fairly
straightforward (assuming adjacent polygons are pretty similar), pretty much
exactly what you described in your illustration.

As for your first question: the routine first adds a z-coordinate of 0 to
each 2D polygon point (essentially turning it into a flat polygon in the
x-y-plane);
it then figures out the appropriate rotation of this 3D polygon to orient
its normal axis along a tangent to the path (well, really the direction
between two adjacent, discrete path points) and applies it (step wise along
the path, so that the twist along the path does not develop discontinuities)

  • that's the part that uses quaternions, but could equally well be done with
    R3 rotation matrices;
    finally, each rotated polygon is translated to its appropriate position
    along the path.

The rest then is really the answer to your first question, i.e. figuring out
a triangulation for the resulting point cloud.

Hope this helps,
Marko

--
Sent from: http://forum.openscad.org/

Kevin, To answer your second question first: you are correct, the ordering of the polygon points has to conform to the OpenSCAD convention. I should have mentioned that my function assumes that the points describing the cross sections (second and third argument to the 'loft()' call) have to be correctly ordered for the resulting auto-generated triangles to be oriented correctly. This is less tricky than it sounds, though - if the order is wrong (can be seen by rendering in 'Thrown together' mode, for example) the order simply has to be reversed. This is a manual step, the loft function will gladly produce an invalid polyhedron. Also, figuring out the triangulation of the swept walls is actually fairly straightforward (assuming adjacent polygons are pretty similar), pretty much exactly what you described in your illustration. As for your first question: the routine first adds a z-coordinate of 0 to each 2D polygon point (essentially turning it into a flat polygon in the x-y-plane); it then figures out the appropriate rotation of this 3D polygon to orient its normal axis along a tangent to the path (well, really the direction between two adjacent, discrete path points) and applies it (step wise along the path, so that the twist along the path does not develop discontinuities) - that's the part that uses quaternions, but could equally well be done with R3 rotation matrices; finally, each rotated polygon is translated to its appropriate position along the path. The rest then is really the answer to your first question, i.e. figuring out a triangulation for the resulting point cloud. Hope this helps, Marko -- Sent from: http://forum.openscad.org/
KT
Kevin Toppenberg
Mon, Dec 3, 2018 11:04 PM

It does help.
Thanks Marko

Kevin

On Mon, Dec 3, 2018 at 6:02 PM berkenb Marko.Kl.Berkenbusch@gmail.com
wrote:

Kevin,

To answer your second question first: you are correct, the ordering of the
polygon points has to conform to the OpenSCAD convention. I should have
mentioned that my function assumes that the points describing the cross
sections (second and third argument to the 'loft()' call)  have to be
correctly ordered for the resulting auto-generated triangles to be oriented
correctly. This is less tricky than it sounds, though - if the order is
wrong (can be seen by rendering in 'Thrown together' mode, for example) the
order simply has to be reversed. This is a manual step, the loft function
will gladly produce an invalid polyhedron.
Also, figuring out the triangulation of the swept walls is actually fairly
straightforward (assuming adjacent polygons are pretty similar), pretty
much
exactly what you described in your illustration.

As for your first question: the routine first adds a z-coordinate of 0 to
each 2D polygon point (essentially turning it into a flat polygon in the
x-y-plane);
it then figures out the appropriate rotation of this 3D polygon to orient
its normal axis along a tangent to the path (well, really the direction
between two adjacent, discrete path points) and applies it (step wise along
the path, so that the twist along the path does not develop
discontinuities)

  • that's the part that uses quaternions, but could equally well be done
    with
    R3 rotation matrices;
    finally, each rotated polygon is translated to its appropriate position
    along the path.

The rest then is really the answer to your first question, i.e. figuring
out
a triangulation for the resulting point cloud.

Hope this helps,
Marko

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

It does help. Thanks Marko Kevin On Mon, Dec 3, 2018 at 6:02 PM berkenb <Marko.Kl.Berkenbusch@gmail.com> wrote: > Kevin, > > To answer your second question first: you are correct, the ordering of the > polygon points has to conform to the OpenSCAD convention. I should have > mentioned that my function assumes that the points describing the cross > sections (second and third argument to the 'loft()' call) have to be > correctly ordered for the resulting auto-generated triangles to be oriented > correctly. This is less tricky than it sounds, though - if the order is > wrong (can be seen by rendering in 'Thrown together' mode, for example) the > order simply has to be reversed. This is a manual step, the loft function > will gladly produce an invalid polyhedron. > Also, figuring out the triangulation of the swept walls is actually fairly > straightforward (assuming adjacent polygons are pretty similar), pretty > much > exactly what you described in your illustration. > > As for your first question: the routine first adds a z-coordinate of 0 to > each 2D polygon point (essentially turning it into a flat polygon in the > x-y-plane); > it then figures out the appropriate rotation of this 3D polygon to orient > its normal axis along a tangent to the path (well, really the direction > between two adjacent, discrete path points) and applies it (step wise along > the path, so that the twist along the path does not develop > discontinuities) > - that's the part that uses quaternions, but could equally well be done > with > R3 rotation matrices; > finally, each rotated polygon is translated to its appropriate position > along the path. > > The rest then is really the answer to your first question, i.e. figuring > out > a triangulation for the resulting point cloud. > > Hope this helps, > Marko > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
R
runsun
Tue, Dec 4, 2018 6:38 PM

wow Marko, this is one of the coolest code I saw in a while !! Thx for
sharing.

btw the name "loft" seems a bit misleading. To my knowledge, "loft" means
(1)sweeping + (2)change of shape (like from square to circle): See :

https://www.cati.com/blog/2012/11/tips-and-tricks-for-using-the-solidworks-loft-feature/
berkenb wrote

Your problem appears to be solved, but for what it's worth, I wrote a
'loft'
function a while ago that sweeps the interpolation between two
two-dimensional profiles along a three-dimensional path. It is very
similar
to the other 'sweep' libraries mentioned (except mine is poorly documented
and the code probably unreadable, sorry ;-)). The transformations along
the
path are computed using quaternions and the usual restrictions apply
(profiles have to be singly-connected and have the same number of points
in
them, no self-intersections).
Maybe it is useful to somebody...
Marko

function flatten(vec) = [for (v=vec) for(e=v) e];
function Q_im(q) = [q[1], q[2], q[3]];
function Q_conj(q) = [q[0], -q[1], -q[2], -q[3]];
function Q_mult(q,p) =
[(q[0]*p[0]-q[1]*p[1]-q[2]*p[2]-q[3]*p[3]),(q[1]*p[0]+q[0]*p[1]+q[2]*p[3]-q[3]*p[2]),(q[2]*p[0]+q[0]*p[2]-q[1]*p[3]+q[3]*p[1]),(q[3]*p[0]+q[0]*p[3]+q[1]*p[2]-q[2]p[1])];
function rotQ(q, a, n) = Q_mult(flatten([cos(a/2),n
sin(a/2)]),q);
function poly_rotQ(list, q) = [for (v=list)
Q_im(Q_mult(q,Q_mult([0,v.x,v.y,v.z],Q_conj(q))))];
function poly_rot2d(list, a) = [for (x=list) [cos(a)*x[0]+sin(a)*x[1],
-sin(a)x[0]+cos(a)x[1]]];
function poly_translate(list, d) = [for (v=list) v+d];
function interp_lists(l1, w1, l2, w2) = [for (i=[0:len(l1)-1])
w1
l1[i]+w2
l2[i]];

function poly_loft_faces (N_z, N_x, closed=false) = flatten([
(closed ? ([for (i=[0:N_x-1]) [(N_z-1)*N_x+i, (N_z-1)*N_x+(i+1)%N_x,
i],
for (i=[0:N_x-1]) [(i+1)%N_x, i, (N_z-1)N_x+(i+1)%N_x]])
: concat([[for (i=[0:N_x-1]) N_x-1-i]], [[for (i=[0:N_x-1])
(N_z-1)N_x+i]])), // caps
for (i=[0:N_z-2],j=[0:N_x-1]) [[(i+1)N_x+j, iN_x+j,
i
N_x+((j+1)%N_x)],[i
N_x+((j+1)%N_x), (i+1)*N_x+((j+1)%N_x),
(i+1)*N_x+j]]]);

// extrude a cross section linearly interpolated between cross sections
cr1
and cr2 along path 'path',
// with optional tangential twist linearly increasing along path
module loft (path, cr1, cr2, twist=0) {
p = flatten([path, [2path[len(path)-1]-path[len(path)-2]]]);
pts = flatten([
for (i=1, d=p[1]-p[0], u=cross([0,0,1], d), un=norm(u), dn=norm(d),
a=asin(un/dn),
q=un>0?rotQ([1,0,0,0],a,u/un) : [1,0,0,0], n=d/dn, cr=cr1;
i<len(p);
d=p[i]-path[i-1], u=cross(n, d), un=norm(u), dn=norm(d),
a=asin(un/dn),
n=d/dn,q=un>0?rotQ(q,a,u/un):q,
cr=interp_lists(cr1,1-(i-1)/(len(p)-1),cr2,(i-1)/(len(p)-1)), i=i+1)
poly_translate(poly_rotQ(twist!=0?[for(v=poly_rot2d([for (v=cr)
[v.x,v.y,0]],i
twist/(len(p)-1))) [v.x,v.y,0]]:[for (v=cr) [v.x,v.y,0]],
q),
p[i-1])
]);
fcs = poly_loft_faces(len(path), len(cr1));
polyhedron(pts, fcs, convexity=8);
}

pH = [[-1, 1], [-0.8,1], [-0.8, 0.1], [0.8, 0.1], [0.8, 1], [1, 1],
[1, -1], [0.8, -1], [0.8, -0.1], [-0.8, -0.1], [-0.8, -1], [-1,
-1]];
pH2 = [for (v=pH) 2v];
phelix = [for (i=[0:6:3
360]) 5*[cos(i), sin(i), i/360]];

loft(phelix, pH, pH2, -170);

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list

Discuss@.openscad


$  Runsun Pan, PhD $ libs: scadx , doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), editor of choice: CudaText  ( OpenSCAD lexer ); $ Tips ; $ Snippets

--
Sent from: http://forum.openscad.org/

wow Marko, this is one of the coolest code I saw in a while !! Thx for sharing. btw the name "loft" seems a bit misleading. To my knowledge, "loft" means (1)sweeping + (2)change of shape (like from square to circle): See : https://www.cati.com/blog/2012/11/tips-and-tricks-for-using-the-solidworks-loft-feature/ berkenb wrote > Your problem appears to be solved, but for what it's worth, I wrote a > 'loft' > function a while ago that sweeps the interpolation between two > two-dimensional profiles along a three-dimensional path. It is very > similar > to the other 'sweep' libraries mentioned (except mine is poorly documented > and the code probably unreadable, sorry ;-)). The transformations along > the > path are computed using quaternions and the usual restrictions apply > (profiles have to be singly-connected and have the same number of points > in > them, no self-intersections). > Maybe it is useful to somebody... > Marko > > function flatten(vec) = [for (v=vec) for(e=v) e]; > function Q_im(q) = [q[1], q[2], q[3]]; > function Q_conj(q) = [q[0], -q[1], -q[2], -q[3]]; > function Q_mult(q,p) = > [(q[0]*p[0]-q[1]*p[1]-q[2]*p[2]-q[3]*p[3]),(q[1]*p[0]+q[0]*p[1]+q[2]*p[3]-q[3]*p[2]),(q[2]*p[0]+q[0]*p[2]-q[1]*p[3]+q[3]*p[1]),(q[3]*p[0]+q[0]*p[3]+q[1]*p[2]-q[2]*p[1])]; > function rotQ(q, a, n) = Q_mult(flatten([cos(a/2),n*sin(a/2)]),q); > function poly_rotQ(list, q) = [for (v=list) > Q_im(Q_mult(q,Q_mult([0,v.x,v.y,v.z],Q_conj(q))))]; > function poly_rot2d(list, a) = [for (x=list) [cos(a)*x[0]+sin(a)*x[1], > -sin(a)*x[0]+cos(a)*x[1]]]; > function poly_translate(list, d) = [for (v=list) v+d]; > function interp_lists(l1, w1, l2, w2) = [for (i=[0:len(l1)-1]) > w1*l1[i]+w2*l2[i]]; > > > function poly_loft_faces (N_z, N_x, closed=false) = flatten([ > (closed ? ([for (i=[0:N_x-1]) [(N_z-1)*N_x+i, (N_z-1)*N_x+(i+1)%N_x, > i], > for (i=[0:N_x-1]) [(i+1)%N_x, i, (N_z-1)*N_x+(i+1)%N_x]]) > : concat([[for (i=[0:N_x-1]) N_x-1-i]], [[for (i=[0:N_x-1]) > (N_z-1)*N_x+i]])), // caps > for (i=[0:N_z-2],j=[0:N_x-1]) [[(i+1)*N_x+j, i*N_x+j, > i*N_x+((j+1)%N_x)],[i*N_x+((j+1)%N_x), (i+1)*N_x+((j+1)%N_x), > (i+1)*N_x+j]]]); > > > // extrude a cross section linearly interpolated between cross sections > cr1 > and cr2 along path 'path', > // with optional tangential twist linearly increasing along path > module loft (path, cr1, cr2, twist=0) { > p = flatten([path, [2*path[len(path)-1]-path[len(path)-2]]]); > pts = flatten([ > for (i=1, d=p[1]-p[0], u=cross([0,0,1], d), un=norm(u), dn=norm(d), > a=asin(un/dn), > q=un>0?rotQ([1,0,0,0],a,u/un) : [1,0,0,0], n=d/dn, cr=cr1; > i&lt;len(p); > d=p[i]-path[i-1], u=cross(n, d), un=norm(u), dn=norm(d), > a=asin(un/dn), > n=d/dn,q=un&gt;0?rotQ(q,a,u/un):q, > cr=interp_lists(cr1,1-(i-1)/(len(p)-1),cr2,(i-1)/(len(p)-1)), i=i+1) > poly_translate(poly_rotQ(twist!=0?[for(v=poly_rot2d([for (v=cr) > [v.x,v.y,0]],i*twist/(len(p)-1))) [v.x,v.y,0]]:[for (v=cr) [v.x,v.y,0]], > q), > p[i-1]) > ]); > fcs = poly_loft_faces(len(path), len(cr1)); > polyhedron(pts, fcs, convexity=8); > } > > pH = [[-1, 1], [-0.8,1], [-0.8, 0.1], [0.8, 0.1], [0.8, 1], [1, 1], > [1, -1], [0.8, -1], [0.8, -0.1], [-0.8, -0.1], [-0.8, -1], [-1, > -1]]; > pH2 = [for (v=pH) 2*v]; > phelix = [for (i=[0:6:3*360]) 5*[cos(i), sin(i), i/360]]; > > loft(phelix, pH, pH2, -170); > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org ----- $ Runsun Pan, PhD $ libs: scadx , doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), editor of choice: CudaText ( OpenSCAD lexer );&nbsp;$ Tips ;&nbsp;$ Snippets -- Sent from: http://forum.openscad.org/
B
berkenb
Tue, Dec 4, 2018 7:10 PM

Hi runsun,

Thanks. The code posted does actually linearly interpolate between two
shapes as it sweeps along the path; the 'loft' call requires a path and two
cross sections. My example is a little weak, as it uses the first profile
scaled by a factor of 2 as the second profile, i.e. the effect isn't
terribly obvious...
Try this example (with the same loft function):

pts = 24;
l = 2;
psquare =  [for (i=[0:pts/4-1]) [-l/2, l/2-i/pts4l],
for (i=[pts/4:pts/2-1]) [-l/2+(i-pts/4)/pts4l, -l/2],
for (i=[pts/2:3pts/4-1]) [l/2, -l/2+(i-pts/2)/pts4l],
for (i=[3
pts/4:pts-1]) [l/2-(i-3pts/4)/pts4l, l/2]
];
pcircle = [for (i=[0:pts-1]) [sin(-i
360/pts), cos(-i360/pts)]];
phelix = [for (i=[0:6:3
360]) 5*[cos(i), sin(i), i/360]];

loft(phelix, pcircle, psquare);

There are probably still a few bugs in the implementation, I haven't used it
too much yet.

Marko

--
Sent from: http://forum.openscad.org/

Hi runsun, Thanks. The code posted does actually linearly interpolate between two shapes as it sweeps along the path; the 'loft' call requires a path and two cross sections. My example is a little weak, as it uses the first profile scaled by a factor of 2 as the second profile, i.e. the effect isn't terribly obvious... Try this example (with the same loft function): pts = 24; l = 2; psquare = [for (i=[0:pts/4-1]) [-l/2, l/2-i/pts*4*l], for (i=[pts/4:pts/2-1]) [-l/2+(i-pts/4)/pts*4*l, -l/2], for (i=[pts/2:3*pts/4-1]) [l/2, -l/2+(i-pts/2)/pts*4*l], for (i=[3*pts/4:pts-1]) [l/2-(i-3*pts/4)/pts*4*l, l/2] ]; pcircle = [for (i=[0:pts-1]) [sin(-i*360/pts), cos(-i*360/pts)]]; phelix = [for (i=[0:6:3*360]) 5*[cos(i), sin(i), i/360]]; loft(phelix, pcircle, psquare); There are probably still a few bugs in the implementation, I haven't used it too much yet. Marko -- Sent from: http://forum.openscad.org/
KT
Kevin Toppenberg
Tue, Dec 4, 2018 8:10 PM

Marko,

I am getting a syntax error on OpenSCAD http://www.openscad.org version
2015.03-2

I can't figure out what the problem is.  I tried reformatting the code so I
could understand it better.  But even before reformatting, it still has the
same error.

[image: Capture.PNG]

Any suggestions?

Thanks
Kevin

On Tue, Dec 4, 2018 at 2:11 PM berkenb Marko.Kl.Berkenbusch@gmail.com
wrote:

Hi runsun,

Thanks. The code posted does actually linearly interpolate between two
shapes as it sweeps along the path; the 'loft' call requires a path and two
cross sections. My example is a little weak, as it uses the first profile
scaled by a factor of 2 as the second profile, i.e. the effect isn't
terribly obvious...
Try this example (with the same loft function):

pts = 24;
l = 2;
psquare =  [for (i=[0:pts/4-1]) [-l/2, l/2-i/pts4l],
for (i=[pts/4:pts/2-1]) [-l/2+(i-pts/4)/pts4l, -l/2],
for (i=[pts/2:3pts/4-1]) [l/2, -l/2+(i-pts/2)/pts4l],
for (i=[3
pts/4:pts-1]) [l/2-(i-3pts/4)/pts4l, l/2]
];
pcircle = [for (i=[0:pts-1]) [sin(-i
360/pts), cos(-i360/pts)]];
phelix = [for (i=[0:6:3
360]) 5*[cos(i), sin(i), i/360]];

loft(phelix, pcircle, psquare);

There are probably still a few bugs in the implementation, I haven't used
it
too much yet.

Marko

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Marko, I am getting a syntax error on OpenSCAD <http://www.openscad.org> version 2015.03-2 I can't figure out what the problem is. I tried reformatting the code so I could understand it better. But even before reformatting, it still has the same error. [image: Capture.PNG] Any suggestions? Thanks Kevin On Tue, Dec 4, 2018 at 2:11 PM berkenb <Marko.Kl.Berkenbusch@gmail.com> wrote: > Hi runsun, > > Thanks. The code posted does actually linearly interpolate between two > shapes as it sweeps along the path; the 'loft' call requires a path and two > cross sections. My example is a little weak, as it uses the first profile > scaled by a factor of 2 as the second profile, i.e. the effect isn't > terribly obvious... > Try this example (with the same loft function): > > pts = 24; > l = 2; > psquare = [for (i=[0:pts/4-1]) [-l/2, l/2-i/pts*4*l], > for (i=[pts/4:pts/2-1]) [-l/2+(i-pts/4)/pts*4*l, -l/2], > for (i=[pts/2:3*pts/4-1]) [l/2, -l/2+(i-pts/2)/pts*4*l], > for (i=[3*pts/4:pts-1]) [l/2-(i-3*pts/4)/pts*4*l, l/2] > ]; > pcircle = [for (i=[0:pts-1]) [sin(-i*360/pts), cos(-i*360/pts)]]; > phelix = [for (i=[0:6:3*360]) 5*[cos(i), sin(i), i/360]]; > > loft(phelix, pcircle, psquare); > > There are probably still a few bugs in the implementation, I haven't used > it > too much yet. > > Marko > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
TP
Torsten Paul
Tue, Dec 4, 2018 8:15 PM

On 04.12.18 21:10, Kevin Toppenberg wrote:

I am getting a syntax error on OpenSCAD version 2015.03-2

This syntax with multiple generators is not available in
the 2015.03 release, you will need the dev version for that.

See http://www.openscad.org/downloads.html#snapshots

ciao,
Torsten.

On 04.12.18 21:10, Kevin Toppenberg wrote: > I am getting a syntax error on OpenSCAD version 2015.03-2 > This syntax with multiple generators is not available in the 2015.03 release, you will need the dev version for that. See http://www.openscad.org/downloads.html#snapshots ciao, Torsten.
R
runsun
Tue, Dec 4, 2018 9:40 PM

Thx Marko. It's phenomenal!! So simple and fast.


$  Runsun Pan, PhD $ libs: scadx , doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), editor of choice: CudaText  ( OpenSCAD lexer ); $ Tips ; $ Snippets

--
Sent from: http://forum.openscad.org/

Thx Marko. It's phenomenal!! So simple and fast. ----- $ Runsun Pan, PhD $ libs: scadx , doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), editor of choice: CudaText ( OpenSCAD lexer );&nbsp;$ Tips ;&nbsp;$ Snippets -- Sent from: http://forum.openscad.org/