diff --git a/.gitignore b/.gitignore index 4b17997..0afe86b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ *.ud build/ dist/ -boxes.py.egg-info/ \ No newline at end of file +boxes.py.egg-info/ +.idea \ No newline at end of file diff --git a/boxes/__init__.py b/boxes/__init__.py index 1690bfb..11c43f2 100755 --- a/boxes/__init__.py +++ b/boxes/__init__.py @@ -16,6 +16,7 @@ try: import cairocffi + cairocffi.install_as_pycairo() except ImportError: pass @@ -31,6 +32,7 @@ from boxes import gears from boxes import pulley from boxes import parts + ### Helpers def dist(dx, dy): @@ -40,7 +42,8 @@ def dist(dx, dy): :param dx: delta x :param dy: delat y """ - return (dx*dx+dy*dy)**0.5 + return (dx * dx + dy * dy) ** 0.5 + def restore(func): """ @@ -49,6 +52,7 @@ def restore(func): :param func: function to wrap """ + @wraps(func) def f(self, *args, **kw): self.ctx.save() @@ -56,8 +60,10 @@ def restore(func): func(self, *args, **kw) self.ctx.restore() self.ctx.move_to(*pt) + return f + def holeCol(func): """ Wrapper: color holes differently @@ -65,6 +71,7 @@ def holeCol(func): :param func: function to wrap """ + @wraps(func) def f(self, *args, **kw): self.ctx.stroke() @@ -72,8 +79,8 @@ def holeCol(func): func(self, *args, **kw) self.ctx.stroke() self.ctx.set_source_rgb(0.0, 0.0, 0.0) - return f + return f ############################################################################# @@ -83,27 +90,27 @@ def holeCol(func): class NutHole: """Draw a hex nut""" sizes = { - "M1.6" : (3.2, 1.3), - "M2" : (4, 1.6), - "M2.5" : (5, 2.0), - "M3" : (5.5, 2.4), - "M4" : (7, 3.2), - "M5" : (8, 4.7), - "M6" : (10, 5.2), - "M8" : (13, 6.8), - "M10" : (16, 8.4), - "M12" : (18, 10.8), - "M14" : (21, 12.8), - "M16" : (24, 14.8), - "M20" : (30, 18.0), - "M24" : (36, 21.5), - "M30" : (46, 25.6), - "M36" : (55, 31), - "M42" : (65, 34), - "M48" : (75, 38), - "M56" : (85, 45), - "M64" : (95, 51), - } + "M1.6": (3.2, 1.3), + "M2": (4, 1.6), + "M2.5": (5, 2.0), + "M3": (5.5, 2.4), + "M4": (7, 3.2), + "M5": (8, 4.7), + "M6": (10, 5.2), + "M8": (13, 6.8), + "M10": (16, 8.4), + "M12": (18, 10.8), + "M14": (21, 12.8), + "M16": (24, 14.8), + "M20": (30, 18.0), + "M24": (36, 21.5), + "M30": (46, 25.6), + "M36": (55, 31), + "M42": (65, 34), + "M48": (75, 38), + "M56": (85, 45), + "M64": (95, 51), + } def __init__(self, boxes, settings): self.boxes = boxes @@ -114,13 +121,14 @@ class NutHole: @holeCol def __call__(self, size, x=0, y=0, angle=0): size = self.sizes.get(size, (size,))[0] - side = size / 3**0.5 + side = size / 3 ** 0.5 self.boxes.moveTo(x, y, angle) - self.boxes.moveTo(-0.5*side, 0.5*size, angle) + self.boxes.moveTo(-0.5 * side, 0.5 * size, angle) for i in range(6): self.boxes.edge(side) self.boxes.corner(-60) + ############################################################################## ### Argument types ############################################################################## @@ -135,17 +143,18 @@ def argparseSections(s): m = re.match(r"(\d+(\.\d+)?)/(\d+)", s) if m: n = int(m.group(3)) - print([ float(m.group(1)) ] * n) - return [ float(m.group(1))/n ] * n + print([float(m.group(1))] * n) + return [float(m.group(1)) / n] * n m = re.match(r"(\d+(\.\d+)?)\*(\d+)", s) if m: n = int(m.group(3)) - return [ float(m.group(1)) ] * n + return [float(m.group(1))] * n try: return [float(part) for part in s.split(":")] except ValueError: raise argparse.ArgumentTypeError("Don't understand sections string") + class ArgparseEdgeType: names = edges.getDescriptions() edges = [] @@ -169,6 +178,7 @@ class ArgparseEdgeType: e, self.names.get(e, "")) for e in self.edges)) return """\n""" % (name, options) + ############################################################################## ### Main class ############################################################################## @@ -182,32 +192,32 @@ class Boxes: self.formats = formats.Formats() self.argparser = ArgumentParser(description=self.__doc__) self.argparser.add_argument( - "--fingerjointfinger", action="store", type=float, default=1.0, + "--fingerjointfinger", action="store", type=float, default=1.0, help="width of the fingers in multiples of thickness") self.argparser.add_argument( - "--fingerjointspace", action="store", type=float, default=1.0, + "--fingerjointspace", action="store", type=float, default=1.0, help="width of the space between fingers in multiples of thickness") self.argparser.add_argument( - "--fingerjointsurrounding", action="store", type=float, default=1.0, + "--fingerjointsurrounding", action="store", type=float, default=1.0, help="amount of space needed at the end in multiples of normal spaces") self.argparser.add_argument( - "--thickness", action="store", type=float, default=4.0, + "--thickness", action="store", type=float, default=4.0, help="thickness of the material") self.argparser.add_argument( - "--output", action="store", type=str, default="box.svg", + "--output", action="store", type=str, default="box.svg", help="name of resulting file") self.argparser.add_argument( - "--format", action="store", type=str, default="svg", + "--format", action="store", type=str, default="svg", choices=self.formats.getFormats(), help="format of resulting file") self.argparser.add_argument( - "--debug", action="store", type=bool, default=False, + "--debug", action="store", type=bool, default=False, help="print surrounding boxes for some structures") self.argparser.add_argument( - "--reference", action="store", type=float, default=100, + "--reference", action="store", type=float, default=100, help="print reference rectangle with given length") self.argparser.add_argument( - "--burn", action="store", type=float, default=0.05, + "--burn", action="store", type=float, default=0.05, help="burn correction in mm") def open(self): @@ -216,21 +226,21 @@ class Boxes: Call this function from your .render() method """ - self.spacing = 2*self.burn + 0.5 * self.thickness + self.spacing = 2 * self.burn + 0.5 * self.thickness - self.bedBoltSettings = (3, 5.5, 2, 20, 15) #d, d_nut, h_nut, l, l1 - self.hexHolesSettings = (5, 3, 'circle') # r, dist, style + self.bedBoltSettings = (3, 5.5, 2, 20, 15) # d, d_nut, h_nut, l, l1 + self.hexHolesSettings = (5, 3, 'circle') # r, dist, style self.surface, self.ctx = self.formats.getSurface(self.format, self.output) - self.ctx.set_line_width(2*self.burn) + self.ctx.set_line_width(2 * self.burn) self._buildObjects() if self.reference: self.move(10, 10, "up", before=True) self.ctx.rectangle(0, 0, self.reference, 10) if self.reference < 40: - self.text("%.fmm" % self.reference, self.reference+5, 5, + self.text("%.fmm" % self.reference, self.reference + 5, 5, align="middle left") else: - self.text("%.fmm" % self.reference, self.reference/2.0, 5, + self.text("%.fmm" % self.reference, self.reference / 2.0, 5, align="middle center") self.move(10, 10, "up") @@ -244,45 +254,45 @@ class Boxes: for arg in l: if arg == "x": self.argparser.add_argument( - "--x", action="store", type=float, default=100.0, + "--x", action="store", type=float, default=100.0, help="inner width in mm") elif arg == "y": self.argparser.add_argument( - "--y", action="store", type=float, default=100.0, + "--y", action="store", type=float, default=100.0, help="inner depth in mm") elif arg == "sx": self.argparser.add_argument( - "--sx", action="store", type=argparseSections, + "--sx", action="store", type=argparseSections, default="50*3", help="""sections left to right in mm. Possible formats: overallwidth/numberof sections e.g. "250/5"; sectionwidth*numberofsections e.g. "50*5"; section widths separated by ":" e.g. "30:25.5:70" """) elif arg == "sy": self.argparser.add_argument( - "--sy", action="store", type=argparseSections, + "--sy", action="store", type=argparseSections, default="50*3", help="""sections back to front in mm. See --sx for format""") elif arg == "h": self.argparser.add_argument( - "--h", action="store", type=float, default=100.0, + "--h", action="store", type=float, default=100.0, help="inner height in mm") elif arg == "hi": self.argparser.add_argument( - "--hi", action="store", type=float, default=0.0, + "--hi", action="store", type=float, default=0.0, help="inner height of inner walls in mm (leave to zero for same as outer walls)") elif arg == "bottom_edge": self.argparser.add_argument( - "--bottom_edge", action="store", + "--bottom_edge", action="store", type=ArgparseEdgeType("Fhs"), choices=list("Fhs"), default="h", help="edge type for bottom edge") elif arg == "top_edge": self.argparser.add_argument( - "--top_edge", action="store", + "--top_edge", action="store", type=ArgparseEdgeType("ecESik"), choices=list("ecESik"), default="e", help="edge type for top edge") - elif arg=="outside": + elif arg == "outside": self.argparser.add_argument( - "--outside", action="store", type=bool, default=False, + "--outside", action="store", type=bool, default=False, help="treat sizes as outside measurements that include the walls") else: raise ValueError("No default for argument", arg) @@ -294,7 +304,7 @@ class Boxes: :param args: (Default value = None) parameters, None for using sys.argv """ - for key,value in vars(self.argparser.parse_args(args=args)).items(): + for key, value in vars(self.argparser.parse_args(args=args)).items(): setattr(self, key, value) # Change file ending to format if not given explicitly @@ -312,7 +322,7 @@ class Boxes: if name is None: name = part.__class__.__name__ name = name[0].lower() + name[1:] - #if not hasattr(self, name): + # if not hasattr(self, name): if isinstance(part, edges.BaseEdge): self.edges[part.char] = part else: @@ -354,9 +364,11 @@ class Boxes: self.addPart(edges.ClickEdge(self, s)) # Hinges s = edges.HingeSettings(self.thickness) + for i in range(1, 4): self.addPart(edges.Hinge(self, s, i)) self.addPart(edges.HingePin(self, s, i)) + # Nuts self.addPart(NutHole(self, None)) # Gears @@ -367,7 +379,7 @@ class Boxes: def adjustSize(self, l, e1=True, e2=True): try: total = sum(l) - walls = (len(l)-1) * self.thickness + walls = (len(l) - 1) * self.thickness except TypeError: total = l walls = 0 @@ -376,14 +388,15 @@ class Boxes: walls += e1.startwidth() + e1.margin() elif e1: walls += self.thickness + if isinstance(e2, edges.BaseEdge): walls += e2.startwidth + e2.margin() elif e2: walls += self.thickness try: - factor = (total-walls) / total - return [s*factor for s in l] + factor = (total - walls) / total + return [s * factor for s in l] except TypeError: return l - walls @@ -414,6 +427,7 @@ class Boxes: callback() else: callback(number) + elif hasattr(callback, '__getitem__'): try: callback = callback[number] @@ -424,6 +438,7 @@ class Boxes: except: self.ctx.restore() raise + self.ctx.restore() def getEntry(self, param, idx): @@ -435,7 +450,7 @@ class Boxes: """ if isinstance(param, list): - if len(param)>idx: + if len(param) > idx: return param[idx] else: return None @@ -466,16 +481,16 @@ class Boxes: :param radius: (Default value = 0) """ - rad = degrees*math.pi/180 + rad = degrees * math.pi / 180 if degrees > 0: - self.ctx.arc(0, radius+self.burn, radius+self.burn, - -0.5*math.pi, rad - 0.5*math.pi) + self.ctx.arc(0, radius + self.burn, radius + self.burn, + -0.5 * math.pi, rad - 0.5 * math.pi) elif radius > self.burn: - self.ctx.arc_negative(0, -(radius-self.burn), radius-self.burn, - 0.5*math.pi, rad + 0.5*math.pi) - else: # not rounded inner corner - self.ctx.arc_negative(0, self.burn-radius, self.burn-radius, - -0.5*math.pi, -0.5*math.pi+rad) + self.ctx.arc_negative(0, -(radius - self.burn), radius - self.burn, + 0.5 * math.pi, rad + 0.5 * math.pi) + else: # not rounded inner corner + self.ctx.arc_negative(0, self.burn - radius, self.burn - radius, + -0.5 * math.pi, -0.5 * math.pi + rad) self.continueDirection(rad) @@ -485,7 +500,7 @@ class Boxes: :param length: length in mm """ - self.ctx.move_to(0,0) + self.ctx.move_to(0, 0) self.ctx.line_to(length, 0) self.ctx.translate(*self.ctx.get_current_point()) @@ -501,8 +516,8 @@ class Boxes: """ self.ctx.curve_to(x1, y1, x2, y2, x3, y3) - dx = x3-x2 - dy = y3-y2 + dx = x3 - x2 + dy = y3 - y2 rad = math.atan2(dy, dx) self.continueDirection(rad) @@ -532,37 +547,37 @@ class Boxes: """ d, d_nut, h_nut, l, l1 = bedBoltSettings or self.bedBoltSettings - self.edge((length-d)/2.0) + self.edge((length - d) / 2.0) self.corner(90) self.edge(l1) self.corner(90) - self.edge((d_nut-d)/2.0) + self.edge((d_nut - d) / 2.0) self.corner(-90) self.edge(h_nut) self.corner(-90) - self.edge((d_nut-d)/2.0) + self.edge((d_nut - d) / 2.0) self.corner(90) - self.edge(l-l1-h_nut) + self.edge(l - l1 - h_nut) self.corner(-90) self.edge(d) self.corner(-90) - self.edge(l-l1-h_nut) + self.edge(l - l1 - h_nut) self.corner(90) - self.edge((d_nut-d)/2.0) + self.edge((d_nut - d) / 2.0) self.corner(-90) self.edge(h_nut) self.corner(-90) - self.edge((d_nut-d)/2.0) + self.edge((d_nut - d) / 2.0) self.corner(90) self.edge(l1) self.corner(90) - self.edge((length-d)/2.0) + self.edge((length - d) / 2.0) def edgeCorner(self, edge1, edge2, angle=90): """Make a corner between two Edges. Take width of edges into account""" - self.edge(edge2.startwidth()/math.sin(math.radians(180-angle))) + self.edge(edge2.startwidth() / math.sin(math.radians(180 - angle))) self.corner(angle) - self.edge(edge1.endwidth()/math.sin(math.radians(180-angle))) + self.edge(edge1.endwidth() / math.sin(math.radians(180 - angle))) def grip(self, length, depth): """Corrugated edge useful as an gipping area @@ -571,7 +586,7 @@ class Boxes: :param depth: depth of the grooves """ - grooves = int(length // (depth*2.0)) + 1 + grooves = int(length // (depth * 2.0)) + 1 depth = length / grooves / 4.0 for groove in range(grooves): self.corner(90, depth) @@ -584,11 +599,11 @@ class Boxes: :param length: """ - self.edge(1.1*self.thickness) + self.edge(1.1 * self.thickness) self.corner(-90) - self.edge(length/2.0+0.2*self.thickness) + self.edge(length / 2.0 + 0.2 * self.thickness) self.corner(-90) - self.edge(1.1*self.thickness) + self.edge(1.1 * self.thickness) def _latchGrip(self, length): """ @@ -596,9 +611,9 @@ class Boxes: :param length: """ - self.corner(90, self.thickness/4.0) - self.grip(length/2.0-self.thickness/2.0-0.2*self.thickness, self.thickness/2.0) - self.corner(90, self.thickness/4.0) + self.corner(90, self.thickness / 4.0) + self.grip(length / 2.0 - self.thickness / 2.0 - 0.2 * self.thickness, self.thickness / 2.0) + self.corner(90, self.thickness / 4.0) def latch(self, length, positive=True, reverse=False): """Latch to fix a flex box door to the box @@ -610,16 +625,16 @@ class Boxes: """ if positive: if reverse: - self.edge(length/2.0-self.burn) + self.edge(length / 2.0 - self.burn) self.corner(-90) self.edge(self.thickness) self.corner(90) - self.edge(length/2.0) + self.edge(length / 2.0) self.corner(90) self.edge(self.thickness) self.corner(-90) if not reverse: - self.edge(length/2.0-self.burn) + self.edge(length / 2.0 - self.burn) else: if reverse: self._latchGrip(length) @@ -640,31 +655,31 @@ class Boxes: :param r: (Default value = 30) radius of the corners """ - d = (x-hl-2*r)/2.0 + d = (x - hl - 2 * r) / 2.0 if d < 0: print("Handle too wide") self.ctx.save() # Hole - self.moveTo(d+2*r, 0) - self.edge(hl-2*r) + self.moveTo(d + 2 * r, 0) + self.edge(hl - 2 * r) self.corner(-90, r) - self.edge(h-3*r) + self.edge(h - 3 * r) self.corner(-90, r) - self.edge(hl-2*r) + self.edge(hl - 2 * r) self.corner(-90, r) - self.edge(h-3*r) + self.edge(h - 3 * r) self.corner(-90, r) self.ctx.restore() - self.moveTo(0,0) + self.moveTo(0, 0) - self.curveTo(d, 0, d, 0, d, -h+r) + self.curveTo(d, 0, d, 0, d, -h + r) self.curveTo(r, 0, r, 0, r, r) self.edge(hl) self.curveTo(r, 0, r, 0, r, r) - self.curveTo(h-r, 0, h-r, 0, h-r, -d) + self.curveTo(h - r, 0, h - r, 0, h - r, -d) ### Navigation @@ -679,7 +694,7 @@ class Boxes: """ self.ctx.move_to(0, 0) self.ctx.translate(x, y) - self.ctx.rotate(degrees*math.pi/180.0) + self.ctx.rotate(degrees * math.pi / 180.0) self.ctx.move_to(0, 0) def continueDirection(self, angle=0): @@ -715,11 +730,11 @@ class Boxes: y += self.spacing moves = { "up": (0, y, False), - "down" : (0, -y, True), - "left" : (-x, 0, True), - "right" : (x, 0, False), - "only" : (0, 0, None), - } + "down": (0, -y, True), + "left": (-x, 0, True), + "right": (x, 0, False), + "only": (0, 0, None), + } if not before: # restore position @@ -737,7 +752,7 @@ class Boxes: if before: # save position self.ctx.save() - self.moveTo(self.spacing/2.0, self.spacing/2.0) + self.moveTo(self.spacing / 2.0, self.spacing / 2.0) return dontdraw @restore @@ -754,8 +769,8 @@ class Boxes: r -= self.burn if r < 0: r = 1E-9 - self.moveTo(x+r, y) - self.ctx.arc(-r, 0, r, 0, 2*math.pi) + self.moveTo(x + r, y) + self.ctx.arc(-r, 0, r, 0, 2 * math.pi) @restore @holeCol @@ -770,10 +785,10 @@ class Boxes: :param r: (Default value = 0) radius of the corners """ - self.moveTo(x+r-dx/2.0, y-dy/2.0, 180) + self.moveTo(x + r - dx / 2.0, y - dy / 2.0, 180) for d in (dy, dx, dy, dx): self.corner(-90, r) - self.edge(d-2*r) + self.edge(d - 2 * r) @restore def text(self, text, x=0, y=0, angle=0, align=""): @@ -791,12 +806,12 @@ class Boxes: (tx, ty, width, height, dx, dy) = self.ctx.text_extents(text) align = align.split() moves = { - "top" : (0, -height), - "middle" : (0, -0.5*height), - "bottom" : (0, 0), - "left" : (0, 0), - "center" : (-0.5*width, 0), - "right" : (-width, 0), + "top": (0, -height), + "middle": (0, -0.5 * height), + "bottom": (0, 0), + "left": (0, 0), + "center": (-0.5 * width, 0), + "right": (-width, 0), } for a in align: if a in moves: @@ -819,27 +834,26 @@ class Boxes: """ nema = { # motor,flange, holes, screws - 8 : (20.3, 16, 15.4, 3), - 11 : (28.2, 22, 23, 4), - 14 : (35.2, 22, 26, 4), - 16 : (39.2, 22, 31, 4), - 17 : (42.2, 22, 31, 4), - 23 : (56.4, 38.1, 47.1, 5.2), - 24 : (60, 36, 49.8, 5.1), - 34 : (86.3, 73, 69.8, 6.6), - 42 : (110, 55.5, 89, 8.5), - } + 8: (20.3, 16, 15.4, 3), + 11: (28.2, 22, 23, 4), + 14: (35.2, 22, 26, 4), + 16: (39.2, 22, 31, 4), + 17: (42.2, 22, 31, 4), + 23: (56.4, 38.1, 47.1, 5.2), + 24: (60, 36, 49.8, 5.1), + 34: (86.3, 73, 69.8, 6.6), + 42: (110, 55.5, 89, 8.5), + } width, flange, holedistance, diameter = nema[size] self.moveTo(x, y, angle) if self.debug: self.rectangularHole(0, 0, width, width) - self.hole(0,0, 0.5*flange) + self.hole(0, 0, 0.5 * flange) for x in (-1, 1): for y in (-1, 1): - self.hole(x*0.5*holedistance, - y*0.5*holedistance, - 0.5*diameter) - + self.hole(x * 0.5 * holedistance, + y * 0.5 * holedistance, + 0.5 * diameter) # hexHoles @@ -863,21 +877,21 @@ class Boxes: settings = self.hexHolesSettings r, b, style = settings - w = r+b/2.0 - dist = w * math.cos(math.pi/6.0) + w = r + b / 2.0 + dist = w * math.cos(math.pi / 6.0) # how many half circles do fit - cx = int((x-2*r) // (w)) + 2 - cy = int((y-2*r) // (dist)) + 2 + cx = int((x - 2 * r) // (w)) + 2 + cy = int((y - 2 * r) // (dist)) + 2 # what's left on the sides - lx = (x - (2*r+(cx-2)*w))/2.0 - ly = (y - (2*r+((cy//2)*2)*dist-2*dist))/2.0 + lx = (x - (2 * r + (cx - 2) * w)) / 2.0 + ly = (y - (2 * r + ((cy // 2) * 2) * dist - 2 * dist)) / 2.0 - for i in range(cy//2): - for j in range((cx-(i%2))//2): - px = 2*j*w + r + lx - py = i*2*dist + r + ly + for i in range(cy // 2): + for j in range((cx - (i % 2)) // 2): + px = 2 * j * w + r + lx + py = i * 2 * dist + r + ly if i % 2: px += w if skip and skip(x, y, r, b, px, py): @@ -885,8 +899,8 @@ class Boxes: self.hole(px, py, r) def __skipcircle(self, x, y, r, b, posx, posy): - cx, cy = x/2.0, y/2.0 - return (dist(posx-cx, posy-cy) > (cx-r)) + cx, cy = x / 2.0, y / 2.0 + return (dist(posx - cx, posy - cy) > (cx - r)) def hexHolesCircle(self, d, settings=None): """ @@ -896,7 +910,7 @@ class Boxes: :param settings: (Default value = None) """ - d2 = d/2.0 + d2 = d / 2.0 self.hexHolesRectangle(d, d, settings=settings, skip=self.__skipcircle) def hexHolesPlate(self, x, y, rc, settings=None): @@ -909,6 +923,7 @@ class Boxes: :param settings: (Default value = None) """ + def skip(x, y, r, b, posx, posy): """ @@ -920,15 +935,15 @@ class Boxes: :param posy: """ - posx = abs(posx-(x/2.0)) - posy = abs(posy-(y/2.0)) + posx = abs(posx - (x / 2.0)) + posy = abs(posy - (y / 2.0)) - wx = 0.5*x-rc-r - wy = 0.5*y-rc-r + wx = 0.5 * x - rc - r + wy = 0.5 * y - rc - r if (posx <= wx) or (posy <= wx): return 0 - return dist(posx-wx, posy-wy) > rc + return dist(posx - wx, posy - wy) > rc self.hexHolesRectangle(x, y, settings, skip=skip) @@ -946,53 +961,53 @@ class Boxes: r, b, style = settings self.ctx.rectangle(0, 0, h, h) - w = r+b/2.0 - dist = w * math.cos(math.pi/6.0) - cy = 2 * int((h-4*dist)// (4*w)) + 1 + w = r + b / 2.0 + dist = w * math.cos(math.pi / 6.0) + cy = 2 * int((h - 4 * dist) // (4 * w)) + 1 - leftover = h-2*r-(cy-1)*2*r - if grow=='space ': - b += leftover / (cy-1) / 2 + leftover = h - 2 * r - (cy - 1) * 2 * r + if grow == 'space ': + b += leftover / (cy - 1) / 2 # recalulate with adjusted values - w = r+b/2.0 - dist = w * math.cos(math.pi/6.0) + w = r + b / 2.0 + dist = w * math.cos(math.pi / 6.0) - self.moveTo(h/2.0-(cy//2)*2*w, h/2.0) + self.moveTo(h / 2.0 - (cy // 2) * 2 * w, h / 2.0) for j in range(cy): - self.hole(2*j*w, 0, r) - for i in range(1, cy/2+1): - for j in range(cy-i): - self.hole(j*2*w+i*w, i*2*dist, r) - self.hole(j*2*w+i*w, -i*2*dist, r) + self.hole(2 * j * w, 0, r) + for i in range(1, cy / 2 + 1): + for j in range(cy - i): + self.hole(j * 2 * w + i * w, i * 2 * dist, r) + self.hole(j * 2 * w + i * w, -i * 2 * dist, r) def flex2D(self, x, y, width=1): width *= self.thickness - cx = int(x // (5*width)) + cx = int(x // (5 * width)) wx = x / 5. / cx - cy = int(y // (5*width)) + cy = int(y // (5 * width)) wy = y / 5. / cy - armx = (4*wx, 90, 4*wy, 90, 2*wx, 90, 2*wy) - army = (4*wy, 90, 4*wx, 90, 2*wy, 90, 2*wx) + armx = (4 * wx, 90, 4 * wy, 90, 2 * wx, 90, 2 * wy) + army = (4 * wy, 90, 4 * wx, 90, 2 * wy, 90, 2 * wx) for i in range(cx): for j in range(cy): - if (i+j) % 2: + if (i + j) % 2: self.ctx.save() - self.moveTo((5*i)*wx, (5*j)*wy) + self.moveTo((5 * i) * wx, (5 * j) * wy) self.polyline(*armx) self.ctx.restore() self.ctx.save() - self.moveTo((5*i+5)*wx, (5*j+5)*wy, -180) + self.moveTo((5 * i + 5) * wx, (5 * j + 5) * wy, -180) self.polyline(*armx) self.ctx.restore() else: self.ctx.save() - self.moveTo((5*i+5)*wx, (5*j)*wy, 90) + self.moveTo((5 * i + 5) * wx, (5 * j) * wy, 90) self.polyline(*army) self.ctx.restore() self.ctx.save() - self.moveTo((5*i)*wx, (5*j+5)*wy, -90) + self.moveTo((5 * i) * wx, (5 * j + 5) * wy, -90) self.polyline(*army) self.ctx.restore() self.ctx.stroke() @@ -1023,8 +1038,8 @@ class Boxes: """ - overallwidth = x+2*self.edges["f"].spacing() - overallheight = y+2*self.edges["f"].spacing() + overallwidth = x + 2 * self.edges["f"].spacing() + overallheight = y + 2 * self.edges["f"].spacing() if self.move(overallwidth, overallheight, move, before=True): return @@ -1034,16 +1049,16 @@ class Boxes: self.moveTo(r, 0) self.cc(callback, 0) - self.edges["f"](x/2.0-r, bedBolts=self.getEntry(bedBolts, 0), - bedBoltSettings=self.getEntry(bedBoltSettings, 0)) + self.edges["f"](x / 2.0 - r, bedBolts=self.getEntry(bedBolts, 0), + bedBoltSettings=self.getEntry(bedBoltSettings, 0)) self.cc(callback, 1) - self.edges["f"](x/2.0-r, bedBolts=self.getEntry(bedBolts, 1), - bedBoltSettings=self.getEntry(bedBoltSettings, 1)) + self.edges["f"](x / 2.0 - r, bedBolts=self.getEntry(bedBolts, 1), + bedBoltSettings=self.getEntry(bedBoltSettings, 1)) for i, l in zip(range(3), (y, x, y)): self.corner(90, r) - self.cc(callback, i+2) - self.edges["f"](l-2*r, bedBolts=self.getEntry(bedBolts, i+2), - bedBoltSettings=self.getEntry(bedBoltSettings, i+2)) + self.cc(callback, i + 2) + self.edges["f"](l - 2 * r, bedBolts=self.getEntry(bedBolts, i + 2), + bedBoltSettings=self.getEntry(bedBoltSettings, i + 2)) self.corner(90, r) self.ctx.restore() @@ -1058,7 +1073,7 @@ class Boxes: r -= holesMargin else: r = 0 - self.hexHolesPlate(x-2*holesMargin, y-2*holesMargin, r, + self.hexHolesPlate(x - 2 * holesMargin, y - 2 * holesMargin, r, settings=holesSettings) self.ctx.stroke() self.move(overallwidth, overallheight, move) @@ -1086,7 +1101,7 @@ class Boxes: :param move: (Default value = None) """ - c4 = (r+self.burn)*math.pi*0.5 # circumference of quarter circle + c4 = (r + self.burn) * math.pi * 0.5 # circumference of quarter circle c4 = c4 / self.edges["X"].settings.stretch top = self.edges.get(top, top) @@ -1098,42 +1113,41 @@ class Boxes: topwidth = top.startwidth() bottomwidth = bottom.startwidth() - overallwidth = 2*x + 2*y - 8*r + 4*c4 + \ - self.edges["d"].spacing() + self.edges["D"].spacing() + overallwidth = 2 * x + 2 * y - 8 * r + 4 * c4 + \ + self.edges["d"].spacing() + self.edges["D"].spacing() overallheight = h + top.spacing() + bottom.spacing() - if self.move(overallwidth, overallheight, move, before=True): return self.moveTo(left.spacing(), bottom.margin()) - self.cc(callback, 0, y=bottomwidth+self.burn) - bottom(x/2.0-r) - if (y-2*r) < 1E-3: - self.edges["X"](2*c4, h+topwidth+bottomwidth) - self.cc(callback, 2, y=bottomwidth+self.burn) - bottom(x-2*r) - self.edges["X"](2*c4, h+topwidth+bottomwidth) - self.cc(callback, 4, y=bottomwidth+self.burn) + self.cc(callback, 0, y=bottomwidth + self.burn) + bottom(x / 2.0 - r) + if (y - 2 * r) < 1E-3: + self.edges["X"](2 * c4, h + topwidth + bottomwidth) + self.cc(callback, 2, y=bottomwidth + self.burn) + bottom(x - 2 * r) + self.edges["X"](2 * c4, h + topwidth + bottomwidth) + self.cc(callback, 4, y=bottomwidth + self.burn) else: for i, l in zip(range(4), (y, x, y, 0)): - self.edges["X"](c4, h+topwidth+bottomwidth) - self.cc(callback, i+1, y=bottomwidth+self.burn) + self.edges["X"](c4, h + topwidth + bottomwidth) + self.cc(callback, i + 1, y=bottomwidth + self.burn) if i < 3: - bottom(l-2*r) - bottom(x/2.0-r) + bottom(l - 2 * r) + bottom(x / 2.0 - r) self.edgeCorner(bottom, right, 90) right(h) self.edgeCorner(right, top, 90) - top(x/2.0-r) + top(x / 2.0 - r) for i, l in zip(range(4), (y, x, y, 0)): self.edge(c4) if i < 3: - top(l - 2*r) - top(x/2.0-r) + top(l - 2 * r) + top(x / 2.0 - r) self.edgeCorner(top, left, 90) left(h) @@ -1165,7 +1179,7 @@ class Boxes: if len(edges) != 4: raise ValueError("four edges required") edges = [self.edges.get(e, e) for e in edges] - edges += edges # append for wrapping around + edges += edges # append for wrapping around overallwidth = x + edges[-1].spacing() + edges[1].spacing() overallheight = y + edges[0].spacing() + edges[2].spacing() @@ -1174,16 +1188,16 @@ class Boxes: 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) + self.cc(callback, i, y=edges[i].startwidth() + self.burn) edges[i](l, bedBolts=self.getEntry(bedBolts, i), bedBoltSettings=self.getEntry(bedBoltSettings, i)) - self.edgeCorner(edges[i], edges[i+1], 90) + self.edgeCorner(edges[i], edges[i + 1], 90) if holesMargin is not None: - self.moveTo(holesMargin+edges[-1].endwidth(), - holesMargin+edges[0].startwidth()) - self.hexHolesRectangle(x-2*holesMargin, y-2*holesMargin) + self.moveTo(holesMargin + edges[-1].endwidth(), + holesMargin + edges[0].startwidth()) + self.hexHolesRectangle(x - 2 * holesMargin, y - 2 * holesMargin) self.ctx.stroke() diff --git a/boxes/edges.py b/boxes/edges.py index 4c233e9..771e25b 100644 --- a/boxes/edges.py +++ b/boxes/edges.py @@ -18,13 +18,15 @@ import math import inspect + def getDescriptions(): - d = {edge.char : edge.description for edge in globals().values() + d = {edge.char: edge.description for edge in globals().values() if inspect.isclass(edge) and issubclass(edge, BaseEdge) and edge.char} d['k'] = "Straight edge with hinge eye (both ends)" return d + class BoltPolicy(object): """Abstract class @@ -32,6 +34,7 @@ class BoltPolicy(object): (fingers of a finger joint) """ + def drawbolt(self, pos): """Add a bolt to this segment? @@ -46,7 +49,7 @@ class BoltPolicy(object): :param numfingers: number of fingers to aim for """ - return numFingers + return numfingers def _even(self, numFingers): """ @@ -55,7 +58,8 @@ class BoltPolicy(object): :param numFingers: """ - return (numFingers//2) * 2 + return (numFingers // 2) * 2 + def _odd(self, numFingers): """ Return same or next smaller odd number @@ -68,8 +72,10 @@ class BoltPolicy(object): else: return numFingers - 1 + class Bolts(BoltPolicy): """Distribute a fixed number of bolts evenly""" + def __init__(self, bolts=1): self.bolts = bolts @@ -78,6 +84,7 @@ class Bolts(BoltPolicy): self.fingers = self._even(numFingers) else: self.fingers = numFingers + return self.fingers def drawBolt(self, pos): @@ -87,16 +94,18 @@ class Bolts(BoltPolicy): :param pos: number of this finger """ - if pos > self.fingers//2: + if pos > self.fingers // 2: pos = self.fingers - pos - if pos==0: + + if pos == 0: return False - if pos == self.fingers//2 and not (self.bolts % 2): + + if pos == self.fingers // 2 and not (self.bolts % 2): return False - result = (math.floor((float(pos)*(self.bolts+1)/self.fingers)-0.01) != - math.floor((float(pos+1)*(self.bolts+1)/self.fingers)-0.01)) - #print pos, result, ((float(pos)*(self.bolts+1)/self.fingers)-0.01), ((float(pos+1)*(self.bolts+1)/self.fingers)-0.01) - return result + + return (math.floor((float(pos) * (self.bolts + 1) / self.fingers) - 0.01) != + math.floor((float(pos + 1) * (self.bolts + 1) / self.fingers) - 0.01)) + ############################################################################# ### Settings @@ -113,8 +122,8 @@ class Settings(object): the suported keys and default values. The values are available via attribute access. """ - absolute_params = { } - relative_params = { } + absolute_params = {} + relative_params = {} def __init__(self, thickness, relative=True, **kw): self.values = self.absolute_params.copy() @@ -150,6 +159,7 @@ class Settings(object): def __getattr__(self, name): return self.values[name] + ############################################################################# ### Edges ############################################################################# @@ -171,7 +181,7 @@ class BaseEdge(object): def __call__(self, length, **kw): """Draw edge of length mm""" - self.ctx.move_to(0,0) + self.ctx.move_to(0, 0) self.ctx.line_to(length, 0) self.ctx.translate(*self.ctx.get_current_point()) @@ -181,7 +191,7 @@ class BaseEdge(object): def endwidth(self): return self.startwidth() - + def margin(self): """Space needed right of the starting point""" return 0.0 @@ -198,10 +208,12 @@ class BaseEdge(object): """Not yet supported""" return 0.0 + class Edge(BaseEdge): """Straight edge""" char = 'e' - description = "Straight Edge" + description = "Straight Edge" + class OutSetEdge(BaseEdge): """Straight edge out set by one thickness""" @@ -211,6 +223,7 @@ class OutSetEdge(BaseEdge): def startwidth(self): return self.boxes.thickness + ############################################################################# #### Gripping Edge ############################################################################# @@ -231,22 +244,22 @@ Values: """ absolute_params = { - "style" : "A", - "outset" : True, + "style": "A", + "outset": True, } relative_params = { - "depth" : 0.3, + "depth": 0.3, } + class GrippingEdge(BaseEdge): description = """Corrugated edge useful as an gipping area""" char = 'g' - def A(self, length): depth = self.settings.depth - grooves = int(length // (depth*2.0)) + 1 + grooves = int(length // (depth * 2.0)) + 1 depth = length / grooves / 4.0 o = 1 if self.settings.outset else -1 @@ -257,18 +270,21 @@ class GrippingEdge(BaseEdge): def B(self, length): depth = self.settings.depth - grooves = int(length // (depth*2.0)) + 1 + grooves = int(length // (depth * 2.0)) + 1 depth = length / grooves / 2.0 o = 1 if self.settings.outset else -1 + if self.settings.outset: self.corner(-90) else: self.corner(90) self.edge(depth) self.corner(-180) + for groove in range(grooves): self.corner(180, depth) self.corner(-180, 0) + if self.settings.outset: self.corner(90) else: @@ -286,12 +302,14 @@ class GrippingEdge(BaseEdge): return getattr(self, self.settings.style)(length) + class CompoundEdge(BaseEdge): """Edge composed of multiple different Edges""" description = "Compound Edge" def __init__(self, boxes, types, lengths): super(CompoundEdge, self).__init__(boxes, None) + self.types = [self.edges.get(edge, edge) for edge in types] self.lengths = lengths self.length = sum(lengths) @@ -301,7 +319,7 @@ class CompoundEdge(BaseEdge): def endwidth(self): return self.types[-1].endwidth() - + def margin(self): return max((e.margin() for e in self.types)) @@ -309,8 +327,10 @@ class CompoundEdge(BaseEdge): if length and abs(length - self.length) > 1E-5: raise ValueError("Wrong length for CompoundEdge") lastwidth = self.types[0].startwidth() + for e, l in zip(self.types, self.lengths): diff = e.startwidth() - lastwidth + if diff > 1E-5: self.boxes.corner(-90) self.boxes.edge(diff) @@ -319,9 +339,11 @@ class CompoundEdge(BaseEdge): self.boxes.corner(90) self.boxes.edge(-diff) self.boxes.corner(-90) + e(l) lastwidth = e.endwidth() + ############################################################################# #### Slots ############################################################################# @@ -333,6 +355,7 @@ class Slot(BaseEdge): def __init__(self, boxes, depth): super(Slot, self).__init__(boxes, None) + self.depth = depth def __call__(self, length, **kw): @@ -347,12 +370,14 @@ class Slot(BaseEdge): else: self.boxes.edge(self.length) + class SlottedEdge(BaseEdge): """Edge with multiple slots""" description = "Straight Edge with slots" def __init__(self, boxes, sections, edge="e", slots=0): super(SlottedEdge, self).__init__(boxes, None) + self.edge = self.edges.get(edge, edge) self.sections = sections self.slots = slots @@ -367,14 +392,18 @@ class SlottedEdge(BaseEdge): return self.edge.margin() def __call__(self, length, **kw): + for l in self.sections[:-1]: self.edge(l) + if self.slots: Slot(self.boxes, self.slots)(self.thickness) else: self.edge(self.thickness) + self.edge(self.sections[-1]) + ############################################################################# #### Finger Joints ############################################################################# @@ -400,16 +429,17 @@ Values: """ absolute_params = { - "surroundingspaces" : 2, - } + "surroundingspaces": 2, + } relative_params = { - "space" : 1.0, - "finger" : 1.0, - "height" : 1.0, - "width" : 1.0, - "edge_width" : 1.0, - } + "space": 1.0, + "finger": 1.0, + "height": 1.0, + "width": 1.0, + "edge_width": 1.0, + } + class FingerJointEdge(BaseEdge): """Finger joint edge """ @@ -417,17 +447,16 @@ class FingerJointEdge(BaseEdge): description = "Finger Joint" positive = True - def __call__(self, length, - bedBolts=None, bedBoltSettings=None, **kw): + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): + positive = self.positive space, finger = self.settings.space, self.settings.finger - - fingers = int((length-(self.settings.surroundingspaces-1)*space) // - (space+finger)) + fingers = int((length - (self.settings.surroundingspaces - 1) * space) // + (space + finger)) if bedBolts: fingers = bedBolts.numFingers(fingers) - leftover = length - fingers*(space+finger) + space + leftover = length - fingers * (space + finger) + space s, f, thickness = space, finger, self.thickness d, d_nut, h_nut, l, l1 = bedBoltSettings or self.boxes.bedBoltSettings @@ -437,29 +466,34 @@ class FingerJointEdge(BaseEdge): fingers = 0 leftover = length - self.edge(leftover/2.0) + self.edge(leftover / 2.0) + for i in range(fingers): - if i !=0: + if i != 0: if not positive and bedBolts and bedBolts.drawBolt(i): - self.hole(0.5*space, - 0.5*self.thickness, 0.5*d) + self.hole(0.5 * space, + 0.5 * self.thickness, 0.5 * d) + if positive and bedBolts and bedBolts.drawBolt(i): self.bedBoltHole(s, bedBoltSettings) else: self.edge(s) - self.corner(-90*p) + + self.corner(-90 * p) self.edge(self.settings.height) - self.corner(90*p) + self.corner(90 * p) self.edge(f) - self.corner(90*p) + self.corner(90 * p) self.edge(self.settings.height) - self.corner(-90*p) - self.edge(leftover/2.0) + self.corner(-90 * p) + + self.edge(leftover / 2.0) def margin(self): """ """ return self.boxes.thickness + class FingerJointEdgeCounterPart(FingerJointEdge): """Finger joint edge - other side""" char = 'F' @@ -474,8 +508,10 @@ class FingerJointEdgeCounterPart(FingerJointEdge): """ """ return 0.0 + class FingerHoles: """Hole matching a finger joint edge""" + def __init__(self, boxes, settings): self.boxes = boxes self.ctx = boxes.ctx @@ -497,38 +533,44 @@ class FingerHoles: self.boxes.moveTo(x, y, angle) s, f = self.settings.space, self.settings.finger - fingers = int((length-(self.settings.surroundingspaces-1)*s) // - (s+f)) + fingers = int((length - (self.settings.surroundingspaces - 1) * s) // + (s + f)) if bedBolts: fingers = bedBolts.numFingers(fingers) d, d_nut, h_nut, l, l1 = bedBoltSettings or self.boxes.bedBoltSettings - leftover = length - fingers*(s+f) - f + + leftover = length - fingers * (s + f) - f b = self.boxes.burn + if self.boxes.debug: - self.ctx.rectangle(0, -self.settings.width/2+b, - length, self.settings.width - 2*b) + self.ctx.rectangle(0, -self.settings.width / 2 + b, + length, self.settings.width - 2 * b) for i in range(fingers): - pos = leftover/2.0+i*(s+f) + pos = leftover / 2.0 + i * (s + f) + if bedBolts and bedBolts.drawBolt(i): - self.boxes.hole(pos+0.5*s, 0, d*0.5) - self.boxes.rectangularHole(pos+s+0.5*f, 0, - f, self.settings.width) + self.boxes.hole(pos + 0.5 * s, 0, d * 0.5) + + self.boxes.rectangularHole(pos + s + 0.5 * f, 0, + f, self.settings.width) self.ctx.restore() + class FingerHoleEdge(BaseEdge): """Edge with holes for a parallel finger joint""" char = 'h' description = "Edge (parallel Finger Joint Holes)" + def __init__(self, boxes, fingerHoles=None, **kw): super(FingerHoleEdge, self).__init__(boxes, None, **kw) + self.fingerHoles = fingerHoles or boxes.fingerHolesAt - def __call__(self, length, - bedBolts=None, bedBoltSettings=None, **kw): + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): dist = self.fingerHoles.settings.edge_width self.ctx.save() - self.fingerHoles(0, dist+self.thickness/2, length, 0, + self.fingerHoles(0, dist + self.thickness / 2, length, 0, bedBolts=bedBolts, bedBoltSettings=bedBoltSettings) self.ctx.restore() # XXX continue path @@ -540,6 +582,7 @@ class FingerHoleEdge(BaseEdge): """ """ return self.fingerHoles.settings.edge_width + self.thickness + class CrossingFingerHoleEdge(BaseEdge): """Edge with holes for finger joints 90° above""" @@ -547,13 +590,15 @@ class CrossingFingerHoleEdge(BaseEdge): def __init__(self, boxes, height, fingerHoles=None, **kw): super(CrossingFingerHoleEdge, self).__init__(boxes, None, **kw) + self.fingerHoles = fingerHoles or boxes.fingerHolesAt self.height = height def __call__(self, length, **kw): - self.fingerHoles(length/2.0, 0, self.height) + self.fingerHoles(length / 2.0, 0, self.height) super(CrossingFingerHoleEdge, self).__call__(length) + ############################################################################# #### Stackable Joints ############################################################################# @@ -576,14 +621,16 @@ Values: """ absolute_params = { - "angle" : 60, + "angle": 60, } + relative_params = { - "height" : 2.0, - "width" : 4.0, - "holedistance" : 1.0, + "height": 2.0, + "width": 4.0, + "holedistance": 1.0, } + class StackableEdge(BaseEdge): """Edge for having stackable Boxes. The Edge creates feet on the bottom and has matching recesses on the top corners.""" @@ -594,46 +641,48 @@ class StackableEdge(BaseEdge): def __init__(self, boxes, settings, fingerjointsettings): super(StackableEdge, self).__init__(boxes, settings) + self.fingerjointsettings = fingerjointsettings def __call__(self, length, **kw): s = self.settings - r = s.height / 2.0 / (1-math.cos(math.radians(s.angle))) + r = s.height / 2.0 / (1 - math.cos(math.radians(s.angle))) l = r * math.sin(math.radians(s.angle)) p = 1 if self.bottom else -1 if self.bottom: - self.boxes.fingerHolesAt(0, s.height+self.settings.holedistance+0.5*self.boxes.thickness, + self.boxes.fingerHolesAt(0, s.height + self.settings.holedistance + 0.5 * self.boxes.thickness, length, 0) self.boxes.edge(s.width) - self.boxes.corner(p*s.angle, r) - self.boxes.corner(-p*s.angle, r) - self.boxes.edge(length-2*s.width-4*l) - self.boxes.corner(-p*s.angle, r) - self.boxes.corner(p*s.angle, r) + self.boxes.corner(p * s.angle, r) + self.boxes.corner(-p * s.angle, r) + self.boxes.edge(length - 2 * s.width - 4 * l) + self.boxes.corner(-p * s.angle, r) + self.boxes.corner(p * s.angle, r) self.boxes.edge(s.width) def _height(self): return self.settings.height + self.settings.holedistance + self.settings.thickness - + def startwidth(self): return self._height() if self.bottom else 0 def margin(self): return 0 if self.bottom else self._height() + class StackableEdgeTop(StackableEdge): char = "S" description = "Stackable (top)" bottom = False + ############################################################################# #### Hinges ############################################################################# class HingeSettings(Settings): - """Settings for Hinge and HingePin classes Values: @@ -652,27 +701,29 @@ Values: """ absolute_params = { - "style" : "A", - "outset" : False, - "pinwidth" : 0.5, - "grip_percentage" : 0, - } + "style": "A", + "outset": False, + "pinwidth": 0.5, + "grip_percentage": 0, + } relative_params = { - "hingestrength" : 1,#1.5-0.5*2**0.5, - "axle" : 2, - "grip_length" : 0, - } + "hingestrength": 1, # 1.5-0.5*2**0.5, + "axle": 2, + "grip_length": 0, + } + class Hinge(BaseEdge): - char = 'i' description = "Straight edge with hinge eye" def __init__(self, boxes, settings=None, layout=1): - super(Hinge, self).__init__(boxes,settings) + super(Hinge, self).__init__(boxes, settings) + if not (0 < layout <= 3): raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout) + self.layout = layout self.char = "eijk"[layout] self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout] @@ -683,33 +734,35 @@ class Hinge(BaseEdge): def A(self, _reversed=False): t = self.thickness r = 0.5 * self.settings.axle - alpha = math.degrees(math.asin(0.5*t/r)) - pinl = (self.settings.axle**2-self.thickness**2)**0.5 * self.settings.pinwidth + alpha = math.degrees(math.asin(0.5 * t / r)) + pinl = (self.settings.axle ** 2 - self.thickness ** 2) ** 0.5 * self.settings.pinwidth pos = math.cos(math.radians(alpha)) * r hinge = ( 0, - 90-alpha, 0, + 90 - alpha, 0, (-360, r), 0, - 90+alpha, + 90 + alpha, t, 90, - 0.5*t, - (180, t+ pos), 0, - (-90, 0.5*t), 0 + 0.5 * t, + (180, t + pos), 0, + (-90, 0.5 * t), 0 ) + if _reversed: hinge = reversed(hinge) self.polyline(*hinge) - self.boxes.rectangularHole(-pos, -0.5*t, pinl, self.thickness) + self.boxes.rectangularHole(-pos, -0.5 * t, pinl, self.thickness) else: - self.boxes.rectangularHole(pos, -0.5*t, pinl, self.thickness) + self.boxes.rectangularHole(pos, -0.5 * t, pinl, self.thickness) self.polyline(*hinge) def Alen(self): t = self.thickness r = 0.5 * self.settings.axle - alpha = math.degrees(math.asin(0.5*t/r)) + alpha = math.degrees(math.asin(0.5 * t / r)) pos = math.cos(math.radians(alpha)) * r + return 2 * pos + 1.5 * t def B(self, _reversed=False): @@ -717,41 +770,48 @@ class Hinge(BaseEdge): hinge = ( 0, -90, - 0.5*t, - (180, 0.5*self.settings.axle+self.settings.hingestrength), 0, - (-90, 0.5*t), 0 + 0.5 * t, + (180, 0.5 * self.settings.axle + self.settings.hingestrength), 0, + (-90, 0.5 * t), 0 ) - pos = 0.5*self.settings.axle+self.settings.hingestrength - pinl = (self.settings.axle**2-self.thickness**2)**0.5 * self.settings.pinwidth + pos = 0.5 * self.settings.axle + self.settings.hingestrength + pinl = (self.settings.axle ** 2 - self.thickness ** 2) ** 0.5 * self.settings.pinwidth if _reversed: hinge = reversed(hinge) - self.hole(0.5*t+pos, -0.5*t, 0.5*self.settings.axle) - self.boxes.rectangularHole(0.5*t+pos, -0.5*t, pinl, self.thickness) + self.hole(0.5 * t + pos, -0.5 * t, 0.5 * self.settings.axle) + self.boxes.rectangularHole(0.5 * t + pos, -0.5 * t, pinl, self.thickness) else: - self.hole(pos, -0.5*t, 0.5*self.settings.axle) - self.boxes.rectangularHole(pos, -0.5*t, pinl, self.thickness) + self.hole(pos, -0.5 * t, 0.5 * self.settings.axle) + self.boxes.rectangularHole(pos, -0.5 * t, pinl, self.thickness) + self.polyline(*hinge) def Blen(self): - return self.settings.axle + 2*self.settings.hingestrength + 0.5*self.thickness + return self.settings.axle + 2 * self.settings.hingestrength + 0.5 * self.thickness def __call__(self, l, **kw): - hlen = getattr(self, self.settings.style+'len')() + hlen = getattr(self, self.settings.style + 'len')() + if self.layout & 1: getattr(self, self.settings.style)() - self.edge(l - (self.layout & 1)*hlen - bool(self.layout & 2)*hlen) + + self.edge(l - (self.layout & 1) * hlen - bool(self.layout & 2) * hlen) + if self.layout & 2: getattr(self, self.settings.style)(True) + class HingePin(BaseEdge): char = 'I' description = "Edge with hinge pin" def __init__(self, boxes, settings=None, layout=1): - super(HingePin, self).__init__(boxes,settings) + super(HingePin, self).__init__(boxes, settings) + if not (0 < layout <= 3): raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout) + self.layout = layout self.char = "EIJK"[layout] self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout] @@ -774,10 +834,10 @@ class HingePin(BaseEdge): def A(self, _reversed=False): t = self.thickness r = 0.5 * self.settings.axle - alpha = math.degrees(math.asin(0.5*t/r)) + alpha = math.degrees(math.asin(0.5 * t / r)) pos = math.cos(math.radians(alpha)) * r - pinl = (self.settings.axle**2-self.thickness**2)**0.5 * self.settings.pinwidth - pin = (pos-0.5*pinl, -90, + pinl = (self.settings.axle ** 2 - self.thickness ** 2) ** 0.5 * self.settings.pinwidth + pin = (pos - 0.5 * pinl, -90, t, 90, pinl, 90, @@ -786,34 +846,36 @@ class HingePin(BaseEdge): if self.settings.outset: pin += ( - pos-0.5*pinl+1.5*t, + pos - 0.5 * pinl + 1.5 * t, -90, t, 90, 0, ) else: - pin += (pos-0.5*pinl,) + pin += (pos - 0.5 * pinl,) + if _reversed: pin = reversed(pin) + self.polyline(*pin) def Alen(self): t = self.thickness r = 0.5 * self.settings.axle - alpha = math.degrees(math.asin(0.5*t/r)) + alpha = math.degrees(math.asin(0.5 * t / r)) pos = math.cos(math.radians(alpha)) * r if self.settings.outset: - return 2 * pos + 1.5* self.thickness + return 2 * pos + 1.5 * self.thickness else: return 2 * pos def B(self, _reversed=False): t = self.thickness - pinl = (self.settings.axle**2-t**2)**0.5 * self.settings.pinwidth + pinl = (self.settings.axle ** 2 - t ** 2) ** 0.5 * self.settings.pinwidth d = (self.settings.axle - pinl) / 2.0 - pin = (self.settings.hingestrength+d, -90, + pin = (self.settings.hingestrength + d, -90, t, 90, pinl, 90, @@ -823,29 +885,35 @@ class HingePin(BaseEdge): if self.settings.outset: pin += ( 0, - self.settings.hingestrength+0.5*t, + self.settings.hingestrength + 0.5 * t, -90, t, 90, 0, ) + if _reversed: pin = reversed(pin) + self.polyline(*pin) def Blen(self): - l = self.settings.hingestrength+self.settings.axle + l = self.settings.hingestrength + self.settings.axle + if self.settings.outset: l += self.settings.hingestrength + 0.5 * self.thickness + return l def __call__(self, l, **kw): - plen = getattr(self, self.settings.style+'len')() + plen = getattr(self, self.settings.style + 'len')() glen = l * self.settings.grip_percentage + \ self.settings.grip_length + if not self.settings.outset: glen = 0.0 - glen = min(glen, l-plen) + + glen = min(glen, l - plen) if self.layout & 1 and self.layout & 2: getattr(self, self.settings.style)() @@ -860,19 +928,21 @@ class HingePin(BaseEdge): self.edge(l - plen - glen) getattr(self, self.settings.style)(True) + ############################################################################# #### Click Joints ############################################################################# class ClickSettings(Settings): absolute_params = { - "angle" : 5, - } + "angle": 5, + } relative_params = { - "depth" : 3.0, - "bottom_radius" : 0.1, - } + "depth": 3.0, + "bottom_radius": 0.1, + } + class ClickConnector(BaseEdge): char = "c" @@ -886,26 +956,26 @@ class ClickConnector(BaseEdge): c = math.cos(math.radians(a)) s = math.sin(math.radians(a)) - p1 = (0, 90-a, c*d) + p1 = (0, 90 - a, c * d) p2 = ( - d+t, + d + t, -90, - t*0.5, + t * 0.5, 135, - t*2**0.5, + t * 2 ** 0.5, 135, - d+2*t+s*0.5*t) - p3 = (c*d-s*c*0.2*t, -a, 0) + d + 2 * t + s * 0.5 * t) + p3 = (c * d - s * c * 0.2 * t, -a, 0) if not reverse: self.polyline(*p1) self.corner(-180, r) self.polyline(*p2) - self.corner(-180+2*a, r) + self.corner(-180 + 2 * a, r) self.polyline(*p3) else: self.polyline(*reversed(p3)) - self.corner(-180+2*a, r) + self.corner(-180 + 2 * a, r) self.polyline(*reversed(p2)) self.corner(-180, r) self.polyline(*reversed(p1)) @@ -917,46 +987,48 @@ class ClickConnector(BaseEdge): r = self.settings.bottom_radius c = math.cos(math.radians(a)) s = math.sin(math.radians(a)) + return 2 * s * d * c + 0.5 * c * t + c * 4 * r def hookOffset(self): - t = self.thickness a = self.settings.angle d = self.settings.depth r = self.settings.bottom_radius c = math.cos(math.radians(a)) s = math.sin(math.radians(a)) + return s * d * c + 2 * r def finger(self, length): t = self.thickness self.polyline( - 2*t, + 2 * t, 90, length, 90, - 2*t, + 2 * t, ) def __call__(self, length, **kw): t = self.thickness - self.edge(4*t) + self.edge(4 * t) self.hook() - self.finger(2*t) + self.finger(2 * t) self.hook(reverse=True) - self.edge(length - 2* (6*t + 2*self.hookWidth())) + self.edge(length - 2 * (6 * t + 2 * self.hookWidth())) self.hook() - self.finger(2*t) + self.finger(2 * t) self.hook(reverse=True) - self.edge(4*t) + self.edge(4 * t) def margin(self): return 2 * self.thickness + class ClickEdge(ClickConnector): - char ="C" + char = "C" description = "Click on (top)" def startwidth(self): @@ -970,23 +1042,24 @@ class ClickEdge(ClickConnector): o = self.hookOffset() w = self.hookWidth() p1 = ( - 4*t + o, + 4 * t + o, 90, t, -90, - 2*(t+w-o), + 2 * (t + w - o), -90, t, 90, 0) self.polyline(*p1) - self.edge(length - 2 * (6*t + 2* w) + 2*o) + self.edge(length - 2 * (6 * t + 2 * w) + 2 * o) self.polyline(*reversed(p1)) + ############################################################################# #### Dove Tail Joints ############################################################################# - + class DoveTailSettings(Settings): """Settings used for dove tail joints @@ -1004,54 +1077,61 @@ Values: """ absolute_params = { - "angle" : 50, - } + "angle": 50, + } + relative_params = { - "size" : 3, - "depth" : 1.5, - "radius" : 0.2, - } + "size": 3, + "depth": 1.5, + "radius": 0.2, + } + class DoveTailJoint(BaseEdge): """Edge with dove tail joints """ + char = 'd' description = "Dove Tail Joint" positive = True def __call__(self, length, **kw): s = self.settings - radius = max(s.radius, self.boxes.burn) # no smaller than burn + radius = max(s.radius, self.boxes.burn) # no smaller than burn positive = self.positive a = s.angle + 90 - alpha = 0.5*math.pi - math.pi*s.angle/180.0 + alpha = 0.5 * math.pi - math.pi * s.angle / 180.0 - l1 = radius/math.tan(alpha/2.0) - diffx = 0.5*s.depth/math.tan(alpha) - l2 = 0.5*s.depth / math.sin(alpha) + l1 = radius / math.tan(alpha / 2.0) + diffx = 0.5 * s.depth / math.tan(alpha) + l2 = 0.5 * s.depth / math.sin(alpha) - sections = int((length) // (s.size*2)) - leftover = length - sections*s.size*2 + sections = int((length) // (s.size * 2)) + leftover = length - sections * s.size * 2 p = 1 if positive else -1 - self.edge((s.size+leftover)/2.0+diffx-l1) + self.edge((s.size + leftover) / 2.0 + diffx - l1) + for i in range(sections): - self.corner(-1*p*a, radius) - self.edge(2*(l2-l1)) - self.corner(p*a, radius) - self.edge(2*(diffx-l1)+s.size) - self.corner(p*a, radius) - self.edge(2*(l2-l1)) - self.corner(-1*p*a, radius) - if i= angle * mount_radius: adj_factor = 1.2 # wrong value. its probably one of the points distances calculated below mount_radius += adj_factor @@ -458,6 +465,7 @@ class Gears(): if collision: # don't draw spokes if no room. messages.append("Not enough room for Spokes. Decrease Spoke width.") else: # draw spokes + for i in range(spoke_count): self.boxes.ctx.save() start_a, end_a = spokes[i], spokes[i+1] @@ -486,6 +494,7 @@ class Gears(): ) self.boxes.ctx.restore() + return messages def sizes(self, **kw): @@ -517,8 +526,10 @@ class Gears(): def gearCarrier(self, r, spoke_width, positions, mount_radius, mount_hole, circle=True, move=None): width = (r+spoke_width)*2 + if self.boxes.move(width, width, move, before=True): return + try: positions = [i*360/positions for i in range(positions)] except TypeError: @@ -528,11 +539,13 @@ class Gears(): self.boxes.moveTo(width/2.0, width/2.0) self.generate_spokes(r+0.5*spoke_width, spoke_width, positions, mount_radius, mount_hole, 1, "") self.boxes.hole(0, 0, mount_hole) + for angle in positions: self.boxes.ctx.save() self.boxes.moveTo(0, 0, angle) self.boxes.hole(r, 0, mount_hole) self.boxes.ctx.restore() + self.boxes.moveTo(r+0.5*spoke_width+self.boxes.burn, 0, 90) self.boxes.corner(360, r+0.5*spoke_width) @@ -578,6 +591,7 @@ class Gears(): else: accuracy_involute = 6 else: accuracy_involute = self.options.accuracy + accuracy_circular = max(3, int(accuracy_involute/2) - 1) # never less than three # print >>self.tty, "accuracy_circular=%s accuracy_involute=%s" % (accuracy_circular, accuracy_involute) # Pitch (circular pitch): Length of the arc from one tooth to the next) @@ -599,17 +613,20 @@ class Gears(): height = base_height+ 2* addendum if self.boxes.move(width, height, move, before=True): return + self.boxes.cc(callback, None, s+b, s+b) self.boxes.moveTo(width/2.0, base_height+addendum, -180) self.drawPoints(points) self.drawPoints(guide_points, kerfdir=0) self.boxes.move(width, height, move) + return # Move only width = height = 2 * outer_radius if self.options.internal_ring: width = height = width + 2 * self.options.spoke_width + if self.boxes.move(width, height, move, before=True): return @@ -624,6 +641,7 @@ class Gears(): # alas annotation cannot handle the degree symbol. Also it ignore newlines. # so split and make a list warnings.extend(msg.split("\n")) + if self.options.undercut_alert: inkex.debug(msg) else: @@ -653,6 +671,7 @@ class Gears(): self.boxes.moveTo(r, 0) self.boxes.ctx.arc(-r, 0, r, 0, 2*pi) self.boxes.ctx.restore() + # Add center if centercross: cs = pitch / 3.0 # centercross length @@ -667,12 +686,13 @@ class Gears(): if pitchcircle: self.boxes.hole(0, 0, pitch_radius) - # Add Annotations (above) if self.options.annotation: outer_dia = outer_radius * 2 + if self.options.internal_ring: outer_dia += 2 * spoke_width + notes = [] notes.extend(warnings) #notes.append('Document (%s) scale conversion = %2.4f' % (self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')).get(inkex.addNS('document-units', 'inkscape')), unit_factor)) @@ -690,6 +710,7 @@ class Gears(): text_height = max(10, min(10+(outer_dia-60)/24, 22)) # position above y = - outer_radius - (len(notes)+1) * text_height * 1.2 + for note in notes: self.boxes.text(note, -outer_radius, y) y += text_height * 1.2 diff --git a/boxes/generators/box.py b/boxes/generators/box.py index 9cef962..a7b0bec 100755 --- a/boxes/generators/box.py +++ b/boxes/generators/box.py @@ -16,15 +16,17 @@ from boxes import * + class Box(Boxes): """Fully closed box""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.argparser.set_defaults( fingerjointfinger=3.0, fingerjointspace=3.0 - ) + ) def render(self): self.open() @@ -47,16 +49,18 @@ class Box(Boxes): self.rectangularWall(y, h, "FfFf", bedBolts=d3, move="up") self.rectangularWall(y, h, "FfFf", bedBolts=d3) self.rectangularWall(x, h, "FFFF", bedBolts=d2, move="left up") - + self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="right") self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3]) self.close() + def main(): b = Box() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/box2.py b/boxes/generators/box2.py index 6c8cbdb..e1c6ac3 100755 --- a/boxes/generators/box2.py +++ b/boxes/generators/box2.py @@ -120,6 +120,7 @@ class Box2(Boxes): bedBolts=[d2], move="up only") self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="right") + if self.top_edge == "c": self.rectangularWall(x, y, "CCCC", bedBolts=[d2, d3, d2, d3], move="up") elif self.top_edge == "i": diff --git a/boxes/generators/box3.py b/boxes/generators/box3.py index fce4633..1d1a153 100755 --- a/boxes/generators/box3.py +++ b/boxes/generators/box3.py @@ -18,6 +18,7 @@ from boxes import * class Box3(Boxes): """Box with just 3 walls""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") diff --git a/boxes/generators/castle.py b/boxes/generators/castle.py index f19d28e..d8dbefc 100755 --- a/boxes/generators/castle.py +++ b/boxes/generators/castle.py @@ -16,8 +16,8 @@ from boxes import * -class Castle(Boxes): +class Castle(Boxes): webinterface = False def __init__(self): @@ -26,21 +26,21 @@ class Castle(Boxes): def render(self, t_x=70, t_h=250, w1_x=300, w1_h=120, w2_x=100, w2_h=120): self.open() s = edges.FingerJointSettings(self.thickness, relative=False, - space = 10, finger=10, height=10, + space=10, finger=10, height=10, width=self.thickness) + p = edges.FingerJointEdge(self, s) p.char = "p" self.addPart(p) + P = edges.FingerJointEdgeCounterPart(self, s) P.char = "P" self.addPart(P) - self.moveTo(0,0) - self.rectangularWall(t_x, t_h, edges="efPf", move="right", callback= - [lambda: self.fingerHolesAt(t_x*0.5, 0, w1_h, 90),]) + self.moveTo(0, 0) + self.rectangularWall(t_x, t_h, edges="efPf", move="right", callback=[lambda: self.fingerHolesAt(t_x * 0.5, 0, w1_h, 90), ]) self.rectangularWall(t_x, t_h, edges="efPf", move="right") - self.rectangularWall(t_x, t_h, edges="eFPF", move="right", callback= - [lambda: self.fingerHolesAt(t_x*0.5, 0, w2_h, 90),]) + self.rectangularWall(t_x, t_h, edges="eFPF", move="right", callback=[lambda: self.fingerHolesAt(t_x * 0.5, 0, w2_h, 90), ]) self.rectangularWall(t_x, t_h, edges="eFPF", move="right") self.rectangularWall(w1_x, w1_h, "efpe", move="right") @@ -48,10 +48,12 @@ class Castle(Boxes): self.close() + def main(): c = Castle() c.parseArgs() c.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/drillbox.py b/boxes/generators/drillbox.py index e33c9a4..17ed7fc 100755 --- a/boxes/generators/drillbox.py +++ b/boxes/generators/drillbox.py @@ -16,9 +16,11 @@ from boxes import * + class DrillBox(Boxes): """Not yet parametrized box for drills from 1 to 12.5mm in 0.5mm steps, 3 holes each size""" + def __init__(self): Boxes.__init__(self) self.x, self.y, self.h = 120, 240, 60 @@ -31,51 +33,46 @@ in 0.5mm steps, 3 holes each size""" self.fingerHolesAt(0, 5, self.y, angle=0) self.fingerHolesAt(0, 25, self.y, angle=0) - def drillholes(self): for i in range(6): for j in range(4): for k in range(3): - r = (12.5-2*i-0.5*j) * 0.5 - self.hole(i*20+10, j*60+k*20+10, r+0.05) + r = (12.5 - 2 * i - 0.5 * j) * 0.5 + self.hole(i * 20 + 10, j * 60 + k * 20 + 10, r + 0.05) def description(self): self.ctx.set_font_size(6) for i in range(4): for j in range(6): - self.rectangularHole(i*60+30, 20*j+10, 58, 14+1*j) - d = 2.5-0.5*i+2*j - self.text("%.1f" % d, i*60+20, 19*j+6, + self.rectangularHole(i * 60 + 30, 20 * j + 10, 58, 14 + 1 * j) + d = 2.5 - 0.5 * i + 2 * j + self.text("%.1f" % d, i * 60 + 20, 19 * j + 6, align="center") - def render(self): x, y, h = self.x, self.y, self.h t = self.thickness self.open() - self.edges["f"].settings.setValues(self.thickness, space=3, finger=3, - surroundingspaces=1) + self.edges["f"].settings.setValues(self.thickness, space=3, finger=3, surroundingspaces=1) - self.rectangularWall(x, h, "FfeF", callback=[self.holesx],move="right") + self.rectangularWall(x, h, "FfeF", callback=[self.holesx], move="right") self.rectangularWall(y, h, "FfeF", callback=[self.holesy], move="up") self.rectangularWall(y, h, "FfeF", callback=[self.holesy]) - self.rectangularWall(x, h, "FfeF", callback=[self.holesx], - move="left up") - + self.rectangularWall(x, h, "FfeF", callback=[self.holesx], move="left up") + self.rectangularWall(x, y, "ffff", move="up") - self.rectangularWall(x, y, "ffff", callback=[self.drillholes], - move="up") - self.rectangularWall(x, y, "ffff", callback=[self.drillholes, - self.description], - move="up") + self.rectangularWall(x, y, "ffff", callback=[self.drillholes], move="up") + self.rectangularWall(x, y, "ffff", callback=[self.drillholes, self.description], move="up") self.close() + def main(): b = DrillBox() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/flexbox.py b/boxes/generators/flexbox.py index e954d53..01cd870 100755 --- a/boxes/generators/flexbox.py +++ b/boxes/generators/flexbox.py @@ -17,29 +17,33 @@ import boxes import math + class FlexBox(boxes.Boxes): """Box with living hinge and round corners""" + def __init__(self): boxes.Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.argparser.add_argument( - "--radius", action="store", type=float, default=15, + "--radius", action="store", type=float, default=15, help="Radius of the latch in mm") - + @boxes.restore def flexBoxSide(self, x, y, r, callback=None): self.moveTo(r, 0) + for i, l in zip(range(2), (x, y)): self.cc(callback, i) - self.edges["f"](l-2*r) + self.edges["f"](l - 2 * r) self.corner(90, r) + self.cc(callback, 2) - self.edge(x-2*r) + self.edge(x - 2 * r) self.corner(90, r) self.cc(callback, 3) self.latch(self.latchsize) self.cc(callback, 4) - self.edges["f"](y-2*r-self.latchsize) + self.edges["f"](y - 2 * r - self.latchsize) self.corner(90, r) def surroundingWall(self): @@ -47,33 +51,33 @@ class FlexBox(boxes.Boxes): c4 = math.pi * r * 0.5 - self.edges["F"](y-2*r-self.latchsize, False) - if x-2*r < self.thickness: - self.edges["X"](2*c4+x-2*r, h+2*self.thickness) + self.edges["F"](y - 2 * r - self.latchsize, False) + if x - 2 * r < self.thickness: + self.edges["X"](2 * c4 + x - 2 * r, h + 2 * self.thickness) else: - self.edges["X"](c4, h+2*self.thickness) - self.edges["F"](x-2*r, False) - self.edges["X"](c4, h+2*self.thickness) - self.edges["F"](y-2*r, False) - if x-2*r < self.thickness: - self.edges["X"](2*c4+x-2*r, h+2*self.thickness) + self.edges["X"](c4, h + 2 * self.thickness) + self.edges["F"](x - 2 * r, False) + self.edges["X"](c4, h + 2 * self.thickness) + self.edges["F"](y - 2 * r, False) + if x - 2 * r < self.thickness: + self.edges["X"](2 * c4 + x - 2 * r, h + 2 * self.thickness) else: - self.edges["X"](c4, h+2*self.thickness) - self.edge(x-2*r) - self.edges["X"](c4, h+2*self.thickness) + self.edges["X"](c4, h + 2 * self.thickness) + self.edge(x - 2 * r) + self.edges["X"](c4, h + 2 * self.thickness) self.latch(self.latchsize, False) - self.edge(h+2*self.thickness) + self.edge(h + 2 * self.thickness) self.latch(self.latchsize, False, True) self.edge(c4) - self.edge(x-2*r) + self.edge(x - 2 * r) self.edge(c4) - self.edges["F"](y-2*r, False) + self.edges["F"](y - 2 * r, False) self.edge(c4) - self.edges["F"](x-2*r, False) + self.edges["F"](x - 2 * r, False) self.edge(c4) - self.edges["F"](y-2*r-self.latchsize, False) + self.edges["F"](y - 2 * r - self.latchsize, False) self.corner(90) - self.edge(h+2*self.thickness) + self.edge(h + 2 * self.thickness) self.corner(90) def render(self): @@ -85,27 +89,29 @@ class FlexBox(boxes.Boxes): x, y, h = self.x, self.y, self.h self.latchsize = 8 * self.thickness - r = self.radius or min(x, y-self.latchsize)/2.0 - r = min(r, x/2.0) - self.radius = r = min(r, max(0, (y-self.latchsize)/2.0)) + r = self.radius or min(x, y - self.latchsize) / 2.0 + r = min(r, x / 2.0) + self.radius = r = min(r, max(0, (y - self.latchsize) / 2.0)) c4 = math.pi * r * 0.5 self.open() self.moveTo(self.thickness, self.thickness) self.surroundingWall() - self.moveTo(self.thickness, self.h+5*self.thickness) + self.moveTo(self.thickness, self.h + 5 * self.thickness) self.flexBoxSide(self.x, self.y, self.radius) - self.moveTo(2*self.x+3*self.thickness, 0) + self.moveTo(2 * self.x + 3 * self.thickness, 0) self.ctx.scale(-1, 1) self.flexBoxSide(self.x, self.y, self.radius) self.close() + def main(): b = FlexBox() b.parseArgs() b.render() -if __name__=="__main__": + +if __name__ == "__main__": main() diff --git a/boxes/generators/flexbox2.py b/boxes/generators/flexbox2.py index 99ca4c3..462bc53 100755 --- a/boxes/generators/flexbox2.py +++ b/boxes/generators/flexbox2.py @@ -17,49 +17,53 @@ from boxes import * import math + class FlexBox2(Boxes): """Box with living hinge and top corners rounded""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.argparser.add_argument( - "--radius", action="store", type=float, default=15, + "--radius", action="store", type=float, default=15, help="Radius of the corners in mm") - + @restore def flexBoxSide(self, x, y, r, callback=None): self.cc(callback, 0) self.edges["f"](x) self.corner(90, 0) self.cc(callback, 1) - self.edges["f"](y-r) + self.edges["f"](y - r) self.corner(90, r) self.cc(callback, 2) - self.edge(x-2*r) + self.edge(x - 2 * r) self.corner(90, r) self.cc(callback, 3) self.latch(self.latchsize) self.cc(callback, 4) - self.edges["f"](y-r-self.latchsize) + self.edges["f"](y - r - self.latchsize) self.corner(90) def surroundingWall(self): x, y, h, r = self.x, self.y, self.h, self.radius - - self.edges["F"](y-r, False) - if (x-2*r < self.thickness): - self.edges["X"](2*self.c4+x-2*r, h+2*self.thickness) + + self.edges["F"](y - r, False) + + if (x - 2 * r < self.thickness): + self.edges["X"](2 * self.c4 + x - 2 * r, h + 2 * self.thickness) else: - self.edges["X"](self.c4, h+2*self.thickness) - self.edge(x-2*r) - self.edges["X"](self.c4, h+2*self.thickness) + self.edges["X"](self.c4, h + 2 * self.thickness) + self.edge(x - 2 * r) + self.edges["X"](self.c4, h + 2 * self.thickness) + self.latch(self.latchsize, False) - self.edge(h+2*self.thickness) + self.edge(h + 2 * self.thickness) self.latch(self.latchsize, False, True) self.edge(self.c4) - self.edge(x-2*r) + self.edge(x - 2 * r) self.edge(self.c4) - self.edges["F"](y-r) + self.edges["F"](y - r) self.corner(90) self.edge(self.thickness) self.edges["f"](h) @@ -67,41 +71,48 @@ class FlexBox2(Boxes): self.corner(90) def render(self): + if self.outside: self.x = self.adjustSize(self.x) self.y = self.adjustSize(self.y) self.h = self.adjustSize(self.h) - self.latchsize = 8*self.thickness - self.radius = self.radius or min(self.x/2.0, self.y-self.latchsize) - self.radius = min(self.radius, self.x/2.0) - self.radius = min(self.radius, max(0, self.y-self.latchsize)) + self.latchsize = 8 * self.thickness + self.radius = self.radius or min(self.x / 2.0, self.y - self.latchsize) + self.radius = min(self.radius, self.x / 2.0) + self.radius = min(self.radius, max(0, self.y - self.latchsize)) self.c4 = c4 = math.pi * self.radius * 0.5 self.open() self.fingerJointSettings = (4, 4) - self.moveTo(2*self.thickness, self.thickness) + self.moveTo(2 * self.thickness, self.thickness) self.ctx.save() self.surroundingWall() - self.moveTo(self.x+self.y-3*self.radius+2*self.c4+self.latchsize+1*self.thickness, 0) + + self.moveTo(self.x + self.y - 3 * self.radius + 2 * self.c4 + self.latchsize + 1 * self.thickness, 0) self.rectangularWall(self.x, self.h, edges="FFFF") self.ctx.restore() - self.moveTo(0, self.h+4*self.thickness) + + self.moveTo(0, self.h + 4 * self.thickness) self.flexBoxSide(self.x, self.y, self.radius) - self.moveTo(2*self.x+3*self.thickness, 0) + + self.moveTo(2 * self.x + 3 * self.thickness, 0) self.ctx.scale(-1, 1) self.flexBoxSide(self.x, self.y, self.radius) self.ctx.scale(-1, 1) - self.moveTo(2*self.thickness, 0) - self.rectangularWall(self.h, self.y-self.radius-self.latchsize, edges="fFeF") + + self.moveTo(2 * self.thickness, 0) + self.rectangularWall(self.h, self.y - self.radius - self.latchsize, edges="fFeF") self.close() + def main(): b = FlexBox2() b.parseArgs() b.render() -if __name__=="__main__": + +if __name__ == "__main__": main() diff --git a/boxes/generators/flexbox3.py b/boxes/generators/flexbox3.py index 6454b5c..26583b3 100755 --- a/boxes/generators/flexbox3.py +++ b/boxes/generators/flexbox3.py @@ -17,22 +17,24 @@ from boxes import * import math + class FlexBox3(Boxes): """Box with living hinge""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "outside") self.argparser.add_argument( - "--z", action="store", type=float, default=100.0, + "--z", action="store", type=float, default=100.0, help="height of the box") self.argparser.add_argument( - "--h", action="store", type=float, default=10.0, + "--h", action="store", type=float, default=10.0, help="height of the lid") self.argparser.add_argument( - "--radius", action="store", type=float, default=10.0, + "--radius", action="store", type=float, default=10.0, help="radius of the lids living hinge") self.argparser.add_argument( - "--c", action="store", type=float, default=1.0, + "--c", action="store", type=float, default=1.0, dest="d", help="clearance of the lid") def rectangleCorner(self, edge1=None, edge2=None): @@ -50,10 +52,10 @@ class FlexBox3(Boxes): self.edges["f"](x) self.corner(90, 0) self.cc(callback, 1) - self.edges["f"](y-r) + self.edges["f"](y - r) self.corner(90, r) self.cc(callback, 2) - self.edge(x-r) + self.edge(x - r) self.corner(90, 0) self.cc(callback, 3) self.edges["f"](y) @@ -61,22 +63,22 @@ class FlexBox3(Boxes): def surroundingWall(self): x, y, z, r, d = self.x, self.y, self.z, self.radius, self.d - - self.edges["F"](y-r, False) - self.edges["X"](self.c4, z+2*self.thickness) + + self.edges["F"](y - r, False) + self.edges["X"](self.c4, z + 2 * self.thickness) self.corner(-90) self.edge(d) self.corner(90) - self.edges["f"](x-r+d) + self.edges["f"](x - r + d) self.corner(90) - self.edges["f"](z+2*self.thickness+2*d) + self.edges["f"](z + 2 * self.thickness + 2 * d) self.corner(90) - self.edges["f"](x-r+d) + self.edges["f"](x - r + d) self.corner(90) self.edge(d) self.corner(-90) self.edge(self.c4) - self.edges["F"](y-r) + self.edges["F"](y - r) self.corner(90) self.edge(self.thickness) self.edges["f"](z) @@ -87,16 +89,16 @@ class FlexBox3(Boxes): def lidSide(self): x, y, z, r, d, h = self.x, self.y, self.z, self.radius, self.d, self.h t = self.thickness - r2 = r+t if r+t <=h+t else h+t + r2 = r + t if r + t <= h + t else h + t self.moveTo(self.thickness, self.thickness) - self.edge(h+self.thickness-r2) + self.edge(h + self.thickness - r2) self.corner(90, r2) - self.edge(r-r2+2*t) - self.edges["F"](x-r) + self.edge(r - r2 + 2 * t) + self.edges["F"](x - r) self.rectangleCorner("F", "f") self.edges["g"](h) self.rectangleCorner("f", "e") - self.edge(x+2*t) + self.edge(x + 2 * t) def render(self): if self.outside: @@ -105,56 +107,57 @@ class FlexBox3(Boxes): self.z = self.adjustSize(self.z) x, y, z, d, h = self.x, self.y, self.z, self.d, self.h - r = self.radius = self.radius or min(x, y)/2.0 + r = self.radius = self.radius or min(x, y) / 2.0 thickness = self.thickness self.c4 = c4 = math.pi * r * 0.5 * 0.95 - self.latchsize = 8*thickness + self.latchsize = 8 * thickness - width = 2*x + y - 2*r + c4 + 14*thickness + 3*h # lock - height = y + z + 8*thickness + width = 2 * x + y - 2 * r + c4 + 14 * thickness + 3 * h # lock + height = y + z + 8 * thickness self.open() - self.edges["f"].settings.setValues( - self.thickness, finger=2, space=2, surroundingspaces=1) + self.edges["f"].settings.setValues(self.thickness, finger=2, space=2, surroundingspaces=1) s = edges.FingerJointSettings(self.thickness, surroundingspaces=1) g = edges.FingerJointEdge(self, s) g.char = "g" self.addPart(g) + G = edges.FingerJointEdgeCounterPart(self, s) G.char = "G" self.addPart(G) - self.moveTo(2*self.thickness, self.thickness+2*d) + self.moveTo(2 * self.thickness, self.thickness + 2 * d) self.ctx.save() self.surroundingWall() - self.moveTo(x+y-2*r+self.c4+2*self.thickness, -2*d-self.thickness) + self.moveTo(x + y - 2 * r + self.c4 + 2 * self.thickness, -2 * d - self.thickness) self.rectangularWall(x, z, edges="FFFF", move="right") - self.rectangularWall(h, z+2*(d+self.thickness), - edges="GeGF", move="right") + self.rectangularWall(h, z + 2 * (d + self.thickness), edges="GeGF", move="right") self.lidSide() - self.moveTo(2*h+5*self.thickness, 0) + self.moveTo(2 * h + 5 * self.thickness, 0) self.ctx.scale(-1, 1) self.lidSide() self.ctx.restore() - self.moveTo(0, z+4*self.thickness+2*d) + self.moveTo(0, z + 4 * self.thickness + 2 * d) self.flexBoxSide(x, y, r) - self.moveTo(2*x+3*self.thickness, 2*d) + self.moveTo(2 * x + 3 * self.thickness, 2 * d) self.ctx.scale(-1, 1) self.flexBoxSide(x, y, r) self.ctx.scale(-1, 1) - self.moveTo(2*self.thickness, -self.thickness) + self.moveTo(2 * self.thickness, -self.thickness) self.rectangularWall(z, y, edges="fFeF") self.close() + def main(): - b = FlexBox3() #100, 40, 100, r=20, h=10, thickness=4.0) + b = FlexBox3() # 100, 40, 100, r=20, h=10, thickness=4.0) b.parseArgs() b.render() -if __name__=="__main__": + +if __name__ == "__main__": main() diff --git a/boxes/generators/flexbox4.py b/boxes/generators/flexbox4.py index 6110762..d68dce6 100644 --- a/boxes/generators/flexbox4.py +++ b/boxes/generators/flexbox4.py @@ -17,51 +17,54 @@ from boxes import * import math + class FlexBox4(Boxes): """Box with living hinge and left corners rounded""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "h", "outside") self.argparser.add_argument( - "--radius", action="store", type=float, default=15, + "--radius", action="store", type=float, default=15, help="Radius of the corners in mm") - + @restore def flexBoxSide(self, x, y, r, callback=None): self.cc(callback, 0) self.edges["f"](x) self.corner(90, 0) self.cc(callback, 1) - self.edges["f"](y-r) + self.edges["f"](y - r) self.corner(90, r) self.cc(callback, 2) - self.edge(x-2*r) + self.edge(x - 2 * r) self.corner(90, r) self.cc(callback, 3) - self.edges["e"](y-r-self.latchsize) + self.edges["e"](y - r - self.latchsize) self.cc(callback, 4) self.latch(self.latchsize) self.corner(90) def surroundingWall(self): x, y, h, r = self.x, self.y, self.h, self.radius - - self.edges["F"](y-r, False) - if (x-2*r < self.thickness): - self.edges["X"](2*self.c4+x-2*r, h+2*self.thickness) + + self.edges["F"](y - r, False) + if (x - 2 * r < self.thickness): + self.edges["X"](2 * self.c4 + x - 2 * r, h + 2 * self.thickness) else: - self.edges["X"](self.c4, h+2*self.thickness) - self.edge(x-2*r) - self.edges["X"](self.c4, h+2*self.thickness) - self.edge(y-r-self.latchsize) + self.edges["X"](self.c4, h + 2 * self.thickness) + self.edge(x - 2 * r) + self.edges["X"](self.c4, h + 2 * self.thickness) + + self.edge(y - r - self.latchsize) self.latch(self.latchsize, False) - self.edge(h+2*self.thickness) + self.edge(h + 2 * self.thickness) self.latch(self.latchsize, False, True) - self.edge(y-r-self.latchsize) + self.edge(y - r - self.latchsize) self.edge(self.c4) - self.edge(x-2*r) + self.edge(x - 2 * r) self.edge(self.c4) - self.edges["F"](y-r) + self.edges["F"](y - r) self.corner(90) self.edge(self.thickness) self.edges["f"](h) @@ -75,33 +78,35 @@ class FlexBox4(Boxes): self.h = self.adjustSize(self.h) self.c4 = c4 = math.pi * self.radius * 0.5 - self.latchsize = 8*self.thickness - self.radius = self.radius or min(self.x/2.0, self.y-self.latchsize) - self.radius = min(self.radius, self.x/2.0) - self.radius = min(self.radius, max(0, self.y-self.latchsize)) + self.latchsize = 8 * self.thickness + self.radius = self.radius or min(self.x / 2.0, self.y - self.latchsize) + self.radius = min(self.radius, self.x / 2.0) + self.radius = min(self.radius, max(0, self.y - self.latchsize)) self.open() self.fingerJointSettings = (4, 4) - self.moveTo(2*self.thickness, self.thickness) + self.moveTo(2 * self.thickness, self.thickness) self.ctx.save() self.surroundingWall() self.ctx.restore() - self.moveTo(0, self.h+4*self.thickness) + self.moveTo(0, self.h + 4 * self.thickness) self.flexBoxSide(self.x, self.y, self.radius) - self.moveTo(2*self.x+3*self.thickness, 0) + self.moveTo(2 * self.x + 3 * self.thickness, 0) self.ctx.scale(-1, 1) self.flexBoxSide(self.x, self.y, self.radius) self.ctx.scale(-1, 1) - self.moveTo(2*self.thickness,-self.thickness) + self.moveTo(2 * self.thickness, -self.thickness) self.rectangularWall(self.x, self.h, edges="FeFF") self.close() + def main(): b = FlexBox4() b.parseArgs() b.render() -if __name__=="__main__": + +if __name__ == "__main__": main() diff --git a/boxes/generators/flextest.py b/boxes/generators/flextest.py index 4aa8d81..4ab16b3 100755 --- a/boxes/generators/flextest.py +++ b/boxes/generators/flextest.py @@ -16,19 +16,21 @@ from boxes import * + class FlexTest(Boxes): "Piece for testing different flex settings" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y") self.argparser.add_argument( - "--fd", action="store", type=float, default=0.5, + "--fd", action="store", type=float, default=0.5, help="distance of flex cuts in multiples of thickness") self.argparser.add_argument( - "--fc", action="store", type=float, default=1.0, + "--fc", action="store", type=float, default=1.0, help="connections of flex cuts in multiples of thickness") self.argparser.add_argument( - "--fw", action="store", type=float, default=5.0, + "--fw", action="store", type=float, default=5.0, help="width of flex cuts in multiples of thickness") def render(self): @@ -46,17 +48,19 @@ class FlexTest(Boxes): self.corner(90) self.edge(y) self.corner(90) - self.edge(x+20) + self.edge(x + 20) self.corner(90) self.edge(y) self.corner(90) self.close() + def main(): f = FlexTest() f.parseArgs() f.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/flextest2.py b/boxes/generators/flextest2.py index 70c3a88..95dfd32 100644 --- a/boxes/generators/flextest2.py +++ b/boxes/generators/flextest2.py @@ -16,26 +16,30 @@ from boxes import * + class FlexTest2(Boxes): "Piece for testing 2D flex settings" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y") self.argparser.add_argument( - "--fw", action="store", type=float, default=1, + "--fw", action="store", type=float, default=1, help="distance of flex cuts in multiples of thickness") def render(self): x, y = self.x, self.y - t = self.thickness + self.open() - self.rectangularWall(x, y, callback=[lambda:self.flex2D(x, y, self.fw)]) + self.rectangularWall(x, y, callback=[lambda: self.flex2D(x, y, self.fw)]) self.close() + def main(): f = FlexTest() f.parseArgs() f.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/folder.py b/boxes/generators/folder.py index 306fb3d..48ea5f2 100755 --- a/boxes/generators/folder.py +++ b/boxes/generators/folder.py @@ -17,6 +17,7 @@ from boxes import * import math + class Folder(Boxes): """Book cover with flex for the spine""" @@ -32,18 +33,18 @@ class Folder(Boxes): x, y, r, h = self.x, self.y, self.r, self.h c2 = math.pi * h self.open() - self.moveTo(r+self.thickness, self.thickness) - self.edge(x-r) + self.moveTo(r + self.thickness, self.thickness) + self.edge(x - r) self.edges["X"](c2, y) - self.edge(x-r) + self.edge(x - r) self.corner(90, r) - self.edge(y-2*r) + self.edge(y - 2 * r) self.corner(90, r) - self.edge(2*x-2*r+c2) + self.edge(2 * x - 2 * r + c2) self.corner(90, r) - self.edge(y-2*r) + self.edge(y - 2 * r) self.corner(90, r) - + self.close() @@ -52,5 +53,6 @@ def main(): f.parseArgs() f.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/gearbox.py b/boxes/generators/gearbox.py index fabba7b..1fa3a44 100644 --- a/boxes/generators/gearbox.py +++ b/boxes/generators/gearbox.py @@ -16,89 +16,91 @@ from boxes import * + class GearBox(Boxes): """Gearbox with multiple identical stages""" + def __init__(self): Boxes.__init__(self) self.argparser.add_argument( - "--teeth1", action="store", type=int, default=8, + "--teeth1", action="store", type=int, default=8, help="number of teeth on ingoing shaft") self.argparser.add_argument( - "--teeth2", action="store", type=int, default=20, + "--teeth2", action="store", type=int, default=20, help="number of teeth on outgoing shaft") self.argparser.add_argument( - "--modulus", action="store", type=float, default=3, + "--modulus", action="store", type=float, default=3, help="modulus of the theeth in mm") self.argparser.add_argument( - "--shaft", action="store", type=float, default=6., + "--shaft", action="store", type=float, default=6., help="diameter of the shaft") self.argparser.add_argument( - "--stages", action="store", type=int, default=4, + "--stages", action="store", type=int, default=4, help="number of stages in the gear reduction") def render(self): # Initialize canvas self.open() - + if self.teeth2 < self.teeth1: self.teeth2, self.teeth1 = self.teeth1, self.teeth2 - pitch1, size1, xxx = self.gears.sizes(teeth=self.teeth1, - dimension=self.modulus) - pitch2, size2, xxx = self.gears.sizes(teeth=self.teeth2, - dimension=self.modulus) + pitch1, size1, xxx = self.gears.sizes(teeth=self.teeth1, dimension=self.modulus) + pitch2, size2, xxx = self.gears.sizes(teeth=self.teeth2, dimension=self.modulus) t = self.thickness - x = 1.1*t*self.stages + x = 1.1 * t * self.stages + if self.stages == 1: y = size1 + size2 - y1 = y/2-(pitch1+pitch2)+pitch1 - y2 = y/2+(pitch1+pitch2)-pitch2 + y1 = y / 2 - (pitch1 + pitch2) + pitch1 + y2 = y / 2 + (pitch1 + pitch2) - pitch2 else: y = 2 * size2 - y1 = y/2 - (pitch1+pitch2)/2 - y2 = y/2 + (pitch1+pitch2)/2 + y1 = y / 2 - (pitch1 + pitch2) / 2 + y2 = y / 2 + (pitch1 + pitch2) / 2 h = max(size1, size2) + t b = "F" - t = "e" # prepare for close box + t = "e" # prepare for close box mh = self.shaft - def sideCB(): - self.hole(y1, h/2, mh/2) - self.hole(y2, h/2, mh/2) - + self.hole(y1, h / 2, mh / 2) + self.hole(y2, h / 2, mh / 2) + self.moveTo(self.thickness, self.thickness) self.rectangularWall(y, h, [b, "f", t, "f"], callback=[sideCB], move="right") self.rectangularWall(x, h, [b, "F", t, "F"], move="up") self.rectangularWall(x, h, [b, "F", t, "F"]) self.rectangularWall(y, h, [b, "f", t, "f"], callback=[sideCB], move="left") self.rectangularWall(x, h, [b, "F", t, "F"], move="up only") - + self.rectangularWall(x, y, "ffff", move="up") profile_shift = 20 pressure_angle = 20 - for i in range(self.stages-1): - self.gears(teeth=self.teeth2, dimension=self.modulus, - angle=pressure_angle, + + for i in range(self.stages - 1): + self.gears(teeth=self.teeth2, dimension=self.modulus, angle=pressure_angle, mount_hole=mh, profile_shift=profile_shift, move="up") - self.gears(teeth=self.teeth2, dimension=self.modulus, - angle=pressure_angle, + + self.gears(teeth=self.teeth2, dimension=self.modulus, angle=pressure_angle, mount_hole=mh, profile_shift=profile_shift, move="right") + for i in range(self.stages): - self.gears(teeth=self.teeth1, dimension=self.modulus, - angle=pressure_angle, + self.gears(teeth=self.teeth1, dimension=self.modulus, angle=pressure_angle, mount_hole=mh, profile_shift=profile_shift, move="down") - + self.close() + def main(): - b = Box() + b = GearBox() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/jigsaw.py b/boxes/generators/jigsaw.py index d472df6..3e80e54 100644 --- a/boxes/generators/jigsaw.py +++ b/boxes/generators/jigsaw.py @@ -17,34 +17,34 @@ from boxes import * import random -class JigsawPuzzle(Boxes): # change class name here and below + +class JigsawPuzzle(Boxes): # change class name here and below """Fractal jigsaw puzzle. Still aplha""" - - webinterface = False # Change to make visible in web interface + + webinterface = False # Change to make visible in web interface def __init__(self): Boxes.__init__(self) self.count = 0 self.argparser.add_argument( - "--size", action="store", type=float, default=100, + "--size", action="store", type=float, default=100, help="size of the puzzle in mm") self.argparser.add_argument( - "--depth", action="store", type=int, default=5, + "--depth", action="store", type=int, default=5, help="depth of the recursion/level of detail") - def peano(self, level): if level == 0: - self.edge(self.size/self.depth) + self.edge(self.size / self.depth) return - self.peano(self, level-1) - self.corner() + self.peano(self, level - 1) + self.corner() def edge(self, l): self.count += 1 Boxes.edge(self, l) - #if (self.count % 2**5) == 0: #level == 3 and parity>0: + # if (self.count % 2**5) == 0: #level == 3 and parity>0: # self.corner(-360, 0.25*self.size/2**self.depth) def hilbert(self, level, parity=1): @@ -53,28 +53,31 @@ class JigsawPuzzle(Boxes): # change class name here and below # rotate and draw first subcurve with opposite parity to big curve self.corner(parity * 90) self.hilbert(level - 1, -parity) + # interface to and draw second subcurve with same parity as big curve - self.edge(self.size/2**self.depth) + self.edge(self.size / 2 ** self.depth) self.corner(parity * -90) self.hilbert(level - 1, parity) + # third subcurve - self.edge(self.size/2**self.depth) + self.edge(self.size / 2 ** self.depth) self.hilbert(level - 1, parity) - #if level == 3: self.corner(-360, 0.4*self.size/2**self.depth) + + # if level == 3: self.corner(-360, 0.4*self.size/2**self.depth) # fourth subcurve self.corner(parity * -90) - self.edge(self.size/2**self.depth) + self.edge(self.size / 2 ** self.depth) self.hilbert(level - 1, -parity) # a final turn is needed to make the turtle # end up facing outward from the large square self.corner(parity * 90) - #if level == 3 and parity>0: # and random.random() < 100*0.5**(self.depth-2): + # if level == 3 and parity>0: # and random.random() < 100*0.5**(self.depth-2): # self.corner(-360, 0.4*self.size/2**self.depth) - #self.ctx.save() - #self.corner(parity*-90) - #self.edge(self.size/2**self.depth) - #self.ctx.restore() - + # self.ctx.save() + # self.corner(parity*-90) + # self.edge(self.size/2**self.depth) + # self.ctx.restore() + def render(self): size = self.size t = self.thickness @@ -85,10 +88,12 @@ class JigsawPuzzle(Boxes): # change class name here and below self.hilbert(self.depth) self.close() + def main(): b = JigsawPuzzle() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/lamp.py b/boxes/generators/lamp.py index c82d99d..066d2f6 100755 --- a/boxes/generators/lamp.py +++ b/boxes/generators/lamp.py @@ -17,37 +17,39 @@ from boxes import * import math - """ 22x7.5x7cm D=23cm, d=21cm d = 8" D = 9" """ + class RoundedTriangleSettings(edges.Settings): absolute_params = { - "angle" : 60, - "radius" : 30, - "r_hole" : None, - } + "angle": 60, + "radius": 30, + "r_hole": None, + } + class RoundedTriangle(edges.Edge): char = "t" + def __call__(self, length, **kw): angle = self.settings.angle r = self.settings.radius if self.settings.r_hole: - x = 0.5*(length-2*r)*math.tan(math.radians(angle)) - y = 0.5*(length) + x = 0.5 * (length - 2 * r) * math.tan(math.radians(angle)) + y = 0.5 * (length) self.hole(x, y, self.settings.r_hole) - l = 0.5 * (length-2*r) / math.cos(math.radians(angle)) - self.corner(90-angle, r) + l = 0.5 * (length - 2 * r) / math.cos(math.radians(angle)) + self.corner(90 - angle, r) self.edge(l) - self.corner(2*angle, r) + self.corner(2 * angle, r) self.edge(l) - self.corner(90-angle, r) + self.corner(90 - angle, r) def startAngle(self): return 90 @@ -55,8 +57,8 @@ class RoundedTriangle(edges.Edge): def endAngle(self): return 90 -class Lamp(Boxes): +class Lamp(Boxes): webinterface = False def __init__(self): @@ -82,7 +84,7 @@ class Lamp(Boxes): self.open() - #self.edges["f"].settings = (5, 5) # XXX + # self.edges["f"].settings = (5, 5) # XXX s = RoundedTriangleSettings(self.thickness, angle=72, r_hole=2) self.addPart(RoundedTriangle(self, s)) @@ -90,19 +92,20 @@ class Lamp(Boxes): self.flexSettings = (3, 5.0, 20.0) self.edges["f"].settings.setValues(self.thickness, finger=5, space=5, relative=False) - d = 2*(r+w) + d = 2 * (r + w) self.roundedPlate(d, d, r, move="right", callback=[ - lambda: self.hole(w, r+w, r),]) - #dist = ((2**0.5)*r-r) / (2**0.5) + 4 - #pos = (w-dist, dist) - self.roundedPlate(d, d, r, holesMargin=w/2.0) #, callback=[ + lambda: self.hole(w, r + w, r), ]) + + # dist = ((2**0.5)*r-r) / (2**0.5) + 4 + # pos = (w-dist, dist) + self.roundedPlate(d, d, r, holesMargin=w / 2.0) # , callback=[ # lambda: self.hole(pos[0], pos[1], 7),]) self.roundedPlate(d, d, r, move="only left up") hole = lambda: self.hole(w, 70, 2) self.surroundingWall(d, d, r, 120, top='h', bottom='h', callback=[ - None, hole, None, hole], move="up") + None, hole, None, hole], move="up") self.ctx.save() self.rectangularWall(x, y, edges="fFfF", holesMargin=5, move="right") @@ -119,10 +122,12 @@ class Lamp(Boxes): self.close() + def main(): l = Lamp() l.parseArgs() - l.render(r=4*25.4, w=20, x=270, y=150, h=100) + l.render(r=4 * 25.4, w=20, x=270, y=150, h=100) + if __name__ == '__main__': main() diff --git a/boxes/generators/magazinefile.py b/boxes/generators/magazinefile.py index 7959d0e..948b1d9 100755 --- a/boxes/generators/magazinefile.py +++ b/boxes/generators/magazinefile.py @@ -16,25 +16,28 @@ from boxes import * + class MagazinFile(Boxes): """Open magazine file""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("x", "y", "h", "hi", "outside") self.argparser.set_defaults( fingerjointfinger=2.0, fingerjointspace=2.0 - ) + ) def side(self, w, h, hi): - r = min(h-hi, w) / 2.0 - if (h-hi) > w: + r = min(h - hi, w) / 2.0 + + if (h - hi) > w: r = w / 2.0 lx = 0 - ly = (h-hi) - w + ly = (h - hi) - w else: r = (h - hi) / 2.0 - lx = (w - 2*r) / 2.0 + lx = (w - 2 * r) / 2.0 ly = 0 e_w = self.edges["F"].startwidth() @@ -51,15 +54,15 @@ class MagazinFile(Boxes): self.corner(-90, r) self.edge(ly) self.corner(90, r) - self.edge(lx) + self.edge(lx) self.edge(e_w) self.corner(90) self.edges["F"](h) self.edge(e_w) self.corner(90) - def render(self): + if self.outside: self.x = self.adjustSize(self.x) self.y = self.adjustSize(self.y) @@ -80,15 +83,17 @@ class MagazinFile(Boxes): self.rectangularWall(x, h, "Ffef", move="right only") self.side(y, h, hi) - self.moveTo(y+15, h+hi+15, 180) + self.moveTo(y + 15, h + hi + 15, 180) self.side(y, h, hi) self.close() + def main(): b = MagazinFile() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/planetary.py b/boxes/generators/planetary.py index b9646d3..521a0cf 100644 --- a/boxes/generators/planetary.py +++ b/boxes/generators/planetary.py @@ -17,29 +17,31 @@ from boxes import * import math + class Planetary(Boxes): """Gearbox with multiple identical stages""" + def __init__(self): Boxes.__init__(self) self.argparser.add_argument( - "--sunteeth", action="store", type=int, default=8, + "--sunteeth", action="store", type=int, default=8, help="number of teeth on sun gear") self.argparser.add_argument( - "--planetteeth", action="store", type=int, default=20, + "--planetteeth", action="store", type=int, default=20, help="number of teeth on planets") self.argparser.add_argument( - "--maxplanets", action="store", type=int, default=0, + "--maxplanets", action="store", type=int, default=0, help="limit the number of planets (0 for as much as fit)") self.argparser.add_argument( - "--deltateeth", action="store", type=int, default=0, + "--deltateeth", action="store", type=int, default=0, help="enable secondary ring with given delta to the ring gear") self.argparser.add_argument( - "--modulus", action="store", type=float, default=3, + "--modulus", action="store", type=float, default=3, help="modulus of the theeth in mm") self.argparser.add_argument( - "--shaft", action="store", type=float, default=6., + "--shaft", action="store", type=float, default=6., help="diameter of the shaft") - #self.argparser.add_argument( + # self.argparser.add_argument( # "--stages", action="store", type=int, default=4, # help="number of stages in the gear reduction") @@ -47,48 +49,50 @@ class Planetary(Boxes): # Initialize canvas self.open() - ringteeth = self.sunteeth+2*self.planetteeth - spoke_width = 3*self.shaft - + ringteeth = self.sunteeth + 2 * self.planetteeth + spoke_width = 3 * self.shaft + pitch1, size1, xxx = self.gears.sizes(teeth=self.sunteeth, - dimension=self.modulus) + dimension=self.modulus) pitch2, size2, xxx = self.gears.sizes(teeth=self.planetteeth, - dimension=self.modulus) + dimension=self.modulus) pitch3, size3, xxx = self.gears.sizes( teeth=ringteeth, internal_ring=True, spoke_width=spoke_width, dimension=self.modulus) t = self.thickness - planets = int(math.pi//math.asin((self.planetteeth+2)/(self.planetteeth+self.sunteeth))) + planets = int(math.pi // math.asin((self.planetteeth + 2) / (self.planetteeth + self.sunteeth))) + if self.maxplanets: planets = min(self.maxplanets, planets) # Make sure the teeth mash - ta = self.sunteeth+ringteeth + ta = self.sunteeth + ringteeth # There are sunteeth+ringteeth mashing positions for the planets if ta % planets: - planetpositions = [round(i*ta/planets)*360/ta for i in range(planets)] + planetpositions = [round(i * ta / planets) * 360 / ta for i in range(planets)] else: planetpositions = planets # XXX make configurable? profile_shift = 20 pressure_angle = 20 - self.parts.disc(size3, callback=lambda:self.hole(0,0,self.shaft/2), move="up") + self.parts.disc(size3, callback=lambda: self.hole(0, 0, self.shaft / 2), move="up") self.gears(teeth=ringteeth, dimension=self.modulus, angle=pressure_angle, internal_ring=True, spoke_width=spoke_width, mount_hole=self.shaft, profile_shift=profile_shift, move="up") - self.gears.gearCarrier(pitch1+pitch2, spoke_width, planetpositions, - 2*spoke_width, self.shaft/2, move="up") + self.gears.gearCarrier(pitch1 + pitch2, spoke_width, planetpositions, + 2 * spoke_width, self.shaft / 2, move="up") self.gears(teeth=self.sunteeth, dimension=self.modulus, angle=pressure_angle, mount_hole=self.shaft, profile_shift=profile_shift, move="up") numplanets = planets + if self.deltateeth: numplanets += planets - deltamodulus = self.modulus*ringteeth/(ringteeth-self.deltateeth) - self.gears(teeth=ringteeth-self.deltateeth, dimension=deltamodulus, + deltamodulus = self.modulus * ringteeth / (ringteeth - self.deltateeth) + self.gears(teeth=ringteeth - self.deltateeth, dimension=deltamodulus, angle=pressure_angle, internal_ring=True, spoke_width=spoke_width, mount_hole=self.shaft, profile_shift=profile_shift, move="up") @@ -97,13 +101,15 @@ class Planetary(Boxes): self.gears(teeth=self.planetteeth, dimension=self.modulus, angle=pressure_angle, mount_hole=self.shaft, profile_shift=profile_shift, move="up") - + self.close() + def main(): b = Box() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/printer.py b/boxes/generators/printer.py index 4939a09..71a90c5 100755 --- a/boxes/generators/printer.py +++ b/boxes/generators/printer.py @@ -16,8 +16,8 @@ from boxes import * -class Printer(Boxes): +class Printer(Boxes): """Work in progress""" webinterface = False @@ -30,46 +30,48 @@ class Printer(Boxes): # idlers self.D_i = 17.0 self.d_i = 5.0 - self.w_i = 7.0 # includes washers - + self.w_i = 7.0 # includes washers def mainPlate(self, nr): r = self.r t2 = 0.5 * self.thickness if nr: return - self.moveTo(r-5, r, -90) - self.hole(0, 0, r-80) + self.moveTo(r - 5, r, -90) + self.hole(0, 0, r - 80) D_i2 = self.D_i / 2 w_i2 = self.w_i / 2 - d_c2 = self.d_c/2 + d_c2 = self.d_c / 2 + for i in range(6): self.ctx.save() - self.moveTo(0, 0, i*60) + self.moveTo(0, 0, i * 60) + # winches if i % 2: - self.fingerHolesAt(r-80, (d_c2+20), 70, angle=0) - self.fingerHolesAt(r-80, -(d_c2+20), 70, angle=0) - if i==5: - self.fingerHolesAt(r-70+t2, -(d_c2+20+t2), 40, angle=-90) + self.fingerHolesAt(r - 80, (d_c2 + 20), 70, angle=0) + self.fingerHolesAt(r - 80, -(d_c2 + 20), 70, angle=0) + if i == 5: + self.fingerHolesAt(r - 70 + t2, -(d_c2 + 20 + t2), 40, angle=-90) else: - self.fingerHolesAt(r-70+t2, (d_c2+20+t2), 40, angle=90) + self.fingerHolesAt(r - 70 + t2, (d_c2 + 20 + t2), 40, angle=90) # idler buck else: - d = 0.5*(self.thickness)+w_i2 - for y in (-d-d_c2, d-d_c2, -d+d_c2, d+d_c2): - self.fingerHolesAt(r-30, y, 30, angle=0) - self.hole(r-15+D_i2, -self.d_c/2, 0.4) - self.hole(r-15+D_i2, self.d_c/2, 0.4) - self.ctx.restore() + d = 0.5 * (self.thickness) + w_i2 + for y in (-d - d_c2, d - d_c2, -d + d_c2, d + d_c2): + self.fingerHolesAt(r - 30, y, 30, angle=0) + + self.hole(r - 15 + D_i2, -self.d_c / 2, 0.4) + self.hole(r - 15 + D_i2, self.d_c / 2, 0.4) + self.ctx.restore() def head(self): d_c = self.d_c - self.moveTo(self.spacing+10, self.spacing) + self.moveTo(self.spacing + 10, self.spacing) for i in range(3): self.hole(0, 5, 0.3) self.fingerHolesAt(25, 0, 20) @@ -78,20 +80,20 @@ class Printer(Boxes): self.hole(0, 5, 0.3) self.corner(120, 10) - def support(self, x, y, edges="ff", pair=False, callback=None, move=None): if len(edges) != 2: raise ValueError("Two edges required") - edges = [self.edges.get(e, e,) for e in edges] + + edges = [self.edges.get(e, e, ) for e in edges] overallwidth = x + edges[0].spacing() + self.edges["e"].spacing() overallheight = y + edges[1].spacing() + self.edges["e"].spacing() - r = 2*self.thickness + r = 2 * self.thickness if pair: - overallwidth+= edges[0].spacing() + r - self.edges["e"].spacing() - overallheight+= edges[1].spacing() + r - self.edges["e"].spacing() + overallwidth += edges[0].spacing() + r - self.edges["e"].spacing() + overallheight += edges[1].spacing() + r - self.edges["e"].spacing() if self.move(overallwidth, overallheight, move, before=True): return @@ -99,16 +101,16 @@ class Printer(Boxes): self.ctx.save() self.moveTo(edges[0].margin(), edges[1].margin()) - angle = math.degrees(math.atan((y-r)/float(x-r))) + angle = math.degrees(math.atan((y - r) / float(x - r))) self.cc(callback, 0) edges[1](x) self.corner(90) - #self.edge(self.thickness) - self.corner(90-angle, r) - self.edge(((x-r)**2+(y-r)**2)**0.5) + # self.edge(self.thickness) + self.corner(90 - angle, r) + self.edge(((x - r) ** 2 + (y - r) ** 2) ** 0.5) self.corner(angle, r) - #self.edge(self.thickness) + # self.edge(self.thickness) self.corner(90) self.cc(callback, 0) edges[0](y) @@ -127,61 +129,69 @@ class Printer(Boxes): self.open() self.edges["f"].settings.setValues(self.thickness, surroundingspaces=0) self.ctx.save() + for i in range(3): # motor mounts self.rectangularWall(70, 70, edges="feee", callback=[ - lambda: self.NEMA(23, 35, 35),], + lambda: self.NEMA(23, 35, 35), ], move="right") # winch bucks self.rectangularWall(50, 70, edges="efee", callback=[ - None, - lambda: self.hole(35, 35, 8.5), - None, - lambda: self.fingerHolesAt(10, 0, 50)], move="right") + None, + lambda: self.hole(35, 35, 8.5), + None, + lambda: self.fingerHolesAt(10, 0, 50)], move="right") + self.support(40, 50, move="right", pair=True) self.support(40, 50, move="right") self.ctx.restore() self.moveTo(0, 80) self.ctx.save() + # idler bucks for i in range(12): self.rectangularWall(30, 30, edges="feee", callback=[ - lambda: self.hole(15, 15, 3),], move="right") + lambda: self.hole(15, 15, 3), ], move="right") # Cable adjustment blocks self.ctx.save() + for i in range(6): def holes(): self.hole(5, 4, 1.5) self.hole(15, 4, 1.5) - self.rectangularWall(20, 8, edges="feee", callback=[holes,], + + self.rectangularWall(20, 8, edges="feee", callback=[holes, ], move="right") self.ctx.restore() self.moveTo(0, 20) + # Cable adjustment glyders for i in range(6): self.rectangularWall(8, 10, move="right", callback=[ - lambda: self.hole(4, 4, 1.5), - None, - lambda: self.hole(4, 1.5, 0.4)]) + lambda: self.hole(4, 4, 1.5), + None, + lambda: self.hole(4, 1.5, 0.4)]) self.rectangularWall(8, 10, move="right", callback=[ - lambda: self.nutHole("M3", 4, 4), - None, - lambda: self.hole(4, 1.5, 0.4)]) + lambda: self.nutHole("M3", 4, 4), + None, + lambda: self.hole(4, 1.5, 0.4)]) self.ctx.restore() self.moveTo(0, 40) # mainPlate - self.rectangularWall(2*self.r-10, 2*self.r-10, edges="ffff", + self.rectangularWall(2 * self.r - 10, 2 * self.r - 10, edges="ffff", callback=self.mainPlate, move="right") - + self.head() self.close() + def main(): p = Printer() p.parseArgs() p.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/pulley.py b/boxes/generators/pulley.py index 95bad57..fff6dbf 100644 --- a/boxes/generators/pulley.py +++ b/boxes/generators/pulley.py @@ -18,64 +18,73 @@ from boxes import * from boxes import pulley import math + class Pulley(Boxes): """Timing belt pulleys for different profiles""" + def __init__(self): Boxes.__init__(self) # remove cli params you do not need self.buildArgParser("h") self.argparser.add_argument( - "--profile", action="store", type=str, default="GT2_2mm", + "--profile", action="store", type=str, default="GT2_2mm", choices=pulley.Pulley.getProfiles(), help="profile of the teeth/belt") self.argparser.add_argument( - "--teeth", action="store", type=int, default=20, + "--teeth", action="store", type=int, default=20, help="number of teeth") self.argparser.add_argument( - "--axle", action="store", type=float, default=5, + "--axle", action="store", type=float, default=5, help="diameter of the axle") self.argparser.add_argument( - "--top", action="store", type=float, default=0, + "--top", action="store", type=float, default=0, help="overlap of top rim (zero for none)") - + # Add non default cli params if needed (see argparse std lib) - #self.argparser.add_argument( + # self.argparser.add_argument( # "--XX", action="store", type=float, default=0.5, # help="DESCRIPTION") - def disk(self, diameter, hole, callback=None, move=""): - w = diameter + 2*self.spacing + w = diameter + 2 * self.spacing + if self.move(w, w, move, before=True): return + self.ctx.save() - self.moveTo(w/2, w/2) + self.moveTo(w / 2, w / 2) self.cc(callback, None, 0.0, 0.0) + if hole: - self.hole(0, 0, hole/2.0) - self.moveTo(diameter/2+self.burn, 0, 90) - self.corner(360, diameter/2) + self.hole(0, 0, hole / 2.0) + + self.moveTo(diameter / 2 + self.burn, 0, 90) + self.corner(360, diameter / 2) self.ctx.restore() self.move(w, w, move) - + def render(self): # adjust to the variables you want in the local scope t = self.thickness # Initialize canvas self.open() + if self.top: self.disk( - self.pulley.diameter(self.teeth, self.profile)+2*self.top, + self.pulley.diameter(self.teeth, self.profile) + 2 * self.top, self.axle, move="right") - for i in range(int(math.ceil(self.h/self.thickness))): - self.pulley(self.teeth, self.profile, r_axle=self.axle/2.0, move="right") + + for i in range(int(math.ceil(self.h / self.thickness))): + self.pulley(self.teeth, self.profile, r_axle=self.axle / 2.0, move="right") self.close() + def main(): b = Box() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/rotary.py b/boxes/generators/rotary.py index 0290c24..a656d43 100644 --- a/boxes/generators/rotary.py +++ b/boxes/generators/rotary.py @@ -16,49 +16,56 @@ from boxes import * + class MotorEdge(edges.BaseEdge): - #def margin(self): + # def margin(self): # return 30 def __call__(self, l, **kw): self.polyline( - l-165, 45, - 25*2**0.5, -45, + l - 165, 45, + 25 * 2 ** 0.5, -45, 60, -45, - 25*2**0.5, 45, + 25 * 2 ** 0.5, 45, 55) + class OutsetEdge(edges.OutSetEdge): def startwidth(self): return 20 + class HangerEdge(edges.BaseEdge): char = "H" + def margin(self): return 40 def __call__(self, l, **kw): - self.fingerHolesAt(0, -0.5*self.thickness, l, angle=0) + self.fingerHolesAt(0, -0.5 * self.thickness, l, angle=0) w = self.settings self.polyline(0, -90, - 22+w, 90, + 22 + w, 90, 70, 135, - 2**0.5*12, 45, + 2 ** 0.5 * 12, 45, 35, -45, - 2**0.5*0.5*w, -90, - 2**0.5*0.5*w, -45, - l -28, 45, - 2**0.5*5, 45, 5, -90) + 2 ** 0.5 * 0.5 * w, -90, + 2 ** 0.5 * 0.5 * w, -45, + l - 28, 45, + 2 ** 0.5 * 5, 45, 5, -90) + class RollerEdge(edges.BaseEdge): def margin(self): return 20 + def __call__(self, l, **kw): - m = 40+100 - self.polyline((l-m)/2.0, -45, - 2**0.5*20, 45, + m = 40 + 100 + self.polyline((l - m) / 2.0, -45, + 2 ** 0.5 * 20, 45, 100, 45, - 2**0.5*20, -45, - (l-m)/2.0) + 2 ** 0.5 * 20, -45, + (l - m) / 2.0) + class RollerEdge2(edges.BaseEdge): def margin(self): @@ -66,98 +73,102 @@ class RollerEdge2(edges.BaseEdge): def __call__(self, l, **kw): a = 30 - f = 1/math.cos(math.radians(a)) + f = 1 / math.cos(math.radians(a)) self.edges["f"](70) - self.polyline(0, a, f*25, -a, l-190, -a, f*25, a, 0) + self.polyline(0, a, f * 25, -a, l - 190, -a, f * 25, a, 0) self.edges["f"](70) + class Rotary(Boxes): """Rotary Attachment for engraving cylindrical objects in a laser cutter""" + def __init__(self): Boxes.__init__(self) # remove cli params you do not need - #self.buildArgParser("x", "sx", "y", "sy", "h", "hi") + # self.buildArgParser("x", "sx", "y", "sy", "h", "hi") # Add non default cli params if needed (see argparse std lib) self.argparser.add_argument( - "--diameter", action="store", type=float, default=72., + "--diameter", action="store", type=float, default=72., help="outer diameter of the wheels") self.argparser.add_argument( - "--rubberthickness", action="store", type=float, default=5., + "--rubberthickness", action="store", type=float, default=5., help="diameter of the strings of the O rings") self.argparser.add_argument( - "--axle", action="store", type=float, default=6., + "--axle", action="store", type=float, default=6., help="diameter of the axles") self.argparser.add_argument( - "--knifethickness", action="store", type=float, default=8., + "--knifethickness", action="store", type=float, default=8., help="thickness of the knifes in mm. Use 0 for use with honey comb table.") - - def mainPlate(self): # Motor block outer side t = self.thickness d = self.diameter a = self.axle - self.hole(1.0*d, 0.6*d, a/2.) - #self.hole(1.0*d, 0.6*d, d/2.) - self.hole(2.0*d+5, 0.6*d, a/2.) - #self.hole(2.0*d+5, 0.6*d, d/2.) + self.hole(1.0 * d, 0.6 * d, a / 2.) + # self.hole(1.0*d, 0.6*d, d/2.) + self.hole(2.0 * d + 5, 0.6 * d, a / 2.) + # self.hole(2.0*d+5, 0.6*d, d/2.) # Main beam - self.rectangularHole(1.5*d+2.5, 3.6, 32, 7.1) + self.rectangularHole(1.5 * d + 2.5, 3.6, 32, 7.1) def frontPlate(self): # Motor block inner side with motor mount t = self.thickness d = self.diameter a = self.axle - self.hole(1.0*d, 0.6*d, a/2.) - #self.hole(1.0*d, 0.6*d, d/2.) - self.hole(2.0*d+5, 0.6*d, a/2.) - #self.hole(2.0*d+5, 0.6*d, d/2.) + self.hole(1.0 * d, 0.6 * d, a / 2.) + # self.hole(1.0*d, 0.6*d, d/2.) + self.hole(2.0 * d + 5, 0.6 * d, a / 2.) + # self.hole(2.0*d+5, 0.6*d, d/2.) # Main beam - self.rectangularHole(1.5*d+2.5, 3.6, 32, 7.1) + self.rectangularHole(1.5 * d + 2.5, 3.6, 32, 7.1) # Motor - mx = 2.7*d+20 - self.rectangularHole(mx, 0.6*d, 36+20, 36, r=36/2.0) + mx = 2.7 * d + 20 + self.rectangularHole(mx, 0.6 * d, 36 + 20, 36, r=36 / 2.0) for x in (-1, 1): - for y in (-1,1): - self.rectangularHole(mx+x*25, 0.6*d+y*25, 20, 4, r=2) + for y in (-1, 1): + self.rectangularHole(mx + x * 25, 0.6 * d + y * 25, 20, 4, r=2) def link(self, x, y, a, middleHole=False, move=None): - t = self. thickness + t = self.thickness overallwidth = x + y overallheight = y - ra = a/2.0 + ra = a / 2.0 + if self.move(overallwidth, overallheight, move, before=True): return - self.moveTo(y/2.0, 0) - self.hole(0, y/2., ra) - self.hole(x, y/2., ra) + self.moveTo(y / 2.0, 0) + self.hole(0, y / 2., ra) + self.hole(x, y / 2., ra) + if middleHole: - self.hole(x/2., y/2., ra) + self.hole(x / 2., y / 2., ra) + self.edge(10) self.edges["F"](60) - self.polyline(x-70, (180, y/2.), x, (180, y/2.)) + self.polyline(x - 70, (180, y / 2.), x, (180, y / 2.)) self.ctx.stroke() self.move(overallwidth, overallheight, move) def holderBaseCB(self): - self.hole(20, 30, self.a/2) - self.rectangularHole(self.hl-70, self.hh-10, 110, self.a, r=self.a/2) - self.rectangularHole(self.hl/2, 3.6, 32, 7.1) + self.hole(20, 30, self.a / 2) + self.rectangularHole(self.hl - 70, self.hh - 10, 110, self.a, r=self.a / 2) + self.rectangularHole(self.hl / 2, 3.6, 32, 7.1) def holderTopCB(self): - self.fingerHolesAt(0, 30-0.5*self.thickness, self.hl, 0) - d = self.diameter/2.0 + 1 - y = -0.6*self.diameter + 2*self.hh + self.fingerHolesAt(0, 30 - 0.5 * self.thickness, self.hl, 0) + d = self.diameter / 2.0 + 1 + y = -0.6 * self.diameter + 2 * self.hh print(y) - self.hole(self.hl/2+d, y, self.axle/2.0) - self.hole(self.hl/2-d, y, self.axle/2.0) - self.hole(self.hl/2+d, y, self.diameter/2.0) - self.hole(self.hl/2-d, y, self.diameter/2.0) + + self.hole(self.hl / 2 + d, y, self.axle / 2.0) + self.hole(self.hl / 2 - d, y, self.axle / 2.0) + self.hole(self.hl / 2 + d, y, self.diameter / 2.0) + self.hole(self.hl / 2 - d, y, self.diameter / 2.0) def render(self): # adjust to the variables you want in the local scope @@ -166,11 +177,11 @@ class Rotary(Boxes): a = self.a = self.axle # Initialize canvas self.open() - #self.spacing = 0.1 * t + # self.spacing = 0.1 * t # Change settings of default edges if needed. E.g.: self.edges["f"].settings.setValues(self.thickness, space=2, finger=2, - surroundingspaces=1) + surroundingspaces=1) if self.knifethickness: self.addPart(HangerEdge(self, self.knifethickness)) else: @@ -181,124 +192,137 @@ class Rotary(Boxes): hh = self.hh = 40. hl = self.hl = 240 # Base - self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB, None, lambda:self.rectangularHole(hl/2+50, hh-t/2-1, 60, t+2)], move="up") + self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB, None, + lambda: self.rectangularHole(hl / 2 + 50, hh - t / 2 - 1, + 60, t + 2)], move="up") self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB], move="up") - self.rectangularWall(hl, hw, edges="ffff", callback=[ - lambda: self.hole(hl/2-16-20, 25, 5)], move="up") + self.rectangularWall(hl, hw, edges="ffff", callback=[lambda: self.hole(hl / 2 - 16 - 20, 25, 5)], move="up") self.ctx.save() - self.rectangularWall(hw, hh, edges="hFeF", callback=[ - lambda: self.hole(hw/2, 15, 4)],move="right") + + self.rectangularWall(hw, hh, edges="hFeF", callback=[lambda: self.hole(hw / 2, 15, 4)], move="right") self.rectangularWall(hw, hh, edges="hFeF", move="right") # Top th = 30 # sides - self.rectangularWall(hw+20, th, edges="fFeF", move="right", - callback=[lambda:self.fingerHolesAt(20-0.5*t,0,th)]) - self.rectangularWall(hw+20, th, edges="fFeF", move="right", - callback=[lambda:self.fingerHolesAt(20-0.5*t,0,th)]) + + self.rectangularWall(hw + 20, th, edges="fFeF", move="right", + callback=[lambda: self.fingerHolesAt(20 - 0.5 * t, 0, th)]) + self.rectangularWall(hw + 20, th, edges="fFeF", move="right", + callback=[lambda: self.fingerHolesAt(20 - 0.5 * t, 0, th)]) self.ctx.restore() + self.rectangularWall(hw, hh, edges="hFeF", move="up only") outset = OutsetEdge(self, None) roller2 = RollerEdge2(self, None) self.rectangularWall(hl, th, edges=[roller2, "f", "e", "f"], callback=[ - lambda:self.hole(20, 15, a/2), None, lambda:self.rectangularHole(50, th-15, 70, a, r=a/2)], move="up") + lambda: self.hole(20, 15, a / 2), None, lambda: self.rectangularHole(50, th - 15, 70, a, r=a / 2)], + move="up") self.rectangularWall(hl, th, edges=[roller2, "f", "e", "f"], callback=[ - lambda:self.hole(20, 15, a/2), None, lambda:self.rectangularHole(50, th-15-t, 70, a, r=a/2)], move="up") + lambda: self.hole(20, 15, a / 2), None, lambda: self.rectangularHole(50, th - 15 - t, 70, a, r=a / 2)], + move="up") self.rectangularWall(hl, th, edges=[roller2, "f", RollerEdge(self, None), "f"], callback=[ self.holderTopCB], move="up") - self.rectangularWall(hl, 20-t, edges="feee", move="up") + self.rectangularWall(hl, 20 - t, edges="feee", move="up") tl = 70 - self.rectangularWall(tl, hw+20, edges="FeFF", move="right", - callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)]) - self.rectangularWall(tl, hw+20, edges="FeFF", move="", - callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)]) - self.rectangularWall(tl, hw+20, edges="FeFF", move="left up only", - callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)]) + self.rectangularWall(tl, hw + 20, edges="FeFF", move="right", + callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)]) + self.rectangularWall(tl, hw + 20, edges="FeFF", move="", + callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)]) + self.rectangularWall(tl, hw + 20, edges="FeFF", move="left up only", + callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)]) # Links - self.link(hl-40, 25, a, True, move="up") - self.link(hl-40, 25, a, True, move="up") - self.link(hl-40, 25, a, True, move="up") - self.link(hl-40, 25, a, True, move="up") + self.link(hl - 40, 25, a, True, move="up") + self.link(hl - 40, 25, a, True, move="up") + self.link(hl - 40, 25, a, True, move="up") + self.link(hl - 40, 25, a, True, move="up") self.ctx.save() - self.rectangularWall(hw-2*t-2, 60, edges="efef",move="right") - self.rectangularWall(hw-4*t-4, 60, edges="efef",move="right") + self.rectangularWall(hw - 2 * t - 2, 60, edges="efef", move="right") + self.rectangularWall(hw - 4 * t - 4, 60, edges="efef", move="right") # Spindel auxiliaries - self.parts.waivyKnob(50, callback=lambda:self.nutHole("M8"), move="right") - self.parts.waivyKnob(50, callback=lambda:self.nutHole("M8"), move="right") + self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right") + self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right") self.ctx.restore() - self.rectangularWall(hw-2*t-4, 60, edges="efef",move="up only") + self.rectangularWall(hw - 2 * t - 4, 60, edges="efef", move="up only") self.ctx.save() - slot = edges.SlottedEdge(self, [(30-t)/2, (30-t)/2], slots=15) + slot = edges.SlottedEdge(self, [(30 - t) / 2, (30 - t) / 2], slots=15) self.rectangularWall(30, 30, edges=["e", "e", slot, "e"], - callback=[lambda:self.hole(7, 23, self.axle/2)], move="right") + callback=[lambda: self.hole(7, 23, self.axle / 2)], move="right") self.rectangularWall(30, 30, edges=["e", "e", slot, "e"], - callback=[lambda:self.hole(7, 23, self.axle/2)], move="right") - leftover = (hw-6*t-6-20) / 2.0 + callback=[lambda: self.hole(7, 23, self.axle / 2)], move="right") + leftover = (hw - 6 * t - 6 - 20) / 2.0 slot = edges.SlottedEdge(self, [leftover, 20, leftover], slots=15) - self.rectangularWall(hw-4*t-6, 30, edges=[slot, "e", "e", "e"], - callback=[lambda:self.hole((hw-4*t-6)/2., 15, 4)], move="right") + self.rectangularWall(hw - 4 * t - 6, 30, edges=[slot, "e", "e", "e"], + callback=[lambda: self.hole((hw - 4 * t - 6) / 2., 15, 4)], move="right") for i in range(3): self.rectangularWall(20, 30, - callback=[lambda:self.nutHole("M8", 10, 15)], move="right") + callback=[lambda: self.nutHole("M8", 10, 15)], move="right") self.rectangularWall(20, 30, - callback=[lambda:self.hole(10, 15, 4)], move="right") - + callback=[lambda: self.hole(10, 15, 4)], move="right") + self.ctx.restore() self.rectangularWall(30, 30, move="up only") + # Other side if self.knifethickness: ow = 10 - self.rectangularWall(3.6*d, 1.1*d, edges="hfFf", callback=[ - lambda:self.rectangularHole(1.8*d, 3.6, 32, 7.1)], move="up") - self.rectangularWall(3.6*d, 1.1*d, edges="hfFf", callback=[ - lambda:self.rectangularHole(1.8*d, 3.6, 32, 7.1)], move="up") - self.rectangularWall(3.6*d, ow, edges="ffff", move="up") - self.rectangularWall(3.6*d, ow, edges="ffff", move="up") + self.rectangularWall(3.6 * d, 1.1 * d, edges="hfFf", callback=[ + lambda: self.rectangularHole(1.8 * d, 3.6, 32, 7.1)], move="up") + self.rectangularWall(3.6 * d, 1.1 * d, edges="hfFf", callback=[ + lambda: self.rectangularHole(1.8 * d, 3.6, 32, 7.1)], move="up") + self.rectangularWall(3.6 * d, ow, edges="ffff", move="up") + self.rectangularWall(3.6 * d, ow, edges="ffff", move="up") self.ctx.save() - self.rectangularWall(ow, 1.1*d, edges="hFFH", move="right") - self.rectangularWall(ow, 1.1*d, edges="hFFH", move="right") + self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="right") + self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="right") self.ctx.restore() - self.rectangularWall(ow, 1.1*d, edges="hFFH", move="up only") - + self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="up only") + # Motor block mw = 40 - self.rectangularWall(3.6*d, 1.1*d, edges=["h", "f", MotorEdge(self, None),"f"], callback=[self.mainPlate], move="up") - self.rectangularWall(3.6*d, 1.1*d, edges=["h", "f", MotorEdge(self, None),"f"], callback=[self.frontPlate], move="up") - self.rectangularWall(3.6*d, mw, edges="ffff", move="up") + self.rectangularWall(3.6 * d, 1.1 * d, edges=["h", "f", MotorEdge(self, None), "f"], callback=[self.mainPlate], + move="up") + self.rectangularWall(3.6 * d, 1.1 * d, edges=["h", "f", MotorEdge(self, None), "f"], callback=[self.frontPlate], + move="up") + self.rectangularWall(3.6 * d, mw, edges="ffff", move="up") self.ctx.save() - self.rectangularWall(mw, 1.1*d, edges="hFeH", move="right") - self.rectangularWall(mw, 1.1*d, edges="hFeH", move="right") + self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="right") + self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="right") - self.pulley(88, "GT2_2mm", r_axle=a/2.0,move="right") - self.pulley(88, "GT2_2mm", r_axle=a/2.0,move="right") + self.pulley(88, "GT2_2mm", r_axle=a / 2.0, move="right") + self.pulley(88, "GT2_2mm", r_axle=a / 2.0, move="right") self.ctx.restore() - self.rectangularWall(mw, 1.1*d, edges="hFeH", move="up only") + self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="up only") self.axle = 19 + for i in range(3): - self.parts.disc(self.diameter-2*self.rubberthickness, + self.parts.disc(self.diameter - 2 * self.rubberthickness, hole=self.axle, move="right") - self.parts.disc(self.diameter-2*self.rubberthickness, + self.parts.disc(self.diameter - 2 * self.rubberthickness, hole=self.axle, move="up right") + for i in range(3): - self.parts.disc(self.diameter-2*self.rubberthickness, + self.parts.disc(self.diameter - 2 * self.rubberthickness, hole=self.axle, move="left") - self.parts.disc(self.diameter-2*self.rubberthickness, + self.parts.disc(self.diameter - 2 * self.rubberthickness, hole=self.axle, move="left up") + for i in range(3): - self.parts.disc(self.diameter-2*self.rubberthickness+4, + self.parts.disc(self.diameter - 2 * self.rubberthickness + 4, hole=self.axle, move="right") - self.parts.disc(self.diameter-2*self.rubberthickness+4, + self.parts.disc(self.diameter - 2 * self.rubberthickness + 4, hole=self.axle, move="right up") self.close() + def main(): b = Box() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/silverwarebox.py b/boxes/generators/silverwarebox.py index aa05d50..27a8e61 100755 --- a/boxes/generators/silverwarebox.py +++ b/boxes/generators/silverwarebox.py @@ -16,6 +16,7 @@ from boxes import Boxes + class Silverware(Boxes): """Not yet parametrized cuttlery stand with carrying grip using flex for rounded corners""" @@ -26,20 +27,20 @@ using flex for rounded corners""" def basePlate(self, x, y, r): self.roundedPlate(x, y, r, callback=[ - lambda: self.fingerHolesAt(x/3.0-r, 0, 0.5*(y-self.thickness)), - lambda: self.fingerHolesAt(x/6.0, 0, 0.5*(y-self.thickness)), - lambda: self.fingerHolesAt(y/2.0-r, 0, x), - lambda: self.fingerHolesAt(x/2.0-r, 0, 0.5*(y-self.thickness)) - ]) + lambda: self.fingerHolesAt(x / 3.0 - r, 0, 0.5 * (y - self.thickness)), + lambda: self.fingerHolesAt(x / 6.0, 0, 0.5 * (y - self.thickness)), + lambda: self.fingerHolesAt(y / 2.0 - r, 0, x), + lambda: self.fingerHolesAt(x / 2.0 - r, 0, 0.5 * (y - self.thickness)) + ]) def wall(self, x=100, y=100, h=100, r=0): - self.surroundingWall(x,y,r,h, bottom='h', callback={ - 0 : lambda: self.fingerHolesAt(x/6.0, 0, h-10), - 4 : lambda: self.fingerHolesAt(x/3.0-r, 0, h-10), - 1 : lambda: self.fingerHolesAt(y/2.0-r, 0, h-10), - 3 : lambda: self.fingerHolesAt(y/2.0-r, 0, h-10), - 2 : lambda: self.fingerHolesAt(x/2.0-r, 0, h-10), - }, + self.surroundingWall(x, y, r, h, bottom='h', callback={ + 0: lambda: self.fingerHolesAt(x / 6.0, 0, h - 10), + 4: lambda: self.fingerHolesAt(x / 3.0 - r, 0, h - 10), + 1: lambda: self.fingerHolesAt(y / 2.0 - r, 0, h - 10), + 3: lambda: self.fingerHolesAt(y / 2.0 - r, 0, h - 10), + 2: lambda: self.fingerHolesAt(x / 2.0 - r, 0, h - 10), + }, move="up") def centerWall(self, x, h): @@ -47,49 +48,52 @@ using flex for rounded corners""" self.moveTo(self.edges["f"].spacing(), self.edges["f"].spacing()) for i in range(2, 5): - self.fingerHolesAt(i*x/6.0, 0, h-10) + self.fingerHolesAt(i * x / 6.0, 0, h - 10) self.edges["f"](x) self.corner(90) - self.edges["f"](h-10) + self.edges["f"](h - 10) self.corner(90) self.handle(x, 150, 120) - #self.handle(x, 40, 30, r=2) + # self.handle(x, 40, 30, r=2) self.corner(90) - self.edges["f"](h-10) + self.edges["f"](h - 10) self.corner(90) self.ctx.restore() - self.moveTo(x+2*self.edges["f"].spacing()) + self.moveTo(x + 2 * self.edges["f"].spacing()) ################################################## ### main ################################################## def render(self): - x, y, h, r = 250, 250/1.618, 120, 30 + x, y, h, r = 250, 250 / 1.618, 120, 30 self.open() t = self.thickness b = self.burn self.wall(x, y, h, r) - self.centerWall(x,h) + self.centerWall(x, h) + + l = (y - t) / 2.0 - l = (y-t)/2.0 for i in range(3): - self.rectangularWall(l, h-10, edges="ffef", move="right") + self.rectangularWall(l, h - 10, edges="ffef", move="right") - self.moveTo(-3.0*(l+2*t+8*b), h-10+2*t+8*b) + self.moveTo(-3.0 * (l + 2 * t + 8 * b), h - 10 + 2 * t + 8 * b) self.basePlate(x, y, r) self.close() + def main(): b = Silverware() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/trayinsert.py b/boxes/generators/trayinsert.py index d51a3e7..9bb779d 100755 --- a/boxes/generators/trayinsert.py +++ b/boxes/generators/trayinsert.py @@ -16,40 +16,44 @@ from boxes import * + class TrayInsert(Boxes): """Tray insert without floor and outer walls - allows only continuous walls""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("sx", "sy", "h", "outside") def render(self): + if self.outside: self.sx = self.adjustSize(self.sx, False, False) self.sy = self.adjustSize(self.sy, False, False) - x = sum(self.sx) + self.thickness * (len(self.sx)-1) - y = sum(self.sy) + self.thickness * (len(self.sy)-1) + x = sum(self.sx) + self.thickness * (len(self.sx) - 1) + y = sum(self.sy) + self.thickness * (len(self.sy) - 1) h = self.h t = self.thickness self.open() # Inner walls - for i in range(len(self.sx)-1): - e = [edges.SlottedEdge(self, self.sy, slots=0.5*h), "e", "e", "e"] - self.rectangularWall(y, h, e, - move="up") - for i in range(len(self.sy)-1): - e = ["e", "e", - edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5*h), "e"] - self.rectangularWall(x, h, e, - move="up") + for i in range(len(self.sx) - 1): + e = [edges.SlottedEdge(self, self.sy, slots=0.5 * h), "e", "e", "e"] + self.rectangularWall(y, h, e, move="up") + + for i in range(len(self.sy) - 1): + e = ["e", "e", edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * h), "e"] + self.rectangularWall(x, h, e, move="up") + self.close() + def main(): b = TrayInsert() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/generators/traylayout.py b/boxes/generators/traylayout.py index b29dac3..b3f6714 100755 --- a/boxes/generators/traylayout.py +++ b/boxes/generators/traylayout.py @@ -18,8 +18,8 @@ import sys, re from boxes import * import boxes -class Layout(Boxes): +class Layout(Boxes): """Generate a typetray from a layout file""" webinterface = False @@ -29,64 +29,69 @@ class Layout(Boxes): self.buildArgParser("h", "hi", "outside") if not webargs: self.argparser.add_argument( - "--input", action="store", type=argparse.FileType('r'), + "--input", action="store", type=argparse.FileType('r'), help="layout file") self.argparser.add_argument( - "--x", action="store", type=int, default=None, + "--x", action="store", type=int, default=None, help="number of compartments side by side") self.argparser.add_argument( - "--y", action="store", type=int, default=None, + "--y", action="store", type=int, default=None, help="number of compartments back to front") else: self.argparser.add_argument( - "--layout", action="store", type=str) + "--layout", action="store", type=str) def fillDefault(self, x, y): self.x = [0.0] * x self.y = [0.0] * y - self.hwalls = [[True for i in range(x)] for j in range(y+1)] - self.vwalls = [[True for i in range(x+1)] for j in range(y)] + self.hwalls = [[True for i in range(x)] for j in range(y + 1)] + self.vwalls = [[True for i in range(x + 1)] for j in range(y)] self.floors = [[True for i in range(x)] for j in range(y)] - + def __str__(self): r = [] + for i, x in enumerate(self.x): r.append(" |" * (i) + " ,> %.1fmm\n" % x) + for hwalls, vwalls, floors, y in zip( self.hwalls, self.vwalls, self.floors, self.y): - r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n") + r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n") r.append("".join((" |"[v] + "X "[f] for v, f in zip(vwalls, floors))) - + " |"[vwalls[-1]] + " %.1fmm\n" % y) + + " |"[vwalls[-1]] + " %.1fmm\n" % y) r.append("".join(("+" + " -"[h] for h in self.hwalls[-1])) + "+\n") + return "".join(r) def vWalls(self, x, y): "Number of vertical walls at a crossing" result = 0 - if y>0 and self.vwalls[y-1][x]: + if y > 0 and self.vwalls[y - 1][x]: result += 1 + if y < len(self.y) and self.vwalls[y][x]: result += 1 + return result def hWalls(self, x, y): "Number of horizontal walls at a crossing" result = 0 - if x>0 and self.hwalls[y][x-1]: + if x > 0 and self.hwalls[y][x - 1]: result += 1 - if x0 and self.floors[y][x-1]) or - (x 0 and self.floors[y][x - 1]) or + (x < len(self.x) and self.floors[y][x])) def hFloor(self, x, y): "Is there foor under horizontal wall" - return ((y>0 and self.floors[y-1][x]) or - (y 0 and self.floors[y - 1][x]) or + (y < len(self.y) and self.floors[y][x])) @restore def edgeAt(self, edge, x, y, length, angle=0): @@ -99,6 +104,7 @@ class Layout(Boxes): self.x = self.adjustSize(self.x) self.y = self.adjustSize(self.y) self.h = self.adjustSize(self.h, e2=False) + if self.hi: self.hi = self.adjustSize(self.hi, e2=False) @@ -117,7 +123,7 @@ class Layout(Boxes): self.open() - self.edges["s"] = boxes.edges.Slot(self, self.hi/2.0) + self.edges["s"] = boxes.edges.Slot(self, self.hi / 2.0) self.edges["C"] = boxes.edges.CrossingFingerHoleEdge(self, self.hi) self.ctx.save() @@ -128,22 +134,30 @@ class Layout(Boxes): h = self.h else: h = self.hi + start = 0 end = 0 + while end < lx: lengths = [] edges = [] - while start < lx and not self.hwalls[y][start]: start += 1 + + while start < lx and not self.hwalls[y][start]: + start += 1 + if start == lx: break + end = start + while end < lx and self.hwalls[y][end]: if self.hFloor(end, y): edges.append("f") else: - edges.append("e") # XXX E? + edges.append("e") # XXX E? + lengths.append(self.x[end]) - edges.append("eCs"[self.vWalls(end+1, y)]) + edges.append("eCs"[self.vWalls(end + 1, y)]) lengths.append(self.thickness) end += 1 @@ -161,7 +175,7 @@ class Layout(Boxes): self.ctx.restore() self.rectangularWall(10, h, "ffef", move="up only") self.ctx.save() - + # Vertical Walls for x in range(lx + 1): if x == 0 or x == lx: @@ -170,20 +184,26 @@ class Layout(Boxes): h = self.hi start = 0 end = 0 + while end < ly: lengths = [] edges = [] - while start < ly and not self.vwalls[start][x]: start += 1 + while start < ly and not self.vwalls[start][x]: + start += 1 + if start == ly: break + end = start - while end < ly and self.vwalls[end][x]: + + while end < ly and self.vwalls[end][x]: if self.vFloor(x, end): edges.append("f") else: - edges.append("e") # XXX E? + edges.append("e") # XXX E? + lengths.append(self.y[end]) - edges.append("eCs"[self.hWalls(x, end+1)]) + edges.append("eCs"[self.hWalls(x, end + 1)]) lengths.append(self.thickness) end += 1 # remove last "slot" @@ -191,23 +211,23 @@ class Layout(Boxes): edges.pop() upper = [{ - "f" : "e", - "s" : "s", - "e" : "e", - "E" : "e", - "C" : "e"}[e] for e in reversed(edges)] + "f": "e", + "s": "s", + "e": "e", + "E": "e", + "C": "e"}[e] for e in reversed(edges)] edges = ["e" if e == "s" else e for e in edges] self.rectangularWall(sum(lengths), h, [ boxes.edges.CompoundEdge(self, edges, lengths), "eFf"[self.hWalls(x, end)], boxes.edges.CompoundEdge(self, upper, list(reversed(lengths))), - "eFf"[self.hWalls(x, start)] ], - move="right") + "eFf"[self.hWalls(x, start)]], + move="right") start = end self.ctx.restore() self.rectangularWall(10, h, "ffef", move="up only") - self.moveTo(2*self.thickness, 2*self.thickness) + self.moveTo(2 * self.thickness, 2 * self.thickness) self.ctx.save() ########################################################## @@ -224,61 +244,61 @@ class Layout(Boxes): else: e = "e" if y < ly and self.floors[y][x]: - if y>0 and self.floors[y-1][x]: + if y > 0 and self.floors[y - 1][x]: # Inside Wall if self.hwalls[y][x]: - self.fingerHolesAt(posx, posy+t2, self.x[x], angle=0) + self.fingerHolesAt(posx, posy + t2, self.x[x], angle=0) else: # Top edge - self.edgeAt(e, posx+self.x[x], posy+t, self.x[x], + self.edgeAt(e, posx + self.x[x], posy + t, self.x[x], -180) - if x==0 or y==0 or not self.floors[y-1][x-1]: - self.edgeAt("e", posx, posy+t, t, -180) - if x==lx-1 or y==0 or not self.floors[y-1][x+1]: - self.edgeAt("e", posx+self.x[x]+t, posy+t, t, -180) - elif y>0 and self.floors[y-1][x]: + if x == 0 or y == 0 or not self.floors[y - 1][x - 1]: + self.edgeAt("e", posx, posy + t, t, -180) + if x == lx - 1 or y == 0 or not self.floors[y - 1][x + 1]: + self.edgeAt("e", posx + self.x[x] + t, posy + t, t, -180) + elif y > 0 and self.floors[y - 1][x]: # Bottom Edge self.edgeAt(e, posx, posy, self.x[x]) - if x==0 or y==ly or not self.floors[y][x-1]: - self.edgeAt("e", posx-t, posy, t) - if x==lx-1 or y==ly or not self.floors[y][x+1]: - self.edgeAt("e", posx+self.x[x], posy, t) - posx += self.x[x] + self. thickness - posy += self.y[y-1] + self.thickness + if x == 0 or y == ly or not self.floors[y][x - 1]: + self.edgeAt("e", posx - t, posy, t) + if x == lx - 1 or y == ly or not self.floors[y][x + 1]: + self.edgeAt("e", posx + self.x[x], posy, t) + posx += self.x[x] + self.thickness + posy += self.y[y - 1] + self.thickness posx = 0 - for x in range(lx+1): + for x in range(lx + 1): posy = self.thickness - for y in range(ly-1, -1, -1): + for y in range(ly - 1, -1, -1): if self.vwalls[y][x]: e = "F" else: e = "e" - if x>0 and self.floors[y][x-1]: + if x > 0 and self.floors[y][x - 1]: if x < lx and self.floors[y][x]: # Inside wall if self.vwalls[y][x]: - self.fingerHolesAt(posx+t2, posy, self.y[y]) + self.fingerHolesAt(posx + t2, posy, self.y[y]) else: # Right edge - self.edgeAt(e, posx+t, posy, self.y[y], 90) - if x==lx or y==0 or not self.floors[y-1][x]: - self.edgeAt("e", posx+t, posy+self.y[y], t, 90) - if x==lx or y==ly-1 or not self.floors[y+1][x]: - self.edgeAt("e", posx+t, posy-t, t, 90) + self.edgeAt(e, posx + t, posy, self.y[y], 90) + if x == lx or y == 0 or not self.floors[y - 1][x]: + self.edgeAt("e", posx + t, posy + self.y[y], t, 90) + if x == lx or y == ly - 1 or not self.floors[y + 1][x]: + self.edgeAt("e", posx + t, posy - t, t, 90) elif x < lx and self.floors[y][x]: # Left edge - self.edgeAt(e, posx, posy+self.y[y], self.y[y], -90) - if x==0 or y==0 or not self.floors[y-1][x-1]: - self.edgeAt("e", posx, posy+self.y[y]+t, t, -90) - if x==0 or y==ly-1 or not self.floors[y+1][x-1]: + self.edgeAt(e, posx, posy + self.y[y], self.y[y], -90) + if x == 0 or y == 0 or not self.floors[y - 1][x - 1]: + self.edgeAt("e", posx, posy + self.y[y] + t, t, -90) + if x == 0 or y == ly - 1 or not self.floors[y + 1][x - 1]: self.edgeAt("e", posx, posy, t, -90) posy += self.y[y] + self.thickness if x < lx: posx += self.x[x] + self.thickness self.close() - + def parse(self, input): x = [] y = [] @@ -302,18 +322,17 @@ class Layout(Boxes): w.append(True) else: pass - #raise ValueError(line) + # raise ValueError(line) else: if c != '+': pass - #raise ValueError(line) + # raise ValueError(line) - hwalls.append(w) if line[0] in " |": w = [] f = [] - for n, c in enumerate(line[:len(x)*2+1]): + for n, c in enumerate(line[:len(x) * 2 + 1]): if n % 2: if c == 'X': f.append(False) @@ -340,25 +359,26 @@ class Layout(Boxes): # check sizes lx = len(x) ly = len(y) - if len(hwalls) != ly+1: - raise ValueError("Wrong number of horizontal wall lines: %i (%i expected)" % (len(hwalls), ly+1)) + if len(hwalls) != ly + 1: + raise ValueError("Wrong number of horizontal wall lines: %i (%i expected)" % (len(hwalls), ly + 1)) for nr, walls in enumerate(hwalls): - if len(walls)!= lx: + if len(walls) != lx: raise ValueError("Wrong number of horizontal walls in line %i: %i (%i expected)" % (nr, len(walls), lx)) if len(vwalls) != ly: raise ValueError("Wrong number of vertical wall lines: %i (%i expected)" % (len(vwalls), ly)) for nr, walls in enumerate(vwalls): - if len(walls) != lx+1: - raise ValueError("Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx+1)) - + if len(walls) != lx + 1: + raise ValueError( + "Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx + 1)) + self.x = x self.y = y self.hwalls = hwalls self.vwalls = vwalls self.floors = floors -class TrayLayout(Layout): +class TrayLayout(Layout): """Type tray with each wall and floor tile being optional""" webinterface = True @@ -367,17 +387,17 @@ class TrayLayout(Layout): Boxes.__init__(self) self.argparser = boxes.ArgumentParser() self.argparser.add_argument( - "--x", action="store", type=int, default=2, + "--x", action="store", type=int, default=2, help="number of compartments side by side") self.argparser.add_argument( - "--y", action="store", type=int, default=2, + "--y", action="store", type=int, default=2, help="number of compartments back to front") def render(self): return -class TrayLayout2(Layout): +class TrayLayout2(Layout): """Generate a typetray from a layout file""" webinterface = True @@ -386,7 +406,8 @@ class TrayLayout2(Layout): Boxes.__init__(self) self.buildArgParser("h", "hi", "outside") self.argparser.add_argument( - "--layout", action="store", type=str) + "--layout", action="store", type=str) + def main(): l = Layout() @@ -404,5 +425,6 @@ def main(): else: l.argparser.print_usage() + if __name__ == '__main__': main() diff --git a/boxes/generators/typetray.py b/boxes/generators/typetray.py index f01aa2f..e5182b0 100755 --- a/boxes/generators/typetray.py +++ b/boxes/generators/typetray.py @@ -16,22 +16,24 @@ from boxes import * + class TypeTray(Boxes): """Type tray - allows only continuous walls""" + def __init__(self): Boxes.__init__(self) self.buildArgParser("sx", "sy", "h", "hi", "outside") self.argparser.add_argument( - "--gripheight", action="store", type=float, default=30, + "--gripheight", action="store", type=float, default=30, dest="gh", help="height of the grip hole in mm") self.argparser.add_argument( - "--gripwidth", action="store", type=float, default=70, + "--gripwidth", action="store", type=float, default=70, dest="gw", help="width of th grip hole in mm (zero for no hole)") self.argparser.set_defaults( fingerjointfinger=3.0, fingerjointspace=3.0, fingerjointsurrounding=0.5, - ) + ) def xSlots(self): posx = -0.5 * self.thickness @@ -67,9 +69,9 @@ class TypeTray(Boxes): if not self.gw: return - x = sum(self.sx) + self.thickness * (len(self.sx)-1) + x = sum(self.sx) + self.thickness * (len(self.sx) - 1) r = min(self.gw, self.gh) / 2.0 - self.rectangularHole(x/2.0, self.gh*1.5, self.gw, self.gh, r) + self.rectangularHole(x / 2.0, self.gh * 1.5, self.gw, self.gh, r) def render(self): if self.outside: @@ -79,8 +81,8 @@ class TypeTray(Boxes): if self.hi: self.hi = self.adjustSize(self.hi, e2=False) - x = sum(self.sx) + self.thickness * (len(self.sx)-1) - y = sum(self.sy) + self.thickness * (len(self.sy)-1) + 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 = self.hi or h t = self.thickness @@ -88,35 +90,31 @@ class TypeTray(Boxes): self.open() # outer walls - self.rectangularWall(x, h, "Ffef", callback=[ - self.xHoles, None, self.gripHole], - move="right") - self.rectangularWall(y, h, "FFeF", callback=[self.yHoles,], - move="up") - self.rectangularWall(y, h, "FFeF", callback=[self.yHoles,]) - self.rectangularWall(x, h, "Ffef", callback=[self.xHoles,], - move="left up") - + self.rectangularWall(x, h, "Ffef", callback=[self.xHoles, None, self.gripHole], move="right") + self.rectangularWall(y, h, "FFeF", callback=[self.yHoles, ], move="up") + self.rectangularWall(y, h, "FFeF", callback=[self.yHoles, ]) + self.rectangularWall(x, h, "Ffef", callback=[self.xHoles, ], move="left up") + # floor - self.rectangularWall(x, y, "ffff", - callback=[self.xSlots, self.ySlots], - move="right") + self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots],move="right") # Inner walls - for i in range(len(self.sx)-1): - e = [edges.SlottedEdge(self, self.sy, "f", slots=0.5*hi), "f", "e", "f"] - self.rectangularWall(y, hi, e, - move="up") - for i in range(len(self.sy)-1): + for i in range(len(self.sx) - 1): + e = [edges.SlottedEdge(self, self.sy, "f", slots=0.5 * hi), "f", "e", "f"] + self.rectangularWall(y, hi, e, move="up") + + for i in range(len(self.sy) - 1): e = [edges.SlottedEdge(self, self.sx, "f"), "f", - edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5*hi), "f"] - self.rectangularWall(x, hi, e, - move="up") + edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * hi), "f"] + self.rectangularWall(x, hi, e, move="up") + self.close() + def main(): b = TypeTray() b.parseArgs() b.render() + if __name__ == '__main__': main() diff --git a/boxes/parts.py b/boxes/parts.py index 8a35b59..45e75ff 100644 --- a/boxes/parts.py +++ b/boxes/parts.py @@ -1,14 +1,16 @@ from math import * + def arcOnCircle(spanning_angle, outgoing_angle, r=1.0): - angle = spanning_angle+2*outgoing_angle - radius = r * sin(radians(0.5*spanning_angle))/sin(radians(180-outgoing_angle-0.5*spanning_angle)) + angle = spanning_angle + 2 * outgoing_angle + radius = r * sin(radians(0.5 * spanning_angle)) / sin(radians(180 - outgoing_angle - 0.5 * spanning_angle)) return angle, abs(radius) -class Parts: +class Parts: def __init__(self, boxes): self.boxes = boxes + """ def roundKnob(self, diameter, n=20, callback=None, move=""): size = diameter+diameter/n @@ -22,55 +24,69 @@ class Parts: def __getattr__(self, name): return getattr(self.boxes, name) - + def disc(self, diameter, hole=0, callback=None, move=""): size = diameter - r = diameter/2.0 + r = diameter / 2.0 + if self.move(size, size, move, before=True): return - self.moveTo(size/2, size/2) + + self.moveTo(size / 2, size / 2) + if hole: - self.hole(0, 0, hole/2) + self.hole(0, 0, hole / 2) + self.cc(callback, None, 0, 0) - self.moveTo(r+self.burn,0, 90) + self.moveTo(r + self.burn, 0, 90) self.corner(360, r) self.move(size, size, move) - + def waivyKnob(self, diameter, n=20, angle=45, hole=0, callback=None, move=""): - size = diameter+pi*diameter/n + size = diameter + pi * diameter / n + if self.move(size, size, move, before=True): return - self.moveTo(size/2, size/2) + + self.moveTo(size / 2, size / 2) self.cc(callback, None, 0, 0) + if hole: - self.hole(0, 0, hole/2) - self.moveTo(diameter/2, 0, angle) - a, r = arcOnCircle(360./n, angle, diameter/2) - a2, r2 = arcOnCircle(360./n, -angle, diameter/2) - for i in range(n//2): + self.hole(0, 0, hole / 2) + + self.moveTo(diameter / 2, 0, angle) + a, r = arcOnCircle(360. / n, angle, diameter / 2) + a2, r2 = arcOnCircle(360. / n, -angle, diameter / 2) + + for i in range(n // 2): self.boxes.corner(a, r) self.boxes.corner(a2, r2) + self.move(size, size, move) def concaveKnob(self, diameter, n=3, rounded=0.2, angle=70, hole=0, callback=None, move=""): size = diameter + if self.move(size, size, move, before=True): return - self.moveTo(size/2, size/2) + + self.moveTo(size / 2, size / 2) + if hole: - self.hole(0, 0, hole/2) + self.hole(0, 0, hole / 2) + self.cc(callback, None, 0, 0) - self.moveTo(diameter/2, 0, 90+angle) - a, r = arcOnCircle(360./n*(1-rounded), -angle, diameter/2) - if abs(a) < 0.01: # avoid trying to make a straight line as an arc - a, r = arcOnCircle(360./n*(1-rounded), -angle-0.01, diameter/2) + self.moveTo(diameter / 2, 0, 90 + angle) + a, r = arcOnCircle(360. / n * (1 - rounded), -angle, diameter / 2) + + if abs(a) < 0.01: # avoid trying to make a straight line as an arc + a, r = arcOnCircle(360. / n * (1 - rounded), -angle - 0.01, diameter / 2) + for i in range(n): self.boxes.corner(a, r) self.corner(angle) - self.corner(360./n*rounded, diameter/2) + self.corner(360. / n * rounded, diameter / 2) self.corner(angle) self.move(size, size, move) - - diff --git a/boxes/pulley.py b/boxes/pulley.py index e5a2d8e..f59651b 100644 --- a/boxes/pulley.py +++ b/boxes/pulley.py @@ -14,11 +14,14 @@ from math import * from boxes.vectors import * + def tooth_spaceing_curvefit(teeth, b, c, d): - return ((c * teeth**d) / (b + teeth**d)) * teeth + return ((c * teeth ** d) / (b + teeth ** d)) * teeth + def tooth_spacing(teeth, tooth_pitch, pitch_line_offset): - return (2*((teeth*tooth_pitch)/(3.14159265*2)-pitch_line_offset)) + return (2 * ((teeth * tooth_pitch) / (3.14159265 * 2) - pitch_line_offset)) + def mirrorx(points): return [[-x, y] for x, y in points] @@ -26,36 +29,37 @@ def mirrorx(points): class Pulley: spacing = { - "MXL" : (False, 2.032,0.254), - "40DP" : (False, 2.07264,0.1778), - "XL" : (False, 5.08,0.254), - "H" : (False, 9.525,0.381), - "T2_5" : (True, 0.7467,0.796,1.026), - "T5" : (True, 0.6523,1.591,1.064), - "T10" : (False, 10,0.93), - "AT5" : (True, 0.6523,1.591,1.064), - "HTD_3mm" : (False, 3,0.381), - "HTD_5mm" : (False, 5,0.5715), - "HTD_8mm" : (False, 8,0.6858), - "GT2_2mm" : (False, 2,0.254), - "GT2_3mm" : (False, 3,0.381), - "GT2_5mm" : (False, 5,0.5715), + "MXL": (False, 2.032, 0.254), + "40DP": (False, 2.07264, 0.1778), + "XL": (False, 5.08, 0.254), + "H": (False, 9.525, 0.381), + "T2_5": (True, 0.7467, 0.796, 1.026), + "T5": (True, 0.6523, 1.591, 1.064), + "T10": (False, 10, 0.93), + "AT5": (True, 0.6523, 1.591, 1.064), + "HTD_3mm": (False, 3, 0.381), + "HTD_5mm": (False, 5, 0.5715), + "HTD_8mm": (False, 8, 0.6858), + "GT2_2mm": (False, 2, 0.254), + "GT2_3mm": (False, 3, 0.381), + "GT2_5mm": (False, 5, 0.5715), } + profile_data = { - "MXL" : (0.508 , 1.321 ), - "40DP" : (0.457 , 1.226 ), - "XL" : (1.27, 3.051 ), - "H" : (1.905 , 5.359 ), - "T2_5" : (0.7 , 1.678 ), - "T5" : (1.19 , 3.264 ), - "T10" : (2.5 , 6.13 ), - "AT5" : (1.19 , 4.268 ), - "HTD_3mm" : (1.289 , 2.27 ), - "HTD_5mm" : (2.199 , 3.781 ), - "HTD_8mm" : (3.607 , 6.603 ), - "GT2_2mm" : (0.764 , 1.494 ), - "GT2_3mm" : (1.169 , 2.31 ), - "GT2_5mm" : (1.969 , 3.952 ), + "MXL": (0.508, 1.321), + "40DP": (0.457, 1.226), + "XL": (1.27, 3.051), + "H": (1.905, 5.359), + "T2_5": (0.7, 1.678), + "T5": (1.19, 3.264), + "T10": (2.5, 6.13), + "AT5": (1.19, 4.268), + "HTD_3mm": (1.289, 2.27), + "HTD_5mm": (2.199, 3.781), + "HTD_8mm": (3.607, 6.603), + "GT2_2mm": (0.764, 1.494), + "GT2_3mm": (1.169, 2.31), + "GT2_5mm": (1.969, 3.952), } teeth = { "MXL" : [[-0.660421,-0.5],[-0.660421,0],[-0.621898,0.006033],[-0.587714,0.023037],[-0.560056,0.049424],[-0.541182,0.083609],[-0.417357,0.424392],[-0.398413,0.458752],[-0.370649,0.48514],[-0.336324,0.502074],[-0.297744,0.508035],[0.297744,0.508035],[0.336268,0.502074],[0.370452,0.48514],[0.39811,0.458752],[0.416983,0.424392],[0.540808,0.083609],[0.559752,0.049424],[0.587516,0.023037],[0.621841,0.006033],[0.660421,0],[0.660421,-0.5]], @@ -83,20 +87,21 @@ class Pulley: def drawPoints(self, lines, kerfdir=1): if kerfdir != 0: - lines = kerf(lines, self.boxes.burn*kerfdir) + lines = kerf(lines, self.boxes.burn * kerfdir) self.boxes.ctx.save() self.boxes.ctx.move_to(*lines[0]) + for x, y in lines[1:]: self.boxes.ctx.line_to(x, y) + self.boxes.ctx.line_to(*lines[0]) self.boxes.ctx.restore() def diameter(self, teeth, profile): if self.spacing[profile][0]: - return tooth_spaceing_curvefit( - teeth, *self.spacing[profile][1:]) - else: - return tooth_spacing(teeth, *self.spacing[profile][1:]) + return tooth_spaceing_curvefit(teeth, *self.spacing[profile][1:]) + + return tooth_spacing(teeth, *self.spacing[profile][1:]) def __call__(self, teeth, profile, move="", r_axle=None, callback=None): @@ -105,31 +110,35 @@ class Pulley: # ******************************** # To improve fit of belt to pulley, set the following constant. Decrease or increase by 0.1mm at a time. We are modelling the *BELT* tooth here, not the tooth on the pulley. Increasing the number will *decrease* the pulley tooth size. Increasing the tooth width will also scale proportionately the tooth depth, to maintain the shape of the tooth, and increase how far into the pulley the tooth is indented. Can be negative - additional_tooth_width = 0.2 #mm + additional_tooth_width = 0.2 # mm # If you need more tooth depth than this provides, adjust the following constant. However, this will cause the shape of the tooth to change. - additional_tooth_depth = 0 #mm + additional_tooth_depth = 0 # mm pulley_OD = self.diameter(teeth, profile) tooth_depth, tooth_width = self.profile_data[profile] - tooth_distance_from_centre = ((pulley_OD/2)**2 - ((tooth_width+additional_tooth_width)/2)**2)**0.5 - tooth_width_scale = (tooth_width + additional_tooth_width ) / tooth_width - tooth_depth_scale = ((tooth_depth + additional_tooth_depth ) / tooth_depth) + tooth_distance_from_centre = ((pulley_OD / 2) ** 2 - ((tooth_width + additional_tooth_width) / 2) ** 2) ** 0.5 + tooth_width_scale = (tooth_width + additional_tooth_width) / tooth_width + tooth_depth_scale = ((tooth_depth + additional_tooth_depth) / tooth_depth) total_width = pulley_OD + if self.boxes.move(total_width, total_width, move, before=True): return - self.boxes.moveTo(total_width/2, total_width/2) + + self.boxes.moveTo(total_width / 2, total_width / 2) self.boxes.cc(callback, None, 0.0, 0.0) + if r_axle: self.boxes.hole(0, 0, r_axle) + points = [] - for i in range(teeth): + for i in range(teeth): m = [[tooth_width_scale, 0, 0], [0, tooth_depth_scale, -tooth_distance_from_centre]] - m = mmul(m, rotm(i*2*pi/teeth)) + m = mmul(m, rotm(i * 2 * pi / teeth)) points.extend((vtransl(pt, m) for pt in self.teeth[profile][1:-1])) - self.drawPoints(points) + self.drawPoints(points) self.boxes.move(total_width, total_width, move) diff --git a/boxes/svgutil.py b/boxes/svgutil.py index 7d7e09e..06c12be 100755 --- a/boxes/svgutil.py +++ b/boxes/svgutil.py @@ -17,8 +17,8 @@ import xml.parsers.expat import re -class SVGFile(object): +class SVGFile(object): pathre = re.compile(r"[MCL]? *((-?\d+(\.\d+)?) (-?\d+(\.\d+)?) *)+") transformre = re.compile(r"matrix\(" + ",".join([r"(-?\d+(\.\d+)?)"] * 6) + "\)") @@ -31,29 +31,35 @@ class SVGFile(object): if name == "path" and "symbol" not in self.tags: minx = maxx = miny = maxy = None m = self.transformre.match(attrs.get("transform", "")) + if m: matrix = [float(m.group(i)) for i in range(1, 12, 2)] else: matrix = [1, 0, 0, 1, 0, 0] + for m in self.pathre.findall(attrs.get("d", "")): x = float(m[1]) y = float(m[3]) - tx = matrix[0]*x+matrix[2]*y+matrix[4] - ty = matrix[1]*x+matrix[3]*y+matrix[5] + tx = matrix[0] * x + matrix[2] * y + matrix[4] + ty = matrix[1] * x + matrix[3] * y + matrix[5] if self.minx is None or self.minx > tx: self.minx = tx + if self.maxx is None or self.maxx < tx: self.maxx = tx + if self.miny is None or self.miny > ty: self.miny = ty + if self.maxy is None or self.maxy < ty: self.maxy = ty def handleEndElement(self, name): last = self.tags.pop() + if last != name: raise ValueError("Got expected " % (name, last)) @@ -70,27 +76,29 @@ class SVGFile(object): m = re.search(r"""]*(width="(\d+pt)" height="(\d+pt)" viewBox="0 (0 (\d+) (\d+))") version="1.1">""", s) - #minx = 10*int(self.minx//10)-10 + # minx = 10*int(self.minx//10)-10 # as we don't rewrite the left border keep it as 0 if 0 <= self.minx <= 50: minx = 0 else: - minx = 10*int(self.minx//10)-10 - #raise ValueError("Left end of drawing at wrong place: %imm (0-50mm expected)" % self.minx) - maxx = 10*int(self.maxx//10)+10 - miny = 10*int(self.miny//10)-10 - maxy = 10*int(self.maxy//10)+10 + minx = 10 * int(self.minx // 10) - 10 + # raise ValueError("Left end of drawing at wrong place: %imm (0-50mm expected)" % self.minx) + maxx = 10 * int(self.maxx // 10) + 10 + miny = 10 * int(self.miny // 10) - 10 + maxy = 10 * int(self.maxy // 10) + 10 if m: f.seek(m.start(1)) s = ('width="%imm" height="%imm" viewBox="0 %i %i %i"' % - (maxx-minx, maxy-miny, miny, maxx, maxy-miny)) + (maxx - minx, maxy - miny, miny, maxx, maxy - miny)) + if len(s) > len(m.group(1)): raise ValueError("Not enough space for size") - f.write(s + " " * (len(m.group(1))- len(s))) + + f.write(s + " " * (len(m.group(1)) - len(s))) else: - raiseValueError("Could not understand SVG file") - + raise ValueError("Could not understand SVG file") + if __name__ == "__main__": svg = SVGFile("examples/box.svg") diff --git a/boxes/vectors.py b/boxes/vectors.py index 7833f2f..10975f1 100644 --- a/boxes/vectors.py +++ b/boxes/vectors.py @@ -14,72 +14,86 @@ # along with this program. If not, see . import math + def normalize(v): "set lenght of vector to one" - l = (v[0]**2+v[1]**2)**0.5 - return (v[0]/l, v[1]/l) + l = (v[0] ** 2 + v[1] ** 2) ** 0.5 + return (v[0] / l, v[1] / l) + def vlength(v): - return (v[0]**2+v[1]**2)**0.5 + return (v[0] ** 2 + v[1] ** 2) ** 0.5 + def vclip(v, length): l = vlength(v) if l > length: - return vscalmul(v, length/l) + return vscalmul(v, length / l) return v + def vdiff(p1, p2): "vector from point1 to point2" - return (p2[0]-p1[0], p2[1]-p1[1]) + return (p2[0] - p1[0], p2[1] - p1[1]) + def vadd(v1, v2): "Sum of two vectors" - return (v1[0]+ v2[0], v1[1]+v2[1]) + return (v1[0] + v2[0], v1[1] + v2[1]) + def vorthogonal(v): "orthogonal vector" "Orthogonal vector" return (-v[1], v[0]) + def vscalmul(v, a): "scale vector by a" - return (a*v[0], a*v[1]) + return (a * v[0], a * v[1]) + def dotproduct(v1, v2): "Dot product" - return v1[0]*v2[0]+v1[1]*v2[1] + return v1[0] * v2[0] + v1[1] * v2[1] + def rotm(angle): "Rotation matrix" return [[math.cos(angle), -math.sin(angle), 0], [math.sin(angle), math.cos(angle), 0]] + def vtransl(v, m): m0, m1 = m - return [m0[0]*v[0]+m0[1]*v[1]+m0[2], - m1[0]*v[0]+m1[1]*v[1]+m1[2]] + return [m0[0] * v[0] + m0[1] * v[1] + m0[2], + m1[0] * v[0] + m1[1] * v[1] + m1[2]] + def mmul(m0, m1): - result = [[0,]*len(m0[0]) for i in range(len(m0))] + result = [[0, ] * len(m0[0]) for i in range(len(m0))] for i in range(len(m0[0])): for j in range(len(m0)): for k in range(len(m0)): result[j][i] += m0[k][i] * m1[j][k] return result + def kerf(points, k): """Outset points by k Assumes a closed loop of points """ result = [] lp = len(points) + for i in range(len(points)): # get normalized orthogonals of both segments - v1 = vorthogonal(normalize(vdiff(points[i-1], points[i]))) - v2 = vorthogonal(normalize(vdiff(points[i], points[(i+1) % lp]))) + v1 = vorthogonal(normalize(vdiff(points[i - 1], points[i]))) + v2 = vorthogonal(normalize(vdiff(points[i], points[(i + 1) % lp]))) # direction the point has to move d = normalize(vadd(v1, v2)) # cos of the half the angle between the segments cos_alpha = dotproduct(v1, d) - result.append(vadd(points[i], vscalmul(d, -k/cos_alpha))) + result.append(vadd(points[i], vscalmul(d, -k / cos_alpha))) + return result