Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ For inserting magnets, check out [the jig](#jig).
- [None](#none)
- [Integer Fraction](#integer-fraction)
- [Dynamic](#dynamic)
- [Lightweight](#lightweight)
- [Wall thickness](#wall-thickness)
- [Bottom lip](#bottom-lip)
- [Corner radius](#corner-radius)
- [Alignment](#alignment)
- [Custom cell size](#custom-cell-size)
Expand Down Expand Up @@ -462,6 +465,39 @@ If you wish to only create larger cells, not smaller cells, you can set the mini
<!-- openscad -o docs/images/filler-dynamic-expand-always.png --camera=0,0,0,40,0,10,150 -D plate_size='[120, 42]' -D filler_x=2 -D filler_minimum_size=[42,42] -->
<img src="docs/images/filler-dynamic-expand-always.png" alt="Dynamic filler always expanded" />

## Lightweight

`hollow` skeletonizes the baseplate: instead of solid material between the cells, only a thin wall following the Gridfinity profile is printed. Neighbouring cells share a hollow channel, open towards the bed and closed at the top by a self-supporting 45° roof. Without magnets this roughly halves filament use and print time — a 4x4 plate drops from 20.6cm³ to 10.5cm³.

<!-- openscad -o docs/images/lightweight.png --camera=0,0,0,150,0,25,260 -D plate_size='[84, 84]' -D hollow=true -D connector_intersection_puzzle=false -D numbering=false -->
<img src="docs/images/lightweight.png" alt="Lightweight baseplate, seen from below" />

The same plate without `hollow`, also seen from below:

<!-- openscad -o docs/images/lightweight-off.png --camera=0,0,0,150,0,25,260 -D plate_size='[84, 84]' -D hollow=false -D connector_intersection_puzzle=false -D numbering=false -->
<img src="docs/images/lightweight-off.png" alt="Standard baseplate, seen from below" />

The top surface, the outer wall of each segment and the puzzle connectors stay solid, so segments link up the same way and the plate looks unchanged from above.

> [!NOTE]
> Hollow mode is incompatible with magnets, `solid_base` and the click latch — there is no material left to hold them, and GridFlock refuses to render that combination.

### Wall thickness

`hollow_wall` sets the wall thickness. It is a *horizontal* thickness, which is what the slicer sees on each layer, so setting it to your nozzle diameter prints the plate as a single wall with no infill. The default 0.8mm is one wall on a 0.8mm nozzle, two to three on a 0.4mm nozzle.

<!-- openscad -o docs/images/lightweight-wall.png --camera=0,0,0,150,0,25,260 -D plate_size='[84, 84]' -D hollow=true -D hollow_wall=1.6 -D connector_intersection_puzzle=false -D numbering=false -->
<img src="docs/images/lightweight-wall.png" alt="Lightweight baseplate with thicker walls" />

### Bottom lip

The Gridfinity profile ends in a 0.7mm lip that slopes back inwards. It is not functionally required; `remove_bottom_lip` replaces it with a straight continuation of the vertical section. This saves a little filament, removes the overhang where two cells meet, and gives the first layer a flat contact area instead of a knife edge.

<!-- openscad -o docs/images/bottom-lip.png --camera=0,0,0,150,0,25,140 -D plate_size='[42, 42]' -D remove_bottom_lip=true -D connector_intersection_puzzle=false -D numbering=false -->
<img src="docs/images/bottom-lip.png" alt="Baseplate with the bottom lip removed" />

It can be used on its own, and is always enabled in hollow mode, where the inward slope would taper the horizontally measured walls to nothing at the first layer.

## Corner radius

GridFlock generates plates with a corner radius of 4mm by default. This matches the Gridfinity corner radius, so when the plate size is a perfect multiple of 42, there is no gap.
Expand Down
Binary file added docs/images/bottom-lip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/lightweight-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/lightweight-wall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/lightweight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion editor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ description-extra-html = """
<br>Apr 8: Glue-in magnets now have a release slot, which can be disabled by setting magnet_release_width to 0.
<span style="display: none" class="alert alert-warning" data-display-condition="click && click_style == 0">The click latch can deform over time, especially if made of PLA. Prefer other materials like PETG.</span>
<span style="display: none" class="alert alert-warning" data-display-condition="click && click_style == 1">The click latch can deform over time if used with non-ClickGroove bins, especially if made of PLA. Prefer other materials like PETG if you plan to use such bins.</span>
<span style="display: none" class="alert alert-danger" data-display-condition="hollow && (magnets || solid_base > 0 || click)">Hollow mode only leaves a thin wall around the gridfinity profile, so it cannot be combined with magnets, a solid base or the click latch.</span>
<span style="display: none" class="alert alert-warning" data-display-condition="connector_intersection_puzzle && connector_edge_puzzle">You have enabled both the edge and intersection puzzle connectors. While technically possible, it is better to focus on and tune one connector type.</span>
"""
umami-track-render = []
umami-track-export = ["magnets", "magnet_style", "connector_intersection_puzzle", "connector_edge_puzzle", "bed_size", "click", "click_style", "filler_x", "filler_y", "stacked_print", "adapter_mode"]
umami-track-export = ["magnets", "magnet_style", "connector_intersection_puzzle", "connector_edge_puzzle", "bed_size", "click", "click_style", "filler_x", "filler_y", "stacked_print", "adapter_mode", "hollow"]

[model.param-metadata.bed_size.presets]
text = "Printer Presets"
Expand Down Expand Up @@ -129,6 +130,18 @@ display-condition = {js = "filler_x == 1 || filler_y == 1"}
help-link = "https://github.com/yawkat/GridFlock/blob/main/README.md#dynamic"
display-condition = {js = "filler_x == 2 || filler_y == 2"}

[model.tab-metadata."Lightweight"]
control-boolean = "hollow"
description-html = "Skeletonize the baseplate so only thin walls are printed. This roughly halves filament use and print time, but leaves no material for magnets, a solid base or the click latch."
help-link = "https://github.com/yawkat/GridFlock/blob/main/README.md#lightweight"
[model.param-metadata.hollow]
display-condition = {fixed = false}
[model.param-metadata.hollow_wall]
help-link = "https://github.com/yawkat/GridFlock/blob/main/README.md#wall-thickness"
[model.param-metadata.remove_bottom_lip]
help-link = "https://github.com/yawkat/GridFlock/blob/main/README.md#bottom-lip"
display-condition = {js = "!hollow"}

[model.tab-metadata."Numbering"]
help-link = "https://github.com/yawkat/GridFlock/blob/main/README.md#numbering"
collapsed = true
Expand Down
77 changes: 71 additions & 6 deletions gridflock.scad
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ filler_minimum_size = [15, 15];
// Padding alignment. The first value is the x direction (east/west), the second value the y direction (north/south). When padding is added to the build plate, this alignment is used to distribute it. A lower value will move the grid towards the west/south direction, adding more padding to the east/north
alignment = [0.5, 0.5]; // [0:0.1:1]

/* [Lightweight] */

// Skeletonize the baseplate: instead of solid material between the cells, only a thin wall following the gridfinity profile is printed. This roughly halves filament use and print time. Incompatible with magnets, a solid base and the click latch
hollow = false;
// Wall thickness in hollow mode. This is a horizontal thickness, which is what the slicer sees on each layer, so a value matching your nozzle diameter prints as a single wall
hollow_wall = 0.8; // 0.05
// Remove the bottom lip of the gridfinity profile, extending the vertical section of the profile straight down instead. The lip is not functionally required; removing it saves filament, removes the overhang between neighbouring cells, and widens the first layer. Always enabled in hollow mode
remove_bottom_lip = false;

/* [Numbering] */

// Enable numbering of the segements, embossed in a corner
Expand Down Expand Up @@ -256,6 +265,10 @@ assert(!magnets || magnet_frame_style != _MAGNET_SOLID || magnet_style != _MAGNE

assert(!thumbscrews || solid_base > 0 || (magnets && magnet_frame_style == _MAGNET_SOLID), "Thumbscrew holes require some sort of solid base, such as magnet_style solid, or an explicit solid_base.");

assert(!hollow || (!magnets && solid_base == 0 && !click), "Hollow mode only leaves a thin wall around the gridfinity profile, so there is nothing left to hold magnets, a solid base or a click latch.");

assert(hollow_wall > 0, "hollow_wall must be positive.");

_OPENGRID_LITE = 1;
_OPENGRID_DIRECTIONAL = 2;
_OPENGRID_VERTICAL = 4;
Expand All @@ -276,6 +289,12 @@ _magnet_extraction_dim_negative = [magnet_release_width, magnet_diameter/2];
_profile_height_raw = 4.65;
// for stacked prints, we cut off a sliver at the top to get a better contact area
_profile_height = _profile_height_raw - top_slice - (stacked_print ? stacked_print_slice : 0);
// The lip slopes inward, so in hollow mode it would taper the horizontally measured shell to nothing
_remove_bottom_lip = remove_bottom_lip || hollow;
// Horizontal distance from the sweep path to the vertical (waist) section of the gridfinity profile
_profile_waist_offset = BASEPLATE_INNER_RADIUS + _BASEPLATE_PROFILE[1].x;
// Height of the bottom lip of the gridfinity profile
_profile_lip_height = _BASEPLATE_PROFILE[1].y;
// height of the magnet level
_magnet_level_height = (magnet_style != _MAGNET_GLUE_TOP ? magnet_top : 0) + (magnet_style != _MAGNET_GLUE_BOTTOM ? magnet_bottom : 0) + magnet_height;
// total height of the non-bin levels (magnets, solid base). These are placed at z<0
Expand Down Expand Up @@ -373,6 +392,24 @@ module cutter(size, below=0) {
}
}
}
// Extend the waist straight down, removing the lip and the overhang between neighbouring cells
if (_remove_bottom_lip) {
floor = -below - 0.001;
translate([0, 0, floor]) linear_extrude(_profile_lip_height - floor) offset(_profile_waist_offset)
square([size.x - BASEPLATE_OUTER_DIAMETER, size.y - BASEPLATE_OUTER_DIAMETER], center=true);
}
}

/**
* @Summary The interior volume of a cell that hollow mode removes, leaving a hollow_wall shell
* @param unit_size Size of the cell, in grid units, in each direction
*/
module cell_core(unit_size=[1, 1]) {
size = [BASEPLATE_DIMENSIONS.x*unit_size.x, BASEPLATE_DIMENSIONS.y*unit_size.y];
difference() {
translate([-size.x/2, -size.y/2, -0.001]) cube([size.x, size.y, _total_height + 0.002]);
cutter([size.x + hollow_wall*2, size.y + hollow_wall*2]);
}
}

/**
Expand Down Expand Up @@ -1024,6 +1061,33 @@ function compute_segment_size(trace, padding) = [
BASEPLATE_DIMENSIONS.y * cumulate(trace.y)[len(trace.y)] + padding[_NORTH] + padding[_SOUTH],
];

/**
* @Summary Decide how a single cell is rendered, from cell_override
* @param index Index of the cell within the segment
*/
function cell_style(index, global_cell_index, global_cell_count) = let(
seq_index = (index.y + global_cell_index.y) * ceil(global_cell_count.x) + (index.x + global_cell_index.x)
) len(cell_override) > seq_index ? cell_override[seq_index] : _CELL_STYLE_NORMAL;

/**
* @Summary The union of the cell cores, clipped so the outer wall and its puzzle connectors stay solid
*/
module segment_core(trace, size, padding, connector, global_cell_index, global_cell_count) {
last = [len(trace.x)-1, len(trace.y)-1];
intersection() {
translate([0, 0, -_extra_height]) linear_extrude(height = _total_height)
offset(-hollow_wall) segment_rectangle(size, connector, include_wall=false);
union() {
for (ix = [0:1:last.x]) for (iy = [0:1:last.y]) navigate_cell(size, trace, padding, [ix, iy]) {
cell_size = [trace.x[ix], trace.y[iy]];
if (cell_style([ix, iy], global_cell_index, global_cell_count) == _CELL_STYLE_NORMAL) {
cell_core(cell_size);
}
}
}
}
}

/**
* @Summary Model a segment, which is piece of the plate without breaks
* @param trace The cell sizes, in grid units, on each axis
Expand Down Expand Up @@ -1061,21 +1125,20 @@ module segment(trace=[[1], [1]], padding=[0, 0, 0, 0], connector=[false, false,
for (ix = [0:1:last.x]) for (iy = [0:1:last.y]) navigate_cell(size, trace, padding, [ix, iy]) {
cell_size = [trace.x[ix], trace.y[iy]];

seq_index = (iy + global_cell_index.y) * ceil(global_cell_count.x) + (ix + global_cell_index.x);
cell_style = len(cell_override) <= seq_index ? _CELL_STYLE_NORMAL : cell_override[seq_index];
if (cell_style == _CELL_STYLE_NORMAL) {
style = cell_style([ix, iy], global_cell_index, global_cell_count);
if (style == _CELL_STYLE_NORMAL) {
at_edge = [iy == last.y, ix == last.x, iy == 0, ix == 0];
cell(
cell_size,
connector=[for (direction = [0:3]) connector[direction] && at_edge[direction]],
bottom_chamfer_takes=[for (direction = [0:3]) at_edge[direction] && !connector[direction] ? max(0, bottom_chamfer[direction] - padding[direction]) : 0]
);
} else if (cell_style == _CELL_STYLE_EMPTY) {
} else if (cell_style == _CELL_STYLE_SOLID) {
} else if (style == _CELL_STYLE_EMPTY) {
} else if (style == _CELL_STYLE_SOLID) {
dim = [BASEPLATE_DIMENSIONS.x * cell_size.x, BASEPLATE_DIMENSIONS.y * cell_size.y];
translate([-dim.x/2, -dim.y/2, -_extra_height]) cube([dim.x, dim.y, _total_height]);
} else {
assert(false, str("Unknown cell style: '", cell_style, "'"));
assert(false, str("Unknown cell style: '", style, "'"));
}
};
};
Expand Down Expand Up @@ -1222,6 +1285,8 @@ module segment(trace=[[1], [1]], padding=[0, 0, 0, 0], connector=[false, false,
translate([0, -size.y/2]) rotate([0, 0, 180]) horizontal_screws(_SOUTH, padding, trace = trace.x, connector = connector);
translate([-size.x/2, 0]) rotate([0, 0, 90]) horizontal_screws(_WEST, padding, trace = trace.y, connector = connector);
translate([size.x/2, 0]) rotate([0, 0, -90]) horizontal_screws(_EAST, padding, trace = trace.y, connector = connector);

if (hollow) segment_core(trace, size, padding, connector, global_cell_index, global_cell_count);
}
}

Expand Down
Loading