Rendering these values. This takes a few seconds.
Download STL
Read this model as text

A Gridfinity bin drawn as a single continuous wall, so it can be printed in spiral vase mode.

The image shows the solid OpenSCAD renders at enable_holes true · enable_zsnap false · enable_lip true · enable_scoop_chamfer true · enable_funnel true · enable_inset true · enable_pinch true.

MIT

Gridfinity bin for vase mode

remixcad/gridfinity-spiral-vase@1.0.0

Remix this
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.

Sign in to report

0reused0 remixes · 0 imports0downloads1.0.0latest

Parameters

from source

Showing the render for these values.

Description

What is this

A Gridfinity bin drawn as a single continuous wall, so it can be printed in spiral vase mode.

What you use it for

The fastest and thinnest way to fill a drawer with bins. Slice it in vase mode and the printer lays down one unbroken spiral, with no infill and no travel moves.

Design notes

Vase mode prints exactly one wall, so the geometry has to be built around your nozzle width and layer height rather than sliced to them. That is why both are parameters here and not slicer settings. Set them to what you will actually print with, or the walls will not meet.

Parameters

nozzle and layer have to match your printer. gridx, gridy and gridz set the size in the usual Gridfinity units, and n_divx splits the inside.

Use with your agent
{
  "mcpServers": {
    "remixcad": {
      "type": "http",
      "url": "https://remix-cad.vercel.app/api/mcp"
    }
  }
}
get_model("remixcad/gridfinity-spiral-vase@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
Parameters of Gridfinity bin for vase mode, with the ranges the source declares
NameDefaultRangeUnitDescription
type1
nozzle0.6mmNozzle width, which sets the single wall thickness.
layer0.35mmLayer height you will print at.
bottom_layer3
gridx1unitsWidth in grid units of 42 mm.
gridy1
gridz6
n_divx2
enable_holestrue
enable_zsnapfalse
enable_liptrue
enable_scoop_chamfertrue
enable_funneltrue
enable_insettrue
enable_pinchtrue
gridz_define0
style_tab0
style_base0
a_tab40
d_hole26
r_c10.8

Lineage

0 models depend on this one

Only what this model came from and what came from it.

Remix, the code changed Import, used unchanged and pinned The version you are on Withdrawn, still recorded

License

Whoever downloads this takes these obligations along
  • remixcad/gridfinity-spiral-vase@1.0.0 is MIT
  • remixcad/gridfinity-core@1.0.0 is MIT
    • @File gridfinity-baseplate.scad @Brief Functions and standards to make a SINGLE gridfinity baseplate. @Copyright Arthur Moore 2025 MIT License @WARNING: Where possible, `use` thise file. Constants may change between minor versions. /
  • remixcad/threads@1.0.0 is CC0

Source

main.scad is the entrypoint
main.scad
// ===== INFORMATION ===== //
/*
 IMPORTANT: rendering will be better in development builds and not the official release of OpenSCAD, but it makes rendering only take a couple seconds, even for comically large bins.


https://github.com/kennetek/gridfinity-rebuilt-openscad

*/

include <remixcad/gridfinity-core/src/core/standard.scad>
use <remixcad/gridfinity-core/src/core/gridfinity-rebuilt-utility.scad>
use <remixcad/gridfinity-core/src/core/base.scad>
use <remixcad/gridfinity-core/src/core/wall.scad>
use <remixcad/gridfinity-core/src/helpers/generic-helpers.scad>
use <remixcad/gridfinity-core/src/helpers/grid.scad>
use <remixcad/gridfinity-core/src/helpers/shapes.scad>

// ===== PARAMETERS ===== //

/* [Special Variables] */
$fa = 8;
$fs = 0.25;

/* [Bin or Base] */
type = 1; // [0:bin, 1:base]

/* [Printer Settings] */
// extrusion width (walls will be twice this size)
nozzle = 0.6;
// slicer layer size
layer = 0.35;
// number of base layers on build plate
bottom_layer = 3;

/* [General Settings] */
// number of bases along x-axis
gridx = 1;
// number of bases along y-axis
gridy = 1;
// bin height. See bin height information and "gridz_define" below.
gridz = 6;
// number of compartments along x-axis
n_divx = 2;

/* [Toggles] */
// toggle holes on the base for magnet
enable_holes = true;
// round up the bin height to match the closest 7mm unit
enable_zsnap = false;
// toggle the lip on the top of the bin that allows stacking
enable_lip = true;
// chamfer inside bin for easy part removal
enable_scoop_chamfer = true;
// funnel-like features on the back of tabs for fingers to grab
enable_funnel = true;
// front inset (added for strength when there is a scoop)
enable_inset = true;
// "pinches" the top lip of the bin, for added strength
enable_pinch = true;

/* [Styles] */
// determine what the variable "gridz" applies to based on your use case
gridz_define = 0; // [0:gridz is the height of bins in units of 7mm increments - Zack's method,1:gridz is the internal height in millimeters, 2:gridz is the overall external height of the bin in millimeters]
// how tabs are implemented
style_tab = 0; // [0:continuous, 1:broken, 2:auto, 3:right, 4:center, 5:left, 6:none]
// where to put X cutouts for attaching bases
// selecting none will also disable crosses on bases
style_base = 0; // [0:all, 1:corners, 2:edges, 3:auto, 4:none]

// tab angle
a_tab = 40;

// ===== IMPLEMENTATION ===== //

color("tomato")
if (type != 0) gridfinityBaseVase(2*nozzle, d_bottom); // Generate a single base
else gridfinityVase(); // Generate the bin


// ===== CONSTRUCTION ===== //

//Deprecated Variables
d_hole = 26;  // center-to-center distance between holes
r_c1 = 0.8;  // lower base chamfer "radius"
d_wall2 = BASE_TOP_RADIUS-r_c1-0.25*sqrt(2);
module profile_wall(h){ _profile_wall(h); }
//End Deprecated Variables

d_bottom = layer*(max(bottom_layer,1));
x_l = l_grid/2;

dht = (gridz_define == 0) ? 7 * gridz
    : (gridz_define == 1) ? gridz + BASE_HEIGHT
    : gridz - (enable_lip ? 3.8 : 0);
d_height = (enable_zsnap?((abs(dht)%7==0)?dht:dht+7-abs(dht)%7):dht)-BASE_HEIGHT;

d_fo1 = 2*+BASE_TOP_RADIUS;

f2c = sqrt(2)*(sqrt(2)-1); // fillet to chamfer ratio
me = ((gridx*l_grid-0.5)/n_divx)-nozzle*4-d_fo1-12.7-4;
m = min(TAB_WIDTH_NOMINAL/1.8 + max(0,me), TAB_WIDTH_NOMINAL/1.25);
d_ramp = f2c*(l_grid*((d_height-2)/7+1)/12-r_f2)+d_wall2;
d_edge = ((gridx*l_grid-0.5)/n_divx-TAB_WIDTH_NOMINAL-d_fo1)/2;
n_st = gridz <= 3 ? 6 : d_edge < 2 && style_tab != 0 && style_tab != 6 ? 1 : style_tab == 1 && n_divx <= 1? 0 : style_tab;

n_x = (n_st==0?1:n_divx);
spacing = (gridx*l_grid-0.5)/(n_divx);
shift = n_st==3?-1:n_st==5?1:0;
shiftauto = function (a,b) n_st!=2?0:a==1?-1:a==b?1:0;

xAll = function (a,b) true;
xCorner = function(a,b) (a==1||a==gridx)&&(b==1||b==gridy);
xEdge = function(a,b) (a==1)||(a==gridx)||(b==1)||(b==gridy);
xAuto = function(a,b) xCorner(a,b) || (a%2==1 && b%2 == 1);
xNone = function(a,b) false;
xFunc = [xAll, xCorner, xEdge, xAuto, xNone];


module gridfinityVase() {
    $dh = d_height;
    difference() {
        union() {
            difference() {
                block_vase_base();

                if (n_st != 6)
                transform_style()
                transform_vtab_base((n_st<2?gridx*l_grid/n_x-0.5-d_fo1:TAB_WIDTH_NOMINAL)-nozzle*4)
                block_tab_base(-nozzle*sqrt(2));
            }

            if (enable_scoop_chamfer)
            intersection() {
                block_vase();
                translate([0,gridy*l_grid/2-0.25-d_wall2/2,d_height/2+0.1])
                cube([gridx*l_grid,d_wall2,d_height-0.2],center=true);
            }

            if (enable_funnel && gridz > 3) {
                x_elements = n_st==0?n_divx>1?n_divx:gridx:1;
                element_dimension = (gridx*l_grid-d_fo1)/x_elements;
                pattern_grid([x_elements, 1], [element_dimension, element_dimension], true, true)
                transform_funnel()
                block_funnel_outside();
            }

            if (n_divx > 1) {
                element_dimension = (gridx*l_grid-0.5)/n_divx;
                pattern_grid([n_divx-1, 1], [element_dimension, element_dimension], true, true)
                block_divider();
            }

            if (n_divx < 1) {
                x_elements = n_st == 0 ? n_divx>1 ? n_divx-1 : gridx-1 : 1;
                element_dimension = (gridx*l_grid-d_fo1)/(n_divx>1 ? n_divx : gridx);
                pattern_grid([x_elements, 1], [element_dimension, element_dimension], true, true)
                block_tabsupport();
            }
        }

        if (enable_funnel && gridz > 3) {
            x_element = n_st==0?n_divx>1?n_divx:gridx:1;
            element_dimension = (gridx*l_grid-d_fo1)/x_element;
            pattern_grid([x_element, 1], [element_dimension, element_dimension], true, true)
            transform_funnel()
            block_funnel_inside();
        }

        if (!enable_lip)
        translate([0,0,1.5*d_height])
        cube([gridx*l_grid,gridy*l_grid,d_height], center=true);

        block_x();
        block_inset();
        if (enable_pinch)
        block_pinch(d_height);

        if (bottom_layer <= 0)
        translate([0,0,-50+layer+0.01])
        cube([gridx*l_grid*10,gridy*l_grid*10,100], center=true);
    }
}

module gridfinityBaseVase(wall_thickness, bottom_thickness) {
    difference() {
        union() {
            base_outer_shell(wall_thickness, bottom_thickness);
            intersection() {
                pattern_circular(4){
                    rotate([0,0,45])
                    translate([-wall_thickness/2, 3, 0])
                    cube([wall_thickness, l_grid, BASE_PROFILE_HEIGHT]);

                    if (enable_holes) {
                        block_magnet_blank(wall_thickness);
                    }
                }
                base_solid();
            }
            if (style_base != 4) {
                translate([0, 0, BASE_PROFILE_HEIGHT])
                linear_extrude(bottom_thickness)
                profile_x(0.1);
            }
        }
        if (enable_holes) {
            pattern_circular(4)
            block_magnet_blank(0, false);
        }

        // magic slice
        // Tricks slicer into not ignoring the center.
        rotate([0, 0, 90])
        translate([0, 0, bottom_thickness])
        cube([0.005, 2*l_grid, 2*BASE_PROFILE_HEIGHT]);
    }
}

module block_magnet_blank(o = 0, half = true) {
    magnet_radius = MAGNET_HOLE_RADIUS + o;

    translate([d_hole/2, d_hole/2, 0.1])
    difference() {
        hull() {
            cylinder(r = magnet_radius, h = MAGNET_HOLE_DEPTH*2, center = true);
            cylinder(r = magnet_radius-(BASE_PROFILE_HEIGHT+0.1-MAGNET_HOLE_DEPTH), h = (BASE_PROFILE_HEIGHT+0.1)*2, center = true);
        }
        if (half)
        mirror([0,0,1])
        cylinder(r=magnet_radius*2, h = (BASE_PROFILE_HEIGHT+0.1)*4);
    }
}

module block_pinch(height_mm) {
    assert(is_num(height_mm));

    translate([0, 0, -BASE_PROFILE_HEIGHT])
    block_wall(gridx, gridy, l_grid) {
        translate([d_wall2-nozzle*2-TOLLERANCE*2,0,0])
        profile_wall(height_mm);
    }
}

module block_tabsupport() {
    intersection() {
        translate([0,0,0.1])
        block_vase(d_height*4);

        cube([nozzle*2, gridy*l_grid, d_height*3], center=true);

        transform_vtab_base(gridx*l_grid*2)
        block_tab_base(-nozzle*sqrt(2));
    }
}

module block_divider() {
    difference() {
        intersection() {
            translate([0,0,0.1])
            block_vase();
            cube([nozzle*2, gridy*l_grid, d_height*2], center=true);
        }

        if (n_st == 0) block_tab(0.1);
        else block_divider_edgecut();

        // cut divider clearance on negative Y side
        translate([-gridx*l_grid/2,-(gridy*l_grid/2-0.25),0])
        cube([gridx*l_grid,nozzle*2+0.1,d_height*2]);

        // cut divider clearance on positive Y side
        mirror([0,1,0])
        if (enable_scoop_chamfer)
            translate([-gridx*l_grid/2,-(gridy*l_grid/2-0.25),0])
            cube([gridx*l_grid,d_wall2+0.1,d_height*2]);
        else block_divider_edgecut();

        // cut divider to have clearance with scoop
        if (enable_scoop_chamfer)
        transform_scoop()
        offset(delta = 0.1)
        polygon([
            [0,0],
            [d_ramp,d_ramp],
            [d_ramp,d_ramp+nozzle/sqrt(2)],
            [-nozzle/sqrt(2),0]
        ]);
    }

    // divider slices
    difference() {
        for (i = [0:(d_height-d_bottom)/(layer)]) {

        if (2*i*layer < d_height-layer/2-d_bottom-0.1)
        mirror([0,1,0])
        translate([0,(gridy*l_grid/2-0.25-nozzle)/2,layer/2+d_bottom+2*i*layer])
        cube([nozzle*2-0.01,gridy*l_grid/2-0.25-nozzle,layer],center=true);

        if ((2*i+1)*layer < d_height-layer/2-d_bottom-0.1)
        translate([0,(gridy*l_grid/2-0.25-nozzle)/2,layer/2+d_bottom+(2*i+1)*layer])
        cube([nozzle*2-0.01,gridy*l_grid/2-0.25-nozzle,layer],center=true);

        }

        // divider slices cut to tabs
        if (n_st == 0)
        transform_style()
        transform_vtab_base((n_st<2?gridx*l_grid/n_x-0.5-d_fo1:TAB_WIDTH_NOMINAL)-nozzle*4)
        block_tab_base(-nozzle*sqrt(2));
    }
}

module block_divider_edgecut() {
    translate([-50,-gridy*l_grid/2+0.25,0])
    rotate([90,0,90])
    linear_extrude(100)
    offset(delta = 0.1)
    mirror([1,0,0])
    translate([-BASE_TOP_RADIUS,0,0])
    profile_wall($dh);
}

module transform_funnel() {
    if (me > 6 && enable_funnel && gridz > 3 && n_st != 6)
    transform_style()
    render()
    children();
}

module block_funnel_inside() {
    intersection() {
        block_tabscoop(m-nozzle*3*sqrt(2), 0.003, nozzle*2, 0.01);
        block_tab(0.1);
    }
}

module block_funnel_outside() {
    intersection() {
        difference() {
            block_tabscoop(m, 0, 0, 0);
            block_tabscoop(m-nozzle*4*sqrt(2), 0.003, nozzle*2, -1);
        }
        block_tab(-nozzle*sqrt(2)/2);
    }
}

module block_vase_base() {
    difference() {
        // base
        translate([0,0,-BASE_PROFILE_HEIGHT]) {
            translate([0,0,-0.1])
            color("firebrick")
            block_bottom(d_bottom, gridx, gridy, l_grid);
            block_wall(gridx, gridy, l_grid) {
                if (enable_lip) profile_wall($dh);
                else profile_wall2($dh);
            }
        }

        // magic slice
        rotate([0,0,90])
        mirror([0,1,0])
        translate([0,0,d_bottom+0.001])
        cube([0.001,l_grid*gridx,d_height+d_bottom*2]);
    }

    // scoop piece
    if (enable_scoop_chamfer)
    transform_scoop()
    polygon([
        [0,0],
        [d_ramp,d_ramp],
        [d_ramp,d_ramp+0.6/sqrt(2)],
        [-0.6/sqrt(2),0]
    ]);

    // outside tab cutter
    if (n_st != 6)
    translate([-(n_x-1)*spacing/2,0,0])
    for (i = [1:n_x])
    translate([(i-1)*spacing,0,0])
    translate([shiftauto(i,n_x)*d_edge + shift*d_edge,0,0])
    intersection() {
        block_vase();
        transform_vtab_base(n_st<2?gridx*l_grid/n_x-0.5-d_fo1:TAB_WIDTH_NOMINAL)
        profile_tab();
    }
}

module block_inset() {
    ixx = (gridx*l_grid-0.5)/2;
    iyy = d_height/2.1;
    izz = sqrt(ixx^2+iyy^2)*tan(40);
    if (enable_scoop_chamfer && enable_inset)
    difference() {
        intersection() {
            rotate([0,90,0])
            translate([-iyy,0,0])
            block_inset_sub(iyy, gridx*l_grid, 45);

            rotate([0,90,0])
            translate([-iyy,0,0])
            rotate([0,90,0])
            block_inset_sub(ixx, d_height*2, 45);
        }

        mirror([0,1,0])
        translate([-gridx*l_grid/2,-(gridy*l_grid-0.5)/2+d_wall2-2*nozzle,0])
        cube([gridx*l_grid,izz,d_height*2]);
    }
}

module block_inset_sub(x, y, ang) {
    translate([0,(gridy*l_grid-0.5)/2+BASE_TOP_RADIUS,0])
    mirror([0,1,0])
    linear_extrude(y,center=true)
    polygon([[-x,0],[x,0],[0,x*tan(ang)]]);
}

module transform_style() {
    translate([-(n_x-1)*spacing/2,0,0])
    for (i = [1:n_x])
    translate([(i-1)*spacing,0,0])
    translate([shiftauto(i,n_x)*d_edge + shift*d_edge,0,0])
    children();
}

module block_flushscoop() {
    translate([0,gridy*l_grid/2-d_wall2-nozzle/2-1,d_height/2])
    linear_extrude(d_height)
    union() {
        copy_mirror([1,0,0])
        polygon([[0,0],[gridx*l_grid/2-BASE_TOP_RADIUS,0],[gridx*l_grid/2-BASE_TOP_RADIUS,1],[gridx*l_grid/2-BASE_TOP_RADIUS-r_c1*5,d_wall2-nozzle*2+1],[0,d_wall2-nozzle*2+1]]);
    }

    transform_scoop()
    polygon([[0,0],[d_ramp,0],[d_ramp,d_ramp]]);
}

module profile_tab() {
    union() {
        copy_mirror([0,1,0])
        polygon([[0,0],[d_tabh*cos(a_tab),0],[d_tabh*cos(a_tab),d_tabh*sin(a_tab)]]);
    }
}

module profile_tabscoop(m) {
    polyhedron([[m/2,0,0],[0,-m,0],[-m/2,0,0],[0,0,m]], [[0,2,1],[1,2,3],[0,1,3],[0,3,2]]);
}

module block_tabscoop(a=m, b=0, c=0, d=-1) {
    translate([0,d_tabh*cos(a_tab)-l_grid*gridy/2+0.25+b,0])
    difference() {
        translate([0,0,-d_tabh*sin(a_tab)*2+d_height+2.1])
        profile_tabscoop(a);

        translate([-gridx*l_grid/2,-m,-m])
        cube([gridx*l_grid,m-d_tabh*cos(a_tab)+0.005+c,d_height*20]);

        if (d >= 0)
        translate([0,0,-d_tabh*sin(a_tab)+d_height+m/2+d+2.1])
        cube([gridx*l_grid,gridy*l_grid,m],center=true);
    }
}

module transform_vtab(a=0,b=1) {
    transform_vtab_base(gridx*l_grid/b-0.5-d_fo1+a)
    children();
}

module transform_vtab_base(a) {
    translate([0,d_tabh*cos(a_tab)-l_grid*gridy/2+0.25,-d_tabh*sin(a_tab)+d_height+2.1])
    rotate([90,0,270])
    linear_extrude(a, center=true)
    children();
}

module block_tab(del, b=1) {
    transform_vtab(-nozzle*4, b)
    block_tab_base(del);
}

module block_tab_base(del) {
    offset(delta = del)
    union() {
        profile_tab();
        translate([d_tabh*cos(a_tab),-d_tabh*sin(a_tab),0])
        square([l_grid,d_tabh*sin(a_tab)*2]);
    }
}

module transform_scoop() {
    intersection() {
        block_vase();
        translate([0,gridy*l_grid/2-d_ramp,layer*max(bottom_layer*1)])
        rotate([90,0,90])
        linear_extrude(2*l_grid*gridx,center=true)
        children();
    }
}

module block_vase(h = d_height*2) {
    translate([0,0,-0.1])
    linear_extrude(h)
    rounded_square([gridx*l_grid-0.5-nozzle, gridy*l_grid-0.5-nozzle], BASE_TOP_RADIUS+0.01-nozzle/2, center=true);
}

module profile_x(x_f = 3) {
    difference() {
        square([x_l,x_l],center=true);

        pattern_circular(4)
        translate([0,nozzle*sqrt(2),0])
        rotate([0,0,45])
        translate([x_f,x_f,0])
        minkowski() {
            square([x_l,x_l]);
            circle(x_f);
        }
    }
}

module block_x() {
    translate([-(gridx-1)*l_grid/2,-(gridy-1)*l_grid/2,0])
    for (i = [1:gridx])
    for (j = [1:gridy])
    if (xFunc[style_base](i,j))
    translate([(i-1)*l_grid,(j-1)*l_grid,0])
    block_x_sub();
}

module block_x_sub() {
    linear_extrude(d_bottom*2+0.01,center=true)
    offset(0.05)
    profile_x();
}

module block_wall(gx, gy, l) {
    translate([0, 0, BASE_PROFILE_HEIGHT])
    sweep_rounded([gx*l-2*BASE_TOP_RADIUS-0.5-0.001, gy*l-2*BASE_TOP_RADIUS-0.5-0.001])
    children();
}

module block_bottom( h = 2.2, gx, gy, l ) {
    translate([0, 0, BASE_PROFILE_HEIGHT + 0.1])
    linear_extrude(h)
    rounded_square([gx*l-0.5-d_wall/4, gy*l-0.5-d_wall/4], BASE_TOP_RADIUS+0.01, center=true);
}

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.

Sign in to comment