Skip to content

Commit

Permalink
New generator: CoinBankSafe
Browse files Browse the repository at this point in the history
A piggy-bank designed to look like a safe.
  • Loading branch information
ojensen5115 authored and florianfesti committed Mar 23, 2024
1 parent 7fdd01e commit 645a9f3
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 0 deletions.
223 changes: 223 additions & 0 deletions boxes/generators/coinbanksafe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# Copyright (C) 2024 Oliver Jensen
#
# 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 CoinBankSafe(Boxes):
"""A piggy-bank designed to look like a safe."""

description ='''
Make sure not to discard the circle cutouts from the lid, base, and door. They are all needed.
![Closed](static/samples/CoinBankSafe-closed.jpg)
![Open](static/samples/CoinBankSafe-open.jpg)
Assemble the locking pins like this: wiggle-disc, number-disc, doorhole-disc, spacer-disc, D-disc.
Glue the first three in place, but do not glue the last two.
Leaving them unglued will allow you change the code, and to remove the pin from the door.
![Pins](static/samples/CoinBankSafe-pins.jpg)
'''

ui_group = "Misc"

def __init__(self) -> None:
Boxes.__init__(self)
self.addSettingsArgs(edges.FingerJointSettings)
self.buildArgParser("x", "y", "h")
self.argparser.add_argument(
"--slotlength", action="store", type=float, default=30,
help="Length of the coin slot in mm")
self.argparser.add_argument(
"--slotwidth", action="store", type=float, default=4,
help="Width of the coin slot in mm")
self.argparser.add_argument(
"--handlelength", action="store", type=float, default=8,
help="Length of handle in multiples of thickness")
self.argparser.add_argument(
"--handleclearance", action="store", type=float, default=1.5,
help="Clearance of handle in multiples of thickness")

def drawNumbers(self, radius, cover):
fontsize = 0.8 * (radius - cover)
for num in range(8):
angle = num*45
x = (cover + fontsize *0.4) * math.sin(math.radians(angle))
y = (cover + fontsize *0.4) * math.cos(math.radians(angle))
self.text(str(num+1), align="center middle", fontsize=fontsize, angle=-angle, color=[1,0,0],
y=y, x=x)

def lockPin(self, layers, move=None):
t = self.thickness
cutout_width = t/3
barb_length = t
base_length = layers * t
base_width = t
total_length = base_length + barb_length
total_width = base_width + cutout_width * 0.5
cutout_angle = math.degrees(math.atan(cutout_width / base_length))
cutout_length = math.sqrt(cutout_width**2 + base_length**2)

#self.rectangularWall(5*t, t)

if self.move(total_length, total_width, move, True):
return

self.edge(total_length)
self.corner(90)
self.edge(base_width * 1/3)
self.corner(90)
self.edge(base_length)
self.corner(180+cutout_angle, 0)
self.edge(cutout_length)
self.corner(90-cutout_angle)
self.edge(cutout_width * 1.5)
self.corner(90)
self.edge(barb_length)
self.corner(90)
self.corner(-90, cutout_width * 0.5)
self.edge(base_length - cutout_width * 0.5)
self.corner(90)
self.edge(t)
self.corner(90)

self.move(total_length, total_width, move)


def render(self):
x, y, h = self.x, self.y, self.h
t = self.thickness

slot_length = self.slotlength
slot_width = self.slotwidth

handle_length = self.handlelength * t
handle_clearance = self.handleclearance * t

# lock parameters
big_radius = 2.25 * t
small_radius = 1.4 * t
doorhole_radius = 1.25 * t
spacing = 1

# side walls
with self.saved_context():
self.rectangularWall(x, h, "seFf", move="mirror right")
self.rectangularWall(y, h, "sFFF", move="right")

# wall with holes for the locking bar
self.rectangularWall(
x, h, "sfFe", ignore_widths=[3,4,7,8],
callback=[lambda: self.fingerHolesAt(2.75*t, 0, h, 90)],
move="mirror right")

# locking bar
self.moveTo(0, self.edges['s'].spacing() + t)
self.rectangularWall(1.33*t, h, "eeef", move="right")
# door
door_clearance = .1 * t # amount to shave off of the door width so it can open
before_hinge = 1.25 * t - door_clearance
after_hinge = y - 2.25 * t - door_clearance
self.moveTo(self.spacing/2, -t)
self.polyline(
after_hinge, -90, t, 90, t, 90, t, -90, before_hinge, 90,
h, 90,
before_hinge, -90, t, 90, t, 90, t, -90, after_hinge, 90,
h, 90)
num_dials = 3
space_under_dials = 6*big_radius
space_not_under_dials = h - space_under_dials
dial_spacing = space_not_under_dials / (num_dials + 1)
if dial_spacing < 1 :
min_height = 6*big_radius + 4
raise ValueError(f"With thickness {t}, h must be at least {min_height} to fit the dials.")

for pos_y in (h/2,
h/2 - (2*big_radius + dial_spacing),
h/2 + (2*big_radius + dial_spacing)):
self.hole(3*t - door_clearance, pos_y, doorhole_radius)
self.rectangularHole(3*t - door_clearance, pos_y, t, t)
self.rectangularHole(y/2 - door_clearance, h/2, t, handle_length / 2)

self.rectangularWall(x, h, "seff", move="up only")

# top
self.rectangularWall(
y, x, "efff", callback=[
lambda: self.rectangularHole(y/2, x/2, slot_length, slot_width),
lambda: (self.hole(1.75*t, 1.75*t, 1.15*t),
self.rectangularHole(1.75*t, 1.75*t, t, t))],
label="top", move="right")

# bottom
self.rectangularWall(
y, x, "efff", callback=[
lambda: (self.hole(1.75*t, 1.75*t, 1.15*t),
self.rectangularHole(1.75*t, 1.75*t, t, t))],
label="bottom", move="right")

def holeCB():
self.rectangularHole(0, 0, t, t)
self.moveTo(0, 0, 45)
self.rectangularHole(0, 0, t, t)

# locks
with self.saved_context():
self.partsMatrix(3, 1, "right", self.parts.disc, 2*big_radius,
callback=lambda: (self.drawNumbers(big_radius, small_radius), self.rectangularHole(0, 0, t, t)))
self.partsMatrix(3, 1, "right", self.parts.disc, 2*big_radius,
dwidth=0.8,callback=holeCB)
self.partsMatrix(
3, 1, "right", self.parts.disc, 2*small_radius,
callback=lambda:self.rectangularHole(0, 0, t, t))
self.partsMatrix(
3, 1, "right", self.parts.waivyKnob, 2*small_radius,
callback=lambda:self.rectangularHole(0, 0, t, t))

self.partsMatrix(3, 1, "up only", self.parts.disc, 2*big_radius)

# lock pins
with self.saved_context():
self.lockPin(5, move="up")
self.lockPin(5, move="up")
self.lockPin(5, move="up")
self.lockPin(5, move="right only")

# handle
self.moveTo(0)
handle_curve_radius = 0.2 * t
self.moveTo(t * 2.5)
self.polyline(
0,
(90, handle_curve_radius),
handle_length - 2 * handle_curve_radius,
(90, handle_curve_radius),
handle_clearance - handle_curve_radius,
90,
handle_length / 4,
-90,
t,
90,
handle_length / 2,
90,
t,
-90,
handle_length / 4,
90,
handle_clearance - handle_curve_radius,
)
Binary file added static/samples/CoinBankSafe-closed-thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-closed.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-open-thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-open.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-pins-thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-pins.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe-thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/samples/CoinBankSafe.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions static/samples/samples.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,9 @@ cdd8658c4bd66de5e00c5a961583986a47c29532e006cb4fedb69faab60c9bac ../static/samp
27699d62eab473d903f2bcf0f334c2c36ff3601772c4c0ddeae89606a7bb10b5 *../static/samples/FlexTest2.jpg
853ace5a13ac571696a1965c72ae456c769ed4bc01ea29bb60bbe40518dcecf9 *../static/samples/RectangularWall.jpg
ed533e9827b58591fccbed9e6d31ff8c10bb7f9981501771f6577616a9fbe71b *../static/samples/WallRack.jpg
f625a31c8f1f08341f8e4c0ba5d34524f92e258ca2ae3027774c399a200ddfc9 ../static/samples/CoinBankSafe.jpg
49d330c7159dd77fc2e7a4d82ca46f2834c7e35a8c59b44739c0defbd045bedf ../static/samples/CoinBankSafe-open.jpg
17e9c4f4da3a74b94168913b78695918096269dfd2c59feb70bd4a833e155d04 ../static/samples/CoinBankSafe-pins.jpg
94e37a41d8b873f39bd3e8c74465b012c8f861031c93f64fd6eda89af02015c0 ../static/samples/FlexBook-2.jpg
4d8b4d5467a88431ba24e893157a6e09997d74654cd7e27878d2a54b1ee751c6 ../static/samples/CoinBankSafe-closed.jpg
bace3582c13ee543f09fd45752d4403e237d01541aaa4ea266e61e64fd12156a ../static/samples/FlexBook.jpg

0 comments on commit 645a9f3

Please sign in to comment.