# No. 84 (Brown 1868): "The double rack-frame, B, is suspended from the rod, A. Continuous rotary motion is
# given to the cam, D. When the shaft of the cam is midway between the two racks, the cam acts upon neither of
# them; but by raising or lowering the rod, A, either the lower or upper rack is brought within range of the cam,
# and the rack-frame moved to the left or right."
#
# Model (cm): the cam D has a single rounded tooth (tip radius 3.0) and turns clockwise, the one driven joint,
# 60 turns a minute. B is a frame with two racks of V teeth (2.4 cm pitch, 1.4 deep), one above the cam's shaft
# and one below, their tips 3.45 cm from it, clear of the tooth. B hangs by two rollers on a rail at the foot of
# rod A and slides along it against a little friction (the valve it would work). A slides up and down in its
# guides, held by a stiff spring whose rest the hand sets (the governor, not drawn): lowered 1.3 cm at 2.25 s, the
# upper rack comes into the tooth's path and each turn the tooth, sweeping it at the top of its circle, carries B
# one tooth to the right; raised at 5.75 s, the lower rack, met at the bottom, carries B to the left; back to the
# middle at 14.75 s, B stands. The V teeth centre the rack on the tooth as it enters, so B goes a whole tooth a turn.
# Generator: opencode-runs/batch8/gen084.py.
number: 84
slug: double-rack-frame-cam
name: A double rack-frame moved either way by a cam
brown: "The double rack-frame, B, is suspended from the rod, A. Continuous rotary motion is given to the cam, D. When the shaft of the cam is midway between the two racks, the cam acts upon neither of them; but by raising or lowering the rod, A, either the lower or upper rack is brought within range of the cam, and the rack-frame moved to the left or right."
units: cm
sim:
  timestep: 0.0002
  integrator: implicitfast
  iterations: 100
  solver: PGS
  multiccd: true
  duration_s: 17.0
  floor: false
bodies:
  - id: cam_d
    shape: part
    pos: [0, 0, 0]
    mass: 0.4
    friction: 0.1
    color: "#d9cfb6"
    pieces:
      - {kind: disc, center: [0, 0], radius: 1.4, inner: 0.5, y: [-0.7, 0.7], collide: 0}
      - {kind: poly, points: [[-0.55, -0.8999999999999999], [-0.45, -2.55], [-0.4157, -2.7222], [-0.3182, -2.8682], [-0.1722, -2.9657], [-0.0, -3.0], [0.1722, -2.9657], [0.3182, -2.8682], [0.4157, -2.7222], [0.45, -2.55], [0.55, -0.8999999999999999]], y: [-0.7, 0.7], collide: 1}   # the tooth
  - id: rod_a
    shape: part
    pos: [0, 0, 8.75]
    mass: 0.3
    color: "#8f8a80"
    pieces:
      - {kind: rod, from: [0, -1.6, 8.75], to: [0, -1.6, 22.75], radius: 0.45}   # the rod
      - {kind: poly, points: [[-21.8, 8.4], [21.8, 8.4], [21.8, 9.1], [-21.8, 9.1]], y: [-1.9, -1.3]}   # the rail at its foot
  - id: frame_b
    shape: part
    pos: [0, 0, 0]
    mount: rod_a
    mass: 0.4
    friction: 0.1
    color: "#b8ad97"
    pieces:
      - {kind: poly, points: [[-16.8, 4.85], [16.8, 4.85], [16.8, 5.75], [-16.8, 5.75]], y: [-0.7, 0.7]}   # the upper rack's bar
      - {kind: poly, points: [[-16.8, -5.75], [16.8, -5.75], [16.8, -4.85], [-16.8, -4.85]], y: [-0.7, 0.7]}   # the lower
      - {kind: poly, points: [[-16.8, 4.85], [-14.4, 4.85], [-15.48, 3.45], [-15.72, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-14.4, 4.85], [-12.0, 4.85], [-13.08, 3.45], [-13.32, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-12.0, 4.85], [-9.6, 4.85], [-10.68, 3.45], [-10.92, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-9.6, 4.85], [-7.2, 4.85], [-8.28, 3.45], [-8.52, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-7.2, 4.85], [-4.8, 4.85], [-5.88, 3.45], [-6.12, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-4.8, 4.85], [-2.4, 4.85], [-3.48, 3.45], [-3.72, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-2.4, 4.85], [0.0, 4.85], [-1.08, 3.45], [-1.32, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[0.0, 4.85], [2.4, 4.85], [1.32, 3.45], [1.08, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[2.4, 4.85], [4.8, 4.85], [3.72, 3.45], [3.48, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[4.8, 4.85], [7.2, 4.85], [6.12, 3.45], [5.88, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[7.2, 4.85], [9.6, 4.85], [8.52, 3.45], [8.28, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[9.6, 4.85], [12.0, 4.85], [10.92, 3.45], [10.68, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[12.0, 4.85], [14.4, 4.85], [13.32, 3.45], [13.08, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[14.4, 4.85], [16.8, 4.85], [15.72, 3.45], [15.48, 3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-16.8, -4.85], [-14.4, -4.85], [-15.48, -3.45], [-15.72, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-14.4, -4.85], [-12.0, -4.85], [-13.08, -3.45], [-13.32, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-12.0, -4.85], [-9.6, -4.85], [-10.68, -3.45], [-10.92, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-9.6, -4.85], [-7.2, -4.85], [-8.28, -3.45], [-8.52, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-7.2, -4.85], [-4.8, -4.85], [-5.88, -3.45], [-6.12, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-4.8, -4.85], [-2.4, -4.85], [-3.48, -3.45], [-3.72, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-2.4, -4.85], [0.0, -4.85], [-1.08, -3.45], [-1.32, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[0.0, -4.85], [2.4, -4.85], [1.32, -3.45], [1.08, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[2.4, -4.85], [4.8, -4.85], [3.72, -3.45], [3.48, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[4.8, -4.85], [7.2, -4.85], [6.12, -3.45], [5.88, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[7.2, -4.85], [9.6, -4.85], [8.52, -3.45], [8.28, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[9.6, -4.85], [12.0, -4.85], [10.92, -3.45], [10.68, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[12.0, -4.85], [14.4, -4.85], [13.32, -3.45], [13.08, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[14.4, -4.85], [16.8, -4.85], [15.72, -3.45], [15.48, -3.45]], y: [-0.7, 0.7], collide: 2}
      - {kind: poly, points: [[-17.7, -5.75], [-16.8, -5.75], [-16.8, 5.75], [-17.7, 5.75]], y: [-0.7, 0.7]}   # the frame's ends
      - {kind: poly, points: [[16.8, -5.75], [17.7, -5.75], [17.7, 5.75], [16.8, 5.75]], y: [-0.7, 0.7]}
      - {kind: rod, from: [-6, -1.1, 5.75], to: [-6, -1.1, 9.1], radius: 0.25}   # its hangers
      - {kind: rod, from: [6, -1.1, 5.75], to: [6, -1.1, 9.1], radius: 0.25}
      - {kind: disc, center: [-6, 9.6], radius: 0.5, y: [-1.85, -1.35]}   # the rollers on the rail
      - {kind: disc, center: [6, 9.6], radius: 0.5, y: [-1.85, -1.35]}
  - id: frame
    shape: part
    pos: [0, 0, 0]
    fixed: true
    color: "#cfc6b4"
    pieces:
      - {kind: rod, from: [0, -0.9, 0], to: [0, 2.6, 0], radius: 0.5, color: "#8f8a80"}   # D's shaft
      - {kind: poly, points: [[-1.4, -8], [1.4, -8], [0.9, 0], [-0.9, 0]], y: [1.6, 2.6]}   # its standard, behind
      - {kind: poly, points: [[-1.6, 17.75], [-0.6, 17.75], [-0.6, 20.75], [-1.6, 20.75]], y: [-2.4, -0.8]}   # A's guide
      - {kind: poly, points: [[0.6, 17.75], [1.6, 17.75], [1.6, 20.75], [0.6, 20.75]], y: [-2.4, -0.8]}
      - {kind: poly, points: [[-2.5, 20.75], [2.5, 20.75], [2.5, 21.75], [-2.5, 21.75]], y: [-2.4, 2.6]}
      - {kind: poly, points: [[-28.8, -9], [28.8, -9], [28.8, -8], [-28.8, -8]], y: [-3, 3], color: "#b9b09c"}
joints:
  - {id: j_D, type: hinge, body: cam_d, axis: [0, 1, 0], damping: 0.002}   # the one driven joint; + : clockwise
  - {id: j_a, type: slide, body: rod_a, axis: [0, 0, 1], range: [-1.8, 1.8], stiffness: 10000.0, springref: 0, damping: 150.0}   # the rod A; its spring's rest set by the hand
  - {id: j_b, type: slide, body: frame_b, axis: [1, 0, 0], range: [-12.5, 12.5], damping: 1.0, frictionloss: 0.4}   # + : to the right
hand:
  - {t: 2.25, springref: {j_a: -1.3}}
  - {t: 5.75, springref: {j_a: 1.3}}
  - {t: 14.75, springref: {j_a: 0}}
contact: {solref: [0.002, 1.0], solimp: [0.95, 0.99, 0.0005], margin: 0}
driver: {joint: j_D, kind: velocity, rpm: 60.0, kv: 0.5, max_torque: 20}
checks:
  - {type: still, joint: j_b, max: 0.001, settle_s: 0.3, window_s: 1.6}   # A in the middle: B stands
  - {type: ratio, out: j_b, in: j_D, expect: 0.00382, tol: 7.6e-05, settle_s: 2.8, window_s: 2}   # lowered: a tooth a turn to the right
  - {type: ratio, out: j_b, in: j_D, expect: -0.00382, tol: 7.6e-05, settle_s: 7.3, window_s: 7}   # raised: to the left
  - {type: still, joint: j_b, max: 0.001, settle_s: 15.45, window_s: 1.45}   # the middle again: B stands
  - {type: penetration, max: 0.001, settle_s: 0.5}
camera: {target: [0, 0, 4], position: [0, -80, 10], fov: 36}
display:
  output: j_b
  labels: {j_D: "Cam D (driver)", j_b: "Double rack-frame B", j_a: "Rod A"}
  proof: {label: "D's tooth out of contact", sub: "A goes down and up, but B never moves"}

