Skip to content
Merged
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
120 changes: 68 additions & 52 deletions gridflock.scad
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ do_half_x = true;
do_half_y = true;
// Thickness of the optional solid base
solid_base = 0;
// Chamfer at the bottom edge of the plate. Configurable for each edge individually (clockwise: north, east, south, west)
bottom_chamfer = [0, 0, 0, 0];

/* [Magnets] */

Expand Down Expand Up @@ -501,6 +503,12 @@ module segment_corner(posy=_NORTH, posx=_WEST, connector=[false, false, false, f
}
}

module chamfer_triangle() {
extend = 20;
// extend far out into -x and -y to make sure we cut everything
polygon([[-extend, -extend], [1 + extend, -extend], [-extend, 1 + extend]]);
}

/**
* @Summary Model a segment, which is piece of the plate without breaks
* @param count The number of cells in this segment, on each axis
Expand All @@ -517,63 +525,71 @@ module segment(count=[1, 1], padding=[0, 0, 0, 0], connector=[false, false, fals
// whether to cut the male edge puzzle connector to make room for the bin in the next cell. For really short connectors this is not necessary, but there's also no good reason to turn this off, so it's not user configurable at the moment
_edge_puzzle_overlap = true;
last = last_cell(count);
intersection() {
difference() {
translate([0, 0, -_extra_height]) linear_extrude(height = _total_height) difference() {
// basic plate with rounded corners
hull() {
translate([-size.x/2+plate_corner_radius, -size.y/2+plate_corner_radius]) segment_corner(_SOUTH, _WEST, connector);
translate([size.x/2-plate_corner_radius, -size.y/2+plate_corner_radius]) segment_corner(_SOUTH, _EAST, connector);
translate([size.x/2-plate_corner_radius, size.y/2-plate_corner_radius]) segment_corner(_NORTH, _EAST, connector);
translate([-size.x/2+plate_corner_radius, size.y/2-plate_corner_radius]) segment_corner(_NORTH, _WEST, connector);
};
if (connector_intersection_puzzle) {
segment_intersection_connectors(false, count, size, padding, connector);
}
}
if (connector_edge_puzzle) {
translate([0, 0, -_extra_height]) linear_extrude(height = _extra_height+edge_puzzle_height_female) segment_edge_connectors(false, count, size, padding, connector);
}
if (numbering && global_segment_index != undef) {
squeeze = count.x <= 1;
navigate_cell(size, count, padding, [0, 0]) translate([BASEPLATE_DIMENSIONS.x/(count.x == 0.5 ? 4 : 2)-(squeeze?2.95/2:0), -BASEPLATE_DIMENSIONS.y/2+4, -_extra_height]) linear_extrude(number_depth) mirror([0, 1]) rotate([0, 0, 90]) text(str(global_segment_index + 1), size = squeeze ? number_squeeze_size : number_size, halign="right", valign = "center", font = number_font);
}
}
difference() {
union() {
// padding cubes
translate([0, 0, -_extra_height]) {
if (padding[_NORTH] > 0) translate([-size.x/2, size.y/2-padding[_NORTH]]) cube([size.x, padding[_NORTH], _total_height]);
if (padding[_EAST] > 0) translate([size.x/2-padding[_EAST], -size.y/2]) cube([padding[_EAST], size.y, _total_height]);
if (padding[_SOUTH] > 0) translate([-size.x/2, -size.y/2]) cube([size.x, padding[_SOUTH], _total_height]);
if (padding[_WEST] > 0) translate([-size.x/2, -size.y/2]) cube([padding[_WEST], size.y, _total_height]);
}
// cells
for (ix = [0:1:last.x]) for (iy = [0:1:last.y]) navigate_cell(size, count, padding, [ix, iy]) {
cell([ix == count.x - 0.5, iy == count.y - 0.5], [
connector[_NORTH] && iy == last.y,
connector[_EAST] && ix == last.x,
connector[_SOUTH] && iy == 0,
connector[_WEST] && ix == 0
]);
};
};
};

if (connector_intersection_puzzle) translate([0, 0, -_extra_height]) linear_extrude(height = _total_height) segment_intersection_connectors(true, count, size, padding, connector);
if (connector_edge_puzzle) {
intersection() {
translate([0, 0, -_extra_height]) linear_extrude(height = _extra_height+_edge_puzzle_height_male) segment_edge_connectors(true, count, size, padding, connector);
if (_edge_puzzle_overlap) union() {
for (ix = [0:1:last.x]) {
if (connector[_SOUTH]) navigate_cell(size, count, padding, [ix, -1]) cell([ix == count.x - 0.5, false], positive=false);
if (connector[_NORTH]) navigate_cell(size, count, padding, [ix, last.y+1]) cell([ix == count.x - 0.5, false], positive=false);
intersection() {
translate([0, 0, -_extra_height]) linear_extrude(height = _total_height) difference() {
// basic plate with rounded corners
hull() {
translate([-size.x/2+plate_corner_radius, -size.y/2+plate_corner_radius]) segment_corner(_SOUTH, _WEST, connector);
translate([size.x/2-plate_corner_radius, -size.y/2+plate_corner_radius]) segment_corner(_SOUTH, _EAST, connector);
translate([size.x/2-plate_corner_radius, size.y/2-plate_corner_radius]) segment_corner(_NORTH, _EAST, connector);
translate([-size.x/2+plate_corner_radius, size.y/2-plate_corner_radius]) segment_corner(_NORTH, _WEST, connector);
};
if (connector_intersection_puzzle) {
segment_intersection_connectors(false, count, size, padding, connector);
}
}
for (iy = [0:1:last.y]) {
if (connector[_WEST]) navigate_cell(size, count, padding, [-1, iy]) cell([false, iy == count.y - 0.5], positive=false);
if (connector[_EAST]) navigate_cell(size, count, padding, [last.x+1, iy]) cell([false, iy == count.y - 0.5], positive=false);
union() {
// padding cubes
translate([0, 0, -_extra_height]) {
if (padding[_NORTH] > 0) translate([-size.x/2, size.y/2-padding[_NORTH]]) cube([size.x, padding[_NORTH], _total_height]);
if (padding[_EAST] > 0) translate([size.x/2-padding[_EAST], -size.y/2]) cube([padding[_EAST], size.y, _total_height]);
if (padding[_SOUTH] > 0) translate([-size.x/2, -size.y/2]) cube([size.x, padding[_SOUTH], _total_height]);
if (padding[_WEST] > 0) translate([-size.x/2, -size.y/2]) cube([padding[_WEST], size.y, _total_height]);
}
// cells
for (ix = [0:1:last.x]) for (iy = [0:1:last.y]) navigate_cell(size, count, padding, [ix, iy]) {
cell([ix == count.x - 0.5, iy == count.y - 0.5], [
connector[_NORTH] && iy == last.y,
connector[_EAST] && ix == last.x,
connector[_SOUTH] && iy == 0,
connector[_WEST] && ix == 0
]);
};
};
};

if (connector_intersection_puzzle) translate([0, 0, -_extra_height]) linear_extrude(height = _total_height) segment_intersection_connectors(true, count, size, padding, connector);
if (connector_edge_puzzle) {
intersection() {
translate([0, 0, -_extra_height]) linear_extrude(height = _extra_height+_edge_puzzle_height_male) segment_edge_connectors(true, count, size, padding, connector);
if (_edge_puzzle_overlap) union() {
for (ix = [0:1:last.x]) {
if (connector[_SOUTH]) navigate_cell(size, count, padding, [ix, -1]) cell([ix == count.x - 0.5, false], positive=false);
if (connector[_NORTH]) navigate_cell(size, count, padding, [ix, last.y+1]) cell([ix == count.x - 0.5, false], positive=false);
}
for (iy = [0:1:last.y]) {
if (connector[_WEST]) navigate_cell(size, count, padding, [-1, iy]) cell([false, iy == count.y - 0.5], positive=false);
if (connector[_EAST]) navigate_cell(size, count, padding, [last.x+1, iy]) cell([false, iy == count.y - 0.5], positive=false);
}
}
}
}
}
if (connector_edge_puzzle) {
translate([0, 0, -_extra_height]) linear_extrude(height = _extra_height+edge_puzzle_height_female) segment_edge_connectors(false, count, size, padding, connector);
}
if (numbering && global_segment_index != undef) {
squeeze = count.x <= 1;
navigate_cell(size, count, padding, [0, 0]) translate([BASEPLATE_DIMENSIONS.x/(count.x == 0.5 ? 4 : 2)-(squeeze?2.95/2:0), -BASEPLATE_DIMENSIONS.y/2+4, -_extra_height]) linear_extrude(number_depth) mirror([0, 1]) rotate([0, 0, 90]) text(str(global_segment_index + 1), size = squeeze ? number_squeeze_size : number_size, halign="right", valign = "center", font = number_font);
}
// extend a bit beyond the segment edges to make sure we cut any overhang
extend = 10;
if (bottom_chamfer[_SOUTH] > 0 && !connector[_SOUTH]) translate([-size.x/2 - extend, -size.y/2, -_extra_height]) rotate([0, 90, 0]) rotate([0, 0, 90]) linear_extrude(size.x + extend * 2) scale(bottom_chamfer[_SOUTH]) chamfer_triangle();
if (bottom_chamfer[_WEST] > 0 && !connector[_WEST]) translate([-size.x/2, -size.y/2 - extend, -_extra_height]) rotate([-90, 0, 0]) rotate([0, 0, -90]) linear_extrude(size.y + extend * 2) scale(bottom_chamfer[_WEST]) chamfer_triangle();
if (bottom_chamfer[_NORTH] > 0 && !connector[_NORTH]) translate([size.x/2 + extend, size.y/2, -_extra_height]) rotate([0, -90, 0]) rotate([0, 0, -90]) linear_extrude(size.x + extend * 2) scale(bottom_chamfer[_NORTH]) chamfer_triangle();
if (bottom_chamfer[_EAST] > 0 && !connector[_EAST]) translate([size.x/2, size.y/2 + extend, -_extra_height]) rotate([90, 0, 0]) rotate([0, 0, 90]) linear_extrude(size.y + extend * 2) scale(bottom_chamfer[_EAST]) chamfer_triangle();
}
}

Expand Down