private static string SelectorText(FrameType type, CelestialBody selected) { string abbreviation = Abbreviation(type, selected); if (abbreviation != null) { return(abbreviation); } switch (type) { case FrameType.BODY_CENTRED_NON_ROTATING: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_SelectorText_BodyCentredNonRotating")); case FrameType.BARYCENTRIC_ROTATING: return("DEPRECATED"); case FrameType.BODY_CENTRED_PARENT_DIRECTION: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_SelectorText_BodyCentredParentDirection")); case FrameType.BODY_SURFACE: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_SelectorText_BodySurface")); default: throw Log.Fatal("Unexpected type " + type.ToString()); } }
private static string TargetFrameDescription(Vessel target) { return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Description_Target", target.vesselName, target.orbit.referenceBody.Name())); }
private static string SelectorTooltip(FrameType type, CelestialBody selected) { string name = Name(type, selected); switch (type) { case FrameType.BODY_CENTRED_NON_ROTATING: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Tooltip_BodyCentredNonRotating", name, selected.Name())); case FrameType.BARYCENTRIC_ROTATING: return("DEPRECATED"); case FrameType.BODY_CENTRED_PARENT_DIRECTION: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Tooltip_BodyCentredParentDirection", name, selected.Name())); case FrameType.BODY_SURFACE: return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Tooltip_BodySurface", name, selected.Name())); default: throw Log.Fatal("Unexpected type " + type.ToString()); } }
protected override void RenderWindowContents(int window_id) { using (new UnityEngine.GUILayout.VerticalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Description_Heading", Name(), Abbreviation())); UnityEngine.GUILayout.Label( target_frame_selected ? TargetFrameDescription(target) : Description(frame_type, selected_celestial), Style.Multiline(UnityEngine.GUI.skin.label), GUILayoutHeight(3)); using (new UnityEngine.GUILayout.HorizontalScope()) { // Left-hand side: tree view of celestials. using (new UnityEngine.GUILayout.VerticalScope(GUILayoutWidth(8))) { RenderSubtree(celestial: Planetarium.fetch.Sun, depth: 0); } // Right-hand side: toggles for reference frame type selection. using (new UnityEngine.GUILayout.VerticalScope()) { RenderSubtreeToggleGrid(Planetarium.fetch.Sun); } } } UnityEngine.GUI.DragWindow(); }
private void RenderOrbitGroundTrack(OrbitGroundTrack?ground_track, CelestialBody primary) { using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat( "#Principia_OrbitAnalyser_GroundTrack_LongitudesOfEquatorialCrossings_Prefix"), UnityEngine.GUILayout.ExpandWidth(false)); string text = UnityEngine.GUILayout.TextField( $"{ground_track_revolution_}", GUILayoutWidth(2)); UnityEngine.GUILayout.Label( L10N.CacheFormat( "#Principia_OrbitAnalyser_GroundTrack_LongitudesOfEquatorialCrossings_Suffix")); if (int.TryParse(text, out int revolution)) { ground_track_revolution_ = revolution; } } var equatorial_crossings = ground_track?.equatorial_crossings; LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_GroundTrack_AscendingPass"), equatorial_crossings?.longitudes_reduced_to_ascending_pass. FormatEquatorialAngleInterval(primary)); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_GroundTrack_DescendingPass"), equatorial_crossings?.longitudes_reduced_to_descending_pass. FormatEquatorialAngleInterval(primary)); }
private void RenderLowestAltitude(OrbitalElements?elements, CelestialBody primary) { double?lowest_distance = elements?.radial_distance.min; LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_LowestAltitude"), (lowest_distance - primary?.Radius)?.FormatAltitude()); double?lowest_primary_distance = primary?.ocean == true ? primary.Radius : primary?.pqsController?.radiusMin; string altitude_warning = lowest_distance < lowest_primary_distance ? L10N.CacheFormat("#Principia_OrbitAnalyser_Warning_Collision") : lowest_distance < primary?.pqsController?.radiusMax ? L10N.CacheFormat( "#Principia_OrbitAnalyser_Warning_CollisionRisk") : lowest_distance < primary?.Radius + primary?.atmosphereDepth ? L10N.CacheFormat( "#Principia_OrbitAnalyser_Warning_Reentry") : ""; UnityEngine.GUILayout.Label(altitude_warning, Style.Warning(UnityEngine.GUI.skin.label)); }
private static string TargetFrameSelectorTooltip(Vessel target) { string name = TargetFrameName(target); return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_Tooltip_Target", name)); }
protected override string ButtonText(string orbit_description) { return(orbit_description == null ? L10N.CacheFormat( "#Principia_PlannedOrbitAnalyser_ToggleButton") : L10N.CacheFormat( "#Principia_PlannedOrbitAnalyser_ToggleButtonWithDescription", orbit_description)); }
public void RenderButton() { if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_ReferenceFrameSelector_ToggleButton", name_, Name()))) { Toggle(); } }
private static string FormatOrNull(string template, params object[] args) { // Optional translations include the language name so that they do not fall // back to English. string qualified_template = $"{template}.{UILanguage()}"; if (!Localizer.Tags.ContainsKey(qualified_template)) { return(null); } return(L10N.CacheFormat(qualified_template, args)); }
public BurnEditor(PrincipiaPluginAdapter adapter, Vessel vessel, double initial_time, int index, Func <int, BurnEditor> get_burn_at_index) { adapter_ = adapter; vessel_ = vessel; initial_time_ = initial_time; this.index = index; get_burn_at_index_ = get_burn_at_index; Δv_tangent_ = new DifferentialSlider( label: L10N.CacheFormat("#Principia_BurnEditor_ΔvTangent"), unit: L10N.CacheFormat("#Principia_BurnEditor_SpeedUnit"), log10_lower_rate: log10_Δv_lower_rate, log10_upper_rate: log10_Δv_upper_rate, text_colour: Style.Tangent); Δv_normal_ = new DifferentialSlider( label: L10N.CacheFormat("#Principia_BurnEditor_ΔvNormal"), unit: L10N.CacheFormat("#Principia_BurnEditor_SpeedUnit"), log10_lower_rate: log10_Δv_lower_rate, log10_upper_rate: log10_Δv_upper_rate, text_colour: Style.Normal); Δv_binormal_ = new DifferentialSlider( label: L10N.CacheFormat("#Principia_BurnEditor_ΔvBinormal"), unit: L10N.CacheFormat("#Principia_BurnEditor_SpeedUnit"), log10_lower_rate: log10_Δv_lower_rate, log10_upper_rate: log10_Δv_upper_rate, text_colour: Style.Binormal); previous_coast_duration_ = new DifferentialSlider( label: L10N.CacheFormat("#Principia_BurnEditor_InitialTime"), unit: null, log10_lower_rate: log10_time_lower_rate, log10_upper_rate: log10_time_upper_rate, // We cannot have a coast of length 0, so let's make it very // short: that will be indistinguishable. zero_value: 0.001, min_value: 0, formatter: FormatPreviousCoastDuration, parser: TryParsePreviousCoastDuration, field_width: 7) { value = initial_time_ - time_base }; reference_frame_selector_ = new ReferenceFrameSelector( adapter_, ReferenceFrameChanged, L10N.CacheFormat("#Principia_BurnEditor_ManœuvringFrame")); reference_frame_selector_.SetFrameParameters( adapter_.plotting_frame_selector_.FrameParameters()); ComputeEngineCharacteristics(); }
private void RenderOrbitalElements(OrbitalElements?elements, CelestialBody primary) { LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_SiderealPeriod"), elements?.sidereal_period.FormatDuration()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_NodalPeriod"), elements?.nodal_period.FormatDuration()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_AnomalisticPeriod"), elements?.anomalistic_period.FormatDuration()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_SemimajorAxis"), elements?.mean_semimajor_axis.FormatLengthInterval()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_Eccentricity"), elements?.mean_eccentricity.FormatInterval()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_Inclination"), elements?.mean_inclination.FormatAngleInterval()); LabeledField( L10N.CacheFormat( "#Principia_OrbitAnalyser_Elements_LongitudeOfAscendingNode"), elements?.mean_longitude_of_ascending_nodes.FormatAngleInterval()); LabeledField( L10N.CacheFormat("#Principia_OrbitAnalyser_Elements_NodalPrecession"), elements?.nodal_precession.FormatAngularFrequency()); string periapsis = L10N.CelestialString( "#Principia_OrbitAnalyser_Elements_Periapsis", new[] { primary }); string apoapsis = L10N.CelestialString( "#Principia_OrbitAnalyser_Elements_Apoapsis", new[] { primary }); LabeledField( L10N.CacheFormat( "#Principia_OrbitAnalyser_Elements_ArgumentOfPeriapsis", periapsis), elements?.mean_argument_of_periapsis.FormatAngleInterval()); LabeledField( L10N.CacheFormat( "#Principia_OrbitAnalyser_Elements_MeanPeriapsisAltitude", periapsis), elements?.mean_periapsis_distance.FormatLengthInterval(primary.Radius)); LabeledField( L10N.CacheFormat( "#Principia_OrbitAnalyser_Elements_MeanApoapsisAltitude", apoapsis), elements?.mean_apoapsis_distance.FormatLengthInterval(primary.Radius)); }
// Similar to |FormatAngleInterval|, but annotated with the equivalent // of the half-width as a distance at the equator in parentheses. public static string FormatEquatorialAngleInterval(this Interval interval, CelestialBody primary) { double half_width_angle = (interval.max - interval.min) / 2; if (half_width_angle > Math.PI) { return(L10N.CacheFormat("#Principia_OrbitAnalyser_Precesses")); } double half_width_distance = half_width_angle * primary.Radius; string formatted_distance = half_width_distance > 1000 ? $"{(half_width_distance / 1000).FormatN(1)} km" : $"{(half_width_distance).FormatN(0)} m"; return($"{interval.FormatAngleInterval()} ({formatted_distance})"); }
// Displays an interval of angles (given in radians) as midpoint°±half-width°, // or the string "(precesses)" if the half-width exceeds 180°. public static string FormatAngleInterval(this Interval interval) { double half_width = (interval.max - interval.min) / 2; double midpoint = interval.min + half_width; if (half_width > Math.PI) { return(L10N.CacheFormat("#Principia_OrbitAnalyser_Precesses")); } const double degree = Math.PI / 180; int fractional_digits = Math.Max(0, 1 - (int)Math.Floor(Math.Log10(half_width / degree))); string formatted_midpoint = (midpoint / degree).FormatN(fractional_digits); string formatted_half_width = (half_width / degree).FormatN(fractional_digits); return($"{formatted_midpoint}°±{formatted_half_width}°"); }
private bool RenderSelector <T>(T[] array, ref int index, string label, string format, bool enabled) { bool changed = false; using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label(text: label, options: GUILayoutWidth(6)); if (UnityEngine.GUILayout.Button( text: index == 0 ? L10N.CacheFormat( "#Principia_DiscreteSelector_Min") : "-", options: GUILayoutWidth(2)) && enabled && index != 0) { --index; changed = true; } UnityEngine.GUILayout.TextArea( text: enabled ? string.Format(Culture.culture, format, array[index]) : "", style: Style.RightAligned(UnityEngine.GUI.skin.textArea), options: GUILayoutWidth(3)); if (UnityEngine.GUILayout.Button( text: index == array.Length - 1 ? L10N.CacheFormat( "#Principia_DiscreteSelector_Max") : "+", options: GUILayoutWidth(2)) && enabled && index != array.Length - 1) { ++index; changed = true; } } return(changed); }
private void ComputeRCSCharacteristics() { ModuleRCS[] active_rcs = (from part in vessel_.parts select(from PartModule module in part.Modules where module is ModuleRCS module_rcs && module_rcs.rcsEnabled select module as ModuleRCS)). SelectMany(x => x).ToArray(); Vector3d reference_direction = vessel_.ReferenceTransform.up; // NOTE(egg): NathanKell informs me that in >= 1.0.5, RCS has a useZaxis // property, that controls whether they thrust -up or -forward. The madness // keeps piling up. double[] thrusts = (from engine in active_rcs select engine.thrusterPower * (from transform in engine.thrusterTransforms where transform.gameObject.activeInHierarchy select Math.Max(0, Vector3d.Dot( reference_direction, -transform.up))).Sum()). ToArray(); thrust_in_kilonewtons_ = thrusts.Sum(); // This would use zip if we had 4.0 or later. We loop for now. double Σ_f_over_i_sp = 0; for (int i = 0; i < active_rcs.Length; ++i) { Σ_f_over_i_sp += thrusts[i] / active_rcs[i].atmosphereCurve.Evaluate(0); } specific_impulse_in_seconds_g0_ = thrust_in_kilonewtons_ / Σ_f_over_i_sp; // If RCS provides no thrust, model a virtually instant burn. if (thrust_in_kilonewtons_ == 0) { engine_warning_ += L10N.CacheFormat("#Principia_BurnEditor_Warning_NoActiveRCS"); UseTheForceLuke(); } }
public FlightPlanner(PrincipiaPluginAdapter adapter, PredictedVessel predicted_vessel) : base( adapter, predicted_vessel) { adapter_ = adapter; predicted_vessel_ = predicted_vessel; final_time_ = new DifferentialSlider( label: L10N.CacheFormat("#Principia_FlightPlan_PlanLength"), unit: null, log10_lower_rate: log10_time_lower_rate, log10_upper_rate: log10_time_upper_rate, min_value: 10, max_value: double.PositiveInfinity, formatter: FormatPlanLength, parser: TryParsePlanLength, field_width: 7); final_trajectory_analyser_ = new PlannedOrbitAnalyser(adapter, predicted_vessel); }
private void RenderKSPFeatures() { display_patched_conics = UnityEngine.GUILayout.Toggle( value: display_patched_conics, text: L10N.CacheFormat( "#Principia_MainWindow_KspFeature_DisplayPatchedConics")); if (MapView.MapIsEnabled && FlightGlobals.ActiveVessel?.orbitTargeter != null) { using (new UnityEngine.GUILayout.HorizontalScope()) { selecting_target_celestial_ = UnityEngine.GUILayout.Toggle( selecting_target_celestial_, L10N.CacheFormat("#Principia_MainWindow_TargetCelestial_Select")); if (selecting_target_celestial_) { selecting_active_vessel_target = false; } CelestialBody target_celestial = FlightGlobals.fetch.VesselTarget as CelestialBody; if (target_celestial != null) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_MainWindow_TargetCelestial_Name", target_celestial.Name()), UnityEngine.GUILayout.ExpandWidth(true)); if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_MainWindow_TargetCelestial_Clear"), GUILayoutWidth(2))) { selecting_target_celestial_ = false; FlightGlobals.fetch.SetVesselTarget(null); } } } } else { selecting_target_celestial_ = false; } }
protected override void RenderWindow(int window_id) { // We must ensure that the GUI elements don't change between Layout and // Repaint. This means that any state change must occur before Layout or // after Repaint. This if statement implements the former. It updates the // vessel and the editors to reflect the current state of the plugin and // then proceeds with the UI code. if (UnityEngine.Event.current.type == UnityEngine.EventType.Layout) { UpdateVesselAndBurnEditors(); } // The UI code proper, executed identically for Layout and Repaint. We // can freely change the state in events like clicks (e.g., in if statements // for buttons) as these don't happen between Layout and Repaint. string vessel_guid = predicted_vessel?.id.ToString(); if (vessel_guid == null) { return; } if (plugin.FlightPlanExists(vessel_guid)) { RenderFlightPlan(vessel_guid); } else if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_Create"))) { plugin.FlightPlanCreate(vessel_guid, plugin.CurrentTime() + 3600, predicted_vessel.GetTotalMass()); final_time_.value_if_different = plugin.FlightPlanGetDesiredFinalTime(vessel_guid); Shrink(); } UnityEngine.GUI.DragWindow(); }
private void ComputeEngineCharacteristics() { ModuleEngines[] active_engines = (from part in vessel_.parts select(from PartModule module in part.Modules where (module as ModuleEngines)?.EngineIgnited == true select module as ModuleEngines)).SelectMany(x => x).ToArray(); Vector3d reference_direction = vessel_.ReferenceTransform.up; double[] thrusts = (from engine in active_engines select engine.MaxThrustOutputVac(useThrustLimiter: true) * (from transform in engine.thrustTransforms select Math.Max(0, Vector3d.Dot(reference_direction, -transform.forward))).Average()). ToArray(); thrust_in_kilonewtons_ = thrusts.Sum(); // This would use zip if we had 4.0 or later. We loop for now. double Σ_f_over_i_sp = 0; for (int i = 0; i < active_engines.Length; ++i) { Σ_f_over_i_sp += thrusts[i] / active_engines[i].atmosphereCurve.Evaluate(0); } specific_impulse_in_seconds_g0_ = thrust_in_kilonewtons_ / Σ_f_over_i_sp; // If there are no engines, fall back onto RCS. if (thrust_in_kilonewtons_ == 0) { engine_warning_ += L10N.CacheFormat("#Principia_BurnEditor_Warning_NoActiveEngines"); ComputeRCSCharacteristics(); } }
public string ReferencePlaneDescription() { if (!target_frame_selected && (frame_type == FrameType.BODY_CENTRED_NON_ROTATING || frame_type == FrameType.BODY_SURFACE)) { return(L10N.CacheFormat( "#Principia_ReferenceFrameSelector_ReferencePlane_Centred", selected_celestial.Name())); } string secondary = target_frame_selected ? L10N.CacheFormat( "#Principia_ReferenceFrameSelector_ReferencePlane_Secondary_Target") : selected_celestial.Name(); string primary = target_frame_selected ? selected_celestial.Name() : selected_celestial.referenceBody.Name(); return(L10N.CacheFormat("#Principia_ReferenceFrameSelector_ReferencePlane", secondary, primary)); }
private static string TargetFrameSelectorText(Vessel target) { return(TargetFrameAbbreviation(target) ?? L10N.CacheFormat( "#Principia_ReferenceFrameSelector_SelectorText_Target")); }
private string GetStatusMessage() { string vessel_guid = predicted_vessel?.id.ToString(); string message = ""; if (vessel_guid != null && !status_.ok()) { int anomalous_manœuvres = plugin.FlightPlanNumberOfAnomalousManoeuvres(vessel_guid); int manœuvres = plugin.FlightPlanNumberOfManoeuvres(vessel_guid); double actual_final_time = plugin.FlightPlanGetActualFinalTime(vessel_guid); bool timed_out = actual_final_time < final_time_.value; string remedy_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_ChangeFlightPlan"); // Preceded by "Try". string status_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_FailedError", status_.error, status_.message); string time_out_message = timed_out ? L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_TimeOut", FormatPositiveTimeSpan( actual_final_time - plugin.FlightPlanGetInitialTime(vessel_guid))) : ""; if (status_.is_aborted()) { status_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_MaxSteps", time_out_message); remedy_message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_MaxSegment"); } else if (status_.is_failed_precondition()) { status_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_Singularity", time_out_message); remedy_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_AvoidingCollision"); } else if (status_.is_invalid_argument()) { status_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_Infinite", first_error_manœuvre_.Value + 1); remedy_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_Adjust", first_error_manœuvre_.Value + 1); } else if (status_.is_out_of_range()) { if (first_error_manœuvre_.HasValue) { status_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange1", first_error_manœuvre_.Value + 1, first_error_manœuvre_.Value == 0 ? L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange2") : L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange3", first_error_manœuvre_.Value), manœuvres == 0 || first_error_manœuvre_.Value == manœuvres - 1 ? L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange4") : L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange5", first_error_manœuvre_.Value + 2)); remedy_message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange6", manœuvres == 0 || first_error_manœuvre_.Value == manœuvres - 1 ? L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_OutRange7") : "", first_error_manœuvre_.Value + 1); } else { status_message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_TooShort"); remedy_message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_Increase"); } } else if (status_.is_unavailable()) { status_message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_CantRebase"); remedy_message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_WaitFinish"); } if (anomalous_manœuvres > 0) { message = L10N.CacheFormat( "#Principia_FlightPlan_StatusMessage_Last", anomalous_manœuvres, status_message, remedy_message, (anomalous_manœuvres < manœuvres ? L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_Last2", manœuvres - anomalous_manœuvres) : "")); } else { message = L10N.CacheFormat("#Principia_FlightPlan_StatusMessage_Result", status_message, remedy_message); } } message_was_displayed_ = true; return(message); }
protected override void RenderWindowContents(int window_id) { // We must ensure that the GUI elements don't change between Layout and // Repaint. This means that any state change must occur before Layout or // after Repaint. This if statement implements the former. It updates the // vessel and the editors to reflect the current state of the plugin and // then proceeds with the UI code. if (UnityEngine.Event.current.type == UnityEngine.EventType.Layout) { UpdateVesselAndBurnEditors(); } // The UI code proper, executed identically for Layout and Repaint. We // can freely change the state in events like clicks (e.g., in if statements // for buttons) as these don't happen between Layout and Repaint. string vessel_guid = predicted_vessel?.id.ToString(); if (vessel_guid == null) { return; } using (new UnityEngine.GUILayout.HorizontalScope()) { int flight_plans = plugin.FlightPlanCount(vessel_guid); int selected_flight_plan = plugin.FlightPlanSelected(vessel_guid); for (int i = 0; i < flight_plans; ++i) { var id = new string(L10N.CacheFormat("#Principia_AlphabeticList")[i], 1); if (UnityEngine.GUILayout.Toggle(i == selected_flight_plan, id, "Button", GUILayoutWidth(1)) && i != selected_flight_plan) { plugin.FlightPlanSelect(vessel_guid, i); final_time_.value_if_different = plugin.FlightPlanGetDesiredFinalTime(vessel_guid); ClearBurnEditors(); UpdateVesselAndBurnEditors(); } } bool must_create_flight_plan = false; if (flight_plans == 0) { must_create_flight_plan = UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_Create")); } else if (flight_plans < max_flight_plans) { must_create_flight_plan = UnityEngine.GUILayout.Button("+", GUILayoutWidth(1)); } if (must_create_flight_plan) { plugin.FlightPlanCreate(vessel_guid, plugin.CurrentTime() + 3600, predicted_vessel.GetTotalMass()); final_time_.value_if_different = plugin.FlightPlanGetDesiredFinalTime(vessel_guid); ClearBurnEditors(); UpdateVesselAndBurnEditors(); return; } } if (plugin.FlightPlanExists(vessel_guid)) { RenderFlightPlan(vessel_guid); } UnityEngine.GUI.DragWindow(); }
private bool RenderCoast(int index, out double?orbital_period) { string vessel_guid = predicted_vessel.id.ToString(); var coast_analysis = plugin.FlightPlanGetCoastAnalysis( vessel_guid, revolutions_per_cycle: null, days_per_cycle: null, ground_track_revolution: 0, index); string orbit_description = null; orbital_period = coast_analysis.elements?.nodal_period; if (coast_analysis.primary_index.HasValue) { var primary = FlightGlobals.Bodies[coast_analysis.primary_index.Value]; int?nodal_revolutions = (int?)(coast_analysis.mission_duration / coast_analysis.elements?.nodal_period); orbit_description = OrbitAnalyser.OrbitDescription( primary, coast_analysis.elements, coast_analysis.recurrence, coast_analysis.ground_track, nodal_revolutions); } using (new UnityEngine.GUILayout.HorizontalScope()) { if (index == burn_editors_.Count) { final_trajectory_analyser_.index = index; final_trajectory_analyser_.RenderButton(); } else { double start_of_coast = index == 0 ? plugin.FlightPlanGetInitialTime( vessel_guid) : burn_editors_[index - 1].final_time; string coast_duration = (burn_editors_[index].initial_time - start_of_coast).FormatDuration( show_seconds: false); string coast_description = orbit_description == null ? L10N.CacheFormat( "#Principia_FlightPlan_Coast", coast_duration) : L10N.CacheFormat( "#Principia_FlightPlan_CoastInOrbit", orbit_description, coast_duration); UnityEngine.GUILayout.Label(coast_description); } if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_AddManœuvre"), GUILayoutWidth(4))) { double initial_time; if (index == 0) { initial_time = plugin.CurrentTime() + 60; } else { initial_time = plugin.FlightPlanGetManoeuvre(vessel_guid, index - 1).final_time + 60; } var editor = new BurnEditor(adapter_, predicted_vessel, initial_time, index, get_burn_at_index: burn_editors_. ElementAtOrDefault); editor.minimized = false; Burn candidate_burn = editor.Burn(); var status = plugin.FlightPlanInsert( vessel_guid, candidate_burn, index); // The previous call did not necessarily create a manœuvre. Check if // we need to add an editor. int number_of_manœuvres = plugin.FlightPlanNumberOfManoeuvres(vessel_guid); if (number_of_manœuvres > burn_editors_.Count) { editor.Reset(plugin.FlightPlanGetManoeuvre(vessel_guid, index)); burn_editors_.Insert(index, editor); UpdateBurnEditorIndices(); UpdateStatus(status, index); Shrink(); return(true); } // TODO(phl): The error messaging here will be either confusing or // wrong. The messages should mention the new manœuvre without // numbering it, since the numbering has not changed (“the new manœuvre // would overlap with manœuvre #1 or manœuvre #2” or something along // these lines). UpdateStatus(status, index); } } return(false); }
private void RenderUpcomingEvents() { string vessel_guid = predicted_vessel.id.ToString(); double current_time = plugin.CurrentTime(); Style.HorizontalLine(); if (first_future_manœuvre_.HasValue) { int first_future_manœuvre = first_future_manœuvre_.Value; NavigationManoeuvre manœuvre = plugin.FlightPlanGetManoeuvre(vessel_guid, first_future_manœuvre); if (manœuvre.burn.initial_time > current_time) { using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_FlightPlan_UpcomingManœuvre", first_future_manœuvre + 1)); UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_FlightPlan_IgnitionCountdown", FormatTimeSpan( current_time - manœuvre.burn.initial_time)), style: Style.RightAligned(UnityEngine.GUI.skin.label)); } } else { using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_FlightPlan_OngoingManœuvre", first_future_manœuvre + 1)); UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_FlightPlan_CutoffCountdown", FormatTimeSpan( current_time - manœuvre.final_time)), style: Style.RightAligned(UnityEngine.GUI.skin.label)); } } // In career mode, the patched conic solver may be null. In that case // we do not offer the option of showing the manœuvre on the navball, // even though the flight planner is still available to plan it. // TODO(egg): We may want to consider setting the burn vector directly // rather than going through the solver. if (predicted_vessel.patchedConicSolver != null) { using (new UnityEngine.GUILayout.HorizontalScope()) { show_guidance_ = UnityEngine.GUILayout.Toggle( show_guidance_, L10N.CacheFormat("#Principia_FlightPlan_ShowManœuvreOnNavball")); if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_WarpToManœuvre"))) { TimeWarp.fetch.WarpTo(manœuvre.burn.initial_time - 60); } } } } else { // Reserve some space to avoid the UI changing shape if we have // nothing to say. UnityEngine.GUILayout.Label( L10N.CacheFormat( "#Principia_FlightPlan_Warning_AllManœuvresInThePast"), Style.Warning(UnityEngine.GUI.skin.label)); UnityEngine.GUILayout.Space(Width(1)); } }
public void RenderButton() { RenderButton(L10N.CacheFormat("#Principia_FlightPlan_ToggleButton"), GUILayoutWidth(4)); }
private void RenderFlightPlan(string vessel_guid) { using (new UnityEngine.GUILayout.VerticalScope()) { if (final_time_.Render(enabled: true)) { var status = plugin.FlightPlanSetDesiredFinalTime( vessel_guid, final_time_.value); UpdateStatus(status, null); } // Always refresh the final time from C++ as it may have changed because // the last burn changed. final_time_.value_if_different = plugin.FlightPlanGetDesiredFinalTime(vessel_guid); FlightPlanAdaptiveStepParameters parameters = plugin.FlightPlanGetAdaptiveStepParameters(vessel_guid); length_integration_tolerance_index_ = Math.Max( 0, Array.FindIndex(integration_tolerances_, (double tolerance) => tolerance >= parameters. length_integration_tolerance)); speed_integration_tolerance_index_ = Math.Max( 0, Array.FindIndex(integration_tolerances_, (double tolerance) => tolerance >= parameters. speed_integration_tolerance)); max_steps_index_ = Math.Max(0, Array.FindIndex( max_steps_, (long step) => step >= parameters.max_steps)); using (new UnityEngine.GUILayout.HorizontalScope()) { using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_FlightPlan_MaxSteps"), GUILayoutWidth(6)); if (max_steps_index_ == 0) { UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_DiscreteSelector_Min")); } else if (UnityEngine.GUILayout.Button("−")) { --max_steps_index_; UpdateAdaptiveStepParameters(ref parameters); var status = plugin.FlightPlanSetAdaptiveStepParameters( vessel_guid, parameters); UpdateStatus(status, null); } UnityEngine.GUILayout.TextArea( max_steps_[max_steps_index_].ToString(), GUILayoutWidth(3)); if (max_steps_index_ == max_steps_.Length - 1) { UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_DiscreteSelector_Max")); } else if (UnityEngine.GUILayout.Button("+")) { ++max_steps_index_; UpdateAdaptiveStepParameters(ref parameters); var status = plugin.FlightPlanSetAdaptiveStepParameters( vessel_guid, parameters); UpdateStatus(status, null); } } using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_PredictionSettings_ToleranceLabel"), GUILayoutWidth(3)); // Prior to Ἵππαρχος the tolerances were powers of 2, see #3395. if (length_integration_tolerance_index_ == 0 || speed_integration_tolerance_index_ == 0) { UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_DiscreteSelector_Min")); } else if (UnityEngine.GUILayout.Button("−")) { --length_integration_tolerance_index_; --speed_integration_tolerance_index_; UpdateAdaptiveStepParameters(ref parameters); var status = plugin.FlightPlanSetAdaptiveStepParameters( vessel_guid, parameters); UpdateStatus(status, null); } UnityEngine.GUILayout.TextArea( length_integration_tolerances_names_[ length_integration_tolerance_index_], GUILayoutWidth(3)); if (length_integration_tolerance_index_ == integration_tolerances_.Length - 1 || speed_integration_tolerance_index_ == integration_tolerances_.Length - 1) { UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_DiscreteSelector_Max")); } else if (UnityEngine.GUILayout.Button("+")) { ++length_integration_tolerance_index_; ++speed_integration_tolerance_index_; UpdateAdaptiveStepParameters(ref parameters); var status = plugin.FlightPlanSetAdaptiveStepParameters( vessel_guid, parameters); UpdateStatus(status, null); } } } double Δv = (from burn_editor in burn_editors_ select burn_editor.Δv()).Sum(); UnityEngine.GUILayout.Label(L10N.CacheFormat( "#Principia_FlightPlan_TotalΔv", Δv.ToString("0.000"))); { var style = Style.Warning(Style.Multiline(UnityEngine.GUI.skin.label)); string message = GetStatusMessage(); // Size the label explicitly so that it doesn't decrease when the // message goes away: that causes annoying flicker. The enclosing // window has a width of 20 units, but not all of that is available, // hence 19. warning_height_ = Math.Max(warning_height_, style.CalcHeight( new UnityEngine.GUIContent(message), Width(19))); UnityEngine.GUILayout.Label(message, style, UnityEngine.GUILayout.Height( warning_height_)); } if (burn_editors_.Count == 0 && UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_Delete"))) { final_trajectory_analyser_.DisposeWindow(); final_trajectory_analyser_ = new PlannedOrbitAnalyser(adapter_, predicted_vessel_); plugin.FlightPlanDelete(vessel_guid); ResetStatus(); Shrink(); // The state change will happen the next time we go through OnGUI. } else { using (new UnityEngine.GUILayout.HorizontalScope()) { if (UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_Rebase"))) { var status = plugin.FlightPlanRebase( vessel_guid, predicted_vessel.GetTotalMass()); UpdateStatus(status, null); if (status.ok()) { // The final time does not change, but since it is displayed with // respect to the beginning of the flight plan, the text must be // recomputed. final_time_.ResetValue( plugin.FlightPlanGetDesiredFinalTime(vessel_guid)); return; } } if (plugin.FlightPlanCount(vessel_guid) < max_flight_plans && UnityEngine.GUILayout.Button( L10N.CacheFormat("#Principia_FlightPlan_Duplicate"))) { plugin.FlightPlanDuplicate(vessel_guid); } } if (burn_editors_.Count > 0) { RenderUpcomingEvents(); } // Compute the final times for each manœuvre before displaying them. var final_times = new List <double>(); for (int i = 0; i < burn_editors_.Count - 1; ++i) { final_times.Add(plugin.FlightPlanGetManoeuvre(vessel_guid, i + 1). burn.initial_time); } // Allow extending the flight plan. final_times.Add(double.PositiveInfinity); for (int i = 0; i < burn_editors_.Count; ++i) { Style.HorizontalLine(); if (RenderCoast(i, out double?orbital_period)) { return; } Style.HorizontalLine(); BurnEditor burn = burn_editors_[i]; switch (burn.Render( header : L10N.CacheFormat("#Principia_FlightPlan_ManœuvreHeader", i + 1), anomalous : i >= burn_editors_.Count - number_of_anomalous_manœuvres_, burn_final_time : final_times[i], orbital_period : orbital_period)) { case BurnEditor.Event.Deleted: { var status = plugin.FlightPlanRemove(vessel_guid, i); UpdateStatus(status, null); burn_editors_[i].Close(); burn_editors_.RemoveAt(i); UpdateBurnEditorIndices(); Shrink(); return; } case BurnEditor.Event.Minimized: case BurnEditor.Event.Maximized: { Shrink(); return; } case BurnEditor.Event.Changed: { var status = plugin.FlightPlanReplace(vessel_guid, burn.Burn(), i); UpdateStatus(status, i); burn.Reset(plugin.FlightPlanGetManoeuvre(vessel_guid, i)); break; } case BurnEditor.Event.None: { break; } } } Style.HorizontalLine(); if (RenderCoast(burn_editors_.Count, orbital_period: out _)) { return; } } } }
private void RenderSubtree(CelestialBody celestial, int depth) { // Horizontal offset between a node and its children. const int offset = 1; using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Space(Width(offset * depth)); if (celestial.is_leaf(target)) { UnityEngine.GUILayout.Button( "", UnityEngine.GUI.skin.label, GUILayoutWidth(offset)); } else { string button_text = expanded_[celestial] ? "−" : "+"; if (UnityEngine.GUILayout.Button( button_text, GUILayoutWidth(offset))) { Shrink(); expanded_[celestial] = !expanded_[celestial]; } } UnityEngine.GUILayout.Label(celestial.StandaloneName()); UnityEngine.GUILayout.FlexibleSpace(); if (celestial.is_root()) { UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_ReferenceFrameSelector_Pin")); } else if (UnityEngine.GUILayout.Toggle(pinned[celestial], "") != pinned[celestial]) { pinned[celestial] = !pinned[celestial]; Shrink(); } } if (!celestial.is_leaf(target)) { if ((expanded_[celestial] || target_pinned_) && target?.orbit.referenceBody == celestial) { using (new UnityEngine.GUILayout.HorizontalScope()) { UnityEngine.GUILayout.Space(Width(offset * (depth + 1))); UnityEngine.GUILayout.Button( "", UnityEngine.GUI.skin.label, GUILayoutWidth(offset)); UnityEngine.GUILayout.Label( L10N.CacheFormat("#Principia_ReferenceFrameSelector_Target", target.vesselName)); UnityEngine.GUILayout.FlexibleSpace(); if (UnityEngine.GUILayout.Toggle(target_pinned_, "") != target_pinned_) { target_pinned_ = !target_pinned_; Shrink(); } } } foreach (CelestialBody child in celestial.orbitingBodies) { if (expanded_[celestial] || AnyDescendantPinned(child)) { RenderSubtree(child, depth + 1); } } } }
public static string Standalone(string name) { return(L10N.CacheFormat("#Principia_GrammaticalForm_Standalone", name)); }