@ -494,6 +494,7 @@ void render_maneuver_create_tab(SimulationState* sim, UIState* ui_state, int x,
Rectangle dir_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( dir_label , " Direction: " ) ;
// FIXME: we need to handle state differently here: see raygui controls_test_suite.c example
if ( ui_state - > maneuver_dialog . craft_index > = 0 & & ui_state - > maneuver_dialog . craft_index < sim - > craft_count ) {
Rectangle dir_combo = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 95 ) , 25 } ;
GuiDropdownBox ( dir_combo , " Prograde;Retrograde;Normal;Antinormal;Radial In;Radial Out;Custom " , & ui_state - > maneuver_dialog . direction_active , false ) ;
@ -634,6 +635,208 @@ void render_orbital_preview(SimulationState* sim, UIState* ui_state, Rectangle b
GuiLabel ( status_bounds , status_text ) ;
}
void render_maneuver_hohmann_tab ( SimulationState * sim , UIState * ui_state , int x , int y , int width , int height ) {
int current_y = y ;
if ( ui_state - > maneuver_dialog . craft_index > = 0 & &
ui_state - > maneuver_dialog . craft_index < sim - > craft_count ) {
Spacecraft * craft = & sim - > spacecraft [ ui_state - > maneuver_dialog . craft_index ] ;
if ( craft - > parent_index > = 0 & & craft - > parent_index < sim - > body_count ) {
ui_state - > maneuver_dialog . current_body_index = craft - > parent_index ;
}
}
GuiLabel ( { ( float ) x , ( float ) current_y , ( float ) width , 25 } , " Calculate optimal Hohmann transfer between orbits " ) ;
current_y + = 30 ;
GuiLabel ( { ( float ) x , ( float ) current_y , 120 , 25 } , " Spacecraft: " ) ;
current_y + = 35 ;
if ( sim - > craft_count > 0 ) {
int craft_buffer_size = sim - > craft_count * 64 + 1 ;
char * craft_list = ( char * ) malloc ( craft_buffer_size ) ;
if ( craft_list ) {
craft_list [ 0 ] = ' \0 ' ;
int offset = 0 ;
for ( int i = 0 ; i < sim - > craft_count ; i + + ) {
if ( i > 0 ) {
offset + = snprintf ( craft_list + offset , craft_buffer_size - offset , " ; " ) ;
}
offset + = snprintf ( craft_list + offset , craft_buffer_size - offset , " %s " , sim - > spacecraft [ i ] . name ) ;
}
GuiDropdownBox ( { ( float ) x + 120 , ( float ) ( current_y - 35 ) , ( float ) ( width - 130 ) , 25 } ,
craft_list , & ui_state - > maneuver_dialog . craft_index , false ) ;
free ( craft_list ) ;
}
}
current_y + = 35 ;
GuiLabel ( { ( float ) x , ( float ) current_y , 120 , 25 } , " Transfer Parent: " ) ;
current_y + = 35 ;
if ( sim - > body_count > 0 ) {
int body_buffer_size = sim - > body_count * 64 + 1 ;
char * body_list = ( char * ) malloc ( body_buffer_size ) ;
if ( body_list ) {
body_list [ 0 ] = ' \0 ' ;
int offset = 0 ;
for ( int i = 0 ; i < sim - > body_count ; i + + ) {
if ( i > 0 ) {
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " ; " ) ;
}
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " %s " , sim - > bodies [ i ] . name ) ;
}
GuiDropdownBox ( { ( float ) x + 120 , ( float ) ( current_y - 35 ) , ( float ) ( width - 130 ) , 25 } ,
body_list , & ui_state - > maneuver_dialog . transfer_body_index , false ) ;
free ( body_list ) ;
}
}
current_y + = 35 ;
GuiLabel ( { ( float ) x , ( float ) current_y , 120 , 25 } , " Current Body: " ) ;
current_y + = 35 ;
if ( sim - > body_count > 0 ) {
int body_buffer_size = sim - > body_count * 64 + 1 ;
char * body_list = ( char * ) malloc ( body_buffer_size ) ;
if ( body_list ) {
body_list [ 0 ] = ' \0 ' ;
int offset = 0 ;
for ( int i = 0 ; i < sim - > body_count ; i + + ) {
if ( i > 0 ) {
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " ; " ) ;
}
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " %s " , sim - > bodies [ i ] . name ) ;
}
GuiDropdownBox ( { ( float ) x + 120 , ( float ) ( current_y - 35 ) , ( float ) ( width - 130 ) , 25 } ,
body_list , & ui_state - > maneuver_dialog . current_body_index , false ) ;
free ( body_list ) ;
}
}
current_y + = 35 ;
GuiLabel ( { ( float ) x , ( float ) current_y , 120 , 25 } , " Target Body: " ) ;
current_y + = 35 ;
if ( sim - > body_count > 0 ) {
int body_buffer_size = sim - > body_count * 64 + 1 ;
char * body_list = ( char * ) malloc ( body_buffer_size ) ;
if ( body_list ) {
body_list [ 0 ] = ' \0 ' ;
int offset = 0 ;
for ( int i = 0 ; i < sim - > body_count ; i + + ) {
if ( i > 0 ) {
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " ; " ) ;
}
offset + = snprintf ( body_list + offset , body_buffer_size - offset , " %s " , sim - > bodies [ i ] . name ) ;
}
GuiDropdownBox ( { ( float ) x + 120 , ( float ) ( current_y - 35 ) , ( float ) ( width - 130 ) , 25 } ,
body_list , & ui_state - > maneuver_dialog . target_body_index , false ) ;
free ( body_list ) ;
}
}
current_y + = 35 ;
Rectangle calculate_btn = { ( float ) x , ( float ) current_y , ( float ) width , 30 } ;
if ( GuiButton ( calculate_btn , " Calculate Transfer " ) ) {
calculate_hohmann_for_dialog ( sim , ui_state ) ;
}
current_y + = 40 ;
if ( ui_state - > maneuver_dialog . show_hohmann_preview ) {
HohmannTransfer * ht = & ui_state - > maneuver_dialog . last_calc ;
char transfer_info [ 256 ] ;
double current_r = ( ui_state - > maneuver_dialog . current_body_index > = 0 & &
ui_state - > maneuver_dialog . current_body_index < sim - > body_count ) ?
sim - > bodies [ ui_state - > maneuver_dialog . current_body_index ] . orbit . semi_major_axis : 0.0 ;
double target_r = ( ui_state - > maneuver_dialog . target_body_index > = 0 & &
ui_state - > maneuver_dialog . target_body_index < sim - > body_count ) ?
sim - > bodies [ ui_state - > maneuver_dialog . target_body_index ] . orbit . semi_major_axis : 0.0 ;
snprintf ( transfer_info , sizeof ( transfer_info ) ,
" Transfer semi-major axis: %.3e m " ,
( current_r + target_r ) / 2.0 ) ;
Rectangle info_bounds = { ( float ) x + 8 , ( float ) current_y , ( float ) width - 16 , 25 } ;
GuiLabel ( info_bounds , transfer_info ) ;
current_y + = 25 ;
char time_info [ 128 ] ;
double days = ht - > transfer_time / 86400.0 ;
snprintf ( time_info , sizeof ( time_info ) , " Transfer time: %.1f days " , days ) ;
Rectangle time_bounds = { ( float ) x + 8 , ( float ) current_y , ( float ) width - 16 , 25 } ;
GuiLabel ( time_bounds , time_info ) ;
current_y + = 30 ;
char burn1_info [ 256 ] ;
snprintf ( burn1_info , sizeof ( burn1_info ) ,
" Burn 1 (at periapsis): Δv1 = %.2f km/s, Prograde, ν = 0.00 rad " ,
ht - > dv1 / 1000.0 ) ;
Rectangle burn1_bounds = { ( float ) x + 8 , ( float ) current_y , ( float ) width - 16 , 25 } ;
GuiLabel ( burn1_bounds , burn1_info ) ;
current_y + = 25 ;
char burn2_info [ 256 ] ;
snprintf ( burn2_info , sizeof ( burn2_info ) ,
" Burn 2 (at apoapsis): Δv2 = %.2f km/s, Prograde, ν = %.2f rad " ,
ht - > dv2 / 1000.0 , ht - > true_anomaly_2 ) ;
Rectangle burn2_bounds = { ( float ) x + 8 , ( float ) current_y , ( float ) width - 16 , 25 } ;
GuiLabel ( burn2_bounds , burn2_info ) ;
current_y + = 30 ;
char total_info [ 128 ] ;
snprintf ( total_info , sizeof ( total_info ) , " Total Δv: %.2f km/s " , ( ht - > dv1 + ht - > dv2 ) / 1000.0 ) ;
Rectangle total_bounds = { ( float ) x + 8 , ( float ) current_y , ( float ) width - 16 , 25 } ;
GuiLabel ( total_bounds , total_info ) ;
current_y + = 30 ;
Rectangle both_btn = { ( float ) x , ( float ) current_y , ( float ) ( width / 2 - 5 ) , 30 } ;
Rectangle burn1_btn = { ( float ) x + ( float ) ( width / 2 + 5 ) , ( float ) current_y , ( float ) ( width / 2 - 5 ) , 30 } ;
if ( GuiButton ( both_btn , " Create Both Burns " ) ) {
create_hohmann_burns ( sim , ui_state , true ) ;
}
if ( GuiButton ( burn1_btn , " Create Burn 1 Only " ) ) {
create_hohmann_burns ( sim , ui_state , false ) ;
}
}
}
void calculate_hohmann_for_dialog ( SimulationState * sim , UIState * ui_state ) {
ui_state - > maneuver_dialog . show_hohmann_preview = false ;
if ( ui_state - > maneuver_dialog . transfer_body_index < 0 | |
ui_state - > maneuver_dialog . transfer_body_index > = sim - > body_count | |
ui_state - > maneuver_dialog . target_body_index < 0 | |
ui_state - > maneuver_dialog . target_body_index > = sim - > body_count ) {
return ;
}
if ( ui_state - > maneuver_dialog . transfer_body_index = = ui_state - > maneuver_dialog . target_body_index ) {
return ;
}
CelestialBody * central_body = & sim - > bodies [ ui_state - > maneuver_dialog . transfer_body_index ] ;
CelestialBody * current_body = & sim - > bodies [ ui_state - > maneuver_dialog . current_body_index ] ;
CelestialBody * target_body = & sim - > bodies [ ui_state - > maneuver_dialog . target_body_index ] ;
double r1 = current_body - > orbit . semi_major_axis ;
double r2 = target_body - > orbit . semi_major_axis ;
if ( r1 < = 0 | | r2 < = 0 ) {
return ;
}
ui_state - > maneuver_dialog . last_calc = calculate_hohmann_transfer ( r1 , r2 , central_body - > mass ) ;
ui_state - > maneuver_dialog . show_hohmann_preview = true ;
}
void render_maneuver_dialog ( SimulationState * sim , RenderState * render_state , UIState * ui_state ) {
if ( ! ui_state - > maneuver_dialog . open ) {
return ;
@ -686,6 +889,7 @@ void render_maneuver_dialog(SimulationState* sim, RenderState* render_state, UIS
if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_CREATE ) {
render_maneuver_create_tab ( sim , ui_state , x + 8 , y + 60 , width - 16 , height - 100 ) ;
} else if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_HOHMANN ) {
render_maneuver_hohmann_tab ( sim , ui_state , x + 8 , y + 60 , width - 16 , height - 100 ) ;
} else if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_EDIT ) {
}
}
@ -839,3 +1043,63 @@ void update_maneuver_from_dialog(SimulationState* sim, UIState* ui_state) {
ui_state - > maneuver_dialog . open = false ;
}
void create_hohmann_burns ( SimulationState * sim , UIState * ui_state , bool create_both_burns ) {
if ( ui_state - > maneuver_dialog . craft_index < 0 | |
ui_state - > maneuver_dialog . craft_index > = sim - > craft_count ) {
return ;
}
if ( ! ui_state - > maneuver_dialog . show_hohmann_preview ) {
return ;
}
HohmannTransfer * ht = & ui_state - > maneuver_dialog . last_calc ;
const char * current_name = ( ui_state - > maneuver_dialog . transfer_body_index > = 0 & &
ui_state - > maneuver_dialog . transfer_body_index < sim - > body_count ) ?
sim - > bodies [ ui_state - > maneuver_dialog . transfer_body_index ] . name : " Unknown " ;
const char * target_name = ( ui_state - > maneuver_dialog . target_body_index > = 0 & &
ui_state - > maneuver_dialog . target_body_index < sim - > body_count ) ?
sim - > bodies [ ui_state - > maneuver_dialog . target_body_index ] . name : " Unknown " ;
char burn1_name [ 128 ] ;
snprintf ( burn1_name , sizeof ( burn1_name ) , " Hohmann Burn 1 (%s→%s) " , current_name , target_name ) ;
Maneuver burn1 = create_maneuver (
burn1_name ,
ui_state - > maneuver_dialog . craft_index ,
BURN_PROGRADE ,
ht - > dv1 ,
TRIGGER_TRUE_ANOMALY ,
0.0
) ;
int burn1_index = add_maneuver_to_simulation ( sim , & burn1 ) ;
if ( burn1_index < 0 ) {
return ;
}
if ( create_both_burns ) {
char burn2_name [ 128 ] ;
snprintf ( burn2_name , sizeof ( burn2_name ) , " Hohmann Burn 2 (%s→%s) " , current_name , target_name ) ;
Maneuver burn2 = create_maneuver (
burn2_name ,
ui_state - > maneuver_dialog . craft_index ,
BURN_PROGRADE ,
ht - > dv2 ,
TRIGGER_TRUE_ANOMALY ,
ht - > true_anomaly_2
) ;
int burn2_index = add_maneuver_to_simulation ( sim , & burn2 ) ;
if ( burn2_index < 0 ) {
return ;
}
}
ui_state - > maneuver_dialog . open = false ;
}