| 1 | module sim |
| 2 | |
| 3 | import math |
| 4 | |
| 5 | pub const default_rope_length = 0.25 |
| 6 | pub const default_bearing_mass = 0.03 |
| 7 | pub const default_magnet_spacing = 0.05 |
| 8 | pub const default_magnet_height = 0.03 |
| 9 | pub const default_magnet_strength = 10.0 |
| 10 | pub const default_gravity = 4.9 |
| 11 | |
| 12 | @[params] |
| 13 | pub struct SimParams { |
| 14 | pub: |
| 15 | rope_length f64 = default_rope_length |
| 16 | bearing_mass f64 = default_bearing_mass |
| 17 | magnet_spacing f64 = default_magnet_spacing |
| 18 | magnet_height f64 = default_magnet_height |
| 19 | magnet_strength f64 = default_magnet_strength |
| 20 | gravity f64 = default_gravity |
| 21 | } |
| 22 | |
| 23 | pub fn sim_params(params SimParams) SimParams { |
| 24 | return SimParams{ |
| 25 | ...params |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | pub fn (params SimParams) get_rope_vector(state SimState) Vector3D { |
| 30 | rope_origin := vector(z: params.rope_length) |
| 31 | |
| 32 | return state.position + rope_origin.scale(-1) |
| 33 | } |
| 34 | |
| 35 | pub fn (params SimParams) get_forces_sum(state SimState) Vector3D { |
| 36 | // force due to gravity |
| 37 | f_gravity := params.get_grav_force(state) |
| 38 | |
| 39 | // force due to magnets |
| 40 | f_magnet1 := params.get_magnet1_force(state) |
| 41 | f_magnet2 := params.get_magnet2_force(state) |
| 42 | f_magnet3 := params.get_magnet3_force(state) |
| 43 | |
| 44 | mut f_passive := vector(x: 0.0, y: 0.0, z: 0.0) |
| 45 | for force in [f_gravity, f_magnet1, f_magnet2, f_magnet3] { |
| 46 | f_passive = f_passive + force |
| 47 | } |
| 48 | |
| 49 | // force due to tension of the rope |
| 50 | f_tension := params.get_tension_force(state, f_passive) |
| 51 | |
| 52 | return f_passive + f_tension |
| 53 | } |
| 54 | |
| 55 | pub fn (params SimParams) get_grav_force(state SimState) Vector3D { |
| 56 | return vector(z: -params.bearing_mass * params.gravity) |
| 57 | } |
| 58 | |
| 59 | pub fn (params SimParams) get_magnet_position(theta f64) Vector3D { |
| 60 | return vector( |
| 61 | x: math.cos(theta) * params.magnet_spacing |
| 62 | y: math.sin(theta) * params.magnet_spacing |
| 63 | z: -params.magnet_height |
| 64 | ) |
| 65 | } |
| 66 | |
| 67 | pub fn (params SimParams) get_magnet_force(theta f64, state SimState) Vector3D { |
| 68 | magnet_position := params.get_magnet_position(theta) |
| 69 | mut diff := magnet_position + state.position.scale(-1) |
| 70 | distance_squared := diff.norm_squared() |
| 71 | diff = diff.scale(1.0 / math.sqrt(distance_squared)) |
| 72 | return diff.scale(params.magnet_strength / distance_squared) |
| 73 | } |
| 74 | |
| 75 | pub fn (params SimParams) get_magnet_dist(theta f64, state SimState) f64 { |
| 76 | return (params.get_magnet_position(theta) + state.position.scale(-1)).norm() |
| 77 | } |
| 78 | |
| 79 | pub fn (params SimParams) get_magnet1_force(state SimState) Vector3D { |
| 80 | return params.get_magnet_force(0.0 * math.pi / 3.0, state) |
| 81 | } |
| 82 | |
| 83 | pub fn (params SimParams) get_magnet2_force(state SimState) Vector3D { |
| 84 | return params.get_magnet_force(2.0 * math.pi / 3.0, state) |
| 85 | } |
| 86 | |
| 87 | pub fn (params SimParams) get_magnet3_force(state SimState) Vector3D { |
| 88 | return params.get_magnet_force(4.0 * math.pi / 3.0, state) |
| 89 | } |
| 90 | |
| 91 | pub fn (params SimParams) get_tension_force(state SimState, f_passive Vector3D) Vector3D { |
| 92 | rope_vector := params.get_rope_vector(state) |
| 93 | rope_vector_norm := rope_vector.scale(1.0 / rope_vector.norm()) |
| 94 | return rope_vector_norm.scale(-1.0 * (rope_vector_norm * f_passive)) |
| 95 | } |
| 96 | |