mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2026-01-18 15:48:03 -08:00
- Reimplemented 2D orbital mechanics functions in physics-2d.ts and encaspulated inside a `Physics2D` namespace. - Deleted physics.ts and moved the required functions into physics-3d.ts Further cleaning and reimplemntation will be done on the physics-3d.ts content. - Forced consistency on error handling : every raised error throws an `Error` object. - Commented sections relative to 2D physics and sequence generation.
153 lines
5.1 KiB
JavaScript
153 lines
5.1 KiB
JavaScript
import { mergeArrayChunks, splitArrayInChunks } from "./array.js";
|
|
export class ComputeWorker extends Worker {
|
|
constructor(source) {
|
|
super(source);
|
|
}
|
|
initialize(config) {
|
|
return new Promise((resolve, reject) => {
|
|
this.onerror = reject;
|
|
this.onmessage = ({ data }) => {
|
|
if (data.label == "initialized")
|
|
resolve(true);
|
|
else
|
|
reject(`WORKER RESPONSE LABEL ERROR: "${data.label}"`);
|
|
};
|
|
this.postMessageSafe({ label: "initialize", config: config });
|
|
});
|
|
}
|
|
run(input, onProgress) {
|
|
return new Promise((resolve, reject) => {
|
|
this.onerror = reject;
|
|
this.onmessage = ({ data }) => {
|
|
switch (data.label) {
|
|
case "complete":
|
|
this.onmessage = null;
|
|
resolve(data.result);
|
|
break;
|
|
case "stopped":
|
|
this.onmessage = null;
|
|
reject("WORKER CANCELLED");
|
|
break;
|
|
case "progress":
|
|
if (onProgress)
|
|
onProgress(data.progress, data.data);
|
|
this.postMessageSafe({ label: "continue" });
|
|
break;
|
|
case "debug":
|
|
console.log(...data.data);
|
|
break;
|
|
default:
|
|
reject(`WORKER RESPONSE LABEL ERROR: "${data.label}"`);
|
|
break;
|
|
}
|
|
};
|
|
this.postMessageSafe({ label: "run", input: input });
|
|
});
|
|
}
|
|
passData(data) {
|
|
return new Promise((resolve, reject) => {
|
|
this.onerror = reject;
|
|
this.onmessage = ({ data }) => {
|
|
if (data.label == "received") {
|
|
resolve(true);
|
|
}
|
|
else {
|
|
reject(`WORKER RESPONSE LABEL ERROR: "${data.label}"`);
|
|
}
|
|
};
|
|
this.postMessageSafe({ label: "pass", data: data });
|
|
});
|
|
}
|
|
postMessageSafe(msg) {
|
|
this.postMessage(msg);
|
|
}
|
|
}
|
|
export class WorkerPool {
|
|
constructor(source, config) {
|
|
this.config = config;
|
|
this._workers = [];
|
|
this._usedWorkers = 0;
|
|
this.progressions = [];
|
|
const { hardwareConcurrency } = navigator;
|
|
const maxWorkers = hardwareConcurrency ? hardwareConcurrency : 4;
|
|
for (let i = 0; i < maxWorkers; i++) {
|
|
this._workers.push(new ComputeWorker(source));
|
|
}
|
|
}
|
|
get workersCount() {
|
|
return this._workers.length;
|
|
}
|
|
get usedWorkers() {
|
|
return this._usedWorkers;
|
|
}
|
|
get totalProgress() {
|
|
let sum = 0;
|
|
for (const progress of this.progressions) {
|
|
sum += progress;
|
|
}
|
|
return sum;
|
|
}
|
|
cancel() {
|
|
for (const worker of this._workers) {
|
|
worker.postMessageSafe({ label: "stop" });
|
|
}
|
|
}
|
|
terminate() {
|
|
for (const worker of this._workers) {
|
|
worker.terminate();
|
|
}
|
|
}
|
|
initialize(config) {
|
|
const promises = [];
|
|
for (const worker of this._workers) {
|
|
promises.push(worker.initialize(config));
|
|
}
|
|
return Promise.all(promises);
|
|
}
|
|
async runPool(inputs, onProgress) {
|
|
if (inputs.length > this.workersCount) {
|
|
throw new Error("More inputs than workers in the worker pool.");
|
|
}
|
|
this.progressions = [];
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
this.progressions.push(0);
|
|
}
|
|
const promises = [];
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
promises.push(this.runWorker(i, inputs[i], onProgress));
|
|
}
|
|
return Promise.all(promises);
|
|
}
|
|
async runPoolChunked(inputs, splitLimit, onProgress) {
|
|
this.optimizeUsedWorkersCount(inputs.length, splitLimit);
|
|
const inputChunks = splitArrayInChunks(inputs, this._usedWorkers);
|
|
const resultChunks = await this.runPool(inputChunks, onProgress);
|
|
return mergeArrayChunks(resultChunks);
|
|
}
|
|
passData(data) {
|
|
const promises = [];
|
|
for (let i = 0; i < this._usedWorkers; i++) {
|
|
promises.push(this._workers[i].passData(data));
|
|
}
|
|
return Promise.all(promises);
|
|
}
|
|
optimizeUsedWorkersCount(inputSize, splitLimit) {
|
|
let usedWorkers = 1;
|
|
while (Math.floor(inputSize / usedWorkers) > splitLimit && usedWorkers < this.workersCount) {
|
|
usedWorkers++;
|
|
}
|
|
this._usedWorkers = usedWorkers;
|
|
return usedWorkers;
|
|
}
|
|
runWorker(id, input, onProgress) {
|
|
const worker = this._workers[id];
|
|
const onWorkerProgress = (progress, data) => {
|
|
this.progressions[id] = progress;
|
|
if (onProgress)
|
|
onProgress(this.totalProgress, data);
|
|
worker.postMessageSafe({ label: "continue" });
|
|
};
|
|
return worker.run(input, onWorkerProgress);
|
|
}
|
|
}
|