"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Simulation = void 0;
const EventEmitter_1 = require("../EventEmitter");
const NOOP_1 = require("../NOOP");
const ANIMATION_C_1 = require("./animation/ANIMATION_C");
const RK1C = 1;
const RK10 = [1];
const RK11 = [1];
const RK2C = 2;
const RK20 = [0, 1];
const RK21 = [1, 1];
const RK3C = 9;
const RK30 = [0, 1 / 2, 3 / 4];
const RK31 = [2, 3, 4];
const RK4C = 6;
const RK40 = [0, 0.5, 0.5, 1];
const RK41 = [1, 2, 2, 1];
const RKC = [RK1C, RK2C, RK3C, RK4C];
const RK = [
    [RK10, RK11],
    [RK20, RK21],
    [RK30, RK31],
    [RK40, RK41],
];
class Simulation extends EventEmitter_1.EventEmitter_ {
    constructor({ alpha = 0.25, alphaMin = 0.001, alphaTarget = 0, alphaDecay, velocityDecay = 0.2, n = 1, t = 3 / ANIMATION_C_1.ANIMATION_C, stability = 1, force = NOOP_1.NOOP, }) {
        super();
        this._paused = true;
        this._alpha = 1;
        this._stability = 1;
        this._n = 1;
        this._t = 1;
        this._force = NOOP_1.NOOP;
        this._tick = () => {
            this._alpha += (this._alphaTarget - this._alpha) * this._alphaDecay;
            if (this._alpha < this._alphaMin) {
                this.stop();
                this.emit('end');
                return;
            }
            const rk = {};
            const F = 0.75; // friction
            const T = this._t;
            // const order = this._stability
            const order = 4;
            const order_1 = order - 1;
            const RKOC = RKC[order_1];
            const RKO = RK[order_1];
            const RKO0 = RKO[0];
            const RKO1 = RKO[1];
            for (let i = 0; i < this._n; i++) {
                for (let j = 0; j < order; j++) {
                    this._force(this._alpha);
                    const k0 = RKO0[j] * T;
                    const k1 = (RKO1[j] * T) / RKOC;
                    for (const node_id in this._nodes) {
                        const node = this._nodes[node_id];
                        if (j === 0) {
                            const { x, y, vx, vy, ax, ay } = node;
                            rk[node_id] = { x, y, vx, vy, _vx: vx, _vy: vy, _ax: ax, _ay: ay };
                        }
                        const { ax, ay, fx, fy } = node;
                        const _rk = rk[node_id];
                        const { x, y, vx, vy, _vx, _vy, _ax, _ay } = _rk;
                        if (fx === undefined) {
                            const __x = x + _vx * k0;
                            const __vx = vx + _ax * k0;
                            const __ax = ax - F * __vx;
                            _rk._vx = __vx;
                            _rk._ax = __ax;
                            node._x = __x;
                            node.x += __vx * k1;
                            node.vx += __ax * k1;
                        }
                        else {
                            node.x = fx;
                            node.vx = 0;
                        }
                        if (j === order_1) {
                            node._x = node.x;
                        }
                        node.ax = 0;
                        if (fy === undefined) {
                            const __y = y + _vy * k0;
                            const __vy = vy + _ay * k0;
                            const __ay = ay - F * __vy;
                            _rk._vy = __vy;
                            _rk._ay = __ay;
                            node._y = __y;
                            node.y += __vy * k1;
                            node.vy += __ay * k1;
                        }
                        else {
                            node.y = fy;
                            node.vy = 0;
                        }
                        if (j === order_1) {
                            node._y = node.y;
                        }
                        node.ay = 0;
                    }
                }
            }
            this.emit('tick');
            this._frame = requestAnimationFrame(this._tick);
        };
        this._alphaDecay =
            alphaDecay === undefined ? 1 - Math.pow(alphaMin, 1 / 300) : alphaDecay;
        this._alpha = alpha;
        this._alphaMin = alphaMin;
        this._alphaTarget = alphaTarget;
        this._velocityDecay = velocityDecay;
        this._stability = stability;
        this._n = n;
        this._t = t;
        this._force = force;
    }
    start() {
        if (this._paused) {
            this._paused = false;
            this._frame = requestAnimationFrame(this._tick);
        }
    }
    restart() {
        this.start();
    }
    stop() {
        this._paused = true;
        cancelAnimationFrame(this._frame);
    }
    nodes(nodes) {
        if (nodes !== undefined) {
            this._nodes = nodes;
        }
        return this._nodes;
    }
    links(links) {
        if (links !== undefined) {
            this._links = links;
        }
        return this._links;
    }
    alphaDecay(alphaDecay) {
        if (alphaDecay !== undefined) {
            this._alphaDecay = alphaDecay;
        }
        return this._alphaDecay;
    }
    alphaMin(_alphaMin) {
        if (_alphaMin !== undefined) {
            this._alphaMin = _alphaMin;
        }
        return this._alphaMin;
    }
    alpha(alpha) {
        if (alpha !== undefined) {
            this._alpha = alpha;
        }
        return this._alpha;
    }
    velocityDecay(_velocityDecay) {
        if (_velocityDecay !== undefined) {
            this._velocityDecay = _velocityDecay;
        }
        return this._velocityDecay;
    }
    alphaTarget(_alphaTarget) {
        if (_alphaTarget !== undefined) {
            this._alphaTarget = _alphaTarget;
        }
        return this._alphaTarget;
    }
    stability(_stability) {
        if (_stability < 1 || _stability > 4) {
            throw new Error('simulation stability must be between 1 and 4, inclusive');
        }
        if (_stability !== undefined) {
            this._stability = _stability;
        }
        return this._stability;
    }
}
exports.Simulation = Simulation;
//# sourceMappingURL=simulation.js.map