114 lines
4.6 KiB
Python
114 lines
4.6 KiB
Python
|
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")
|
||
|
|