You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
13 KiB
391 lines
13 KiB
5 years ago
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
var PositionStats = (function () {
|
||
|
function PositionStats(scale) {
|
||
|
this.scale = scale;
|
||
|
this.AB = 0;
|
||
|
this.AD = 0;
|
||
|
this.A2 = 0;
|
||
|
}
|
||
|
PositionStats.prototype.addVariable = function (v) {
|
||
|
var ai = this.scale / v.scale;
|
||
|
var bi = v.offset / v.scale;
|
||
|
var wi = v.weight;
|
||
|
this.AB += wi * ai * bi;
|
||
|
this.AD += wi * ai * v.desiredPosition;
|
||
|
this.A2 += wi * ai * ai;
|
||
|
};
|
||
|
PositionStats.prototype.getPosn = function () {
|
||
|
return (this.AD - this.AB) / this.A2;
|
||
|
};
|
||
|
return PositionStats;
|
||
|
}());
|
||
|
exports.PositionStats = PositionStats;
|
||
|
var Constraint = (function () {
|
||
|
function Constraint(left, right, gap, equality) {
|
||
|
if (equality === void 0) { equality = false; }
|
||
|
this.left = left;
|
||
|
this.right = right;
|
||
|
this.gap = gap;
|
||
|
this.equality = equality;
|
||
|
this.active = false;
|
||
|
this.unsatisfiable = false;
|
||
|
this.left = left;
|
||
|
this.right = right;
|
||
|
this.gap = gap;
|
||
|
this.equality = equality;
|
||
|
}
|
||
|
Constraint.prototype.slack = function () {
|
||
|
return this.unsatisfiable ? Number.MAX_VALUE
|
||
|
: this.right.scale * this.right.position() - this.gap
|
||
|
- this.left.scale * this.left.position();
|
||
|
};
|
||
|
return Constraint;
|
||
|
}());
|
||
|
exports.Constraint = Constraint;
|
||
|
var Variable = (function () {
|
||
|
function Variable(desiredPosition, weight, scale) {
|
||
|
if (weight === void 0) { weight = 1; }
|
||
|
if (scale === void 0) { scale = 1; }
|
||
|
this.desiredPosition = desiredPosition;
|
||
|
this.weight = weight;
|
||
|
this.scale = scale;
|
||
|
this.offset = 0;
|
||
|
}
|
||
|
Variable.prototype.dfdv = function () {
|
||
|
return 2.0 * this.weight * (this.position() - this.desiredPosition);
|
||
|
};
|
||
|
Variable.prototype.position = function () {
|
||
|
return (this.block.ps.scale * this.block.posn + this.offset) / this.scale;
|
||
|
};
|
||
|
Variable.prototype.visitNeighbours = function (prev, f) {
|
||
|
var ff = function (c, next) { return c.active && prev !== next && f(c, next); };
|
||
|
this.cOut.forEach(function (c) { return ff(c, c.right); });
|
||
|
this.cIn.forEach(function (c) { return ff(c, c.left); });
|
||
|
};
|
||
|
return Variable;
|
||
|
}());
|
||
|
exports.Variable = Variable;
|
||
|
var Block = (function () {
|
||
|
function Block(v) {
|
||
|
this.vars = [];
|
||
|
v.offset = 0;
|
||
|
this.ps = new PositionStats(v.scale);
|
||
|
this.addVariable(v);
|
||
|
}
|
||
|
Block.prototype.addVariable = function (v) {
|
||
|
v.block = this;
|
||
|
this.vars.push(v);
|
||
|
this.ps.addVariable(v);
|
||
|
this.posn = this.ps.getPosn();
|
||
|
};
|
||
|
Block.prototype.updateWeightedPosition = function () {
|
||
|
this.ps.AB = this.ps.AD = this.ps.A2 = 0;
|
||
|
for (var i = 0, n = this.vars.length; i < n; ++i)
|
||
|
this.ps.addVariable(this.vars[i]);
|
||
|
this.posn = this.ps.getPosn();
|
||
|
};
|
||
|
Block.prototype.compute_lm = function (v, u, postAction) {
|
||
|
var _this = this;
|
||
|
var dfdv = v.dfdv();
|
||
|
v.visitNeighbours(u, function (c, next) {
|
||
|
var _dfdv = _this.compute_lm(next, v, postAction);
|
||
|
if (next === c.right) {
|
||
|
dfdv += _dfdv * c.left.scale;
|
||
|
c.lm = _dfdv;
|
||
|
}
|
||
|
else {
|
||
|
dfdv += _dfdv * c.right.scale;
|
||
|
c.lm = -_dfdv;
|
||
|
}
|
||
|
postAction(c);
|
||
|
});
|
||
|
return dfdv / v.scale;
|
||
|
};
|
||
|
Block.prototype.populateSplitBlock = function (v, prev) {
|
||
|
var _this = this;
|
||
|
v.visitNeighbours(prev, function (c, next) {
|
||
|
next.offset = v.offset + (next === c.right ? c.gap : -c.gap);
|
||
|
_this.addVariable(next);
|
||
|
_this.populateSplitBlock(next, v);
|
||
|
});
|
||
|
};
|
||
|
Block.prototype.traverse = function (visit, acc, v, prev) {
|
||
|
var _this = this;
|
||
|
if (v === void 0) { v = this.vars[0]; }
|
||
|
if (prev === void 0) { prev = null; }
|
||
|
v.visitNeighbours(prev, function (c, next) {
|
||
|
acc.push(visit(c));
|
||
|
_this.traverse(visit, acc, next, v);
|
||
|
});
|
||
|
};
|
||
|
Block.prototype.findMinLM = function () {
|
||
|
var m = null;
|
||
|
this.compute_lm(this.vars[0], null, function (c) {
|
||
|
if (!c.equality && (m === null || c.lm < m.lm))
|
||
|
m = c;
|
||
|
});
|
||
|
return m;
|
||
|
};
|
||
|
Block.prototype.findMinLMBetween = function (lv, rv) {
|
||
|
this.compute_lm(lv, null, function () { });
|
||
|
var m = null;
|
||
|
this.findPath(lv, null, rv, function (c, next) {
|
||
|
if (!c.equality && c.right === next && (m === null || c.lm < m.lm))
|
||
|
m = c;
|
||
|
});
|
||
|
return m;
|
||
|
};
|
||
|
Block.prototype.findPath = function (v, prev, to, visit) {
|
||
|
var _this = this;
|
||
|
var endFound = false;
|
||
|
v.visitNeighbours(prev, function (c, next) {
|
||
|
if (!endFound && (next === to || _this.findPath(next, v, to, visit))) {
|
||
|
endFound = true;
|
||
|
visit(c, next);
|
||
|
}
|
||
|
});
|
||
|
return endFound;
|
||
|
};
|
||
|
Block.prototype.isActiveDirectedPathBetween = function (u, v) {
|
||
|
if (u === v)
|
||
|
return true;
|
||
|
var i = u.cOut.length;
|
||
|
while (i--) {
|
||
|
var c = u.cOut[i];
|
||
|
if (c.active && this.isActiveDirectedPathBetween(c.right, v))
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
Block.split = function (c) {
|
||
|
c.active = false;
|
||
|
return [Block.createSplitBlock(c.left), Block.createSplitBlock(c.right)];
|
||
|
};
|
||
|
Block.createSplitBlock = function (startVar) {
|
||
|
var b = new Block(startVar);
|
||
|
b.populateSplitBlock(startVar, null);
|
||
|
return b;
|
||
|
};
|
||
|
Block.prototype.splitBetween = function (vl, vr) {
|
||
|
var c = this.findMinLMBetween(vl, vr);
|
||
|
if (c !== null) {
|
||
|
var bs = Block.split(c);
|
||
|
return { constraint: c, lb: bs[0], rb: bs[1] };
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
Block.prototype.mergeAcross = function (b, c, dist) {
|
||
|
c.active = true;
|
||
|
for (var i = 0, n = b.vars.length; i < n; ++i) {
|
||
|
var v = b.vars[i];
|
||
|
v.offset += dist;
|
||
|
this.addVariable(v);
|
||
|
}
|
||
|
this.posn = this.ps.getPosn();
|
||
|
};
|
||
|
Block.prototype.cost = function () {
|
||
|
var sum = 0, i = this.vars.length;
|
||
|
while (i--) {
|
||
|
var v = this.vars[i], d = v.position() - v.desiredPosition;
|
||
|
sum += d * d * v.weight;
|
||
|
}
|
||
|
return sum;
|
||
|
};
|
||
|
return Block;
|
||
|
}());
|
||
|
exports.Block = Block;
|
||
|
var Blocks = (function () {
|
||
|
function Blocks(vs) {
|
||
|
this.vs = vs;
|
||
|
var n = vs.length;
|
||
|
this.list = new Array(n);
|
||
|
while (n--) {
|
||
|
var b = new Block(vs[n]);
|
||
|
this.list[n] = b;
|
||
|
b.blockInd = n;
|
||
|
}
|
||
|
}
|
||
|
Blocks.prototype.cost = function () {
|
||
|
var sum = 0, i = this.list.length;
|
||
|
while (i--)
|
||
|
sum += this.list[i].cost();
|
||
|
return sum;
|
||
|
};
|
||
|
Blocks.prototype.insert = function (b) {
|
||
|
b.blockInd = this.list.length;
|
||
|
this.list.push(b);
|
||
|
};
|
||
|
Blocks.prototype.remove = function (b) {
|
||
|
var last = this.list.length - 1;
|
||
|
var swapBlock = this.list[last];
|
||
|
this.list.length = last;
|
||
|
if (b !== swapBlock) {
|
||
|
this.list[b.blockInd] = swapBlock;
|
||
|
swapBlock.blockInd = b.blockInd;
|
||
|
}
|
||
|
};
|
||
|
Blocks.prototype.merge = function (c) {
|
||
|
var l = c.left.block, r = c.right.block;
|
||
|
var dist = c.right.offset - c.left.offset - c.gap;
|
||
|
if (l.vars.length < r.vars.length) {
|
||
|
r.mergeAcross(l, c, dist);
|
||
|
this.remove(l);
|
||
|
}
|
||
|
else {
|
||
|
l.mergeAcross(r, c, -dist);
|
||
|
this.remove(r);
|
||
|
}
|
||
|
};
|
||
|
Blocks.prototype.forEach = function (f) {
|
||
|
this.list.forEach(f);
|
||
|
};
|
||
|
Blocks.prototype.updateBlockPositions = function () {
|
||
|
this.list.forEach(function (b) { return b.updateWeightedPosition(); });
|
||
|
};
|
||
|
Blocks.prototype.split = function (inactive) {
|
||
|
var _this = this;
|
||
|
this.updateBlockPositions();
|
||
|
this.list.forEach(function (b) {
|
||
|
var v = b.findMinLM();
|
||
|
if (v !== null && v.lm < Solver.LAGRANGIAN_TOLERANCE) {
|
||
|
b = v.left.block;
|
||
|
Block.split(v).forEach(function (nb) { return _this.insert(nb); });
|
||
|
_this.remove(b);
|
||
|
inactive.push(v);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
return Blocks;
|
||
|
}());
|
||
|
exports.Blocks = Blocks;
|
||
|
var Solver = (function () {
|
||
|
function Solver(vs, cs) {
|
||
|
this.vs = vs;
|
||
|
this.cs = cs;
|
||
|
this.vs = vs;
|
||
|
vs.forEach(function (v) {
|
||
|
v.cIn = [], v.cOut = [];
|
||
|
});
|
||
|
this.cs = cs;
|
||
|
cs.forEach(function (c) {
|
||
|
c.left.cOut.push(c);
|
||
|
c.right.cIn.push(c);
|
||
|
});
|
||
|
this.inactive = cs.map(function (c) { c.active = false; return c; });
|
||
|
this.bs = null;
|
||
|
}
|
||
|
Solver.prototype.cost = function () {
|
||
|
return this.bs.cost();
|
||
|
};
|
||
|
Solver.prototype.setStartingPositions = function (ps) {
|
||
|
this.inactive = this.cs.map(function (c) { c.active = false; return c; });
|
||
|
this.bs = new Blocks(this.vs);
|
||
|
this.bs.forEach(function (b, i) { return b.posn = ps[i]; });
|
||
|
};
|
||
|
Solver.prototype.setDesiredPositions = function (ps) {
|
||
|
this.vs.forEach(function (v, i) { return v.desiredPosition = ps[i]; });
|
||
|
};
|
||
|
Solver.prototype.mostViolated = function () {
|
||
|
var minSlack = Number.MAX_VALUE, v = null, l = this.inactive, n = l.length, deletePoint = n;
|
||
|
for (var i = 0; i < n; ++i) {
|
||
|
var c = l[i];
|
||
|
if (c.unsatisfiable)
|
||
|
continue;
|
||
|
var slack = c.slack();
|
||
|
if (c.equality || slack < minSlack) {
|
||
|
minSlack = slack;
|
||
|
v = c;
|
||
|
deletePoint = i;
|
||
|
if (c.equality)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (deletePoint !== n &&
|
||
|
(minSlack < Solver.ZERO_UPPERBOUND && !v.active || v.equality)) {
|
||
|
l[deletePoint] = l[n - 1];
|
||
|
l.length = n - 1;
|
||
|
}
|
||
|
return v;
|
||
|
};
|
||
|
Solver.prototype.satisfy = function () {
|
||
|
if (this.bs == null) {
|
||
|
this.bs = new Blocks(this.vs);
|
||
|
}
|
||
|
this.bs.split(this.inactive);
|
||
|
var v = null;
|
||
|
while ((v = this.mostViolated()) && (v.equality || v.slack() < Solver.ZERO_UPPERBOUND && !v.active)) {
|
||
|
var lb = v.left.block, rb = v.right.block;
|
||
|
if (lb !== rb) {
|
||
|
this.bs.merge(v);
|
||
|
}
|
||
|
else {
|
||
|
if (lb.isActiveDirectedPathBetween(v.right, v.left)) {
|
||
|
v.unsatisfiable = true;
|
||
|
continue;
|
||
|
}
|
||
|
var split = lb.splitBetween(v.left, v.right);
|
||
|
if (split !== null) {
|
||
|
this.bs.insert(split.lb);
|
||
|
this.bs.insert(split.rb);
|
||
|
this.bs.remove(lb);
|
||
|
this.inactive.push(split.constraint);
|
||
|
}
|
||
|
else {
|
||
|
v.unsatisfiable = true;
|
||
|
continue;
|
||
|
}
|
||
|
if (v.slack() >= 0) {
|
||
|
this.inactive.push(v);
|
||
|
}
|
||
|
else {
|
||
|
this.bs.merge(v);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
Solver.prototype.solve = function () {
|
||
|
this.satisfy();
|
||
|
var lastcost = Number.MAX_VALUE, cost = this.bs.cost();
|
||
|
while (Math.abs(lastcost - cost) > 0.0001) {
|
||
|
this.satisfy();
|
||
|
lastcost = cost;
|
||
|
cost = this.bs.cost();
|
||
|
}
|
||
|
return cost;
|
||
|
};
|
||
|
Solver.LAGRANGIAN_TOLERANCE = -1e-4;
|
||
|
Solver.ZERO_UPPERBOUND = -1e-10;
|
||
|
return Solver;
|
||
|
}());
|
||
|
exports.Solver = Solver;
|
||
|
function removeOverlapInOneDimension(spans, lowerBound, upperBound) {
|
||
|
var vs = spans.map(function (s) { return new Variable(s.desiredCenter); });
|
||
|
var cs = [];
|
||
|
var n = spans.length;
|
||
|
for (var i = 0; i < n - 1; i++) {
|
||
|
var left = spans[i], right = spans[i + 1];
|
||
|
cs.push(new Constraint(vs[i], vs[i + 1], (left.size + right.size) / 2));
|
||
|
}
|
||
|
var leftMost = vs[0], rightMost = vs[n - 1], leftMostSize = spans[0].size / 2, rightMostSize = spans[n - 1].size / 2;
|
||
|
var vLower = null, vUpper = null;
|
||
|
if (lowerBound) {
|
||
|
vLower = new Variable(lowerBound, leftMost.weight * 1000);
|
||
|
vs.push(vLower);
|
||
|
cs.push(new Constraint(vLower, leftMost, leftMostSize));
|
||
|
}
|
||
|
if (upperBound) {
|
||
|
vUpper = new Variable(upperBound, rightMost.weight * 1000);
|
||
|
vs.push(vUpper);
|
||
|
cs.push(new Constraint(rightMost, vUpper, rightMostSize));
|
||
|
}
|
||
|
var solver = new Solver(vs, cs);
|
||
|
solver.solve();
|
||
|
return {
|
||
|
newCenters: vs.slice(0, spans.length).map(function (v) { return v.position(); }),
|
||
|
lowerBound: vLower ? vLower.position() : leftMost.position() - leftMostSize,
|
||
|
upperBound: vUpper ? vUpper.position() : rightMost.position() + rightMostSize
|
||
|
};
|
||
|
}
|
||
|
exports.removeOverlapInOneDimension = removeOverlapInOneDimension;
|
||
|
//# sourceMappingURL=vpsc.js.map
|