Read this model as text
A kitchen roll holder that clamps onto the edge of a worktop, made for a caravan kitchenette. Two identical clamp brackets, each with a printed thumb-screw bolt on top and an arm in front of the edge that carries an open-top slot. Two loose plugs go into the ends of the roll core, their pins drop into the slots from above. No drilling, nothing to glue, and every part fits a 180 mm bed.
The image shows the solid OpenSCAD renders at part assembly · worktop_thickness 30 · grip_depth 30 · hook_height 0 · roll_diameter 130 · color_print SteelBlue.
Clamp-on kitchen roll holder for a caravan worktop
mick/clamp-kitchen-roll-holder@1.0.0
Report
For a legal or policy problem: a license violation, personal data, somebody else’s work republished as their own. A model that prints badly is a comment, not a report.
Parameters
from sourceDescription
What is this
A kitchen roll holder that clamps onto the edge of a worktop, made for a caravan kitchenette. Two identical clamp brackets, each with a printed thumb-screw bolt on top and an arm in front of the edge that carries an open-top slot. Two loose plugs go into the ends of the roll core, their pins drop into the slots from above. No drilling, nothing to glue, and every part fits a 180 mm bed.
What you use it for
Hanging a standard kitchen roll in front of the worktop edge without screwing into the caravan. The bolt clamps from the top, so the underside only needs room for a thin jaw. If there is a rail or lip under the worktop, set hook_height and the bottom jaw hooks up behind it. If not, leave it at zero and the jaw stays flat.
Design notes
The bracket is one 2D profile of axis-aligned squares: upright, top jaw, bottom jaw, optional hook and the arm with its slot. Rounding only polishes it afterwards: an offset pair rounds the profile corners and the extrusion gets a small chamfer on both faces.
The clamping bolt sits on top because the top of the worktop is always free, while the space under it in a caravan is the unknown. The jaw opening is the worktop thickness plus the hook height plus 3 mm, so the hook can pass under a lip while fitting, and the bolt takes that play back up. The bolt tip is narrower than the thread so the bolt can actually be screwed through the jaw. Stick a felt dot under it if the worktop marks easily.
The plugs are universal: a 36 mm nose that fits any core, and a 45 degree shoulder that centres cores from 36 to 52 mm. Standard kitchen rolls are 22 to 26 cm long, 10 to 14 cm across with a core around 40 to 45 mm. The roll length is not a parameter because the two brackets clamp wherever you put them. The full roll sits with its top flush with the worktop surface and 10 mm clear of the edge.
Print per holder: 2 brackets on their side (layers along the arm), 2 bolts standing on the knob, 2 plugs standing on the nose.
Uses threads.scad by Ryan A. Colyer (CC0), published as remixcad/threads.
Parameters
part picks the assembly or one part laid down for printing. worktop_thickness is the thickness at the edge, including any lip hanging down. grip_depth is how far the jaws reach over and under the worktop. hook_height is the height of the hook at the end of the bottom jaw, zero removes it. roll_diameter is the largest full roll. color_print is display only.
Use with your agent
{
"mcpServers": {
"remixcad": {
"type": "http",
"url": "https://remix-cad.vercel.app/api/mcp"
}
}
}get_model("mick/clamp-kitchen-roll-holder@1.0.0")Returns the manifest, the source, the import closure and the license obligations. Full MCP setup.
Parameter table
Parsed from the Customizer annotations in main.scad| Name | Default | Range | Unit | Description |
|---|---|---|---|---|
| part | "assembly" | assembly, bracket, bolt, plug | ||
| worktop_thickness | 30 | 10 to 60 step 1 | ||
| grip_depth | 30 | 18 to 80 step 1 | ||
| hook_height | 0 | 0 to 30 step 1 | ||
| roll_diameter | 130 | 90 to 180 step 5 | ||
| color_print | "SteelBlue" | SteelBlue, White, Gainsboro, Silver, DimGray, Black, SlateGray, FireBrick, ForestGreen, Goldenrod |
Lineage
0 models depend on this oneOnly what this model came from and what came from it.
License
Whoever downloads this takes these obligations along- mick/clamp-kitchen-roll-holder@1.0.0 is MIT
- remixcad/threads@1.0.0 is CC0
Source
main.scad is the entrypointmain.scad
// A kitchen roll holder that clamps onto the edge of a worktop, made for a caravan
// kitchenette. Two identical clamp brackets with a thumb-screw bolt on top; two plugs in
// the roll core drop into open-top slots. Built from straight rectangles; corners and
// edges are polished afterwards as a final step, never as part of the construction.
// Uses threads.scad by Ryan A. Colyer, released with CC0 into the public domain.
// Published on RemixCAD as remixcad/threads.
use <remixcad/threads/threads.scad>
/* [Parts] */
// Render the assembly, or one part laid down for printing.
part = "assembly"; // [assembly, bracket, bolt, plug]
/* [Worktop] */
// Thickness of the worktop at the edge, including any lip hanging down.
worktop_thickness = 30; // [10:1:60]
// How far the jaws reach over and under the worktop.
grip_depth = 30; // [18:1:80]
// Height of the hook at the end of the bottom jaw, to grab behind a rail or lip. Zero removes it.
hook_height = 0; // [0:1:30]
/* [Roll] */
// Largest diameter of a full kitchen roll.
roll_diameter = 130; // [90:5:180]
/* [Colour] */
// Colour the printed parts are shown in. Display only, the STL carries no colour.
color_print = "SteelBlue"; // [SteelBlue, White, Gainsboro, Silver, DimGray, Black, SlateGray, FireBrick, ForestGreen, Goldenrod]
/* [Hidden] */
T = worktop_thickness;
width = 22; // bracket width along the edge, the print height
back = 10; // upright in front of the edge
jaw = 10; // top jaw, carries the thread
foot = 6; // bottom jaw, thin so it fits over a cabinet
hook_t = 5; // hook thickness
slack = 3; // spare jaw opening, taken up by the bolt
bolt_size = 10; // M10
tolerance = 0.5; // taken off the thread for printing
knob_d = 28;
knob_h = 8;
pad_h = 2;
pin_d = 10; // pin on the plug
pin_fit = 0.6;
under = 10; // material under the pin
lip = 8; // lip in front of the slot
rise = 8; // arm height above the top of the pin
funnel = 3; // funnel at the slot entry
edge_gap = 10; // room between a full roll and the worktop edge
plug_d = 36; // nose of the plug, fits any core
plug_tip_d = 32;
plug_len = 28;
flange_d = 52;
flange_t = 2;
round_r = 2; // profile corner polish
chamfer = 1.2; // face edge polish
smoothness = 48;
// Preview only.
roll_length = 230;
core_d = 42;
gap = T + hook_height + slack;
top0 = hook_height + slack; // underside of the top jaw, worktop surface at z = 0
bolt_x = grip_depth / 2;
bolt_len = jaw + hook_height + slack + 6;
slot_w = pin_d + pin_fit;
arm_h = under + pin_d + rise;
seat_x = -(roll_diameter / 2 + edge_gap);
reach = -seat_x + slot_w / 2 + lip;
arm_z0 = -roll_diameter / 2 - slot_w / 2 - under;
pin_z = arm_z0 + under + slot_w / 2;
up_z0 = min(arm_z0, -T - foot);
shoulder_h = (flange_d - plug_d) / 2;
pin_len = width + 8;
plug_h = plug_len + shoulder_h + flange_t + pin_len;
// Where a typical core lands on the 45 degree shoulder.
contact_z = plug_len + (core_d - plug_d) / 2;
flange_out = plug_len + shoulder_h + flange_t;
bracket_y = roll_length / 2 - contact_z + flange_out + 1 + width / 2;
echo(str("Bracket: ", reach + grip_depth, " x ", top0 + jaw - up_z0, " x ", width, " mm"));
// The polish: a closing pass rounds the concave corners, an opening pass the convex
// ones. Applied over the finished straight shape, never during it.
module polish2d(r) {
if (r > 0)
offset(r = r, $fn = smoothness)
offset(r = -2 * r, $fn = smoothness)
offset(r = r, $fn = smoothness)
children();
else
children();
}
// Extrude with a 45 degree chamfer on both faces, built from thin slices so it stays
// fast. One slice per 0.2 mm, the same as a print layer.
module polish_extrude(h, c) {
if (c > 0) {
n = ceil(c / 0.2);
s = c / n;
translate([0, 0, c]) linear_extrude(height = h - 2 * c) children();
for (i = [0 : n - 1]) {
inset = c - (i + 0.5) * s;
translate([0, 0, i * s])
linear_extrude(height = s + 0.001) offset(delta = -inset) children();
translate([0, 0, h - (i + 1) * s - 0.001])
linear_extrude(height = s + 0.001) offset(delta = -inset) children();
}
} else {
linear_extrude(height = h) children();
}
}
// ---- Bracket ----
// One 2D profile of axis-aligned squares, in the XZ plane.
// Origin: the worktop surface at z = 0, its edge at x = 0, the worktop toward +x.
module profile_raw() {
// Upright, in front of the worktop edge.
translate([-back, up_z0]) square([back, top0 + jaw - up_z0]);
// Top jaw, carries the clamping bolt.
translate([-1, top0]) square([grip_depth + 1, jaw]);
// Bottom jaw, pulled up against the underside.
translate([-1, -T - foot]) square([grip_depth + 1, foot]);
// Hook, grabs behind a rail or lip under the worktop.
if (hook_height > 0)
translate([grip_depth - hook_t, -T - 1]) square([hook_t, hook_height + 1]);
// Arm in front of the edge, with the open-top slot for the plug pin.
difference() {
translate([-reach, arm_z0]) square([reach, arm_h]);
translate([seat_x, pin_z]) {
circle(d = slot_w, $fn = smoothness);
translate([-slot_w / 2, 0]) square([slot_w, arm_h]);
}
translate([seat_x, arm_z0 + arm_h])
polygon([[-slot_w / 2 - funnel, 1], [slot_w / 2 + funnel, 1],
[slot_w / 2 + funnel, 0], [slot_w / 2, -funnel],
[-slot_w / 2, -funnel], [-slot_w / 2 - funnel, 0]]);
}
}
module profile() {
polish2d(round_r) profile_raw();
}
module bracket_body() {
translate([0, width / 2, 0])
rotate([90, 0, 0])
polish_extrude(width, chamfer) profile();
}
module bracket() {
// The clamp thread is cut straight into the top jaw.
ScrewHole(bolt_size, jaw + 2, [bolt_x, 0, top0 - 1], [0, 0, 0],
tolerance = tolerance)
bracket_body();
}
// ---- Clamp bolt ----
// An octagonal knob, the thread, and a flat tip narrower than the thread so the bolt
// can be screwed through the jaw. Modelled knob down, the way it prints.
module clamp_bolt() {
polish_extrude(knob_h, chamfer) polish2d(2) circle(d = knob_d, $fn = 8);
translate([0, 0, knob_h - 0.01])
ScrewThread(bolt_size, bolt_len, tolerance = tolerance);
translate([0, 0, knob_h + bolt_len - 0.01])
cylinder(d = bolt_size - 3, h = pad_h, $fn = smoothness);
}
// ---- Core plug ----
// A nose that fits any core, a 45 degree shoulder that centres the core, a flange and
// the pin that drops into the slot. Modelled nose down, the way it prints.
module plug() {
c = 1;
z1 = plug_len;
z2 = z1 + shoulder_h;
z3 = z2 + flange_t;
rotate_extrude($fn = smoothness)
polygon([[0, 0], [plug_tip_d / 2, 0], [plug_d / 2, z1], [flange_d / 2, z2],
[flange_d / 2, z3], [pin_d / 2, z3], [pin_d / 2, plug_h - c],
[pin_d / 2 - c, plug_h], [0, plug_h]]);
}
// ---- Preview only: worktop and roll ----
module worktop() {
w = 2 * bracket_y + 120;
if (hook_height > 0) {
// A lip at the edge, the worktop thinner behind it.
translate([0, -w / 2, -T]) cube([grip_depth - hook_t - 1, w, T]);
thin = max(4, T - hook_height - 1);
translate([0, -w / 2, -thin]) cube([grip_depth + 90, w, thin]);
} else {
translate([0, -w / 2, -T]) cube([grip_depth + 90, w, T]);
}
}
module roll() {
translate([seat_x, roll_length / 2, pin_z]) rotate([90, 0, 0])
difference() {
cylinder(d = roll_diameter, h = roll_length, $fn = smoothness);
translate([0, 0, -1]) cylinder(d = core_d, h = roll_length + 2, $fn = smoothness);
}
}
// ---- Layout ----
if (part == "bracket") {
// On its side, ready to print. Print it twice, left and right are the same.
color(color_print) translate([0, 0, width / 2]) rotate([90, 0, 0]) bracket();
} else if (part == "bolt") {
color(color_print) clamp_bolt();
} else if (part == "plug") {
color(color_print) plug();
} else {
color("BurlyWood") worktop();
color("WhiteSmoke") roll();
for (s = [-1, 1]) {
color(color_print) translate([0, s * bracket_y, 0]) bracket();
// Bolt from the top, its tip on the worktop.
color("DimGray") translate([bolt_x, s * bracket_y, knob_h + bolt_len + pad_h])
rotate([180, 0, 0]) clamp_bolt();
}
color("DimGray") translate([seat_x, roll_length / 2 - contact_z, pin_z])
rotate([-90, 0, 0]) plug();
color("DimGray") translate([seat_x, -(roll_length / 2 - contact_z), pin_z])
rotate([90, 0, 0]) plug();
}
Comments
Context, not a score. Comments never move a model up or down the results.No comments yet
Printed it? A note about your material and the clearance you landed on saves the next person a failed print.
