2019-11-24 00:40:27 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2013-2019 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 FrontEdge(edges.Edge):
|
|
|
|
|
|
|
|
def __call__(self, length, **kw):
|
|
|
|
with self.saved_context():
|
|
|
|
a = 90
|
|
|
|
r = (self.diameter +self.space) / 2
|
|
|
|
self.ctx.scale(1, self.edge_width/r)
|
|
|
|
for i in range(self.numx):
|
|
|
|
self.corner(-a)
|
|
|
|
self.corner(180, r)
|
|
|
|
self.corner(-a)
|
|
|
|
self.moveTo(length)
|
|
|
|
|
|
|
|
def margin(self):
|
|
|
|
return self.edge_width
|
|
|
|
|
|
|
|
class SpicesRack(Boxes):
|
|
|
|
"""Rack for cans of spices"""
|
|
|
|
|
|
|
|
ui_group = "Shelf"
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
Boxes.__init__(self)
|
|
|
|
|
|
|
|
self.addSettingsArgs(edges.FingerJointSettings, surroundingspaces=1.0)
|
|
|
|
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--diameter", action="store", type=float, default=55.,
|
|
|
|
help="diameter of spice cans")
|
|
|
|
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--height", action="store", type=float, default=60.,
|
|
|
|
help="height of the cans that needs to be supported")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--space", action="store", type=float, default=10.,
|
|
|
|
help="space between cans")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--numx", action="store", type=int, default=5,
|
|
|
|
help="number of cans in a row")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--numy", action="store", type=int, default=6,
|
|
|
|
help="number of cans in a column")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--in_place_supports", action="store", type=boolarg, default=False,
|
|
|
|
help="place supports pieces in holes (check for fit yourself)")
|
|
|
|
self.argparser.add_argument(
|
|
|
|
"--feet", action="store", type=boolarg, default=False,
|
|
|
|
help="add feet so the rack can stand on the ground")
|
|
|
|
|
|
|
|
|
|
|
|
def support(self, width, height, move=None):
|
|
|
|
t = self.thickness
|
|
|
|
tw = width + t
|
|
|
|
th = height
|
|
|
|
|
|
|
|
r = min(width - 2*t, height - 2*t)
|
|
|
|
|
|
|
|
if self.move(tw, th, move, True):
|
|
|
|
return
|
|
|
|
|
|
|
|
self.polyline(width-r, 90, 0, (-90, r), 0, 90, height-r, 90, width, 90)
|
|
|
|
self.edges["f"](height)
|
|
|
|
|
|
|
|
self.move(tw, th, move)
|
|
|
|
|
2020-10-13 22:49:19 +02:00
|
|
|
def foot(self, width, height, move=None):
|
|
|
|
t = self.thickness
|
|
|
|
tw, th = height, width + t
|
|
|
|
|
|
|
|
if self.move(tw, th, move, True):
|
|
|
|
return
|
|
|
|
|
|
|
|
self.moveTo(0, t)
|
|
|
|
self.edges["f"](height)
|
|
|
|
self.polyline(0, 90, width, 90, 0, (90, height), width-height, 90)
|
|
|
|
|
|
|
|
self.move(tw, th, move)
|
|
|
|
|
2019-11-24 00:40:27 +01:00
|
|
|
def holes(self):
|
|
|
|
w = 2* self.base_r
|
|
|
|
r = self.diameter / 2
|
|
|
|
a = self.base_angle
|
|
|
|
l = self.hole_length
|
|
|
|
self.moveTo(0, self.hole_distance)
|
|
|
|
|
|
|
|
with self.saved_context():
|
|
|
|
self.ctx.scale(1, l/self.base_h)
|
|
|
|
self.moveTo(self.space/2, 0, 90)
|
|
|
|
for i in range(self.numx):
|
|
|
|
self.polyline(0, -a, 0, (-180+2*a, r), 0, -90-a, w, -90)
|
|
|
|
self.moveTo(0, -(self.diameter+self.space))
|
|
|
|
self.ctx.stroke()
|
|
|
|
if self.feet and not self.feet_done:
|
|
|
|
self.feet_done = True
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.in_place_supports:
|
|
|
|
return
|
|
|
|
inner_width = self.hole_distance + self.hole_length/3
|
|
|
|
t = self.thickness
|
|
|
|
for i in range(self.numx-1):
|
|
|
|
with self.saved_context():
|
|
|
|
self.moveTo((self.diameter+self.space)*(i+0.5)- (inner_width+t)/2, self.spacing)
|
|
|
|
self.support(inner_width, (self.h-t)/2)
|
|
|
|
|
|
|
|
def backCB(self):
|
|
|
|
t = self.thickness
|
|
|
|
dy = self.h/2 - t/2
|
|
|
|
for i in range(self.numy):
|
|
|
|
self.fingerHolesAt(0, (i+1)*self.h-0.5*self.thickness-dy, self.x, 0)
|
|
|
|
for j in range(1, self.numx):
|
|
|
|
self.fingerHolesAt(
|
|
|
|
j*(self.diameter+self.space),
|
|
|
|
(i+1)*self.h-t-dy, (self.h-t)/2, -90)
|
|
|
|
|
|
|
|
def render(self):
|
|
|
|
self.feet_done = False
|
|
|
|
t = self.thickness
|
|
|
|
self.x = x = self.numx * (self.diameter+self.space)
|
|
|
|
d = self.diameter
|
|
|
|
|
|
|
|
self.base_angle = 10
|
|
|
|
self.base_r = self.diameter/2 * math.cos(math.radians(self.base_angle))
|
|
|
|
self.base_h = self.diameter/2 * (1-math.sin(math.radians(self.base_angle)))
|
|
|
|
self.angle = math.degrees(math.atan(self.base_r/self.height))
|
|
|
|
self.hole_length = (self.base_h**2+self.height**2)**0.5
|
|
|
|
self.hole_distance = (self.diameter-self.base_r) * math.sin(math.radians(self.angle))
|
|
|
|
|
|
|
|
self.h = (self.space + d) / math.cos(math.radians(self.angle))
|
|
|
|
h = self.numy * self.h - self.h / 2 + 6*t
|
|
|
|
|
|
|
|
width = self.hole_distance + self.hole_length + self.space/2
|
|
|
|
inner_width = self.hole_distance + self.hole_length/3
|
|
|
|
|
|
|
|
self.edge_width = width - inner_width
|
|
|
|
|
|
|
|
for i in range(self.numy):
|
|
|
|
self.rectangularWall(x, inner_width,[
|
|
|
|
"f", "e", FrontEdge(self, self), "e"],
|
|
|
|
callback=[self.holes], move="up")
|
|
|
|
|
|
|
|
self.rectangularWall(x, h,
|
|
|
|
callback=[self.backCB,
|
|
|
|
None,
|
|
|
|
lambda:self.hole(3*t, 3*t, 1.5),
|
|
|
|
lambda:self.hole(3*t, 3*t, 1.5),
|
|
|
|
], move="up")
|
|
|
|
|
|
|
|
|
|
|
|
if not self.in_place_supports:
|
|
|
|
self.partsMatrix((self.numx-1)*self.numy, self.numx-1, "up",
|
|
|
|
self.support, inner_width, (self.h-t)/2)
|
|
|
|
if self.feet:
|
|
|
|
self.partsMatrix(self.numx-1, self.numx-1, "up",
|
2020-10-13 22:49:19 +02:00
|
|
|
self.foot, width, (self.h-t)/2)
|
2019-11-24 00:40:27 +01:00
|
|
|
|