diff --git a/boxes/robot.py b/boxes/robot.py new file mode 100644 index 0000000..bfbbb2f --- /dev/null +++ b/boxes/robot.py @@ -0,0 +1,187 @@ +import boxes + +__all__ = [ + "RobotArg", + "RobotArmMM", + "RobotArmMm", + "RobotArmUU", + "RobotArmUu", + "RobotArmMu", +] + +class RobotArg: + + def __init__(self, includenone=False): + self.robotarms = [ + (name, globals()[name].__doc__[23:]) for name in __all__ + if name.startswith("RobotArm")] + if includenone: + self.robotarms[0:0] = [("none", "")] + + def __call__(self, arg): + return str(arg) + + def choices(self): + return [name for name, descr in self.robotarms] + + def html(self, name, default): + options = "\n".join( + ("""""" % + (name, ' selected="selected"' if name == default else "", + name, descr) for name, descr in self.robotarms)) + return """\n""" % (name, options) + + +class _RobotArm: + + def __init__(self, boxes, servo, servo2=None): + self.boxes = boxes + self.servo = servo + self.servo2 = servo2 or servo + + def __getattr__(self, name): + """Hack for easy access of Boxes methods""" + return getattr(self.boxes, name) + +class RobotArmMM(_RobotArm): + """Robot arm segment with two parallel servos""" + def __call__(self, length, move=None): + t = self.thickness + w = self.servo.height + l = max(self.servo.length * 2, length + 2*self.servo.axle_pos) + + th = max(2 * t + l, 2*w + 4*t + self.spacing) + tw = 5 * (w + 2*self.thickness + self.spacing) + + if self.move(tw, th, move, True): + return + + self.rectangularWall(w, l, "FfFf", callback=[ + lambda:self.servo.top(w/2), None, + lambda:self.servo.top(w/2)], move="right") + self.rectangularWall(w, l, "FfFf", callback=[ + lambda:self.servo.bottom(w/2), None, + lambda:self.servo.bottom(w/2)], move="right") + self.rectangularWall(w, l, "FFFF", move="right") + self.rectangularWall(w, l, "FFFF", move="right") + self.rectangularWall(w, w, "ffff", callback=[ + lambda:self.servo.front(w/2)], move="up") + self.rectangularWall(w, w, "ffff", callback=[ + lambda:self.servo.front(w/2)], move="") + + self.move(tw, th, move) + +class RobotArmMm(_RobotArm): + """Robot arm segment with two orthogonal servos""" + def __call__(self, length, move=None): + t = self.thickness + w = self.servo.height + w2 = self.servo2.height + l = max(self.servo.length * 2, length + 2*self.servo.axle_pos) + + th = max(2 * self.thickness + l, w + w2 + 4*t + self.spacing) + tw = 5 * (max(w, w2) + 2*self.thickness + self.spacing) + + if self.move(tw, th, move, True): + return + + self.rectangularWall(w2, l, "FfFf", callback=[ + lambda:self.servo.top(w2/2)], move="right") + self.rectangularWall(w2, l, "FfFf", callback=[ + lambda:self.servo.bottom(w2/2)], move="right") + self.rectangularWall(w, l, "FFFF", callback=[ + None, None, lambda:self.servo2.top(w/2)], move="right") + self.rectangularWall(w, l, "FFFF", callback=[ + None, None, lambda:self.servo2.bottom(w/2)], move="right") + self.rectangularWall(w2, w, "ffff", callback=[ + lambda:self.servo.front(w2/2)], move="up") + self.rectangularWall(w, w2, "ffff", callback=[ + lambda:self.servo2.front(w/2)], move="") + + self.move(tw, th, move) + +class RobotArmUU(_RobotArm): + """Robot arm segment with two parallel sets of hinge knuckles""" + def __call__(self, length, move=None): + t = self.thickness + w = self.servo.hinge_width() + l = max(4*self.thickness, length - 2*t - 2*self.servo.height) + + th = max(2 * self.servo._edges["m"].spacing() + l, + 2*w + 4*t + self.spacing) + tw = 5 * (w + 2*self.thickness + self.spacing) + + if self.move(tw, th, move, True): + return + + iw = (0, 3, 4, 7) + e = self.servo.edges + self.rectangularWall(w, l, e("mFmF"), ignore_widths=iw, move="right") + self.rectangularWall(w, l, e("MFMF"), ignore_widths=iw, move="right") + self.rectangularWall(w, l, "FfFf", move="right") + self.rectangularWall(w, l, "FfFf", move="right") + self.rectangularWall(w, w, "ffff", callback=[ + lambda: self.hole(w/2, w/2, 6)], move="up") + self.rectangularWall(w, w, "ffff", callback=[ + lambda: self.hole(w/2, w/2, 6)], move="") + + self.move(tw, th, move) + +class RobotArmUu(_RobotArm): + """Robot arm segment with two orthogonal sets of hinge knuckles""" + def __call__(self, length, move=None): + t = self.thickness + w = self.servo.hinge_width() + w2 = self.servo2.hinge_width() + l = max(4*self.thickness, length - 2*t - 2*self.servo.height) + + th = max(self.thickness + self.servo._edges["m"].spacing() + l, + 2*w + self.thickness + 4 * self.edges["f"].spacing()) + tw = 5 * (w + 2*self.thickness + self.spacing) + + if self.move(tw, th, move, True): + return + iw = (3, 4) + e = self.servo.edges + self.rectangularWall(w2, l, e("nfFf"), move="right") + self.rectangularWall(w2, l, e("NfFf"), move="right") + self.rectangularWall(w, l, e("FFmF"), ignore_widths=iw, move="right") + self.rectangularWall(w, l, e("FFMF"), ignore_widths=iw, move="right") + self.rectangularWall(w2, w, "ffff", callback=[ + lambda: self.hole(w2/2, w/2, 6)], move="up") + self.rectangularWall(w2, w, "ffff", callback=[ + lambda: self.hole(w2/2, w/2, 6)], move="") + + self.move(tw, th, move) + +class RobotArmMu(_RobotArm): + """Robot arm segment with a servo and an orthogonal sets of hinge knuckles""" + def __call__(self, length, move=None): + t = self.thickness + w = self.servo.height + w2 = self.servo2.hinge_width() + l = max(self.servo.length, length + self.servo.axle_pos - self.servo.height - t) + + th = max(t + l + self.servo2._edges["m"].spacing(), + w + w2 + self.thickness + 4 * self.edges["f"].spacing()) + tw = 5 * (w + 2*self.thickness + self.spacing) + + if self.move(tw, th, move, True): + return + + e = self.servo2.edges + iw = (3, 4) + self.rectangularWall(w2, l, "FfFf", callback=[ + lambda:self.servo.top(w2/2)], move="right") + self.rectangularWall(w2, l, "FfFf", callback=[ + lambda:self.servo.bottom(w2/2)], move="right") + self.rectangularWall(w, l, e("FFmF"), ignore_widths=iw, move="right") + self.rectangularWall(w, l, e("FFMF"), ignore_widths=iw, move="right") + self.rectangularWall(w2, w, "ffff", callback=[ + lambda:self.servo.front(w2/2)], move="up") + self.rectangularWall(w2, w, "ffff", callback=[ + lambda: self.hole(w2/2, w/2, 6)], move="") + + self.move(tw, th, move) + +# class RobotArmMU(_RobotArm): diff --git a/boxes/servos.py b/boxes/servos.py new file mode 100644 index 0000000..4a716da --- /dev/null +++ b/boxes/servos.py @@ -0,0 +1,120 @@ +import boxes.vectors +import math + +class EyeEdge(boxes.edges.FingerHoleEdge): + + char = "m" + + def __init__(self, boxes, servo, fingerHoles=None, driven=False, + outset=False, **kw): + self.servo = servo + self.outset = outset + self.driven = driven + super(EyeEdge, self).__init__(boxes, fingerHoles, **kw) + + def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw): + t = self.fingerHoles.settings.thickness + dist = self.fingerHoles.settings.edge_width + + pos_axle = self.servo.hinge_depth() + self.ctx.save() + self.hole(length/2.0, -pos_axle, + self.servo.axle/2.0 if self.driven else + self.servo.servo_axle/2.0) + if self.outset: + self.fingerHoles(t, self.thickness / 2, length-2*t, 0) + else: + self.fingerHoles(0, self.thickness / 2, length, 0) + self.ctx.restore() + r = self.servo.servo_axle * 2 + a, l = boxes.vectors.tangent(length/2, pos_axle, r) + angle = math.degrees(a) + self.polyline(0, -angle, l, (2*angle, r), l, -angle, 0) + + def startwidth(self): + return self.fingerHoles.settings.thickness + + def margin(self): + return self.servo.hinge_depth() + self.fingerHoles.settings.thickness + self.servo.servo_axle * 2 + +def buildEdges(boxes, servo, chars="mMnN"): + result = {} + for n, char in enumerate(chars): + e = EyeEdge(boxes, servo, outset=(n<2), driven=(n % 2)) + e.char = char + result[char] = e + return result + +class ServoArg: + + def __init__(self, includenone=False): + self.servos = ["Servo9g"] + if includenone: + self.robotarms[0:0] = ["none"] + + def __call__(self, arg): + return str(arg) + + def choices(self): + return [name for name, descr in self.robotarms] + + def html(self, name, default): + options = "\n".join( + ("""""" % + (name, ' selected="selected"' if name == default else "", + name) for name in self.servos)) + return """\n""" % (name, options) + + +class Servo: + def __init__(self, boxes, axle=3): + self.boxes = boxes + self.axle = axle + self._edges = buildEdges(boxes, self) + + def edges(self, edges): + return [self._edges.get(e, e) for e in edges] + +class Servo9g(Servo): + + height = 22.5 + length = 28.0 # one tab in the wall + width = 12.0 + axle_pos = 6.0 + servo_axle = 4.6 # 6.9 for servo arm + + def top(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.hole(6, 0, 6) + self.boxes.hole(12, 0, 3) + + def bottom(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.hole(6, 0, self.axle/2.0) + + def front(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.rectangularHole(5.4, 0, 2.4, 12) + self.boxes.rectangularHole(17, 0, 4, 16) + + def hinge_width(self): + return self.height + self.boxes.thickness + 4.5 + + def hinge_depth(self): + return self.height # XXX + +class Servo9gt(Servo9g): + height = 35 + + def top(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.hole(6, 0, 6) + self.boxes.hole(12, 0, 5) + + def bottom(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.hole(6, 0, self.axle) + + def front(self, x=0.0, y=0.0, angle=90.0): + self.boxes.moveTo(x, y, angle) + self.boxes.rectangularHole(5.4, 0, 2.4, 12)