New genarators

CompartmentBox
Desksign
Piratechest
Shoe
This commit is contained in:
J-Waal 2023-04-08 23:39:41 +02:00
parent 7bd0ab9e7d
commit 0e6e5ff6a6
4 changed files with 657 additions and 0 deletions

View File

@ -0,0 +1,288 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2014 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from boxes import *
class CompartmentBox(Boxes):
"""Type tray variation with sliding lid"""
description = """Sliding lid rests on inner walls,
so will not work if no inner walls are present.
Suggested to place walls close to both sides for maximum stability."""
ui_group = "Unstable"
def __init__(self) -> None:
Boxes.__init__(self)
self.buildArgParser("sx", "sy", "h", "outside", "bottom_edge")
self.argparser.add_argument(
"--handle", action="store", type=str, default="lip",
choices={"none","lip","hole"},
help="how to grab the lid to remove")
self.argparser.add_argument(
"--radius", action="store", type=float, default=10,
dest="radius", help="radius of the grip hole in mm")
self.argparser.add_argument(
"--holes", action="store", type=str,
default="70",
help="width of hole(s) in percentage of maximum hole width")
def xSlots(self):
posx = -0.5 * self.thickness
for x in self.sx[:-1]:
posx += x + self.thickness
posy = 0
for y in self.sy:
self.fingerHolesAt(posx, posy, y)
posy += y + self.thickness
def ySlots(self):
posy = -0.5 * self.thickness
for y in self.sy[:-1]:
posy += y + self.thickness
posx = 0
for x in reversed(self.sx):
self.fingerHolesAt(posy, posx, x)
posx += x + self.thickness
def xHoles(self):
posx = -0.5 * self.thickness
for x in self.sx[:-1]:
posx += x + self.thickness
self.fingerHolesAt(posx, 0, self.h)
def yHoles(self):
posy = -0.5 * self.thickness
for y in self.sy[:-1]:
posy += y + self.thickness
self.fingerHolesAt(posy, 0, self.h)
def render(self):
t = self.thickness
if self.outside:
self.sx = self.adjustSize(self.sx)
self.sy = self.adjustSize(self.sy)
self.h = self.adjustSize(self.h) - 2 * t
x = sum(self.sx) + self.thickness * (len(self.sx) - 1)
y = sum(self.sy) + self.thickness * (len(self.sy) - 1)
h = self.h
# Create new Edges here if needed E.g.:
s = edges.FingerJointSettings(self.thickness, relative=False,
style = "rectangular")
p = edges.FingerJointEdge(self, s)
p.char = "a" # 'a', 'A', 'b' and 'B' is reserved for beeing used within generators
self.addPart(p)
# outer walls
b = self.bottom_edge
tl, tb, tr, tf = "FEFe"
tl, tb, tr, tf = "ŠSŠe" if b == "s" else "FEFe"
# x sides
self.ctx.save()
# outer walls - front/back
hb = h+t * (3 if tb == "S" else 1)
self.rectangularWall(x, hb, [b, "F", tb, "F"],
callback=[self.xHoles],
ignore_widths=[1,2,5,6],
move="up", label="back")
self.rectangularWall(x, h, [b, "F", tf, "F"],
callback=[self.mirrorX(self.xHoles, x)],
ignore_widths=[1,6],
move="up", label="front")
# floor
if b != "e":
self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots], move="up", label="bottom")
# Inner walls
be = "f" if b != "e" else "e"
for i in range(len(self.sy) - 1):
e = [edges.SlottedEdge(self, self.sx, be), "f",
edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * h), "f"]
self.rectangularWall(x, h, e, move="up", label=f"inner x {i+1}")
# top / lid
handle = self.handle
if handle == "lip":
self.rectangularWall(x, y, "feee", move="up", label="lid")
self.rectangularWall(x, t * (2 if b == "s" else 1), "fe" + ("S" if b == "s" else "e") + "e", move="up", label="lid lip")
if handle == "hole":
self.rectangularWall(x, y + t, move="up", label="lid", callback=[self.gripHole])
if handle == "none":
self.rectangularWall(x, y + t, move="up", label="lid")
self.ctx.restore()
self.rectangularWall(x, h, "ffff", move="right only")
# y walls
# outer walls - left/right
self.sideWall(
y, h+t, "left", [b, "f", tl, "f"], callback=[self.yHoles, ],
move="up", label="left side")
self.sideWall(
y, h+t, "right", [b, "f", tr, "f"],
callback=[self.mirrorX(self.yHoles, y), ],
move="up", label="right side")
# inner walls
for i in range(len(self.sx) - 1):
e = [edges.SlottedEdge(self, self.sy, be, slots=0.5 * h),
"f", "e", "f"]
self.rectangularWall(y, h, e, move="up", label=f"inner y {i+1}")
lipy = y-t if self.handle == "lip" else y
self.rectangularWall(lipy, t, "eefe", move="up", label="Lip Left")
self.rectangularWall(lipy, t, "feee", move="up", label="Lip Right")
def sideWall(self, x, y, side, edges="eeee",
holesMargin=None, holesSettings=None,
bedBolts=None, bedBoltSettings=None,
callback=None,
move=None,
label=""):
if len(edges) != 4:
raise ValueError("four edges required")
if side not in {"left", "right"}:
raise ValueError("side must be left or right")
b = edges[0]
edges = [self.edges.get(e, e) for e in edges]
edges += edges # append for wrapping around
overallwidth = x + edges[-1].spacing() + edges[1].spacing()
overallheight = y + edges[0].spacing() + edges[2].spacing()
t = self.thickness
if self.move(overallwidth, overallheight, move, before=True):
return
self.moveTo(edges[-1].spacing(), edges[0].margin())
for i, l in enumerate((x, y, x, y)):
self.cc(callback, i, y=edges[i].startwidth() + self.burn)
e1, e2 = edges[i], edges[i + 1]
if i == 3 and side == "right":
l -= t
self.edges.get('e')(t)
l += edges[i+1].startwidth()
e2 = self.edges["e"]
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
elif i == 1 and side == "left":
l -= t
l += edges[i-1].startwidth()
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
self.edges.get('e')(t)
else:
if i == 3 and side == "left":
l += edges[i+1].startwidth() + edges[i-1].startwidth()
e2 = self.edges["e"]
if i == 1 and side == "right":
l += edges[i+1].startwidth() + edges[i-1].startwidth()
e2 = self.edges["e"]
if i == 2 and self.handle == "lip":
if b != "s":
l -= t
print(b)
if side == "left":
self.edges.get('e')(t)
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
elif side == "right":
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
self.edges.get('e')(t)
else:
self.edges.get('S')(l)
self.fingerHolesAt(0 if side == "left" else -t, 1.5*t, l-t, angle=180)
else:
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
if i == 2 and side == "left":
e1 = self.edges["e"]
if i == 0 and side == "right":
e1 = self.edges["e"]
if i == 0 and side == "left":
e1 = self.edges["e"]
self.edgeCorner(e1, e2, 90)
if holesMargin is not None:
self.moveTo(holesMargin,
holesMargin + edges[0].startwidth())
self.hexHolesRectangle(x - 2 * holesMargin, y - 2 * holesMargin, settings=holesSettings)
self.move(overallwidth, overallheight, move, label=label)
def gripHole(self):
if not self.radius:
return
radius = self.radius
t = self.thickness
widths = argparseSections(self.holes)
x = sum(self.sx) + self.thickness * (len(self.sx) - 1)
if sum(widths) > 0:
if sum(widths) < 100:
slot_offset = ((1 - sum(widths) / 100) * (x - (len(widths) + 1) * self.thickness)) / (len(widths) * 2)
else:
slot_offset = 0
slot_height = 2* radius # (self.settings.height - 2 * self.thickness) * self.settings.hole_height / 100
slot_x = self.thickness + slot_offset
for w in widths:
if sum(widths) > 100:
slotwidth = w / sum(widths) * (x - (len(widths) + 1) * self.thickness)
else:
slotwidth = w / 100 * (x - (len(widths) + 1) * self.thickness)
slot_x += slotwidth / 2
with self.saved_context():
#self.moveTo(20, slot_x, 0)
self.rectangularHole(slot_x,radius+t,slotwidth,slot_height,radius,True,True)
slot_x += slotwidth / 2 + slot_offset + self.thickness + slot_offset
#todo stackable top/bottom

View File

@ -0,0 +1,72 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2014 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from boxes import *
class Desksign(Boxes):
"""Simple diagonal plate with stands to show name or mesage"""
description = """Text to be engraved can be genarated by inputing the label and fontsize fields.
height represents the area that can be used for writing text, does not match the actual
height when standing. Generated text is put in the center. Currently only a single
line of text is supported."""
ui_group = "Misc"
def __init__(self) -> None:
Boxes.__init__(self)
self.addSettingsArgs(edges.FingerJointSettings)
#self.buildArgParser("x", "y", "h", "outside", "bottom_edge")
self.argparser.add_argument(
"--width", action="store", type=float, default=150,
help="plate width in mm (excluding holes)")
self.argparser.add_argument(
"--height", action="store", type=float, default=80,
help="plate height in mm")
self.argparser.add_argument(
"--angle", action="store", type=float, default=60,
help="plate angle in degrees (90 is vertical)")
self.argparser.add_argument(
"--label", action="store", type=str, default="",
help="optional text to engrave (leave blank to omit)")
self.argparser.add_argument(
"--fontsize", action="store", type=float, default=20,
help="height of text")
def render(self):
width = self.width
height = self.height
angle = self.angle
t = self.thickness
if not (0 < angle and angle < 90):
raise ValueError("angle has to between 0 and 90 degrees")
base = math.cos(math.radians(angle)) * height
h = math.sin(math.radians(angle)) * height
label = self.label
fontsize = self.fontsize
if label and fontsize:
self.rectangularWall(width, height, "eheh", move="right", callback=[
lambda: self.text("%s" % label, width/2, (height-fontsize)/2,
fontsize = fontsize, align="center", color=Color.ETCHING)]) # add text
else:
self.rectangularWall(width, height, "eheh", move="right") # front
self.rectangularTriangle(base, h, "eef", num=2, move="right") # stands at back/side

View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2016 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from boxes import *
class Piratechest(Boxes): # Change class name!
"""Box with polygon lid with chest hinges."""
description = """Do not assemble sides before attaching the lid!
Hinge of the lid has to be placed first because it is impossible
to get it in position without removing the side wall. The lid can
be a bit tricky to assemble. Keep track of how the parts fit together."""
ui_group = "Box" # see ./__init__.py for names
def __init__(self) -> None:
Boxes.__init__(self)
# Uncomment the settings for the edge types you use
# use keyword args to set default values
self.addSettingsArgs(edges.FingerJointSettings, finger=1.0,space=1.0)
# self.addSettingsArgs(edges.StackableSettings)
self.addSettingsArgs(edges.HingeSettings)
# self.addSettingsArgs(edges.LidSettings)
# self.addSettingsArgs(edges.ClickSettings)
# self.addSettingsArgs(edges.FlexSettings)
# remove cli params you do not need
self.buildArgParser(x=100, y=100, h=100)
# Add non default cli params if needed (see argparse std lib)
self.argparser.add_argument(
"--n", action="store", type=int, default=5,
help="number of sides on the lid")
def render(self):
# adjust to the variables you want in the local scope
x, y, h = self.x, self.y, self.h
t = self.thickness
n = self.n
# Create new Edges here if needed E.g.:
s = edges.FingerJointSettings(self.thickness, relative=False,
space = 10, finger=10,
width=self.thickness)
p = edges.FingerJointEdge(self, s)
p.char = "a" # 'a', 'A', 'b' and 'B' is reserved for beeing used within generators
self.addPart(p)
# code from regularbox
fingerJointSettings = copy.deepcopy(self.edges["f"].settings)
fingerJointSettings.setValues(self.thickness, angle=180./(n-1))
fingerJointSettings.edgeObjects(self, chars="gGH")
# render your parts here
self.ctx.save()
self.rectangularWall(x, y, "FFFF", move="up", label="Bottom")
frontlid, toplids, backlid = self.topside(y, n = n, move="only", bottem='P')
self.rectangularWall(x, backlid, "qFgF", move="up", label="lid back")
for _ in range(n-2):
self.rectangularWall(x, toplids, "GFgF", move="up", label="lid top")
self.rectangularWall(x, frontlid, "GFeF", move="up", label="lid front")
self.ctx.restore()
self.rectangularWall(x, y, "FFFF", move="right only")
with self.saved_context():
self.rectangularWall(x, h, "fFQF", ignore_widths=[2, 5], move="right", label="front")
self.rectangularWall(y, h, "ffof", ignore_widths=[5], move="right", label="right")
self.rectangularWall(0, h, "eeep", move="right only")
self.rectangularWall(x, h, "fFoF", move="up only")
self.rectangularWall(x, 0, "Peee", move="up only")
hy = self.edges["O"].startwidth()
hy2 = self.edges["P"].startwidth()
e1 = edges.CompoundEdge(self, "Fe", (h, hy))
e2 = edges.CompoundEdge(self, "eF", (hy, h))
e_back = ("f", e1, "e", e2)
with self.saved_context():
#self.rectangularWall(x, h, "fFeF", ignore_widths=[2, 5], move="right", label="back")
self.rectangularWall(x, h+hy, e_back, move="right", label="back") # extend back to correct height
self.rectangularWall(0, h, "ePee", move="right only")
self.rectangularWall(y, h, "ffOf", ignore_widths=[2], move="right", label="left")
self.rectangularWall(x, h, "fFOF", move="up only")
self.rectangularWall(x, 0, "peee", move="up only")
self.topside(y, n = n, move="right", bottem='p', label="lid left")
self.topside(y, n = n, move="right", bottem='P', label="lid right")
def topside(self, y, n, bottem='e', move=None, label=""):
radius, hp, side = self.regularPolygon((n - 1) * 2, h=y/2.0)
t = self.thickness
#edge = self.edges.get('f')
tx = y + 2 * self.edges.get('f').spacing()
lidheight = hp if n % 2 else radius
ty = lidheight + self.edges.get('f').spacing() + self.edges.get(bottem).spacing()
if self.move(tx, ty, move, before=True):
if bottem in "pP":
return side/2 + self.edges.get(bottem).spacing(), side, side/2
else:
return side/2, side, side/2
self.moveTo(self.edges.get('f').margin(), self.edges.get(bottem).margin())
self.edges.get(bottem)(y)
self.corner(90)
if bottem == 'p':
self.edges.get('f')(side/2 + self.edges.get(bottem).spacing())
else:
self.edges.get('f')(side/2)
#self.edgeCorner(self.edges.get('f'), self.edges.get('f'), 180 / (n - 1))
self.corner(180 / (n - 1))
for i in range(n-2):
self.edges.get('f')(side)
#self.edgeCorner(self.edges.get('f'), self.edges.get('f'), 180 / (n - 1))
self.corner(180 / (n - 1))
if bottem == 'P':
self.edges.get('f')(side/2 + self.edges.get(bottem).spacing())
else:
self.edges.get('f')(side/2)
self.corner(90)
self.move(tx, ty, move, label=label)
if bottem in "pP":
return side/2 + self.edges.get(bottem).spacing(), side, side/2
else:
return side/2, side, side/2

143
boxes/generators/shoe.py Normal file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2016 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from boxes import *
class Shoe(Boxes): # Change class name!
"""Shoe shaped box"""
description = """Shoe shaped box with flat sides and rounded top.
Works best if flex if under slight compression.
Also make sure that the following condition is met,
y > tophole + r + fronttop."""
ui_group = "Misc" # see ./__init__.py for names
def __init__(self) -> None:
Boxes.__init__(self)
# Uncomment the settings for the edge types you use
# use keyword args to set default values
self.addSettingsArgs(edges.FingerJointSettings)
# self.addSettingsArgs(edges.StackableSettings)
# self.addSettingsArgs(edges.HingeSettings)
# self.addSettingsArgs(edges.LidSettings)
# self.addSettingsArgs(edges.ClickSettings)
self.addSettingsArgs(edges.FlexSettings)
# remove cli params you do not need
self.buildArgParser(x=65, y=175, h=100)
# Add non default cli params if needed (see argparse std lib)
self.argparser.add_argument(
"--frontheight", action="store", type=float, default=35,
help="height at the front of the shoe")
self.argparser.add_argument(
"--fronttop", action="store", type=float, default=20,
help="length of the flat part at the front of the shoe")
self.argparser.add_argument(
"--tophole", action="store", type=float, default=75,
help="length of the opening at the top")
self.argparser.add_argument(
"--radius", action="store", type=float, default=30,
help="radius of the bend")
def render(self):
# adjust to the variables you want in the local scope
x, y, h = self.x, self.y, self.h
t = self.thickness
hf = self.frontheight
yg = self.tophole
tf = self.fronttop
r=self.radius
stretch = (self.edges["X"].settings.stretch)
self.ctx.save()
self.rectangularWall(y, x, "FFFF", move="up", label="Bottom")
lf,a=self.shoeside(y,h,hf,yg,tf,r, move="up", label="Side")
self.shoeside(y,h,hf,yg,tf,r, move="mirror up", label="Side")
self.ctx.restore()
self.rectangularWall(y, x, "FFFF", move="right only")
self.rectangularWall(x, h, "ffef", move="up", label="Back")
self.rectangularWall(x, hf, "ffff", move="up", label="front")
dr = a*(r-t)/stretch
self.shoelip(x, tf, dr, lf, label="top")
def shoelip(self, x, tf, dr, lf, move=None, label=""):
w = self.edges["F"].spacing()
th = tf + dr + lf + self.edges["F"].spacing() + self.edges["e"].spacing()
tw = x + 2*w
if self.move(tw, th, move, True, label=label):
return
self.moveTo(self.edges["F"].spacing(), self.edges["e"].spacing())
self.edges["F"](x)
self.edgeCorner("F", "F")
self.edges["F"](tf)
self.edges["X"](dr, h=x+2*w)
self.edges["F"](lf)
self.edgeCorner("F", "e")
self.edges["e"](x)
self.edgeCorner("e", "F")
self.edges["F"](lf)
self.edges["E"](dr)
self.edges["F"](tf)
self.edgeCorner("F", "F")
self.move(tw, th, move, label=label)
def shoeside(self, y, h, hf, yg, tf, r, move=None, label=""):
import math
tx = y + 2 * self.edges.get('F').spacing()
ty = h + self.edges.get('f').spacing() + self.edges.get("e").spacing()
if self.move(tx, ty, move, before=True):
return
lf = math.sqrt((h-hf)**2+(y-yg-tf)**2)
af = 90-math.degrees(math.atan((h-hf)/(y-yg-tf)))
atemp = math.degrees(math.atan((h-hf-r)/(y-yg-tf)))
dtemp = math.sqrt((h-hf-r)**2+(y-yg-tf)**2)
lf = math.sqrt(dtemp**2-r**2)
af = 90-atemp-math.degrees(math.atan(r/lf))
self.moveTo(self.edges.get('f').margin(), self.edges.get("f").margin())
self.edges.get('f')(y)
self.edgeCorner(self.edges["f"],self.edges["F"],90)
self.edges.get('F')(hf)
self.edgeCorner(self.edges["F"],self.edges["f"],90)
self.edges.get('f')(tf)
self.corner(af-90,r)
self.edges.get('f')(lf)
self.edgeCorner(self.edges["f"],self.edges["e"],90-af)
self.edges.get('e')(yg)
self.edgeCorner(self.edges["e"],self.edges["F"],90)
self.edges.get('F')(h)
self.edgeCorner(self.edges["F"],self.edges["f"],90)
self.move(tx, ty, move, label=label)
return lf,math.radians(90-af)