@ -365,6 +365,20 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
( float ) panel_height - 29
} ;
GuiLabel ( text_bounds , " No maneuvers defined " ) ;
// Add button to open maneuver dialog
Rectangle dialog_btn = {
( float ) panel_x + 8 ,
( float ) panel_y + ( float ) panel_height - 28 ,
( float ) panel_width - 16 ,
20
} ;
if ( GuiButton ( dialog_btn , " [+] Manage Maneuvers " ) ) {
ui_state - > maneuver_dialog . open = true ;
ui_state - > maneuver_dialog . active_tab = MD_TAB_CREATE ;
ui_state - > maneuver_dialog . craft_index = ui_state - > selected_craft_index ;
ui_state - > maneuver_dialog . edit_maneuver_index = - 1 ;
}
return ;
}
@ -429,5 +443,365 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
GuiLabel ( details_bounds , details_text ) ;
}
// Add button to open maneuver dialog
Rectangle dialog_btn = {
( float ) panel_x + 8 ,
( float ) panel_y + ( float ) panel_height - 28 ,
( float ) panel_width - 16 ,
20
} ;
if ( GuiButton ( dialog_btn , " [+] Manage Maneuvers " ) ) {
ui_state - > maneuver_dialog . open = true ;
ui_state - > maneuver_dialog . active_tab = MD_TAB_CREATE ;
ui_state - > maneuver_dialog . craft_index = ui_state - > selected_craft_index ;
ui_state - > maneuver_dialog . edit_maneuver_index = - 1 ;
}
free ( maneuver_list_text ) ;
}
void render_maneuver_create_tab ( SimulationState * sim , UIState * ui_state , int x , int y , int width , int height ) {
int current_y = y ;
Rectangle craft_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( craft_label , " Spacecraft: " ) ;
current_y + = 35 ;
Rectangle name_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( name_label , " Name: " ) ;
Rectangle name_text = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 95 ) , 25 } ;
GuiTextBox ( name_text , ui_state - > maneuver_dialog . name , 64 , false ) ;
current_y + = 35 ;
Rectangle dir_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( dir_label , " Direction: " ) ;
const char * directions = " Prograde;Retrograde;Normal;Antinormal;Radial In;Radial Out;Custom " ;
Rectangle dir_combo = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 95 ) , 25 } ;
GuiComboBox ( dir_combo , directions , & ui_state - > maneuver_dialog . direction_active ) ;
current_y + = 35 ;
Rectangle dv_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( dv_label , " Delta-V: " ) ;
Rectangle dv_slider = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 180 ) , 20 } ;
float dv_float = ( float ) ui_state - > maneuver_dialog . delta_v ;
GuiSlider ( dv_slider , " 0 " , " 50000 " , & dv_float , 0 , 50000 ) ;
ui_state - > maneuver_dialog . delta_v = dv_float ;
char dv_text [ 32 ] ;
snprintf ( dv_text , sizeof ( dv_text ) , " %.1f m/s " , ui_state - > maneuver_dialog . delta_v ) ;
Rectangle dv_value = { ( float ) x + width - 90 , ( float ) current_y , 85 , 25 } ;
GuiLabel ( dv_value , dv_text ) ;
current_y + = 35 ;
Rectangle trigger_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( trigger_label , " Trigger: " ) ;
const char * triggers = " Time;True Anomaly " ;
Rectangle trigger_combo = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 95 ) , 25 } ;
GuiComboBox ( trigger_combo , triggers , & ui_state - > maneuver_dialog . trigger_type_active ) ;
current_y + = 35 ;
Rectangle value_label = { ( float ) x , ( float ) current_y , 80 , 25 } ;
GuiLabel ( value_label , " Value: " ) ;
Rectangle value_slider = { ( float ) x + 85 , ( float ) current_y , ( float ) ( width - 180 ) , 20 } ;
float value_float = ( float ) ui_state - > maneuver_dialog . trigger_value ;
if ( ui_state - > maneuver_dialog . trigger_type_active = = 0 ) {
GuiSlider ( value_slider , " 0 " , " 1000000 " , & value_float , 0 , 1000000 ) ;
} else {
GuiSlider ( value_slider , " 0 " , " 6.28 " , & value_float , 0 , 2 * M_PI ) ;
}
ui_state - > maneuver_dialog . trigger_value = value_float ;
char value_text [ 32 ] ;
if ( ui_state - > maneuver_dialog . trigger_type_active = = 0 ) {
snprintf ( value_text , sizeof ( value_text ) , " %.1f s " , ui_state - > maneuver_dialog . trigger_value ) ;
} else {
snprintf ( value_text , sizeof ( value_text ) , " %.2f rad " , ui_state - > maneuver_dialog . trigger_value ) ;
}
Rectangle value_text_label = { ( float ) x + width - 90 , ( float ) current_y , 85 , 25 } ;
GuiLabel ( value_text_label , value_text ) ;
update_orbital_preview ( sim , ui_state ) ;
if ( ui_state - > maneuver_dialog . show_preview ) {
Rectangle preview_bounds = { ( float ) x + 8 , ( float ) current_y + 10 , ( float ) width - 16 , 110 } ;
render_orbital_preview ( sim , ui_state , preview_bounds ) ;
}
}
void update_orbital_preview ( SimulationState * sim , UIState * ui_state ) {
if ( ui_state - > maneuver_dialog . craft_index < 0 | |
ui_state - > maneuver_dialog . craft_index > = sim - > craft_count ) {
ui_state - > maneuver_dialog . show_preview = false ;
return ;
}
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 . show_preview = false ;
return ;
}
CelestialBody * parent = & sim - > bodies [ craft - > parent_index ] ;
double parent_mass = parent - > mass ;
BurnDirection direction = ( BurnDirection ) ui_state - > maneuver_dialog . direction_active ;
double delta_v = ui_state - > maneuver_dialog . delta_v ;
ui_state - > maneuver_dialog . preview_elements = preview_burn_result ( craft , direction , delta_v , sim ) ;
ui_state - > maneuver_dialog . show_preview = true ;
ui_state - > maneuver_dialog . preview_valid = validate_burn_parameters ( craft , direction , delta_v , parent_mass ) ;
}
void render_orbital_preview ( SimulationState * sim , UIState * ui_state , Rectangle bounds ) {
if ( ! ui_state - > maneuver_dialog . show_preview ) {
return ;
}
if ( ui_state - > maneuver_dialog . craft_index < 0 | |
ui_state - > maneuver_dialog . craft_index > = sim - > craft_count ) {
return ;
}
Spacecraft * craft = & sim - > spacecraft [ ui_state - > maneuver_dialog . craft_index ] ;
OrbitalElements * current = & craft - > orbit ;
OrbitalElements * preview = & ui_state - > maneuver_dialog . preview_elements ;
double current_periapsis = current - > semi_major_axis * ( 1.0 - current - > eccentricity ) ;
double current_apoapsis = current - > semi_major_axis * ( 1.0 + current - > eccentricity ) ;
double preview_periapsis = preview - > semi_major_axis * ( 1.0 - preview - > eccentricity ) ;
double preview_apoapsis = preview - > semi_major_axis * ( 1.0 + preview - > eccentricity ) ;
double delta_periapsis = preview_periapsis - current_periapsis ;
double delta_apoapsis = preview_apoapsis - current_apoapsis ;
int y = ( int ) bounds . y ;
int x = ( int ) bounds . x ;
int width = ( int ) bounds . width ;
char current_text [ 128 ] ;
snprintf ( current_text , sizeof ( current_text ) ,
" Current: e=%.4f, a=%.3e m, ν=%.2f rad " ,
current - > eccentricity , current - > semi_major_axis , current - > true_anomaly ) ;
char preview_text [ 128 ] ;
snprintf ( preview_text , sizeof ( preview_text ) ,
" Preview: e=%.4f, a=%.3e m, ν=%.2f rad " ,
preview - > eccentricity , preview - > semi_major_axis , preview - > true_anomaly ) ;
char delta_text [ 128 ] ;
snprintf ( delta_text , sizeof ( delta_text ) ,
" Δperiapsis: %+.3e m Δapoapsis: %+.3e m " ,
delta_periapsis , delta_apoapsis ) ;
Rectangle current_bounds = { ( float ) x , ( float ) y , ( float ) width , 25 } ;
GuiLabel ( current_bounds , current_text ) ;
y + = 25 ;
Rectangle preview_bounds = { ( float ) x , ( float ) y , ( float ) width , 25 } ;
GuiLabel ( preview_bounds , preview_text ) ;
y + = 25 ;
Rectangle delta_bounds = { ( float ) x , ( float ) y , ( float ) width , 25 } ;
GuiLabel ( delta_bounds , delta_text ) ;
y + = 30 ;
Rectangle status_bounds = { ( float ) x , ( float ) y , ( float ) width , 25 } ;
const char * status_text = ui_state - > maneuver_dialog . preview_valid ? " ✓ All parameters valid " : " ✗ Invalid parameters " ;
GuiLabel ( status_bounds , status_text ) ;
}
void render_maneuver_dialog ( SimulationState * sim , RenderState * render_state , UIState * ui_state ) {
if ( ! ui_state - > maneuver_dialog . open ) {
return ;
}
int width = 550 ;
int height = 480 ;
int x = ( GetScreenWidth ( ) - width ) / 2 ;
int y = ( GetScreenHeight ( ) - height ) / 2 ;
Rectangle dialog_bounds = { ( float ) x , ( float ) y , ( float ) width , ( float ) height } ;
if ( GuiWindowBox ( dialog_bounds , " Maneuver Management " ) ) {
ui_state - > maneuver_dialog . open = false ;
return ;
}
const char * tabs [ ] = { " Create Maneuver " , " Hohmann Transfer " , " Edit Maneuver " } ;
int tab_count = 3 ;
Rectangle tab_bounds = { x + 8 , y + 30 , width - 16 , 25 } ;
GuiTabBar ( tab_bounds , tabs , tab_count , ( int * ) & ui_state - > maneuver_dialog . active_tab ) ;
Rectangle cancel_btn = { x + width - 280 , y + height - 50 , 100 , 35 } ;
Rectangle action_btn = { x + width - 170 , y + height - 50 , 160 , 35 } ;
if ( GuiButton ( cancel_btn , " Cancel " ) ) {
ui_state - > maneuver_dialog . open = false ;
}
const char * action_text = " Create Maneuver " ;
if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_HOHMANN ) {
action_text = " Calculate Transfer " ;
} else if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_EDIT ) {
action_text = " Save Changes " ;
}
if ( GuiButton ( action_btn , action_text ) ) {
switch ( ui_state - > maneuver_dialog . active_tab ) {
case MD_TAB_CREATE :
create_maneuver_from_dialog ( sim , ui_state ) ;
break ;
case MD_TAB_HOHMANN :
break ;
case MD_TAB_EDIT :
update_maneuver_from_dialog ( sim , ui_state ) ;
break ;
}
}
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 ) {
} else if ( ui_state - > maneuver_dialog . active_tab = = MD_TAB_EDIT ) {
}
}
bool validate_maneuver_inputs ( SimulationState * sim , UIState * ui_state ) {
ui_state - > maneuver_dialog . show_error = false ;
ui_state - > maneuver_dialog . error_message [ 0 ] = ' \0 ' ;
if ( ui_state - > maneuver_dialog . craft_index < 0 | |
ui_state - > maneuver_dialog . craft_index > = sim - > craft_count ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" No spacecraft selected " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
if ( strlen ( ui_state - > maneuver_dialog . name ) = = 0 ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Maneuver name is required " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
for ( int i = 0 ; i < sim - > maneuver_count ; i + + ) {
if ( strcmp ( sim - > maneuvers [ i ] . name , ui_state - > maneuver_dialog . name ) = = 0 ) {
if ( ui_state - > maneuver_dialog . edit_maneuver_index > = 0 & &
ui_state - > maneuver_dialog . edit_maneuver_index = = i ) {
continue ;
}
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Maneuver name already exists " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
}
if ( ui_state - > maneuver_dialog . delta_v < 0 ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Delta-V cannot be negative " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
if ( ui_state - > maneuver_dialog . delta_v > 50000 ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Delta-V too large (max: 50000 m/s) " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
if ( ui_state - > maneuver_dialog . trigger_type_active = = 1 ) {
if ( ui_state - > maneuver_dialog . trigger_value < 0 | |
ui_state - > maneuver_dialog . trigger_value > 2 * M_PI ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" True anomaly must be 0 to 6.28 rad " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
} else {
if ( ui_state - > maneuver_dialog . trigger_value < 0 ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Time cannot be negative " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
}
Spacecraft * craft = & sim - > spacecraft [ ui_state - > maneuver_dialog . craft_index ] ;
if ( craft - > parent_index < 0 | | craft - > parent_index > = sim - > body_count ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Spacecraft has no valid parent body " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
if ( ui_state - > maneuver_dialog . edit_maneuver_index < 0 ) {
if ( sim - > maneuver_count > = sim - > max_maneuvers ) {
snprintf ( ui_state - > maneuver_dialog . error_message ,
sizeof ( ui_state - > maneuver_dialog . error_message ) ,
" Maximum maneuvers reached " ) ;
ui_state - > maneuver_dialog . show_error = true ;
return false ;
}
}
return true ;
}
void create_maneuver_from_dialog ( SimulationState * sim , UIState * ui_state ) {
if ( ! validate_maneuver_inputs ( sim , ui_state ) ) {
return ;
}
Maneuver maneuver = create_maneuver (
ui_state - > maneuver_dialog . name ,
ui_state - > maneuver_dialog . craft_index ,
( BurnDirection ) ui_state - > maneuver_dialog . direction_active ,
ui_state - > maneuver_dialog . delta_v ,
( TriggerType ) ui_state - > maneuver_dialog . trigger_type_active ,
ui_state - > maneuver_dialog . trigger_value
) ;
int maneuver_index = add_maneuver_to_simulation ( sim , & maneuver ) ;
if ( maneuver_index > = 0 ) {
ui_state - > maneuver_dialog . open = false ;
}
}
void update_maneuver_from_dialog ( SimulationState * sim , UIState * ui_state ) {
int edit_index = ui_state - > maneuver_dialog . edit_maneuver_index ;
if ( edit_index < 0 | | edit_index > = sim - > maneuver_count ) {
return ;
}
if ( ! validate_maneuver_inputs ( sim , ui_state ) ) {
return ;
}
Maneuver * maneuver = & sim - > maneuvers [ edit_index ] ;
strncpy ( maneuver - > name , ui_state - > maneuver_dialog . name , 64 ) ;
maneuver - > craft_index = ui_state - > maneuver_dialog . craft_index ;
maneuver - > direction = ( BurnDirection ) ui_state - > maneuver_dialog . direction_active ;
maneuver - > delta_v = ui_state - > maneuver_dialog . delta_v ;
maneuver - > trigger_type = ( TriggerType ) ui_state - > maneuver_dialog . trigger_type_active ;
maneuver - > trigger_value = ui_state - > maneuver_dialog . trigger_value ;
ui_state - > maneuver_dialog . open = false ;
}