diff --git a/boxes/generators/gridfinitytraylayout.py b/boxes/generators/gridfinitytraylayout.py new file mode 100644 index 0000000..2c685b0 --- /dev/null +++ b/boxes/generators/gridfinitytraylayout.py @@ -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") + diff --git a/boxes/generators/traylayout.py b/boxes/generators/traylayout.py index e5120f2..81dcce0 100644 --- a/boxes/generators/traylayout.py +++ b/boxes/generators/traylayout.py @@ -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 = [] diff --git a/static/samples/GridfinityTrayLayout-thumb.jpg b/static/samples/GridfinityTrayLayout-thumb.jpg new file mode 100644 index 0000000..d1cc411 Binary files /dev/null and b/static/samples/GridfinityTrayLayout-thumb.jpg differ diff --git a/static/samples/GridfinityTrayLayout.jpg b/static/samples/GridfinityTrayLayout.jpg new file mode 100644 index 0000000..132be5a Binary files /dev/null and b/static/samples/GridfinityTrayLayout.jpg differ diff --git a/static/samples/samples.sha256 b/static/samples/samples.sha256 index 69539c0..77654bd 100644 --- a/static/samples/samples.sha256 +++ b/static/samples/samples.sha256 @@ -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 diff --git a/static/self.js b/static/self.js index f5421be..d43f5d6 100644 --- a/static/self.js +++ b/static/self.js @@ -76,4 +76,85 @@ function initPage(num_hide = null) { } const t = document.getElementsByClassName("thumbnail"); for (let el of t) initThumbnail(el); -} \ No newline at end of file +} + +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); + + +