mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2025-12-15 09:00:49 -08:00
Recompile and moved files.
This commit is contained in:
2
dist/main/editor/editor.js
vendored
2
dist/main/editor/editor.js
vendored
@@ -8,7 +8,7 @@ import { EvolutionPlot } from "./plot.js";
|
||||
import { ProgressMessage } from "./progress-msg.js";
|
||||
import { SequenceSelector } from "./sequence-selector.js";
|
||||
import { SubmitButton, StopButton } from "./buttons.js";
|
||||
import { Trajectory } from "../objects/trajectory.js";
|
||||
import { Trajectory } from "../solvers/trajectory.js";
|
||||
import { Selector } from "./selector.js";
|
||||
import { DiscreteRange } from "./range.js";
|
||||
export function initEditor(controls, system, config, canvas) {
|
||||
|
||||
2
dist/main/editor/time-selector.js
vendored
2
dist/main/editor/time-selector.js
vendored
@@ -1,4 +1,4 @@
|
||||
import { TimeAndDate } from "../objects/time.js";
|
||||
import { TimeAndDate } from "../utilities/time.js";
|
||||
export class TimeSelector {
|
||||
constructor(namePrefix, config, autoValidate = false) {
|
||||
this.config = config;
|
||||
|
||||
65
dist/main/editor/time.js
vendored
65
dist/main/editor/time.js
vendored
@@ -1,65 +0,0 @@
|
||||
export class TimeSelector {
|
||||
constructor(namePrefix, config, autoValidate = false) {
|
||||
this.config = config;
|
||||
this.yearInput = document.getElementById(`${namePrefix}-year`);
|
||||
this.dayInput = document.getElementById(`${namePrefix}-day`);
|
||||
this.hourInput = document.getElementById(`${namePrefix}-hour`);
|
||||
this.selector = document.getElementById(`${namePrefix}-time`);
|
||||
if (autoValidate) {
|
||||
this.selector.onchange = () => this.validate();
|
||||
}
|
||||
}
|
||||
get date() {
|
||||
const { hoursPerDay, daysPerYear } = this.config.time;
|
||||
const timeElapsed = this.validate();
|
||||
const days = timeElapsed.year * daysPerYear + timeElapsed.day;
|
||||
const hours = days * hoursPerDay + timeElapsed.hour;
|
||||
const date = hours * 3600;
|
||||
return date;
|
||||
}
|
||||
change(action) {
|
||||
this.selector.onchange = () => action();
|
||||
}
|
||||
validate() {
|
||||
const { hoursPerDay, daysPerYear } = this.config.time;
|
||||
let year = parseInt(this.yearInput.value);
|
||||
let day = parseInt(this.dayInput.value);
|
||||
let hour = parseInt(this.hourInput.value);
|
||||
if (isNaN(year) || isNaN(day) || isNaN(hour)) {
|
||||
this.yearInput.value = "1";
|
||||
this.dayInput.value = "1";
|
||||
this.hourInput.value = "0";
|
||||
return { year: 0, day: 0, hour: 0 };
|
||||
}
|
||||
year -= 1;
|
||||
day -= 1;
|
||||
const hpd = Math.round(hoursPerDay);
|
||||
const dpy = Math.round(daysPerYear);
|
||||
if (hour >= 0) {
|
||||
day += Math.floor(hour / hpd);
|
||||
}
|
||||
else if (day > 0) {
|
||||
day -= Math.floor((hpd - hour) / hpd);
|
||||
hour = hpd - ((-hour) % hpd);
|
||||
}
|
||||
hour %= hpd;
|
||||
if (day >= 0) {
|
||||
year += Math.floor(day / dpy);
|
||||
}
|
||||
else if (year > 0) {
|
||||
year -= Math.floor((dpy - day) / dpy);
|
||||
day = dpy - ((-day) % dpy);
|
||||
}
|
||||
day %= dpy;
|
||||
year = Math.max(0, year);
|
||||
day = Math.max(0, day);
|
||||
hour = Math.max(0, hour);
|
||||
const timeElapsed = { year: year, day: day, hour: hour };
|
||||
year += 1;
|
||||
day += 1;
|
||||
this.yearInput.value = year.toString();
|
||||
this.dayInput.value = day.toString();
|
||||
this.hourInput.value = hour.toString();
|
||||
return timeElapsed;
|
||||
}
|
||||
}
|
||||
2
dist/main/main.js
vendored
2
dist/main/main.js
vendored
@@ -2,7 +2,7 @@ import { initEditor } from "./editor/editor.js";
|
||||
import { SolarSystem } from "./objects/system.js";
|
||||
import { CameraController } from "./objects/camera.js";
|
||||
import { loadConfig, loadBodiesData } from "./utilities/data.js";
|
||||
import { Trajectory } from "./objects/trajectory.js";
|
||||
import { Trajectory } from "./solvers/trajectory.js";
|
||||
window.onload = main;
|
||||
async function main() {
|
||||
const canvas = document.getElementById("three-canvas");
|
||||
|
||||
17
dist/main/objects/sequence.js
vendored
17
dist/main/objects/sequence.js
vendored
@@ -1,17 +0,0 @@
|
||||
export class FlybySequence {
|
||||
constructor(system, ids, cost) {
|
||||
this.ids = ids;
|
||||
this.cost = cost;
|
||||
this.bodies = [];
|
||||
for (const id of ids) {
|
||||
this.bodies.push(system.bodyFromId(id));
|
||||
}
|
||||
this.length = this.bodies.length;
|
||||
const getSubstr = (i) => this.bodies[i].name.substring(0, 2);
|
||||
let str = getSubstr(0);
|
||||
for (let i = 1; i < this.length; i++) {
|
||||
str += "-" + getSubstr(i);
|
||||
}
|
||||
this.seqString = str;
|
||||
}
|
||||
}
|
||||
159
dist/main/objects/trajectory.js
vendored
159
dist/main/objects/trajectory.js
vendored
@@ -1,159 +0,0 @@
|
||||
import { createOrbitPoints, createLine, createSprite } from "../utilities/geometry.js";
|
||||
import { Orbit } from "./orbit.js";
|
||||
import { TimeAndDate } from "./time.js";
|
||||
export class Trajectory {
|
||||
constructor(steps, system, config) {
|
||||
this.steps = steps;
|
||||
this.system = system;
|
||||
this.config = config;
|
||||
this.orbits = [];
|
||||
this._objects = [];
|
||||
this._maneuvres = [];
|
||||
for (const { orbitElts, attractorId } of this.steps) {
|
||||
const attractor = this.system.bodyFromId(attractorId);
|
||||
const orbit = Orbit.fromOrbitalElements(orbitElts, attractor, config.orbit);
|
||||
this.orbits.push(orbit);
|
||||
}
|
||||
}
|
||||
static preloadArrowMaterial() {
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
const loaded = (texture) => {
|
||||
this.arrowMaterial = new THREE.SpriteMaterial({
|
||||
map: texture
|
||||
});
|
||||
};
|
||||
textureLoader.load("sprites/arrow-512.png", loaded);
|
||||
}
|
||||
draw(resolution) {
|
||||
this._createTrajectoryArcs(resolution);
|
||||
this._createManeuvreSprites();
|
||||
this._calculateManeuvresDetails();
|
||||
}
|
||||
_createTrajectoryArcs(resolution) {
|
||||
const { lineWidth } = this.config.orbit;
|
||||
const { samplePoints } = this.config.trajectoryDraw;
|
||||
const { scale } = this.config.rendering;
|
||||
for (let i = 0; i < this.orbits.length; i++) {
|
||||
const orbit = this.orbits[i];
|
||||
const { beginAngle, endAngle } = this.steps[i];
|
||||
const orbitPoints = createOrbitPoints(orbit, samplePoints, scale, beginAngle, endAngle);
|
||||
const color = new THREE.Color(`hsl(${i * 35 % 360}, 100%, 85%)`);
|
||||
const orbitLine = createLine(orbitPoints, resolution, {
|
||||
color: color.getHex(),
|
||||
linewidth: lineWidth,
|
||||
});
|
||||
const group = this.system.objectsOfBody(orbit.attractor.id);
|
||||
group.add(orbitLine);
|
||||
this._objects.push(orbitLine);
|
||||
}
|
||||
}
|
||||
_createManeuvreSprites() {
|
||||
const { maneuvreArrowSize } = this.config.trajectoryDraw;
|
||||
const { scale } = this.config.rendering;
|
||||
for (const step of this.steps) {
|
||||
if (step.maneuvre) {
|
||||
const group = this.system.objectsOfBody(step.attractorId);
|
||||
const sprite = createSprite(Trajectory.arrowMaterial, 0xFFFFFF, false, maneuvreArrowSize);
|
||||
const { x, y, z } = step.maneuvre.manoeuvrePosition;
|
||||
sprite.position.set(x, y, z);
|
||||
sprite.position.multiplyScalar(scale);
|
||||
group.add(sprite);
|
||||
this._objects.push(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
_calculateManeuvresDetails() {
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const step = this.steps[i];
|
||||
const { maneuvre } = step;
|
||||
if (maneuvre) {
|
||||
const orbit = this.orbits[i];
|
||||
const progradeDir = new THREE.Vector3(maneuvre.progradeDir.x, maneuvre.progradeDir.y, maneuvre.progradeDir.z);
|
||||
const normalDir = orbit.normal.clone();
|
||||
const radialDir = progradeDir.clone();
|
||||
radialDir.cross(normalDir);
|
||||
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,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV)
|
||||
};
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
fillResultControls(maneuvreSelector, resultSpans, stepSlider, systemTime) {
|
||||
const depDate = new TimeAndDate(this.steps[0].dateOfStart, this.config.time);
|
||||
resultSpans.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultSpans.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "date");
|
||||
resultSpans.depDateSpan.onclick = () => {
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds;
|
||||
systemTime.update();
|
||||
systemTime.onChange();
|
||||
};
|
||||
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);
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
maneuvreSelector.fill(selectorOptions);
|
||||
maneuvreSelector.change((_, index) => {
|
||||
const details = this._maneuvres[index];
|
||||
const dateEMT = new TimeAndDate(details.dateMET, this.config.time);
|
||||
resultSpans.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "elapsed");
|
||||
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 = () => {
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
systemTime.update();
|
||||
systemTime.onChange();
|
||||
};
|
||||
});
|
||||
}
|
||||
_displayStepsUpTo(index) {
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const orbitLine = this._objects[i];
|
||||
orbitLine.visible = i <= index;
|
||||
}
|
||||
const spritesStart = this.steps.length;
|
||||
for (let i = 0; i < this._maneuvres.length; i++) {
|
||||
const visible = this._objects[this._maneuvres[i].stepIndex].visible;
|
||||
this._objects[spritesStart + i].visible = visible;
|
||||
}
|
||||
}
|
||||
get _totalDeltaV() {
|
||||
let total = 0;
|
||||
for (const details of this._maneuvres) {
|
||||
const x = details.progradeDV;
|
||||
const y = details.normalDV;
|
||||
const z = details.radialDV;
|
||||
total += new THREE.Vector3(x, y, z).length();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
remove() {
|
||||
for (const object of this._objects) {
|
||||
if (object.parent)
|
||||
object.parent.remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
dist/main/solvers/sequence-solver.js
vendored
2
dist/main/solvers/sequence-solver.js
vendored
@@ -1,6 +1,6 @@
|
||||
import { ComputeWorker, WorkerPool } from "../utilities/worker.js";
|
||||
import { shuffleArray } from "../utilities/array.js";
|
||||
import { FlybySequence } from "../objects/sequence.js";
|
||||
import { FlybySequence } from "./sequence.js";
|
||||
export class FlybySequenceGenerator {
|
||||
constructor(system, config) {
|
||||
this.system = system;
|
||||
|
||||
61
dist/main/solvers/sequence.js
vendored
61
dist/main/solvers/sequence.js
vendored
@@ -1,5 +1,3 @@
|
||||
import { ComputeWorker, WorkerPool } from "../utilities/worker.js";
|
||||
import { shuffleArray } from "../utilities/array.js";
|
||||
export class FlybySequence {
|
||||
constructor(system, ids, cost) {
|
||||
this.ids = ids;
|
||||
@@ -17,62 +15,3 @@ export class FlybySequence {
|
||||
this.seqString = str;
|
||||
}
|
||||
}
|
||||
export class FlybySequenceGenerator {
|
||||
constructor(system, config) {
|
||||
this.system = system;
|
||||
this.config = config;
|
||||
this.totalFeasible = 0;
|
||||
this._workerPool = new WorkerPool("dedicated-workers/sequence-evaluator.js", this.config);
|
||||
this._workerPool.initialize({ system: this.system.data, config: this.config });
|
||||
this._sequenceWorker = new ComputeWorker("dedicated-workers/sequence-generator.js");
|
||||
this._sequenceWorker.initialize(this.config);
|
||||
}
|
||||
cancel() {
|
||||
this._workerPool.cancel();
|
||||
}
|
||||
async generateFlybySequences(params, onProgress) {
|
||||
const toHigherOrbit = params.destinationId > params.departureId;
|
||||
const departureBody = this.system.bodyFromId(params.departureId);
|
||||
const allowedBodies = this._getAllowedBodies(departureBody, params, toHigherOrbit);
|
||||
const feasibleSet = await this._generateFeasibleSet(allowedBodies, params);
|
||||
this.totalFeasible = feasibleSet.length;
|
||||
const evaluations = await this._evaluateSequences(feasibleSet, onProgress);
|
||||
evaluations.sort((a, b) => a.cost - b.cost);
|
||||
const { maxPropositions } = this.config.flybySequence;
|
||||
const sequences = [];
|
||||
for (let i = 0; i < Math.min(maxPropositions, evaluations.length); i++) {
|
||||
const result = evaluations[i];
|
||||
const sequence = new FlybySequence(this.system, feasibleSet[result.seq], result.cost);
|
||||
sequences.push(sequence);
|
||||
}
|
||||
return sequences;
|
||||
}
|
||||
_getAllowedBodies(departureBody, params, toHigherOrbit) {
|
||||
const allowedBodies = [];
|
||||
for (const body of departureBody.attractor.orbiters) {
|
||||
if ((toHigherOrbit && body.id <= params.destinationId) ||
|
||||
(!toHigherOrbit && body.id >= params.destinationId)) {
|
||||
allowedBodies.push(body.id);
|
||||
}
|
||||
}
|
||||
return allowedBodies;
|
||||
}
|
||||
_generateFeasibleSet(allowedBodies, params) {
|
||||
return this._sequenceWorker.run({ bodies: allowedBodies, params: params });
|
||||
}
|
||||
async _evaluateSequences(sequences, onProgress) {
|
||||
shuffleArray(sequences);
|
||||
const { splitLimit } = this.config.flybySequence;
|
||||
const costs = await this._workerPool.runPoolChunked(sequences, splitLimit, onProgress);
|
||||
const evaluations = [];
|
||||
for (let i = 0; i < sequences.length; i++) {
|
||||
if (costs[i] != undefined) {
|
||||
evaluations.push({ seq: i, cost: costs[i] });
|
||||
}
|
||||
}
|
||||
return evaluations;
|
||||
}
|
||||
get progression() {
|
||||
return this._workerPool.totalProgress;
|
||||
}
|
||||
}
|
||||
|
||||
288
dist/main/solvers/trajectory.js
vendored
288
dist/main/solvers/trajectory.js
vendored
@@ -1,151 +1,159 @@
|
||||
import { mergeArrayChunks } from "../utilities/array.js";
|
||||
import { WorkerPool } from "../utilities/worker.js";
|
||||
export class TrajectorySolver {
|
||||
constructor(system, config, plot) {
|
||||
import { createOrbitPoints, createLine, createSprite } from "../utilities/geometry.js";
|
||||
import { Orbit } from "../objects/orbit.js";
|
||||
import { TimeAndDate } from "../utilities/time.js";
|
||||
export class Trajectory {
|
||||
constructor(steps, system, config) {
|
||||
this.steps = steps;
|
||||
this.system = system;
|
||||
this.config = config;
|
||||
this.plot = plot;
|
||||
this.popSize = 0;
|
||||
this.dim = 0;
|
||||
this._cancelled = false;
|
||||
this._running = false;
|
||||
this._population = [];
|
||||
this._deltaVs = [];
|
||||
this._numChunks = 0;
|
||||
this._chunkIndices = [];
|
||||
this._workerPool = new WorkerPool("dedicated-workers/trajectory-optimizer.js", this.config);
|
||||
this._workerPool.initialize({ system: this.system.data, config: this.config });
|
||||
}
|
||||
_initPlot() {
|
||||
this.plot.clearPlot();
|
||||
}
|
||||
_updatePlot(iteration) {
|
||||
const { best, mean } = this._getBestMeanDeltaV;
|
||||
this.plot.addIterationData(iteration, mean, best);
|
||||
}
|
||||
cancel() {
|
||||
if (this._running)
|
||||
this._cancelled = true;
|
||||
}
|
||||
_checkCancellation() {
|
||||
if (this._cancelled) {
|
||||
this._cancelled = false;
|
||||
this._running = false;
|
||||
throw "TRAJECTORY FINDER CANCELLED";
|
||||
this.orbits = [];
|
||||
this._objects = [];
|
||||
this._maneuvres = [];
|
||||
for (const { orbitElts, attractorId } of this.steps) {
|
||||
const attractor = this.system.bodyFromId(attractorId);
|
||||
const orbit = Orbit.fromOrbitalElements(orbitElts, attractor, config.orbit);
|
||||
this.orbits.push(orbit);
|
||||
}
|
||||
}
|
||||
async searchOptimalTrajectory(sequence, startDateMin, startDateMax) {
|
||||
this._running = true;
|
||||
this._initPlot();
|
||||
this._calculatePopulationSizes(sequence);
|
||||
this._calculatePopulationChunks();
|
||||
await this._createStartPopulation(sequence, startDateMin, startDateMax);
|
||||
return;
|
||||
this._updatePlot(0);
|
||||
this._checkCancellation();
|
||||
for (let i = 0; i < 50; i++) {
|
||||
await this._generateNextPopulation();
|
||||
this._updatePlot(1 + i);
|
||||
this._checkCancellation();
|
||||
}
|
||||
this._running = false;
|
||||
}
|
||||
async _createStartPopulation(sequence, startDateMin, startDateMax) {
|
||||
const agentSettings = {
|
||||
startDateMin: startDateMin,
|
||||
startDateMax: startDateMax,
|
||||
dim: this.dim
|
||||
};
|
||||
const inputs = this._firstGenerationInputs(sequence, agentSettings);
|
||||
const results = await this._workerPool.runPool(inputs);
|
||||
console.log(results);
|
||||
return;
|
||||
const { population, deltaVs } = this._mergeResultsChunks(results);
|
||||
this._population = population;
|
||||
this._deltaVs = deltaVs;
|
||||
}
|
||||
_calculatePopulationChunks() {
|
||||
const { splitLimit } = this.config.trajectorySearch;
|
||||
const numChunks = this._workerPool.optimizeUsedWorkersCount(this.popSize, splitLimit);
|
||||
const chunkSize = Math.floor(this.popSize / numChunks);
|
||||
const chunkIndices = [0, chunkSize - 1];
|
||||
for (let i = 2; i < numChunks * 2; i += 2) {
|
||||
const start = chunkIndices[i - 1] + 1;
|
||||
const end = start + chunkSize;
|
||||
chunkIndices.push(start, end);
|
||||
}
|
||||
chunkIndices[numChunks * 2 - 1] = this.popSize - 1;
|
||||
this._numChunks = numChunks;
|
||||
this._chunkIndices = chunkIndices;
|
||||
}
|
||||
_calculatePopulationSizes(sequence) {
|
||||
const swingBys = sequence.length - 2;
|
||||
this.dim = 4 * swingBys + 6;
|
||||
this.popSize = 10 * this.dim;
|
||||
}
|
||||
async _generateNextPopulation() {
|
||||
const inputs = this._nextGenerationInputs();
|
||||
const results = await this._workerPool.runPool(inputs);
|
||||
const { population, deltaVs } = this._mergeResultsChunks(results);
|
||||
this._population = population;
|
||||
this._deltaVs = deltaVs;
|
||||
}
|
||||
_mergeResultsChunks(results) {
|
||||
const popChunks = [];
|
||||
const dVChunks = [];
|
||||
for (let i = 0; i < this._numChunks; i++) {
|
||||
popChunks.push(results[i].popChunk);
|
||||
dVChunks.push(results[i].fitChunk);
|
||||
}
|
||||
return {
|
||||
population: mergeArrayChunks(popChunks),
|
||||
deltaVs: mergeArrayChunks(dVChunks)
|
||||
};
|
||||
}
|
||||
_firstGenerationInputs(sequence, agentSettings) {
|
||||
const inputs = [];
|
||||
for (let i = 0; i < this._numChunks; i++) {
|
||||
const { start, end } = this._chunkStartEnd(i);
|
||||
inputs.push({
|
||||
start: true,
|
||||
chunkStart: start,
|
||||
chunkEnd: end,
|
||||
sequence: sequence.ids,
|
||||
agentSettings: agentSettings
|
||||
static preloadArrowMaterial() {
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
const loaded = (texture) => {
|
||||
this.arrowMaterial = new THREE.SpriteMaterial({
|
||||
map: texture
|
||||
});
|
||||
}
|
||||
return inputs;
|
||||
};
|
||||
textureLoader.load("sprites/arrow-512.png", loaded);
|
||||
}
|
||||
_nextGenerationInputs() {
|
||||
const inputs = [];
|
||||
for (let i = 0; i < this._numChunks; i++) {
|
||||
const { start, end } = this._chunkStartEnd(i);
|
||||
inputs[i] = {
|
||||
population: this._population,
|
||||
deltaVs: this._deltaVs,
|
||||
chunkStart: start,
|
||||
chunkEnd: end
|
||||
draw(resolution) {
|
||||
this._createTrajectoryArcs(resolution);
|
||||
this._createManeuvreSprites();
|
||||
this._calculateManeuvresDetails();
|
||||
}
|
||||
_createTrajectoryArcs(resolution) {
|
||||
const { lineWidth } = this.config.orbit;
|
||||
const { samplePoints } = this.config.trajectoryDraw;
|
||||
const { scale } = this.config.rendering;
|
||||
for (let i = 0; i < this.orbits.length; i++) {
|
||||
const orbit = this.orbits[i];
|
||||
const { beginAngle, endAngle } = this.steps[i];
|
||||
const orbitPoints = createOrbitPoints(orbit, samplePoints, scale, beginAngle, endAngle);
|
||||
const color = new THREE.Color(`hsl(${i * 35 % 360}, 100%, 85%)`);
|
||||
const orbitLine = createLine(orbitPoints, resolution, {
|
||||
color: color.getHex(),
|
||||
linewidth: lineWidth,
|
||||
});
|
||||
const group = this.system.objectsOfBody(orbit.attractor.id);
|
||||
group.add(orbitLine);
|
||||
this._objects.push(orbitLine);
|
||||
}
|
||||
}
|
||||
_createManeuvreSprites() {
|
||||
const { maneuvreArrowSize } = this.config.trajectoryDraw;
|
||||
const { scale } = this.config.rendering;
|
||||
for (const step of this.steps) {
|
||||
if (step.maneuvre) {
|
||||
const group = this.system.objectsOfBody(step.attractorId);
|
||||
const sprite = createSprite(Trajectory.arrowMaterial, 0xFFFFFF, false, maneuvreArrowSize);
|
||||
const { x, y, z } = step.maneuvre.manoeuvrePosition;
|
||||
sprite.position.set(x, y, z);
|
||||
sprite.position.multiplyScalar(scale);
|
||||
group.add(sprite);
|
||||
this._objects.push(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
_calculateManeuvresDetails() {
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const step = this.steps[i];
|
||||
const { maneuvre } = step;
|
||||
if (maneuvre) {
|
||||
const orbit = this.orbits[i];
|
||||
const progradeDir = new THREE.Vector3(maneuvre.progradeDir.x, maneuvre.progradeDir.y, maneuvre.progradeDir.z);
|
||||
const normalDir = orbit.normal.clone();
|
||||
const radialDir = progradeDir.clone();
|
||||
radialDir.cross(normalDir);
|
||||
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,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV)
|
||||
};
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
fillResultControls(maneuvreSelector, resultSpans, stepSlider, systemTime) {
|
||||
const depDate = new TimeAndDate(this.steps[0].dateOfStart, this.config.time);
|
||||
resultSpans.totalDVSpan.innerHTML = this._totalDeltaV.toFixed(1);
|
||||
resultSpans.depDateSpan.innerHTML = depDate.stringYDHMS("hms", "date");
|
||||
resultSpans.depDateSpan.onclick = () => {
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds;
|
||||
systemTime.update();
|
||||
systemTime.onChange();
|
||||
};
|
||||
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);
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
maneuvreSelector.fill(selectorOptions);
|
||||
maneuvreSelector.change((_, index) => {
|
||||
const details = this._maneuvres[index];
|
||||
const dateEMT = new TimeAndDate(details.dateMET, this.config.time);
|
||||
resultSpans.dateSpan.innerHTML = dateEMT.stringYDHMS("hm", "elapsed");
|
||||
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 = () => {
|
||||
systemTime.time.dateSeconds = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
systemTime.update();
|
||||
systemTime.onChange();
|
||||
};
|
||||
}
|
||||
return inputs;
|
||||
});
|
||||
}
|
||||
_chunkStartEnd(index) {
|
||||
return {
|
||||
start: this._chunkIndices[index * 2],
|
||||
end: this._chunkIndices[index * 2 + 1]
|
||||
};
|
||||
}
|
||||
get _getBestMeanDeltaV() {
|
||||
let mean = 0;
|
||||
let best = Infinity;
|
||||
for (const dv of this._deltaVs) {
|
||||
mean += dv;
|
||||
best = Math.min(best, dv);
|
||||
_displayStepsUpTo(index) {
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const orbitLine = this._objects[i];
|
||||
orbitLine.visible = i <= index;
|
||||
}
|
||||
const spritesStart = this.steps.length;
|
||||
for (let i = 0; i < this._maneuvres.length; i++) {
|
||||
const visible = this._objects[this._maneuvres[i].stepIndex].visible;
|
||||
this._objects[spritesStart + i].visible = visible;
|
||||
}
|
||||
}
|
||||
get _totalDeltaV() {
|
||||
let total = 0;
|
||||
for (const details of this._maneuvres) {
|
||||
const x = details.progradeDV;
|
||||
const y = details.normalDV;
|
||||
const z = details.radialDV;
|
||||
total += new THREE.Vector3(x, y, z).length();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
remove() {
|
||||
for (const object of this._objects) {
|
||||
if (object.parent)
|
||||
object.parent.remove(object);
|
||||
}
|
||||
mean /= this.popSize;
|
||||
return {
|
||||
mean: mean,
|
||||
best: best
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import { EvolutionPlot } from "./plot.js";
|
||||
import { ProgressMessage } from "./progress-msg.js";
|
||||
import { SequenceSelector } from "./sequence-selector.js";
|
||||
import { SubmitButton, StopButton } from "./buttons.js";
|
||||
import { FlybySequence } from "../objects/sequence.js";
|
||||
import { Trajectory } from "../objects/trajectory.js";
|
||||
import { FlybySequence } from "../solvers/sequence.js";
|
||||
import { Trajectory } from "../solvers/trajectory.js";
|
||||
import { Selector } from "./selector.js";
|
||||
import { DiscreteRange } from "./range.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FlybySequence } from "../objects/sequence.js";
|
||||
import { FlybySequence } from "../solvers/sequence.js";
|
||||
import { Selector } from "./selector.js";
|
||||
|
||||
export class SequenceSelector extends Selector {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TimeAndDate } from "../objects/time.js";
|
||||
import { TimeAndDate } from "../utilities/time.js";
|
||||
|
||||
export class TimeSelector {
|
||||
readonly time!: TimeAndDate;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { initEditor } from "./editor/editor.js";
|
||||
import { SolarSystem } from "./objects/system.js";
|
||||
import { CameraController } from "./objects/camera.js";
|
||||
import { loadConfig, loadBodiesData } from "./utilities/data.js";
|
||||
import { Trajectory } from "./objects/trajectory.js";
|
||||
import { Trajectory } from "./solvers/trajectory.js";
|
||||
|
||||
window.onload = main;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { OrbitingBody } from "../objects/body.js";
|
||||
import { SolarSystem } from "../objects/system.js";
|
||||
import { ComputeWorker, WorkerPool } from "../utilities/worker.js";
|
||||
import { shuffleArray } from "../utilities/array.js";
|
||||
import { FlybySequence } from "../objects/sequence.js";
|
||||
import { FlybySequence } from "./sequence.js";
|
||||
|
||||
export class FlybySequenceGenerator {
|
||||
private readonly _workerPool!: WorkerPool;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OrbitingBody } from "./body";
|
||||
import { SolarSystem } from "./system";
|
||||
import { OrbitingBody } from "../objects/body";
|
||||
import { SolarSystem } from "../objects/system";
|
||||
|
||||
export class FlybySequence {
|
||||
public readonly bodies!: OrbitingBody[];
|
||||
@@ -2,7 +2,7 @@ import { EvolutionPlot } from "../editor/plot.js";
|
||||
import { SolarSystem } from "../objects/system.js";
|
||||
import { mergeArrayChunks } from "../utilities/array.js";
|
||||
import { WorkerPool } from "../utilities/worker.js";
|
||||
import { FlybySequence } from "../objects/sequence.js";
|
||||
import { FlybySequence } from "./sequence.js";
|
||||
|
||||
export class TrajectorySolver {
|
||||
private readonly _workerPool!: WorkerPool;
|
||||
|
||||
@@ -2,9 +2,9 @@ import { DiscreteRange } from "../editor/range.js";
|
||||
import { Selector } from "../editor/selector.js";
|
||||
import { TimeSelector } from "../editor/time-selector.js";
|
||||
import { createOrbitPoints, createLine, createSprite } from "../utilities/geometry.js";
|
||||
import { Orbit } from "./orbit.js";
|
||||
import { SolarSystem } from "./system.js";
|
||||
import { TimeAndDate } from "./time.js";
|
||||
import { Orbit } from "../objects/orbit.js";
|
||||
import { SolarSystem } from "../objects/system.js";
|
||||
import { TimeAndDate } from "../utilities/time.js";
|
||||
|
||||
export class Trajectory {
|
||||
public readonly orbits: Orbit[] = [];
|
||||
Reference in New Issue
Block a user