UNPKG

webcola

Version:
345 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Locks = (function () { function Locks() { this.locks = {}; } Locks.prototype.add = function (id, x) { this.locks[id] = x; }; Locks.prototype.clear = function () { this.locks = {}; }; Locks.prototype.isEmpty = function () { for (var l in this.locks) return false; return true; }; Locks.prototype.apply = function (f) { for (var l in this.locks) { f(Number(l), this.locks[l]); } }; return Locks; }()); exports.Locks = Locks; var Descent = (function () { function Descent(x, D, G) { if (G === void 0) { G = null; } this.D = D; this.G = G; this.threshold = 0.0001; this.numGridSnapNodes = 0; this.snapGridSize = 100; this.snapStrength = 1000; this.scaleSnapByMaxH = false; this.random = new PseudoRandom(); this.project = null; this.x = x; this.k = x.length; var n = this.n = x[0].length; this.H = new Array(this.k); this.g = new Array(this.k); this.Hd = new Array(this.k); this.a = new Array(this.k); this.b = new Array(this.k); this.c = new Array(this.k); this.d = new Array(this.k); this.e = new Array(this.k); this.ia = new Array(this.k); this.ib = new Array(this.k); this.xtmp = new Array(this.k); this.locks = new Locks(); this.minD = Number.MAX_VALUE; var i = n, j; while (i--) { j = n; while (--j > i) { var d = D[i][j]; if (d > 0 && d < this.minD) { this.minD = d; } } } if (this.minD === Number.MAX_VALUE) this.minD = 1; i = this.k; while (i--) { this.g[i] = new Array(n); this.H[i] = new Array(n); j = n; while (j--) { this.H[i][j] = new Array(n); } this.Hd[i] = new Array(n); this.a[i] = new Array(n); this.b[i] = new Array(n); this.c[i] = new Array(n); this.d[i] = new Array(n); this.e[i] = new Array(n); this.ia[i] = new Array(n); this.ib[i] = new Array(n); this.xtmp[i] = new Array(n); } } Descent.createSquareMatrix = function (n, f) { var M = new Array(n); for (var i = 0; i < n; ++i) { M[i] = new Array(n); for (var j = 0; j < n; ++j) { M[i][j] = f(i, j); } } return M; }; Descent.prototype.offsetDir = function () { var _this = this; var u = new Array(this.k); var l = 0; for (var i = 0; i < this.k; ++i) { var x = u[i] = this.random.getNextBetween(0.01, 1) - 0.5; l += x * x; } l = Math.sqrt(l); return u.map(function (x) { return x *= _this.minD / l; }); }; Descent.prototype.computeDerivatives = function (x) { var _this = this; var n = this.n; if (n < 1) return; var i; var d = new Array(this.k); var d2 = new Array(this.k); var Huu = new Array(this.k); var maxH = 0; for (var u = 0; u < n; ++u) { for (i = 0; i < this.k; ++i) Huu[i] = this.g[i][u] = 0; for (var v = 0; v < n; ++v) { if (u === v) continue; var maxDisplaces = n; while (maxDisplaces--) { var sd2 = 0; for (i = 0; i < this.k; ++i) { var dx = d[i] = x[i][u] - x[i][v]; sd2 += d2[i] = dx * dx; } if (sd2 > 1e-9) break; var rd = this.offsetDir(); for (i = 0; i < this.k; ++i) x[i][v] += rd[i]; } var l = Math.sqrt(sd2); var D = this.D[u][v]; var weight = this.G != null ? this.G[u][v] : 1; if (weight > 1 && l > D || !isFinite(D)) { for (i = 0; i < this.k; ++i) this.H[i][u][v] = 0; continue; } if (weight > 1) { weight = 1; } var D2 = D * D; var gs = 2 * weight * (l - D) / (D2 * l); var l3 = l * l * l; var hs = 2 * -weight / (D2 * l3); if (!isFinite(gs)) console.log(gs); for (i = 0; i < this.k; ++i) { this.g[i][u] += d[i] * gs; Huu[i] -= this.H[i][u][v] = hs * (l3 + D * (d2[i] - sd2) + l * sd2); } } for (i = 0; i < this.k; ++i) maxH = Math.max(maxH, this.H[i][u][u] = Huu[i]); } var r = this.snapGridSize / 2; var g = this.snapGridSize; var w = this.snapStrength; var k = w / (r * r); var numNodes = this.numGridSnapNodes; for (var u = 0; u < numNodes; ++u) { for (i = 0; i < this.k; ++i) { var xiu = this.x[i][u]; var m = xiu / g; var f = m % 1; var q = m - f; var a = Math.abs(f); var dx = (a <= 0.5) ? xiu - q * g : (xiu > 0) ? xiu - (q + 1) * g : xiu - (q - 1) * g; if (-r < dx && dx <= r) { if (this.scaleSnapByMaxH) { this.g[i][u] += maxH * k * dx; this.H[i][u][u] += maxH * k; } else { this.g[i][u] += k * dx; this.H[i][u][u] += k; } } } } if (!this.locks.isEmpty()) { this.locks.apply(function (u, p) { for (i = 0; i < _this.k; ++i) { _this.H[i][u][u] += maxH; _this.g[i][u] -= maxH * (p[i] - x[i][u]); } }); } }; Descent.dotProd = function (a, b) { var x = 0, i = a.length; while (i--) x += a[i] * b[i]; return x; }; Descent.rightMultiply = function (m, v, r) { var i = m.length; while (i--) r[i] = Descent.dotProd(m[i], v); }; Descent.prototype.computeStepSize = function (d) { var numerator = 0, denominator = 0; for (var i = 0; i < this.k; ++i) { numerator += Descent.dotProd(this.g[i], d[i]); Descent.rightMultiply(this.H[i], d[i], this.Hd[i]); denominator += Descent.dotProd(d[i], this.Hd[i]); } if (denominator === 0 || !isFinite(denominator)) return 0; return 1 * numerator / denominator; }; Descent.prototype.reduceStress = function () { this.computeDerivatives(this.x); var alpha = this.computeStepSize(this.g); for (var i = 0; i < this.k; ++i) { this.takeDescentStep(this.x[i], this.g[i], alpha); } return this.computeStress(); }; Descent.copy = function (a, b) { var m = a.length, n = b[0].length; for (var i = 0; i < m; ++i) { for (var j = 0; j < n; ++j) { b[i][j] = a[i][j]; } } }; Descent.prototype.stepAndProject = function (x0, r, d, stepSize) { Descent.copy(x0, r); this.takeDescentStep(r[0], d[0], stepSize); if (this.project) this.project[0](x0[0], x0[1], r[0]); this.takeDescentStep(r[1], d[1], stepSize); if (this.project) this.project[1](r[0], x0[1], r[1]); for (var i = 2; i < this.k; i++) this.takeDescentStep(r[i], d[i], stepSize); }; Descent.mApply = function (m, n, f) { var i = m; while (i-- > 0) { var j = n; while (j-- > 0) f(i, j); } }; Descent.prototype.matrixApply = function (f) { Descent.mApply(this.k, this.n, f); }; Descent.prototype.computeNextPosition = function (x0, r) { var _this = this; this.computeDerivatives(x0); var alpha = this.computeStepSize(this.g); this.stepAndProject(x0, r, this.g, alpha); if (this.project) { this.matrixApply(function (i, j) { return _this.e[i][j] = x0[i][j] - r[i][j]; }); var beta = this.computeStepSize(this.e); beta = Math.max(0.2, Math.min(beta, 1)); this.stepAndProject(x0, r, this.e, beta); } }; Descent.prototype.run = function (iterations) { var stress = Number.MAX_VALUE, converged = false; while (!converged && iterations-- > 0) { var s = this.rungeKutta(); converged = Math.abs(stress / s - 1) < this.threshold; stress = s; } return stress; }; Descent.prototype.rungeKutta = function () { var _this = this; this.computeNextPosition(this.x, this.a); Descent.mid(this.x, this.a, this.ia); this.computeNextPosition(this.ia, this.b); Descent.mid(this.x, this.b, this.ib); this.computeNextPosition(this.ib, this.c); this.computeNextPosition(this.c, this.d); var disp = 0; this.matrixApply(function (i, j) { var x = (_this.a[i][j] + 2.0 * _this.b[i][j] + 2.0 * _this.c[i][j] + _this.d[i][j]) / 6.0, d = _this.x[i][j] - x; disp += d * d; _this.x[i][j] = x; }); return disp; }; Descent.mid = function (a, b, m) { Descent.mApply(a.length, a[0].length, function (i, j) { return m[i][j] = a[i][j] + (b[i][j] - a[i][j]) / 2.0; }); }; Descent.prototype.takeDescentStep = function (x, d, stepSize) { for (var i = 0; i < this.n; ++i) { x[i] = x[i] - stepSize * d[i]; } }; Descent.prototype.computeStress = function () { var stress = 0; for (var u = 0, nMinus1 = this.n - 1; u < nMinus1; ++u) { for (var v = u + 1, n = this.n; v < n; ++v) { var l = 0; for (var i = 0; i < this.k; ++i) { var dx = this.x[i][u] - this.x[i][v]; l += dx * dx; } l = Math.sqrt(l); var d = this.D[u][v]; if (!isFinite(d)) continue; var rl = d - l; var d2 = d * d; stress += rl * rl / d2; } } return stress; }; Descent.zeroDistance = 1e-10; return Descent; }()); exports.Descent = Descent; var PseudoRandom = (function () { function PseudoRandom(seed) { if (seed === void 0) { seed = 1; } this.seed = seed; this.a = 214013; this.c = 2531011; this.m = 2147483648; this.range = 32767; } PseudoRandom.prototype.getNext = function () { this.seed = (this.seed * this.a + this.c) % this.m; return (this.seed >> 16) / this.range; }; PseudoRandom.prototype.getNextBetween = function (min, max) { return min + this.getNext() * (max - min); }; return PseudoRandom; }()); exports.PseudoRandom = PseudoRandom; //# sourceMappingURL=descent.js.map