Create a GridfinityTrayLayout object.

This is based on the TrayLayout2, but it designed to just give exactly
the correct size boxes to fit into a gridfinity system.

It has a modified TrayLayout setup, so it works in one step.  It uses
javascript to auto-generate the grid, which means it doesn't currently
work for Inkscape or command line.
This commit is contained in:
caleb crome 2023-01-29 19:43:11 -08:00 committed by Florian Festi
parent 42944a015b
commit bfc710698f
6 changed files with 196 additions and 2 deletions

View File

@ -0,0 +1,113 @@
import boxes
from boxes import Boxes
from boxes.generators.traylayout import TrayLayout, TrayLayout2
from boxes.Color import Color
from boxes import restore
class GridfinityTrayLayout(TrayLayout2):
"""A Gridfinity Tray Generator based on TrayLayout"""
description = """
This is a general purpose gridfinity tray generator. You can create
somewhat arbitrarily shaped trays, or just do nothing for simple grid
shaped trays.
The dimensions are automatically calculated to fit perfectly into a
gridfinity grid (like the GridfinityBase, or any other Gridfinity
based base).
Edit the layout text graphics to adjust your tray.
You can replace the hyphens and vertical bars representing the walls
with a space character to remove the walls. You can replace the space
characters representing the floor by a "X" to remove the floor for
this compartment.
"""
def __init__(self) -> None:
Boxes.__init__(self)
self.addSettingsArgs(boxes.edges.FingerJointSettings)
self.buildArgParser(h=50)
self.outside = True # We're *always* outside for gridfinity
self.pitch = 42.0 # gridfinity pitch is defined as 42.
self.opening = 38
self.opening_margin = 2
self.argparser.add_argument("--hi", type=float, default=0, help="inner height of inner walls in mm (leave to zero for same as outer walls)")
self.argparser.add_argument("--nx", type=int, default=3, help="number of gridfinity grids in X direction")
self.argparser.add_argument("--ny", type=int, default=2, help="number of gridfinity grids in Y direction")
self.argparser.add_argument("--countx", type=int, default=5, help="split x into this many grid sections. 0 means same as --nx")
self.argparser.add_argument("--county", type=int, default=3, help="split y into this many grid sections. 0 means same as --ny")
self.argparser.add_argument("--margin", type=float, default=0.75, help="Leave this much total margin on the outside, in mm")
self.argparser.add_argument("--layout", type=str, help="You can hand edit this before generating", default="");
def generate_layout(self):
layout = ''
countx = self.countx
county = self.county
if countx == 0:
countx = self.nx
if county == 0:
county = self.ny
stepx = self.x / countx
stepy = self.y / county
for i in range(countx):
line = ' |' * i + f" ,> {stepx}mm\n"
layout += line
for i in range(county):
layout += "+-" * countx + f"+\n"
layout += "| " * countx + f"|{stepy}mm\n"
layout += "+-" * countx + "+\n"
return layout
@restore
def rectangularEtching(self, x, y, dx, dy, r=0, center_x=True, center_y=True):
"""
Draw a rectangular hole
:param x: position
:param y: position
:param dx: width
:param dy: height
:param r: (Default value = 0) radius of the corners
:param center_x: (Default value = True) if True, x position is the center, else the start
:param center_y: (Default value = True) if True, y position is the center, else the start
"""
r = min(r, dx/2., dy/2.)
x_start = x if center_x else x + dx / 2.0
y_start = y - dy / 2.0 if center_y else y
self.moveTo(x_start, y_start, 180)
self.edge(dx / 2.0 - r) # start with an edge to allow easier change of inner corners
for d in (dy, dx, dy, dx / 2.0 + r):
self.corner(-90, r)
self.edge(d - 2 * r)
def baseplate_etching(self):
x = 0
y = 0
o = self.opening
p = self.pitch
m = self.opening_margin
self.ctx.stroke()
with self.saved_context():
for xx in [0, self.nx-1]:
for yy in [0, self.ny-1]:
self.set_source_color(Color.ETCHING)
self.rectangularEtching(x+p/2+xx*p, y+p/2+yy*p, o-m, o-m)
self.ctx.stroke()
def render(self):
# Create a layout
self.x = self.pitch * self.nx - self.margin
self.y = self.pitch * self.ny - self.margin
self.outer_x = self.x
self.outer_y = self.y
super().render()
# relies on still being at the place of the base plate
self.baseplate_etching()
foot = self.opening - self.opening_margin
self.move(self.outer_x, self.outer_y, "right", before=False)
n_grids = self.nx * self.ny
if n_grids > 4:
n_grids = 4
for i in range(n_grids):
self.rectangularWall(foot, foot, move="right")

View File

@ -368,7 +368,6 @@ You can replace the space characters representing the floor by a "X" to remove t
if x < lx:
posx += self.x[x] + self.thickness
def parse(self, input):
x = []
y = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

View File

@ -122,3 +122,4 @@ cfec3b4f915befc29efc3913c131204948e63a444fd41370989173b0243617d7 ../static/samp
6a5222b9d886454f6c4fda8ead777f2136f8c1d0231e145a30ea853430ee7baa ../static/samples/WallRollHolder.jpg
cedf3c9ce40a379c4ca52089d0cda51cdf4c3899df1368c6374ed45afcb8cef8 ../static/samples/WallWrenchHolder.jpg
5b123ea245e7670cd0a55cea8348aa1f2628080f5ec005902cdd86bab231aad1 ../static/samples/WallConsole.jpg
d7b9c4a9ea70c2cc7ede5e992862fa4c001cff0f592a689ed4d5fab5ac339249 ../static/samples/GridfinityTrayLayout.jpg

View File

@ -76,4 +76,85 @@ function initPage(num_hide = null) {
}
const t = document.getElementsByClassName("thumbnail");
for (let el of t) initThumbnail(el);
}
}
function GridfinityTrayLayout_GenerateLayout(x, y, nx, ny, countx, county) {
// x = width in mm
// y = height in mm
// nx # of gridfinity grids in X
// ny # of gridfinity grids in Y
// countx split x into this many
// county split y into this many
layout = '';
if (countx == 0)
countx = nx;
if (county == 0)
county = ny
stepx = x / countx;
stepy = y / county;
for (i = 0; i < countx; i++) {
line = ' |'.repeat(i) + ` ,> ${stepx}mm\n`;
layout += line;
}
for (i = 0; i < county; i++) {
layout += "+-".repeat(countx) + "+\n";
layout += "| ".repeat(countx) + `|${stepy}mm\n`;
}
layout += "+-".repeat(countx) + "+\n";
return layout
}
function GridfinityTrayUpdateLayout(event) {
console.log("update");
if (window.layoutUpdated == true) {
// Don't do the update if the layout has been manually touched.
if (confirm("You have manually updated the Layout. Do you wish to regenerate it?")) {
window.layoutUpdated = false;
} else {
return;
}
}
console.log("updating");
nx = document.getElementById('nx').value;
ny = document.getElementById('ny').value;
countx = document.getElementById('countx').value;
county = document.getElementById('county').value;
margin = document.getElementById('margin').value;
x = nx*42 - margin
y = ny*42 - margin
layout_id = document.getElementById('layout');
layout_id.value = GridfinityTrayLayout_GenerateLayout(x, y, nx, ny, countx, county);
}
function setUpdated() {
console.log("this was updated");
window.layoutUpdated=true;
}
function GridfinityTrayLayoutInit() {
console.log("update init");
ids = ['nx', 'ny', 'countx', 'county', 'margin'];
window.layoutUpdated=false;
for (id_string of ids) {
id = document.getElementById(id_string);
id.addEventListener('input', GridfinityTrayUpdateLayout);
}
layout_id = document.getElementById('layout');
layout_id.addEventListener('change', setUpdated);
layout_id.addEventListener('input', setUpdated);
GridfinityTrayUpdateLayout();
layout_id = document.getElementById('layout');
layout_id.rows = 20;
layout_id.cols = 24;
}
function addCallbacks() {
if (window.location.href.includes("/GridfinityTrayLayout"))
GridfinityTrayLayoutInit();
}
document.addEventListener('DOMContentLoaded', function() {
addCallbacks();
}, false);