diff --git a/boxes/__init__.py b/boxes/__init__.py index 8f9d129..ec65246 100755 --- a/boxes/__init__.py +++ b/boxes/__init__.py @@ -1237,6 +1237,17 @@ class Boxes: @restore @holeCol def dHole(self, x, y, r=None, d=None, w=None, rel_w=0.75, angle=0): + """ + Draw a hole for a shaft with flat edge - D shaped hole + :param x: center position + :param y: center position + :param r: radius (overrides d) + :param d: diameter + :param w: width measured against flat side in mm + :param rel_w: width in percent + :param angle: orentation (rotation) of the flat side + """ + if r is None: r = d / 2.0 if w is None: @@ -1257,6 +1268,18 @@ class Boxes: @restore @holeCol def flatHole(self, x, y, r=None, d=None, w=None, rel_w=0.75, angle=0): + """ + Draw a hole for a shaft with two opposed flat edges - ( ) shaped hole + :param x: center position + :param y: center position + :param r: radius (overrides d) + :param d: diameter + :param w: width measured against flat side in mm + :param rel_w: width in percent + :param angle: orientation (rotation) of the flat sides + """ + + if r is None: r = d / 2.0 if w is None: @@ -1278,6 +1301,40 @@ class Boxes: self.edge(2*r*math.sin(math.radians(a))) self.corner(-a) + @restore + @holeCol + def mountingHole(self, x, y, d_shaft, d_head=0.0, angle=0, tabs=0): + """ + Draw a pear shaped mounting hole for sliding over a screw head. Total height = 1.5* d_shaft + d_head + + :param x: position + :param y: postion + :param d_shaft: diameter of the screw shaft + :param d_head: diameter of the screw head + :param angle: rotation angle of the hole + + """ + + if d_shaft < (2 * self.burn): + return # no hole if diameter is smaller then the capabilities of the machine + + if not d_head or d_head < (2 * self.burn): # if no head diameter is given + self.hole(x, y ,d=d_shaft, tabs=tabs) # only a round hole is generated + return + + rs = d_shaft / 2 + rh = d_head / 2 + + self.moveTo(x, y, angle) + self.moveTo(0, rs - self.burn, 0) + self.corner(-180, rs, tabs) + self.edge(2 * rs,tabs) + a = math.degrees(math.asin(rs / rh)) + self.corner(90 - a, 0, tabs) + self.corner(-360 + 2 * a, rh, tabs) + self.corner(90 - a, 0, tabs) + self.edge(2 * rs, tabs) + @restore def text(self, text, x=0, y=0, angle=0, align="", fontsize=10, color=[0.0, 0.0, 0.0], font="Arial"): """ diff --git a/boxes/generators/bintray.py b/boxes/generators/bintray.py index e0760a8..bf32901 100644 --- a/boxes/generators/bintray.py +++ b/boxes/generators/bintray.py @@ -55,7 +55,8 @@ class BinTray(Boxes): self.argparser.add_argument( "--front", action="store", type=float, default=0.4, help="fraction of bin height covert with slope") - + self.argparser.add_argument("--hole_dD", action="store", type=argparseSections, default="3.5:6.5", help="mounting hole diameter (shaft:head) in mm [\U0001F6C8](https://florianfesti.github.io/boxes/html/usermanual.html#mounting-holes)") + def xSlots(self): posx = -0.5 * self.thickness for x in self.sx[:-1]: @@ -73,6 +74,23 @@ class BinTray(Boxes): for x in self.sx: self.fingerHolesAt(posy, posx, x) posx += x + self.thickness + + def addMount(self): + ds = self.hole_dD[0] + + if len(self.hole_dD) < 2: # if no head diameter is given + dh = 0 # only a round hole is generated + y = max (self.thickness * 1.25, self.thickness * 1.0 + ds) # and we assume that a typical screw head diameter is twice the shaft diameter + else: + dh = self.hole_dD[1] # use given head diameter + y = max (self.thickness * 1.25, self.thickness * 1.0 + dh / 2) # and offset the hole to have enough space for the head + + dx = sum(self.sx) + self.thickness * (len(self.sx) - 1) + x1 = dx * 0.125 + x2 = dx * 0.875 + + self.mountingHole(x1, y, ds, dh, -90) + self.mountingHole(x2, y, ds, dh, -90) def xHoles(self): posx = -0.5 * self.thickness @@ -102,6 +120,7 @@ class BinTray(Boxes): x = sum(self.sx) + self.thickness * (len(self.sx) - 1) y = sum(self.sy) + self.thickness * (len(self.sy) - 1) + h = self.h hi = self.hi = h t = self.thickness @@ -124,7 +143,7 @@ class BinTray(Boxes): self.rectangularWall(y, h, "FFBF", move="up only") # floor - self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots],move="right", label="back") + self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots, self.addMount], move="right", label="back") # Inner walls for i in range(len(self.sx) - 1): e = [edges.SlottedEdge(self, self.sy, "f"), "f", "B", "f"] @@ -139,6 +158,3 @@ class BinTray(Boxes): for i in range(len(self.sy)): e = [edges.SlottedEdge(self, self.sx, "g"), "F", "e", "F"] self.rectangularWall(x, self.sy[i]*self.front*2**0.5, e, callback=[self.frontHoles(i)], move="up", label="retainer " + str(i+1)) - - - diff --git a/boxes/generators/trafficlight.py b/boxes/generators/trafficlight.py index 2ba76ea..fe5a658 100644 --- a/boxes/generators/trafficlight.py +++ b/boxes/generators/trafficlight.py @@ -63,6 +63,8 @@ When turned by 90°, it can be also used to create a bottle holder.""" "--upright", action="store", type=boolarg, default=True, help="stack lights upright (or side by side)") + self.argparser.add_argument("--hole_dD", action="store", type=argparseSections, default="3.5:6.5", help="mounting hole diameter (shaft:head) in mm [\U0001F6C8](https://florianfesti.github.io/boxes/html/usermanual.html#mounting-holes)") + def backCB(self): t = self.thickness for i in range(1, self.n): @@ -85,13 +87,13 @@ When turned by 90°, it can be also used to create a bottle holder.""" def frontCB(self): self.hole(self.h/2, self.h/2, self.h/2-self.thickness) - def wall(self, h1, h2, w, edges="ffef", callback=None, move="", label = ""): + def wall(self, h1, h2, w, edges="ffef", callback=None, move="", label=""): edges = [self.edges.get(e, e) for e in edges] edges += edges # append for wrapping around overallwidth = w + edges[-1].spacing() + edges[1].spacing() overallheight = max(h1, h2) + edges[0].spacing() + edges[2].spacing() - if self.move(overallwidth, overallheight, move, before=True, label= label): + if self.move(overallwidth, overallheight, move, before=True, label=label): return a = math.atan((h2-h1)/float(w)) @@ -112,7 +114,45 @@ When turned by 90°, it can be also used to create a bottle holder.""" edges[3](h1) self.edgeCorner(edges[3], edges[3 + 1], 90) - self.move(overallwidth, overallheight, move, label = label) + self.move(overallwidth, overallheight, move, label=label) + + def addMountH(self, width, height): + ds = self.hole_dD[0] + + if len(self.hole_dD) < 2: # if no head diameter is given + dh = 0 # only a round hole is generated + y = height - max (self.thickness * 1.25, self.thickness * 1.0 + ds) # and we assume that a typical screw head diameter is twice the shaft diameter + else: + dh = self.hole_dD[1] # use given head diameter + y = height - max (self.thickness * 1.25, self.thickness * 1.0 + dh / 2) # and offset the hole to have enough space for the head + + dx = width + x1 = dx * 0.125 + x2 = dx * 0.875 + + self.mountingHole(x1, y, ds, dh, 90) + self.mountingHole(x2, y, ds, dh, 90) + + def addMountV(self, width, height): + if self.hole_dD[0] < 2 * self.burn: + return # no hole if no diameter is given + + ds = self.hole_dD[0] + + if len(self.hole_dD) < 2: # if no head diameter is given + dh = 0 # only a round hole is generated + x = max (self.thickness * 2.75, self.thickness * 2.25 + ds) # and we assume that a typical screw head diameter is twice the shaft diameter + else: + dh = self.hole_dD[1] # use given head diameter + x = max (self.thickness * 2.75, self.thickness * 2.25 + dh / 2) # and offset the hole to have enough space for the head + + dy = height + + y1 = self.thickness * 0.75 + dy * 0.125 + y2 = self.thickness * 0.75 + dy * 0.875 + + self.mountingHole(x, y1, ds, dh, 180) + self.mountingHole(x, y2, ds, dh, 180) def render(self): # adjust to the variables you want in the local scope @@ -126,12 +166,15 @@ When turned by 90°, it can be also used to create a bottle holder.""" self.addPart(ShadyEdge(self, None)) # back - self.rectangularWall(th, h, "FFFF", callback=[self.backCB], move="up", label = "back") + if self.upright: + self.rectangularWall(th, h, "FFFF", callback=[self.backCB, self.addMountV(th, h)], move="up", label="back") + else: + self.rectangularWall(th, h, "FFFF", callback=[self.backCB, self.addMountH(th, h)], move="up", label="back") if self.upright: # sides - self.rectangularWall(th, d, "fFsF", callback=[self.sideCB], move="up", label = "left") - self.rectangularWall(th, d, "fFsF", callback=[self.sideCB], move="up", label = "right") + self.rectangularWall(th, d, "fFsF", callback=[self.sideCB], move="up", label="left") + self.rectangularWall(th, d, "fFsF", callback=[self.sideCB], move="up", label="right") # horizontal Walls / blinds tops e = edges.CompoundEdge(self, "fF", (d, s)) @@ -164,6 +207,4 @@ When turned by 90°, it can be also used to create a bottle holder.""" # Colored windows for i in range(n): - self.parts.disc(h-2*t, move="right", label="colored window " + str(i+1)) - - + self.parts.disc(h-2*t, move="right") # , label="colored windows") --> todo diff --git a/documentation/src/usermanual.rst b/documentation/src/usermanual.rst index 07587a4..8dbeb18 100644 --- a/documentation/src/usermanual.rst +++ b/documentation/src/usermanual.rst @@ -165,6 +165,9 @@ Most generators will add walls between the comparments, so the total size might The sizes of the sections are divided by a colon (``:``) e.g. ``30:25.5:70``. Instead of repeating the same value they can be replaced by ``value*numberofsections`` e.g. ``50*3`` meaning the same as ``50:50:50``. To equally divide a length into several sections ``overallwidth/numberofsections`` can be used - e.g. ``120/4`` being the same as ``30:30:30:30``. All these formats can be freely mixed. +mounting_holes +.................. +Some generators provide the option to create pear shaped mounting holes. To generate the right size holes, the shaft and the head diameter of the mounting screw must be configured. The format is "shaft:head", both diameters given in mm (e.g ``3.5:6.5``). If only the shaft diameter is given (e.g. ``3.5``), a round mounting hole is generated. Setting the mounting hole diameter parameter to ``0`` disables the creation of mounting holes. outside .......