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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ For inserting magnets, check out [the jig](#jig).
- [Plate Wall](#plate-wall)
- [Top plate wall](#top-plate-wall)
- [Bottom plate wall](#bottom-plate-wall)
- [Variable wall height](#variable-wall-height)
- [Vertical Screws](#vertical-screws)
- [Screw dimensions](#screw-dimensions)
- [Screw locations](#screw-locations)
Expand Down Expand Up @@ -553,6 +554,16 @@ A bottom wall can be used to keep your grid from slipping off e.g. a table, with
<!-- openscad -o docs/images/wall-bottom.png --camera=0,0,0,140,0,20,200 -D plate_size='[84, 84]' -D magnets=false -D plate_wall_thickness='[1,1,1,1]' -D plate_wall_height='[0,5]' -->
<img src="docs/images/wall-bottom.png" alt="Bottom wall" />

### Variable wall height

The top and bottom wall height can also be adjusted individually for each corner using the `plate_wall_below` and `plate_wall_above` options. This allows you to create shelf-style grids. The following example has a plate wall thickness of 1mm on all sides except south, and a variable height of 20mm for the NW and NE corners.

<!-- openscad -o docs/images/wall-variable.png --camera=0,0,0,40,0,20,300 -D plate_size='[84, 84]' -D plate_wall_thickness='[1,1,0,1]' -D plate_wall_above='[0, 20, 20, 0]' -->
<img src="docs/images/wall-variable.png" alt="Shelf" />

> [!NOTE]
> Variable height does not currently render correctly with segmented baseplates.

## Vertical Screws

Vertical screws are inserted at cell intersections. They can be used to screw down the plate. Screws can be placed at various positions depending on use case.
Expand Down
Binary file modified docs/images/hscrews.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 modified docs/images/wall-bottom.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 modified docs/images/wall-top.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/wall-variable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 101 additions & 21 deletions gridflock.scad
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ top_chamfer = [0, 0, 0, 0]; // 0.1
plate_wall_thickness = [0, 0, 0, 0]; // 0.5
// Plate wall height. The first value is the height above the plate, the second value the height below the plate
plate_wall_height = [0, 0];
// Variable wall height, above the plate. Specified for each corner individually. Corners are SW, NW, NE, SE.
plate_wall_above = [0, 0, 0, 0];
// Variable wall height, below the plate. Specified for each corner individually. Corners are SW, NW, NE, SE.
plate_wall_below = [0, 0, 0, 0];

/* [Vertical Screws] */

Expand Down Expand Up @@ -890,13 +894,32 @@ module segment_corner(posy=_NORTH, posx=_WEST, connector=[false, false, false, f
/**
* This is an "inverted" quarter-circle that is used to punch out the corner of a rounded rectangle.
*/
module corner_punch() {
difference() {
module corner_punch(size) {
if (size.x != 0 && size.y != 0) scale(size) difference() {
square([1, 1]);
translate([1, 1]) circle(r=1);
}
}

module segment_corner_punch(size, connector, include_wall) {
// wall thickness to cut off, by side
wall_t = function (side) include_wall || connector[side] ? 0 : plate_wall_thickness[side];
// corner radius by side
bounds_min = [
-size.x/2 + wall_t(_WEST),
-size.y/2 + wall_t(_SOUTH)
];
bounds_max = [
size.x/2 - wall_t(_EAST),
size.y/2 - wall_t(_NORTH)
];
compute_radius = function (side) plate_corner_radius - wall_t(side);
if (!connector[_SOUTH] && !connector[_WEST]) translate(bounds_min) corner_punch([compute_radius(_WEST), compute_radius(_SOUTH)]);
if (!connector[_NORTH] && !connector[_WEST]) translate([bounds_min.x, bounds_max.y]) rotate(-90) corner_punch([compute_radius(_NORTH), compute_radius(_WEST)]);
if (!connector[_SOUTH] && !connector[_EAST]) translate([bounds_max.x, bounds_min.y]) rotate(90) corner_punch([compute_radius(_SOUTH), compute_radius(_EAST)]);
if (!connector[_NORTH] && !connector[_EAST]) translate(bounds_max) rotate(180) corner_punch([compute_radius(_EAST), compute_radius(_NORTH)]);
}

/**
* @Summary Draw the 2D shape of a segment, including rounded corners
* @param size The size of the segment
Expand All @@ -906,23 +929,12 @@ module corner_punch() {
module segment_rectangle(size, connector=[false, false, false, false], include_wall=false) {
// wall thickness to cut off, by side
wall_t = function (side) include_wall || connector[side] ? 0 : plate_wall_thickness[side];
// corner radius by side
compute_radius = function (side) max(0.01, plate_corner_radius - wall_t(side));
bounds_offset = function (side) wall_t(side);
bounds_min = [
-size.x/2 + bounds_offset(_WEST),
-size.y/2 + bounds_offset(_SOUTH)
];
bounds_max = [
size.x/2 - bounds_offset(_EAST),
size.y/2 - bounds_offset(_NORTH)
];
difference() {
translate(bounds_min) square([bounds_max.x - bounds_min.x, bounds_max.y - bounds_min.y]);
if (!connector[_SOUTH] && !connector[_WEST]) translate(bounds_min) scale([compute_radius(_WEST), compute_radius(_SOUTH)]) corner_punch();
if (!connector[_NORTH] && !connector[_WEST]) translate([bounds_min.x, bounds_max.y]) scale([compute_radius(_WEST), compute_radius(_NORTH)]) rotate(-90) corner_punch();
if (!connector[_SOUTH] && !connector[_EAST]) translate([bounds_max.x, bounds_min.y]) scale([compute_radius(_EAST), compute_radius(_SOUTH)]) rotate(90) corner_punch();
if (!connector[_NORTH] && !connector[_EAST]) translate(bounds_max) scale([compute_radius(_EAST), compute_radius(_NORTH)]) rotate(180) corner_punch();
translate([
-size.x/2 + wall_t(_WEST),
-size.y/2 + wall_t(_SOUTH)
]) square([size.x - wall_t(_EAST) - wall_t(_WEST), size.y - wall_t(_NORTH) - wall_t(_SOUTH)]);
segment_corner_punch(size, connector, include_wall);
}
}

Expand Down Expand Up @@ -994,9 +1006,77 @@ module segment(trace=[[1], [1]], padding=[0, 0, 0, 0], connector=[false, false,
};
};

if (plate_wall_thickness != [0,0,0,0]) translate([0, 0, -_extra_height-plate_wall_height[1]]) linear_extrude(_total_height + plate_wall_height[0] + plate_wall_height[1]) difference() {
segment_rectangle(size, connector, include_wall=true);
segment_rectangle(size, connector, include_wall=false);
// draw walls
if (plate_wall_thickness != [0,0,0,0]) {
// we draw an outer polyhedron with the walls, and then cut out an inner polyhedron without those walls (both rounded).
// the polyhedron has a bottom and a top surface. the top surface has, from above, this point numbering layout:
// ( 3) XX( 7)XXX(11)XX (15)
// X X
// ( 2) X ( 6) (10) X (14)
// X X
// ( 1) X ( 5) ( 9) X (13)
// X X
// ( 0) XX( 4)XXX( 8)XX (12)

corner_coordinate = function (i, bottom, include_wall=[true, true]) let(
side_x = i == 0 || i == 1 ? _WEST : _EAST,
side_y = i == 0 || i == 3 ? _SOUTH : _NORTH
) [
(size.x/2 - (include_wall.x ? 0 : plate_wall_thickness[side_x])) * (side_x == _WEST ? -1 : 1),
(size.y/2 - (include_wall.y ? 0 : plate_wall_thickness[side_y])) * (side_y == _SOUTH ? -1 : 1),
(bottom ? -1 : 1) * ((bottom ? plate_wall_below : plate_wall_above)[i] + plate_wall_height[bottom ? 1 : 0] + (bottom ? _extra_height : _profile_height))
];
wall_points = function(bottom) [
corner_coordinate(0, bottom, [true, true]),
corner_coordinate(0, bottom, [true, false]),
corner_coordinate(1, bottom, [true, false]),
corner_coordinate(1, bottom, [true, true]),
corner_coordinate(0, bottom, [false, true]),
corner_coordinate(0, bottom, [false, false]),
corner_coordinate(1, bottom, [false, false]),
corner_coordinate(1, bottom, [false, true]),
corner_coordinate(3, bottom, [false, true]),
corner_coordinate(3, bottom, [false, false]),
corner_coordinate(2, bottom, [false, false]),
corner_coordinate(2, bottom, [false, true]),
corner_coordinate(3, bottom, [true, true]),
corner_coordinate(3, bottom, [true, false]),
corner_coordinate(2, bottom, [true, false]),
corner_coordinate(2, bottom, [true, true]),
];
wall_face_base = [
[0, 1, 5, 4],
[1, 2, 6, 5],
[2, 3, 7, 6],
[4, 5, 9, 8],
[5, 6, 10, 9],
[6, 7, 11, 10],
[8, 9, 13, 12],
[9, 10, 14, 13],
[10, 11, 15, 14]
];
wall_bounds = [
max(plate_wall_below) + plate_wall_height[0],
max(plate_wall_above) + plate_wall_height[1]
];
difference() {
polyhedron(points = [
each wall_points(false),
each wall_points(true)
], faces = [
each wall_face_base,
for (face = wall_face_base) [for (i = [1:len(face)]) face[len(face) - i] + 16],
// side faces
[0, 16, 17, 18, 19, 3, 2, 1],
[3, 19, 23, 27, 31, 15, 11, 7],
[15, 31, 30, 29, 28, 12, 13, 14],
[0, 4, 8, 12, 28, 24, 20, 16]
]);
translate([0, 0, -_extra_height - plate_wall_height[1] - max(plate_wall_below)]) linear_extrude(_total_height + plate_wall_height[0] + plate_wall_height[1] + max(plate_wall_below) + max(plate_wall_above)) {
segment_corner_punch(size, connector, include_wall = true);
segment_rectangle(size, connector, include_wall = false);
}
}
}

if (connector_intersection_puzzle) translate([0, 0, -_extra_height]) linear_extrude(height = _total_height) segment_intersection_connectors(true, trace, size, padding, connector);
Expand Down
Loading