ygoe
ygoe

Reputation: 20394

3D cube with individually rounded corners

It doesn't seem to be easy (or possible?) to get a truly rounded corners 3D cube in OpenSCAD. I tried something with hull() and spheres but that didn't work correctly. Then I tried to make something with hulls of circles in each plane, then intersect all 3 planes. The edges are good but the corners are not smooth. I understand that the linear extrusion of each edge is the problem because it won't follow the curve defines on another plane. I didn't get my head around the minkowski() function yet but it doesn't look like it would solve the problem either.

So is there any hope to get this working properly?

Here's my code so far:

// Lengths in X, Y, Z dimension
dx = 10;
dy = 20;
dz = 5;

// Corner radius on XY plane (from origin clockwise around positive Z axis)
rxy = [1, 1, 3, 1];
// Corner radius on YZ plane (from origin clockwise around positive X axis)
ryz = [1, 1, 1, 1];
// Corner radius on XZ plane (from origin clockwise around positive Y axis)
rxz = [1, 1, 2, 1];

intersection()
{
    // XY plane
    linear_extrude(dz)
    hull()
    {
        for (ix = [0:1], iy = [0:1])
        {
            i = [0, 1, 3, 2][ix * 2 + iy];
            r = rxy[i] == 0 ? 0.0001 : rxy[i];
            x = ix == 0 ? r : dx - r;
            y = iy == 0 ? r : dy - r;
            translate([x, y, 0])
            circle(r, $fn=40);
        }
    }

    // YZ plane
    translate([0, 0, dz])
    rotate([0, 90, 0])
    linear_extrude(dx)
    hull()
    {
        for (iy = [0:1], iz = [0:1])
        {
            i = [1, 0, 2, 3][iy * 2 + iz];
            r = ryz[i] == 0 ? 0.0001 : ryz[i];
            y = iy == 0 ? r : dy - r;
            z = iz == 0 ? r : dz - r;
            intersection()
            {
                translate([z, y, 0])
                circle(r, $fn=40);

                square([dz, dy]);
            }
        }
    }

    // XZ plane
    translate([0, dy, 0])
    rotate([90, 0, 0])
    linear_extrude(dy)
    hull()
    {
        for (ix = [0:1], iz = [0:1])
        {
            i = [0, 3, 1, 2][ix * 2 + iz];
            r = rxz[i] == 0 ? 0.0001 : rxz[i];
            x = ix == 0 ? r : dx - r;
            z = iz == 0 ? r : dz - r;
            intersection()
            {
                translate([x, z, 0])
                circle(r, $fn=40);

                square([dx, dz]);
            }
        }
    }
}

The corners look like this:

enter image description here

One application of this would be a shape like this thing: https://www.printables.com/model/62314-cable-clamp#preview It has slightly rounded corners everywhere and a massively rounded corner at exactly one edge.

Upvotes: 2

Views: 2424

Answers (3)

Markus
Markus

Reputation: 2153

Also worth to look at is the immensive NopSCADLib, especially: https://github.com/nophead/NopSCADlib?tab=readme-ov-file#rounded_rectangle

Example:

rounded_cube_xy(size, r = 0, xy_center = false, z_center = false)

Like cube() but corners rounded in XY plane and separate centre options for xy and z.

Upvotes: 1

Cadeyrn
Cadeyrn

Reputation: 647

I found a partial solution to your problem : you can only determine 6 radii (all the radii of the edges of the top face are the same and the same thing applies to the bottom face) but I think it's enough for the example you shown.

code:

module rounded_cube(size = [4, 2, 1], face_radii = [1/8, 1/16], edges_radii = [1, 1/2, 1/4, 1/8, ], $fn = 16){
    
    module block(size = [1, 2], radii = [1/2, 1/4], $fn = 16){
        intersection(){
            union(){
                square([size.x - radii[0], radii[0]]);
                translate([0, size.y - radii[1]])
                square([size.x - radii[1], radii[1]]);
                
                translate([0, radii[0]])
                square([size.x, size.y - radii[0] - radii[1]]);
            
                translate([size.x - radii[0], radii[0]])
                circle(radii[0]);
                translate([size.x - radii[1], size.y - radii[1]])
                circle(radii[1]);
            }
            square([size.x, size.y]);
        }
    }

    //block();
    
    assert(max(face_radii) <= min(edges_radii), "all face radii must be smaller than all edges radii");
    
    assert(face_radii[0] + face_radii[1] <= size.z, "the sum of the face radii must be smaller than the height of the rounded cube");
    
    assert(
        edges_radii[0] + edges_radii[1] <= size.x &&
        edges_radii[1] + edges_radii[2] <= size.y &&
        edges_radii[2] + edges_radii[3] <= size.x &&
        edges_radii[3] + edges_radii[0] <= size.y, 
        "the sum of two consecutive edges radii must be smaller than the side they lie on");

    for(e = [0 : 3]){
        translate([size.x * [0, 1, 1, 0][e], size.y * [0, 0, 1, 1][e]])
        rotate([0, 0, 180 + e*90]){
            translate([-edges_radii[e], -edges_radii[e]])
            rotate_extrude(angle = 90, $fn = $fn * 4)
            block([edges_radii[e], size.z], [face_radii[0], face_radii[1]], $fn = $fn * 4);
            
            rotate([-90, 0, 90])
            translate([-size[(e + 1) % 2] / 2, -size.z, edges_radii[e]])
            linear_extrude(size[e % 2] - edges_radii[e] - edges_radii[(e + 1) % 4])
            block([size[(e + 1) % 2] / 2, size.z], [face_radii[1], face_radii[0]], $fn = $fn * 4);
        }
    }
}

rounded_cube(size = [4, 2, 1], face_radii = [1/8, 1/16], edges_radii = [1, 1/2, 1/4, 1/8], $fn = 16);

Upvotes: 2

FractalLotus
FractalLotus

Reputation: 368

There is an OpenSCAD module that does this. That page also lists a link to a simple version that uses Minkowski. Here is the code from the simple version.

// More information: https://danielupshaw.com/openscad-rounded-corners/

module roundedcube_simple(size = [1, 1, 1], center = false, radius = 0.5) {
    // If single value, convert to [x, y, z] vector
    size = (size[0] == undef) ? [size, size, size] : size;

    translate = (center == false) ?
        [radius, radius, radius] :
        [
            radius - (size[0] / 2),
            radius - (size[1] / 2),
            radius - (size[2] / 2)
    ];

    translate(v = translate)
    minkowski() {
        cube(size = [
            size[0] - (radius * 2),
            size[1] - (radius * 2),
            size[2] - (radius * 2)
        ]);
        sphere(r = radius);
    }
}

radius * 2 is subtracted from the cube dimensions since Minkowski adds the radius to all sides.

Upvotes: 3

Related Questions