~starkingdoms/starkingdoms

bb445989bb26c177aea0eef22ff275f03b161fad — core 18 days ago 451ad04
fix(milp): improve predictability of solver
M crates/unified/assets/config/parts/housing.part.toml => crates/unified/assets/config/parts/housing.part.toml +0 -5
@@ 13,11 13,6 @@ id = "main"
apply_force_at_local = [ 0, 0 ]
thrust_vector = [ 0.0, -20000.0 ]

[[thruster]]
id = "back"
apply_force_at_local = [ 0, 0 ]
thrust_vector = [ 0.0, 20000.0 ]

[[joint]]
id = "Top"
target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 }

M crates/unified/assets/config/world.wc.toml => crates/unified/assets/config/world.wc.toml +3 -3
@@ 7,9 7,9 @@ spawn_parts_interval_secs = 1
default_height = 50
default_width = 50
default_mass = 100
joint_align_compliance = 0.0002
joint_angle_compliance = 0.00000002
joint_limit_compliance = 0.006
joint_align_compliance = 0.00002
joint_angle_compliance = 0.000000002
joint_limit_compliance = 0.0006
joint_linear_damping = 4.0
joint_angular_damping = 2.0


M crates/unified/src/client/ship/thrusters.rs => crates/unified/src/client/ship/thrusters.rs +37 -8
@@ 55,6 55,7 @@ fn draw_thruster_debug(
    }
}

// TODO: split this into two passes
fn solve_thrust(
    me: Query<(Option<&Parts>, &GlobalTransform, Entity), With<Me>>,
    parts: Query<&PartThrusters>,


@@ 90,28 91,40 @@ fn solve_thrust(
    // +Z == counterclockwise/ccw
    // -Z == clockwise/cw

    const MAGIC_TORQUE_SCALE_FACTOR: f32 = 100.0; // mystery units

    let mut target_unit_vector = Vec3::ZERO;

    let mut anything_pressed = false;

    if input.pressed(&ClientAction::ThrustForward) {
        anything_pressed = true;
        target_unit_vector += hearty_transform.rotation() * Vec3::new(0.0, 1.0, 0.0);
    }
    if input.pressed(&ClientAction::ThrustBackward) {
        anything_pressed = true;
        target_unit_vector += hearty_transform.rotation() * Vec3::new(0.0, -1.0, 0.0);
    }
    if input.pressed(&ClientAction::ThrustRight) {
        anything_pressed = true;
        target_unit_vector += hearty_transform.rotation() * Vec3::new(1.0, 0.0, 0.0);
    }
    if input.pressed(&ClientAction::ThrustLeft) {
        anything_pressed = true;
        target_unit_vector += hearty_transform.rotation() * Vec3::new(-1.0, 0.0, 0.0);
    }
    if input.pressed(&ClientAction::TorqueCw) {
        target_unit_vector += Vec3::new(0.0, 0.0, -1.0);
        anything_pressed = true;
        target_unit_vector += Vec3::new(0.0, 0.0, -1.0 / MAGIC_TORQUE_SCALE_FACTOR);
    }
    if input.pressed(&ClientAction::TorqueCcw) {
        target_unit_vector += Vec3::new(0.0, 0.0, 1.0);
        anything_pressed = true;
        target_unit_vector += Vec3::new(0.0, 0.0, 1.0 / MAGIC_TORQUE_SCALE_FACTOR);
    }

    if target_unit_vector == Vec3::ZERO {
    target_unit_vector = target_unit_vector.normalize();

    if target_unit_vector == Vec3::ZERO || !anything_pressed {
        trace!("no buttons are pressed; zeroing thrust solution");
        trace!("solved thrust in {}ms", start.elapsed().as_millis());
        solution.converged = true;


@@ 145,7 158,7 @@ fn solve_thrust(
            // determine our rotational offset from hearty
            let relative_rotation = thruster_transform.rotation() * -hearty_transform.rotation();

            let thruster_torque = relative_translation.extend(0.0).cross(thruster_vector.extend(0.0)).z;
            let thruster_torque = relative_translation.extend(0.0).cross(thruster_vector.extend(0.0)).z / MAGIC_TORQUE_SCALE_FACTOR;

            // magically assemble the worldspace vector! for the solver (not shipspace)
            let target_vector = thruster_vector.extend(thruster_torque);


@@ 162,7 175,22 @@ fn solve_thrust(
    }

    let coefficients = all_thrusters.iter()
        .map(|u| target_unit_vector.dot(u.1))
        .map(|u| {
            trace!("{} dot {}", target_unit_vector, u.1.normalize());
            target_unit_vector.dot(u.1.normalize())
        })
        .map(|u| {
            trace!("=> {u}");
            // improve reliability:
            // if dot is <0.1, zap it entirely (this thruster is not helping)
            // TODO: figure out how to make this adjustable
            if u.abs() < 0.1 {
                0.0
            } else {
                u
            }
        })
        //.map(|u| u.powi(3))
        .collect::<Vec<_>>();

    trace!("preparing model");


@@ 170,7 198,7 @@ fn solve_thrust(

    // add variables to problem
    let variables = coefficients.iter()
        .map(|u| problem.add_binary_var(*u as f64))
        .map(|u| problem.add_var(*u as f64, (0.0, 1.0)))
        .collect::<Vec<_>>();

    trace!("prepared {} variables; solving", variables.len());


@@ 207,8 235,9 @@ fn solve_thrust(
    };

    for thruster in all_thrusters.iter().enumerate() {
        trace!("solution: thruster #{} ({:?}): {}", thruster.0, thruster.1.0, ssolution.var_value_rounded(variables[thruster.0]));
        if ssolution.var_value_rounded(variables[thruster.0]) == 1.0 {
        trace!("solution: thruster #{} ({:?}): {} @ coeff {}", thruster.0, thruster.1.0, ssolution.var_value(variables[thruster.0]), coefficients[thruster.0]);
        // TODO: make this more easily adjustable
        if *ssolution.var_value(variables[thruster.0]) > 0.8 {
            new_soln.thrusters_on.insert(*thruster.1.0);
        }
    }