Skip to content
Open
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
4 changes: 3 additions & 1 deletion gridfinity-rebuilt-bins.scad
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ chamfer_holes = true;
printable_hole_top = true;
// Enable "gridfinity-refined" thumbscrew hole in the center of each base: https://www.printables.com/model/413761-gridfinity-refined
enable_thumbscrew = false;
// Where to place the bases
base_locations = 1; // [0: All bases, 1: Corners only, 2: Edges only]

hole_options = bundle_hole_options(refined_holes, magnet_holes, screw_holes, crush_ribs, chamfer_holes, printable_hole_top);
grid_dimensions = GRID_DIMENSIONS_MM / (half_grid ? 2 : 1);
Expand All @@ -119,7 +121,7 @@ gridfinityInit(gridx, gridy, height(gridz, gridz_define, style_lip, enable_zsnap
cutCylinders(n_divx=cdivx, n_divy=cdivy, cylinder_diameter=cd, cylinder_height=ch, coutout_depth=c_depth, orientation=c_orientation, chamfer=c_chamfer);
}
}
gridfinityBase([gridx, gridy], grid_dimensions=grid_dimensions, hole_options=hole_options, only_corners=only_corners || half_grid, thumbscrew=enable_thumbscrew);
gridfinityBase([gridx, gridy], grid_dimensions=grid_dimensions, hole_options=hole_options, only_corners=only_corners || half_grid, thumbscrew=enable_thumbscrew, base_locations=base_locations);
//}


Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 images/base_hole_options/magnet_and_screw_holes_all.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 images/base_hole_options/magnet_and_screw_holes_plain.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 images/base_hole_options/magnet_and_screw_holes_printable.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 images/base_hole_options/magnet_holes_chamfered.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 images/base_hole_options/magnet_holes_plain.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 images/base_hole_options/magnet_holes_printable.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 images/base_hole_options/magnet_holes_with_crush_ribs.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 images/base_hole_options/refined_and_screw_holes.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 images/base_hole_options/refined_holes.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 images/base_hole_options/screw_holes_plain.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 images/base_hole_options/screw_holes_printable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
105 changes: 96 additions & 9 deletions src/core/gridfinity-rebuilt-utility.scad
Original file line number Diff line number Diff line change
Expand Up @@ -240,13 +240,103 @@ module cut_move(x, y, w, h) {

// ===== Modules ===== //

/**
* @brief Internal module to handle base pattern generation based on location options
*/
module _pattern_bases(grid_size, grid_dimensions, individual_base_size_mm, thumbscrew, base_locations, hole_options, off, base_solid=true) {
if(base_locations == 0) {
// All bases
pattern_linear(grid_size.x, grid_size.y, grid_dimensions.x, grid_dimensions.y)
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
} else if(base_locations == 1) {
// Corners only
// Bottom left
translate([-grid_dimensions.x * (grid_size.x-1)/2, -grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}

// Bottom right
translate([grid_dimensions.x * (grid_size.x-1)/2, -grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}

// Top left
translate([-grid_dimensions.x * (grid_size.x-1)/2, grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}

// Top right
translate([grid_dimensions.x * (grid_size.x-1)/2, grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
} else if(base_locations == 2) {
// Edges only
// Bottom edge
for(x = [0:grid_size.x-1]) {
translate([grid_dimensions.x * (x - (grid_size.x-1)/2), -grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
}

// Top edge
for(x = [0:grid_size.x-1]) {
translate([grid_dimensions.x * (x - (grid_size.x-1)/2), grid_dimensions.y * (grid_size.y-1)/2, 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
}

// Left edge (excluding corners which are already done)
for(y = [1:grid_size.y-2]) {
translate([-grid_dimensions.x * (grid_size.x-1)/2, grid_dimensions.y * (y - (grid_size.y-1)/2), 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
}

// Right edge (excluding corners which are already done)
for(y = [1:grid_size.y-2]) {
translate([grid_dimensions.x * (grid_size.x-1)/2, grid_dimensions.y * (y - (grid_size.y-1)/2), 0])
if (solid_base) {
base_solid(individual_base_size_mm);
} else {
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
}
}
}
}

/**
* @brief Create the base of a gridfinity bin, or use it for a custom object.
* @param grid_size Number of bases in each dimension. [x, y]
* @param grid_dimensions [length, width] of a single Gridfinity base.
* @param thumbscrew Enable "gridfinity-refined" thumbscrew hole in the center of each base unit. This is a ISO Metric Profile, 15.0mm size, M15x1.5 designation.
* @param base_locations Where to place the bases. [0: All bases, 1: Corners only, 2: Edges only]
*/
module gridfinityBase(grid_size, grid_dimensions=GRID_DIMENSIONS_MM, hole_options=bundle_hole_options(), off=0, final_cut=true, only_corners=false, thumbscrew=false) {
module gridfinityBase(grid_size, grid_dimensions=GRID_DIMENSIONS_MM, hole_options=bundle_hole_options(), off=0, final_cut=true, only_corners=false, thumbscrew=false, base_locations=0) {
assert(is_list(grid_dimensions) && len(grid_dimensions) == 2 &&
grid_dimensions.x > 0 && grid_dimensions.y > 0);
assert(is_list(grid_size) && len(grid_size) == 2 &&
Expand All @@ -256,6 +346,7 @@ module gridfinityBase(grid_size, grid_dimensions=GRID_DIMENSIONS_MM, hole_option
is_bool(only_corners) &&
is_bool(thumbscrew)
);
assert(base_locations >= 0 && base_locations <= 2, "base_locations must be 0 (all), 1 (corners only), or 2 (edges only)");

// Per spec, there's a 0.5mm gap between each base.
// This must be kept constant or half bins may not work correctly.
Expand All @@ -277,10 +368,8 @@ module gridfinityBase(grid_size, grid_dimensions=GRID_DIMENSIONS_MM, hole_option
}

if(only_corners) {
difference(){
pattern_linear(grid_size.x, grid_size.y, grid_dimensions.x, grid_dimensions.y) {
base_solid(individual_base_size_mm);
}
difference() {
_pattern_bases(grid_size, grid_dimensions, individual_base_size_mm, thumbscrew, base_locations, hole_options, off, base_solid=true);

if(thumbscrew) {
thumbscrew_position = grid_size_mm - individual_base_size_mm;
Expand All @@ -292,10 +381,8 @@ module gridfinityBase(grid_size, grid_dimensions=GRID_DIMENSIONS_MM, hole_option
_base_holes(hole_options, off, grid_size_mm);
_base_preview_fix();
}
}
else {
pattern_linear(grid_size.x, grid_size.y, grid_dimensions.x, grid_dimensions.y)
block_base(hole_options, off, individual_base_size_mm, thumbscrew=thumbscrew);
} else {
_pattern_bases(grid_size, grid_dimensions, individual_base_size_mm, thumbscrew, base_locations, hole_options, off);
}
}

Expand Down
3 changes: 2 additions & 1 deletion tests/gridfinity-rebuilt-bins.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"scoop": "0",
"screw_holes": "false",
"style_lip": "0",
"style_tab": "1"
"style_tab": "1",
"base_locations": "0"
}
}
}
4 changes: 3 additions & 1 deletion tests/openscad_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import json
import subprocess
import platform
from dataclasses import dataclass, is_dataclass, asdict
from pathlib import Path
from tempfile import NamedTemporaryFile
Expand Down Expand Up @@ -105,6 +106,7 @@ class OpenScadRunner:
parameters: Optional[dict]
'''If set, a temporary parameter file is created, and used with these variables'''

MACOS_DEFAULT_PATH = '/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD'
WINDOWS_DEFAULT_PATH = 'C:\\Program Files\\OpenSCAD\\openscad.exe'
TOP_ANGLE_CAMERA = CameraArguments(Vec3(0,0,0),Vec3(45,0,45),150)

Expand All @@ -120,7 +122,7 @@ class OpenScadRunner:
set_variable_argument('$fa', 8) + set_variable_argument('$fs', 0.25)

def __init__(self, file_path: Path):
self.openscad_binary_path = self.WINDOWS_DEFAULT_PATH
self.openscad_binary_path = self.MACOS_DEFAULT_PATH if platform.system() == 'Darwin' else self.WINDOWS_DEFAULT_PATH
self.scad_file_path = file_path
self.image_folder_base = Path('.')
self.camera_arguments = None
Expand Down
4 changes: 4 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest>=7.0.0
pathlib>=1.0.1
dataclasses>=0.6
typing>=3.7.4
60 changes: 60 additions & 0 deletions tests/test_bins.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,63 @@ def test_magnet_and_screw_holes_all(self, openscad_runner):
vars["chamfer_holes"] = True
vars["printable_hole_top"] = True
openscad_runner.create_image([], Path('magnet_and_screw_holes_all.png'))

def test_base_locations_all_bases(self, openscad_runner):
"""Test base_locations=0 (All locations)"""
vars = openscad_runner.parameters
vars["gridx"] = 3
vars["gridy"] = 3
vars["gridz"] = 2
vars["base_locations"] = 0 # All locations
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 350)
openscad_runner.create_image([], Path('base_locations_all_bases.png'))

def test_base_locations_corners_only(self, openscad_runner):
"""Test base_locations=1 (Corners only)"""
vars = openscad_runner.parameters
vars["gridx"] = 3
vars["gridy"] = 3
vars["gridz"] = 2
vars["base_locations"] = 1
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 350)
openscad_runner.create_image([], Path('base_locations_corners_only.png'))

def test_base_locations_edges_only(self, openscad_runner):
"""Test base_locations=2 (Edges only)"""
vars = openscad_runner.parameters
vars["gridx"] = 3
vars["gridy"] = 3
vars["gridz"] = 2
vars["base_locations"] = 2
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 350)
openscad_runner.create_image([], Path('base_locations_edges_only.png'))

def test_base_locations_large_grid_all_bases(self, openscad_runner):
"""Test base_locations=0 with a larger grid"""
vars = openscad_runner.parameters
vars["gridx"] = 5
vars["gridy"] = 5
vars["gridz"] = 2
vars["base_locations"] = 0
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 550)
openscad_runner.create_image([], Path('base_locations_large_grid_all_bases.png'))

def test_base_locations_large_grid_corners_only(self, openscad_runner):
"""Test base_locations=1 with a larger grid"""
vars = openscad_runner.parameters
vars["gridx"] = 5
vars["gridy"] = 5
vars["gridz"] = 2
vars["base_locations"] = 1
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 550)
openscad_runner.create_image([], Path('base_locations_large_grid_corners_only.png'))

def test_base_locations_large_grid_edges_only(self, openscad_runner):
"""Test base_locations=2 with a larger grid"""
vars = openscad_runner.parameters
vars["gridx"] = 5
vars["gridy"] = 5
vars["gridz"] = 2
vars["base_locations"] = 2
openscad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 550)
openscad_runner.create_image([], Path('base_locations_large_grid_edges_only.png'))