Hi all,
Just sharing a tool that I think is pretty useful for rounding the points on
a polygon. The idea is that along with the list of coordinates that you
would normally pass to polygon(), a list of radii is also given and they are
used to round the corners at each of the coordinates.
http://forum.openscad.org/file/n21897/PolyRoundexample1.png
code here
https://github.com/Irev-Dev/Round-Anything/blob/master/polyround.scad
I normally design SCAD things mostly with primitives, but I always get
frustrated when it comes to adding fillets later (internal radii in
particular), I'm going to try and design things from now on by extruding
these rounded polygons (where practical).
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Oh Yes this will be used extensively , i assure you.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21903.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Good to hear :)
I forgot to add, one current issue is that if three consecutive points are
col-linear it breaks :/
It's probably easy to fix, I've just hit a wall in motivation.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21904.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Yeah i get some runaway error too. Not sure if its the same issue though.
Co-linearity is easily fixed in function round3points.
if more tha 98% collinear just return input back as is.
swap the end of round3points to:
swapout:for:
I think my issue occur when radius for p2 dont fit between p1 -p3.
Maybe that should be left to be expected
or limited so the arc never can go outside the three points?
Also i probably will use [x,y,r], format points in my trials.
Easy enough to separate them before calling polyRound.
Any reason you selected against that?
Thanks again for a great module that will com to much use. Not least with
sweeps.
http://forum.openscad.org/file/n21908/100.jpg
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21908.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
TLC123 wrote
Co-linearity is easily fixed in function round3points.
if more tha 98% collinear just return input back as is.
swap the end of round3points to:
swapout:
for:
Thanks for having a look at the co-linearity problem. I've only had a quick
look at your code now before bed so I mightn't understand it properly but I
would think that if you test for co-linearity and the result is true, then
you should return nothing and move onto the next set. My thought experiment
is that if there are 5 points where points 2 , 3 and 4 are co-linear,
processing points 1,2&3 together is fine likewise so is 3,4&5. If we just
skip processing points 2,3&4 it should be fine. Returning the original three
points might cause problems, I'll have a look at it though later.
TLC123 wrote
I think my issue occur when radius for p2 dont fit between p1 -p3.
Maybe that should be left to be expected
or limited so the arc never can go outside the three points?
Your suggestion to limit the arc to stay within the three points, I'm
thinking out loud now, at first I thought I wouldn't work, but now I think
it could potentially work if instead of just processing the list of points
three at a time like it is currently p[i],p[i+1],p[i+2] instead it grabs the
last point from the previous arc so arcpoints[last point of last
arc],p[i+1],p[i+2]. it would have to do something different on the first and
last iteration of the for loop, but that's seem like a nice simple solution.
TLC123 wrote
Also i probably will use [x,y,r], format points in my trials.
Easy enough to separate them before calling polyRound.
Any reason you selected against that?
Thanks again for a great module that will com to much use. Not least with
sweeps.
In terms of the format [[x,y,r],[x,y,r],etc], I think you're right, I think
it's better. I posted this code as soon at it was mostly working so I
haven't actually used it for anything other than the examples, but trying to
implement it today on an actual project of mine I found it confusing as to
which radius lined up with which point on the polygon. So I think I'll make
this change, but with any of those three things, feel free to make a pull
request.
I'll have a closer look at your code soon, though probably not until
Tuesday, I'm busy tomorrow.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21909.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
TLC123 wrote
Also i probably will use [x,y,r], format points in my trials.
<br>
Easy enough to separate them before calling polyRound.
<br>
Any reason you selected against that?
<br>
Probably so he could draw the original polygon easily, independently of the
script, for testing.
The main reason I started the other script was to enable CSG export to
FreeCAD and then into Fusion 360, as this couldn't be done using hull (read
the thread here
http://forum.openscad.org/Script-to-replicate-hull-and-minkoswki-for-CSG-export-import-into-FreeCAD-td16537.html
). This meant that circles and arcs keep their definition, rather than being
broken up into a mesh. Your script does seem to create a mesh, ie arcs are
broken up into straight segments, so it doesn't maintain the geometry of
circles when you import it into FreeCAD.
Unlike the other script, the points don't have to go around in a clockwise
order with yours, which is nice.
Ian
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21910.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Thinking about it, an approach that is somewhere between the two scripts
might be the best of both. Take the initial polygon, calculate the tangent
points between each three points and the radius, redraw the polygon without
the middle point but with the tangent points, then union or difference the
circle from it. This would give you the same shape as your version, but
constructed from a polygon with unioned or differenced circles, like my
version. This would mean on CSG export that it would maintain it's geometry,
rather than being converted to a mesh, so can be opened in other CAD
software as a solid body, rather than a mesh.
Ian
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21911.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
There seem's to be some miss understandings about FreeCAD's import of SCAD
and CSG files.
FreeCAD does try and support hull and minkowski requests. For it to work
FreeCAD must be able to find the OpenSCAD executable. The path to the
OpenSCAD executable is an option of the OpenSCAD work bench. ( It can be
very slow.)
For hull & minkowski to work with 2D objects the option to allow FreeCAD
to download and update DXF libraries must be enabled see general
import/export preference, The downloadable library is in general no longer
used by FreeCAD but IS used by importCSG for hull & Minkowski requests
involving 2D objects. There was a bug with LWPOLYINE in the DXF library that
has not long been fixed.
polyhedron should also be supported, but again there was a bug until
recently.
So if you have problems then please try the latest 0.17 version and if
still, problems please report in the FreeCAD forum.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21912.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
droftarts wrote
Thinking about it, an approach that is somewhere between the two scripts
might be the best of both. Take the initial polygon, calculate the tangent
points between each three points and the radius, redraw the polygon
without the middle point but with the tangent points, then union or
difference the circle from it. This would give you the same shape as your
version, but constructed from a polygon with unioned or differenced
circles, like my version. This would mean on CSG export that it would
maintain it's geometry, rather than being converted to a mesh, so can be
opened in other CAD software as a solid body, rather than a mesh.
Ian
Hi Ian,
short answer is if you use fn of 1 with my code it will draw the polygon
along all the tangent points only.
This was my initial approach, but I dumped it when I realised if you wanted
to have a relatively large radius compare to the overall size of the shape
than unioning it with a circle would mean the circle would take over. ie the
radius of 10 in the example, a circle that big would be bigger than most of
the shape. I thought it could be fixed by cutting the circle down before it
got unioned but at that point it seemed drawing points of the circle would
be easier.
If you wanted go back to this like I said you can use fn of 1 or you could
modify the round3points() function so that instead of passing the tangent
points and circle center to CentreN2PointsArc() it just returns these values
instead, that way you would also have the coordinates of the circle center
for the circle unioning.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21913.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Ah yes, I see the problem, and now I try it, the other script has this
problem too. Though it probably shows how often it's a problem, as neither I
nor anyone else has noticed it before! Thanks for the extra info, I'll take
a look.
Keith: I'd like to see some examples of what FreeCad can import correctly
from OpenSCAD, but I think that belongs in another thread, rather than
hijack this one!
Ian
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21914.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
2017-07-23 5:21 GMT-03:00 TLC123 torleif.ceder@gmail.com:
I think my issue occur when radius for p2 dont fit between p1 -p3.
Maybe that should be left to be expected
or limited so the arc never can go outside the three points?
The issue is not restricted to that extreme cases. It is enough to have
tangent points in an edge in reversed order.
module test2(size,r) {
p = [ [0,0], [size,0],[size,size],[0,size] ];
polygon( polyRound(p,r,10));
translate([0,0,-1])%polygon(p);
}
test2(100,[80,20,30,50]);
If polyRound is recoded to identify those cases, either it should do
nothing or reduce some radiuses. Unhapilly, the radius reduction problem
has usually many solutions.
As Ronaldo mentioned it is not straight forward to deal with conflicting
radii. But, even a robust implemention will be quite difficult, this
function is a nice-to-have if handled with care.
Here is my implementation of it. It uses $fs to control arc graining and
seems to be semantically equivalents for the rest:
$fs=.2; // graining
b=[[-5,0],[5,3],[0,7],[8,7],[20,20],[10,0]]; //points
br=[1, 1.5, 1, 10, 1.5, 5]; //radiuses
polygon(polyRound(b,br));
%translate([0,0,0.2])polygon(b);
p=[[0,0],[0,20],[15,15],[3,10],[15,0],[6,2]];//points
pr=[2, 4, 3, 3, 1, 8];//radiuses
translate([25,0,0])polygon(polyRound(p,pr,$fs=.4));
%translate([25,0,0.2])polygon(p);
function polyRound(b, r) = [for(i = [0:len(b)-1]) each tri(b, i,
r[(i+1)%len(b)])];
function tri(L,i, r) = let(N=len(L)) // uses $fs
let(A = L[i]) let(B=L[(i+1)%N], C=L[(i+2)%N]) //
points
let(a = (A-B)/norm((A-B)), b = (C-B)/norm((C-B))) //
vectors
let(a_= [-a[1], a[0]], b_=[b[1],-b[0]]) //
normals
let(m = r*(b_-a_),k=(a[0]m[1]-a[1]m[0])/(b[0]a[1]-b[1]a[0])) //
equation stuff
let(s = sign(k), w = 2atn([r,sk]), w0 = atn(-sa_)) //
angles
let(n = round(abs(rw/180PI)/$fs), M = B+ska+sra_) //
middle point
[for(z = [w0:-sw/(n+1):w0-sw-s.0001]) M+r*[cos(z), sin(z)]]; // arc
// full circle atan
function atn(X) = let(w = X[0]==0?X[1]<0?270:90:atan(X[1]/X[0]))
X[0]<0?180+w:(w+360)%360;
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21928.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
I made some experiments in a fork on github. Don't know if I posted it
correctly, very unused to github.
First version dealt with the collinear case and radius zero case.
Next i clamped tangD value to be inside either leg of the triangle.
Now I'm working on reducing radius
if tangD +tangD of previous point is larger than the length of the leg or
tangD +tangD of following points is larger than the length of the following
leg.
Then
Mutiply radius by the proportional amount of that oversize.
And repeat for all points.
If no changes occured done else retry recursive with new set of radii.
Currently working but a bit to eagerly with reducing radius. I think I know
what part of logic that needs change and I will deal with it as soon as
possible
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21945.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
TLC123 wrote
Mutiply radius by the proportional amount of that oversize.
And repeat for all points.
Are you sure, it isn't better to report an error, instead of doing a silent
repair?
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21948.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
Report what radius have been corrected by how much and have a
option to autofix or not?
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21982.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
I've got a pretty big update to the polyRound() code, some general tidying,
improvements and new features, mostly notably radii conflict handling. link
here again, if you don't want to scroll up
https://github.com/Irev-Dev/Round-Anything
First a big thank you to TLC123 who's made a substantial contribution.
So the interface of the function has changed, instead of taking two separate
lists for points and radii, it takes one list where each point takes the
format [x,y,r]. TLC suggested it and it seems more intuitive to me.
http://forum.openscad.org/file/n21983/example1.png
The radii conflict handling works by reducing two conflicting radii by a
factor, this means that the two will keep there ratios, for example if two
conflicting radii of 30 and 30 would be reduced to 10 and 10, but
conflicting radii of 10 and 40 might be reduced to 4 and 16, ie 30/30=10/10
and 10/40=4/16. There is no way to explain how it does this with out drawing
something, so here are some pictures.
http://forum.openscad.org/file/n21983/formulas.png
Not sure how well that image will turn out, here is a link for it
https://github.com/Irev-Dev/Round-Anything/blob/master/images/formulas.png
It's not the perfect solution but I'm happy with it for now as it seems
pretty robust and isn't too complicated.
For those who don't want radii conflict handling polycarious() has no
protection built in. So as it stands the radii conflict handling is not an
option within the same function? should I make it enable/disable-able within
one function?
One problem with the radii conflict handling is if you were to have 3
consecutive radii, where the 1st and 2nd radii conflict a lot and the 2nd
and 3rd conflict but less so, what will happen is that the 2nd radii will
reduce enough for the worst case which is the 1st and 2nd radii conflict.
The problem here is that with the second radius reducing, once it has been
reduced it may no longer conflict with the 3rd radius, and therefore the 3rd
radius it may not need to be reduced, but the code doesn't check for this
and so the 3rd radius will be reduced as if the original conflicting 2nd
radius were still there.
Trying to fix this would be difficult because how would it be handled? would
you go through the array in order giving the first point priority? or do you
write logic so that it reduces the radii at the worst conflict and then the
2nd worst, 3rd etc?
I think it's fine as it is because I think the radii reduction should not be
relied on in the first place, I did included a debugging option that will
tell you if any of the radii have been reduced because of a conflict. see
some of the examples.
--
View this message in context: http://forum.openscad.org/Rounded-Polygon-tp21897p21983.html
Sent from the OpenSCAD mailing list archive at Nabble.com.
I really like this capability and have been examining the code, thinking it
would be desirable to have a cleaned up version (which I'm willing to work
to produce). It seems like it would be nice to change the interface a bit,
to eliminate the cryptic numeric "mode" and replace it either with booleans,
(return_adjustments=true/false, allow_conflicts=true/false) or with text
(mode="handle_conflicts", mode="return_adjustments",
mode="allow_conflicts"). What is better? (The word "debug" is a little
vague, but my ideas are a little long.) Another thing that seems desirable
is making the naming systematic. Why do I use polyround.scad to get the
polyRound module? And is the library round-anything or Round-Anything?
(Under Linux case matters.)
The polyround.scad file contains a bunch of extra stuff, much of which is
undocumented. I've tried to use RailCustomiser and have not really figured
it out. Sometimes it returns nan. I don't know why.
Also it appears that the colinearity handling in polyRound has a bug, namely
that it doesn't do anything at all. The code is:
p=getpoints(radiipoints), //make list of coordinates without radii
Lp=len(p),
//remove the middle point of any three colinear points
newrp=[
for(i=[0:len(p)-1])
if(isColinear(p[wrap(i-1,Lp)],p[wrap(i+0,Lp)],p[wrap(i+1,Lp)])==0*||p[wrap(i+0,Lp)].z!=0*)radiipoints[wrap(i+0,Lp)]
],
and the second part of the test is always true because p[i].z==undef, so you
never delete any points. I tried deleting that second part of the test and
that appears to fix it. Have I missed something here? Is there some
reason to retain colinear points if they have a nonzero radius, which
appears to be the intention of that extra test.
I did notice that I get different results when colinear points are retained:
because there are more points, it appears that the conflict resolution
applies in my example and I get a smaller roundover than requested. This
behavior seems undesirable.
What does processRadiiPoints do? In the cases I've tried it on, I just get
back an output equal to the input. Is there some other way to specify the
radius of rounding than a direct value?
--
Sent from: http://forum.openscad.org/
Here's my take on a similar subject, incidentally also named polyround. It's
not as powerful (all corners have the same radius), but it works on any 2D
object. You can select if it will affect inside corners, outside corners or
both. If you just want, say, to round the corners of a square (or some other
shape), it's simpler to use.
Known bugs is that if the radius is big enough, it might snip off narrow
parts of the object. Don't make the radius more than 2 times the narrowest
part and you'll be fine.
When I get some spare time, I'll refactor it a bit to make it cleaner (two
internal modules for inside() and outside(), so I don't need to duplicate
code).
module polyround(radius,inside=true,outside=true){
if(inside==true){
if(outside==true){
//Inside corners
offset(r=-radius)
offset(delta=radius)
//Outside corners
offset(r=radius)
offset(delta=-radius)
children();
}else{
//Inside corners
offset(r=-radius)
offset(delta=radius)
children();
}
}else{
if(outside==true){
//Outside corners
offset(r=radius)
offset(delta=-radius)
children();
}else{
children();
}
}
}
--
Sent from: http://forum.openscad.org/
Yes I always round 2D shapes with a single radius using two or three
offsets but I haven't grouped it into a module. I don't use delta though. I
just use offset(-rad) offset(2 * rad) offset(-rad). Not sure what
difference it makes.
On Tue, 12 Mar 2019 at 06:44, Troberg troberg.anders@gmail.com wrote:
Here's my take on a similar subject, incidentally also named polyround.
It's
not as powerful (all corners have the same radius), but it works on any 2D
object. You can select if it will affect inside corners, outside corners or
both. If you just want, say, to round the corners of a square (or some
other
shape), it's simpler to use.
Known bugs is that if the radius is big enough, it might snip off narrow
parts of the object. Don't make the radius more than 2 times the narrowest
part and you'll be fine.
When I get some spare time, I'll refactor it a bit to make it cleaner (two
internal modules for inside() and outside(), so I don't need to duplicate
code).
module polyround(radius,inside=true,outside=true){
if(inside==true){
if(outside==true){
//Inside corners
offset(r=-radius)
offset(delta=radius)
//Outside corners
offset(r=radius)
offset(delta=-radius)
children();
}else{
//Inside corners
offset(r=-radius)
offset(delta=radius)
children();
}
}else{
if(outside==true){
//Outside corners
offset(r=radius)
offset(delta=-radius)
children();
}else{
children();
}
}
}
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Your variant does both inside and outside corners. Mine has them separated.
Apart from that, they are the same.
--
Sent from: http://forum.openscad.org/
The round-anything library includes this, which rounds inside and outside
corners independently with separate rounding radii. You can set IR or OR to
zero as desired if you want only internal or external rounding. Looks short
and elegant to me. Does this have some shortcomings I'm overlooking
compared to the other versions?
module round2d(OR=3,IR=1){
offset(OR){
offset(-IR-OR){
offset(IR){
children();
}
}
}
}
So this is nice for the cases that it addresses, but it doesn't address the
problem that the more general polyround does, with the possibility of
rounding only some corners, and rounding different corners by different
amounts, and doing the rounding to a point list that can be input to sweep
type operations.
--
Sent from: http://forum.openscad.org/
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
If not, could someone suggest a suitable technique to achieve this?
William
On Tue, Mar 12, 2019 at 9:40 AM adrianv avm4@cornell.edu wrote:
The round-anything library includes this, which rounds inside and outside
corners independently with separate rounding radii. You can set IR or OR
to
zero as desired if you want only internal or external rounding. Looks
short
and elegant to me. Does this have some shortcomings I'm overlooking
compared to the other versions?
module round2d(OR=3,IR=1){
offset(OR){
offset(-IR-OR){
offset(IR){
children();
}
}
}
}
So this is nice for the cases that it addresses, but it doesn't address the
problem that the more general polyround does, with the possibility of
rounding only some corners, and rounding different corners by different
amounts, and doing the rounding to a point list that can be input to sweep
type operations.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I don't thinks so because all the rounding discussed so far uses circular
arcs that meet straight lines tangentially. That is continuous in gradient
but has a discontinuity of curvature. Possibly Bezier splines would do the
job?
On Tue, 12 Mar 2019 at 14:27, William Adams will.adams@frycomm.com wrote:
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
If not, could someone suggest a suitable technique to achieve this?
William
On Tue, Mar 12, 2019 at 9:40 AM adrianv avm4@cornell.edu wrote:
The round-anything library includes this, which rounds inside and outside
corners independently with separate rounding radii. You can set IR or OR
to
zero as desired if you want only internal or external rounding. Looks
short
and elegant to me. Does this have some shortcomings I'm overlooking
compared to the other versions?
module round2d(OR=3,IR=1){
offset(OR){
offset(-IR-OR){
offset(IR){
children();
}
}
}
}
So this is nice for the cases that it addresses, but it doesn't address
the
problem that the more general polyround does, with the possibility of
rounding only some corners, and rounding different corners by different
amounts, and doing the rounding to a point list that can be input to sweep
type operations.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
On Tue, Mar 12, 2019 at 03:06:43PM +0000, nop head wrote:
I don't thinks so because all the rounding discussed so far uses circular
arcs that meet straight lines tangentially. That is continuous in gradient
but has a discontinuity of curvature. Possibly Bezier splines would do the
job?
I would REALLY like to have bezier curves in openscad. The $fn parameter
would determine how many interpolation points would be used to convert the
spline to triangles.
The problem is a bit how to integrate the splines with the rest of
openscad: all primitives are 3D OBJECTS and splines usually define a
surface (in 3D if required).
We already can create an object from arbitrary 3D points, right?
(or is this only possible for 2D objects? Ah! I should know this: I
designed pyramids from 5 vertices with my niece this weekend, Duh!).
So it would suffice if a function could return a list of points (*)
corresponding to a definition of an object defined by bezier surfaces.
On the other hand, the "define bezier primitive" could internally
build on the polyhedron function: building the list-of-vertices and
faces as it processes its own input...
I think the "create bezier object" primitve would have very similar
arguments as the polyhedron primitve: A list of vertices and a list of
faces. To define a bezier patch you need precisely 16 vertices for
each patch.
Roger.
(*) Not sure if such a function can return a complex object in the
current implementation: both the list-of-points and the
list-of-vertices that define the triangles. That's be nice.
On Tue, 12 Mar 2019 at 14:27, William Adams will.adams@frycomm.com wrote:
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
If not, could someone suggest a suitable technique to achieve this?
William
On Tue, Mar 12, 2019 at 9:40 AM adrianv avm4@cornell.edu wrote:
The round-anything library includes this, which rounds inside and outside
corners independently with separate rounding radii. You can set IR or OR
to
zero as desired if you want only internal or external rounding. Looks
short
and elegant to me. Does this have some shortcomings I'm overlooking
compared to the other versions?
module round2d(OR=3,IR=1){
offset(OR){
offset(-IR-OR){
offset(IR){
children();
}
}
}
}
So this is nice for the cases that it addresses, but it doesn't address
the
problem that the more general polyround does, with the possibility of
rounding only some corners, and rounding different corners by different
amounts, and doing the rounding to a point list that can be input to sweep
type operations.
--
Sent from: http://forum.openscad.org/
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
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
On 3/12/2019 10:55 AM, Rogier Wolff wrote:
(*) Not sure if such a function can return a complex object in the
current implementation: both the list-of-points and the
list-of-vertices that define the triangles. That's be nice.
Sure. Just return a vector, the first element of which is a vector of
points and the second element of which is a vector of vertices.
Definitely none of the rounding code I've seen does this directly. Thanks
for bringing this to my attention. I'm actually interested in mplementing
this myself. Does it make sense to use tangent circles to define the
location of the curved section and then fit a bezier instead of a circular
arc into the space? Or is there some better way to define the space where
the curve should go at a given corner? Like perhaps a setback distance
along the edge from the corner?
nophead wrote
I don't thinks so because all the rounding discussed so far uses circular
arcs that meet straight lines tangentially. That is continuous in gradient
but has a discontinuity of curvature. Possibly Bezier splines would do the
job?
On Tue, 12 Mar 2019 at 14:27, William Adams <
will.adams@
> wrote:
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
If not, could someone suggest a suitable technique to achieve this?
William
--
Sent from: http://forum.openscad.org/
rew wrote
On Tue, Mar 12, 2019 at 03:06:43PM +0000, nop head wrote:
I don't thinks so because all the rounding discussed so far uses circular
arcs that meet straight lines tangentially. That is continuous in
gradient
but has a discontinuity of curvature. Possibly Bezier splines would do
the
job?
I would REALLY like to have bezier curves in openscad. The $fn parameter
would determine how many interpolation points would be used to convert the
spline to triangles.
Does it need to be in the base language for some reason? Bezier curves have
been implemented several times. Here are three examples:
https://github.com/revarbat/BOSL/wiki/beziers.scad
https://www.thingiverse.com/thing:8483/attribution_is_important
https://github.com/JustinSDK/dotSCAD
And actually that third one has a bezier_smooth function that maybe does the
smooth curvature smoothing that was requested.
--
Sent from: http://forum.openscad.org/
I don't have an artistic bone in my body, so to me circles meeting tangents
are fine. I don't have any Apple products and probably never will as they
are just a waste of money. They look nice but are very unreliable and
locked in.
If I could see a need for avoiding discontinuities in curvature I am sure I
could work something out with Bezier splines but life is too short. I use
Bezier splines to approximate minimum energy curves to model bent strips.
2D Bezier curves are easy in OpenSCAD, and 3D Bezier curves are certainly
possible with polyhedron. I don't think new primitives are needed.
On Tue, 12 Mar 2019 at 23:15, adrianv avm4@cornell.edu wrote:
Definitely none of the rounding code I've seen does this directly. Thanks
for bringing this to my attention. I'm actually interested in mplementing
this myself. Does it make sense to use tangent circles to define the
location of the curved section and then fit a bezier instead of a circular
arc into the space? Or is there some better way to define the space where
the curve should go at a given corner? Like perhaps a setback distance
along the edge from the corner?
nophead wrote
I don't thinks so because all the rounding discussed so far uses circular
arcs that meet straight lines tangentially. That is continuous in
gradient
but has a discontinuity of curvature. Possibly Bezier splines would do
the
job?
On Tue, 12 Mar 2019 at 14:27, William Adams <
will.adams@
> wrote:
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
If not, could someone suggest a suitable technique to achieve this?
William
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
On Tue, Mar 12, 2019 at 11:28:24PM +0000, nop head wrote:
I don't have an artistic bone in my body, so to me circles meeting tangents
are fine. I don't have any Apple products and probably never will as they
are just a waste of money. They look nice but are very unreliable and
locked in.
If I could see a need for avoiding discontinuities in curvature I am sure I
could work something out with Bezier splines but life is too short. I use
Bezier splines to approximate minimum energy curves to model bent strips.
2D Bezier curves are easy in OpenSCAD, and 3D Bezier curves are certainly
possible with polyhedron. I don't think new primitives are needed.
At first I didn't think so, then I did, now I do....
It would be VERY nice if a recursive bezier module could be built. But
that requires returning "patches" that will build up to the sides of a
polyhedron. Writing out the recursion by hand and implementing the
stack on an array is very tedious. It's like on the first day of work
you new boss points at an actual Turing machine, and says: "that's
just as turing-complete as that windows machine over there, you can
use the turing machine."
It's theoretically possible, but WAY more convenient if there is some
language support.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
Isn't it just some for loops and list comprehension? I don't think it is
much harder than sweep if you want a regular array of surface points.
On Wed, 13 Mar 2019, 11:00 Rogier Wolff <R.E.Wolff@bitwizard.nl wrote:
On Tue, Mar 12, 2019 at 11:28:24PM +0000, nop head wrote:
I don't have an artistic bone in my body, so to me circles meeting
tangents
are fine. I don't have any Apple products and probably never will as they
are just a waste of money. They look nice but are very unreliable and
locked in.
If I could see a need for avoiding discontinuities in curvature I am
sure I
could work something out with Bezier splines but life is too short. I use
Bezier splines to approximate minimum energy curves to model bent strips.
2D Bezier curves are easy in OpenSCAD, and 3D Bezier curves are certainly
possible with polyhedron. I don't think new primitives are needed.
At first I didn't think so, then I did, now I do....
It would be VERY nice if a recursive bezier module could be built. But
that requires returning "patches" that will build up to the sides of a
polyhedron. Writing out the recursion by hand and implementing the
stack on an array is very tedious. It's like on the first day of work
you new boss points at an actual Turing machine, and says: "that's
just as turing-complete as that windows machine over there, you can
use the turing machine."
It's theoretically possible, but WAY more convenient if there is some
language support.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
**
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
rew wrote
If I could see a need for avoiding discontinuities in curvature I am sure
I
could work something out with Bezier splines but life is too short. I use
Bezier splines to approximate minimum energy curves to model bent strips.
2D Bezier curves are easy in OpenSCAD, and 3D Bezier curves are certainly
possible with polyhedron. I don't think new primitives are needed.
At first I didn't think so, then I did, now I do....
It would be VERY nice if a recursive bezier module could be built. But
that requires returning "patches" that will build up to the sides of a
polyhedron. Writing out the recursion by hand and implementing the
stack on an array is very tedious. It's like on the first day of work
you new boss points at an actual Turing machine, and says: "that's
just as turing-complete as that windows machine over there, you can
use the turing machine."
It's theoretically possible, but WAY more convenient if there is some
language support.
If it's "theoretically possible" then after someone does it you can use the
code and you don't really care how hard it was to write. (How hard is it to
write into OpenSCAD as a primitive?) OpenSCAD can do recursion, so why
would you want to maintain your own stack? What is it that you think can't
be done? Or maybe more to the point, what exactly would your desired module
do? Maybe we can write it.
--
Sent from: http://forum.openscad.org/
On Wed, Mar 13, 2019 at 11:41:30AM +0000, nop head wrote:
Isn't it just some for loops and list comprehension? I don't think it is
much harder than sweep if you want a regular array of surface points.
Hmm.. Ok. Maybe it's doable... I'll give it a go later on. Maybe this
weekend. Feel free to remind me. I might forget.
(I already wrote the first test-control-points and the call of the
module, and the level zero approximation.... )
Roger.
On Wed, 13 Mar 2019, 11:00 Rogier Wolff <R.E.Wolff@bitwizard.nl wrote:
On Tue, Mar 12, 2019 at 11:28:24PM +0000, nop head wrote:
I don't have an artistic bone in my body, so to me circles meeting
tangents
are fine. I don't have any Apple products and probably never will as they
are just a waste of money. They look nice but are very unreliable and
locked in.
If I could see a need for avoiding discontinuities in curvature I am
sure I
could work something out with Bezier splines but life is too short. I use
Bezier splines to approximate minimum energy curves to model bent strips.
2D Bezier curves are easy in OpenSCAD, and 3D Bezier curves are certainly
possible with polyhedron. I don't think new primitives are needed.
At first I didn't think so, then I did, now I do....
It would be VERY nice if a recursive bezier module could be built. But
that requires returning "patches" that will build up to the sides of a
polyhedron. Writing out the recursion by hand and implementing the
stack on an array is very tedious. It's like on the first day of work
you new boss points at an actual Turing machine, and says: "that's
just as turing-complete as that windows machine over there, you can
use the turing machine."
It's theoretically possible, but WAY more convenient if there is some
language support.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
**
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
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
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
William Adams-2 wrote
Would it be possible to use this tool to get rounding as is done in some
industrial designs?
https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
If not, could someone suggest a suitable technique to achieve this?
William
I studied Bezier curves last night (about which I knew nothing) and figured
out how cubic bezier curves can be used to achieve continuous curvature
rounding. One parameter needs to be specified at each corner. I'm
wondering what the best way to specify this parameter is. It could be
specified as the distance to the intersection point of the bezier with the
edge. Or it could be specified as the distance from the corner tip to the
maximum projection of the curve (e.g. how much is cut off). Or perhaps
some other way relating to rounding with circular arcs?
I also wonder if it might be desirable to round in a continuous curvature
fashion but more aggressively using a higher order Bezier. These roundovers
with the cubic bezier are rather gentle. Anybody know how to calculate the
curvature at the endpoints of an order 4 (or general order) bezier?
Here are two examples. The green line shows the bezier curve fit to the
corner shown in red. Note how far back it extends before finally
terminating on the linear portion? It means you need a long flat edge to
have room for a fairly modest roundover.
http://forum.openscad.org/file/t2477/bez.png
--
Sent from: http://forum.openscad.org/
I have not looked at the detailed properties, but Catmull–Rom
spline have the nice benefit that they don't need additional
control points. So for "just rounding stuff" that seems to be
quite interesting.
ciao,
Torsten.
tp3 wrote
I have not looked at the detailed properties, but Catmull–Rom
spline have the nice benefit that they don't need additional
control points. So for "just rounding stuff" that seems to be
quite interesting.
I also didn't look in detail, but it's not apparent that these splines have
any nice derivative properties, like even first derivative control. Having
no control parameters isn't necessary a good thing, as indicated by the
cubic bezier, which I think doesn't produce a curved enough roundover.
I did a little more analysis and I think a good way to do continuous
curvature roundovers is to use order 4 beziers. This ends up providing two
parameters. I'm still not sure of the optimal way to expose these
parameters to the user. Basically with these beziers you set p0 to one end
point, p4 to the other endpoint, p2 to the point of the corner, and the
remaining p1 and p3 are symmetrically placed on the intervals [p0,p2] and
[p2,p4]. The parameters that need to be chosen are the distance, d, of p0
and p4 from the corner and the distance a, of p1 and p2 from the corner.
Choosing a=d gives the maximum roundover, which I think is probably what is
generally desired. If we consider doing a chamfer by simply connecting p0
and p4 then the roundover that results from a=d will put the tip of the
roundover at 5/8 of the way from the corner point to the chamfer. Setting
a=0 puts the roundover 1/8 of the way from the corner to the chamfer.
So I could have the user specify d (the distance along the line segment to
the start of the roundover curve) or I could have the user specify the
distance to the line between p0 and p4. And for a I can have the user
specify a value on [0,1] to control the range from 0 to d, or I could have
the user specify something from [1/8 to 5/8]*d---specifying the actual
location of the roundover tip.
Maybe I'll post some pictures later since I think the above may not be
clear.
--
Sent from: http://forum.openscad.org/
http://forum.openscad.org/file/t2477/diagram.jpg So there's an image
showing a corner to be smoothed. The control points p0, ..., p4 are located
as shown, with p0 and p4 at the base of the bezier curve, p2 at the tip of
the corner, and p1 and p3 on the line segment in between somewhere. It
takes two parameters to specify the 4th order curve: d, the distance from
the corner (p2) to the base control points (p0 and p4) and a, the distance
to the secondary control points p1 and p3. The most rounding occurs when
a=d, at which point c = (5/8)*h. The least rounding occurs when a=0, at
which point c=(1/8)*h. So the user could specify d or h. Which is more
natural? For the amount of curvature the user could specify a (which
seems not very meaningful) or a/d which is a sort of smoothing factor
that ranges on [0,1] c, how much of the corner to "cut off" (but must be
in the range [1/8,5/8]*h the amount to cut off as a fraction of h, so a
value in the range [1/8, 5/8]
--
Sent from: http://forum.openscad.org/
You are right, a degree 3 Bezier arc would at most be zero curvature at one
extreme and tangent continuous at the other. The minimal degree for a zero
curvature at each extremes is really 5. However, I would use a degree 6
Bezier arc because you get too restricted setting p2 to the vertex of the
polygon. I did a experiment with degree 6 with the following strategy.
[image: pc.PNG]
The collinearity of P0, P1 and P2 implies zero curvature of the arc at P0
besides the tangent to segment [C.P0] (similarly for P5) .
The position of P2 (respect. P3) follows a ratio r0 in the interval (0,1)
in the segment [C,P0] (respect. [C,P5]).
The position of P1 (respect. P4) follows a ratio r1 in the interval (0,1)
in the segment [P2,P0] (respect. [P3,P5]).
This two ratios give some flexibility to the shape of the curve.
Here is a code that follows that strategy to round a polygon. I haven't
tried with non-convex polygons but it should work fine.
// polygon to be rounded
q = [ [0,0], [30,0], [15,20] ];
color("blue") roundedPoly(q, 20, 1/5);
color("yellow") roundedPoly(q, 20, 1/3);
color("red") roundedPoly(q, 20, 1/2);
translate([30,0,0]) {
color("blue") roundedPoly(q, 20, 1/5, 1/3);
color("yellow") roundedPoly(q, 20, 1/3, 1/3);
color("red") roundedPoly(q, 20, 1/2, 1/3);
}
module roundedPoly(p, n=20, r=1/4, r0=2/3, r1=1/2) {
assert(len(p)>2, "polygonal has less than 3 points");
assert(r>0 && r<1, "improper value of argument r");
l = len(p);
q = [for(i=[0:l-1])
let( p0 = (1-r)p[i] + rp[(i+l-1)%l] ,
p1 = p[i],
p2 = (1-r)p[i] + rp[(i+1)%l] )
each BZeroCurvature(p0,p1,p2,n,r0,r1) ];
line(q, closed=true);
}
// a Bezier arc of degree 5 from p0 to p2, tangent to
// [p0,p1] at p0, tangent to [p1,p2) at p2 and
// with zero curvature at p0 and p2
// n = # of points in the arc
// r0, r1 - form factors in the open interval (0,1)
function BZeroCurvature(p0,p1,p2,n=20,r0=2/3,r1=1/2) =
assert(r0>0 && r0<1 && r1>0 && r1<1, "improper value of r0 or r1")
let( p = [ p0,
p0 + r0*(p1-p0)r1,
p0 + r0(p1-p0),
p2 + r0*(p1-p2),
p2 + r0*(p1-p2)*r1,
p2 ] )
BezierCurve(p,n);
// p are the curve control points, the curve degree is len(p)-1
function BezierCurve(p, n=10) =
[for(i=[0:n-1]) BezierPoint(p, i/(n-1)) ];
function BezierPoint(p, u) =
(len(p) == 2)?
u*p[1] + (1-u)p[0] :
uBezierPoint([for(i=[1:len(p)-1]) p[i] ], u)
+ (1-u)*BezierPoint([for(i=[0:len(p)-2]) p[i] ], u);
module line(p,closed=false,w=0.1)
for(i=[0:len(p)-(closed? 1:2)]){
hull(){ translate(to3d(p[i])) sphere(w);
translate(to3d(p[(i+1)%len(p)])) sphere(w); }
}
function to3d(p) = len(p)==3 ? p : [p.x,p.y,0];
[image: roundedPoly.PNG]
I would not expect any good interface for Bezier stuffs could be designed
to solve a large set of problems to justify its implementation in the core
of OpenSCAD. Each modeling problem requires specific codes to be written
and that is easily done in users space if you have the needed mathematical
background. Anyway, to work with Bezier curves and surfaces, even using
other people good libraries, a minimal mathematical background is necessary.
I missed to mention that the argument r of roundedPoly() controls how much
of the corners are rounded. It has an effect similar to radius when
circular arcs are used.
Ronaldo wrote
You are right, a degree 3 Bezier arc would at most be zero curvature at
one
extreme and tangent continuous at the other. The minimal degree for a zero
curvature at each extremes is really 5. However, I would use a degree 6
Bezier arc because you get too restricted setting p2 to the vertex of the
polygon. I did a experiment with degree 6 with the following strategy.
I'm a little puzzled. Maybe you are using the word degree differently than
I think? I was thinking a degree 3 Bezier is a cubic polynomial and has 4
control points. It can be set to zero curvature at both ends but has only
one degree of freedom, which just controls the size of the curve. That
isn't enough. It appears to me that degree 4, with 5 control points, is
sufficient. Yes, you do need to put p2 on the vertex. This doesn't seem to
cause any undesirable restriction, and it is definitely possible to satisfy
the zero curvature condition. One parameter remains that controls the
degree of curvature, as I noted in my previous message. Having fewer
parameters is often better than more, so I'm not sure about what the value
of more degrees of freedom is of going to the case with 6 control points
(which is a 5th degree polynomial). I did experiment a bit with your code
and you can get some rather sharp cornered results. It seemed like r1
usually had little effect.
--
Sent from: http://forum.openscad.org/
On Wed, Mar 13, 2019 at 12:52:07PM +0100, Rogier Wolff wrote:
On Wed, Mar 13, 2019 at 11:41:30AM +0000, nop head wrote:
Isn't it just some for loops and list comprehension? I don't think it is
much harder than sweep if you want a regular array of surface points.
Hmm.. Ok. Maybe it's doable... I'll give it a go later on. Maybe this
weekend. Feel free to remind me. I might forget.
[... I already wrote .. ]
wrong!
I started over. I now have a bezier curve in 2D. This is at $fn = 15,
the parameter has not yet been added to the function.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
Hi,
I now have a bezier patch in 3d: <see attachment>
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
adrianv avm4@cornell.edu wrote:
I'm a little puzzled. Maybe you are using the word degree differently than
I think? I was thinking a degree 3 Bezier is a cubic polynomial and has 4
control points. It can be set to zero curvature at both ends but has only
one degree of freedom, which just controls the size of the curve. That
isn't enough. It appears to me that degree 4, with 5 control points, is
sufficient. Yes, you do need to put p2 on the vertex. This doesn't seem
to
cause any undesirable restriction, and it is definitely possible to satisfy
the zero curvature condition. One parameter remains that controls the
degree of curvature, as I noted in my previous message. Having fewer
parameters is often better than more, so I'm not sure about what the value
of more degrees of freedom is of going to the case with 6 control points
(which is a 5th degree polynomial). I did experiment a bit with your code
and you can get some rather sharp cornered results. It seemed like r1
usually had little effect.
You are right regarding the degrees. I wrongly said 5 instead of 4 and 6
instead of 5. A degree n Bezier curve has n+1 control points.
However I disagree that you could meet the zero curvature constraint at
both end with degree 3 curves. To have a zero curvature at the beginning of
the arc, the first 3 control points should co-linear. The same is true for
the ending point. If we require zero curvature at both ends all four
control points should be co-linear and the arc reduces to a straight
segment.
Degree 4 is in fact the minimal degree for zero curvature at both ends for
a non-straight line Bezier arc. However degree 5 solution give us not only
more degrees of freedom but a better arc shape as will see bellow. Degree 4
solution produces an arc with a greater curvature variation and a more
pointed arc. Degree 5 shape is able to produce arcs that resemble circular
arcs.
I have revised my codes in order to generate alternatively both solutions.
The two missed functions and line() module are defined as before.
q = [ [0,0], [30,0], [30,30], [0,30] ];
color("blue") roundedPoly(q, 20, 4, 1/3);
color("yellow") roundedPoly(q, 20, 4, 2/3);
color("red") roundedPoly(q, 20, 4, 1);
translate([35,0,0]) {
color("blue") roundedPoly(q, 20, 5, 1/3, 17/32);
color("yellow") roundedPoly(q, 20, 5, 2/3, 17/32);
color("red") roundedPoly(q, 20, 5, 1, 17/32);
color("green") translate([15,15,-1]) circle(15);
}
module roundedPoly(p, n=20, degree=4, r=1/4, r0=2/3, r1=17/32)
{
assert(len(p)>2, "polygonal has less than 3 points");
assert(r>0 && r<=1, "r out of range");
assert(r0>0 && r<=1, "r0 out of range");
assert(degree==4 || degree==5, "degree should be 4 or 5");
assert(degree==5 || (r1>0 && r1<=1), "r1 out of range");
l = len(p);
q = [for(i=[0:l-1])
let( p0 = (1-r/2)p[i] + rp[(i+l-1)%l]/2 ,
p1 = p[i],
p2 = (1-r/2)p[i] + rp[(i+1)%l]/2 )
each BZeroCurvature(p0,p1,p2,n,degree,r0,r1) ];
line(q, closed=true,w=0.2);
}
// a Bezier arc of degree 4 or 5 from p0 to p2, tangent to
// [p0,p1] at p0, tangent to [p1,p2) at p2 and
// with zero curvature at p0 and p2
// n - # of points in the arc
// r0 - form factor in the interval [0,1]
// r1 - form factor in the interval [0,1] (for degree=5 only)
function BZeroCurvature(p0,p1,p2,n=20,degree=4,r0=2/3,r1=1/2) =
assert(r0>0 && r0<=1, "r0 out of range")
assert(degree==4 || degree==5, "degree should be 4 or 5")
assert(degree==5 || (r1>0 && r1<=1), "r1 out of range")
let( r1 = degree==4 ? 1 : r1,
p = [ p0,
p0 + r0*(p1-p0)r1,
each degree==4 ?
[p1] :
[p0 + r0(p1-p0), p2 + r0*(p1-p2)],
p2 + r0*(p1-p2)*r1,
p2 ] )
BezierCurve(p,n);
And got the following image:
[image: roundedPoly2.PNG]
At left we have the arcs of degree 4 and at right the degree 5 alternative
for similar form factors. With r=1, which means that the straight lines
between arcs reduce to a point, drawn here with red lines, the degree 4
solution keeps the middle point of each arc much nearer to the polygon
vertex. With degree 5 on the other hand it is possible to get a good
approximation of a circular arc with r1~= 17/32, a value I got empirically.
I played around a little bit with Bezier curves in OpenSCAD before, and its
fairly trivial to write a recursive bezier curve function that can handle
arbitrary curve orders. They are also agnostic to the dimension of points
2D/3D(or higher dimension?)
Here are the functions I have used:
// return point along curve at position "t" in range [0,1]
// use ctlPts[index] as the first control point
// Bezier curve has order == n
function BezierPoint(ctlPts, t, index, n) =
let (
l = len(ctlPts),
end = index+n
)
//assert(end < l)
(n > 0) ?
BezierPoint([
for (i = [index:end])
let (p1 = ctlPts[i], p2 = ctlPts[i+1]) p1 + t * (p2 - p1)
], t, 0, n-1) :
ctlPts[0];
function flatten(l) = [ for (a = l) for (b = a) b ];
// n sets the order of the Bezier curves that will be stitched together
// if no parameter n is given, points will be generated for a single curve
of order == len(ctlPts) - 1
function BezierPath(ctlPts, index, n) =
let (
l1 = $fn > 3 ? $fn-1 : 200,
index = index == undef ? 0 : index,
l2 = len(ctlPts),
n = (n == undef || n > l2-1) ? l2 - 1 : n
)
//assert(n > 0)
flatten([for (segment = [index:1:l2-1-n])
[for (i = [0:l1] ) BezierPoint(ctlPts, i / l1, index+segment*n, n)]
]);
On Thu, Mar 14, 2019 at 1:53 PM Ronaldo Persiano rcmpersiano@gmail.com
wrote:
adrianv avm4@cornell.edu wrote:
I'm a little puzzled. Maybe you are using the word degree differently
than
I think? I was thinking a degree 3 Bezier is a cubic polynomial and has 4
control points. It can be set to zero curvature at both ends but has only
one degree of freedom, which just controls the size of the curve. That
isn't enough. It appears to me that degree 4, with 5 control points, is
sufficient. Yes, you do need to put p2 on the vertex. This doesn't seem
to
cause any undesirable restriction, and it is definitely possible to
satisfy
the zero curvature condition. One parameter remains that controls the
degree of curvature, as I noted in my previous message. Having fewer
parameters is often better than more, so I'm not sure about what the value
of more degrees of freedom is of going to the case with 6 control points
(which is a 5th degree polynomial). I did experiment a bit with your code
and you can get some rather sharp cornered results. It seemed like r1
usually had little effect.
You are right regarding the degrees. I wrongly said 5 instead of 4 and 6
instead of 5. A degree n Bezier curve has n+1 control points.
However I disagree that you could meet the zero curvature constraint at
both end with degree 3 curves. To have a zero curvature at the beginning of
the arc, the first 3 control points should co-linear. The same is true for
the ending point. If we require zero curvature at both ends all four
control points should be co-linear and the arc reduces to a straight
segment.
Degree 4 is in fact the minimal degree for zero curvature at both ends for
a non-straight line Bezier arc. However degree 5 solution give us not only
more degrees of freedom but a better arc shape as will see bellow. Degree 4
solution produces an arc with a greater curvature variation and a more
pointed arc. Degree 5 shape is able to produce arcs that resemble circular
arcs.
I have revised my codes in order to generate alternatively both solutions.
The two missed functions and line() module are defined as before.
q = [ [0,0], [30,0], [30,30], [0,30] ];
color("blue") roundedPoly(q, 20, 4, 1/3);
color("yellow") roundedPoly(q, 20, 4, 2/3);
color("red") roundedPoly(q, 20, 4, 1);
translate([35,0,0]) {
color("blue") roundedPoly(q, 20, 5, 1/3, 17/32);
color("yellow") roundedPoly(q, 20, 5, 2/3, 17/32);
color("red") roundedPoly(q, 20, 5, 1, 17/32);
color("green") translate([15,15,-1]) circle(15);
}
module roundedPoly(p, n=20, degree=4, r=1/4, r0=2/3, r1=17/32)
{
assert(len(p)>2, "polygonal has less than 3 points");
assert(r>0 && r<=1, "r out of range");
assert(r0>0 && r<=1, "r0 out of range");
assert(degree==4 || degree==5, "degree should be 4 or 5");
assert(degree==5 || (r1>0 && r1<=1), "r1 out of range");
l = len(p);
q = [for(i=[0:l-1])
let( p0 = (1-r/2)p[i] + rp[(i+l-1)%l]/2 ,
p1 = p[i],
p2 = (1-r/2)p[i] + rp[(i+1)%l]/2 )
each BZeroCurvature(p0,p1,p2,n,degree,r0,r1) ];
line(q, closed=true,w=0.2);
}
// a Bezier arc of degree 4 or 5 from p0 to p2, tangent to
// [p0,p1] at p0, tangent to [p1,p2) at p2 and
// with zero curvature at p0 and p2
// n - # of points in the arc
// r0 - form factor in the interval [0,1]
// r1 - form factor in the interval [0,1] (for degree=5 only)
function BZeroCurvature(p0,p1,p2,n=20,degree=4,r0=2/3,r1=1/2) =
assert(r0>0 && r0<=1, "r0 out of range")
assert(degree==4 || degree==5, "degree should be 4 or 5")
assert(degree==5 || (r1>0 && r1<=1), "r1 out of range")
let( r1 = degree==4 ? 1 : r1,
p = [ p0,
p0 + r0*(p1-p0)r1,
each degree==4 ?
[p1] :
[p0 + r0(p1-p0), p2 + r0*(p1-p2)],
p2 + r0*(p1-p2)*r1,
p2 ] )
BezierCurve(p,n);
And got the following image:
[image: roundedPoly2.PNG]
At left we have the arcs of degree 4 and at right the degree 5 alternative
for similar form factors. With r=1, which means that the straight lines
between arcs reduce to a point, drawn here with red lines, the degree 4
solution keeps the middle point of each arc much nearer to the polygon
vertex. With degree 5 on the other hand it is possible to get a good
approximation of a circular arc with r1~= 17/32, a value I got empirically.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Ronaldo wrote
adrianv <
avm4@
> wrote:
However I disagree that you could meet the zero curvature constraint at
both end with degree 3 curves. To have a zero curvature at the beginning
of
the arc, the first 3 control points should co-linear. The same is true for
the ending point. If we require zero curvature at both ends all four
control points should be co-linear and the arc reduces to a straight
segment.
No it doesn't. I posted graphs a few messages back showing zero curvature
order 3 beziers, and I had directly verified that the curvature was indeed
zero by calculating the derivatives both symbolically and numerically, so I
am reasonably sure it was correct. With p0=p3 set to the endpoints of the
arc you then set p1=p2 equal to the point of the corner. You achieve the
co-linearity condition in a degenerate fashion, since p1 and p2 are on both
lines.
Degree 4 is in fact the minimal degree for zero curvature at both ends for
a non-straight line Bezier arc. However degree 5 solution give us not only
more degrees of freedom but a better arc shape as will see bellow. Degree
4
solution produces an arc with a greater curvature variation and a more
pointed arc. Degree 5 shape is able to produce arcs that resemble circular
arcs.
It appears that your degree 4 code can also generate a nearly circular arc:
just set r1 very small, like .01 and you get something visually
indistinguishable from a circle. In fact, it looks very similar to your
empirically determined 17/32 value. I wonder what are the parameters for
the order 5 bezier that reduce it to the order 4.
On the left is order 4 with r1=0.01 and on the right, order 5 with r1=17/32.
http://forum.openscad.org/file/t2477/round2.png
--
Sent from: http://forum.openscad.org/
Well I'm a bit embarrassed after playing with the Bezier code I previously
posted(hadn't messed with it in a while). I realized it had some bugs and
some inefficiencies.
I fixed it up a bit and included a more complete example with example of
various orders along random path, and a classic circle approximation.
Posting gist this time in case I need to update it again, but I believe the
code is correct now :
https://gist.github.com/thehans/2da9f7c608f4a689456e714eaa2189e6
On Thu, Mar 14, 2019 at 5:03 PM adrianv avm4@cornell.edu wrote:
Ronaldo wrote
adrianv <
avm4@
> wrote:
However I disagree that you could meet the zero curvature constraint at
both end with degree 3 curves. To have a zero curvature at the beginning
of
the arc, the first 3 control points should co-linear. The same is true
for
the ending point. If we require zero curvature at both ends all four
control points should be co-linear and the arc reduces to a straight
segment.
No it doesn't. I posted graphs a few messages back showing zero curvature
order 3 beziers, and I had directly verified that the curvature was indeed
zero by calculating the derivatives both symbolically and numerically, so I
am reasonably sure it was correct. With p0=p3 set to the endpoints of the
arc you then set p1=p2 equal to the point of the corner. You achieve the
co-linearity condition in a degenerate fashion, since p1 and p2 are on both
lines.
Degree 4 is in fact the minimal degree for zero curvature at both ends
for
a non-straight line Bezier arc. However degree 5 solution give us not
only
more degrees of freedom but a better arc shape as will see bellow. Degree
4
solution produces an arc with a greater curvature variation and a more
pointed arc. Degree 5 shape is able to produce arcs that resemble
circular
arcs.
It appears that your degree 4 code can also generate a nearly circular arc:
just set r1 very small, like .01 and you get something visually
indistinguishable from a circle. In fact, it looks very similar to your
empirically determined 17/32 value. I wonder what are the parameters for
the order 5 bezier that reduce it to the order 4.
On the left is order 4 with r1=0.01 and on the right, order 5 with
r1=17/32.
http://forum.openscad.org/file/t2477/round2.png
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
No it doesn't. I posted graphs a few messages back showing zero curvature
order 3 beziers, and I had directly verified that the curvature was indeed
zero by calculating the derivatives both symbolically and numerically, so I
am reasonably sure it was correct. With p0=p3 set to the endpoints of the
arc you then set p1=p2 equal to the point of the corner. You achieve the
co-linearity condition in a degenerate fashion, since p1 and p2 are on both
lines.
You are right. Putting both intermediate control points at the corner will
meet the conditions of zero curvature at the two ends of a degree 3 Bezier
arc. I could not imagine that solution before. And certainly this is a very
constrained alternative.
It appears that your degree 4 code can also generate a nearly circular arc:
just set r1 very small, like .01 and you get something visually
indistinguishable from a circle. In fact, it looks very similar to your
empirically determined 17/32 value. I wonder what are the parameters for
the order 5 bezier that reduce it to the order 4.
I have confirmed your statement about the approximation of circular arcs by
degree 4 curves. It seems even better than the degree 5 arc with r0=17/32.
I have applied the degree elevation formulas to deduce algebraically the
conditions r0 and r1 should satisfy in order the degree 4 and degree 5
curves be equal. From my math exercise, we should have:
degree 5 with r0 = 3/5 and r1=0 will be equivalent to
degree 4 with r0=0
I also found that r0 may be set to 0 for degree 4 and r1 may be set to 0
for degree 5 but r0 should not be 0 for degree 5 because the arc degenerate
into a line segment. I have changed the assert conditions in the my
functions accordingly.
I agree that rounding corners with degree 4 Bezier arcs give us enough
flexibility and there is no point to use greater degree. In my functions,
the parameter r of roundedPoly() controls how much the corner is rounded
like your proposed d parameter. That parameter sets the end points
relatively to the polygon edge length instead of an absolute value. The
problem with this solution is that the arc will not be symmetrical when the
two edges meeting a corner have different length. But the alternative of a
absolute value d requires a validation to avoid that two arcs rounding the
two corners of an edge intercept.
Finally, I have checked that my code works well even with not convex
polygons as can be seen in the image.
Going back to the apple article I don't think the change in shade is
anything to do with curvature. Reflected light is related to the surface
angle and that doesn't change discontinuously. It's rate of change does but
I don't see how that would affect reflected light. It looks like the left
corner is lit from both sides and the right corner is lit from right side
only.
On Fri, 15 Mar 2019 at 03:15, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
No it doesn't. I posted graphs a few messages back showing zero curvature
order 3 beziers, and I had directly verified that the curvature was indeed
zero by calculating the derivatives both symbolically and numerically, so
I
am reasonably sure it was correct. With p0=p3 set to the endpoints of
the
arc you then set p1=p2 equal to the point of the corner. You achieve the
co-linearity condition in a degenerate fashion, since p1 and p2 are on
both
lines.
You are right. Putting both intermediate control points at the corner will
meet the conditions of zero curvature at the two ends of a degree 3 Bezier
arc. I could not imagine that solution before. And certainly this is a very
constrained alternative.
It appears that your degree 4 code can also generate a nearly circular
arc:
just set r1 very small, like .01 and you get something visually
indistinguishable from a circle. In fact, it looks very similar to your
empirically determined 17/32 value. I wonder what are the parameters for
the order 5 bezier that reduce it to the order 4.
I have confirmed your statement about the approximation of circular arcs
by degree 4 curves. It seems even better than the degree 5 arc with
r0=17/32.
I have applied the degree elevation formulas to deduce algebraically the
conditions r0 and r1 should satisfy in order the degree 4 and degree 5
curves be equal. From my math exercise, we should have:
degree 5 with r0 = 3/5 and r1=0 will be equivalent to
degree 4 with r0=0
I also found that r0 may be set to 0 for degree 4 and r1 may be set to 0
for degree 5 but r0 should not be 0 for degree 5 because the arc degenerate
into a line segment. I have changed the assert conditions in the my
functions accordingly.
I agree that rounding corners with degree 4 Bezier arcs give us enough
flexibility and there is no point to use greater degree. In my functions,
the parameter r of roundedPoly() controls how much the corner is rounded
like your proposed d parameter. That parameter sets the end points
relatively to the polygon edge length instead of an absolute value. The
problem with this solution is that the arc will not be symmetrical when the
two edges meeting a corner have different length. But the alternative of a
absolute value d requires a validation to avoid that two arcs rounding the
two corners of an edge intercept.
Finally, I have checked that my code works well even with not convex
polygons as can be seen in the image.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I understand your point because I am also unable to perceive visually
curvature discontinuities. But people from visual arts have training eye to
perceive it. However, even for people like us it may be noticible on
shinning and reflecting surfaces.
The following YouTube video gives an idea of the effect of different kinds
of continuity.
It was clearer to me the effect of the various kinds of continuity with the
stripe view. Designers from automobile industry are very concerned with the
effect of shadows and light reflexions on the surface of their models.
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.
nophead wrote
Going back to the apple article I don't think the change in shade is
anything to do with curvature. Reflected light is related to the surface
angle and that doesn't change discontinuously. It's rate of change does
but
I don't see how that would affect reflected light. It looks like the left
corner is lit from both sides and the right corner is lit from right side
only.
You wouldn't see it on completely mat or reflected surface. But on brushed
or structured surfaces a lot of additional effects accumulate. Neverless,
DIY-people like most of us in this forum, me including, with a "function
first" attitude usually have only a poor understanding of surface erotics.
Only after having read "Zen And The Art Of Motorcycle Maintenance" I could
develop a first poor understandig about what kind of gears seem to purr
within Apple customer brains.
In Open Source based 3D printing, where 90% of the prints are done with
layers >= 0.2 mm and nozzles >= 0.4mm there is no point in trying to
optimize curvatures to C3 or more.
--
Sent from: http://forum.openscad.org/
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.
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/
I did a 3d printing test to assess how much of a difference these roundovers
make in the real world. I printed four 50mm squares and rounded them
using my previous code with a "cut" setting. I made one using circular arcs
with a cut of 5mm (and found a bug in my code---circles are definitely more
trouble) and I did the other three using the settings [5,.7], [5,.5], and
[5*.97,.3] where I reduced the last one so the roundover would fit on the
square.
In comparing the printed objects, I note first of all that the circular
rounded model shows a clear line visible in reflected light as I shift it
around, so the transition between the flat part of the model and the curved
part is visible. No such transition line is visible in any of the
continuous curvature 3 cases. In comparing the 0.3 case to the 0.7 case I
am able to see the flat section light up in reflected light all at once, but
it does so without the sharp edges of the circular case. The 0.3 case has
no flat section so it never has a section that lights up.
If I feel the models the circular model has what feels like a perceptible
lump at the transition. The transition is tactile. The other three cases
are indistinguishable and all feel smooth, with no perceptible transition.
My conclusion is that for models where appearance and/or feel are important,
it's better to choose the continuous curvature roundover. And really,
there's no reason not to use it for 2d scenarios since the code is now
available in cases like this (or, if you prefer to write yourself...it's
easier to write than the circular case).
The other conclusion is that it doesn't seem to matter much if you pick the
curvature parameter anywhere in the range of [0.3,0.7], as they all feel the
same. I think that the 0.3 looks slightly more elegant visually---the more
gradual curve is visually perceptible---but it does require a lot more room
to execute the curve. I would suggest that if elegant appearance is
paramount, choose the smallest curvature parameter that fits with your
model.
I would post pictures but I don't think any of this stuff can be conveyed
photographically. I asked a second person to examine the models without
explaining what was different, and presenting the models in a blind fashion,
and my observations were confirmed, so I think I didn't just dream it up.
Of course, I now wonder about the analogous 3d roundover problem. Can we
make a continuous curvature rounded cube in a similar fashion?
--
Sent from: http://forum.openscad.org/
adrianv wrote
Of course, I now wonder about the analogous 3d roundover problem. Can we
make a continuous curvature rounded cube in a similar fashion?
It shouldn't be too difficult to rotate_extrude such an arc rounding a 90°
corner and to translate/rotate 8 instances to the corners of a cube and to
hull them. Have a try.
--
Sent from: http://forum.openscad.org/
Parkinbot wrote
adrianv wrote
Of course, I now wonder about the analogous 3d roundover problem. Can we
make a continuous curvature rounded cube in a similar fashion?
It shouldn't be too difficult to rotate_extrude such an arc rounding a 90°
corner and to translate/rotate 8 instances to the corners of a cube and to
hull them. Have a try.
Wouldn't rotate extruding give a shape with discontinuous curvature in the
direction of rotation? It's the equivalent of the circular roundover, so
where it meets the linear section (the rounded edge) the curvature of the
edge will be zero and the curvature of the extruded corner will be nonzero.
It seems like some 3d bezier approach is necessary to construct the shape in
the corner. And curvature on a surface now is a vector of two values, so
maybe matching curvature is more difficult?
If I wanted a cylinder with a rounded end the rotate extrude method should
work.
Perhaps if I did a sweep of the shape I have already constructed along a
path defined by the same shape? Would that work?
--
Sent from: http://forum.openscad.org/
adrianv wrote
Wouldn't rotate extruding give a shape with discontinuous curvature in the
direction of rotation? It's the equivalent of the circular roundover, so
this is correct. But it is a start.
Looking at a poor man's cube with rounded edges:
r0 = 5;
r = 10;
hull() for(i=[-r, r], j=[-r, r],k=[-r, r]) translate([i,j,k]) sphere(r0,
$fn=40);
http://forum.openscad.org/file/t887/roundedcube.png
shows that you have to produce appropriate corner pieces that will have
proper transitions of the three corners. If you know where you have to go,
you can try to define a sweep path for it. For this you need to find a
proper parametrization of your path along the z-axis, that will transit from
a rect to the rounded path.
--
Sent from: http://forum.openscad.org/
Maybe my idea wasn't clear. Suppose I start with square =
[[0,0],[1,0],[1,1],[0,1]], and then apply my roundcorners function, so
roundsquare = roundedcorners(square,...). Now roundsquare is a path that
traces out a square with continuously rounded corners. If I use roundsquare
as a shape and sweep it along the path (elevated to 3d) of 5*roundsquare
that should give me a sort of rectangular torus with smooth edges. So I
union in some filler cube in the center and the result should hopefully be a
rounded cube with continuous curvature.
It seems like the alternative approach would be to actually figure out how
bezier curves work in 3d and directly implement the required corner patch
--
Sent from: http://forum.openscad.org/
adrianv wrote
Maybe my idea wasn't clear. Suppose I start with square =
[[0,0],[1,0],[1,1],[0,1]], and then apply my roundcorners function, so
roundsquare = roundedcorners(square,...). Now roundsquare is a path that
traces out a square with continuously rounded corners.
I understand this and that you want to go the hard way (for which
rotate_extrude will not do).
In order to do a sweep you need to create a sequence of polygons that can be
coated. Thus you have to define this sequence so that the polygons also will
grow in the desired way (as given by roundsquare) and find the
z-coordinate sequence that will also reflect the roundsquare path and the
roundedcorners rules.
With reference to my last image: You will have to define and arrange the
extrusion polygons in the way it is shown there as layers.
As I understand your approach you currently can gradually refine a given
corner in xy-space, but you don't have the means (parameters) to produce a
sequence arranged in the same sense as these layers are: so that the middle
point will follow the roundsquare path in z direction, as well as in any
other direction.
--
Sent from: http://forum.openscad.org/
Here some code for my initial approach using rotate_extrude to make a corner
and hull it. I used Ronaldos Bezier implementation. The horizontal edge
roundings are Bezier roundings, and the vertical ones are quarter circles,
due to the rotate_extrude call.
Now, look at the corner and study how you would sweep the rounding (xy) with
a Bezier instead of a quarter circle.
http://forum.openscad.org/file/t887/BezierCube.png
BezierCube(30, 200);
// #cube(200, center = true); // sizetest
module BezierCube(r = 30, s = 100)
{
q = [[0,r], [r,r], [r,0]];
p = concat([[0,0]], BZeroCurvature(q[0],q[1],q[2],n=20,r0=2/3,r1=1/2)); //
the polygon
hull() // construct the cube by hulling the corners
for(i=[0:3], j=[1,-1]) scale(j)rotate(i*90) translate([s/2-r,s/2-r,
s/2-r]) corner(p);
}
module corner(p) rotate_extrude(angle = 90, $fa=1) polygon(p);
function BezierPoint(p, u) =
(len(p) == 2)?
u*p[1] + (1-u)p[0] :
uBezierPoint([for(i=[1:len(p)-1]) p[i] ], u)
+ (1-u)*BezierPoint([for(i=[0:len(p)-2]) p[i] ], u);
function BezierCurve(p, n=10) = [for(i=[0:n-1]) BezierPoint(p, i/(n-1)) ];
function BZeroCurvature(p0,p1,p2,n=20,r0=2/3,r1=1/2) =
assert(r0>0 && r0<1 && r1>0 && r1<1, "improper value of r0 or r1")
let( p = [ p0,
p0 + r0*(p1-p0)r1,
p0 + r0(p1-p0),
p2 + r0*(p1-p2),
p2 + r0*(p1-p2)*r1,
p2 ] )
BezierCurve(p,n);
--
Sent from: http://forum.openscad.org/
OK, and here is some code that sweeps the corners in the desired way. It
scales each polygon in the sequence to get the desired extension for the xy
path before putting it into 3D (by z-rotating it (Rz)). By this you get a
bezier in all three directions.
Although it is another piece of work, it shouldn't be too difficult to
transfer this scheme into a single (and fast processing) sweep path to avoid
the unions.
http://forum.openscad.org/file/t887/BezierCube1.png
use <Naca_Sweep.scad> // https://www.thingiverse.com/thing:1208001
BezierCube(30, 200);
module BezierCube(r = 30, s = 100)
{
q = [[0.001,r], [r,r], [r,0.001]];
b = BZeroCurvature(q[0],q[1],q[2],n=20,r0=4/5,r1=2/3);
hull() // construct the cube by hulling the corners
for(i=[0:3], j=[1,-1]) scale(j)rotate(i*90) translate([s/2-r,s/2-r,
s/2-r])
bcorner(b, r);
}
module bcorner(b, r)
{
sweep(gendata(b,r));
function gendata(b,r) =
[
let(step = 90/(len(b)-1))
for(x=[0:len(b)-1])
let(l = norm(b[x])/r) // scale by bezier
Rz(x*step, Sx(l, Rx(90, vec3(b)))) // rotatex, scalex and rotatez
];
}
--
Sent from: http://forum.openscad.org/
adrianv avm4@cornell.edu wrote:
It seems like the alternative approach would be to actually figure out how
bezier curves work in 3d and directly implement the required corner patch.
Yes, that is far better and easier than the sweep idea. I am travelling
without my notebook and have no way to develop it here in detail. But I
will give you some general ideas.
A rectangular Bézier surface patch is a parametric bivariate polynomial.
Its control points (CP) are usually represented by a bidimensional matrix
of points whose columns represent the control of one of the parameter and
the rows the control of the other parameter. For instance, the surface
patch of the roundover of a cube edge like you suggested has a CP matrix
with two columns with 5 points each. It is a degree 1 by degree 4 Bézier
surface.
For the corner, we will need at least a degree 4 by degree 4 Bézier patch
which has 25 control points in a matrix 5x5.
A point in a Bézier patch with a matrix of control points P corresponding
to a pair of parameters (u, v) can be easily computed by the following
simple code:
function BPatchPoint(P, u, v) =
transpose(BezierPoint(transpose(BezierPoint(P, u)), v));
where transpose() is the matrix transpose and BezierPoint() is the function
I defined before. A polyhedral approximation of the patch can be generated
by calling this function for a matrix of parameters (ui, vi).
Given two patches joined side by side, to have curvature continuity between
them all that is needed is to satisfy the curvature continuity condition by
the rows of CPs, row by row, as they were CPs of simple independent curves.
With that in mind, we could devise a patch to roundover a cube corner with
curvature continuity.
I will be back home soon and I hope to detail this stuff in two days. I
also have some comments about your curvature study and graphs but I have to
test my ideas first.
A point in a Bézier patch with a matrix of control points P corresponding
to a pair of parameters (u, v) can be easily computed by the following
simple code:
function BPatchPoint(P, u, v) =
transpose(BezierPoint(transpose(BezierPoint(P, u)), v));
where transpose() is the matrix transpose and BezierPoint() is the
function I defined before.
There is an error in that code: transposition is not needed. The correct
code is even simpler:
function BPatchPoint(P, u, v) =
BezierPoint(BezierPoint(P, u), v);
As the shape of the corner roundover patch is triangular I am considering
to model it with triangular Bezier patches of degree 6.
Ronaldo wrote
As the shape of the corner roundover patch is triangular I am considering
to model it with triangular Bezier patches of degree 6.
No doubt that this can be easily done. I remember an older thread, where you
showed it. But how would you automatically weave in such a patch into a
sweep or polyhedron that coats a larger structure like a roundedCube? Wasn't
that one the problems?
I didn't show it in my code, but it is straightforward if you have a quad
patch (which is squashed at one end).
--
Sent from: http://forum.openscad.org/
Given two patches joined side by side, to have curvature continuity
between them all that is needed is to satisfy the curvature continuity
condition by the rows of CPs, row by row, as they were CPs of simple
independent curves.
With that in mind, we could devise a patch to roundover a cube corner with
curvature continuity.
Following that line of thought, here is a rectangular patch to provide a
curvature continuity in rounding a corner of a cube.
[image: cornerRoundingI (1).PNG]
The following image shows the mesh of control points (CPs) compared with
with the patch itself.
[image: cornerRounding-CP.PNG]
As can be seen, all CPs rest on the faces of the cube in an array where one
full row of the mesh is collapsed to a point. Some intermediate rows and
columns of the mesh are movable by changing a shape parameter, others CPs
have immutable positions. The mesh of CPs of a corner were computed by the
following code:
function cornerPatchCP(P0,d,r0=0.5) =
let( P1 = P0+d*[1,0,0],
P2 = P0+d*[0,1,0],
P3 = P0+d*[0,0,1] )
[ [for(j=[0:4]) P1+P2], // i=0
let(p0=P1+(1-r0)P2,p1=(P1+P2)(1-r0),p2=P1*(1-r0)+P2) // i=1
[p0,p0+r0*(p1-p0),p1,p2+r0*(p1-p2),p2],
let(p0=P1,p1=P0,p2=P2) // i=2
[p0,p0+r0*(p1-p0),p1,p2+r0*(p1-p2),p2],
let(p0=P1+(1-r0)P3,p1=(1-r0)P3,p2=P2+(1-r0)P3) // i=3
[p0,p0+r0(p1-p0),p1,p2+r0(p1-p2),p2],
let(p0=P1+P3,p1=P3,p2=P2+P3) // i=4
[p0,p0+r0(p1-p0),p1,p2+r0*(p1-p2),p2] ];
Although it is an awkward code, it was easier to be written following the
continuity conditions. The arguments of this function are:
P0 - the coordinates of the corner
d - the extent of cube corner that will be rounded
r0 - a shape parameter equivalent to the shape parameter of the degree 4
curves with curvature continuity
From the CP matrix, we can sample points on the corner surface by
evaluating BPatchPoint(CPs,u,v) for various values of u and v in the
interval [0,1] and draw isoparametric lines or build a mesh for a
polyhedron call.
I don't like this solution. The collapse of one CP matrix row creates an
inconvenient asymmetry that can be observed by comparing the corner
rounding surface for r0 = 0.073 with the surface of a sphere:
[image: cornerRounding-sphere.PNG]
The image suggests that we have just one symmetry axis instead of 3 as
would be desirable.
Besides, a regular sample of parameters to compute points on the surface
are very irregular with a high concentration of points in the neighborhood
of the point the row was collapsed. That is a reason to pursuit a solution
based on Bezier triangular patches.
The corner surface described above has 25 CPs and a total degree of 8. My
first glance on that indicates that a degree 4 and degree 5 Bezier
triangular patch have not enough degree of freedom to accommodate the
curvature continuity conditions. Possibly a degree 6 triangular patch, with
28 CPs, will have room to satisfy all needed conditions. That will be my
next investigation.
Parkinbot rudolf@digitaldocument.de wrote:
But how would you automatically weave in such a patch into a
sweep or polyhedron that coats a larger structure like a roundedCube?
Wasn't
that one the problems?
I have solved this problem before and reported here. My lazyUnion function
is able to not only union closed manifold but stitch patches. It is
irrelevant for that module whether the patches are manifold or not. The
only condition is that each element of the incoming list is in a polyhedron
data format. And to generate a polyhedron data format for a matrix of
points (a regular mesh) or even a triangular patch is an easy task. To have
a manifold at the end is user responsability. To get it we need that the
points on the border of a patch match the points on border of a patch it
should join to and that the whole model is watertight.
Ronaldo,
well, the outcome of your solution doesn't look very different to mine. I
had to debug the corner() code a bit to use the correct rotation sequence.
The code for the Bezier triangle is rather simple and moved into the
function corner(). I think it shows a triple symmetrie. I transposed the
vertex matrix which is NxN to get a polygon sequence ordered by z. Therefore
the polygons can easily be extended to prepare a more complex sweep, like a
Bezier cube.
A BezierCube module implementing such a sweep on the basis of your Bezier
functions is shown by the following code. It renders in 0.5s on my system.
However, the union test with a cube lasts 15s.
http://forum.openscad.org/file/t887/BezierCube2.png
use <Naca_sweep.scad> // https://www.thingiverse.com/thing:1208001
BezierCube([200, 100, 50], 30, $fn=30, center =true);
module BezierCube(s = 100, r = 30, r0 = 3/4, r1=2/3, center = false)
{
n=$fn?$fn:360/$fa; // resolution
s=s[0]==undef?[s,s,s]:s; // allow for vector and number
r = abs(r);
if(r==0)
cube(s, center=center);
else
translate(center?[0, 0, 0]:s/2+[r, r, r])
{
q = [[0.001,r], [r,r], [r,0.001]];
b = BZeroCurvature(q[0],q[1],q[2],n=n,r0=r0,r1=r1);
gd = corner(b,r); // just a corner
sweep(composeCube(s/2, r, data=gd));
}
function composeCube(s, r, data) =
let(upper = [for(j=[0:len(data)-1])
let(S=[[s[0],s[1],0],[-s[0],s[1],0],[-s[0],-s[1],0],[s[0],-s[1], 0]])
[each for(i=[0:3]) Tz(s[2],T(S[i], Rz(90i, data[j])))]])
let(lower = [for(j=[len(data)-1:-1:0])
let(S=[[s[0],s[1],0],[-s[0],s[1],0],[-s[0],-s[1],0],[s[0],-s[1], 0]])
[each for(i=[0:3]) Tz(-s[2],T(S[i], Rz(90i, Sz(-1, data[j]))))]])
concat(upper, lower) ;
function corner(b,r) = //
let(step = 90/(len(b)-1))
let (m=[for(x=[0: len(b)-1])
let(l = norm(b[x])/r) // scale by bezier
let(a=atan(b[x][0]/b[x][1])) // get angle
Rz(a, Sx((l), Rx(90, vec3(b))))]) // rotatex, scalex and
rotatez
[for(i=[0:len(m)-1]) [for( j=[0:len(m[0])-1]) m[j][i]]]; // transpose
}
function BezierPoint(p, u) =
(len(p) == 2)?
u*p[1] + (1-u)p[0] :
uBezierPoint([for(i=[1:len(p)-1]) p[i] ], u)
+ (1-u)*BezierPoint([for(i=[0:len(p)-2]) p[i] ], u);
function BezierCurve(p, n=10) = [for(i=[0:n-1]) BezierPoint(p, i/(n-1)) ];
function BZeroCurvature(p0,p1,p2,n=20,r0=2/3,r1=1/2) =
assert(r0>0 && r0<1 && r1>0 && r1<1, "improper value of r0 or r1")
let( p = [ p0,
p0 + r0*(p1-p0)r1,
p0 + r0(p1-p0),
p2 + r0*(p1-p2),
p2 + r0*(p1-p2)*r1,
p2 ] )
BezierCurve(p,n);
--
Sent from: http://forum.openscad.org/
That seems to be nice (I have not tried yet). However, I have devised what
seems to be a solution simplest than the patchwork I suggested before.
Compute one Bezier corner, lazyUnion() it with its rotation and mirror to
cover all cube vertices roundover and hull() it. As lazyUnion() and hull()
are fast, that may be faster than any other solution.
Ronaldo wrote
Compute one Bezier corner, lazyUnion() it with its rotation and mirror to
cover all cube vertices roundover and hull() it. As lazyUnion() and hull()
are fast, that may be faster than any other solution.
Good point and strategy.
That was actually the solution I showed in
http://forum.openscad.org/Rounded-Polygon-tp21897p25905.html where I did a
sweep to create a corner and hulled over 8 instances of this corner.
And it wasn't as fast as the full sweep() (30s vs. 5s), but that was (as I
started to remember), because I had used a for-loop. And a for loop always
implies a union.
I just tried a run for which I put each corner as an explicite instance into
the hull body. It looks like the compile time is indeed even faster than a
full sweep (1s only). This seems to shout for a hull_for() operator that
behaves similar like the intersection_for.
Anyway, "fast" is of course always relative, because any further Boolean
operation will take its time with these vertex monsters.
--
Sent from: http://forum.openscad.org/
Parkinbot wrote
I just tried a run for which I put each corner as an explicite instance
into
the hull body. It looks like the compile time is indeed even faster than a
full sweep (1s only). This seems to shout for a hull_for() operator that
behaves similar like the intersection_for.
I have not yet had the time to go over what you guys have done, but I will
get to it in a few days.
What would hull_for() do? And is the real answer not another special
command but rather a way of passing the output of a for command as a set of
children to a calling module? Because it seems like there are multiple
occasions where you'd like to be able to generate a set of objects with
for() and then pass them to another module that operates on them
individually. Iff a non-unioning for() command existed then it could
replace intersection_for and would have applications in a variety of places,
I think. Is this a simpler concept than the idea of generically being able
to return multiple children from a module?
--
Sent from: http://forum.openscad.org/
There is already a PR for a non-union option for for() as part of a built
in sweep recently. It would make intersection_for() redundant as well I
think.
On Sat, 23 Mar 2019 at 23:01, adrianv avm4@cornell.edu wrote:
Parkinbot wrote
I just tried a run for which I put each corner as an explicite instance
into
the hull body. It looks like the compile time is indeed even faster than
a
full sweep (1s only). This seems to shout for a hull_for() operator that
behaves similar like the intersection_for.
I have not yet had the time to go over what you guys have done, but I will
get to it in a few days.
What would hull_for() do? And is the real answer not another special
command but rather a way of passing the output of a for command as a set of
children to a calling module? Because it seems like there are multiple
occasions where you'd like to be able to generate a set of objects with
for() and then pass them to another module that operates on them
individually. Iff a non-unioning for() command existed then it could
replace intersection_for and would have applications in a variety of
places,
I think. Is this a simpler concept than the idea of generically being able
to return multiple children from a module?
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
adrianv wrote
Iff a non-unioning for() command existed then it could
replace intersection_for and would have applications in a variety of
places,
I think.
This is correct. And it has been discussed several times before. I think a
practical solution would be to introduce an ungroup() operator that cancels
out a following group(){} clause in the csg file, which implicitly forces a
union.
hull() for(i=[10,20]) cube(i);
translates into the CSG tree:
hull() {
group() {
cube(size = [10, 10, 10], center = false);
cube(size = [20, 20, 20], center = false);
}
}
If you edit the CSG file to
hull() {
cube(size = [10, 10, 10], center = false);
cube(size = [20, 20, 20], center = false);
}
you obviously get the desired result. Therefore
hull() ungroup() for(i=[10,20]) cube(i);
would translate into
hull() {
ungroup{
group() {
cube(size = [10, 10, 10], center = false);
cube(size = [20, 20, 20], center = false);
}
}
}
and ungroup() would inhibit the immediately following group() clause. If no
immediate group() follows, ungroup() will be ignored or cancelled out. But,
I guess there might be also semantical implications.
@thehans, what do you think?
--
Sent from: http://forum.openscad.org/
It's not a problem of the language description, it's the internal
processing logic that currently forces each node to return a single
geometry object.
Changing that should open up further options. So basically right
now, every node has to do the implicit union regardless of the
actual need for that. Pushing the responsibility of the to the
level above should help improving a couple of cases, like hull()
with children generated with for(), translate() just translating
the list of children separately or doing an intersection() on
multiple volumes imported from a single 3MF file.
ciao,
Torsten.
Somehow this PR gets around the problem with hull() for( ..., union=false).
See https://github.com/openscad/openscad/pull/2796#issuecomment-466836941
On Sun, 24 Mar 2019 at 00:50, Torsten Paul Torsten.Paul@gmx.de wrote:
It's not a problem of the language description, it's the internal
processing logic that currently forces each node to return a single
geometry object.
Changing that should open up further options. So basically right
now, every node has to do the implicit union regardless of the
actual need for that. Pushing the responsibility of the to the
level above should help improving a couple of cases, like hull()
with children generated with for(), translate() just translating
the list of children separately or doing an intersection() on
multiple volumes imported from a single 3MF file.
ciao,
Torsten.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Parkinbot wrote
adrianv wrote
hull() for(i=[10,20]) cube(i);
translates into the CSG tree:
hull() {
group() {
cube(size = [10, 10, 10], center = false);
cube(size = [20, 20, 20], center = false);
}
}
If you edit the CSG file to
hull() {
cube(size = [10, 10, 10], center = false);
cube(size = [20, 20, 20], center = false);
}
you obviously get the desired result. Therefore
What is the difference between these two things? Taking the hull() of an
individual cube won't do anything.
I thought the interesting case was wanting to do sequential hulls, like the
hull of every adjacent pair of objects in a list, where the list was
produced by for().
--
Sent from: http://forum.openscad.org/
adrianv wrote
What is the difference between these two things? Taking the hull() of an
individual cube won't do anything.
The code hulls two cubes. They could be translated to create something
meaningful, but this doesn't matter for the argument. The cubes will get
unioned in the first case and then hulled, and in the second case they
only get hulled. (Better think of 8 (translated) corners to be hulled to
gain a BezierCube).
While the result is the same, it is a significant runtime difference,
whether hull() will operate over a set of objects or a unioned object.
hull() is much faster then union().
--
Sent from: http://forum.openscad.org/
Parkinbot rudolf@digitaldocument.de wrote:
While the result is the same, it is a significant runtime difference,
whether hull() will operate over a set of objects or a unioned object.
hull() is much faster then union().
Hum... There is something strange here. The following code generates a
regular cube even with F6:
hull()
for(i=[0,1]){
polyhedron([[0,0,i],[1,0,i],[1,1,i],[0,1,i]],[[0,1,2,3,4]]);
cube(0.5);
}
although the two polyhedron are defective (non-manifold). If we drop the
hull() we get a non-manifold warning with F6. So, I don't think the hull()
for(){ } construct really does any union before the hull().
Parkinbot rudolf@digitaldocument.de wrote:
I just tried a run for which I put each corner as an explicite instance
into
the hull body. It looks like the compile time is indeed even faster than a
full sweep (1s only). This seems to shout for a hull_for() operator that
behaves similar like the intersection_for.
We don't need a hull_for() to have a faster run. hull() disregards any edge
or facets of the objects we collect for it. It operates just on the
vertices. As we don't have a primitive point, we need to resort to
polyhedron to hull() a list of points like I do here:
$fn= 10; // number of points in all roundover
r0 = 0.15; // shape parameter
r = 2; // rounding "radius"
s = [10,15,20]; // cube edge length
roundedCube(s,r,r0);
module roundedCube(s=10, r=1, r0=0.073) {
n=$fn?$fn:360/$fa; // resolution
s=s[0]==undef?[s,s,s]:s; // allow for vector and number
r = abs(r);
if(r==0)
cube(s, center=true);
else {
cp = cornerPatchCP(-s/2,r,r0);
cp0 = PatchSample(cp,n); // corner at -s/2
Mx =[[-1,0,0],[0, 1,0],[0,0, 1]];
My =[[ 1,0,0],[0,-1,0],[0,0, 1]];
Mz =[[ 1,0,0],[0, 1,0],[0,0,-1]];
cp2 = concat( cp0, [for(pi=cp0) Mxpi] );
cp4 = concat( cp2, [for(pi=cp2) Mypi] );
cp8 = concat( cp4, [for(pi=cp4) Mz*pi] );
hull() polyhedron(cp8, [[for(i=[0:len(cp8)-1]) i]]);
}
}
function BezierPoint(p, u) =
(len(p) == 2)?
u*p[1] + (1-u)p[0] :
uBezierPoint([for(i=[1:len(p)-1]) p[i] ], u)
+ (1-u)*BezierPoint([for(i=[0:len(p)-2]) p[i] ], u);
function BPatchPoint(p,u,v) =
BezierPoint(BezierPoint(p, u), v);
function PatchSample(cp,n) =
[for(i=[0:n-1], j=[0:i])
BPatchPoint(cp,i/(n-1),i==0? 0 : j/i) ];
function cornerPatchCP(P0,d,r0=0.5) =
let( dx = d*[1,0,0],
dy = d*[0,1,0],
dz = d*[0,0,1] )
[ [for(j=[0:4]) P0+dx+dy],
cCP([P0+dx+(1-r0)dy, P0+(dx+dy)(1-r0), P0+dx*(1-r0)+dy],r0),
cCP([P0+dx, P0, P0+dy], r0),
cCP([P0+dx+(1-r0)*dz, P0+(1-r0)*dz, P0+dy+(1-r0)*dz],r0),
cCP([P0+dx+dz, P0+dz, P0+dy+dz],r0) ];
function cCP(p,r0=0.5) =
[ p[0],
p[0]+r0*(p[1]-p[0]),
p[1],
p[2]+r0*(p[1]-p[2]),
p[2] ];
This is the fastest strategy I can imagine. The points sampled from the
corner patches are collected in a simple list which will be the vertices of
the fake polyhedron to be hulled. That fake polyhedron has just one face
collecting all the vertex indices. This polyhedron is defective and it is
not a manifold at all but that polyhedron is not really built, just hulled.
The second aspect of the code is that it samples the corner patch in an non
uniform distribution in the parameter space. The sample rate is poor near
the collapsed row of the rectangular patch and increases linearly up to the
opposed patch edge. As the patch is really triangular, the sampling is
geometrically more uniform.
[image: cornerSampling.PNG]
I haven't tried your code yet (although I have borrowed some lines of code
from it :) ) but I believe this last code is faster. It renders the cube in
1s with $fn=50 and in 12s with $fn=100.
Anyway, "fast" is of course always relative, because any further Boolean
operation will take its time with these vertex monsters.
It is not a monster. The number of vertices of the rounded cube is about
the same of one sphere with the same $fn. So, I would not expect anything
worst than the boolean operation with a sphere. In fact, it required just
9s to difference a cylinder crossing a rounded edge with $fn=32.
Ronaldo wrote
although the two polyhedron are defective (non-manifold). If we drop the
hull() we get a non-manifold warning with F6. So, I don't think the hull()
for(){ } construct really does any union before the hull().
I think warnings are only tested and emitted on the final object.
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
We don't need a hull_for() to have a faster run. hull() disregards any
edge
or facets of the objects we collect for it. It operates just on the
vertices. As we don't have a primitive point, we need to resort to
polyhedron to hull() a list of points like I do here:
If you create a convex body it is indeed avoidable - even I would say, it is
a work-around if you have to recur to a lazy-union to avoid the "for union
problem".
The following code creates a random convex body and looks a bit dirty. I am
somehow very reluctant to build a serious library module on this technique:
points = [for(i=[0:30]) rands(-10, 10, 3)];
faces = [[for(i=[0:len(points)-1]) i]];
hull() polyhedron(points, faces); // tricky convex body
However, there are also non-convex shapes. For them you need to do a proper
sweep, so this strategy is quite limited. In general I prefer to create such
a patch in a way so that it can also be swept.
--
Sent from: http://forum.openscad.org/
Wouldn't Oskar Linde's hull() function solve this problem? I don't know its
runtime performance, but it takes a point list and computes faces of the
convex hull without the uncomfortable scheme of creating a bogus polyhedron.
https://github.com/openscad/scad-utils/blob/master/hull.scad
--
Sent from: http://forum.openscad.org/
adrianv wrote
Wouldn't Oskar Linde's hull() function solve this problem?
It wouldn't solve the problem, because in general we have the case in which
a for-loop will create objects (even fancy polyhedra are objects) that get
hulled, and Oskar Linde's hull() function depends like a lazy union on a
point representation, which OpenSCAD is successfully hiding from user space.
But for fun, I did a quick runtime test and compared Oskar Linde's hull()
function with the fancy hull()polyhedron() approach. It turned out that
Oskar's function is indeed pretty much faster, when it comes to a higher
number of points. Good to know!
points = [for(i=[0:3000]) rands(-10, 10, 3)];
// polyhedron (points, hull(points)); // Oskar's approach: 6s
faces = [[for(i=[0:len(points)-1]) i]];
hull() polyhedron(points, faces); // tricky convex body 74s:
--
Sent from: http://forum.openscad.org/
I have never tried Oskar Linde's hull(). I will give it a go.
However, the bogus polyhedron technique seems to be faster and simpler. For
the advocates of more orthodox solutions, it is possible to apply hull to a
set of spherical regular polyhedron built by lazyUnion() the 8 corner
patches glued together. As hull() will consider just the vertices and
disregard the edges and faces, that seems to be a waste of running time and
coding effort.
I have not looked at the sweep technique in detail yet. My concern would
certainly be on the curvature continuity.
It is a great news (and surprise) that Oskar Linde's hull is so fast. I
will study it.
A domingo, 24/03/2019, 11:44, adrianv avm4@cornell.edu escreveu:
Wouldn't Oskar Linde's hull() function solve this problem? I don't know
its
runtime performance, but it takes a point list and computes faces of the
convex hull without the uncomfortable scheme of creating a bogus
polyhedron.
https://github.com/openscad/scad-utils/blob/master/hull.scad
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Parkinbot rudolf@digitaldocument.de wrote:
adrianv wrote
Wouldn't Oskar Linde's hull() function solve this problem?
(...)
But for fun, I did a quick runtime test and compared Oskar Linde's hull()
function with the fancy hull()polyhedron() approach. It turned out that
Oskar's function is indeed pretty much faster, when it comes to a higher
number of points. Good to know!
points = [for(i=[0:3000]) rands(-10, 10, 3)];
// polyhedron (points, hull(points)); // Oskar's approach: 6s
faces = [[for(i=[0:len(points)-1]) i]];
hull() polyhedron(points, faces); // tricky convex body 74s:
I have checked this comparison and concluded that the delay of the second
alternative is due to the big face in the polyhedron call. I guess that the
system does a triangulation of that bogus face. Instead of one big face I
have defined a big list of triangular faces covering all vertices and the
results were very different:
points = [for(i=[0:120000-1]) rands(-10, 10, 3)];
faces = [for(i=[0:3:len(points)-1]) [i,i+1,i+2]];
hull() // hull() spent 0s
polyhedron(points, faces); // tricky polyhedron spent 3s
On the other hand, Oskar Linde's hull() crashed with 6000 or more points.
If that function were used in a roundedCube code, it would crash with
$fn>=38.
Good solution. Your code took 16s on my machine.
--
Sent from: http://forum.openscad.org/
Ronaldo Persiano rcmpersiano@gmail.com wrote:
This is the fastest strategy I can imagine. The points sampled from the
corner patches are collected in a simple list which will be the vertices of
the fake polyhedron to be hulled. That fake polyhedron has just one face
collecting all the vertex indices. This polyhedron is defective and it is
not a manifold at all but that polyhedron is not really built, just hulled.
There is no need to resort to bogus polyhedra to follow the strategy based
on hull(). Instead, we just build a valid polyhedron for each corner and
hull() them all. Surprisingly, the preview and render times are nearly the
same as the bogus polyhedron previous code although the code is a bit more
complex.
$fn = 40; // number of points in all roundover
// 6560 vertices for $fn = 40
r0 = 0.073; // shape parameter (this value approximates a circular
arc)
r = 1.5; // rounding "radius"
s = [15,15,20]; // cube edge length
difference(){
roundedCube(s,r,r0);
rotate(90,[1,1,0])cylinder(r=2,h=40); // to check F6 validity
}
// round the edges and vertices of a block with dimensions s centered at
the origin
// s - block dimensions; may be a number or a 3d vector
// r - the extension of the edge ends that are rounded;
// equivalent to the radius of a circular rouding
// r0 - rounding shape parameter ( 0<=r0<1 )
module roundedCube(s=10, r=1, r0=0.073) {
n = $fn ? $fn: 360/$fa; // resolution
assert(0s==0 || 0s==[0,0,0], "improper size value s");
s = s[0]==undef ? [s,s,s]: s; // allow for vector and number
assert(s[0]>0 && s[1]>0 && s[2]>0, "improper cube dimensions s");
r = abs(r);
if(r==0)
cube(s, center=true);
else {
assert(2*r<=min(s), "radius r too large for the cube dimensions");
assert(r0>=0 && r0<1, "shape parameter r0 must satisfy 0<=ro<1" );
cp = cubeCornerPatchCP(-s/2,r,r0); // corner at -s/2
cp0 = PatchSample(cp,n);
pd0 = cornerPoly(cp0); // base corner patch pdat
Mx = [[-1,0,0],[0, 1,0],[0,0, 1]]; // mirror matrices
My = [[ 1,0,0],[0,-1,0],[0,0, 1]];
Mz = [[ 1,0,0],[0, 1,0],[0,0,-1]];
pd1 = [pd0];
pd2 = concat(pd1, transfPdata(Mx,pd1)); // patch mirrorings
pd4 = concat(pd2, transfPdata(My,pd2));
pd8 = concat(pd4, transfPdata(Mz,pd4));
hull()
lazyUnion(pd8); // union of the 8 corners
}
}
// unify the polyhedron data in list mPoly and call polyhedron
// assume the final polyhedron has no self-intersections
module lazyUnion(mPoly) {
// acc = accumSum(l) => acc[0]==0, acc[i]==acc[i-1]+l[i-1]
function accSum(l, res=[0]) =
len(res) == len(l) ?
res :
accSum(l, concat(res, [ res[len(res)-1]+l[len(res)-1] ] ));
verts = [for(p=mPoly) each p[0] ];
nvert = [for(p=mPoly) len(p[0]) ];
accv = accSum(nvert);
faces = [for(i=[0:len(mPoly)-1], fac=mPoly[i][1] )
[for(v=fac) v+accv[i] ] ];
polyhedron(verts, faces);
}
// transform all the vertices of each polyhedron data in list pdat by
matrix M
function transfPdata(M, pdat) = [for(pdi=pdat) [ pdi[0]*M, pdi[1] ] ];
// the polyhedron of a corner in pdat format
function cornerPoly(tpatch) =
let( n = len(tpatch) ) echo(len(tpatch),[for(i=[0:n-1]) (n-1)n/2+i ])
[ [ for(i=[0:n-1], j=[0:i]) tpatch[i][j] ], // corner vertices
// corner faces
[ for(i=[1:n-1], j=[0:i-1]) let( k = i(i+1)/2 )
each [ [ k+j, k+j-i, k+j+1 ],
if(j<i-1)[ k+j-i, k+j-i+1, k+j+1] ],
// triangular patch back faces
[for(i=[0:n-1]) i*(i+1)/2 ],
[for(i=[n-1:-1:0]) i*(i+1)/2+i ],
[for(i=[0:n-1]) (n-1)*n/2+i ],
[ 0, (n-1)*n/2, (n-1)*n/2+n-1 ]
]
];
// a point in a Bezier curve
// p - patch control points
// u - parameter value
function BezierPoint(p, u) =
(len(p) == 2)?
u*p[1] + (1-u)p[0] :
uBezierPoint([for(i=[1:len(p)-1]) p[i] ], u)
+ (1-u)*BezierPoint([for(i=[0:len(p)-2]) p[i] ], u);
// a point in a Bezier surface patch
// p - patch control points (matrix)
// u,v - parameter values
function BPatchPoint(p,u,v) =
BezierPoint(BezierPoint(p, u), v);
// sample a Bezier patch with a triangular distribuition
// cp - control points of the patch
// n - resolution
// a total of n*(n+1)/2 points are sampled
function PatchSample(cp,n) =
[for(i=[0:n-1])
[for(j=[0:i]) BPatchPoint(cp,i/(n-1),i==0? 0 : j/i) ] ];
// control points of a Bezier patch of a cube corner with curvature
continuity
// P0 - cube corner vertex
// r - the ammount of the 3 edges at the corner to be rounded
// r0 - shape parameter
function cubeCornerPatchCP(P0,r,r0=0.5) =
[for(p=curveCP([P0+[r,r,0],P0+[r,0,0],P0+[r,0,r]],r0))
curveCP([p, [p[1],p[1],p[2]], [p[1],p[0],p[2]] ], r0) ];
// control points of a degree 4 Bezier curve
// starting and ending with zero curvature
// p - corner to be rounded (list of 3 points)
// r0 - shape parameter
function curveCP(p,r0=0.5) =
[ p[0], p[0]+r0*(p[1]-p[0]), p[1], p[2]+r0*(p[1]-p[2]), p[2] ];
That code is rather fast. It takes 14s to render a rounded block with 6560
vertices.
Parkinbot, wrote:
However, there are also non-convex shapes. For them you need to do a proper
sweep, so this strategy is quite limited. In general I prefer to create
such
a patch in a way so that it can also be swept.
Yes, this technique is valid just for convex bodies. For non-convex
objects, we will have eventually to recode the computation of the patches
and it will be hard to find general solutions for corners with more than 4
incoming edges. On the other hand, I don't see how to manage the rounding
for general cases with sweeps.
I took a look at the code. Is it the case, Ronaldo, that your method is
creating a true bezier triangle, with the desired 3-axis symmetry? I read
that the bezier triangular patch can be obtained by sampling a patch in a
triangle or by collapsing two points together, but details were sparse on
what happens to the control points under these transformations.
Is the only difference between the last two versions the method of getting
the hull()? (Do they produce the same patch?)
And the sweep method Parkinbot posted is my original notion of sweeping the
bezier around the bezier, right? Do you think the curvature condition will
not hold for this approach? It seems like you could get into trouble if the
sweeping trajectory doesn't meet some kind of conditions (maybe a maximum
curvature condition?)
It seems hard to imagine generalizing continuous curvature corners beyond
solids created by linear extrusion, and for that case, it seems like the
sweep approach will be easier, no? I could imagine, instead of making
corners and hulling, making a sweep around an entire shape, though I suppose
it gets to be a lot of vertices. But this would handle concave corners, I
think? (Corners that are concave in one direction but convex in the
other.)
With the bezier patch we have 4 edges so we could round over an octahedron,
I suppose, but it not a particularly powerful generalization.
I also noticed a couple things about using bogus faces to polyhedron().
When I use the second method of creating triangles, I somtimes get "PolySet
has degenerate polygons". What does that mean? Some triangle is colinear,
or a polygon includes some colinear points? What does polyhedron do if you
give it a non-coplanar polygon?
When I tried swapping in this method into the bezier code the run time
increased from 2s to 3s for me (with
$fn=100, 40400 points in a corner patch.) I tried increasing to $fn=200,
and now 160800 points in a corner patch, and then the triangles are much
faster.
--
Sent from: http://forum.openscad.org/
adrianv avm4@cornell.edu wrote:
I took a look at the code. Is it the case, Ronaldo, that your method is
creating a true bezier triangle, with the desired 3-axis symmetry?
In that last code, I still use rectangular Bezier patches with a collapsing
row of control points so I would not expect a 3-axis symmetry. The corner
patch is the same it was used in the previous code (I have just simplified
the function that computes the patch CP).
I read that the bezier triangular patch can be obtained by sampling a
patch in a
triangle or by collapsing two points together, but details were sparse on
what happens to the control points under these transformations.
I am afraid that information is not correct.
Is the only difference between the last two versions the method of getting
the hull()? (Do they produce the same patch?)
Yes, see above.
And the sweep method Parkinbot posted is my original notion of sweeping the
bezier around the bezier, right? Do you think the curvature condition will
not hold for this approach? It seems like you could get into trouble if
the
sweeping trajectory doesn't meet some kind of conditions (maybe a maximum
curvature condition?)
Your sweep conception seems to lead to a curvature continuity in the two
axis but I have not analysed it in detail. I can not say that the Parkinbot
sweep code really follows you original conception because I have not
studied his code. Anyway, the results of a sweeping method would be
different in nature from what I have done. I would expect that the planar
face of the rounded block by the sweep method will not be a rectangle (as
in my model) but a rounded rectangle.
It seems hard to imagine generalizing continuous curvature corners beyond
solids created by linear extrusion, and for that case, it seems like the
sweep approach will be easier, no?
I would say that this last code could be easily extended to round any
convex polyhedron where just three edges meet at each vertices (like for
instance a dodecahedron). It is possible to remodel the corner patch in
order to round corners where 4 edges meet but I don't know how extend that
to more general cases where more than 4 edges meet at some corner. I don't
know how to apply the sweep method to round a dodecahedron.
I could imagine, instead of making
corners and hulling, making a sweep around an entire shape, though I
suppose
it gets to be a lot of vertices. But this would handle concave corners, I
think? (Corners that are concave in one direction but convex in the
other.)
With the bezier patch we have 4 edges so we could round over an octahedron,
I suppose, but it not a particularly powerful generalization.
Yes, see above.
I also noticed a couple things about using bogus faces to polyhedron().
When I use the second method of creating triangles, I somtimes get "PolySet
has degenerate polygons". What does that mean? Some triangle is colinear,
or a polygon includes some colinear points? What does polyhedron do if you
give it a non-coplanar polygon?
Did you get that warning with my last code? That warning usually means that
there is degenerated faces (colinear points) or a badly structured face
list or the point list has some point not referenced by any face. If some
face is non-planar, the system triangulate it in some arbitrary way. In my
last code, the corner polyhedron has 3 non-triangulated planar faces.
When I tried swapping in this method into the bezier code the run time
increased from 2s to 3s for me (with
$fn=100, 40400 points in a corner patch.) I tried increasing to $fn=200,
and now 160800 points in a corner patch, and then the triangles are much
faster.
I haven't understood what you have done here.
Ronaldo wrote
adrianv <
avm4@
> wrote:
I took a look at the code. Is it the case, Ronaldo, that your method is
creating a true bezier triangle, with the desired 3-axis symmetry?
In that last code, I still use rectangular Bezier patches with a
collapsing
row of control points so I would not expect a 3-axis symmetry. The corner
patch is the same it was used in the previous code (I have just simplified
the function that computes the patch CP).
I read that the bezier triangular patch can be obtained by sampling a
patch in a
triangle or by collapsing two points together, but details were sparse
on
what happens to the control points under these transformations.
I am afraid that information is not correct.
Are you sure? Are bezier patches or curves a different representation of
the degree n (or degree n,m) polynomial, or are there polynomials not
represented by the bezier framework? It appears just based on counting
parameters that it should be possible to get all polynomials with a bezier
representation, which would mean the claim I quoted above is true...but
perhaps not very interesting without a control point mapping.
And the sweep method Parkinbot posted is my original notion of sweeping
the
bezier around the bezier, right? Do you think the curvature condition
will
not hold for this approach? It seems like you could get into trouble if
the
sweeping trajectory doesn't meet some kind of conditions (maybe a maximum
curvature condition?)
Your sweep conception seems to lead to a curvature continuity in the two
axis but I have not analysed it in detail. I can not say that the
Parkinbot
sweep code really follows you original conception because I have not
studied his code. Anyway, the results of a sweeping method would be
different in nature from what I have done. I would expect that the planar
face of the rounded block by the sweep method will not be a rectangle (as
in my model) but a rounded rectangle.
I haven't studied his code yet either. I have to first understand the sweep
function, so it seemed like a bit more work. It seems possible that the
result would be different in nature, but I don't agree that the planar face
would be rounded. Suppose I sweep a square along a line. The result---a
standard linear extrude---is a cuboid shape with rectangular faces. If I
sweep a rounded-corner square along a line I will get a cuboid shape with
rounded edges, four rectangular faces, and then two ends whose faces are the
rounded corner square. If I sweep a rounded corner square along a rounded
corner square then (assuming the roundover doesn't dominate the square), the
square's sides will have flat sections and the sweep will convert these into
rectangular faces. I think that the resulting shape should actually be
identical to the shape produced from the bezier method except (possibly) at
the corners.
It seems hard to imagine generalizing continuous curvature corners beyond
solids created by linear extrusion, and for that case, it seems like the
sweep approach will be easier, no?
I would say that this last code could be easily extended to round any
convex polyhedron where just three edges meet at each vertices (like for
instance a dodecahedron). It is possible to remodel the corner patch in
order to round corners where 4 edges meet but I don't know how extend that
to more general cases where more than 4 edges meet at some corner. I don't
know how to apply the sweep method to round a dodecahedron.
Yes, definitely it should be straight forward to adapt your approach to a
meeting of 3 edges, and the ordinary square bezier can handle corners where
4 edges meet. I wonder if the triangular bezier can be generalized to an
n-gon bezier defined somehow on a regular n-gon?
What the sweep approach can (at least in principle) do is apply a roundover
to an (arbitrary?) shape that is generated by linear extrusion. Basically
instead of doing linear extrusion you sweep along the shape's boundary a
rounded rectangle of the appropriate dimensions to create the desired shape.
I also noticed a couple things about using bogus faces to polyhedron().
When I use the second method of creating triangles, I somtimes get
"PolySet
has degenerate polygons". What does that mean? Some triangle is
colinear,
or a polygon includes some colinear points? What does polyhedron do if
you
give it a non-coplanar polygon?
Did you get that warning with my last code? That warning usually means
that
there is degenerated faces (colinear points) or a badly structured face
list or the point list has some point not referenced by any face. If some
face is non-planar, the system triangulate it in some arbitrary way. In my
last code, the corner polyhedron has 3 non-triangulated planar faces.
No, not with the latest version of your code. With tests of the application
of hull() to bogus polyhedra.
When I tried swapping in this method into the bezier code the run time
increased from 2s to 3s for me (with
$fn=100, 40400 points in a corner patch.) I tried increasing to
$fn=200,
and now 160800 points in a corner patch, and then the triangles are much
faster.
I haven't understood what you have done here.
Your earlier version of the bezier code used the bogus polyhedron method
with one large face. I substituted small bogus triangles and it got
slightly slower when there were 40000 points in the patch. But when there
were 160000 bogus triangles were much faster than one huge bogus face.
--
Sent from: http://forum.openscad.org/
adrianv avm4@cornell.edu wrote:
I read that the bezier triangular patch can be obtained by sampling a
patch in a
triangle or by collapsing two points together, but details were sparse
on
what happens to the control points under these transformations.
I am afraid that information is not correct.
Are you sure? Are bezier patches or curves a different representation of
the degree n (or degree n,m) polynomial, or are there polynomials not
represented by the bezier framework? It appears just based on counting
parameters that it should be possible to get all polynomials with a bezier
representation, which would mean the claim I quoted above is true...but
perhaps not very interesting without a control point mapping.
Yes, I am quite sure. A rectangular Bezier patch with degrees n and m is in
a (large but incomplete) subset of all two variables polynomials of degree
(n+m). A triangular Bezier patch of degree n is really a two variable
polynomial of degree n and it has (n+1)(n+2)/2 coefficients (CPs). In our
case, degree 4, triangular patches have 15 CPs while a rectangular patch of
degree 4x4 will have 25 CPs. I guess that positioning appropriately those
25 CPs we would get any degree 4 polynomial in two variable. But the
interrelations of those CPs will be something much more complex than you
have referred. At the end, just 15 degree of freedom will remain. There is
no sampling that would exempt the fulfillment of those 10 relations.
Yes, definitely it should be straight forward to adapt your approach to a
meeting of 3 edges, and the ordinary square bezier can handle corners where
4 edges meet. I wonder if the triangular bezier can be generalized to an
n-gon bezier defined somehow on a regular n-gon?
As far as I know, there is no n-gon Bézier patch for n>4.
Did you get that warning with my last code? That warning usually means
that
there is degenerated faces (colinear points) or a badly structured face
list or the point list has some point not referenced by any face. If some
face is non-planar, the system triangulate it in some arbitrary way. In
my
last code, the corner polyhedron has 3 non-triangulated planar faces.
No, not with the latest version of your code. With tests of the
application
of hull() to bogus polyhedra.
The way I have defined that large bogus face, by taking each 3 points in
sequence as vertices of a triangle, some vertices may remain untouched by
any face when the number of vertices is not multiple of 3 and that will
generate a warning like you get.
Your earlier version of the bezier code used the bogus polyhedron method
with one large face. I substituted small bogus triangles and it got
slightly slower when there were 40000 points in the patch. But when there
were 160000 bogus triangles were much faster than one huge bogus face.
Were you comparing the running time of the bogus polyhedron process with
the Oskar Linde's hull() of points?
I have made an attempt at the triangular bezier patch. I do not know if I
have picked parameters that achieve continuous curvature, but the patch is
looking reasonable. Here is one corner, with control points displayed.
There are 3 control points that appear "free" in some sense---that is, not
falling on the edge with their value already determined to achieve the
continuous curvature edge curve.
http://forum.openscad.org/file/t2477/tribez2.png
Here's the code. It seems like computing the bezier points is kind of slow,
but I don't know if there's a more clever way to do it.
h=0.6;
corner = [0,0,0];
dx = [-1,0,0];
dy = [0,-1,0];
dz = [0,0,-1];
P1 = corner-dx-dy;
P2 = corner-dx-dz;
P3 = corner-dy-dz;
P12 = corner-dx;
P13 = corner-dy;
P23 = corner-dz;
P2face = corner-hdx-hdz;
P1face = corner - hdx-hdy;
P3face = corner-hdy-hdz;
P = [
[P1, P12+h*(P1-P12), P12, P12+h*(P2-P12), P2],
[P13 + h*(P1-P13), P1face, P2face, P23+h*(P2-P23)],
[P13, P3face, P23],
[P13 + h*(P3-P13), P23 + h*(P3-P23)],
[P3]
];
sdx=1/16;
pts = ( [for(u=[0:sdx:1])
each [for(v=[0:sdx:1-u]) tribez(P,u,v)]]);
echo(len(pts), "points");
fastpointhull(pts); //polyhedron(pts, faces=hull(pts));
function tribez(P,u,v) = len(P) == 1 ? P[0][0] :
let(
n = len(P)-1,
Pu = [for(i=[0:n-1]) select(P[i],[1:len(P[i])-1])], //
select(P[i],1,-1)],
Pv = [for(i=[0:n-1]) select(P[i],[0:len(P[i])-2])], //
select(P[i],0,len(P[i])-2)],
Pw = select(P,[1:n])
)
tribez(uPu+vPv+(1-u-v)*Pw,u,v);
%cube(size=[1,1,1]);
module fastpointhull(points){
//points = simplify3d_path(points); // colinear points are not on the
hull and generate a warning message
extra = len(points)%3;
list = concat(
[[for(i=[0:extra+2])i]],
[for(i=[extra+3:3:len(points)-3])[i,i+1,i+2]]);
hull() polyhedron(points, faces=list);
}
function select(vector,indices) = [ for (index = indices) vector[index] ];
--
Sent from: http://forum.openscad.org/
adrianv avm4@cornell.edu wrote:
I have made an attempt at the triangular bezier patch. I do not know if I
have picked parameters that achieve continuous curvature, but the patch is
looking reasonable. Here is one corner, with control points displayed.
There are 3 control points that appear "free" in some sense---that is, not
falling on the edge with their value already determined to achieve the
continuous curvature edge curve.
Nice work! I was following the same route but with degree 6 triangular
patch which is, I suppose, the minimum degree to have curvature continuity.
But I could not find a good way to compute curvature for triangular patches
to check my models. Anyway, I am afraid that the 3 CPs you consider free to
shape the surface should have precise positions to get curvature
continuity. I have drawn the triangular grid of your CPs and got the
following image with h=0.3.
As the central triangle is not coplanar with any of the corner planes I
think we would not have zero cross border second derivative at the patch
borders. Perhaps we get zero cross border curvature with h=0 but that is
too much restrictive.
My next step will be to compute the cross border curvature to check the
surface models.
Looks like a patch, but it doesn't make a smooth connection on rotation ...
translate([-1, -1])fastpointhull(pts);
rotate([0,0,90])
translate([-1, -1])fastpointhull(pts);
http://forum.openscad.org/file/t887/patch.png
--
Sent from: http://forum.openscad.org/
It's not clear to me what the constraints are for controlling the derivative
matching for the triangular patch, but yes, it appears I have with order 4
not even matched the first derivative.
So this raises the questions: Is the code correct? Is it possible to match
derivatives as desired (with a patch of sufficient order)?
Should the edges of the patch match the 4th order 1d bezier curve with the
same control points?
--
Sent from: http://forum.openscad.org/
Em sex, 29 de mar de 2019 às 17:44, adrianv avm4@cornell.edu escreveu:
It's not clear to me what the constraints are for controlling the
derivative
matching for the triangular patch, but yes, it appears I have with order 4
not even matched the first derivative.
The C1 continuity (first derivative) between two adjacent triangular
patches involves the co-planarity of some triangle pairs adjacent to the
common border (see fig. 1). It is in some sense an extension of the
condition that holds for curves. The C2 continuity (second order) requires
the coindicence of some points extending from some triangles in a second
row away from the common border (see fig. 2). The figures were taken from
Farin's paper found at
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.413.7346&rep=rep1&type=pdf.
Although this is the main reference on the subject, its nomenclature is
hard to follow. I have tried without success, to express by triangular
patches the cylindrical surface of an edge rounded with the degree 4 curve.
That would help me to try meeting the necessary C2 continuity.
So this raises the questions: Is the code correct? Is it possible to match
derivatives as desired (with a patch of sufficient order)?
This depends on what you mean by correct. I haven't seen nothing wrong with
your functions.
I still believe we could achieve the necessary C2 conditions with
triangular patches with even degree. If the 4x4 rectangular patch I built
really matches the conditions (and I am still unsure about it), then at
least a degree 8=4+4 triangular patch will do it.
Should the edges of the patch match the 4th order 1d bezier curve with the
same control points?
Yes, possibly with a higher degree representation.
// given p, the Bezier CP of an arc of degree len(p),
// find the control points of the same arc expressed as a curve of degree
len(p)+n
function BzDegreeElevation(p, n) =
n==0 ? p :
let(q = [ p[0],
for(i=[1:len(p)-1], u = i/len(p))
u*p[i-1] + (1-u)*p[i] ,
p[len(p)-1] ] )
BzDegreeElevation (q, n-1);
Ronaldo wrote
Em sex, 29 de mar de 2019 às 17:44, adrianv <
avm4@
> escreveu:
It's not clear to me what the constraints are for controlling the
derivative
matching for the triangular patch, but yes, it appears I have with order
4
not even matched the first derivative.
The C1 continuity (first derivative) between two adjacent triangular
patches involves the co-planarity of some triangle pairs adjacent to the
common border (see fig. 1). It is in some sense an extension of the
condition that holds for curves. The C2 continuity (second order) requires
the coindicence of some points extending from some triangles in a second
row away from the common border (see fig. 2). The figures were taken from
Farin's paper found at
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.413.7346&rep=rep1&type=pdf.
Although this is the main reference on the subject, its nomenclature is
hard to follow. I have tried without success, to express by triangular
patches the cylindrical surface of an edge rounded with the degree 4
curve.
That would help me to try meeting the necessary C2 continuity.
My patch is failing C1 even though it appears to me that I meet the
coplanarity condition for the triangles adjacent to the edge. What is
wrong?
I have not been successful thus far at deciphering the C2 condition from the
paper.
I did confirm that my patch edges matches the shape of the 1d curve I
previously produced with the order 4 bezier.
--
Sent from: http://forum.openscad.org/
I remain baffled by the lack of C1 agreement for my triangular patch. I ran
across another reference which I think is a little bit easier to follow:
https://pdfs.semanticscholar.org/3854/c6e3ea6367eb7ad8b340f944ee4f1502ac10.pdf
But at least for C1, I don't see anything new---my control points are
coplanar and yet my patch lacks C1 continuity at the edge. I tried
implementing a non-recursive calculation of the patch and it matched my
previous calculation.
--
Sent from: http://forum.openscad.org/
adrianv avm4@cornell.edu wrote:
I remain baffled by the lack of C1 agreement for my triangular patch. I
ran
across another reference which I think is a little bit easier to follow:
Thank you for that reference: a very useful procedure.
I am also surprised that the coplanarity condition is not enough to a C1
joint. I guess I missed something from the references and that something
else is needed. I was not successful in my attempts to model a C2 or even a
C1 corner with degree 6 tripatches. However I got a tripatch model for the
cylindrical shape of the edges.
It is clear that the coplanarity condition is not preserved when we promote
a degree elevation of your degree 4 model. When we apply the degree
elevation of 1 degree to your model, the middle triangle of the border is
no longer orthogonal to the edge curve plane.
[image: DegreeElevationTo5.PNG]
To get the degree elevation I use the following function:
// find the representation of a degree n tripatch p as a degree n+m tripatch
function triPatchDegreeElevation(p, m) =
m==0? p:
let( n = len(p),
q = [ p[0],
for(i=[1:n-1], u = 1-i/n )
[ up[i][0] + (1-u)p[i-1][0],
for(j=[1:1:i-1], v = j/i )
(1-u)vp[i-1][j-1] + (1-u)(1-v)p[i-1][j] +
up[i][j],
up[i][i] + (1-u)p[i-1][i-1]
],
[ p[n-1][0],
for(j=[1:1:n-1], v = j/n ) vp[n-1][j-1] + (1-v)*p[n-1][j],
p[n-1][n-1]
]
] )
triPatchDegreeElevation(q, m-1);
Note that this function supposes the input tripatch p is a triangular
matrix bellow the diagonal and not above as you defined in your codes.
The cylindrical shape of a rounded edge (a sweep of the degree 4 border
curve) may be modeled by:
function cylindricalPatchCP(p0, p1, p2, d, r0) =
let( cv = curveG2CP([p0,p1,p2],r0),
bs = BzSubdiv(cv,1/2) )
[ [bs[4]+d],
[ bs[3]+3d/4, bs[5]+3d/4 ],
[ bs[2]+d/2, 2*(bs[4]+d/2)-(bs[2]+bs[6]+d)/2 , bs[6]+d/2 ],
[ bs[1]+d/4, (cv[1]+cv[2])/2+d/4, (cv[2]+cv[3])/2+d/4, bs[7]+d/4 ],
cv
];
// our well known degree 4 curve
function curveG2CP(p,r0=0.5) =
[ p[0], p[0]+r0*(p[1]-p[0]), p[1], p[2]+r0*(p[1]-p[2]), p[2] ];
// Bezier subdivision of CP p
function BzSubdiv(p,u) =
len(p) == 2 ? [p[0], (1-u)p[0]+up[1], p[1]] :
[ p[0],
each BzSubdiv([for(i=[0:len(p)-2]) (1-u)p[i]+up[i+1]],u),
p[len(p)-1] ];
That patch makes a clear C1 joint with its mirror image along axis x. This
two patches satisfy the planarity condition at the common border even with
a degree elevation.
Taking all this in consideration, my next attempt will be to find a model
such that both itself and its 1 degree elevation satisfy the condition of
orthogonality of the border triangles and the plane of border curve.
I forgot to mention: the parameter d in cylindricalPatchCP() should be
[0,0,0] to get the cylindrical shape. Parameter d deforms the shape without
changing its border derivatives.
Wrong explanation!
In this version of cylindricalPatchCP(), p0, p1 and p2 are the three points
defining the degree 4 border of the patch. The parameter d is the tip of
the tripatch opposed to that border. A typical call would be:
cylCP = cylindricalPatchCP([10,0,10], [10,0,0], [10,10,0], [30,0,0], r0);
Em sex, 5 de abr de 2019 às 16:36, Ronaldo Persiano rcmpersiano@gmail.com
escreveu:
I forgot to mention: the parameter d in cylindricalPatchCP() should be
[0,0,0] to get the cylindrical shape. Parameter d deforms the shape without
changing its border derivatives.
I am also surprised that the coplanarity condition is not enough to a C1
joint. I guess I missed something from the references and that something
else is needed.
I understand now what was my misunderstanding of the coplanarity condition
for C1 joints. The condition is valid for the graph of real-valued
polynomials and not for muiti-valued polynomials as the tripatches. Both
references we got are concerned with interpolation problems of real-valued
data. In this case, the first two coordinates of the graph have very
specific position depending just on the triangle domain and the polynomial
degree. Although the graph may be regarded as a surface patch, the specific
positions of the projection of the CP on the xy plane does mater.
To get a C1 condition valid for three-variate polynomials we will need to
verify the coplanarity condition for each coordinate polynomial. Taking
adrian model degree 4 patch P as an example, I graphed the three
coordinates of it:
graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[0,2,0], 0.05);
mirror([0,1,0]) graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[0,2,0], 0.05);
translate([7,7,0]) scale([1,1,0.01]) text("X", size=1);
translate([10,0,0]){
graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[2,0,0], 0.05);
mirror([0,1,0]) graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[2,0,0], 0.05);
translate([7,7,0]) scale([1,1,0.01]) text("Y", size=1);
}
translate([20,0,0]){
graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[0,0,2], 0.05);
mirror([0,1,0]) graph(revert(P), [0,0,0], [10,0,0], [0,10,0],[0,0,2], 0.05);
translate([7,7,0]) scale([1,1,0.01]) text("Z", size=1);
}
module graph(p, P0, P1, P2, d=[1,0,0], w) {
n = len(p);
base = [ [P0], for(i=[1:n-1]) [for(j=[0:i]) (1-i/n)P0 + i/n(
(1-j/i)P1 + j/iP2) ] ];
tri_grid(base,w);
graph = [for(i=[0:n-1]) [for(j=[0:i]) [base[i][j][0], base[i][j][1],
p[i][j]*d ] ] ];
translate([0,0,1]) color("red") tri_grid(graph,w);
}
module tri_grid(p, w=0.05) {
n = len(p);
for(i=[0:n-2]) for(j=[1:n-i-1]) line([p[n-i-1][j], p[n-i-1][j-1]], w=w);
for(j=[0:n-2]) for(i=[0:n-j-2]) line([p[n-i-1][j], p[n-i-2][j]], w=w);
for(k=[0:n-2]) for(j=[1:n-k-1]) line([p[j+k][j], p[j+k-1][j-1]], w=w);
}
function revert(p) = [for(i=[len(p)-1:-1:0]) p[i]];
The revert is needed due to the order adrian defined his patches.
The image outcome of this code is:
[image: graphs.PNG]
where the grids in blue represent the specific arranges the down projection
of the graph CP should have and the grids in red show the graph of each
coordinate of adrian surface. Besides, I mirror the graphs in order to
check the C1 conditions. It is clear from the image that just the
coordinate z has a C1 joint with it mirror image. Both coordinates X and Y
fails to have C1 continuity at those joints.
Based on that considerations we could try to build a patch that meets the
C1 condition (and perhaps the C2 condition) at the joints. However, what we
really need is G1 and G2 continuity, that is a geometric differentiabilty
and not a parametric one. Hard stuff!
Based on that considerations we could try to build a patch that meets the
C1 condition (and perhaps the C2 condition) at the joints. However, what we
really need is G1 and G2 continuity, that is a geometric differentiability and
not a parametric one. Hard stuff!
Well, I have a good news and a definitely bad one. I have found in Farin's
book ("Curves and Surfaces for CAGD") the full condition for a C1 joint
between to tripatches. Besides the co-planarity condition it is required
that any pair of CP grid triangle adjacent to the meeting border of the two
patches is an affine image of the other pairs. This affine transform should
not include any reflection, just translations, rotations and positive
scales. The following image shows the C1 joint of a degree 2 patch with its
mirror image:
[image: C1joint.PNG]
The grid in red is a mirror image of the grid in blue. The transparent
pairs of triangles satisfy the conditions of being co-planar and each one
is an affine transform of the other pair without any reflection. The joint
is C1. For patches of degree n the number of pairs to be considered will be
n. Note that the condition excludes reflections as a possible affine
transform and the following image shows what happens when the affine
transform between pairs does not meet that requirement:
[image: NonC1joint.PNG]
In this example the adjacent pairs of triangles although co-planar does not
meet the affine condition restriction. The joint is clearly not C1.
I did try to apply this condition to model a rounded corner with a tripatch
and that is the bad news: it seems impossible to get, even the C1
condition, with a tripatch of any degree with all the border constraints of
a rounded corner we need. To understand my point, consider the following
image that schematically represents a tripatch of undefined degree intended
to round a corner:
[image: SchematicCorner.PNG]
The blue lines are the patch border and some of the grid triangles near the
patch corner are represented. Besides I added the grid triangles of the
mirrored patch near patch corner B. To meet our constraints, the grid
triangles in the patch corner should be a rectangle triangle whose
hypotenuse is towards the patch center. Then, along the border AB, we need
to have the grid triangles as illustrated and no affine transform will
bring a triangle pair near A to a triangle pair near B except with some
negative scale factor (which is a reflection). So, the conditions to have a
rounded corner with curvature continuity does not allow to meet the
condition of of a C1 joint with tripatch of any degree.
We could try to model the corner with a set of tripatches like the
Clough-Tocher scheme that uses three patches, or Power-Sabin scheme with 6
tripatches. However, to get a C2 conditions between two patches of the
scheme seems to require higher degree polynomials.
The great advantage of the use of tripatch over the rectangular patch would
be the 3 symmetries that lacks in the later. It is possible to get a 3
symmetry based on the rectangular patch solution by taking the media of
sampling the three surface patches each one collapsing a row to one corner
vertex. That would be time consuming (three evaluations of a degree 8
polynomial for each sample point).
The asymmetry of the rectangular patch does not seems great enough to
justify that onus. Besides, I have confirmed that the collapsed rectangular
patch that rounds a corner is indeed curvature continuous as we had
supposed.
It is great to have a resolution to the questions about the triangular
patches.
Can you elaborate a bit on the condition? In the first example, it appears
the two top triangles are not coplanar. Is that just a rendering issue? In
your second example (with the obvious C1 failure) it appears that the
bordering triangles can be mapped onto each other by a rotation. I assume
the affine transformation is confined to the plane that contains the
triangles.
In musing a bit about symmetry it has occurred to me that most applications
for this would be shapes that lacked 3 axis symmetry anyway. Most likely
an object would be rounded in a symmetric way in the x-y plane but then in a
different way in the z direction because the object might be much thinner in
the z direction, for example. It seems like it makes sense to simply align
the odd axis along the collapsed line of the square in the patch.
It does raise the question about potential advantages of the extrusion
approach.
--
Sent from: http://forum.openscad.org/
Can you elaborate a bit on the condition? In the first example, it appears
the two top triangles are not coplanar. Is that just a rendering issue?
The condition for a C1 joint between two tripatches I have found in Farin's
book was just that small reference:
[image: C1Farin.PNG]
It is clear that if the pair is an affine map of the domain then each pair
is an affine map the other pairs. Although the text does not say it, I have
found in my experiments that the affine map in that condition cannot
include any reflection but just translations, rotations and positive
scales. The second example of my previous message corroborates that. I have
not checked whether shears are allowed.
In my examples, the pairs of triangles meeting at the border are all
coplanars. The view angle I used to generate the images might not have
being the best. You may experiment and check my conclusions playing with
the code I have used to produce the images:
// The C1 case CP
P = [ [[0,-3,0]],
[[0,-2,1.5],[-2,-2,0]],
[[2,0,0],[0,0,1.5],[-2,0,0]]
];
// The non-C1 case CP
Q = [ [[0,-3,0]],
[[2,-2,0],[-2,-2,0]],
[[2,0,0],[0,0,2],[-2,0,0]]
];
// The C1 case
translate(){
color("red")
tri_grid(P,w);
showTriPatch(P);
%showTriPatch([[P[2][0]], [P[2][1], P[1][0]]]);
%showTriPatch([[P[2][1]], [P[2][2], P[1][1]]]);
}
// Its mirror image
mirror([0,1,0]) {
color("blue")
tri_grid(P,w);
showTriPatch(P);
%showTriPatch([[P[2][0]], [P[2][1], P[1][0]]]);
%showTriPatch([[P[2][1]], [P[2][2], P[1][1]]]);
}
// The non-C1 case
translate([0,7,0]){
color("red")
tri_grid(Q,w);
showTriPatch(Q);
scale([1,1.5,1])
mirror([0,1,0]){
showTriPatch(Q);
color("blue")
tri_grid(Q,w);
}
}
// sample a tripatch with CP cp in a triangular array
function triPatchSample(cp,n) =
[for(i=[0:n-1]) [for(j=[0:i]) BtriPatchPoint(cp, (n-1-i)/(n-1), j/(n-1))
] ];
// generates the image of the surface of a tripatch p
module showTriPatch(p,n=10) {
pdat = trimesh2polyhedron(triPatchSample(p,n));
polyhedron(pdat[0],pdat[1]);
}
// computes the point in a tripatch mapped by the parameters (u,v)
function BtriPatchPoint(p,u,v) =
len(p) == 1 ? p[0][0] :
let( n = len(p),
pu = [for(i=[0:n-2]) p[i] ],
pv = [for(i=[1:n-1]) [for(j=[0:i-1]) p[i][j] ] ],
pw = [for(i=[1:n-1]) [for(j=[1: i]) p[i][j] ] ] )
BtriPatchPoint(upu + vpv + (1-u-v)*pw, u, v);
// generates the mesh of a triangular array of 3D points in a polyhedron
data format [verts, faces]
function trimesh2polyhedron(p,inv=false) =
let( n = len(p),
vertices = inv ? [ for(i=[0:n-1], j=[0:i]) p[i][i-j] ]:
[ for(pt=p, q=pt) q ] ,
tris = concat(
[for(i=[1:n-1], j=[1:i])
[ j+(i+1)(i)/2, j-1+(i-1)(i)/2, j-1+(i+1)(i)/2 ]
],
n <= 2 ? [] :
[for(i=[1:n-2], j=[1:i])
[ j+(i+1)(i+2)/2, j+(i+1)(i)/2, j-1+(i+1)(i)/2 ]
]) )
[ vertices, tris ];
module line(p,closed=false,dots=false,w=0.1)
if (dots)
for(i=[0:len(p)-1])
translate(p[i]) sphere(4*w);
else
for(i=[0:len(p)-(closed? 1:2)])
hull(){ translate(p[i]) sphere(w);
translate(p[(i+1)%len(p)]) sphere(w);
}
// draw the mesh of a triangular array of points
module tri_grid(p, w=0.05) {
n = len(p);
for(i=[0:n-2]) for(j=[1:n-i-1]) line([p[n-i-1][j], p[n-i-1][j-1]], w=w);
for(j=[0:n-2]) for(i=[0:n-j-2]) line([p[n-i-1][j], p[n-i-2][j]], w=w);
for(k=[0:n-2]) for(j=[1:n-k-1]) line([p[j+k][j], p[j+k-1][j-1]], w=w);
}
In your second example (with the obvious C1 failure) it appears that the
bordering triangles can be mapped onto each other by a rotation. I assume
the affine transformation is confined to the plane that contains the
triangles.
Yes, they can but mapping the triangle of a patch in the triangle of the
other patch. In the code above, I changed one of the patches in order to
make this clearer. The two patches now are not the mirror image of each
other.
In musing a bit about symmetry it has occurred to me that most applications
for this would be shapes that lacked 3 axis symmetry anyway. Most likely
an object would be rounded in a symmetric way in the x-y plane but then in
a
different way in the z direction because the object might be much thinner
in
the z direction, for example. It seems like it makes sense to simply align
the odd axis along the collapsed line of the square in the patch.
I am not sure this makes sense. In the method of rounding a cube by hulling
spheres, would you consider a smashed sphere to have that asymmetry?
Anyway, the asymmetry of the rectangular patch with a collapsed row is very
very mild and should not have any significant effect.
It does raise the question about potential advantages of the extrusion
approach.
I agree.
Trigger warning: Rant ahead!
So this rounded business talk goes round and round.
First its rounded polygons.
Then rounded 3d primitives.
Then it turns into rounded 3d boolean operations.
Some suggest the operations need to be built ins.
Some counter it can all be done in user-space with polyhedrons.
Followed by a long exchange that in essence asks how to replicate well
established B-rep operations like those in Parasolid/Opencascade etc based
systems, but now in open-scad, in user-space. Great fun project and all but
completely insane as a task.
Some suggest various nested Minkowski operations as a side note, always with
some huge buts.
Rinse and repeat every few months.
The argument that time consuming builtin operations breaks fast preview
completely falls apart when the suggested alternative is hundred times
slower user-space polyhedron operations. Implemented with only lists of
list, no mutable data or linked structures and some good old float point
errors as a throwback to the very core issue why precision math CGAL was
chosen for Openscad in the first place.
Addressing every other feature request with "solve it yourself in
user-space" cannot be a sane way forward when those ideas already been
ruminated exhaustively in ever repeating waves.
Maybe a more honest answer is that it is hard, very hard, close to
impossible, to implement rounded modelling in this kind of preview/render
hybrid system. A system that unfortunately painted itself into a bit of a
corner from the beginning. But then again it is infinitely harder to do all
that in the so called user-space.
If a willing few has them magic skills to solve this in pure openscad then
surely they are the kind that could expand CGAL with similar functions for
far lesser effort.
end rant.
My question to the community is: What level of features is sane to be solved
by users and what
belongs in the developer world?
--
Sent from: http://forum.openscad.org/
I am the OP of the recent reddit post on this subject:
https://www.reddit.com/r/openscad/comments/bfx897/openscad_rounded_boxes/
Being just a beginner I had no idea how to use the hull() function. A day
later I now I know how to use hull() I can say it definitely solves the
problem of generated rounded solid a non issue. The hull() function also is
very fast and is almost instant when used in render(). So in short, rounded
shapes is easy if you just use the hull() function.
A rounded cube with hull():
module roundCube(w, d, h, radius)
{
hull()
{
translate([w / 2, d / 2, 0]) sphere(radius);
translate([w / 2, d / -2, 0]) sphere(radius);
translate([-w / 2, d / 2, 0]) sphere(radius);
translate([-w / 2, d / -2, 0]) sphere(radius);
translate([w / 2, d / 2, h]) sphere(radius);
translate([w / 2, d / -2, h]) sphere(radius);
translate([-w / 2, d / 2, h]) sphere(radius);
translate([-w / 2, d / -2, h]) sphere(radius);
}
}
Easy peezee and done.
Hull only works for convex shapes, though. Minkowsky works for non-convex,
but is much slower
On Thu, 25 Apr 2019, 16:16 Anthony Walter, sysrpl@gmail.com wrote:
I am the OP of the recent reddit post on this subject:
https://www.reddit.com/r/openscad/comments/bfx897/openscad_rounded_boxes/
Being just a beginner I had no idea how to use the hull() function. A day
later I now I know how to use hull() I can say it definitely solves the
problem of generated rounded solid a non issue. The hull() function also is
very fast and is almost instant when used in render(). So in short, rounded
shapes is easy if you just use the hull() function.
A rounded cube with hull():
module roundCube(w, d, h, radius)
{
hull()
{
translate([w / 2, d / 2, 0]) sphere(radius);
translate([w / 2, d / -2, 0]) sphere(radius);
translate([-w / 2, d / 2, 0]) sphere(radius);
translate([-w / 2, d / -2, 0]) sphere(radius);
translate([w / 2, d / 2, h]) sphere(radius);
translate([w / 2, d / -2, h]) sphere(radius);
translate([-w / 2, d / 2, h]) sphere(radius);
translate([-w / 2, d / -2, h]) sphere(radius);
}
}
Easy peezee and done.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
On 25.04.19 21:44, TLC123 wrote:
My question to the community is: What level of features is sane
to be solved by users and what belongs in the developer world?
This is a very good question. Burying it in this topic after the
rant seems a bit strange though...
Also, I suspect there's no easy and single answer. Maybe that
discussion could be had, but separately. So I won't start now.
ciao,
Torsten.
I don't claim to really understand the math, but a Minkowski sum
basically involves moving an object A along the entire perimeter of
another object B, and the result is the entire area swept. Right?
Is there another operation (Minkowski difference, maybe?) that moves B
around the inside of A, and that doesn't include anything that B can't
reach? (Do I need to draw a picture to describe what I mean?)
This latter operation would seem like a powerful way to take a shape and
round all of its edges, while preserving its major dimensions.
On 4/25/2019 4:25 PM, Jordan Brown wrote:
I don't claim to really understand the math, but a Minkowski sum
basically involves moving an object A along the entire perimeter of
another object B, and the result is the entire area swept. Right?
Is there another operation (Minkowski difference, maybe?) that moves B
around the inside of A, and that doesn't include anything that B
can't reach? (Do I need to draw a picture to describe what I mean?)
This latter operation would seem like a powerful way to take a shape
and round all of its edges, while preserving its major dimensions.
And is often the case, a few searches and clicks later:
https://en.wikipedia.org/wiki/Opening_(morphology)
See https://github.com/openscad/openscad/issues/1678
Minkwoski difference can be done with Minkowski sum and difference but it
is very slow and uses an enormous amount of memory for values of $fn that
make things smooth.
On Fri, 26 Apr 2019 at 06:25, Jordan Brown openscad@jordan.maileater.net
wrote:
On 4/25/2019 4:25 PM, Jordan Brown wrote:
I don't claim to really understand the math, but a Minkowski sum basically
involves moving an object A along the entire perimeter of another object B,
and the result is the entire area swept. Right?
Is there another operation (Minkowski difference, maybe?) that moves B
around the inside of A, and that doesn't include anything that B can't
reach? (Do I need to draw a picture to describe what I mean?)
This latter operation would seem like a powerful way to take a shape and
round all of its edges, while preserving its major dimensions.
And is often the case, a few searches and clicks later:
https://en.wikipedia.org/wiki/Opening_(morphology)
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Even for convex shapes hull has limitations. Anthony, if you think rounding
is "easy peezee" then first I suggest fixing the module so the rounded cube
is the right size rather than 2*r oversized. Then I ask how you would make
a rounded pyramid with an arbitrary base polygon and apex that is a
designated size and not oversized. Next, how would you use the hull method
to make a cylinder with rounded ends? What if the cylinder's ends are not
normal to its axis? If that's too easy then suppose I construct a parabola
and extrude it. How would you round that?
data = [for(x=[-2:.05:2])[x,x*x]];
linear_extrude(height=4)polygon(data);
I'm not saying these things can't be done. For making a rounded polyhedron
you need to put the spheres in the right place, which is not "easy peezee"
in my opinion when the polyhedron is not a cube. (Oh, and don't forget that
spheres are actually polyhedra, and undersized.) And the methods that leap
to mind for the cylinder seem little complex. For the case of the parabolic
shape I don't see a way of using hull that is easier than just generating
the whole shape directly.
The other problem with using hull is that it assumes that it makes sense to
build up your convex object from a union of convex objects. What if you
create your convex object through difference() operations? Then you cannot
round the final object using hull() operations. If everything has to be
constructed with hull() it limits your options for how you can model stuff.
As acwest notes, non-convex shapes cannot be rounded with hull. In my
experience, nothing I design ends up being convex. I've been pondering the
task of making a box where the inside joint between the walls and base is
rounded and the top edge is rounded. To do this for a general box (with an
arbitrary shaped base) appears to be very difficult.
Regarding run time, hull() can be slow. In fact, for polyhedra with large
numbers of faces it seems that minkowski() is faster than hull(). I just
did a timing test and the hull of 50 spheres with $fn=32 took 38 seconds to
preview. I understand that technically what's slow for hull() is not the
hull operation but the unavoidable yet unnecessary union operation between
all the spheres.
It is possible to make a general minkowski rounding function with 3 calls to
minkowski. But is has the limitation that the shape cannot have any areas
thinner than the roundover radius. Also there is the question of speed.
Reportedly rounding the union of a cylinder and cube (that don't meet at
right angles) takes 12 hours. And of course it enforces the same rounding
radius globally on the model, which may not be what you need.
acwest wrote
Hull only works for convex shapes, though. Minkowsky works for non-convex,
but is much slower
On Thu, 25 Apr 2019, 16:16 Anthony Walter, <
sysrpl@
> wrote:
I am the OP of the recent reddit post on this subject:
https://www.reddit.com/r/openscad/comments/bfx897/openscad_rounded_boxes/
Being just a beginner I had no idea how to use the hull() function. A day
later I now I know how to use hull() I can say it definitely solves the
problem of generated rounded solid a non issue. The hull() function also
is
very fast and is almost instant when used in render(). So in short,
rounded
shapes is easy if you just use the hull() function.
A rounded cube with hull():
module roundCube(w, d, h, radius)
{
hull()
{
translate([w / 2, d / 2, 0]) sphere(radius);
translate([w / 2, d / -2, 0]) sphere(radius);
translate([-w / 2, d / 2, 0]) sphere(radius);
translate([-w / 2, d / -2, 0]) sphere(radius);
translate([w / 2, d / 2, h]) sphere(radius);
translate([w / 2, d / -2, h]) sphere(radius);
translate([-w / 2, d / 2, h]) sphere(radius);
translate([-w / 2, d / -2, h]) sphere(radius);
}
}
Easy peezee and done.
--
Sent from: http://forum.openscad.org/
Hull is only slow if it has an implicit union inside it. For example if you
place spheres with a for loop or a common translation or rotation then they
get unioned first and that is very slow with CGAL. If you place each sphere
individually, like the example above, then hull is very fast,
Spheres can be made exact by rotate extruding a semi circle with $fn a
multiple of 4. I redefine sphere myself to be that.
//
// Replicate OpenSCAD logic to calculate number of sides from radius
//
function r2sides(r) = $fn ? $fn : ceil(max(min(360/ $fa, r * 2 * PI / $fs),
5));
//
// Round up number of sides to multiple of 4 to ensure points on all axes
//
function r2sides4n(r) = floor((r2sides(r) + 3) / 4) * 4;
//
// Circle with multiple of 4 vertices
//
module Circle(r, d = undef) {
R = is_undef(d) ? r : d / 2;
circle(R, $fn = r2sides4n(R));
}
//
// Semi circle
//
module semi_circle(r)
intersection() {
Circle(r);
sq = r + 1;
translate([-sq, 0])
square([2 * sq, sq]);
}
//
// Sphere that has vertices on all three axes. Has the advantage of giving
correct dimensions when hulled
//
module sphere(r = 1, d = undef) {
R = is_undef(d) ? r : d / 2;
rotate_extrude($fn = r2sides4n(R))
rotate(-90)
semi_circle(R);
}
[image: spheres.png]
I manage to make everything I need with these techniques but a version of
3D Minkowski that would run before the heat death of the universe and
consume less than an infinite amount of memory would be nice.
On Fri, 26 Apr 2019 at 12:16, adrianv avm4@cornell.edu wrote:
Even for convex shapes hull has limitations. Anthony, if you think
rounding
is "easy peezee" then first I suggest fixing the module so the rounded cube
is the right size rather than 2*r oversized. Then I ask how you would make
a rounded pyramid with an arbitrary base polygon and apex that is a
designated size and not oversized. Next, how would you use the hull method
to make a cylinder with rounded ends? What if the cylinder's ends are not
normal to its axis? If that's too easy then suppose I construct a parabola
and extrude it. How would you round that?
data = [for(x=[-2:.05:2])[x,x*x]];
linear_extrude(height=4)polygon(data);
I'm not saying these things can't be done. For making a rounded polyhedron
you need to put the spheres in the right place, which is not "easy peezee"
in my opinion when the polyhedron is not a cube. (Oh, and don't forget
that
spheres are actually polyhedra, and undersized.) And the methods that leap
to mind for the cylinder seem little complex. For the case of the
parabolic
shape I don't see a way of using hull that is easier than just generating
the whole shape directly.
The other problem with using hull is that it assumes that it makes sense to
build up your convex object from a union of convex objects. What if you
create your convex object through difference() operations? Then you cannot
round the final object using hull() operations. If everything has to be
constructed with hull() it limits your options for how you can model
stuff.
As acwest notes, non-convex shapes cannot be rounded with hull. In my
experience, nothing I design ends up being convex. I've been pondering the
task of making a box where the inside joint between the walls and base is
rounded and the top edge is rounded. To do this for a general box (with an
arbitrary shaped base) appears to be very difficult.
Regarding run time, hull() can be slow. In fact, for polyhedra with large
numbers of faces it seems that minkowski() is faster than hull(). I just
did a timing test and the hull of 50 spheres with $fn=32 took 38 seconds to
preview. I understand that technically what's slow for hull() is not the
hull operation but the unavoidable yet unnecessary union operation between
all the spheres.
It is possible to make a general minkowski rounding function with 3 calls
to
minkowski. But is has the limitation that the shape cannot have any areas
thinner than the roundover radius. Also there is the question of speed.
Reportedly rounding the union of a cylinder and cube (that don't meet at
right angles) takes 12 hours. And of course it enforces the same rounding
radius globally on the model, which may not be what you need.
acwest wrote
Hull only works for convex shapes, though. Minkowsky works for
non-convex,
but is much slower
On Thu, 25 Apr 2019, 16:16 Anthony Walter, <
sysrpl@
> wrote:
I am the OP of the recent reddit post on this subject:
Being just a beginner I had no idea how to use the hull() function. A
day
later I now I know how to use hull() I can say it definitely solves the
problem of generated rounded solid a non issue. The hull() function also
is
very fast and is almost instant when used in render(). So in short,
rounded
shapes is easy if you just use the hull() function.
A rounded cube with hull():
module roundCube(w, d, h, radius)
{
hull()
{
translate([w / 2, d / 2, 0]) sphere(radius);
translate([w / 2, d / -2, 0]) sphere(radius);
translate([-w / 2, d / 2, 0]) sphere(radius);
translate([-w / 2, d / -2, 0]) sphere(radius);
translate([w / 2, d / 2, h]) sphere(radius);
translate([w / 2, d / -2, h]) sphere(radius);
translate([-w / 2, d / 2, h]) sphere(radius);
translate([-w / 2, d / -2, h]) sphere(radius);
}
}
Easy peezee and done.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
nophead wrote
Hull is only slow if it has an implicit union inside it. For example if
you
place spheres with a for loop or a common translation or rotation then
they
get unioned first and that is very slow with CGAL. If you place each
sphere
individually, like the example above, then hull is very fast,
I had forgotten about that. It's not a very attractive coding method if you
have a lot of stuff to hull, though. Also note that I had an error in my
code. It actually takes 3.5 minutes to compute the hull of the 50 spheres.
Spheres can be made exact by rotate extruding a semi circle with $fn a
multiple of 4. I redefine sphere myself to be that.
That's a nice idea.
Note, however, that it only makes hulls exact for faces parallel to the
coordinate planes. In my example problem of the pyramid it's going to be
very difficult to figure out the effective radius of the "sphere" in the
direction of each face so that you can position them exactly right to get a
hull that actually has the exact dimensions desired.
--
Sent from: http://forum.openscad.org/