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 codecircles 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 visuallythe more gradual curve is visually perceptiblebut 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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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 zaxis, that will transit from a rect to the rounded path.  Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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 zcoordinate 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 xyspace, 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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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/2r,s/2r, s/2r]) corner(p); } module corner(p) rotate_extrude(angle = 90, $fa=1) polygon(p); function BezierPoint(p, u) = (len(p) == 2)? u*p[1] + (1u)*p[0] : u*BezierPoint([for(i=[1:len(p)1]) p[i] ], u) + (1u)*BezierPoint([for(i=[0:len(p)2]) p[i] ], u); function BezierCurve(p, n=10) = [for(i=[0:n1]) BezierPoint(p, i/(n1)) ]; 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*(p1p0)*r1, p0 + r0*(p1p0), p2 + r0*(p1p2), p2 + r0*(p1p2)*r1, p2 ] ) BezierCurve(p,n);  Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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 zrotating 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/2r,s/2r, s/2r]) 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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
In reply to this post by adrianv
adrianv <[hidden email]> wrote:
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. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
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. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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. Following that line of thought, here is a rectangular patch to provide a curvature continuity in rounding a corner of a cube. The following image shows the mesh of control points (CPs) compared with with the patch itself. 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:
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: 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. But how would you automatically weave in such a patch into a 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. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
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(90*i, 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(90*i, 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] + (1u)*p[0] : u*BezierPoint([for(i=[1:len(p)1]) p[i] ], u) + (1u)*BezierPoint([for(i=[0:len(p)2]) p[i] ], u); function BezierCurve(p, n=10) = [for(i=[0:n1]) BezierPoint(p, i/(n1)) ]; 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*(p1p0)*r1, p0 + r0*(p1p0), p2 + r0*(p1p2), p2 + r0*(p1p2)*r1, p2 ] ) BezierCurve(p,n);  Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
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/RoundedPolygontp21897p25905.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 forloop. 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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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 nonunioning 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 [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
There is already a PR for a nonunion 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 <[hidden email]> wrote: Parkinbot wrote _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
In reply to this post by adrianv
adrianv wrote
> Iff a nonunioning 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/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.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. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
 Torsten

Somehow this PR gets around the problem with hull() for( ..., union=false). See https://github.com/openscad/openscad/pull/2796#issuecomment466836941 On Sun, 24 Mar 2019 at 00:50, Torsten Paul <[hidden email]> wrote: It's not a problem of the language description, it's the internal _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org 
Free forum by Nabble  Edit this page 