← Back to Models
Soap dish v3 (radial lines)

Soap dish v3 (radial lines)

by Mach3 ·

Radial lines. Optional water collector tray. This is an assembly, so switch "part" to download separate .stl models for dish and tray parts.

Interactive Preview & Customizer

3D STL viewer with browser-based OpenSCAD rendering on parameter changes.

Loading previewLoading preview...

Which part to preview or export

Vertical gap between tray and dish in assembly preview (mm)

step 0.5

Outer frame width (mm)

step 1

Outer frame height (mm)

step 1

Total dish thickness (mm)

step 0.5

Border wall thickness (mm)

step 0.1

Outer corner radius (mm)

step 0.5

Distance between radial ribs (mm)

step 0.5

Thickness of each radial rib (mm)

step 0.1

Ripple center X offset from model center (mm)

step 1

Ripple center Y offset from model center (mm)

step 1

Radius of spherical scoop cutout (mm)

step 1

Depth of scoop from top surface (mm)

step 0.5

Clearance around dish footprint inside tray (mm)

step 0.1

Tray wall and floor thickness (mm)

step 0.1

Total tray height (mm)

step 0.5

Curve smoothness for rounded geometry

step 1
WASM compiler loads on first parameter change.
Download current STL

Prompt

lets replace hexes with waved lines

OpenSCAD Code

// Offset Radial Soap Dish + Optional Drip Tray
// Concentric arcs anchored to walls for printability

/* [Output] */
// Which part to preview or export
part = "assembly";               // [dish,tray,assembly]
// Vertical gap between tray and dish in assembly preview (mm)
assembly_gap = 1.0;          // [0:0.5:8]

/* [Frame] */
// Outer frame width (mm)
frame_width = 135;           // [80:1:220]
// Outer frame height (mm)
frame_height = 135;          // [80:1:220]
// Total dish thickness (mm)
frame_depth = 24;            // [8:0.5:60]
// Border wall thickness (mm)
frame_border = 2.0;          // [1:0.1:8]
// Outer corner radius (mm)
corner_radius = 35;          // [8:0.5:70]

/* [Ribs] */
// Distance between radial ribs (mm)
ring_spacing = 11;           // [4:0.5:24]
// Thickness of each radial rib (mm)
ring_thickness = 2.5;        // [0.6:0.1:8]
// Ripple center X offset from model center (mm)
ripple_offset_x = 120;       // [0:1:260]
// Ripple center Y offset from model center (mm)
ripple_offset_y = 120;       // [0:1:260]

/* [Bowl] */
// Radius of spherical scoop cutout (mm)
dish_radius = 220;           // [120:1:420]
// Depth of scoop from top surface (mm)
dish_depth = 16;             // [4:0.5:36]

/* [Tray] */
// Clearance around dish footprint inside tray (mm)
tray_tolerance = 1.0;        // [0:0.1:3]
// Tray wall and floor thickness (mm)
tray_wall_thickness = 2.0;   // [0.8:0.1:8]
// Total tray height (mm)
tray_height = 8;             // [2:0.5:30]

/* [Quality] */
// Curve smoothness for rounded geometry
$fn = 100;                   // [48:1:180]

// --- Render selection ---
if (part == "dish") {
    soap_dish_body();
} else if (part == "tray") {
    drip_tray();
} else if (part == "assembly") {
    drip_tray();
    translate([0, 0, tray_height + assembly_gap]) soap_dish_body();
}

// --- Modules ---

module soap_dish_body() {
    // Position sphere so its bottom reaches the desired depth
    sphere_z_offset = (frame_depth - dish_depth) + dish_radius;

    // Calculate max distance to cover the opposite corner
    max_dist = sqrt(
        pow(ripple_offset_x + frame_width / 2, 2)
        + pow(ripple_offset_y + frame_height / 2, 2)
    );

    difference() {
        union() {
            // 1. Outer border frame
            difference() {
                rounded_square(frame_width, frame_height, frame_depth, corner_radius);

                // Interior cutout
                translate([0, 0, -1])
                    rounded_square(
                        frame_width - frame_border * 2,
                        frame_height - frame_border * 2,
                        frame_depth + 2,
                        corner_radius - frame_border
                    );
            }

            // 2. Anchored arc fill
            intersection() {
                // Keep arcs inside the frame boundary
                rounded_square(
                    frame_width - frame_border,
                    frame_height - frame_border,
                    frame_depth,
                    corner_radius - frame_border / 2
                );

                // Generate rings from the offset center
                translate([ripple_offset_x, ripple_offset_y, 0])
                    concentric_rings(max_dist, ring_spacing, ring_thickness, frame_depth);
            }
        }

        // 3. Concave scoop
        translate([0, 0, sphere_z_offset])
            sphere(r = dish_radius);
    }
}

module drip_tray() {
    // Fit tray to the soap dish outer footprint
    tray_inner_w = frame_width + (tray_tolerance * 2);
    tray_inner_h = frame_height + (tray_tolerance * 2);
    tray_inner_r = corner_radius + tray_tolerance;

    tray_outer_w = tray_inner_w + (tray_wall_thickness * 2);
    tray_outer_h = tray_inner_h + (tray_wall_thickness * 2);
    tray_outer_r = tray_inner_r + tray_wall_thickness;

    difference() {
        // Outer tray body
        rounded_square(tray_outer_w, tray_outer_h, tray_height, tray_outer_r);

        // Inner cavity; shifted up to keep floor thickness
        translate([0, 0, tray_wall_thickness])
            rounded_square(tray_inner_w, tray_inner_h, tray_height, tray_inner_r);
    }
}

module concentric_rings(max_r, spacing, thick, height) {
    // Start from 0 to max_r, though only arcs hitting the frame will be rendered
    for (r = [0 : spacing : max_r]) {
        difference() {
            cylinder(h = height, r = r + thick / 2);
            translate([0, 0, -0.5])
                cylinder(h = height + 1, r = r - thick / 2);
        }
    }
}

module rounded_square(w, h, z, r) {
    translate([-w / 2 + r, -h / 2 + r, 0])
    hull() {
        cylinder(h = z, r = r);
        translate([w - 2 * r, 0, 0]) cylinder(h = z, r = r);
        translate([0, h - 2 * r, 0]) cylinder(h = z, r = r);
        translate([w - 2 * r, h - 2 * r, 0]) cylinder(h = z, r = r);
    }
}