pub fn course_projection_system(
    mut commands: Commands,
    k_bods: Query<(&Kinimatics, &Transform, Option<&Engine>), Without<ProjectionDot>>,
    mut dots: Query<(Entity, &mut Transform), With<ProjectionDot>>,
    sprites: Res<UISprites>,
) {
    // make a copy of all the entities
    let mut entities: Vec<(Kinimatics, Transform, Option<Engine>)> = k_bods
        .iter()
        .map(|(kinimatics, transform, engine)| {
            if let Some(e) = engine {
                return (kinimatics.clone(), transform.clone(), Some(e.clone()));
            } else {
                return (kinimatics.clone(), transform.clone(), None);
            }
        })
        .collect();

    let num_seconds = 5; // number of seconds to look ahead
    let step_precision = 10; // steps/second

    let mut steps: Vec<Vec<Transform>> = Vec::new();
    steps.reserve(num_seconds * step_precision);

    // each element will have a corresponding entry in this list.
    let num_bods = entities.iter().count();
    let mut all_forces: Vec<Vec<Vec3>> = Vec::new();
    all_forces.reserve(num_bods);
    for _ in 0..num_bods {
        all_forces.push(Vec::new());
    }

    // account for force due to gravity
    const GRAVITATIONAL_CONSTANT: f32 = 6.67430e-11;
    for step in 0..num_seconds * step_precision {
        let dt = 1.0 / (step_precision as f32);

        // calculate forces due to gravity
        for (i, e1) in entities.iter().enumerate() {
            // NOTE do I need to do bounds checking here?
            entities
                .split_at(i + 1)
                .1
                .iter()
                .enumerate()
                .for_each(|(j, e2)| {
                    // calculate magnitude of the force
                    let force_mag = GRAVITATIONAL_CONSTANT * (e1.0.mass * e2.0.mass)
                        / e1.1.translation.distance_squared(e2.1.translation);

                    // calculate direction and magnitude of the forces for each object.
                    let d1: Vec3 = (e2.1.translation - e1.1.translation).normalize() * force_mag;
                    let d2 = (e1.1.translation - e2.1.translation).normalize() * force_mag;

                    // add these forces to a list of forces
                    all_forces[i].push(d1);
                    all_forces[i + j + 1].push(d2);
                });
        }

        // create a new vector to hold this snapshot
        steps.push(Vec::new());
        // Calculate other forces and update kinimatics
        for (i, (kin, tran, engine)) in entities.iter_mut().enumerate() {
            // handle acceleration from ship engine
            if let Some(t) = engine {
                all_forces[i].push(
                    tran.rotation.mul_vec3(Vec3::Y)
                        * match t.throttle {
                            Throttle::Fixed(true) => t.max_thrust,
                            Throttle::Fixed(false) => 0.0,
                            Throttle::Variable(amount) => amount * t.max_thrust,
                        },
                );
            }

            // add up forces, then apply them
            kin.acceleration = all_forces[i]
                .iter()
                .copied()
                .reduce(|acc, x| acc + x)
                .expect("0 forces")
                / kin.mass;

            kin.velocity = kin.velocity + kin.acceleration * dt;
            tran.translation = tran.translation + kin.velocity * dt;

            steps[step].push(tran.clone());
            all_forces[i].clear();
        }
    }

    // total number of dots needed for projection
    let total_dots = steps.len() * entities.len();
    let available_dots = dots.iter_mut().count();

    if available_dots > total_dots {
        // remove extra dots
        let mut dots = dots.iter_mut();
        for _ in 0..(available_dots - total_dots) {
            if let Some(d) = dots.next() {
                commands.entity(d.0).despawn();
            }
        }
    } else if available_dots < total_dots {
        // spawn in missing dots
        for _ in 0..(total_dots - available_dots) {
            commands
                .spawn()
                .insert(ProjectionDot {})
                .insert(Transform::default())
                .insert(GlobalTransform::default())
                .with_children(|p| {
                    p.spawn_bundle(sprites.projection_dot.clone());
                });
        }
    }

    let steps: Vec<Transform> = steps.into_iter().flatten().collect();

    for (i, (_, mut transform)) in dots.iter_mut().enumerate() {
        *transform = steps[i];
    }
}
/// :SYSTEM: Projects the motion of all kinimatic bodies.
///
/// Currently, the projection is displayed by using a bunch of `ProjectionDot entities which
/// are moved to the entities projected locations. In the future, the plan is to transition to
/// a shader to display the dot.
:SYSTEM: Projects the motion of all kinimatic bodies.
Currently, the projection is displayed by using a bunch of `ProjectionDot entities which
are moved to the entities projected locations. In the future, the plan is to transition to
a shader to display the dot.
: SYSTEM : Projects the motion of all kinimatic bodies . Currently the projection is displayed by using a bunch of ` ProjectionDot entities which are moved to the entities projected locations . In the future the plan is to transition to a shader to display the dot .
pub fn course_projection_system(
    mut commands: Commands,
    k_bods: Query<(&Kinimatics, &Transform, Option<&Engine>), Without<ProjectionDot>>,
    mut dots: Query<(Entity, &mut Transform), With<ProjectionDot>>,
    sprites: Res<UISprites>,
) {
    // make a copy of all the entities
    let mut entities: Vec<(Kinimatics, Transform, Option<Engine>)> = k_bods
        .iter()
        .map(|(kinimatics, transform, engine)| {
            if let Some(e) = engine {
                return (kinimatics.clone(), transform.clone(), Some(e.clone()));
            } else {
                return (kinimatics.clone(), transform.clone(), None);
            }
        })
        .collect();
    let num_seconds = 5; // number of seconds to look ahead
    let step_precision = 10; // steps/second
    let mut steps: Vec<Vec<Transform>> = Vec::new();
    steps.reserve(num_seconds * step_precision);
    // each element will have a corresponding entry in this list.
    let num_bods = entities.iter().count();
    let mut all_forces: Vec<Vec<Vec3>> = Vec::new();
    all_forces.reserve(num_bods);
    for _ in 0..num_bods {
        all_forces.push(Vec::new());
    }
    // account for force due to gravity
    const GRAVITATIONAL_CONSTANT: f32 = 6.67430e-11;
    for step in 0..num_seconds * step_precision {
        let dt = 1.0 / (step_precision as f32);
        // calculate forces due to gravity
        for (i, e1) in entities.iter().enumerate() {
            // NOTE do I need to do bounds checking here?
            entities
                .split_at(i + 1)
                .1
                .iter()
                .enumerate()
                .for_each(|(j, e2)| {
                    // calculate magnitude of the force
                    let force_mag = GRAVITATIONAL_CONSTANT * (e1.0.mass * e2.0.mass)
                        / e1.1.translation.distance_squared(e2.1.translation);
                    // calculate direction and magnitude of the forces for each object.
                    let d1: Vec3 = (e2.1.translation - e1.1.translation).normalize() * force_mag;
                    let d2 = (e1.1.translation - e2.1.translation).normalize() * force_mag;
                    // add these forces to a list of forces
                    all_forces[i].push(d1);
                    all_forces[i + j + 1].push(d2);
                });
        }
        // create a new vector to hold this snapshot
        steps.push(Vec::new());
        // Calculate other forces and update kinimatics
        for (i, (kin, tran, engine)) in entities.iter_mut().enumerate() {
            // handle acceleration from ship engine
            if let Some(t) = engine {
                all_forces[i].push(
                    tran.rotation.mul_vec3(Vec3::Y)
                        * match t.throttle {
                            Throttle::Fixed(true) => t.max_thrust,
                            Throttle::Fixed(false) => 0.0,
                            Throttle::Variable(amount) => amount * t.max_thrust,
                        },
                );
            }
            // add up forces, then apply them
            kin.acceleration = all_forces[i]
                .iter()
                .copied()
                .reduce(|acc, x| acc + x)
                .expect("0 forces")
                / kin.mass;
            kin.velocity = kin.velocity + kin.acceleration * dt;
            tran.translation = tran.translation + kin.velocity * dt;
            steps[step].push(tran.clone());
            all_forces[i].clear();
        }
    }
    // total number of dots needed for projection
    let total_dots = steps.len() * entities.len();
    let available_dots = dots.iter_mut().count();
    if available_dots > total_dots {
        // remove extra dots
        let mut dots = dots.iter_mut();
        for _ in 0..(available_dots - total_dots) {
            if let Some(d) = dots.next() {
                commands.entity(d.0).despawn();
            }
        }
    } else if available_dots < total_dots {
        // spawn in missing dots
        for _ in 0..(total_dots - available_dots) {
            commands
                .spawn()
                .insert(ProjectionDot {})
                .insert(Transform::default())
                .insert(GlobalTransform::default())
                .with_children(|p| {
                    p.spawn_bundle(sprites.projection_dot.clone());
                });
        }
    }
    let steps: Vec<Transform> = steps.into_iter().flatten().collect();
    for (i, (_, mut transform)) in dots.iter_mut().enumerate() {
        *transform = steps[i];
    }
}
pub fn course_projection_system ( mut commands : Commands , k_bods : Query < ( & Kinimatics , & Transform , Option < & Engine > ) , Without < ProjectionDot > > , mut dots : Query < ( Entity , & mut Transform ) , With < ProjectionDot > > , sprites : Res < UISprites > , ) { // make a copy of all the entities let mut entities : Vec < ( Kinimatics , Transform , Option < Engine > ) > = k_bods . iter ( ) . map ( | ( kinimatics , transform , engine ) | { if let Some ( e ) = engine { return ( kinimatics . clone ( ) , transform . clone ( ) , Some ( e . clone ( ) ) ) ; } else { return ( kinimatics . clone ( ) , transform . clone ( ) , None ) ; } } ) . collect ( ) ; let num_seconds = 5 ; // number of seconds to look ahead let step_precision = 10 ; // steps/second let mut steps : Vec < Vec < Transform > > = Vec :: new ( ) ; steps . reserve ( num_seconds * step_precision ) ; // each element will have a corresponding entry in this list. let num_bods = entities . iter ( ) . count ( ) ; let mut all_forces : Vec < Vec < Vec3 > > = Vec :: new ( ) ; all_forces . reserve ( num_bods ) ; for _ in 0 .. num_bods { all_forces . push ( Vec :: new ( ) ) ; } // account for force due to gravity const GRAVITATIONAL_CONSTANT : f32 = 6.67430e-11 ; for step in 0 .. num_seconds * step_precision { let dt = 1.0 / ( step_precision as f32 ) ; // calculate forces due to gravity for ( i , e1 ) in entities . iter ( ) . enumerate ( ) { // NOTE do I need to do bounds checking here? entities . split_at ( i + 1 ) . 1 . iter ( ) . enumerate ( ) . for_each ( | ( j , e2 ) | { // calculate magnitude of the force let force_mag = GRAVITATIONAL_CONSTANT * ( e1 . 0 . mass * e2 . 0 . mass ) / e1 . 1 . translation . distance_squared ( e2 . 1 . translation ) ; // calculate direction and magnitude of the forces for each object. let d1 : Vec3 = ( e2 . 1 . translation - e1 . 1 . translation ) . normalize ( ) * force_mag ; let d2 = ( e1 . 1 . translation - e2 . 1 . translation ) . normalize ( ) * force_mag ; // add these forces to a list of forces all_forces [ i ] . push ( d1 ) ; all_forces [ i + j + 1 ] . push ( d2 ) ; } ) ; } // create a new vector to hold this snapshot steps . push ( Vec :: new ( ) ) ; // Calculate other forces and update kinimatics for ( i , ( kin , tran , engine ) ) in entities . iter_mut ( ) . enumerate ( ) { // handle acceleration from ship engine if let Some ( t ) = engine { all_forces [ i ] . push ( tran . rotation . mul_vec3 ( Vec3 :: Y ) * match t . throttle { Throttle :: Fixed ( true ) => t . max_thrust , Throttle :: Fixed ( false ) => 0.0 , Throttle :: Variable ( amount ) => amount * t . max_thrust , } , ) ; } // add up forces, then apply them kin . acceleration = all_forces [ i ] . iter ( ) . copied ( ) . reduce ( | acc , x | acc + x ) . expect ( " " ) / kin . mass ; kin . velocity = kin . velocity + kin . acceleration * dt ; tran . translation = tran . translation + kin . velocity * dt ; steps [ step ] . push ( tran . clone ( ) ) ; all_forces [ i ] . clear ( ) ; } } // total number of dots needed for projection let total_dots = steps . len ( ) * entities . len ( ) ; let available_dots = dots . iter_mut ( ) . count ( ) ; if available_dots > total_dots { // remove extra dots let mut dots = dots . iter_mut ( ) ; for _ in 0 .. ( available_dots - total_dots ) { if let Some ( d ) = dots . next ( ) { commands . entity ( d . 0 ) . despawn ( ) ; } } } else if available_dots < total_dots { // spawn in missing dots for _ in 0 .. ( total_dots - available_dots ) { commands . spawn ( ) . insert ( ProjectionDot { } ) . insert ( Transform :: default ( ) ) . insert ( GlobalTransform :: default ( ) ) . with_children ( | p | { p . spawn_bundle ( sprites . projection_dot . clone ( ) ) ; } ) ; } } let steps : Vec < Transform > = steps . into_iter ( ) . flatten ( ) . collect ( ) ; for ( i , ( _ , mut transform ) ) in dots . iter_mut ( ) . enumerate ( ) { * transform = steps [ i ] ; } }
:SYSTEM: Projects the motion of all kinimatic bodies.
: SYSTEM : Projects the motion of all kinimatic bodies .
commands
Commands
k_bods
Query<(&Kinimatics, &Transform, Option<&Engine>), Without<ProjectionDot>>
dots
Query<(Entity, &mut Transform), With<ProjectionDot>>
sprites
Res<UISprites>
commands
Commands
k_bods
Query<(&Kinimatics, &Transform, Option<&Engine>), Without<ProjectionDot>>
dots
Query<(Entity, &mut Transform), With<ProjectionDot>>
sprites
Res<UISprites>