BelfrySCAD / BOSL2

The Belfry OpenScad Library, v2.0. An OpenSCAD library of shapes, masks, and manipulators to make working with OpenSCAD easier. BETA
https://github.com/BelfrySCAD/BOSL2/wiki
BSD 2-Clause "Simplified" License
1.01k stars 115 forks source link

[BUG] path_sweep() with non uniform path breaks with twist #1409

Closed lijon closed 7 months ago

lijon commented 8 months ago

Code To Reproduce Bug

include <BOSL2/std.scad>
include <BOSL2/rounding.scad>

$fn = 24;

function a(h) = arc(points=[[-20,0],[0,h],[20,0]],n=16);

s1 = flatten([
    a(2), // bottom
    move([0,6],reverse(a(4))) // top
]);

p1 = xrot(90,path3d(arc(points=[[-40,0],[0,10],[40,-40]],n=16)));

function path_trim(p, start=0, end=0) =
    (start==0 && end==0) ? p : path_cut(p,[if(start>0) start, if(end>0) path_length(p)-end])[start>0?1:0];

p1_trimmed = path_trim(p1, 6, 7);

path_sweep(s1,p1_trimmed,profiles=false,width=0.5,normal=UP,twist=90, scale=1, uniform=false);

The path is trimmed at the ends, so it's not uniform anymore. However this breaks with twist. See artifact at the end:

Screenshots

Screenshot 2024-03-21 at 15 58 14
adrianVmariano commented 8 months ago

Your step size when you trim (last step) is too small to produce a valid polyhedron. The geometry is correct, I think, but because of the rotation it self-intersects.

adrianVmariano commented 8 months ago

To elaborate a bit, here's the result when I use p1_trimmed = path_trim(p1, 6, 5.5);

image

It still looks OK, but that face on the left edge is getting very skinny.

With p1_trimmed = path_trim(p1, 6, 6); the face has crossed over zero length and is going backwards.

image

So the problem is that this is simply an invalid input. Why does it work with twist=0? It's because the profile is much longer in one direction than the other, and the twist changes which direction is aligned with the curvature of the path. You can make it fail without any twist if you rotate the profile 90 deg.

lijon commented 8 months ago

So the problem is in my path_trim()?

adrianVmariano commented 8 months ago

Well, indirectly. I thought a bit more about what exactly what's going on. If you sample your path finely then there is no problem. The problem seems to arise specifically as a result of the tangent at the ends. I don't know if there's a way to do the last point tangent that is better. The tangent that is computed doesn't seem to be consistent with the rest of the shape, though.

adrianVmariano commented 7 months ago

I took a look a bit more at this. It doesn't seem like anything is wrong anywhere that can be fixed. Here's the path with tangents computed with uniform=false. They look reasonable:

image

The problem as I said before has to do with how the profile rotates into itself when you have the small step size and a small error in the tangent calculated at the ends. So possible solutions: avoid small stepsize. The best way to do that would be to sample the correct arc from the beginning. But if that's not convenient, then rewrite path_trim to omit the penultimate point at the ends if it's too close to the final point. I did that directly with list_remove for the example above and the result then looks fine:

image

Or alternatively but less accurately for a case like this you could use resample_path() after trimming. (You could also generate the original path at high resolution, trim, and then resample, which would be more accurate.)

Another option would be to make the tangent more accurate---so compute your own tangent vector from the formula for the arc at every point instead of doing this numerically from sampled data. I haven't tried this, but I believe it would fix the problem. It might be possible to get a more accurate numerical estimate of the tangent by using more points, but that could be bad for data which is less smooth, so it's not clear it's a good idea as a general code modification.

I'm thinking this issue can be closed.

adrianVmariano commented 7 months ago

Another option that might also work would be to compute the tangent numerically from the full path and interpolate it in the same way to get a tangent at the ends. That tangent should be more accurate, and consist with the other tangents, because it will be a 2-sided estimate instead of a 1-sided estimate of the derivative.