mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2025-12-05 20:40:13 -08:00
Added flyby details display in maneuvers selector.
This commit is contained in:
@@ -270,13 +270,21 @@ class TrajectoryCalculator {
|
||||
const periapsisState = { pos: periPos, vel: periVel };
|
||||
const flybyOrbit = Physics3D.stateToOrbitElements(periapsisState, body);
|
||||
const tof = Physics3D.tofBetweenAnomalies(flybyOrbit, body, angles.begin, angles.end);
|
||||
const flybyDetails = {
|
||||
bodyId: body.id,
|
||||
soiEnterDate: this._lastStepEndDate,
|
||||
soiExitDate: this._lastStepEndDate + tof,
|
||||
periRadius: periRadius,
|
||||
inclination: flybyOrbit.inclination
|
||||
};
|
||||
this.steps.push({
|
||||
orbitElts: flybyOrbit,
|
||||
attractorId: body.id,
|
||||
angles: angles,
|
||||
drawAngles: drawAngles,
|
||||
duration: tof,
|
||||
dateOfStart: this._lastStepEndDate
|
||||
dateOfStart: this._lastStepEndDate,
|
||||
flyby: flybyDetails
|
||||
});
|
||||
const exitState = Physics3D.orbitElementsToState(flybyOrbit, body, exitAngle);
|
||||
this._vesselState = exitState;
|
||||
|
||||
40
dist/main/editor/editor.js
vendored
40
dist/main/editor/editor.js
vendored
@@ -100,23 +100,33 @@ export function initEditor(controls, system, config, canvas) {
|
||||
const solver = new TrajectorySolver(system, config, deltaVPlot);
|
||||
const paramsErr = new ErrorMessage("search-params-error");
|
||||
let trajectory;
|
||||
const maneuvreSelector = new Selector("maneuvre-selector");
|
||||
const detailsSelector = new Selector("details-selector");
|
||||
const stepSlider = new DiscreteRange("displayed-steps-slider");
|
||||
maneuvreSelector.disable();
|
||||
detailsSelector.disable();
|
||||
stepSlider.disable();
|
||||
const resultSpans = {
|
||||
dateSpan: document.getElementById("maneuvre-date"),
|
||||
progradeDVSpan: document.getElementById("prograde-delta-v"),
|
||||
normalDVSpan: document.getElementById("normal-delta-v"),
|
||||
radialDVSpan: document.getElementById("radial-delta-v"),
|
||||
depDateSpan: document.getElementById("result-departure-date"),
|
||||
totalDVSpan: document.getElementById("result-total-delta-v"),
|
||||
maneuvreNumber: document.getElementById("maneuvre-number"),
|
||||
const getSpan = (id) => document.getElementById(id);
|
||||
const resultItems = {
|
||||
dateSpan: getSpan("maneuvre-date"),
|
||||
progradeDVSpan: getSpan("prograde-delta-v"),
|
||||
normalDVSpan: getSpan("normal-delta-v"),
|
||||
radialDVSpan: getSpan("radial-delta-v"),
|
||||
depDateSpan: getSpan("result-departure-date"),
|
||||
totalDVSpan: getSpan("result-total-delta-v"),
|
||||
maneuvreNumber: getSpan("maneuvre-number"),
|
||||
flybyNumberSpan: getSpan("flyby-number"),
|
||||
startDateSpan: getSpan("flyby-start-date"),
|
||||
endDateSpan: getSpan("flyby-end-date"),
|
||||
periAltitudeSpan: getSpan("flyby-periapsis-altitude"),
|
||||
inclinationSpan: getSpan("flyby-inclination"),
|
||||
detailsSelector: detailsSelector,
|
||||
stepSlider: stepSlider,
|
||||
maneuverDiv: document.getElementById("maneuvre-details"),
|
||||
flybyDiv: document.getElementById("flyby-details")
|
||||
};
|
||||
const resetFoundTrajectory = () => {
|
||||
deltaVPlot.reveal();
|
||||
maneuvreSelector.clear();
|
||||
maneuvreSelector.disable();
|
||||
detailsSelector.clear();
|
||||
detailsSelector.disable();
|
||||
stepSlider.disable();
|
||||
if (trajectory) {
|
||||
trajectory.remove();
|
||||
@@ -125,9 +135,9 @@ export function initEditor(controls, system, config, canvas) {
|
||||
const displayFoundTrajectory = () => {
|
||||
trajectory = new Trajectory(solver.bestTrajectorySteps, system, config);
|
||||
trajectory.draw(canvas);
|
||||
trajectory.fillResultControls(maneuvreSelector, resultSpans, stepSlider, systemTime, controls);
|
||||
maneuvreSelector.select(0);
|
||||
maneuvreSelector.enable();
|
||||
trajectory.fillResultControls(resultItems, systemTime, controls);
|
||||
detailsSelector.select(0);
|
||||
detailsSelector.enable();
|
||||
stepSlider.enable();
|
||||
console.log(solver.bestDeltaV);
|
||||
};
|
||||
|
||||
148
dist/main/solvers/trajectory.js
vendored
148
dist/main/solvers/trajectory.js
vendored
@@ -9,6 +9,7 @@ export class Trajectory {
|
||||
this.orbits = [];
|
||||
this._objects = [];
|
||||
this._maneuvres = [];
|
||||
this._flybys = [];
|
||||
for (const { orbitElts, attractorId } of this.steps) {
|
||||
const attractor = this.system.bodyFromId(attractorId);
|
||||
const orbit = Orbit.fromOrbitalElements(orbitElts, attractor, config.orbit);
|
||||
@@ -28,6 +29,7 @@ export class Trajectory {
|
||||
this._createTrajectoryArcs(resolution);
|
||||
this._createManeuvreSprites();
|
||||
this._calculateManeuvresDetails();
|
||||
this._calculateFlybyDetails();
|
||||
}
|
||||
_createTrajectoryArcs(resolution) {
|
||||
const { lineWidth } = this.config.orbit;
|
||||
@@ -63,6 +65,7 @@ export class Trajectory {
|
||||
}
|
||||
}
|
||||
_calculateManeuvresDetails() {
|
||||
const departureDate = this.steps[0].dateOfStart;
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const step = this.steps[i];
|
||||
const { maneuvre } = step;
|
||||
@@ -75,70 +78,127 @@ export class Trajectory {
|
||||
const deltaV = new THREE.Vector3(maneuvre.deltaVToPrevStep.x, maneuvre.deltaVToPrevStep.y, maneuvre.deltaVToPrevStep.z);
|
||||
const details = {
|
||||
stepIndex: i,
|
||||
dateMET: step.dateOfStart - this.steps[0].dateOfStart,
|
||||
dateMET: step.dateOfStart - departureDate,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV)
|
||||
radialDV: radialDir.dot(deltaV),
|
||||
};
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
fillResultControls(maneuvreSelector, resultSpans, stepSlider, systemTime, controls) {
|
||||
_calculateFlybyDetails() {
|
||||
const departureDate = this.steps[0].dateOfStart;
|
||||
for (const { flyby } of this.steps) {
|
||||
if (flyby) {
|
||||
const body = this.system.bodyFromId(flyby.bodyId);
|
||||
let inc = flyby.inclination * 57.2957795131;
|
||||
inc = inc > 90 ? 180 - inc : inc;
|
||||
const details = {
|
||||
bodyId: flyby.bodyId,
|
||||
soiEnterDateMET: flyby.soiEnterDate - departureDate,
|
||||
soiExitDateMET: flyby.soiExitDate - departureDate,
|
||||
periAltitude: (flyby.periRadius - body.radius) / 1000,
|
||||
inclinationDeg: inc
|
||||
};
|
||||
this._flybys.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
fillResultControls(resultItems, systemTime, controls) {
|
||||
const depDate = new KSPTime(this.steps[0].dateOfStart, this.config.time);
|
||||
resultSpans.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultSpans.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "ut");
|
||||
resultSpans.depDateSpan.onclick = () => {
|
||||
this.system.date = depDate.dateSeconds;
|
||||
resultItems.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultItems.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "ut");
|
||||
const onDateClick = (date) => () => {
|
||||
this.system.date = date;
|
||||
controls.centerOnTarget();
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds;
|
||||
systemTime.time.dateSeconds = date;
|
||||
systemTime.update();
|
||||
};
|
||||
resultItems.depDateSpan.onclick = onDateClick(depDate.dateSeconds);
|
||||
const { stepSlider } = resultItems;
|
||||
stepSlider.setMinMax(0, this.steps.length - 1);
|
||||
stepSlider.input((index) => this._displayStepsUpTo(index));
|
||||
stepSlider.value = this.steps.length - 1;
|
||||
const selectorOptions = [];
|
||||
for (let i = 0; i < this._maneuvres.length; i++) {
|
||||
const details = this._maneuvres[i];
|
||||
const step = this.steps[details.stepIndex];
|
||||
const context = step.maneuvre.context;
|
||||
if (context.type == "ejection") {
|
||||
const startBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
const optionName = `${i + 1}: ${startBodyName} escape`;
|
||||
selectorOptions.push(optionName);
|
||||
let maneuvreIdx = 0, flybyIdx = 0;
|
||||
let optionNumber = 0;
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const { maneuvre, flyby } = this.steps[i];
|
||||
if (maneuvre) {
|
||||
const details = this._maneuvres[maneuvreIdx];
|
||||
const step = this.steps[details.stepIndex];
|
||||
const context = step.maneuvre.context;
|
||||
let optionName;
|
||||
if (context.type == "ejection") {
|
||||
const startBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
optionName = `${++optionNumber}: ${startBodyName} escape`;
|
||||
}
|
||||
else if (context.type == "dsm") {
|
||||
const originName = this.system.bodyFromId(context.originId).name;
|
||||
const targetName = this.system.bodyFromId(context.targetId).name;
|
||||
optionName = `${++optionNumber}: ${originName}-${targetName} DSM`;
|
||||
}
|
||||
else {
|
||||
const arrivalBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
optionName = `${++optionNumber}: ${arrivalBodyName} circularization`;
|
||||
}
|
||||
const option = {
|
||||
text: optionName,
|
||||
index: i,
|
||||
type: "maneuver",
|
||||
origin: maneuvreIdx++
|
||||
};
|
||||
selectorOptions.push(option);
|
||||
}
|
||||
else if (context.type == "dsm") {
|
||||
const originName = this.system.bodyFromId(context.originId).name;
|
||||
const targetName = this.system.bodyFromId(context.targetId).name;
|
||||
const optionName = `${i + 1}: ${originName}-${targetName} DSM`;
|
||||
selectorOptions.push(optionName);
|
||||
}
|
||||
else {
|
||||
const arrivalBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
const optionName = `${i + 1}: ${arrivalBodyName} circularization`;
|
||||
selectorOptions.push(optionName);
|
||||
else if (flyby) {
|
||||
const details = this._flybys[flybyIdx];
|
||||
const bodyName = this.system.bodyFromId(details.bodyId).name;
|
||||
const optionName = `${++optionNumber}: ${bodyName} flyby`;
|
||||
const option = {
|
||||
text: optionName,
|
||||
index: i,
|
||||
type: "flyby",
|
||||
origin: flybyIdx++
|
||||
};
|
||||
selectorOptions.push(option);
|
||||
}
|
||||
}
|
||||
maneuvreSelector.fill(selectorOptions);
|
||||
maneuvreSelector.change((_, index) => {
|
||||
const details = this._maneuvres[index];
|
||||
const dateEMT = new KSPTime(details.dateMET, this.config.time);
|
||||
resultSpans.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "emt");
|
||||
resultSpans.progradeDVSpan.innerHTML = details.progradeDV.toFixed(1);
|
||||
resultSpans.normalDVSpan.innerHTML = details.normalDV.toFixed(1);
|
||||
resultSpans.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultSpans.maneuvreNumber.innerHTML = (index + 1).toString();
|
||||
resultSpans.dateSpan.onclick = () => {
|
||||
const optionNames = selectorOptions.map(opt => opt.text);
|
||||
const { detailsSelector } = resultItems;
|
||||
detailsSelector.fill(optionNames);
|
||||
detailsSelector.change((_, index) => {
|
||||
const option = selectorOptions[index];
|
||||
if (option.type == "maneuver") {
|
||||
const details = this._maneuvres[option.origin];
|
||||
const dateEMT = new KSPTime(details.dateMET, this.config.time);
|
||||
resultItems.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.progradeDVSpan.innerHTML = details.progradeDV.toFixed(1);
|
||||
resultItems.normalDVSpan.innerHTML = details.normalDV.toFixed(1);
|
||||
resultItems.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultItems.maneuvreNumber.innerHTML = (option.origin + 1).toString();
|
||||
const date = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
this.system.date = date;
|
||||
controls.centerOnTarget();
|
||||
systemTime.time.dateSeconds = date;
|
||||
systemTime.update();
|
||||
};
|
||||
resultItems.dateSpan.onclick = onDateClick(date);
|
||||
resultItems.flybyDiv.hidden = true;
|
||||
resultItems.maneuverDiv.hidden = false;
|
||||
}
|
||||
else if (option.type == "flyby") {
|
||||
const details = this._flybys[option.origin];
|
||||
const startDateEMT = new KSPTime(details.soiEnterDateMET, this.config.time);
|
||||
const endDateEMT = new KSPTime(details.soiExitDateMET, this.config.time);
|
||||
resultItems.startDateSpan.innerHTML = startDateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.endDateSpan.innerHTML = endDateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.periAltitudeSpan.innerHTML = details.periAltitude.toFixed(0);
|
||||
resultItems.inclinationSpan.innerHTML = details.inclinationDeg.toFixed(0);
|
||||
resultItems.flybyNumberSpan.innerHTML = (option.origin + 1).toString();
|
||||
let enterDate = depDate.dateSeconds + startDateEMT.dateSeconds;
|
||||
resultItems.startDateSpan.onclick = onDateClick(enterDate);
|
||||
let exitDate = depDate.dateSeconds + endDateEMT.dateSeconds;
|
||||
resultItems.endDateSpan.onclick = onDateClick(exitDate);
|
||||
resultItems.flybyDiv.hidden = false;
|
||||
resultItems.maneuverDiv.hidden = true;
|
||||
}
|
||||
});
|
||||
for (const step of this.steps) {
|
||||
console.log(step);
|
||||
}
|
||||
}
|
||||
_displayStepsUpTo(index) {
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
|
||||
55
index.html
55
index.html
@@ -222,13 +222,21 @@
|
||||
<!-- End of the 3D display panel -->
|
||||
|
||||
<div id="result-panel">
|
||||
<h2 class="calculation-panel-title">Calculated trajectory</h2>
|
||||
<div id="result-panel-header">
|
||||
<h2 class="calculation-panel-title">Calculated trajectory</h2>
|
||||
<div id="steps-slider-control-group" class="control-group">
|
||||
<label class="control-label" for="displayed-steps-slider">Displayed steps:</label>
|
||||
<div id="steps-slider-controls" class="controls">
|
||||
<input name="displayed-steps-slider" id="displayed-steps-slider" type="range" min="0" max="1" value="1" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="result-sub-panels">
|
||||
<div id="result-controls">
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Departure date:</label>
|
||||
<p><strong><span id="result-departure-date">--</span> UT</strong></p>
|
||||
<p><strong><span id="result-departure-date" class="clickable-date">--</span> UT</strong></p>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
@@ -237,32 +245,37 @@
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="displayed-steps-slider">Displayed steps:</label>
|
||||
<label class="control-label" for="details-selector">Maneuvers:</label>
|
||||
<div class="controls">
|
||||
<input name="displayed-steps-slider" id="displayed-steps-slider" type="range" min="0" max="1" value="1" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="maneuvre-selector">Maneuvres:</label>
|
||||
<div class="controls">
|
||||
<select id="maneuvre-selector" name="maneuvre-selector" disabled>
|
||||
<select id="details-selector" name="details-selector" disabled>
|
||||
<!-- filled by js -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- End of result controls -->
|
||||
|
||||
<div id="maneuvre-details">
|
||||
<h3>Maneuver <span id="maneuvre-number"></span> details:</h3>
|
||||
<ul>
|
||||
<li><strong>Date:</strong> <span id="maneuvre-date">--</span> MET</li>
|
||||
<li><strong>Prograde ΔV:</strong> <span id="prograde-delta-v">--</span> m/s</li>
|
||||
<li><strong>Normal ΔV:</strong> <span id="normal-delta-v">--</span> m/s</li>
|
||||
<li><strong>Radial ΔV:</strong> <span id="radial-delta-v">--</span> m/s</li>
|
||||
</ul>
|
||||
<div>
|
||||
<div id="maneuvre-details" class="result-details">
|
||||
<h3>Maneuver <span id="maneuvre-number"></span> details:</h3>
|
||||
<ul>
|
||||
<li><strong>Date:</strong> <span id="maneuvre-date" class="clickable-date">--</span> MET</li>
|
||||
<li><strong>Prograde ΔV:</strong> <span id="prograde-delta-v">--</span> m/s</li>
|
||||
<li><strong>Normal ΔV:</strong> <span id="normal-delta-v">--</span> m/s</li>
|
||||
<li><strong>Radial ΔV:</strong> <span id="radial-delta-v">--</span> m/s</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="flyby-details" class="result-details" hidden>
|
||||
<h3>Flyby <span id="flyby-number"></span> details:</h3>
|
||||
<ul>
|
||||
<li><strong>SOI enter date:</strong> <span id="flyby-start-date" class="clickable-date">--</span> MET</li>
|
||||
<li><strong>SOI exit date:</strong> <span id="flyby-end-date" class="clickable-date">--</span> MET</li>
|
||||
<li><strong>Periapsis altitude:</strong> <span id="flyby-periapsis-altitude">--</span> km</li>
|
||||
<li><strong>Inclination:</strong> <span id="flyby-inclination">--</span>°</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End of maneuvre details -->
|
||||
</div>
|
||||
@@ -366,7 +379,7 @@
|
||||
</p>
|
||||
<p>
|
||||
The resulting trajectory will be displayed in the interactive 3D window, alongside with
|
||||
the details of each maneuver (ejection from the departure body and DSMs).
|
||||
the details of each maneuver (ejection from the departure body and DSMs) and flybys' information.
|
||||
</p>
|
||||
|
||||
<figure id="example-image-container">
|
||||
|
||||
@@ -15,9 +15,9 @@ class TrajectoryCalculator {
|
||||
|
||||
private _bodiesOrbits!: OrbitalElements3D[];
|
||||
|
||||
private _legs: LegInfo[] = [];
|
||||
private _flybys: FlybyInfo[] = [];
|
||||
private _departureInfos!: DepartureInfo;
|
||||
private _legs: AgentLegInfo[] = [];
|
||||
private _flybys: AgentFlybyInfo[] = [];
|
||||
private _departureInfos!: AgentDepartureInfo;
|
||||
|
||||
private _secondArcsData: SecondArcData[] = [];
|
||||
|
||||
@@ -167,7 +167,7 @@ class TrajectoryCalculator {
|
||||
* parameters
|
||||
* @param infos The leg infos to complete
|
||||
*/
|
||||
private _computeLegDuration(infos: LegInfo){
|
||||
private _computeLegDuration(infos: AgentLegInfo){
|
||||
const exitedBody = this.system[infos.exitedBodyId];
|
||||
const {durationParam} = infos;
|
||||
const attr = this._mainAttractor;
|
||||
@@ -381,7 +381,7 @@ class TrajectoryCalculator {
|
||||
* Computes the flyby orbit with the provided parameters.
|
||||
* @param flybyInfo The flyby parameters
|
||||
*/
|
||||
private _computeFlyby(flybyInfo: FlybyInfo){
|
||||
private _computeFlyby(flybyInfo: AgentFlybyInfo){
|
||||
const body = this.system[flybyInfo.flybyBodyId];
|
||||
|
||||
// Compute the relative velocity to the body when entering its SOI
|
||||
@@ -442,13 +442,22 @@ class TrajectoryCalculator {
|
||||
const tof = Physics3D.tofBetweenAnomalies(flybyOrbit, body, angles.begin, angles.end);
|
||||
|
||||
// Append the flyby orbit
|
||||
const flybyDetails: FlybyInfo = {
|
||||
bodyId: body.id,
|
||||
soiEnterDate: this._lastStepEndDate,
|
||||
soiExitDate: this._lastStepEndDate + tof,
|
||||
periRadius: periRadius,
|
||||
inclination: flybyOrbit.inclination
|
||||
};
|
||||
|
||||
this.steps.push({
|
||||
orbitElts: flybyOrbit,
|
||||
attractorId: body.id,
|
||||
angles: angles,
|
||||
drawAngles: drawAngles,
|
||||
duration: tof,
|
||||
dateOfStart: this._lastStepEndDate
|
||||
dateOfStart: this._lastStepEndDate,
|
||||
flyby: flybyDetails
|
||||
});
|
||||
|
||||
// Compute the vessel state relative to the body when exiting its SOI
|
||||
@@ -471,7 +480,7 @@ class TrajectoryCalculator {
|
||||
* supposing a null radius SOI.
|
||||
* @param legInfo The leg parameters
|
||||
*/
|
||||
private _computeLegSecondArcSimple(legInfo: LegInfo){
|
||||
private _computeLegSecondArcSimple(legInfo: AgentLegInfo){
|
||||
const attr = this._mainAttractor;
|
||||
const lastStep = this._lastStep;
|
||||
const preDSMState = this._vesselState;
|
||||
@@ -540,7 +549,7 @@ class TrajectoryCalculator {
|
||||
* Computes the next leg first arc with the provided parameters.
|
||||
* @param legInfo The parameters of the leg
|
||||
*/
|
||||
private _computeFirstLegArc(legInfo: LegInfo){
|
||||
private _computeFirstLegArc(legInfo: AgentLegInfo){
|
||||
// Compute the state of the vessel relatived to the exited body after exiting its SOI
|
||||
/*const lastStep = this._lastStep;
|
||||
const exitedBody = this.system[lastStep.attractorId];
|
||||
|
||||
@@ -139,25 +139,40 @@ export function initEditor(controls: CameraController, system: SolarSystem, conf
|
||||
let trajectory: Trajectory | undefined;
|
||||
|
||||
// Result panel
|
||||
const maneuvreSelector = new Selector("maneuvre-selector");
|
||||
const detailsSelector = new Selector("details-selector");
|
||||
const stepSlider = new DiscreteRange("displayed-steps-slider");
|
||||
maneuvreSelector.disable();
|
||||
|
||||
detailsSelector.disable();
|
||||
stepSlider.disable();
|
||||
|
||||
const resultSpans = {
|
||||
dateSpan: document.getElementById("maneuvre-date") as HTMLSpanElement,
|
||||
progradeDVSpan: document.getElementById("prograde-delta-v") as HTMLSpanElement,
|
||||
normalDVSpan: document.getElementById("normal-delta-v") as HTMLSpanElement,
|
||||
radialDVSpan: document.getElementById("radial-delta-v") as HTMLSpanElement,
|
||||
depDateSpan: document.getElementById("result-departure-date") as HTMLSpanElement,
|
||||
totalDVSpan: document.getElementById("result-total-delta-v") as HTMLSpanElement,
|
||||
maneuvreNumber: document.getElementById("maneuvre-number") as HTMLSpanElement,
|
||||
const getSpan = (id: string) => document.getElementById(id) as HTMLSpanElement;
|
||||
|
||||
const resultItems = {
|
||||
dateSpan: getSpan("maneuvre-date"),
|
||||
progradeDVSpan: getSpan("prograde-delta-v"),
|
||||
normalDVSpan: getSpan("normal-delta-v"),
|
||||
radialDVSpan: getSpan("radial-delta-v"),
|
||||
depDateSpan: getSpan("result-departure-date") ,
|
||||
totalDVSpan: getSpan("result-total-delta-v"),
|
||||
maneuvreNumber: getSpan("maneuvre-number"),
|
||||
|
||||
flybyNumberSpan: getSpan("flyby-number"),
|
||||
startDateSpan: getSpan("flyby-start-date"),
|
||||
endDateSpan: getSpan("flyby-end-date"),
|
||||
periAltitudeSpan: getSpan("flyby-periapsis-altitude"),
|
||||
inclinationSpan: getSpan("flyby-inclination"),
|
||||
|
||||
detailsSelector: detailsSelector,
|
||||
stepSlider: stepSlider,
|
||||
|
||||
maneuverDiv: document.getElementById("maneuvre-details") as HTMLDivElement,
|
||||
flybyDiv: document.getElementById("flyby-details") as HTMLDivElement
|
||||
};
|
||||
|
||||
const resetFoundTrajectory = () => {
|
||||
deltaVPlot.reveal();
|
||||
maneuvreSelector.clear();
|
||||
maneuvreSelector.disable();
|
||||
detailsSelector.clear();
|
||||
detailsSelector.disable();
|
||||
stepSlider.disable();
|
||||
if(trajectory){
|
||||
trajectory.remove();
|
||||
@@ -167,10 +182,10 @@ export function initEditor(controls: CameraController, system: SolarSystem, conf
|
||||
const displayFoundTrajectory = () => {
|
||||
trajectory = new Trajectory(solver.bestTrajectorySteps, system, config);
|
||||
trajectory.draw(canvas);
|
||||
trajectory.fillResultControls(maneuvreSelector, resultSpans, stepSlider, systemTime, controls);
|
||||
trajectory.fillResultControls(resultItems, systemTime, controls);
|
||||
|
||||
maneuvreSelector.select(0);
|
||||
maneuvreSelector.enable();
|
||||
detailsSelector.select(0);
|
||||
detailsSelector.enable();
|
||||
stepSlider.enable();
|
||||
|
||||
console.log(solver.bestDeltaV);
|
||||
|
||||
@@ -10,8 +10,9 @@ import { CameraController } from "../objects/camera.js";
|
||||
export class Trajectory {
|
||||
public readonly orbits: Orbit[] = [];
|
||||
|
||||
private readonly _objects: THREE.Object3D[] = [];
|
||||
private readonly _objects: THREE.Object3D[] = [];
|
||||
private readonly _maneuvres: ManeuvreDetails[] = [];
|
||||
private readonly _flybys: FlybyDetails[] = [];
|
||||
|
||||
static arrowMaterial: THREE.SpriteMaterial;
|
||||
|
||||
@@ -37,6 +38,7 @@ export class Trajectory {
|
||||
this._createTrajectoryArcs(resolution);
|
||||
this._createManeuvreSprites();
|
||||
this._calculateManeuvresDetails();
|
||||
this._calculateFlybyDetails();
|
||||
}
|
||||
|
||||
private _createTrajectoryArcs(resolution: {width: number, height: number}){
|
||||
@@ -76,6 +78,8 @@ export class Trajectory {
|
||||
}
|
||||
|
||||
private _calculateManeuvresDetails(){
|
||||
const departureDate = this.steps[0].dateOfStart;
|
||||
|
||||
for(let i = 0; i < this.steps.length; i++){
|
||||
const step = this.steps[i];
|
||||
const {maneuvre} = step;
|
||||
@@ -97,80 +101,154 @@ export class Trajectory {
|
||||
maneuvre.deltaVToPrevStep.z,
|
||||
);
|
||||
|
||||
const details: ManeuvreDetails = {
|
||||
stepIndex: i,
|
||||
dateMET: step.dateOfStart - this.steps[0].dateOfStart,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV)
|
||||
const details = {
|
||||
stepIndex: i,
|
||||
dateMET: step.dateOfStart - departureDate,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV),
|
||||
};
|
||||
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _calculateFlybyDetails(){
|
||||
const departureDate = this.steps[0].dateOfStart;
|
||||
for(const {flyby} of this.steps){
|
||||
if(flyby){
|
||||
const body = this.system.bodyFromId(flyby.bodyId);
|
||||
// non oriented inclination compared to x-z plane
|
||||
let inc = flyby.inclination * 57.2957795131 // in degrees
|
||||
inc = inc > 90 ? 180 - inc : inc;
|
||||
const details: FlybyDetails = {
|
||||
bodyId: flyby.bodyId,
|
||||
soiEnterDateMET: flyby.soiEnterDate - departureDate,
|
||||
soiExitDateMET: flyby.soiExitDate - departureDate,
|
||||
periAltitude: (flyby.periRadius - body.radius) / 1000, // in km
|
||||
inclinationDeg: inc
|
||||
}
|
||||
this._flybys.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fillResultControls(maneuvreSelector: Selector, resultSpans: ResultPanelSpans, stepSlider: DiscreteRange, systemTime: TimeSelector, controls: CameraController){
|
||||
public fillResultControls(resultItems: ResultPannelItems, systemTime: TimeSelector, controls: CameraController){
|
||||
const depDate = new KSPTime(this.steps[0].dateOfStart, this.config.time);
|
||||
|
||||
resultSpans.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultSpans.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "ut");
|
||||
resultItems.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultItems.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "ut");
|
||||
|
||||
resultSpans.depDateSpan.onclick = () => {
|
||||
this.system.date = depDate.dateSeconds;
|
||||
const onDateClick = (date: number) => () => {
|
||||
this.system.date = date;
|
||||
controls.centerOnTarget();
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds;
|
||||
systemTime.time.dateSeconds = date;
|
||||
systemTime.update();
|
||||
};
|
||||
|
||||
resultItems.depDateSpan.onclick = onDateClick(depDate.dateSeconds);
|
||||
|
||||
const {stepSlider} = resultItems;
|
||||
stepSlider.setMinMax(0, this.steps.length - 1);
|
||||
stepSlider.input((index: number) => this._displayStepsUpTo(index));
|
||||
stepSlider.value = this.steps.length - 1;
|
||||
|
||||
const selectorOptions: string[] = [];
|
||||
for(let i = 0; i < this._maneuvres.length; i++){
|
||||
const details = this._maneuvres[i];
|
||||
const step = this.steps[details.stepIndex];
|
||||
const context = (<ManeuvreInfo>step.maneuvre).context;
|
||||
if(context.type == "ejection") {
|
||||
const startBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
const optionName = `${i+1}: ${startBodyName} escape`;
|
||||
selectorOptions.push(optionName);
|
||||
} else if(context.type == "dsm") {
|
||||
const originName = this.system.bodyFromId(context.originId).name;
|
||||
const targetName = this.system.bodyFromId(context.targetId).name;
|
||||
const optionName = `${i+1}: ${originName}-${targetName} DSM`;
|
||||
selectorOptions.push(optionName);
|
||||
} else {
|
||||
const arrivalBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
const optionName = `${i+1}: ${arrivalBodyName} circularization`;
|
||||
selectorOptions.push(optionName);
|
||||
|
||||
const selectorOptions: DetailsSelectorOption[] = [];
|
||||
|
||||
let maneuvreIdx = 0, flybyIdx = 0;
|
||||
let optionNumber = 0;
|
||||
|
||||
for(let i = 0; i < this.steps.length; i++){
|
||||
const {maneuvre, flyby} = this.steps[i];
|
||||
if(maneuvre){
|
||||
const details = this._maneuvres[maneuvreIdx];
|
||||
const step = this.steps[details.stepIndex];
|
||||
const context = (<ManeuvreInfo>step.maneuvre).context;
|
||||
|
||||
let optionName: string;
|
||||
if(context.type == "ejection") {
|
||||
const startBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
optionName = `${++optionNumber}: ${startBodyName} escape`;
|
||||
} else if(context.type == "dsm") {
|
||||
const originName = this.system.bodyFromId(context.originId).name;
|
||||
const targetName = this.system.bodyFromId(context.targetId).name;
|
||||
optionName = `${++optionNumber}: ${originName}-${targetName} DSM`;
|
||||
} else {
|
||||
const arrivalBodyName = this.system.bodyFromId(step.attractorId).name;
|
||||
optionName = `${++optionNumber}: ${arrivalBodyName} circularization`;
|
||||
}
|
||||
|
||||
const option: DetailsSelectorOption = {
|
||||
text: optionName,
|
||||
index: i,
|
||||
type: "maneuver",
|
||||
origin: maneuvreIdx++
|
||||
};
|
||||
selectorOptions.push(option);
|
||||
|
||||
} else if(flyby){
|
||||
const details = this._flybys[flybyIdx];
|
||||
const bodyName = this.system.bodyFromId(details.bodyId).name;
|
||||
const optionName = `${++optionNumber}: ${bodyName} flyby`;
|
||||
|
||||
const option: DetailsSelectorOption = {
|
||||
text: optionName,
|
||||
index: i,
|
||||
type: "flyby",
|
||||
origin: flybyIdx++
|
||||
};
|
||||
selectorOptions.push(option);
|
||||
}
|
||||
}
|
||||
|
||||
maneuvreSelector.fill(selectorOptions);
|
||||
maneuvreSelector.change((_: string, index: number) => {
|
||||
const details = this._maneuvres[index];
|
||||
const dateEMT = new KSPTime(details.dateMET, this.config.time);
|
||||
const optionNames = selectorOptions.map(opt => opt.text);
|
||||
|
||||
const {detailsSelector} = resultItems;
|
||||
detailsSelector.fill(optionNames);
|
||||
detailsSelector.change((_: string, index: number) => {
|
||||
const option = selectorOptions[index];
|
||||
|
||||
resultSpans.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "emt");
|
||||
resultSpans.progradeDVSpan.innerHTML = details.progradeDV.toFixed(1);
|
||||
resultSpans.normalDVSpan.innerHTML = details.normalDV.toFixed(1);
|
||||
resultSpans.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultSpans.maneuvreNumber.innerHTML = (index + 1).toString();
|
||||
if(option.type == "maneuver"){
|
||||
const details = this._maneuvres[option.origin];
|
||||
const dateEMT = new KSPTime(details.dateMET, this.config.time);
|
||||
|
||||
resultItems.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.progradeDVSpan.innerHTML = details.progradeDV.toFixed(1);
|
||||
resultItems.normalDVSpan.innerHTML = details.normalDV.toFixed(1);
|
||||
resultItems.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultItems.maneuvreNumber.innerHTML = (option.origin + 1).toString();
|
||||
|
||||
resultSpans.dateSpan.onclick = () => {
|
||||
const date = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
this.system.date = date;
|
||||
controls.centerOnTarget();
|
||||
systemTime.time.dateSeconds = date;
|
||||
systemTime.update();
|
||||
};
|
||||
});
|
||||
resultItems.dateSpan.onclick = onDateClick(date);
|
||||
|
||||
for(const step of this.steps){
|
||||
resultItems.flybyDiv.hidden = true;
|
||||
resultItems.maneuverDiv.hidden = false;
|
||||
|
||||
} else if(option.type == "flyby"){
|
||||
const details = this._flybys[option.origin];
|
||||
const startDateEMT = new KSPTime(details.soiEnterDateMET, this.config.time);
|
||||
const endDateEMT = new KSPTime(details.soiExitDateMET, this.config.time);
|
||||
|
||||
resultItems.startDateSpan.innerHTML = startDateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.endDateSpan.innerHTML = endDateEMT.stringYDHMS("hm", "emt");
|
||||
resultItems.periAltitudeSpan.innerHTML = details.periAltitude.toFixed(0);
|
||||
resultItems.inclinationSpan.innerHTML = details.inclinationDeg.toFixed(0);
|
||||
resultItems.flybyNumberSpan.innerHTML = (option.origin + 1).toString();
|
||||
|
||||
let enterDate = depDate.dateSeconds + startDateEMT.dateSeconds;
|
||||
resultItems.startDateSpan.onclick = onDateClick(enterDate);
|
||||
let exitDate = depDate.dateSeconds + endDateEMT.dateSeconds;
|
||||
resultItems.endDateSpan.onclick = onDateClick(exitDate);
|
||||
|
||||
resultItems.flybyDiv.hidden = false;
|
||||
resultItems.maneuverDiv.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
/*for(const step of this.steps){
|
||||
console.log(step);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private _displayStepsUpTo(index: number){
|
||||
|
||||
73
src/types.d.ts
vendored
73
src/types.d.ts
vendored
@@ -200,7 +200,8 @@ type TrajectoryStep = {
|
||||
drawAngles: ArcEndsAngles,
|
||||
dateOfStart: number,
|
||||
duration: number,
|
||||
maneuvre?: ManeuvreInfo
|
||||
maneuvre?: ManeuvreInfo,
|
||||
flyby?: FlybyInfo
|
||||
};
|
||||
|
||||
type ManeuvreInfo = {
|
||||
@@ -210,19 +211,28 @@ type ManeuvreInfo = {
|
||||
context: ManeuvreContext
|
||||
};
|
||||
|
||||
type FlybyInfo = {
|
||||
bodyId: number,
|
||||
soiEnterDate: number,
|
||||
soiExitDate: number,
|
||||
periRadius: number,
|
||||
inclination: number
|
||||
};
|
||||
|
||||
|
||||
type ManeuvreContext =
|
||||
| {type: "ejection"}
|
||||
| {type: "dsm", originId: number, targetId: number}
|
||||
| {type: "circularization"}
|
||||
;
|
||||
|
||||
type DepartureInfo = {
|
||||
type AgentDepartureInfo = {
|
||||
phaseParam: number,
|
||||
ejVelParam: number,
|
||||
dateParam: number,
|
||||
};
|
||||
|
||||
type LegInfo = {
|
||||
type AgentLegInfo = {
|
||||
exitedBodyId: number,
|
||||
targetBodyId: number,
|
||||
durationParam: number,
|
||||
@@ -230,7 +240,7 @@ type LegInfo = {
|
||||
dsmParam: number,
|
||||
}
|
||||
|
||||
type FlybyInfo = {
|
||||
type AgentFlybyInfo = {
|
||||
flybyBodyId: number,
|
||||
normAngleParam: number,
|
||||
periRadiParam: number
|
||||
@@ -252,19 +262,46 @@ type GenerationResult = {
|
||||
}
|
||||
|
||||
type ManeuvreDetails = {
|
||||
stepIndex: number,
|
||||
dateMET: number,
|
||||
progradeDV: number,
|
||||
normalDV: number,
|
||||
radialDV: number
|
||||
stepIndex: number,
|
||||
dateMET: number,
|
||||
progradeDV: number,
|
||||
normalDV: number,
|
||||
radialDV: number,
|
||||
};
|
||||
|
||||
type ResultPanelSpans = {
|
||||
dateSpan: HTMLSpanElement,
|
||||
progradeDVSpan: HTMLSpanElement,
|
||||
normalDVSpan: HTMLSpanElement,
|
||||
radialDVSpan: HTMLSpanElement,
|
||||
depDateSpan: HTMLSpanElement,
|
||||
totalDVSpan: HTMLSpanElement;
|
||||
maneuvreNumber: HTMLSpanElement
|
||||
}
|
||||
type FlybyDetails = {
|
||||
bodyId: number,
|
||||
soiEnterDateMET: number,
|
||||
soiExitDateMET: number,
|
||||
periAltitude: number,
|
||||
inclinationDeg: number
|
||||
};
|
||||
|
||||
type ResultPannelItems = {
|
||||
dateSpan: HTMLSpanElement,
|
||||
progradeDVSpan: HTMLSpanElement,
|
||||
normalDVSpan: HTMLSpanElement,
|
||||
radialDVSpan: HTMLSpanElement,
|
||||
depDateSpan: HTMLSpanElement,
|
||||
totalDVSpan: HTMLSpanElement,
|
||||
maneuvreNumber: HTMLSpanElement,
|
||||
|
||||
flybyNumberSpan: HTMLSpanElement,
|
||||
startDateSpan: HTMLSpanElement,
|
||||
endDateSpan: HTMLSpanElement,
|
||||
periAltitudeSpan: HTMLSpanElement,
|
||||
inclinationSpan: HTMLSpanElement
|
||||
|
||||
detailsSelector: Selector,
|
||||
stepSlider: DiscreteRange,
|
||||
|
||||
maneuverDiv: HTMLDivElement,
|
||||
flybyDiv: HTMLDivElement
|
||||
};
|
||||
|
||||
type DetailsSelectorOption = {
|
||||
text: string,
|
||||
index: number,
|
||||
type: "maneuver" | "flyby",
|
||||
origin: number
|
||||
};
|
||||
46
style.css
46
style.css
@@ -387,39 +387,71 @@ input[type="range"]
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#maneuvre-details
|
||||
.result-details
|
||||
{
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#maneuvre-details ul
|
||||
.result-details ul
|
||||
{
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#maneuvre-details h3
|
||||
.result-details h3
|
||||
{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#maneuvre-details li strong
|
||||
.result-details li strong
|
||||
{
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
width: 127px;
|
||||
}
|
||||
|
||||
#result-departure-date, #maneuvre-date
|
||||
.clickable-date
|
||||
{
|
||||
color: rgb(86, 169, 224);
|
||||
}
|
||||
|
||||
#result-departure-date:hover, #maneuvre-date:hover
|
||||
.clickable-date:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#result-panel-header
|
||||
{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#steps-slider-control-group
|
||||
{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#steps-slider-controls
|
||||
{
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#displayed-steps-slider
|
||||
{
|
||||
margin-bottom: 0;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#result-controls .controls
|
||||
{
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#result-controls
|
||||
{
|
||||
width: 413px;
|
||||
}
|
||||
|
||||
/* Paragraphs */
|
||||
|
||||
p
|
||||
|
||||
Reference in New Issue
Block a user