mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2026-01-27 15:25:21 -08:00
Updated worker scripts structure.
Created a `WorkerEnvironment` class to contain the scripts in the dedicated workers.
This commit is contained in:
59
dist/dedicated-workers/libs/common.js
vendored
59
dist/dedicated-workers/libs/common.js
vendored
@@ -1,9 +1,11 @@
|
||||
"use strict";
|
||||
let onWorkerInitialize = () => { };
|
||||
let onWorkerRun = () => { };
|
||||
let onWorkerContinue = () => { };
|
||||
let onWorkerStop = () => { };
|
||||
let onWorkerDataPass = () => { };
|
||||
class WorkerEnvironment {
|
||||
onWorkerInitialize(data) { }
|
||||
onWorkerRun(input) { }
|
||||
onWorkerContinue(input) { }
|
||||
onWorkerStop() { }
|
||||
onWorkerDataPass(data) { }
|
||||
}
|
||||
function postMessageSafe(msg) {
|
||||
postMessage(msg);
|
||||
}
|
||||
@@ -16,25 +18,28 @@ function debug(...data) {
|
||||
function sendResult(result) {
|
||||
postMessageSafe({ label: "complete", result: result });
|
||||
}
|
||||
onmessage = ({ data }) => {
|
||||
switch (data.label) {
|
||||
case "initialize":
|
||||
onWorkerInitialize(data.config);
|
||||
postMessageSafe({ label: "initialized" });
|
||||
break;
|
||||
case "run":
|
||||
onWorkerRun(data.input);
|
||||
break;
|
||||
case "continue":
|
||||
onWorkerContinue();
|
||||
break;
|
||||
case "stop":
|
||||
onWorkerStop();
|
||||
postMessageSafe({ label: "stopped" });
|
||||
break;
|
||||
case "pass":
|
||||
onWorkerDataPass(data.data);
|
||||
postMessageSafe({ label: "received" });
|
||||
break;
|
||||
}
|
||||
};
|
||||
function initWorker(Env) {
|
||||
const env = new Env();
|
||||
onmessage = ({ data }) => {
|
||||
switch (data.label) {
|
||||
case "initialize":
|
||||
env.onWorkerInitialize(data.config);
|
||||
postMessageSafe({ label: "initialized" });
|
||||
break;
|
||||
case "run":
|
||||
env.onWorkerRun(data.input);
|
||||
break;
|
||||
case "continue":
|
||||
env.onWorkerContinue();
|
||||
break;
|
||||
case "stop":
|
||||
env.onWorkerStop();
|
||||
postMessageSafe({ label: "stopped" });
|
||||
break;
|
||||
case "pass":
|
||||
env.onWorkerDataPass(data.data);
|
||||
postMessageSafe({ label: "received" });
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
2
dist/dedicated-workers/libs/physics-2d.js
vendored
2
dist/dedicated-workers/libs/physics-2d.js
vendored
@@ -13,7 +13,7 @@ function getRelativeVelocity2D(globalVel, bodyVel) {
|
||||
function getGlobalVelocity2D(relativeVel, bodyVel) {
|
||||
return add2(relativeVel, bodyVel);
|
||||
}
|
||||
function calculateNextIntersection(pos0, vel0, body, attractor, config) {
|
||||
function calculateNextIntersection(pos0, vel0, body, attractor) {
|
||||
const mu = attractor.stdGravParam;
|
||||
const v0 = mag2(vel0);
|
||||
const r0 = mag2(pos0);
|
||||
|
||||
70
dist/dedicated-workers/sequence-evaluator.js
vendored
70
dist/dedicated-workers/sequence-evaluator.js
vendored
@@ -1,30 +1,36 @@
|
||||
"use strict";
|
||||
{
|
||||
importScripts("libs/common.js", "libs/math.js", "libs/physics.js", "libs/physics-2d.js");
|
||||
let system;
|
||||
let config;
|
||||
let current = null;
|
||||
onWorkerInitialize = data => {
|
||||
system = data.system;
|
||||
config = data.config;
|
||||
};
|
||||
onWorkerRun = input => {
|
||||
current = evaluateInChunks(input);
|
||||
importScripts("libs/common.js", "libs/math.js", "libs/physics.js", "libs/physics-2d.js");
|
||||
class SequenceEvaluator extends WorkerEnvironment {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._current = null;
|
||||
}
|
||||
onWorkerInitialize(data) {
|
||||
this._system = data.system;
|
||||
this._config = data.config;
|
||||
}
|
||||
onWorkerRun(input) {
|
||||
this._sequences = input;
|
||||
const { orbiting } = this._system[this._sequences[0][0]];
|
||||
this._attractor = this._system[orbiting];
|
||||
this._current = this._evaluateSequenceChunks();
|
||||
sendProgress(0);
|
||||
};
|
||||
onWorkerContinue = () => {
|
||||
if (!current)
|
||||
}
|
||||
onWorkerContinue() {
|
||||
if (!this._current)
|
||||
return;
|
||||
const { done, value } = current.next();
|
||||
const { done, value } = this._current.next();
|
||||
if (done)
|
||||
sendResult(value);
|
||||
};
|
||||
onWorkerStop = () => current = null;
|
||||
function* evaluateInChunks(sequences) {
|
||||
}
|
||||
onWorkerStop() {
|
||||
this._current = null;
|
||||
}
|
||||
*_evaluateSequenceChunks() {
|
||||
const results = [];
|
||||
const { progressStep } = config.workers;
|
||||
for (let i = 0; i < sequences.length; i++) {
|
||||
results.push(evaluateSequence(sequences[i]));
|
||||
const { progressStep } = this._config.workers;
|
||||
for (let i = 0; i < this._sequences.length; i++) {
|
||||
results.push(this._evaluateSequence(this._sequences[i]));
|
||||
if (i % progressStep == 0) {
|
||||
sendProgress(i);
|
||||
yield;
|
||||
@@ -32,17 +38,16 @@
|
||||
}
|
||||
return results;
|
||||
}
|
||||
function evaluateSequence(sequence) {
|
||||
const bodies = getSequenceBodies(sequence);
|
||||
const attractor = system[bodies[0].orbiting];
|
||||
_evaluateSequence(sequence) {
|
||||
const bodies = this._bodySequenceOf(sequence);
|
||||
const depDeltaV = hohmannTransferDeltaV(bodies[0], bodies[1]);
|
||||
if (bodies.length == 2) {
|
||||
const depVel = depDeltaV + bodies[0].circularVel;
|
||||
const relativeArrVel = hohmannEncounterRelativeVel(bodies[0], bodies[1], depVel, attractor);
|
||||
const relativeArrVel = hohmannEncounterRelativeVel(bodies[0], bodies[1], depVel, this._attractor);
|
||||
return Math.abs(depDeltaV) + Math.abs(relativeArrVel);
|
||||
}
|
||||
const statuses = generateInitialStatuses(bodies[0], depDeltaV);
|
||||
const { maxEvalStatuses, radiusSamples } = config.flybySequence;
|
||||
const statuses = this._generateInitialStatuses(bodies[0], depDeltaV);
|
||||
const { maxEvalStatuses, radiusSamples } = this._config.flybySequence;
|
||||
let evalCount = 0;
|
||||
while (statuses.length > 0) {
|
||||
evalCount++;
|
||||
@@ -51,7 +56,7 @@
|
||||
}
|
||||
const status = statuses.pop();
|
||||
const targetBody = bodies[status.target];
|
||||
const itsc = calculateNextIntersection(status.pos, status.vel, targetBody, attractor, config);
|
||||
const itsc = calculateNextIntersection(status.pos, status.vel, targetBody, this._attractor);
|
||||
if (itsc) {
|
||||
const targetBodyVel = getBodyVelocity2D(itsc.pos, targetBody);
|
||||
const arrivalVel = getRelativeVelocity2D(itsc.vel, targetBodyVel);
|
||||
@@ -75,16 +80,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
function getSequenceBodies(sequence) {
|
||||
_bodySequenceOf(sequence) {
|
||||
const bodies = [];
|
||||
for (const id of sequence) {
|
||||
bodies.push(system[id]);
|
||||
bodies.push(this._system[id]);
|
||||
}
|
||||
return bodies;
|
||||
}
|
||||
function generateInitialStatuses(depBody, depDeltaV) {
|
||||
_generateInitialStatuses(depBody, depDeltaV) {
|
||||
const statuses = [];
|
||||
const { initVelMaxScale, initVelSamples } = config.flybySequence;
|
||||
const { initVelMaxScale, initVelSamples } = this._config.flybySequence;
|
||||
for (let i = 0; i < initVelSamples; ++i) {
|
||||
const scale = (i / initVelSamples) * initVelMaxScale;
|
||||
statuses.push({
|
||||
@@ -96,3 +101,4 @@
|
||||
return statuses;
|
||||
}
|
||||
}
|
||||
initWorker(SequenceEvaluator);
|
||||
|
||||
86
dist/dedicated-workers/sequence-generator.js
vendored
86
dist/dedicated-workers/sequence-generator.js
vendored
@@ -1,34 +1,39 @@
|
||||
"use strict";
|
||||
{
|
||||
importScripts("libs/common.js");
|
||||
let config;
|
||||
onWorkerInitialize = data => config = data;
|
||||
onWorkerRun = input => {
|
||||
importScripts("libs/common.js");
|
||||
class SequenceGenerator extends WorkerEnvironment {
|
||||
onWorkerInitialize(data) {
|
||||
this._config = data;
|
||||
}
|
||||
onWorkerRun(input) {
|
||||
const { bodies, params } = input;
|
||||
const feasible = generateFeasibleSet(bodies, params);
|
||||
this._bodies = bodies;
|
||||
this._params = params;
|
||||
this._getRelativePositions();
|
||||
const feasible = this._generateFeasibleSet();
|
||||
sendResult(feasible);
|
||||
};
|
||||
function generateFeasibleSet(allowedBodies, params) {
|
||||
}
|
||||
_generateFeasibleSet() {
|
||||
let incFeasibleSet = [{
|
||||
sequence: [params.departureId],
|
||||
sequence: [this._params.departureId],
|
||||
resonant: 0,
|
||||
backLegs: 0
|
||||
backLegs: 0,
|
||||
backSpacingExceeded: false
|
||||
}];
|
||||
let tempSet = [];
|
||||
const feasibleSet = [];
|
||||
const relativePos = getRelativePositions(allowedBodies);
|
||||
const feasible = (seq) => checkSequenceFeasibility(seq, relativePos, params);
|
||||
const { maxEvalSequences } = config.flybySequence;
|
||||
const { maxEvalSequences } = this._config.flybySequence;
|
||||
outerloop: while (incFeasibleSet.length > 0) {
|
||||
for (const incSeq of incFeasibleSet) {
|
||||
for (const bodyId of allowedBodies) {
|
||||
for (const bodyId of this._bodies) {
|
||||
const tempSeq = {
|
||||
sequence: [...incSeq.sequence, bodyId],
|
||||
resonant: incSeq.resonant,
|
||||
backLegs: incSeq.backLegs
|
||||
backLegs: incSeq.backLegs,
|
||||
backSpacingExceeded: false
|
||||
};
|
||||
if (feasible(tempSeq)) {
|
||||
if (bodyId == params.destinationId) {
|
||||
this._updateSequenceInfo(tempSeq);
|
||||
if (this._isSequenceFeasible(tempSeq)) {
|
||||
if (bodyId == this._params.destinationId) {
|
||||
feasibleSet.push(tempSeq.sequence);
|
||||
if (feasibleSet.length >= maxEvalSequences)
|
||||
break outerloop;
|
||||
@@ -44,31 +49,36 @@
|
||||
}
|
||||
return feasibleSet;
|
||||
}
|
||||
function getRelativePositions(allowedBodies) {
|
||||
const relativePos = new Map();
|
||||
for (let i = 0; i < allowedBodies.length; ++i) {
|
||||
relativePos.set(allowedBodies[i], i);
|
||||
_getRelativePositions() {
|
||||
const maxId = Math.max(...this._bodies);
|
||||
const relativePos = Array(maxId + 1).fill(0);
|
||||
for (let i = 0; i < this._bodies.length; ++i) {
|
||||
relativePos[this._bodies[i]] = i;
|
||||
}
|
||||
return relativePos;
|
||||
this._relativePos = relativePos;
|
||||
}
|
||||
function checkSequenceFeasibility(seq, relativePos, params) {
|
||||
const numSwingBys = seq.sequence.length - 2;
|
||||
_isSequenceFeasible(info) {
|
||||
const params = this._params;
|
||||
const numSwingBys = info.sequence.length - 2;
|
||||
if (numSwingBys > params.maxSwingBys)
|
||||
return false;
|
||||
const posCurr = relativePos.get(seq.sequence[seq.sequence.length - 1]);
|
||||
const posPrev = relativePos.get(seq.sequence[seq.sequence.length - 2]);
|
||||
const toHigherOrbit = params.destinationId > params.departureId;
|
||||
if (isBackLeg(posCurr, posPrev, toHigherOrbit)) {
|
||||
const jumpSpacing = Math.abs(posPrev - posCurr);
|
||||
if (jumpSpacing > params.maxBackSpacing)
|
||||
return false;
|
||||
seq.backLegs++;
|
||||
}
|
||||
if (posCurr == posPrev)
|
||||
seq.resonant++;
|
||||
return seq.resonant <= params.maxResonant && seq.backLegs <= params.maxBackLegs;
|
||||
const resonancesOk = info.resonant <= this._params.maxResonant;
|
||||
const backLegsOk = info.backLegs <= this._params.maxBackLegs;
|
||||
return resonancesOk && backLegsOk && !info.backSpacingExceeded;
|
||||
}
|
||||
function isBackLeg(posCurr, posPrev, toHigherOrbit) {
|
||||
return (toHigherOrbit && posCurr < posPrev) || (!toHigherOrbit && posCurr > posPrev);
|
||||
_updateSequenceInfo(info) {
|
||||
const params = this._params;
|
||||
const { sequence } = info;
|
||||
const posCurr = this._relativePos[sequence[sequence.length - 1]];
|
||||
const posPrev = this._relativePos[sequence[sequence.length - 2]];
|
||||
const toHigherOrbit = params.destinationId > params.departureId;
|
||||
const isBackLeg = (toHigherOrbit && posCurr < posPrev) || (!toHigherOrbit && posCurr > posPrev);
|
||||
const spacing = Math.abs(posCurr - posPrev);
|
||||
info.backSpacingExceeded = isBackLeg && spacing > params.maxBackSpacing;
|
||||
if (isBackLeg)
|
||||
info.backLegs++;
|
||||
if (posCurr == posPrev)
|
||||
info.resonant++;
|
||||
}
|
||||
}
|
||||
initWorker(SequenceGenerator);
|
||||
|
||||
102
dist/dedicated-workers/trajectory-optimizer.js
vendored
102
dist/dedicated-workers/trajectory-optimizer.js
vendored
@@ -1,85 +1,78 @@
|
||||
"use strict";
|
||||
{
|
||||
importScripts("libs/common.js", "libs/evolution.js", "libs/math.js", "libs/physics.js", "libs/physics-3d.js", "libs/lambert.js", "libs/trajectory-calculator.js");
|
||||
let config;
|
||||
let system;
|
||||
let depAltitude;
|
||||
let sequence;
|
||||
let startDateMin;
|
||||
let startDateMax;
|
||||
let bestDeltaV;
|
||||
let bestSteps;
|
||||
onWorkerInitialize = data => {
|
||||
config = data.config;
|
||||
system = data.system;
|
||||
};
|
||||
onWorkerDataPass = data => {
|
||||
depAltitude = data.depAltitude;
|
||||
sequence = data.sequence;
|
||||
startDateMin = data.startDateMin;
|
||||
startDateMax = data.startDateMax;
|
||||
};
|
||||
onWorkerRun = input => {
|
||||
importScripts("libs/common.js", "libs/evolution.js", "libs/math.js", "libs/physics.js", "libs/physics-3d.js", "libs/lambert.js", "libs/trajectory-calculator.js");
|
||||
class TrajectoryOptimizer extends WorkerEnvironment {
|
||||
onWorkerInitialize(data) {
|
||||
this._config = data.config;
|
||||
this._system = data.system;
|
||||
}
|
||||
onWorkerDataPass(data) {
|
||||
this._depAltitude = data.depAltitude;
|
||||
this._sequence = data.sequence;
|
||||
this._startDateMin = data.startDateMin;
|
||||
this._startDateMax = data.startDateMax;
|
||||
}
|
||||
onWorkerRun(input) {
|
||||
const evaluate = (params) => this._evaluate(params);
|
||||
if (input.start) {
|
||||
bestDeltaV = Infinity;
|
||||
this._bestDeltaV = Infinity;
|
||||
const chunkSize = input.chunkEnd - input.chunkStart + 1;
|
||||
const popChunk = createRandomMGAPopulationChunk(chunkSize);
|
||||
const popChunk = this._createRandomMGAPopulationChunk(chunkSize);
|
||||
const dvChunk = populationChunkFitness(popChunk, evaluate);
|
||||
sendResult({
|
||||
bestSteps: bestSteps,
|
||||
bestDeltaV: bestDeltaV,
|
||||
bestSteps: this._bestSteps,
|
||||
bestDeltaV: this._bestDeltaV,
|
||||
fitChunk: dvChunk,
|
||||
popChunk: popChunk,
|
||||
});
|
||||
}
|
||||
else {
|
||||
const { crossoverProba, diffWeight } = config.trajectorySearch;
|
||||
const { crossoverProba, diffWeight } = this._config.trajectorySearch;
|
||||
const results = evolvePopulationChunk(input.population, input.deltaVs, input.chunkStart, input.chunkEnd, crossoverProba, diffWeight, evaluate);
|
||||
sendResult({
|
||||
...results,
|
||||
bestSteps: bestSteps,
|
||||
bestDeltaV: bestDeltaV
|
||||
bestSteps: this._bestSteps,
|
||||
bestDeltaV: this._bestDeltaV
|
||||
});
|
||||
}
|
||||
};
|
||||
function evaluate(params) {
|
||||
const trajectory = computeTrajectory(params);
|
||||
if (trajectory.totalDeltaV < bestDeltaV) {
|
||||
bestDeltaV = trajectory.totalDeltaV;
|
||||
bestSteps = trajectory.steps;
|
||||
}
|
||||
_evaluate(params) {
|
||||
const trajectory = this._computeTrajectory(params);
|
||||
if (trajectory.totalDeltaV < this._bestDeltaV) {
|
||||
this._bestDeltaV = trajectory.totalDeltaV;
|
||||
this._bestSteps = trajectory.steps;
|
||||
}
|
||||
return trajectory.totalDeltaV;
|
||||
}
|
||||
;
|
||||
function computeTrajectory(params) {
|
||||
_computeTrajectory(params) {
|
||||
const calculate = () => {
|
||||
const trajectory = new TrajectoryCalculator(system, config.trajectorySearch, sequence);
|
||||
trajectory.setParameters(depAltitude, startDateMin, startDateMax, params);
|
||||
const trajectory = new TrajectoryCalculator(this._system, this._config.trajectorySearch, this._sequence);
|
||||
trajectory.setParameters(this._depAltitude, this._startDateMin, this._startDateMax, params);
|
||||
trajectory.compute();
|
||||
return trajectory;
|
||||
};
|
||||
let trajectory = calculate();
|
||||
while (!trajectory.noError) {
|
||||
randomizeExistingAgent(params);
|
||||
this._randomizeExistingAgent(params);
|
||||
trajectory = calculate();
|
||||
}
|
||||
return trajectory;
|
||||
}
|
||||
function createRandomMGAPopulationChunk(chunkSize) {
|
||||
_createRandomMGAPopulationChunk(chunkSize) {
|
||||
const popChunk = [];
|
||||
for (let i = 0; i < chunkSize; i++) {
|
||||
popChunk.push(createRandomMGAAgent());
|
||||
popChunk.push(this._createRandomMGAAgent());
|
||||
}
|
||||
return popChunk;
|
||||
}
|
||||
function createRandomMGAAgent() {
|
||||
const dim = 4 * (sequence.length - 1) + 2;
|
||||
_createRandomMGAAgent() {
|
||||
const dim = 4 * (this._sequence.length - 1) + 2;
|
||||
const solution = Array(dim).fill(0);
|
||||
solution[0] = randomInInterval(startDateMin, startDateMax);
|
||||
solution[0] = randomInInterval(this._startDateMin, this._startDateMax);
|
||||
solution[1] = randomInInterval(1, 3);
|
||||
for (let i = 1; i < sequence.length; i++) {
|
||||
for (let i = 1; i < this._sequence.length; i++) {
|
||||
const j = 2 + (i - 1) * 4;
|
||||
const { legDuration, dsmOffset } = legDurationAndDSM(i);
|
||||
const { legDuration, dsmOffset } = this._legDurationAndDSM(i);
|
||||
solution[j] = legDuration;
|
||||
solution[j + 1] = dsmOffset;
|
||||
const { theta, phi } = randomPointOnSphereRing(0, Math.PI / 2);
|
||||
@@ -88,17 +81,17 @@
|
||||
}
|
||||
return solution;
|
||||
}
|
||||
function randomizeExistingAgent(agent) {
|
||||
const newAgent = createRandomMGAAgent();
|
||||
_randomizeExistingAgent(agent) {
|
||||
const newAgent = this._createRandomMGAAgent();
|
||||
for (let i = 0; i < agent.length; i++) {
|
||||
agent[i] = newAgent[i];
|
||||
}
|
||||
}
|
||||
function legDurationAndDSM(index) {
|
||||
const isResonant = sequence[index - 1] == sequence[index];
|
||||
const { dsmOffsetMin, dsmOffsetMax } = config.trajectorySearch;
|
||||
_legDurationAndDSM(index) {
|
||||
const isResonant = this._sequence[index - 1] == this._sequence[index];
|
||||
const { dsmOffsetMin, dsmOffsetMax } = this._config.trajectorySearch;
|
||||
if (isResonant) {
|
||||
const sideralPeriod = system[sequence[index]].orbit.sideralPeriod;
|
||||
const sideralPeriod = this._system[this._sequence[index]].orbit.sideralPeriod;
|
||||
const revs = randomInInterval(1, 4);
|
||||
const legDuration = revs * sideralPeriod;
|
||||
const minOffset = clamp((revs - 1) / revs, dsmOffsetMin, dsmOffsetMax);
|
||||
@@ -106,9 +99,9 @@
|
||||
return { legDuration, dsmOffset };
|
||||
}
|
||||
else {
|
||||
const body1 = system[sequence[index - 1]];
|
||||
const body2 = system[sequence[index]];
|
||||
const attractor = system[body1.orbiting];
|
||||
const body1 = this._system[this._sequence[index - 1]];
|
||||
const body2 = this._system[this._sequence[index]];
|
||||
const attractor = this._system[body1.orbiting];
|
||||
const period = getHohmannPeriod(body1, body2, attractor);
|
||||
const legDuration = randomInInterval(0.1, 1) * period;
|
||||
const dsmOffset = randomInInterval(dsmOffsetMin, dsmOffsetMax);
|
||||
@@ -116,3 +109,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
initWorker(TrajectoryOptimizer);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
let onWorkerInitialize: (data: any) => void = () => {};
|
||||
let onWorkerRun: (input?: any) => void = () => {};
|
||||
let onWorkerContinue: (input?: any) => void = () => {};
|
||||
let onWorkerStop: () => void = () => {};
|
||||
let onWorkerDataPass: (data: any) => void = () => {};
|
||||
class WorkerEnvironment {
|
||||
public onWorkerInitialize(data: any) {}
|
||||
public onWorkerRun(input?: any) {}
|
||||
public onWorkerContinue(input?: any) {}
|
||||
public onWorkerStop() {}
|
||||
public onWorkerDataPass(data: any) {}
|
||||
}
|
||||
|
||||
function postMessageSafe(msg: MessageFromWorker) {
|
||||
postMessage(msg);
|
||||
@@ -20,25 +22,28 @@ function sendResult(result: any) {
|
||||
postMessageSafe({label: "complete", result: result});
|
||||
}
|
||||
|
||||
onmessage = ({data}: MessageEvent<MessageToWorker>) => {
|
||||
switch(data.label) {
|
||||
case "initialize":
|
||||
onWorkerInitialize(data.config);
|
||||
postMessageSafe({label: "initialized"});
|
||||
break;
|
||||
case "run":
|
||||
onWorkerRun(data.input);
|
||||
break;
|
||||
case "continue":
|
||||
onWorkerContinue();
|
||||
break;
|
||||
case "stop":
|
||||
onWorkerStop();
|
||||
postMessageSafe({label: "stopped"});
|
||||
break;
|
||||
case "pass":
|
||||
onWorkerDataPass(data.data);
|
||||
postMessageSafe({label: "received"});
|
||||
break;
|
||||
function initWorker(Env: typeof WorkerEnvironment){
|
||||
const env = new Env();
|
||||
onmessage = ({data}: MessageEvent<MessageToWorker>) => {
|
||||
switch(data.label) {
|
||||
case "initialize":
|
||||
env.onWorkerInitialize(data.config);
|
||||
postMessageSafe({label: "initialized"});
|
||||
break;
|
||||
case "run":
|
||||
env.onWorkerRun(data.input);
|
||||
break;
|
||||
case "continue":
|
||||
env.onWorkerContinue();
|
||||
break;
|
||||
case "stop":
|
||||
env.onWorkerStop();
|
||||
postMessageSafe({label: "stopped"});
|
||||
break;
|
||||
case "pass":
|
||||
env.onWorkerDataPass(data.data);
|
||||
postMessageSafe({label: "received"});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @param popChunk The populatio chunk
|
||||
* @param popChunk The population chunk
|
||||
* @param fitness The fitness function
|
||||
* @returns The fitness value for each agent in the chunk.
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ function getGlobalVelocity2D(relativeVel: Vector2, bodyVel: Vector2) {
|
||||
* @param config The configuration data
|
||||
* @returns The velocity and positon when intersecting the body's orbit, or undefined if no intersection
|
||||
*/
|
||||
function calculateNextIntersection(pos0: Vector2, vel0: Vector2, body: IOrbitingBody, attractor: ICelestialBody, config: Config)
|
||||
function calculateNextIntersection(pos0: Vector2, vel0: Vector2, body: IOrbitingBody, attractor: ICelestialBody)
|
||||
: {pos: Vector2, vel: Vector2} | undefined {
|
||||
const mu = attractor.stdGravParam;
|
||||
|
||||
|
||||
@@ -1,40 +1,50 @@
|
||||
{ // <- hack to make typescript consider file scope only
|
||||
// Mozilla doesn't support module workers :(
|
||||
importScripts("libs/common.js", "libs/math.js", "libs/physics.js", "libs/physics-2d.js");
|
||||
// Mozilla doesn't support module workers :(
|
||||
importScripts("libs/common.js", "libs/math.js", "libs/physics.js", "libs/physics-2d.js");
|
||||
|
||||
class SequenceEvaluator extends WorkerEnvironment {
|
||||
private _system!: IOrbitingBody[];
|
||||
private _config!: Config;
|
||||
|
||||
let system: IOrbitingBody[];
|
||||
let config: Config;
|
||||
let current: Generator<undefined, (number | undefined)[], unknown> | null = null;
|
||||
private _sequences!: number[][];
|
||||
private _current: Generator<undefined, (number | undefined)[], unknown> | null = null;
|
||||
private _attractor!: ICelestialBody;
|
||||
|
||||
onWorkerInitialize = data => {
|
||||
system = data.system;
|
||||
config = data.config;
|
||||
};
|
||||
override onWorkerInitialize(data: any){
|
||||
this._system = data.system;
|
||||
this._config = data.config;
|
||||
}
|
||||
|
||||
onWorkerRun = input => {
|
||||
current = evaluateInChunks(input as number[][]);
|
||||
override onWorkerRun(input: any){
|
||||
this._sequences = input;
|
||||
|
||||
const {orbiting} = this._system[this._sequences[0][0]];
|
||||
this._attractor = this._system[orbiting];
|
||||
|
||||
this._current = this._evaluateSequenceChunks();
|
||||
sendProgress(0);
|
||||
};
|
||||
}
|
||||
|
||||
onWorkerContinue = () => {
|
||||
if(!current) return;
|
||||
const {done, value} = current.next();
|
||||
override onWorkerContinue(){
|
||||
if(!this._current) return;
|
||||
const {done, value} = this._current.next();
|
||||
if(done) sendResult(value);
|
||||
};
|
||||
}
|
||||
|
||||
onWorkerStop = () => current = null;
|
||||
override onWorkerStop(){
|
||||
this._current = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cost of each provided sequences and pauses regularly to send progression.
|
||||
* @param sequences The list of sequences to evaluate
|
||||
* @returns The cost of each sequences.
|
||||
*/
|
||||
function* evaluateInChunks(sequences: number[][]){
|
||||
private *_evaluateSequenceChunks(){
|
||||
const results = [];
|
||||
const {progressStep} = config.workers;
|
||||
|
||||
for(let i = 0; i < sequences.length; i++){
|
||||
results.push(evaluateSequence(sequences[i]));
|
||||
const {progressStep} = this._config.workers;
|
||||
|
||||
for(let i = 0; i < this._sequences.length; i++){
|
||||
results.push(this._evaluateSequence(this._sequences[i]));
|
||||
if(i % progressStep == 0) {
|
||||
sendProgress(i);
|
||||
yield;
|
||||
@@ -49,22 +59,22 @@
|
||||
* @param sequence The sequence to evaluate
|
||||
* @returns The estimated cost in velocity of the sequence
|
||||
*/
|
||||
function evaluateSequence(sequence: number[]){
|
||||
const bodies = getSequenceBodies(sequence);
|
||||
const attractor = system[bodies[0].orbiting];
|
||||
private _evaluateSequence(sequence: number[]){
|
||||
const bodies = this._bodySequenceOf(sequence);
|
||||
|
||||
// Calculate departure deltaV, considering simple Hohmann transfer
|
||||
const depDeltaV = hohmannTransferDeltaV(bodies[0], bodies[1]);
|
||||
|
||||
if(bodies.length == 2) {
|
||||
// Calculate relative velocity at encounter with the arrival body after transfer
|
||||
const depVel = depDeltaV + bodies[0].circularVel;
|
||||
const relativeArrVel = hohmannEncounterRelativeVel(bodies[0], bodies[1], depVel, attractor);
|
||||
const relativeArrVel = hohmannEncounterRelativeVel(bodies[0], bodies[1], depVel, this._attractor);
|
||||
return Math.abs(depDeltaV) + Math.abs(relativeArrVel);
|
||||
}
|
||||
|
||||
const statuses = generateInitialStatuses(bodies[0], depDeltaV);
|
||||
const statuses = this._generateInitialStatuses(bodies[0], depDeltaV);
|
||||
|
||||
const {maxEvalStatuses, radiusSamples} = config.flybySequence;
|
||||
const {maxEvalStatuses, radiusSamples} = this._config.flybySequence;
|
||||
let evalCount = 0;
|
||||
|
||||
while(statuses.length > 0) {
|
||||
@@ -76,7 +86,7 @@
|
||||
const status = statuses.pop() as OrbitalState2D;
|
||||
|
||||
const targetBody = bodies[status.target];
|
||||
const itsc = calculateNextIntersection(status.pos, status.vel, targetBody, attractor, config);
|
||||
const itsc = calculateNextIntersection(status.pos, status.vel, targetBody, this._attractor);
|
||||
if(itsc) {
|
||||
const targetBodyVel = getBodyVelocity2D(itsc.pos, targetBody);
|
||||
const arrivalVel = getRelativeVelocity2D(itsc.vel, targetBodyVel);
|
||||
@@ -105,16 +115,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
*
|
||||
* @param sequence The sequence to get the bodies from
|
||||
* @returns The corresponding sequence of bodies.
|
||||
*/
|
||||
function getSequenceBodies(sequence: number[]) {
|
||||
// We suppose the sequence bodies are orbiting around the same attractor
|
||||
private _bodySequenceOf(sequence: number[]) {
|
||||
const bodies: IOrbitingBody[] = [];
|
||||
for(const id of sequence){
|
||||
bodies.push(system[id]);
|
||||
bodies.push(this._system[id]);
|
||||
}
|
||||
return bodies;
|
||||
}
|
||||
@@ -125,10 +134,10 @@
|
||||
* @param depDeltaV The departure deltaV for direct Hohmann transfer
|
||||
* @returns The initial statuses to start the search from.
|
||||
*/
|
||||
function generateInitialStatuses(depBody: IOrbitingBody, depDeltaV: number){
|
||||
private _generateInitialStatuses(depBody: IOrbitingBody, depDeltaV: number){
|
||||
const statuses: OrbitalState2D[] = [];
|
||||
|
||||
const {initVelMaxScale, initVelSamples} = config.flybySequence;
|
||||
const {initVelMaxScale, initVelSamples} = this._config.flybySequence;
|
||||
for(let i = 0; i < initVelSamples; ++i) {
|
||||
const scale = (i / initVelSamples) * initVelMaxScale;
|
||||
statuses.push({
|
||||
@@ -140,4 +149,6 @@
|
||||
|
||||
return statuses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initWorker(SequenceEvaluator);
|
||||
@@ -1,48 +1,53 @@
|
||||
{
|
||||
importScripts("libs/common.js");
|
||||
|
||||
let config: Config;
|
||||
importScripts("libs/common.js");
|
||||
|
||||
onWorkerInitialize = data => config = data;
|
||||
class SequenceGenerator extends WorkerEnvironment {
|
||||
private _config!: Config;
|
||||
private _bodies!: number[];
|
||||
private _params!: SequenceParameters;
|
||||
private _relativePos!: number[];
|
||||
|
||||
onWorkerRun = input => {
|
||||
override onWorkerInitialize(data: any){
|
||||
this._config = data;
|
||||
}
|
||||
|
||||
override onWorkerRun(input: any){
|
||||
const {bodies, params} = input;
|
||||
const feasible = generateFeasibleSet(bodies, params);
|
||||
this._bodies = bodies;
|
||||
this._params = params;
|
||||
this._getRelativePositions();
|
||||
const feasible = this._generateFeasibleSet();
|
||||
sendResult(feasible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the feasible set of body sequences, i.e a list of body ids sequences that respect
|
||||
* the constraints of the user settings.
|
||||
* @param allowedBodies Bodies reachable according to the user settings
|
||||
* @param params Parameters of the sequences to gener
|
||||
* @returns The feasible set
|
||||
*/
|
||||
function generateFeasibleSet(allowedBodies: number[], params: SequenceParameters){
|
||||
private _generateFeasibleSet(){
|
||||
let incFeasibleSet: GeneratingSequence[] = [{
|
||||
sequence: [params.departureId],
|
||||
sequence: [this._params.departureId],
|
||||
resonant: 0,
|
||||
backLegs: 0
|
||||
backLegs: 0,
|
||||
backSpacingExceeded: false
|
||||
}];
|
||||
let tempSet: GeneratingSequence[] = [];
|
||||
const feasibleSet: number[][] = [];
|
||||
|
||||
const relativePos = getRelativePositions(allowedBodies);
|
||||
const feasible = (seq: GeneratingSequence) => checkSequenceFeasibility(seq, relativePos, params);
|
||||
|
||||
const {maxEvalSequences} = config.flybySequence;
|
||||
const {maxEvalSequences} = this._config.flybySequence;
|
||||
outerloop:
|
||||
while(incFeasibleSet.length > 0){
|
||||
for(const incSeq of incFeasibleSet){
|
||||
for(const bodyId of allowedBodies){
|
||||
for(const bodyId of this._bodies){
|
||||
const tempSeq = {
|
||||
sequence: [...incSeq.sequence, bodyId],
|
||||
resonant: incSeq.resonant,
|
||||
backLegs: incSeq.backLegs
|
||||
backLegs: incSeq.backLegs,
|
||||
backSpacingExceeded: false
|
||||
};
|
||||
|
||||
if(feasible(tempSeq)) {
|
||||
if(bodyId == params.destinationId) {
|
||||
this._updateSequenceInfo(tempSeq);
|
||||
if(this._isSequenceFeasible(tempSeq)) {
|
||||
if(bodyId == this._params.destinationId) {
|
||||
feasibleSet.push(tempSeq.sequence);
|
||||
if(feasibleSet.length >= maxEvalSequences)
|
||||
break outerloop;
|
||||
@@ -60,57 +65,56 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mapping to the relative position/order of bodies from the attractator.
|
||||
* @param allowedBodies Bodies allowed to be reached in the sequence, ordered according to their radiuses to the attractor
|
||||
* @returns The mapping to relative positions.
|
||||
* Calculates a mapping to the relative position/order of bodies from the attractator.
|
||||
*/
|
||||
function getRelativePositions(allowedBodies: number[]){
|
||||
private _getRelativePositions(){
|
||||
// We suppose that the bodies are sorted according to their semimajor axis radius
|
||||
const relativePos: Map<number, number> = new Map();
|
||||
for(let i = 0; i < allowedBodies.length; ++i){
|
||||
relativePos.set(allowedBodies[i], i);
|
||||
const maxId = Math.max(...this._bodies);
|
||||
const relativePos = Array<number>(maxId + 1).fill(0);
|
||||
for(let i = 0; i < this._bodies.length; ++i){
|
||||
relativePos[this._bodies[i]] = i;
|
||||
}
|
||||
return relativePos;
|
||||
this._relativePos = relativePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a (non fully) generated sequence respects the user contraints and updates the sequence informations.
|
||||
* @param seq A (non fully) generated sequence to check
|
||||
* @param relativePos The mapping of bodies' relative positions to the attractor
|
||||
* @param params The parameters constraining the generated sequences
|
||||
* Checks if a (non fully) generated sequence respects the user contraints.
|
||||
* @param info A (non fully) generated sequence to check
|
||||
* @returns Whether the sequence respects the constraints or not.
|
||||
*/
|
||||
function checkSequenceFeasibility(seq: GeneratingSequence, relativePos: Map<number, number>, params: SequenceParameters) {
|
||||
const numSwingBys = seq.sequence.length - 2;
|
||||
private _isSequenceFeasible(info: GeneratingSequence) {
|
||||
const params = this._params;
|
||||
|
||||
const numSwingBys = info.sequence.length - 2;
|
||||
if(numSwingBys > params.maxSwingBys)
|
||||
return false;
|
||||
|
||||
const posCurr = <number>relativePos.get(seq.sequence[seq.sequence.length - 1]);
|
||||
const posPrev = <number>relativePos.get(seq.sequence[seq.sequence.length - 2]);
|
||||
|
||||
const toHigherOrbit = params.destinationId > params.departureId;
|
||||
|
||||
if(isBackLeg(posCurr, posPrev, toHigherOrbit)) {
|
||||
const jumpSpacing = Math.abs(posPrev - posCurr);
|
||||
if(jumpSpacing > params.maxBackSpacing)
|
||||
return false;
|
||||
seq.backLegs++;
|
||||
}
|
||||
|
||||
if(posCurr == posPrev)
|
||||
seq.resonant++;
|
||||
|
||||
return seq.resonant <= params.maxResonant && seq.backLegs <= params.maxBackLegs;
|
||||
const resonancesOk = info.resonant <= this._params.maxResonant;
|
||||
const backLegsOk = info.backLegs <= this._params.maxBackLegs;
|
||||
|
||||
return resonancesOk && backLegsOk && !info.backSpacingExceeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a given leg is a back leg according to departure and destination bodies.
|
||||
* @param posCurr The relative position of the body reached at the end of the leg
|
||||
* @param posPrev The relative position of the starting body of the leg
|
||||
* @param toHigherOrbit Whether the destination body has a higher orbit than the departure body
|
||||
* @returns Whether this leg is a back leg or not.
|
||||
* Updates the informations about a generating sequence which received another step.
|
||||
* @param info The (non fully) generated sequence to update
|
||||
*/
|
||||
function isBackLeg(posCurr: number, posPrev: number, toHigherOrbit: boolean){
|
||||
return (toHigherOrbit && posCurr < posPrev) || (!toHigherOrbit && posCurr > posPrev);
|
||||
private _updateSequenceInfo(info: GeneratingSequence) {
|
||||
const params = this._params;
|
||||
|
||||
const {sequence} = info;
|
||||
const posCurr = this._relativePos[sequence[sequence.length - 1]];
|
||||
const posPrev = this._relativePos[sequence[sequence.length - 2]];
|
||||
|
||||
const toHigherOrbit = params.destinationId > params.departureId;
|
||||
const isBackLeg = (toHigherOrbit && posCurr < posPrev) || (!toHigherOrbit && posCurr > posPrev);
|
||||
|
||||
const spacing = Math.abs(posCurr - posPrev);
|
||||
info.backSpacingExceeded = isBackLeg && spacing > params.maxBackSpacing;
|
||||
|
||||
if(isBackLeg) info.backLegs++;
|
||||
if(posCurr == posPrev) info.resonant++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initWorker(SequenceGenerator);
|
||||
@@ -1,43 +1,46 @@
|
||||
{
|
||||
importScripts("libs/common.js", "libs/evolution.js", "libs/math.js", "libs/physics.js", "libs/physics-3d.js", "libs/lambert.js", "libs/trajectory-calculator.js");
|
||||
importScripts("libs/common.js", "libs/evolution.js", "libs/math.js", "libs/physics.js", "libs/physics-3d.js", "libs/lambert.js", "libs/trajectory-calculator.js");
|
||||
|
||||
let config: Config;
|
||||
let system: IOrbitingBody[];
|
||||
let depAltitude: number;
|
||||
let sequence: number[];
|
||||
let startDateMin: number;
|
||||
let startDateMax: number;
|
||||
class TrajectoryOptimizer extends WorkerEnvironment {
|
||||
private _config!: Config;
|
||||
private _system!: IOrbitingBody[];
|
||||
|
||||
let bestDeltaV: number;
|
||||
let bestSteps: TrajectoryStep[];
|
||||
private _depAltitude!: number;
|
||||
private _sequence!: number[];
|
||||
private _startDateMin!: number;
|
||||
private _startDateMax!: number;
|
||||
|
||||
onWorkerInitialize = data => {
|
||||
config = data.config;
|
||||
system = data.system;
|
||||
};
|
||||
private _bestDeltaV!: number;
|
||||
private _bestSteps!: TrajectoryStep[];
|
||||
|
||||
onWorkerDataPass = data => {
|
||||
depAltitude = data.depAltitude;
|
||||
sequence = data.sequence;
|
||||
startDateMin = data.startDateMin;
|
||||
startDateMax = data.startDateMax;
|
||||
};
|
||||
override onWorkerInitialize(data: any){
|
||||
this._config = data.config;
|
||||
this._system = data.system;
|
||||
}
|
||||
|
||||
override onWorkerDataPass(data: any){
|
||||
this._depAltitude = data.depAltitude;
|
||||
this._sequence = data.sequence;
|
||||
this._startDateMin = data.startDateMin;
|
||||
this._startDateMax = data.startDateMax;
|
||||
}
|
||||
|
||||
override onWorkerRun(input: any){
|
||||
const evaluate = (params: Agent) => this._evaluate(params);
|
||||
|
||||
onWorkerRun = input => {
|
||||
if(input.start) {
|
||||
bestDeltaV = Infinity;
|
||||
this._bestDeltaV = Infinity;
|
||||
const chunkSize = input.chunkEnd - input.chunkStart + 1;
|
||||
const popChunk = createRandomMGAPopulationChunk(chunkSize);
|
||||
const popChunk = this._createRandomMGAPopulationChunk(chunkSize);
|
||||
const dvChunk = populationChunkFitness(popChunk, evaluate);
|
||||
sendResult({
|
||||
bestSteps: bestSteps,
|
||||
bestDeltaV: bestDeltaV,
|
||||
bestSteps: this._bestSteps,
|
||||
bestDeltaV: this._bestDeltaV,
|
||||
fitChunk: dvChunk,
|
||||
popChunk: popChunk,
|
||||
});
|
||||
|
||||
} else {
|
||||
const {crossoverProba, diffWeight} = config.trajectorySearch;
|
||||
const {crossoverProba, diffWeight} = this._config.trajectorySearch;
|
||||
const results = evolvePopulationChunk(
|
||||
input.population,
|
||||
input.deltaVs,
|
||||
@@ -47,24 +50,23 @@
|
||||
);
|
||||
sendResult({
|
||||
...results,
|
||||
bestSteps: bestSteps,
|
||||
bestDeltaV: bestDeltaV
|
||||
bestSteps: this._bestSteps,
|
||||
bestDeltaV: this._bestDeltaV
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a trajectory cost and stores it if it's the best found so far.
|
||||
* @param params A trajectory paremeters (agent)
|
||||
* @returns The fitness (total deltaV) of the trajectory, used for the DE algorithm.
|
||||
*/
|
||||
function evaluate(params: Agent) {
|
||||
const trajectory = computeTrajectory(params);
|
||||
if(trajectory.totalDeltaV < bestDeltaV){
|
||||
bestDeltaV = trajectory.totalDeltaV;
|
||||
bestSteps = trajectory.steps;
|
||||
private _evaluate(params: Agent) {
|
||||
const trajectory = this._computeTrajectory(params);
|
||||
if(trajectory.totalDeltaV < this._bestDeltaV){
|
||||
this._bestDeltaV = trajectory.totalDeltaV;
|
||||
this._bestSteps = trajectory.steps;
|
||||
}
|
||||
|
||||
return trajectory.totalDeltaV;
|
||||
};
|
||||
|
||||
@@ -75,16 +77,16 @@
|
||||
* @param params A trajectory paremeters (agent)
|
||||
* @returns The calculated trajectory
|
||||
*/
|
||||
function computeTrajectory(params: Agent){
|
||||
private _computeTrajectory(params: Agent){
|
||||
const calculate = () => {
|
||||
const trajectory = new TrajectoryCalculator(system, config.trajectorySearch, sequence);
|
||||
trajectory.setParameters(depAltitude, startDateMin, startDateMax, params);
|
||||
const trajectory = new TrajectoryCalculator(this._system, this._config.trajectorySearch, this._sequence);
|
||||
trajectory.setParameters(this._depAltitude, this._startDateMin, this._startDateMax, params);
|
||||
trajectory.compute();
|
||||
return trajectory;
|
||||
}
|
||||
let trajectory = calculate();
|
||||
while(!trajectory.noError) {
|
||||
randomizeExistingAgent(params);
|
||||
this._randomizeExistingAgent(params);
|
||||
trajectory = calculate();
|
||||
}
|
||||
return trajectory;
|
||||
@@ -96,10 +98,10 @@
|
||||
* @param chunkSize The size of the population chunk to generate
|
||||
* @returns A random population chunk.
|
||||
*/
|
||||
function createRandomMGAPopulationChunk(chunkSize: number, ){
|
||||
private _createRandomMGAPopulationChunk(chunkSize: number, ){
|
||||
const popChunk: Agent[] = [];
|
||||
for(let i = 0; i < chunkSize; i++) {
|
||||
popChunk.push(createRandomMGAAgent());
|
||||
popChunk.push(this._createRandomMGAAgent());
|
||||
}
|
||||
return popChunk;
|
||||
}
|
||||
@@ -108,19 +110,19 @@
|
||||
* Generates a random agent representing an MGA trajectory based on the given sequence.
|
||||
* @returns A random trajectory agent
|
||||
*/
|
||||
function createRandomMGAAgent() {
|
||||
const dim = 4 * (sequence.length - 1) + 2;
|
||||
private _createRandomMGAAgent() {
|
||||
const dim = 4 * (this._sequence.length - 1) + 2;
|
||||
const solution: Agent = Array<number>(dim).fill(0);
|
||||
|
||||
// Initial ejection condition on departure body
|
||||
solution[0] = randomInInterval(startDateMin, startDateMax); // departure date
|
||||
solution[0] = randomInInterval(this._startDateMin, this._startDateMax); // departure date
|
||||
solution[1] = randomInInterval(1, 3); // ejection deltaV scale
|
||||
|
||||
// Interplanetary legs' parameters
|
||||
for(let i = 1; i < sequence.length; i++){
|
||||
for(let i = 1; i < this._sequence.length; i++){
|
||||
const j = 2 + (i - 1) * 4;
|
||||
|
||||
const {legDuration, dsmOffset} = legDurationAndDSM(i);
|
||||
const {legDuration, dsmOffset} = this._legDurationAndDSM(i);
|
||||
solution[j] = legDuration;
|
||||
solution[j+1] = dsmOffset;
|
||||
|
||||
@@ -136,8 +138,8 @@
|
||||
* Randomizes an existing agent instance.
|
||||
* @param agent The angent to randomize
|
||||
*/
|
||||
function randomizeExistingAgent(agent: Agent){
|
||||
const newAgent = createRandomMGAAgent();
|
||||
private _randomizeExistingAgent(agent: Agent){
|
||||
const newAgent = this._createRandomMGAAgent();
|
||||
for(let i = 0; i < agent.length; i++){
|
||||
agent[i] = newAgent[i];
|
||||
}
|
||||
@@ -147,12 +149,12 @@
|
||||
* @param index The index on the planetary sequence.
|
||||
* @returns A random leg duration and DSM offset
|
||||
*/
|
||||
function legDurationAndDSM(index: number){
|
||||
const isResonant = sequence[index-1] == sequence[index];
|
||||
const {dsmOffsetMin, dsmOffsetMax} = config.trajectorySearch;
|
||||
private _legDurationAndDSM(index: number){
|
||||
const isResonant = this._sequence[index-1] == this._sequence[index];
|
||||
const {dsmOffsetMin, dsmOffsetMax} = this._config.trajectorySearch;
|
||||
|
||||
if(isResonant) {
|
||||
const sideralPeriod = <number>system[sequence[index]].orbit.sideralPeriod;
|
||||
const sideralPeriod = <number>this._system[this._sequence[index]].orbit.sideralPeriod;
|
||||
|
||||
const revs = randomInInterval(1, 4);
|
||||
const legDuration = revs * sideralPeriod;
|
||||
@@ -163,10 +165,10 @@
|
||||
return {legDuration, dsmOffset};
|
||||
|
||||
} else {
|
||||
const body1 = system[sequence[index-1]];
|
||||
const body2 = system[sequence[index]];
|
||||
const body1 = this._system[this._sequence[index-1]];
|
||||
const body2 = this._system[this._sequence[index]];
|
||||
|
||||
const attractor = system[body1.orbiting];
|
||||
const attractor = this._system[body1.orbiting];
|
||||
const period = getHohmannPeriod(body1, body2, attractor);
|
||||
|
||||
const legDuration = randomInInterval(0.1, 1) * period;
|
||||
@@ -175,4 +177,6 @@
|
||||
return {legDuration, dsmOffset};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initWorker(TrajectoryOptimizer);
|
||||
3
src/types.d.ts
vendored
3
src/types.d.ts
vendored
@@ -146,7 +146,8 @@ type ProgressCallback = (progress: number, data?: any) => any;
|
||||
type GeneratingSequence = {
|
||||
sequence: number[],
|
||||
resonant: number,
|
||||
backLegs: number
|
||||
backLegs: number,
|
||||
backSpacingExceeded: boolean;
|
||||
};
|
||||
|
||||
type Agent = number[];
|
||||
|
||||
Reference in New Issue
Block a user