# No. 209 (Brown 1868): "Represents a mode of obtaining motion from rolling contact. The teeth are for making the motion continuous, or it would cease at
# the point of contact shown in the figure. The forked catch is to guide the teeth into proper contact."
#
# Model (cm): No. 33's pair of equal elliptical wheels (semi-axes 10 and 8.660, eccentricity 0.5), each on a shaft through one focus, the shafts 2a = 20
# apart, so the pitch ellipses roll on each other. Each carries 24 straight-flanked teeth at equal arc pitch 2.446, except over the part where, as the
# plate draws them, they roll plain: the left wheel's far vertex on the right one's near vertex. Two of the left wheel's teeth and one of the right's are
# left out there and the rims run out to the pitch curves. In a plane in front of the teeth the left wheel carries a pointed horn in the middle of its
# plain part and the right one a forked catch, two prongs 0.5 pitch either side; the horn enters the fork as the plain parts come round and carries the
# drive across them until the teeth take it up again. Only the left wheel's shaft is driven (20 rpm); the right one turns by contact alone, at
# -r1/r2 of the driver: from -1/3 to -3 and back each turn, -1 on average. Generator: opencode-runs/batch11/gen209.py.
number: 209
slug: rolling-contact-forked-catch
name: Rolling contact with teeth and a forked catch
brown: "Represents a mode of obtaining motion from rolling contact. The teeth are for making the motion continuous, or it would cease at the point of contact shown in the figure. The forked catch is to guide the teeth into proper contact."
units: cm
sim:
  timestep: 0.0002
  integrator: implicitfast
  iterations: 100
  solver: CG
  multiccd: true
  duration_s: 8
  floor: false
bodies:
  - id: ga
    shape: part
    pos: [-10, 0, 0]
    density: 7800
    friction: 0.1
    color: "#e4d9bf"
    pieces:
      - {kind: disc, center: [-10, 0], radius: 1.4, y: [-1.0, 1.0], collide: 0}   # hub on the focus
      - {kind: poly, points: [[4.1506, 0.0], [4.101, 0.8115], [3.9531, 1.6121], [3.7102, 2.3913], [3.3771, 3.1398], [2.9601, 3.849], [2.4665, 4.512], [1.9042, 5.1228], [1.2811, 5.6771], [0.6053, 6.1713], [-0.1157, 6.603], [-0.8743, 6.9704], [-1.6637, 7.2722], [-2.4771, 7.5075], [-3.3083, 7.676], [-4.1513, 7.7771], [-5.0, 7.8109], [-5.8487, 7.7771], [-6.6917, 7.676], [-7.5229, 7.5075], [-8.3363, 7.2722], [-9.1257, 6.9704], [-9.8843, 6.603], [-10.6053, 6.1713], [-11.2811, 5.6771], [-11.9042, 5.1228], [-12.4665, 4.512], [-12.9601, 3.849], [-13.3771, 3.1398], [-13.7102, 2.3913], [-13.9531, 1.6121], [-14.101, 0.8115], [-14.1506, -0.0], [-14.101, -0.8115], [-13.9531, -1.6121], [-13.7102, -2.3913], [-13.3771, -3.1398], [-12.9601, -3.849], [-12.4665, -4.512], [-11.9042, -5.1228], [-11.2811, -5.6771], [-10.6053, -6.1713], [-9.8843, -6.603], [-9.1257, -6.9704], [-8.3363, -7.2722], [-7.5229, -7.5075], [-6.6917, -7.676], [-5.8487, -7.7771], [-5.0, -7.8109], [-4.1513, -7.7771], [-3.3083, -7.676], [-2.4771, -7.5075], [-1.6637, -7.2722], [-0.8743, -6.9704], [-0.1157, -6.603], [0.6053, -6.1713], [1.2811, -5.6771], [1.9042, -5.1228], [2.4665, -4.512], [2.9601, -3.849], [3.3771, -3.1398], [3.7102, -2.3913], [3.9531, -1.6121], [4.101, -0.8115]], y: [-0.6, 0.6], collide: 0}   # the plate, inside the root curve
      - {kind: poly, points: [[3.7074, 2.3242], [2.9046, 3.8822], [4.6603, 4.1058], [4.9085, 3.624]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[2.6453, 4.2356], [1.4364, 5.5046], [3.0581, 6.2135], [3.432, 5.821]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[1.0725, 5.7929], [-0.4176, 6.7157], [0.9727, 7.8109], [1.4335, 7.5255]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-0.8581, 6.9181], [-2.5201, 7.4745], [-1.418, 8.8594], [-0.9041, 8.6873]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-3.0064, 7.5785], [-4.7492, 7.7641], [-3.9712, 9.3538], [-3.4322, 9.2964]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-5.2508, 7.7641], [-6.9936, 7.5785], [-6.5678, 9.2964], [-6.0288, 9.3538]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-7.4799, 7.4745], [-9.1419, 6.9181], [-9.0959, 8.6873], [-8.582, 8.8594]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-9.5824, 6.7157], [-11.0725, 5.7929], [-11.4335, 7.5255], [-10.9727, 7.8109]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-11.4364, 5.5046], [-12.6453, 4.2356], [-13.432, 5.821], [-13.0581, 6.2135]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-12.9046, 3.8822], [-13.7074, 2.3242], [-14.9085, 3.624], [-14.6603, 4.1058]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-13.8417, 1.9319], [-14.1253, 0.2023], [-15.6686, 1.0688], [-15.5809, 1.6037]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-14.1253, -0.2023], [-13.8417, -1.9319], [-15.5809, -1.6037], [-15.6686, -1.0688]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-13.7074, -2.3242], [-12.9046, -3.8822], [-14.6603, -4.1058], [-14.9085, -3.624]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-12.6453, -4.2356], [-11.4364, -5.5046], [-13.0581, -6.2135], [-13.432, -5.821]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-11.0725, -5.7929], [-9.5824, -6.7157], [-10.9727, -7.8109], [-11.4335, -7.5255]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-9.1419, -6.9181], [-7.4799, -7.4745], [-8.582, -8.8594], [-9.0959, -8.6873]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-6.9936, -7.5785], [-5.2508, -7.7641], [-6.0288, -9.3538], [-6.5678, -9.2964]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-4.7492, -7.7641], [-3.0064, -7.5785], [-3.4322, -9.2964], [-3.9712, -9.3538]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-2.5201, -7.4745], [-0.8581, -6.9181], [-0.9041, -8.6873], [-1.418, -8.8594]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[-0.4176, -6.7157], [1.0725, -5.7929], [1.4335, -7.5255], [0.9727, -7.8109]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[1.4364, -5.5046], [2.6453, -4.2356], [3.432, -5.821], [3.0581, -6.2135]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[2.9046, -3.8822], [3.7074, -2.3242], [4.9085, -3.624], [4.6603, -4.1058]], y: [-0.6, 0.6], collide: 1}
      - {kind: poly, points: [[3.5352, -2.6136], [3.7256, -2.1092], [4.5883, -2.397], [4.3729, -2.9674]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.7256, -2.1092], [3.8755, -1.5928], [4.7582, -1.8114], [4.5883, -2.397]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.8755, -1.5928], [3.9835, -1.0671], [4.881, -1.2143], [4.7582, -1.8114]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.9835, -1.0671], [4.0488, -0.5351], [4.9552, -0.6091], [4.881, -1.2143]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[4.0488, -0.5351], [4.0706, 0.0], [4.98, 0.0], [4.9552, -0.6091]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[4.0706, 0.0], [4.0488, 0.5351], [4.9552, 0.6091], [4.98, 0.0]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[4.0488, 0.5351], [3.9835, 1.0671], [4.881, 1.2143], [4.9552, 0.6091]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.9835, 1.0671], [3.8755, 1.5928], [4.7582, 1.8114], [4.881, 1.2143]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.8755, 1.5928], [3.7256, 2.1092], [4.5883, 2.397], [4.7582, 1.8114]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[3.7256, 2.1092], [3.5352, 2.6136], [4.3729, 2.9674], [4.5883, 2.397]], y: [-0.6, 0.6], collide: 0}   # A's plain rim
      - {kind: poly, points: [[4.0706, -0.5503], [4.0706, 0.5503], [5.9783, 0.1467], [5.9783, -0.1467]], y: [-1.3, -0.7], collide: 1, color: "#8f8a80"}   # the horn, in front
  - id: gb
    shape: part
    pos: [10, 0, 0]
    density: 7800
    friction: 0.1
    color: "#e8dec6"
    pieces:
      - {kind: disc, center: [10, 0], radius: 1.4, y: [-1.0, 1.0], collide: 0}
      - {kind: poly, points: [[24.1506, 0.0], [24.101, 0.8115], [23.9531, 1.6121], [23.7102, 2.3913], [23.3771, 3.1398], [22.9601, 3.849], [22.4665, 4.512], [21.9042, 5.1228], [21.2811, 5.6771], [20.6053, 6.1713], [19.8843, 6.603], [19.1257, 6.9704], [18.3363, 7.2722], [17.5229, 7.5075], [16.6917, 7.676], [15.8487, 7.7771], [15.0, 7.8109], [14.1513, 7.7771], [13.3083, 7.676], [12.4771, 7.5075], [11.6637, 7.2722], [10.8743, 6.9704], [10.1157, 6.603], [9.3947, 6.1713], [8.7189, 5.6771], [8.0958, 5.1228], [7.5335, 4.512], [7.0399, 3.849], [6.6229, 3.1398], [6.2898, 2.3913], [6.0469, 1.6121], [5.899, 0.8115], [5.8494, -0.0], [5.899, -0.8115], [6.0469, -1.6121], [6.2898, -2.3913], [6.6229, -3.1398], [7.0399, -3.849], [7.5335, -4.512], [8.0958, -5.1228], [8.7189, -5.6771], [9.3947, -6.1713], [10.1157, -6.603], [10.8743, -6.9704], [11.6637, -7.2722], [12.4771, -7.5075], [13.3083, -7.676], [14.1513, -7.7771], [15.0, -7.8109], [15.8487, -7.7771], [16.6917, -7.676], [17.5229, -7.5075], [18.3363, -7.2722], [19.1257, -6.9704], [19.8843, -6.603], [20.6053, -6.1713], [21.2811, -5.6771], [21.9042, -5.1228], [22.4665, -4.512], [22.9601, -3.849], [23.3771, -3.1398], [23.7102, -2.3913], [23.9531, -1.6121], [24.101, -0.8115]], y: [-0.6, 0.6], collide: 0}   # the plate, inside the root curve
      - {kind: poly, points: [[24.0706, -0.8763], [24.0706, 0.8763], [25.7337, 0.271], [25.7337, -0.271]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[24.0029, 1.2779], [23.4483, 2.9406], [25.2175, 2.8926], [25.389, 2.3784]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[23.2496, 3.3168], [22.2279, 4.7409], [23.932, 5.2186], [24.248, 4.7782]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[21.9131, 5.0647], [20.5487, 6.1649], [22.0639, 7.0796], [22.4858, 6.7394]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[20.1427, 6.4123], [18.5543, 7.153], [19.8058, 8.4045], [20.2971, 8.1754]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[18.087, 7.3073], [16.3741, 7.6785], [17.3179, 9.1757], [17.8476, 9.0609]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[15.8763, 7.7309], [14.1237, 7.7309], [14.729, 9.394], [15.271, 9.394]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[13.6259, 7.6785], [11.913, 7.3073], [12.1524, 9.0609], [12.6821, 9.1757]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[11.4457, 7.153], [9.8573, 6.4123], [9.7029, 8.1754], [10.1942, 8.4045]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[9.4513, 6.1649], [8.0869, 5.0647], [7.5142, 6.7394], [7.9361, 7.0796]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[7.7721, 4.7409], [6.7504, 3.3168], [5.752, 4.7782], [6.068, 5.2186]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[6.5517, 2.9406], [5.9971, 1.2779], [4.611, 2.3784], [4.7825, 2.8926]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[5.9971, -1.2779], [6.5517, -2.9406], [4.7825, -2.8926], [4.611, -2.3784]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[6.7504, -3.3168], [7.7721, -4.7409], [6.068, -5.2186], [5.752, -4.7782]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[8.0869, -5.0647], [9.4513, -6.1649], [7.9361, -7.0796], [7.5142, -6.7394]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[9.8573, -6.4123], [11.4457, -7.153], [10.1942, -8.4045], [9.7029, -8.1754]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[11.913, -7.3073], [13.6259, -7.6785], [12.6821, -9.1757], [12.1524, -9.0609]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[14.1237, -7.7309], [15.8763, -7.7309], [15.271, -9.394], [14.729, -9.394]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[16.3741, -7.6785], [18.087, -7.3073], [17.8476, -9.0609], [17.3179, -9.1757]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[18.5543, -7.153], [20.1427, -6.4123], [20.2971, -8.1754], [19.8058, -8.4045]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[20.5487, -6.1649], [21.9131, -5.0647], [22.4858, -6.7394], [22.0639, -7.0796]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[22.2279, -4.7409], [23.2496, -3.3168], [24.248, -4.7782], [23.932, -5.2186]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[23.4483, -2.9406], [24.0029, -1.2779], [25.389, -2.3784], [25.2175, -2.8926]], y: [-0.6, 0.6], collide: 2}
      - {kind: poly, points: [[6.4648, 2.6136], [6.2744, 2.1092], [5.4117, 2.397], [5.6271, 2.9674]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.2744, 2.1092], [6.1245, 1.5928], [5.2418, 1.8114], [5.4117, 2.397]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.1245, 1.5928], [6.0165, 1.0671], [5.119, 1.2143], [5.2418, 1.8114]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.0165, 1.0671], [5.9512, 0.5351], [5.0448, 0.6091], [5.119, 1.2143]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[5.9512, 0.5351], [5.9294, -0.0], [5.02, -0.0], [5.0448, 0.6091]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[5.9294, -0.0], [5.9512, -0.5351], [5.0448, -0.6091], [5.02, -0.0]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[5.9512, -0.5351], [6.0165, -1.0671], [5.119, -1.2143], [5.0448, -0.6091]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.0165, -1.0671], [6.1245, -1.5928], [5.2418, -1.8114], [5.119, -1.2143]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.1245, -1.5928], [6.2744, -2.1092], [5.4117, -2.397], [5.2418, -1.8114]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.2744, -2.1092], [6.4648, -2.6136], [5.6271, -2.9674], [5.4117, -2.397]], y: [-0.6, 0.6], collide: 0}   # B's plain rim
      - {kind: poly, points: [[6.1583, 1.9319], [5.8747, 0.2023], [4.09, 1.1083], [4.1777, 1.6432]], y: [-1.3, -0.7], collide: 2, color: "#8f8a80"}   # a prong of the forked catch
      - {kind: poly, points: [[5.8747, -0.2023], [6.1583, -1.9319], [4.1777, -1.6432], [4.09, -1.1083]], y: [-1.3, -0.7], collide: 2, color: "#8f8a80"}   # a prong of the forked catch
      - {kind: poly, points: [[6.0165, 1.0671], [6.0165, -1.0671], [5.6711, -1.1237], [5.6711, 1.1237]], y: [-1.3, -0.7], collide: 0, color: "#8f8a80"}   # the fork's back
  - {id: stubs, shape: frame, rods: [{from: [-10, -2.4, 0], to: [-10, 2.4, 0], radius: 0.6}, {from: [10, -2.4, 0], to: [10, 2.4, 0], radius: 0.6}], color: "#8f8a80"}
joints:
  - {id: j_a, type: hinge, body: ga, axis: [0, 1, 0], damping: 0.001}
  - {id: j_b, type: hinge, body: gb, axis: [0, 1, 0], damping: 0.01}
contact: {solref: [0.002, 1.0], solimp: [0.95, 0.99, 0.0005], margin: 0}
driver: {joint: j_a, kind: velocity, rpm: 20, kv: 6, max_torque: 8}
checks:
  - {type: ratio, out: j_b, in: j_a, expect: -1.0, tol: 0.03, settle_s: 1, window_s: 6}
  - {type: ratio_range, out: j_b, in: j_a, min: -3.3, max: -0.25, min_swing: 2.2, settle_s: 1, window_s: 6}
  - {type: penetration, max: 0.001, settle_s: 0.5}
camera: {target: [0, 0, 0], position: [0, -75, 1], fov: 34}
display:
  output: j_b
  labels: {j_a: "Driver (left)", j_b: "Driven (right)"}
  proof: {label: "Teeth and catch out of contact", sub: "the left wheel turns alone: the right one stands still"}

