NH
nop head
Fri, Feb 1, 2019 5:42 PM
I think your accumulate_rotations will access two past the end of rots. My
equivalent code looks like this:
let(len = len(path),
n = len - 1,
tangents = [tangent(path, loop ? n : 0, 0, 1),
for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, n - 1, n, loop ? 0 : n)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
rot = i < n ? rotate_from_to(tangents[i],
tangents[i + 1]) * rot : undef, i = i + 1) rot],
The third section of the C style loop gets executed before the test
condition.
On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano rcmpersiano@gmail.com wrote:
One thing to note is that it is possible to accumulate the rotations in a
C style for loop now. It no longer requires a recursive function.
Yes, I just changed accumulate_rotations() in my code to:
function accumulate_rotations(rots) =
[for( i = 0, R = rots[0];
i <= len(rots);
i=i+1, R = rots[i]*R)
R ];
and minimizing_rotations() to:
function minimizing_rotations(tangents) =
// avoid singularity at the first rotation of the sequence
[ let( axis = unit(cross([0,0,1],tangents[1])) )
axis*axis >= 0.99 || tangents[1][2]>=0 ?
rotate_from_to([0,0,1],tangents[1]) :
[[1,0,0],[0,-1,0],[0,0,-1]],
for (i = [1:len(tangents)-2])
rotate_from_to(tangents[i],tangents[i+1])
];
I think I am in position now to update my code in github with the
conclusions of this discussion.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I think your accumulate_rotations will access two past the end of rots. My
equivalent code looks like this:
let(len = len(path),
n = len - 1,
tangents = [tangent(path, loop ? n : 0, 0, 1),
for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, n - 1, n, loop ? 0 : n)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
rot = i < n ? rotate_from_to(tangents[i],
tangents[i + 1]) * rot : undef, i = i + 1) rot],
The third section of the C style loop gets executed before the test
condition.
On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano <rcmpersiano@gmail.com> wrote:
>
> nop head <nop.head@gmail.com> wrote:
>
>> One thing to note is that it is possible to accumulate the rotations in a
>> C style for loop now. It no longer requires a recursive function.
>>
>
> Yes, I just changed accumulate_rotations() in my code to:
>
> function accumulate_rotations(rots) =
> [for( i = 0, R = rots[0];
> i <= len(rots);
> i=i+1, R = rots[i]*R)
> R ];
>
>
> and minimizing_rotations() to:
>
> function minimizing_rotations(tangents) =
> // avoid singularity at the first rotation of the sequence
> [ let( axis = unit(cross([0,0,1],tangents[1])) )
> axis*axis >= 0.99 || tangents[1][2]>=0 ?
> rotate_from_to([0,0,1],tangents[1]) :
> [[1,0,0],[0,-1,0],[0,0,-1]],
> for (i = [1:len(tangents)-2])
> rotate_from_to(tangents[i],tangents[i+1])
> ];
>
>
> I think I am in position now to update my code in github with the
> conclusions of this discussion.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
NH
nop head
Fri, Feb 1, 2019 6:10 PM
A slightly more readable and more complete version:
//
// Calculate the unit tangent at a vertex given the indices before and
after. One of these can be the same as i in the case
// of the start and end of a non closed path.
//
function tangent(path, before, i, after) = unit(unit(path[after] - path[i])
-
unit(path[before] - path[i]));
//
// Generate all the surface points of the swept volume.
//
function skin_points(profile, path, loop, twist = 0) =
let(len = len(path),
last = len - 1,
tangents = [tangent(path, loop ? last : 0, 0, 1),
for(i = [1 : last - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, last - 1, last, loop ? 0 : last)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
i = i + 1,
rot = i < len ? rotate_from_to(tangents[i - 1],
tangents[i]) * rot : undef) rot],
missmatch = loop ? calculate_twist(rotations[0], rotations[last]) :
0,
rotation = missmatch + twist
)
[for(i = [0 : last])
let(za = rotation * i / last,
t = orientate_r(path[i], rotations[i])
)
each transform_points(profile, za ? m_rotate([0, 0, za]) * t : t)
];
On Fri, 1 Feb 2019 at 17:42, nop head nop.head@gmail.com wrote:
I think your accumulate_rotations will access two past the end of rots. My
equivalent code looks like this:
let(len = len(path),
n = len - 1,
tangents = [tangent(path, loop ? n : 0, 0, 1),
for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, n - 1, n, loop ? 0 : n)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
rot = i < n ? rotate_from_to(tangents[i],
tangents[i + 1]) * rot : undef, i = i + 1) rot],
The third section of the C style loop gets executed before the test
condition.
On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
One thing to note is that it is possible to accumulate the rotations in
a C style for loop now. It no longer requires a recursive function.
Yes, I just changed accumulate_rotations() in my code to:
function accumulate_rotations(rots) =
[for( i = 0, R = rots[0];
i <= len(rots);
i=i+1, R = rots[i]*R)
R ];
and minimizing_rotations() to:
function minimizing_rotations(tangents) =
// avoid singularity at the first rotation of the sequence
[ let( axis = unit(cross([0,0,1],tangents[1])) )
axis*axis >= 0.99 || tangents[1][2]>=0 ?
rotate_from_to([0,0,1],tangents[1]) :
[[1,0,0],[0,-1,0],[0,0,-1]],
for (i = [1:len(tangents)-2])
rotate_from_to(tangents[i],tangents[i+1])
];
I think I am in position now to update my code in github with the
conclusions of this discussion.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
A slightly more readable and more complete version:
//
// Calculate the unit tangent at a vertex given the indices before and
after. One of these can be the same as i in the case
// of the start and end of a non closed path.
//
function tangent(path, before, i, after) = unit(unit(path[after] - path[i])
- unit(path[before] - path[i]));
//
// Generate all the surface points of the swept volume.
//
function skin_points(profile, path, loop, twist = 0) =
let(len = len(path),
last = len - 1,
tangents = [tangent(path, loop ? last : 0, 0, 1),
for(i = [1 : last - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, last - 1, last, loop ? 0 : last)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
i = i + 1,
rot = i < len ? rotate_from_to(tangents[i - 1],
tangents[i]) * rot : undef) rot],
missmatch = loop ? calculate_twist(rotations[0], rotations[last]) :
0,
rotation = missmatch + twist
)
[for(i = [0 : last])
let(za = rotation * i / last,
t = orientate_r(path[i], rotations[i])
)
each transform_points(profile, za ? m_rotate([0, 0, za]) * t : t)
];
On Fri, 1 Feb 2019 at 17:42, nop head <nop.head@gmail.com> wrote:
> I think your accumulate_rotations will access two past the end of rots. My
> equivalent code looks like this:
>
> let(len = len(path),
> n = len - 1,
>
> tangents = [tangent(path, loop ? n : 0, 0, 1),
> for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
> tangent(path, n - 1, n, loop ? 0 : n)],
>
> rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
> tangents[0], tangents[1]);
> i < len;
> rot = i < n ? rotate_from_to(tangents[i],
> tangents[i + 1]) * rot : undef, i = i + 1) rot],
>
> The third section of the C style loop gets executed before the test
> condition.
>
>
> On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano <rcmpersiano@gmail.com>
> wrote:
>
>>
>> nop head <nop.head@gmail.com> wrote:
>>
>>> One thing to note is that it is possible to accumulate the rotations in
>>> a C style for loop now. It no longer requires a recursive function.
>>>
>>
>> Yes, I just changed accumulate_rotations() in my code to:
>>
>> function accumulate_rotations(rots) =
>> [for( i = 0, R = rots[0];
>> i <= len(rots);
>> i=i+1, R = rots[i]*R)
>> R ];
>>
>>
>> and minimizing_rotations() to:
>>
>> function minimizing_rotations(tangents) =
>> // avoid singularity at the first rotation of the sequence
>> [ let( axis = unit(cross([0,0,1],tangents[1])) )
>> axis*axis >= 0.99 || tangents[1][2]>=0 ?
>> rotate_from_to([0,0,1],tangents[1]) :
>> [[1,0,0],[0,-1,0],[0,0,-1]],
>> for (i = [1:len(tangents)-2])
>> rotate_from_to(tangents[i],tangents[i+1])
>> ];
>>
>>
>> I think I am in position now to update my code in github with the
>> conclusions of this discussion.
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
RP
Ronaldo Persiano
Fri, Feb 1, 2019 8:18 PM
Yes, it is a good solution to incorporate the operations of
minimizing_rotations() inside accumulate_rotations().
Btw, considering that rotate_from_to() always receive unit vectors, it can
be simplified to:
function rotate_from_to(a,b) =
let( axis = cross(a,b) )
axis*axis>0.0001? let( ax = unit(axis) )
transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
[[1,0,0],[0,1,0],[0,0,1]];
I noticed that you have a three argument version of it.
Yes, it is a good solution to incorporate the operations of
minimizing_rotations() inside accumulate_rotations().
Btw, considering that rotate_from_to() always receive unit vectors, it can
be simplified to:
function rotate_from_to(a,b) =
let( axis = cross(a,b) )
axis*axis>0.0001? let( ax = unit(axis) )
transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
[[1,0,0],[0,1,0],[0,0,1]];
I noticed that you have a three argument version of it.
NH
nop head
Fri, Feb 1, 2019 8:49 PM
Yes I made my tangents units to simply rotate_from_to. My version currently
looks like this:
//
// Computes the rotation with minimum angle that brings UNIT vectors a to b
// the code fails if a and b are opposed to each other unless a third vector
// is specified to break the ambiguity.
//
function rotate_from_to(a, b, c) =
let(axis = unit(cross(a, b)))
axis * axis >= 0.99 ? m_transpose([b, axis, cross(axis, b)]) * [a,
axis, cross(axis, a)]
: a * b > 0 ? m_identity(3)
: assert(!is_undef(c), "path
doubles back on itself")
rotate_from_to(a, b + c / 1000);
I am still experimenting with different ways to decide the first rotation.
Why do you dot axis with itself before making it a unit? I don't really
understand how that test works. The dot product of a and b gives the cosine
of the angle between them does it not? +1 if coincident and -1 if opposed.
A very odd thing happened today. I spent the morning trying to speed up the
code drawing my ribbon cable but none of the optimisations made significant
difference. It was stuck at 11-12 seconds. I went for a walk, came back and
tidied up my variable names to be more meaningful and it suddenly runs
twice as fast in 5-6 seconds. No idea why as I didn't change anything
significant.
On Fri, 1 Feb 2019 at 20:18, Ronaldo Persiano rcmpersiano@gmail.com wrote:
Yes, it is a good solution to incorporate the operations of
minimizing_rotations() inside accumulate_rotations().
Btw, considering that rotate_from_to() always receive unit vectors, it can
be simplified to:
function rotate_from_to(a,b) =
let( axis = cross(a,b) )
axis*axis>0.0001? let( ax = unit(axis) )
transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
[[1,0,0],[0,1,0],[0,0,1]];
I noticed that you have a three argument version of it.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Yes I made my tangents units to simply rotate_from_to. My version currently
looks like this:
//
// Computes the rotation with minimum angle that brings UNIT vectors a to b
// the code fails if a and b are opposed to each other unless a third vector
// is specified to break the ambiguity.
//
function rotate_from_to(a, b, c) =
let(axis = unit(cross(a, b)))
axis * axis >= 0.99 ? m_transpose([b, axis, cross(axis, b)]) * [a,
axis, cross(axis, a)]
: a * b > 0 ? m_identity(3)
: assert(!is_undef(c), "path
doubles back on itself")
rotate_from_to(a, b + c / 1000);
I am still experimenting with different ways to decide the first rotation.
Why do you dot axis with itself before making it a unit? I don't really
understand how that test works. The dot product of a and b gives the cosine
of the angle between them does it not? +1 if coincident and -1 if opposed.
A very odd thing happened today. I spent the morning trying to speed up the
code drawing my ribbon cable but none of the optimisations made significant
difference. It was stuck at 11-12 seconds. I went for a walk, came back and
tidied up my variable names to be more meaningful and it suddenly runs
twice as fast in 5-6 seconds. No idea why as I didn't change anything
significant.
On Fri, 1 Feb 2019 at 20:18, Ronaldo Persiano <rcmpersiano@gmail.com> wrote:
> Yes, it is a good solution to incorporate the operations of
> minimizing_rotations() inside accumulate_rotations().
>
> Btw, considering that rotate_from_to() always receive unit vectors, it can
> be simplified to:
>
> function rotate_from_to(a,b) =
> let( axis = cross(a,b) )
> axis*axis>0.0001? let( ax = unit(axis) )
> transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
> [[1,0,0],[0,1,0],[0,0,1]];
>
>
> I noticed that you have a three argument version of it.
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
HL
Hans L
Fri, Feb 1, 2019 9:10 PM
Not sure if this is useful to the current discussion, but this is my
version of "rotate_from_to" that I think is pretty robust.
function normalize(v) = v/norm(v);
// return a(any) unit vector which is orthogonal to the input
function get_orthogonal_vector(v) = v.x == 0 && v.y == 0 ? [0,1,0] :
normalize([-v.y, v.x, 0]);
function angle_between_vectors(v1,v2) = acos((v1*v2)/(norm(v1)*norm(v2)));
// rotate from vector v1 to v2
module rotatev(v1, v2) {
//echo("v1", v1, "v2", v2);
a = angle_between_vectors(v1, v2);
v = (a == 180) ? get_orthogonal_vector(v1) : cross(v1, v2);
//echo("a", a, "v", v);
rotate(a=a, v=v) children();
}
On Fri, Feb 1, 2019 at 2:50 PM nop head nop.head@gmail.com wrote:
Yes I made my tangents units to simply rotate_from_to. My version
currently looks like this:
//
// Computes the rotation with minimum angle that brings UNIT vectors a to b
// the code fails if a and b are opposed to each other unless a third
vector
// is specified to break the ambiguity.
//
function rotate_from_to(a, b, c) =
let(axis = unit(cross(a, b)))
axis * axis >= 0.99 ? m_transpose([b, axis, cross(axis, b)]) * [a,
axis, cross(axis, a)]
: a * b > 0 ? m_identity(3)
: assert(!is_undef(c), "path
doubles back on itself")
rotate_from_to(a, b + c /
1000);
I am still experimenting with different ways to decide the first rotation.
Why do you dot axis with itself before making it a unit? I don't really
understand how that test works. The dot product of a and b gives the cosine
of the angle between them does it not? +1 if coincident and -1 if opposed.
A very odd thing happened today. I spent the morning trying to speed up
the code drawing my ribbon cable but none of the optimisations made
significant difference. It was stuck at 11-12 seconds. I went for a walk,
came back and tidied up my variable names to be more meaningful and it
suddenly runs twice as fast in 5-6 seconds. No idea why as I didn't change
anything significant.
On Fri, 1 Feb 2019 at 20:18, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
Yes, it is a good solution to incorporate the operations of
minimizing_rotations() inside accumulate_rotations().
Btw, considering that rotate_from_to() always receive unit vectors, it
can be simplified to:
function rotate_from_to(a,b) =
let( axis = cross(a,b) )
axis*axis>0.0001? let( ax = unit(axis) )
transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
[[1,0,0],[0,1,0],[0,0,1]];
I noticed that you have a three argument version of it.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Not sure if this is useful to the current discussion, but this is my
version of "rotate_from_to" that I think is pretty robust.
```
function normalize(v) = v/norm(v);
// return a(any) unit vector which is orthogonal to the input
function get_orthogonal_vector(v) = v.x == 0 && v.y == 0 ? [0,1,0] :
normalize([-v.y, v.x, 0]);
function angle_between_vectors(v1,v2) = acos((v1*v2)/(norm(v1)*norm(v2)));
// rotate from vector v1 to v2
module rotatev(v1, v2) {
//echo("v1", v1, "v2", v2);
a = angle_between_vectors(v1, v2);
v = (a == 180) ? get_orthogonal_vector(v1) : cross(v1, v2);
//echo("a", a, "v", v);
rotate(a=a, v=v) children();
}
```
On Fri, Feb 1, 2019 at 2:50 PM nop head <nop.head@gmail.com> wrote:
> Yes I made my tangents units to simply rotate_from_to. My version
> currently looks like this:
>
> //
> // Computes the rotation with minimum angle that brings UNIT vectors a to b
> // the code fails if a and b are opposed to each other unless a third
> vector
> // is specified to break the ambiguity.
> //
> function rotate_from_to(a, b, c) =
> let(axis = unit(cross(a, b)))
> axis * axis >= 0.99 ? m_transpose([b, axis, cross(axis, b)]) * [a,
> axis, cross(axis, a)]
> : a * b > 0 ? m_identity(3)
> : assert(!is_undef(c), "path
> doubles back on itself")
> rotate_from_to(a, b + c /
> 1000);
>
> I am still experimenting with different ways to decide the first rotation.
>
> Why do you dot axis with itself before making it a unit? I don't really
> understand how that test works. The dot product of a and b gives the cosine
> of the angle between them does it not? +1 if coincident and -1 if opposed.
>
> A very odd thing happened today. I spent the morning trying to speed up
> the code drawing my ribbon cable but none of the optimisations made
> significant difference. It was stuck at 11-12 seconds. I went for a walk,
> came back and tidied up my variable names to be more meaningful and it
> suddenly runs twice as fast in 5-6 seconds. No idea why as I didn't change
> anything significant.
>
>
>
>
>
> On Fri, 1 Feb 2019 at 20:18, Ronaldo Persiano <rcmpersiano@gmail.com>
> wrote:
>
>> Yes, it is a good solution to incorporate the operations of
>> minimizing_rotations() inside accumulate_rotations().
>>
>> Btw, considering that rotate_from_to() always receive unit vectors, it
>> can be simplified to:
>>
>> function rotate_from_to(a,b) =
>> let( axis = cross(a,b) )
>> axis*axis>0.0001? let( ax = unit(axis) )
>> transpose([b, ax, cross(ax, b)]) * [a, ax, cross(ax, a)] :
>> [[1,0,0],[0,1,0],[0,0,1]];
>>
>>
>> I noticed that you have a three argument version of it.
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RP
Ronaldo Persiano
Fri, Feb 1, 2019 9:28 PM
I am still experimenting with different ways to decide the first rotation.
Nice. I will wait your conclusions before updating my repository.
Why do you dot axis with itself before making it a unit? I don't really
understand how that test works. The dot product of a and b gives the cosine
of the angle between them does it not? +1 if coincident and -1 if opposed.
The test axis*axis>0.0001? checks whether the vectors a and b are (almost)
collinear. We don't need that axis be unitary. As a and b are unitary, the
cross product norm is equal to the sin of their angle.
A very odd thing happened today. I spent the morning trying to speed up the
code drawing my ribbon cable but none of the optimisations made significant
difference. It was stuck at 11-12 seconds. I went for a walk, came back and
tidied up my variable names to be more meaningful and it suddenly runs
twice as fast in 5-6 seconds. No idea why as I didn't change anything
significant.
Sometimes, I have a goblin around here too...
>
> I am still experimenting with different ways to decide the first rotation.
>
Nice. I will wait your conclusions before updating my repository.
> Why do you dot axis with itself before making it a unit? I don't really
> understand how that test works. The dot product of a and b gives the cosine
> of the angle between them does it not? +1 if coincident and -1 if opposed.
>
The test axis*axis>0.0001? checks whether the vectors a and b are (almost)
collinear. We don't need that axis be unitary. As a and b are unitary, the
cross product norm is equal to the sin of their angle.
A very odd thing happened today. I spent the morning trying to speed up the
> code drawing my ribbon cable but none of the optimisations made significant
> difference. It was stuck at 11-12 seconds. I went for a walk, came back and
> tidied up my variable names to be more meaningful and it suddenly runs
> twice as fast in 5-6 seconds. No idea why as I didn't change anything
> significant.
>
Sometimes, I have a goblin around here too...
NH
nop head
Sat, Feb 2, 2019 10:13 AM
The Frenet-Serat frame is useless when we have the first few points
aligned in a vertical line once the initial few tangents are all equal.
I don't have any redundant points in my paths, so there will never be three
co-linear points. If the first two are in a vertical line then the third
one is not.
This is what I don't like about the fixed flip method:
[image: image.png]
These are three point extrusions of an L shape, going downwards, rotated to
8 orientations around z. The yellow ones start with a vertical segment so
get a fixed starting position. The grey ones have a very slightly off
vertical segment so they flip one of four ways due the minimum rotation.
This is what I mean about discontinuity of behaviour. A very slightly
different path gives a completely different result.
With my perturb in the direction of the second tangent method I match the
flips.
[image: image.png]
However this is totally different to what happens when the path goes
upwards as it then always starts at or close to the identity.
[image: image.png]
This is what happens with a Frenet-Serret style starting frame.
[image: image.png]
The shape of the sweep is now invariant under rotation and there is no
special case for straight down. There are no discontinuities of behaviour.
I.e. a small change in the sweep path never makes an abrupt change of shape.
On Fri, 1 Feb 2019 at 18:10, nop head nop.head@gmail.com wrote:
A slightly more readable and more complete version:
//
// Calculate the unit tangent at a vertex given the indices before and
after. One of these can be the same as i in the case
// of the start and end of a non closed path.
//
function tangent(path, before, i, after) = unit(unit(path[after] -
path[i]) - unit(path[before] - path[i]));
//
// Generate all the surface points of the swept volume.
//
function skin_points(profile, path, loop, twist = 0) =
let(len = len(path),
last = len - 1,
tangents = [tangent(path, loop ? last : 0, 0, 1),
for(i = [1 : last - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, last - 1, last, loop ? 0 : last)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
i = i + 1,
rot = i < len ? rotate_from_to(tangents[i - 1],
tangents[i]) * rot : undef) rot],
missmatch = loop ? calculate_twist(rotations[0], rotations[last])
: 0,
rotation = missmatch + twist
)
[for(i = [0 : last])
let(za = rotation * i / last,
t = orientate_r(path[i], rotations[i])
)
each transform_points(profile, za ? m_rotate([0, 0, za]) * t : t)
];
On Fri, 1 Feb 2019 at 17:42, nop head nop.head@gmail.com wrote:
I think your accumulate_rotations will access two past the end of rots.
My equivalent code looks like this:
let(len = len(path),
n = len - 1,
tangents = [tangent(path, loop ? n : 0, 0, 1),
for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
tangent(path, n - 1, n, loop ? 0 : n)],
rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
tangents[0], tangents[1]);
i < len;
rot = i < n ? rotate_from_to(tangents[i],
tangents[i + 1]) * rot : undef, i = i + 1) rot],
The third section of the C style loop gets executed before the test
condition.
On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
One thing to note is that it is possible to accumulate the rotations in
a C style for loop now. It no longer requires a recursive function.
Yes, I just changed accumulate_rotations() in my code to:
function accumulate_rotations(rots) =
[for( i = 0, R = rots[0];
i <= len(rots);
i=i+1, R = rots[i]*R)
R ];
and minimizing_rotations() to:
function minimizing_rotations(tangents) =
// avoid singularity at the first rotation of the sequence
[ let( axis = unit(cross([0,0,1],tangents[1])) )
axis*axis >= 0.99 || tangents[1][2]>=0 ?
rotate_from_to([0,0,1],tangents[1]) :
[[1,0,0],[0,-1,0],[0,0,-1]],
for (i = [1:len(tangents)-2])
rotate_from_to(tangents[i],tangents[i+1])
];
I think I am in position now to update my code in github with the
conclusions of this discussion.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
> The Frenet-Serat frame is useless when we have the first few points
aligned in a vertical line once the initial few tangents are all equal.
I don't have any redundant points in my paths, so there will never be three
co-linear points. If the first two are in a vertical line then the third
one is not.
This is what I don't like about the fixed flip method:
[image: image.png]
These are three point extrusions of an L shape, going downwards, rotated to
8 orientations around z. The yellow ones start with a vertical segment so
get a fixed starting position. The grey ones have a very slightly off
vertical segment so they flip one of four ways due the minimum rotation.
This is what I mean about discontinuity of behaviour. A very slightly
different path gives a completely different result.
With my perturb in the direction of the second tangent method I match the
flips.
[image: image.png]
However this is totally different to what happens when the path goes
upwards as it then always starts at or close to the identity.
[image: image.png]
This is what happens with a Frenet-Serret style starting frame.
[image: image.png]
The shape of the sweep is now invariant under rotation and there is no
special case for straight down. There are no discontinuities of behaviour.
I.e. a small change in the sweep path never makes an abrupt change of shape.
On Fri, 1 Feb 2019 at 18:10, nop head <nop.head@gmail.com> wrote:
> A slightly more readable and more complete version:
>
> //
> // Calculate the unit tangent at a vertex given the indices before and
> after. One of these can be the same as i in the case
> // of the start and end of a non closed path.
> //
> function tangent(path, before, i, after) = unit(unit(path[after] -
> path[i]) - unit(path[before] - path[i]));
> //
> // Generate all the surface points of the swept volume.
> //
> function skin_points(profile, path, loop, twist = 0) =
> let(len = len(path),
> last = len - 1,
>
> tangents = [tangent(path, loop ? last : 0, 0, 1),
> for(i = [1 : last - 1]) tangent(path, i - 1, i, i + 1),
> tangent(path, last - 1, last, loop ? 0 : last)],
>
> rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
> tangents[0], tangents[1]);
> i < len;
> i = i + 1,
> rot = i < len ? rotate_from_to(tangents[i - 1],
> tangents[i]) * rot : undef) rot],
>
> missmatch = loop ? calculate_twist(rotations[0], rotations[last])
> : 0,
> rotation = missmatch + twist
> )
> [for(i = [0 : last])
> let(za = rotation * i / last,
> t = orientate_r(path[i], rotations[i])
> )
> each transform_points(profile, za ? m_rotate([0, 0, za]) * t : t)
> ];
>
>
> On Fri, 1 Feb 2019 at 17:42, nop head <nop.head@gmail.com> wrote:
>
>> I think your accumulate_rotations will access two past the end of rots.
>> My equivalent code looks like this:
>>
>> let(len = len(path),
>> n = len - 1,
>>
>> tangents = [tangent(path, loop ? n : 0, 0, 1),
>> for(i = [1 : n - 1]) tangent(path, i - 1, i, i + 1),
>> tangent(path, n - 1, n, loop ? 0 : n)],
>>
>> rotations = [for(i = 0, rot = rotate_from_to([0, 0, 1],
>> tangents[0], tangents[1]);
>> i < len;
>> rot = i < n ? rotate_from_to(tangents[i],
>> tangents[i + 1]) * rot : undef, i = i + 1) rot],
>>
>> The third section of the C style loop gets executed before the test
>> condition.
>>
>>
>> On Fri, 1 Feb 2019 at 17:14, Ronaldo Persiano <rcmpersiano@gmail.com>
>> wrote:
>>
>>>
>>> nop head <nop.head@gmail.com> wrote:
>>>
>>>> One thing to note is that it is possible to accumulate the rotations in
>>>> a C style for loop now. It no longer requires a recursive function.
>>>>
>>>
>>> Yes, I just changed accumulate_rotations() in my code to:
>>>
>>> function accumulate_rotations(rots) =
>>> [for( i = 0, R = rots[0];
>>> i <= len(rots);
>>> i=i+1, R = rots[i]*R)
>>> R ];
>>>
>>>
>>> and minimizing_rotations() to:
>>>
>>> function minimizing_rotations(tangents) =
>>> // avoid singularity at the first rotation of the sequence
>>> [ let( axis = unit(cross([0,0,1],tangents[1])) )
>>> axis*axis >= 0.99 || tangents[1][2]>=0 ?
>>> rotate_from_to([0,0,1],tangents[1]) :
>>> [[1,0,0],[0,-1,0],[0,0,-1]],
>>> for (i = [1:len(tangents)-2])
>>> rotate_from_to(tangents[i],tangents[i+1])
>>> ];
>>>
>>>
>>> I think I am in position now to update my code in github with the
>>> conclusions of this discussion.
>>>
>>> _______________________________________________
>>> OpenSCAD mailing list
>>> Discuss@lists.openscad.org
>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>>
>>
P
Parkinbot
Sat, Feb 2, 2019 1:11 PM
What if one just discriminates for the initial rotation alpha
if alpha == 180° then use [[1,0,0],[0,-1,0],[0,0,1],]
else if |180°-alpha|< threshold then apply e.g. a 90°-rotation to the shape
and go on with relative rotations
else go on with relative rotations
--
Sent from: http://forum.openscad.org/
What if one just discriminates for the initial rotation alpha
if alpha == 180° then use [[1,0,0],[0,-1,0],[0,0,1],]
else if |180°-alpha|< threshold then apply e.g. a 90°-rotation to the shape
and go on with relative rotations
else go on with relative rotations
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Sat, Feb 2, 2019 1:52 PM
nophead,
Your results are not surprising. The rotation of the initial section is
always arbitrary in the local method and, as a consequence, the sweep is
not path rotation invariant: a rotation of the path generates a sweep that
is not in general the rotation of the sweep with the original path. That is
the reason I introduced the function adjusted_directions(path_transf, v0,
vf=undef, turns=0, closed=false) in my sweep library. This function
receives as input a path transform sequence path_transf constructed the
usual way and a direction vector v0. It changes path_transf so that the
x-axis of the section is mapped to a direction "near" to v0 and the user
has a tool to control the initial section rotation. A second vector, vf,
allows that the final section rotation is also under control by means of a
twist if needed. Here is an example of usage:
[image: AdjDirections.PNG]
generated by the code:
Lshape = [[0,0],[10,0],[10,1],[1,1],[1,5],[0,5]];
m=10;
path = [for(i=[0:m]) [0,5*i,0]];
pC= construct_transform_path(path);
sweep(Lshape,pC);
pA = adjusted_directions(path_transf=pC, v0=[0,0,1], vf=undef, turns=0,
closed=false);
translate([0,0,10]) sweep(Lshape,pA);
path2 = [path[len(path)-1], path[len(path)-1]+[0,15,0],
path[len(path)-1]+[0,30,0]];
pA1 = adjusted_directions(path_transf=pC, v0=[0,0,1], vf=[1,0,0], turns=0,
closed=false);
pC2 = construct_transform_path(path2);
pA2 = adjusted_directions(path_transf=pC2, v0=[0,1,0], vf=undef, turns=0,
closed=false);
translate([0,0,30]){
sweep(Lshape, pA1);
sweep(Lshape,pA2);
}
Note that this non-invariance is not restricted to paths that start
straight downwards. The minimum rotation is great between two subsequent
sweep sections but
it is an arbitrary choice for the first section and that is the cause of
its non-invariance to path rotations.
The great value of your experiment is to show that, referring the first
section rotation to the path and not only to its first tangent, it is
possible to make sweep invariant to rotations of the path. The choice of
the Frenet-Serrat starting rotation is as good as any other choice that
refers to something in the path that changes with the path rotation. For
instance, take any three points of the path that defines a plane containing
the first tangent. Build the first rotation (first frame) maping [0,0,1] to
the tangent and [1,0,0] to the normal of the plane. That will work just as
your Frenet-Serrat solution. So, we still have room for some arbitrary
choices.
nophead,
Your results are not surprising. The rotation of the initial section is
always arbitrary in the local method and, as a consequence, the sweep is
not path rotation invariant: a rotation of the path generates a sweep that
is not in general the rotation of the sweep with the original path. That is
the reason I introduced the function adjusted_directions(path_transf, v0,
vf=undef, turns=0, closed=false) in my sweep library. This function
receives as input a path transform sequence path_transf constructed the
usual way and a direction vector v0. It changes path_transf so that the
x-axis of the section is mapped to a direction "near" to v0 and the user
has a tool to control the initial section rotation. A second vector, vf,
allows that the final section rotation is also under control by means of a
twist if needed. Here is an example of usage:
[image: AdjDirections.PNG]
generated by the code:
Lshape = [[0,0],[10,0],[10,1],[1,1],[1,5],[0,5]];
m=10;
path = [for(i=[0:m]) [0,5*i,0]];
pC= construct_transform_path(path);
sweep(Lshape,pC);
pA = adjusted_directions(path_transf=pC, v0=[0,0,1], vf=undef, turns=0,
closed=false);
translate([0,0,10]) sweep(Lshape,pA);
path2 = [path[len(path)-1], path[len(path)-1]+[0,15,0],
path[len(path)-1]+[0,30,0]];
pA1 = adjusted_directions(path_transf=pC, v0=[0,0,1], vf=[1,0,0], turns=0,
closed=false);
pC2 = construct_transform_path(path2);
pA2 = adjusted_directions(path_transf=pC2, v0=[0,1,0], vf=undef, turns=0,
closed=false);
translate([0,0,30]){
sweep(Lshape, pA1);
sweep(Lshape,pA2);
}
Note that this non-invariance is not restricted to paths that start
straight downwards. The minimum rotation is great between two subsequent
sweep sections but
it is an arbitrary choice for the first section and that is the cause of
its non-invariance to path rotations.
The great value of your experiment is to show that, referring the first
section rotation to the path and not only to its first tangent, it is
possible to make sweep invariant to rotations of the path. The choice of
the Frenet-Serrat starting rotation is as good as any other choice that
refers to something in the path that changes with the path rotation. For
instance, take any three points of the path that defines a plane containing
the first tangent. Build the first rotation (first frame) maping [0,0,1] to
the tangent and [1,0,0] to the normal of the plane. That will work just as
your Frenet-Serrat solution. So, we still have room for some arbitrary
choices.
NH
nop head
Sat, Feb 2, 2019 2:05 PM
Because [[1,0,0],[0,-1,0],[0,0,1]] is arbitrary, you can just as easily
flip it three other ways and be just as valid, and other nearly vertical
paths do, and so a minute variance in a path pointing almost straight down
cause a big change to the results, which I don't like.
Using FS means there is a single valid result and I can predict what it
will be.
On Sat, 2 Feb 2019 at 13:14, Parkinbot rudolf@digitaldocument.de wrote:
Because [[1,0,0],[0,-1,0],[0,0,1]] is arbitrary, you can just as easily
flip it three other ways and be just as valid, and other nearly vertical
paths do, and so a minute variance in a path pointing almost straight down
cause a big change to the results, which I don't like.
Using FS means there is a single valid result and I can predict what it
will be.
On Sat, 2 Feb 2019 at 13:14, Parkinbot <rudolf@digitaldocument.de> wrote:
> What if one just discriminates for the initial rotation alpha
>
> if alpha == 180° then use [[1,0,0],[0,-1,0],[0,0,1],]
> else if |180°-alpha|< threshold then apply e.g. a 90°-rotation to the shape
> and go on with relative rotations
> else go on with relative rotations
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>