# No. 96 (Brown 1868): "A heart-cam. Uniform traversing motion is imparted to the horizontal bar by the rotation of
# the heart-shaped cam. The dotted lines show the mode of striking out the curve of the cam. The length of traverse is
# divided into any number of parts; and from the center a series of concentric circles are described through these
# points. The outside circle is then divided into double the number of these divisions, and lines drawn to the center.
# The curve is then drawn through the intersections of the concentric circles and the radiating lines."
#
# Model (cm): the heart-cam (the one driven joint, 10 turns a minute) is struck out as the plate says: the radius
# grows 1.273 cm to the radian over the first half-turn, from 3 to 7 cm, and falls as fast over the second. The
# horizontal bar ends in a roller (0.5 cm) that a spring (its joint's) holds on the cam. The roller's centre is
# carried 3.96 cm out and back, uniformly, by real contact. Generator: opencode-runs/batch9/gen096.py.
number: 96
slug: heart-cam
name: A heart-cam giving the bar a uniform traverse
brown: "A heart-cam. Uniform traversing motion is imparted to the horizontal bar by the rotation of the heart-shaped cam. The dotted lines show the mode of striking out the curve of the cam. The length of traverse is divided into any number of parts; and from the center a series of concentric circles are described through these points. The outside circle is then divided into double the number of these divisions, and lines drawn to the center. The curve is then drawn through the intersections of the concentric circles and the radiating lines."
units: cm
sim:
  timestep: 0.0005
  integrator: implicitfast
  iterations: 50
  solver: PGS
  duration_s: 12
  floor: false
bodies:
  - id: cam
    shape: part
    pos: [0, 0, 0]
    mass: 1.0
    friction: 0.05
    color: "#d9cfb6"
    pieces:
      - {kind: rod, from: [0, 0.4, 0], to: [0, 3.0, 0], radius: 0.7, color: "#8f8a80"}   # the cam's shaft
      - {kind: poly, points: [[0, 0], [3.0, 0.0], [3.0426, 0.1062], [3.0814, 0.2155], [3.1162, 0.3275], [3.1469, 0.4423], [3.1733, 0.5595], [3.1953, 0.6792], [3.2128, 0.801], [3.2256, 0.9249], [3.2336, 1.0507], [3.2367, 1.1781], [3.2348, 1.307], [3.2279, 1.4371], [3.2157, 1.5684], [3.1982, 1.7005], [3.1754, 1.8333], [3.1472, 1.9666], [3.1135, 2.1001], [3.0743, 2.2336], [3.0295, 2.3669], [2.9791, 2.4997], [2.923, 2.6319], [2.8614, 2.7632], [2.7941, 2.8933], [2.7211, 3.0221], [2.6426, 3.1493], [2.5584, 3.2746], [2.4687, 3.3979], [2.3735, 3.5188], [2.2728, 3.6372], [2.1667, 3.7528], [2.0552, 3.8653], [1.9386, 3.9747], [1.8168, 4.0805], [1.6899, 4.1826], [1.5581, 4.2808], [1.4215, 4.3749], [1.2802, 4.4645], [1.1343, 4.5496], [0.9841, 4.6299], [0.8297, 4.7052], [0.6711, 4.7753], [0.5087, 4.84], [0.3426, 4.8991], [0.1729, 4.9525], [0.0, 5.0], [-0.176, 5.0414], [-0.355, 5.0765], [-0.5366, 5.1052], [-0.7206, 5.1274], [-0.9068, 5.1429], [-1.095, 5.1516], [-1.2849, 5.1533], [-1.4762, 5.1481], [-1.6687, 5.1357], [-1.8621, 5.1161], [-2.0562, 5.0892], [-2.2506, 5.055], [-2.4451, 5.0133], [-2.6395, 4.9641], [-2.8333, 4.9075], [-3.0264, 4.8433], [-3.2185, 4.7716], [-3.4092, 4.6923], [-3.5982, 4.6055], [-3.7853, 4.5112], [-3.9702, 4.4093], [-4.1525, 4.3001], [-4.332, 4.1834], [-4.5084, 4.0594], [-4.6814, 3.9281], [-4.8506, 3.7897], [-5.0159, 3.6443], [-5.1769, 3.4918], [-5.3333, 3.3326], [-5.4848, 3.1667], [-5.6312, 2.9942], [-5.7723, 2.8153], [-5.9076, 2.6302], [-6.037, 2.4391], [-6.1602, 2.2421], [-6.277, 2.0395], [-6.387, 1.8315], [-6.4902, 1.6182], [-6.5862, 1.3999], [-6.6748, 1.1769], [-6.7558, 0.9495], [-6.8291, 0.7178], [-6.8943, 0.4821], [-6.9513, 0.2427], [-7.0, 0.0]], y: [-0.4, 0.4], collide: 1}   # the heart's upper half
      - {kind: poly, points: [[0, 0], [-7.0, 0.0], [-6.9513, -0.2427], [-6.8943, -0.4821], [-6.8291, -0.7178], [-6.7558, -0.9495], [-6.6748, -1.1769], [-6.5862, -1.3999], [-6.4902, -1.6182], [-6.387, -1.8315], [-6.277, -2.0395], [-6.1602, -2.2421], [-6.037, -2.4391], [-5.9076, -2.6302], [-5.7723, -2.8153], [-5.6312, -2.9942], [-5.4848, -3.1667], [-5.3333, -3.3326], [-5.1769, -3.4918], [-5.0159, -3.6443], [-4.8506, -3.7897], [-4.6814, -3.9281], [-4.5084, -4.0594], [-4.332, -4.1834], [-4.1525, -4.3001], [-3.9702, -4.4093], [-3.7853, -4.5112], [-3.5982, -4.6055], [-3.4092, -4.6923], [-3.2185, -4.7716], [-3.0264, -4.8433], [-2.8333, -4.9075], [-2.6395, -4.9641], [-2.4451, -5.0133], [-2.2506, -5.055], [-2.0562, -5.0892], [-1.8621, -5.1161], [-1.6687, -5.1357], [-1.4762, -5.1481], [-1.2849, -5.1533], [-1.095, -5.1516], [-0.9068, -5.1429], [-0.7206, -5.1274], [-0.5366, -5.1052], [-0.355, -5.0765], [-0.176, -5.0414], [-0.0, -5.0], [0.1729, -4.9525], [0.3426, -4.8991], [0.5087, -4.84], [0.6711, -4.7753], [0.8297, -4.7052], [0.9841, -4.6299], [1.1343, -4.5496], [1.2802, -4.4645], [1.4215, -4.3749], [1.5581, -4.2808], [1.6899, -4.1826], [1.8168, -4.0805], [1.9386, -3.9747], [2.0552, -3.8653], [2.1667, -3.7528], [2.2728, -3.6372], [2.3735, -3.5188], [2.4687, -3.3979], [2.5584, -3.2746], [2.6426, -3.1493], [2.7211, -3.0221], [2.7941, -2.8933], [2.8614, -2.7632], [2.923, -2.6319], [2.9791, -2.4997], [3.0295, -2.3669], [3.0743, -2.2336], [3.1135, -2.1001], [3.1472, -1.9666], [3.1754, -1.8333], [3.1982, -1.7005], [3.2157, -1.5684], [3.2279, -1.4371], [3.2348, -1.307], [3.2367, -1.1781], [3.2336, -1.0507], [3.2256, -0.9249], [3.2128, -0.801], [3.1953, -0.6792], [3.1733, -0.5595], [3.1469, -0.4423], [3.1162, -0.3275], [3.0814, -0.2155], [3.0426, -0.1062], [3.0, -0.0]], y: [-0.4, 0.4], collide: 1}   # its lower half
  - id: bar
    shape: part
    pos: [3.5361, 0, 0]
    mass: 0.2
    friction: 0.05
    color: "#b8ad97"
    pieces:
      - {kind: rod, from: [3.5361, -0.25, 0], to: [3.5361, 0.25, 0], radius: 0.5, collide: 2, capsule: true, color: "#8f8a80"}   # the roller
      - {kind: poly, points: [[4.0361, -0.5], [20.5361, -0.5], [20.5361, 0.5], [4.0361, 0.5]], y: [-0.4, 0.4]}   # the horizontal bar
  - id: frame
    shape: part
    pos: [0, 0, 0]
    fixed: true
    color: "#cfc6b4"
    pieces:
      - {kind: poly, points: [[-1.4, -11], [1.4, -11], [1.0, 0], [-1.0, 0]], y: [1.8, 2.8]}   # the cam's standard, behind
      - {kind: poly, points: [[6.5361, -1.6], [22.5361, -1.6], [22.5361, -0.7], [6.5361, -0.7]], y: [-0.8, 1.2]}   # the bar's lower guide
      - {kind: poly, points: [[6.5361, 0.7], [22.5361, 0.7], [22.5361, 1.6], [6.5361, 1.6]], y: [-0.8, 1.2]}   # the upper guide
      - {kind: poly, points: [[6.5361, -1.6], [22.5361, -1.6], [22.5361, 1.6], [6.5361, 1.6]], y: [1.2, 2.2]}   # their back plate
      - {kind: poly, points: [[17.5361, -11], [19.5361, -11], [19.5361, -1.6], [17.5361, -1.6]], y: [1.2, 2.2]}   # their post
      - {kind: poly, points: [[-10, -12], [26.5361, -12], [26.5361, -11], [-10, -11]], y: [-3, 3], color: "#b9b09c"}
joints:
  - {id: j_c, type: hinge, body: cam, axis: [0, 1, 0], damping: 0.002}   # the one driven joint; + : clockwise
  - {id: j_x, type: slide, body: bar, axis: [1, 0, 0], range: [-1.0, 4.9639], stiffness: 250.0, springref: -0.8, damping: 3.0}   # the bar in its guides, a spring holding the roller on the cam; + : out
driver: {joint: j_c, kind: velocity, rpm: 10.0, kv: 20.0, max_torque: 20}
checks:
  - {type: follow, out: j_x, in: j_c, expect: 0.01267, tol: 0.00038, min_r2: 0.995, settle_s: 6.8333, window_s: 1.6667}   # out: K a radian
  - {type: follow, out: j_x, in: j_c, expect: -0.01266, tol: 0.00038, min_r2: 0.995, settle_s: 9.8333, window_s: 1.3333}   # and back
  - {type: position, joint: j_x, min: -0.05, max: 4.0139, settle_s: 0.5, window_s: 11}
camera: {target: [9.268, 0, -2.5], position: [9.268, -80, 0], fov: 34}
display:
  output: j_x
  labels: {j_c: "Heart-cam (driver)", j_x: "Bar"}
  proof: {label: "Cam out of contact", sub: "the cam turns but the bar stands"}

