Interesting stuff. I wonder if we should use Euler spirals instead of
Bezier splines to round polygons.
On Fri, 15 Mar 2019 at 19:45, Gadgetmind lists@foxhill.co.uk wrote:
On 15/03/2019 14:07, Ronaldo Persiano wrote:
Besides, curvature continuity is a requeriment on the design of a road
track. Any discontinuity on the track is prone to an accident because a
sudden wheel turn is needed to keep the vehicle on track when you cross the
discontinuity.
Railways used to be designed with straight and curves, and it made them
uncomfortable and restricted speeds, and ditto "loop the loop" on roller
coasters as the sudden changes in acceleration (high jerk) caused neck
injuries.
Then along came what I still call clothoid curves, but wikipedia prefers
Euler Spirals.
https://en.wikipedia.org/wiki/Euler_spiral
They are used to link straights to circles on railways, rollercoasters,
and much more.
https://en.wikipedia.org/wiki/Track_transition_curve
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
On 15.03.2019 17:39, Gadgetmind wrote:
Railways used to be designed with straight and curves, and it made them
uncomfortable and restricted speeds, and ditto "loop the loop" on roller
coasters as the sudden changes in acceleration (high jerk) caused neck
injuries.
Then along came what I still call clothoid curves, but wikipedia prefers
Euler Spirals.
Ditto, I learned about clothoid curves 35 years ago. A railway track
with immediate transition from a straight segment with zero curvature to
1/r in a circular segment will introduce a sudden sideways acceleration.
Not good. Railway tracks and motorways therefore use clothoid transition
curves.
I agree with those who say this does not matter for 3d printing though.
Carsten Arnholm
nophead wrote
Interesting stuff. I wonder if we should use Euler spirals instead of
Bezier splines to round polygons.
It looks to me like the Bezier solution is probably easier than trying to
fit a fragment of an Euler spiral to transition between a flat section and a
circular arc.
I compared a 4th order bezier smoothed square to a circle rounded square and
they didn't look very different. I asked myself why not? And after some
investigation, reached a conclusion that I think is interesting. I also
wonder if, perhaps, I was too quick to deny the utility of 5th order bezier
for this application. Here are three examples. The left uses circular
arcs. The center uses 4th order bezier with the control points chosen for
the flattest arc, which is closest to circular. It sure looks a lot like
the left hand image. It looks like there are little corners where the
roundover meets the side. The right hand example is a 4th order bezier with
different parameters and it looks smooth. I would say that the difference is
apparent, and I think it would be apparent if I printed the three squares.
(Maybe I should do a test print.)
http://forum.openscad.org/file/t2477/three_squares_crop.png
So what's going on. Do I have continuous curvature or not? Using a
curvature parameter that goes from [0,1] where 1 places p1=p0 and p3=4, and
0 places p1=p2=p3, I calculated the endpoint curvature and it is zero on
[0,1). At 1 I have a problem because the derivative is zero, so it's a bad
parametrization. But then I graphed the curvature for different parameter
values:
http://forum.openscad.org/file/t2477/bezier4curvature.png
The graphs show the curvature across half the bezier curve. Of course the
other half is a mirror image. So what I find is that when the parameter is
close to 1 the curvature starts at 0 but there is a big spike. So it's
continuous technically, but not really practically. It seems like a
parameter value in the range of [.5,.8] or so should give a moderate
derivative of the curvature. If you go smaller the curvature rises a lot
in the middle of the curve, so the curve becomes pointed. Note also that
if you want the curve to approximate a circle it's clear that this is
accomplished best when the parameter is close to 1, because then the
curvature is constant across most of the range.
So why am I having second thoughts about wanting order 5? Well, it seems
that with order 4, once you scale the curves to be the same size (to cut off
the same amount of the corner) there is very little variation in that middle
range. I felt like the full range of curves coming out of the 5th order
bezier was producing some weird looking flat stuff, but maybe some more
limited parameter set would be interesting. I may plot more graphs later.
Also from this graph I can justify why we shouldn't bother with the Euler
spiral. The Euler spiral's nice property is that the curvature starts at
zero and increases linearly. The 0.7 curve and 0.5 curves look like
reasonable approximations to linear. So the results wouldn't be much
different. And this bezier approach is very easy to work with. I have
done an implementation that does both bezier 4th order and circular arcs,
where I allow the user to specify the size of each corner's curve
independently, and I check for collisions (i.e. you pick too large of a
curve to fit). My intention was that it could do closed paths (polygons)
but also open paths, and paths in 3d. And the circular arc code was more
difficult to write than the bezier code, where once you've picked the
control points, you're basically done. Once I've done a bit more testing
I'll post it here for comment.
Is there any mechanism other than assert() to generate warning or error
messages? Assert seems kind of clumsy, since it prints the test, and it is
always a fatal error instead of a warning.
--
Sent from: http://forum.openscad.org/
Very nice research! I am already waiting for the episode 2 :)
Is there any mechanism other than assert() to generate warning or error
messages? Assert seems kind of clumsy, since it prints the test, and it is
always a fatal error instead of a warning.
Yes, you may insert an echo. I usually do something like this:
let( x = test? echo(a,b,c) 0 : 0 )
As echo() accepts HTML codes you are able to use a background color similar
to the OpenSCAD warnings.
Ronaldo wrote
Very nice research! I am already waiting for the episode 2 :)
Maybe not as exciting as the first plot, but I did plot the shape of the
curve as I varied the curvature parameter. It changes so slightly that it's
hard to see if I put too many curves on the graph, so I'm showing curvature
parameters of just 0, 0.5, and 1. What changes a lot is the location of p0
and p4. For this case, I have set the tip of the rounded corner to be 2
units back from the corner. The bezier curve intersects the edge of the
right angle corner 4.5 units away from the corner when the curve parameter
is 1 (red line), 7.5 units when the parameter is 0.5 (green line), and a
whopping 22.6 units if you set the parameter to zero (blue line). So
choosing a smaller curvature parameter causes it to really vary the
curvature slowly. This I think makes those small values not very useful,
since you are very likely to run out of room on your object for the curve.
Is there any mechanism other than assert() to generate warning or error
messages? Assert seems kind of clumsy, since it prints the test, and it
is
always a fatal error instead of a warning.
Yes, you may insert an echo. I usually do something like this:
So with tagging I can get the color, but it's not going to be included in
the warning count. I normally find that all the warning message scroll off
the message area due to the statistics report at the end. Has anybody ever
talked about adding a warning command to the language, or extending echo to
have a warning flag that makes it count on the warning list?
--
Sent from: http://forum.openscad.org/
Here's the current version of my code, which appears to be working. It can
round over each corner with an independently set rounding parameter
(including no rounding), and it can handle open and closed paths in 2d or
3d.
use<lib/BOSL/math.scad>
use<lib/BOSL/beziers.scad>
// TODO:
// remove colinear points?
//
// roundcorners(path, curve, type, all, closed)
//
// path: a list of 2d or 3d points, possibly with an extra coordinate giving
smoothing parameters, e.g.
// ex: [[0,0],[0,1],[1,1],[0,1]] 2d point list
// [[0,0,0], [0,1,1], [1,1,2], [0,1,3]] 3d point list
// [[0,0,.2],[0,1,.1],[1,1,0],[0,1,.3]] 2d point list with
smoothing parameters
// [[0,0,0,.2], [0,1,1,.1], [1,1,2,0], [0,1,3,.3]] 3d point
list with smooth parameters
// [[0,0,[.3,.5], [4,0,[.2,.6]], [4,4,0], [0,4,[
// curve: set to "smooth" to get continuous curvature 4th order bezier
curves
// set to "circle" to get circular arcs
// type: set to "cut" with either curve type to specify how much of the
corner to "cut" off. The
// smoothing parameter will be the distance from the
corner to the tip of the rounded curve
// set to "radius" to use with curve=="circle" to specify the curve
radius
// set to "joint" to use with curve=="smooth" to specify the distance
from the corner at which
// the curve joints the shape
// all: set this to a curvature parameter to apply to all points on the
list. If this is set then all
// of the values given in the path are assumed to be geometrical
coordinates. If you don't set it
// then the last value of each entry in path is assumed to be the
smoothing parameters
// closed: set to true (the default) if the curve is closed and false if the
curve is open at the ends
//
// If you select curve=="smooth" then there are two smoothing parameters.
The first one
// is the cut or joint distance as given type "type". The second one is a
curvature
// parameter which is a number in [0,1], where larger numbers have a more
abrupt
// transition and smaller ones a more gradual transition. If the curvature
parameter is
// close to zero the transition is so gradual that it may take a very large
distance.
//
// If you select curves that are too large to fit the code will fail with an
error. It displays a set
// of scale factors that you can apply to the (first) smoothing parameter
which will reduce the size of the
// curves so that they will fit on the shape. If the scale factors are
larger than one then they
// indicate how much you can increase the curve size before collisions will
occur.
//
function roundcorners(path, curve, type, all=undef, closed=true) =
let(
default_curvature = 0.7, // default curvature for "smooth" curves
typeok = type == "cut" || (curve=="circle" && type=="radius") ||
(curve=="smooth" && type=="joint")
)
assert(curve=="smooth" || curve=="circle", "Unknown 'curve' setting in
roundcorners")
assert(typeok, curve=="circle" ? "In roundcorners curve=="circle"
requires 'type' of 'radius' or 'cut'":
"In roundcorners curve=="smooth"
requires 'type' of 'joint' or 'cut'")
let(
pathfixed= all == undef ? path : array_zip([path,
replist([all],len(path))]),
dim = len(pathfixed[0])-1,
points = array_subindex(pathfixed, [0:dim-1]),
parm = array_subindex(pathfixed, dim),
// dk will be a list of parameters, for the "smooth" type the distance
and curvature parameter pair,
// and for the circle type, distance and radius.
dk = [for(i=[0:len(points)-1]) let(
angle = pathangle(wrap_range(points,i-1,i+1))/2,
parm0 = is_list(parm[i]) ? parm[i][0] : parm[i],
k = curve=="circle" && type=="radius" ? parm0 :
curve=="circle" && type=="cut" ? parm0 / (1/sin(angle) - 1) :
is_list(parm[i]) && len(parm[i])==2 ? parm[i][1] :
default_curvature
) !closed && (i==0 || i==len(points)-1) ? [0,0] :
curve=="circle" ? [k/tan(angle), k] :
curve=="smooth" && type=="joint" ? [parm0,k] :
[8parm0/cos(angle)/(1+4k),k]
],
lengths = [for(i=[0:len(points)]) norm(flatten(diff(wrap_range(points,
i-1,i))))],
scalefactors = [for(i=[0:len(points)-1])
min(lengths[i]/sum(array_subindex(wrap_range(dk,i-1,i),0)),
lengths[i+1]/sum(array_subindex(wrap_range(dk,i,i+1),0)))]
)
echo("Roundover scale factors:",scalefactors)
assert(min(scalefactors)>=1,"Curves are too big for the path")
[ for(i=[0:len(points)-1])
each dk[i][0] == 0 ? [points[i]] :
curve=="smooth" ? bezcorner(wrap_range(points,i-1,i+1), dk[i]) :
circlecorner(wrap_range(points,i-1,i+1),
dk[i])
];
function bezcorner(points, parm) =
let(
d = parm[0],
k = parm[1],
prev = normalize(points[0]-points[1]),
next = normalize(points[2]-points[1]),
P = [points[1]+dprev,
points[1]+kdprev,
points[1],
points[1]+kdnext,
points[1]+dnext])
bezier_curve(P,20);
function circlecorner(points, parm) =
let(
angle = pathangle(points)/2,
d = parm[0],
r = parm[1],
prev = normalize(points[0]-points[1]),
next = normalize(points[2]-points[1]),
center = r/sin(angle) * normalize(prev+next)+points[1]
)
circular_arc(center, points[1]+prevd, points[1]+nextd, 20);
function circular_arc(center, p1, p2, N) = let(
angle = pathangle([p1,center,p2]),
v1 = p1-center,
v2 = p2-center)
len(center)==2 ?
let ( start = atan2(v1[1],v1[0]),
end = atan2(v2[1],v2[0]),
r=norm(v1))
[for(i=[0:N-1]) let(theta=(end-start)i/(N-1)+start)
r[cos(theta),sin(theta)]+center] :
let(axis = cross(v1,v2))
[for(i=[0:N-1]) matrix3_rot_by_axis(axis, i*angle/(N-1)) * v1 +
center];
function bezier_curve(P,N) =
[for(i=[0:N-1]) bez_point(P, i/(N-1))];
function pathangle(pts) =
let( d = [for(i=[0:2]) norm(flatten(diff(wrap_range(pts,i,i+1))))] )
acos(constrain(
(d[0]*d[0] + d[1]*d[1] - d[2]*d[2]) / 2 / d[0] / d[1], -1,1));
function array_subindex(vect, index) =
[for(entry=vect)
let(value=[for(i=index) entry[i]])
len(value)==1 ? value[0] : value];
function array_zip(vecs,v2,v3) =
v3!=undef ? array_zip([vecs,v2,v3]) :
v2!=undef ? array_zip([vecs,v2]) :
let(
length = len(vecs[0]),
samesize = [for (v=vecs) len(v)==length?0:1],
dummy=assert(sum(samesize)==0,"Input vectors must have the same length")
)
[for(i=[0:length-1])
[for(v=vecs) each v[i]]
];
function replist(list, N) = [for(i=[0:N-1]) list];
function diff(v) =
[for(i=[0:len(v)-2]) v[i+1]-v[i]];
sq = [[0,0],[2,0],[2,2],[0,2]];
polygon(roundcorners(sq, curve="circle", type="cut", all=.2));
translate([2.5,0,0])polygon(roundcorners(sq, all=[.2,.5], curve="smooth",
type="cut"));
--
Sent from: http://forum.openscad.org/
This may be more graphs than anyone wants to look at, but I did an analysis
of order 5 smoothing to see if it presented any obvious benefits. Since
there are now two parameters, it's more to look at. I'm defining has h1 the
location of P1 and P4 as the fraction of the distance away from the apex
towards P0 and P5, and I define h2 as the fractional distance from the apex
to P1 or P4.
http://forum.openscad.org/file/t2477/case1.png
http://forum.openscad.org/file/t2477/case2.png
http://forum.openscad.org/file/t2477/case3.png
http://forum.openscad.org/file/t2477/case4.png
http://forum.openscad.org/file/t2477/case5.png
In this case, unlike the previous plot the curvatures are calculated for the
scaled roundovers that are shown on the left side. It looks to me like
the cases h2=0.5 and maybe h2=0.7 are the most promising, so focusing on
those and comparing to the order 4 case I have the following:
http://forum.openscad.org/file/t2477/order4.png
http://forum.openscad.org/file/t2477/order5_case1.png
http://forum.openscad.org/file/t2477/order5_case2.png
After examining these various plots I am hard pressed to see an obvious
benefit to using order 5. There is only one matter left to consider. It
turns out that if you like h2=0.7 you may be able to achieve a roundover in
a shorter space by using order 5. But note that this is only true for h1
values above about 0.4. If h1 gets small then order 4 is much more
efficient.
http://forum.openscad.org/file/t2477/size.png
Here is a comparison of a few selected "nice" looking parameter sets, where
d gives the location of p0 relative to the apex (so smaller is better).
http://forum.openscad.org/file/t2477/compare.png
So maybe the argument could be made for the blue curve, which gives d 5%
smaller than the next best option. But really it's not much difference.
Again, it seems hard to justify using 5th order, and especially with the
worsening behavior as the h1 parameter shrinks.
So have I beat this to death yet?
--
Sent from: http://forum.openscad.org/
adrianv wrote
So have I beat this to death yet?
I realized that the curvature plots should really be done as arc-length vs
curvature instead of Bezier parameter vs curvature. The curves with larger
parameter value have parameterizations that move very slowly near the end
points, so my previous plots underestimate the slope of the curvature there.
Also plotting based on arc-length makes it easier to understand the trade
off of really long curves to shorter ones. I can post updated plots if
anybody actually wants to see them.
--
Sent from: http://forum.openscad.org/
I would like to see them. I would expect a parametric slow down where the
curvature is greater due to smaller norms of the parametric derivatives
there.
adrianv avm4@cornell.edu wrote:
The curves with larger
parameter value have parameterizations that move very slowly near the end
points, so my previous plots underestimate the slope of the curvature
there.
Also plotting based on arc-length makes it easier to understand the trade
off of really long curves to shorter ones. I can post updated plots if
anybody actually wants to see them.
Ronaldo wrote
I would like to see them.
Ok. Here they are. These are the same plots posted before, but the
curvature graph (right side) is now plotted as arc length of the bezier
curve against the curvature. I placed the center of the bezier curve at 0
and only show half the curve, so the curves start at some negative number
equal to half their length. Some of the curves with small h parameters are
very long indeed (up to about length 40) so I plotted everything on the same
scale and cut off some of them so it's easier to compare between graphs. I
don't feel like the new graphs lead to any change in the conclusions.
There's not a big difference between the order 4 and order 5 curves, but the
order 5 do manage with slightly shorter lengths. The graphs do highlight
the poor properties of the curves with h1 close to 1---worse than using a
circle in terms of smoothness due to the overshoot at the ends. I also do
wonder how much the derivative of the curvature matters. Will I be able to
feel the difference between cases where the curvature plot has a steeper
slope? Or does matching curvature really suffice and seeking higher levels
of continuity is just unnecessary. I was thinking I need to print out some
tests. The final graph has a different scale for the curvatures to make it
easier to compare between the rather similar lines that appear on the plot.
http://forum.openscad.org/file/t2477/ncase1.png
http://forum.openscad.org/file/t2477/ncase2.png
http://forum.openscad.org/file/t2477/ncase3.png
http://forum.openscad.org/file/t2477/ncase4.png
http://forum.openscad.org/file/t2477/ncase5.png
http://forum.openscad.org/file/t2477/norder4.png
http://forum.openscad.org/file/t2477/norder5_1.png
http://forum.openscad.org/file/t2477/norder5_2.png
http://forum.openscad.org/file/t2477/ncompare.png
--
Sent from: http://forum.openscad.org/