min-cycles
Version:
Minimal cycle basis for a planar graph
124 lines (104 loc) • 3.73 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
exports.extract_cycles = extract_cycles;
function extract_cycles(vertices, edges) {
var cycles = [];
while (vertices.length > 0) {
var v = leftBottomVertex(vertices),
walk = reduceWalk(closedWalkFrom(v));
if (walk.length > 2) cycles.push(walk);
removeEdge(walk[0], walk[1]);
vertices = removeFilamentAt(walk[0], vertices);
vertices = removeFilamentAt(walk[1], vertices);
};
return cycles;
}
;
function leftBottomVertex(vertices) {
return vertices.reduce(function (m, v) {
var dx = v.x - m.x;
if (dx < 0) return v;
if (dx > 0) return m;
return v.y - m.y < 0 ? m : v;
});
}
function closedWalkFrom(v) {
var walk = [],
curr = v,
prev = undefined;
do {
walk.push(curr);
var _getNext = getNext(curr, prev);
var _getNext2 = _slicedToArray(_getNext, 2);
curr = _getNext2[0];
prev = _getNext2[1];
} while (curr !== v);
return walk;
}
function reduceWalk(w) {
for (var i = 1; i < w.length; i++) {
var idup = w.lastIndexOf(w[i]);
if (idup > i) w.splice(i + 1, idup - i);
}
return w;
}
function withoutVertex(v, vertices) {
return vertices.filter(function (vi) {
return vi !== v;
});
}
function removeEdge(v1, v2) {
v1.adj = withoutVertex(v2, v1.adj);
v2.adj = withoutVertex(v1, v2.adj);
}
function removeFilamentAt(v, vertices) {
var current = v,
next = undefined;
while (current && current.adj.length < 2) {
vertices = withoutVertex(current, vertices);
next = current.adj[0];
if (next) removeEdge(current, next);
current = next;
}
return vertices;
}
function getNext(v, prev) {
var next = v.adj.length == 1 ? v.adj[0] : best_by_kind(prev, v, prev ? 'ccw' : 'cw');
return [next, v];
}
function best_by_kind(v_prev, v_curr, kind) {
var d_curr = undefined,
adj = v_curr.adj;
if (v_prev) {
d_curr = vsub(v_curr, v_prev);
adj = withoutVertex(v_prev, adj);
} else {
d_curr = { x: 0, y: -1 };
}
return adj.reduce(function (v_so_far, v) {
return better_by_kind(v, v_so_far, v_curr, d_curr, kind);
});
};
function better_by_kind(v, v_so_far, v_curr, d_curr, kind) {
var d = vsub(v, v_curr),
d_so_far = vsub(v_so_far, v_curr),
is_convex = dot_perp(d_so_far, d_curr) > 0,
curr2v = dot_perp(d_curr, d),
vsf2v = dot_perp(d_so_far, d),
v_is_better = undefined;
if (kind == 'cw') {
v_is_better = is_convex && (curr2v >= 0 || vsf2v >= 0) || !is_convex && curr2v >= 0 && vsf2v >= 0;
} else {
v_is_better = !is_convex && (curr2v < 0 || vsf2v < 0) || is_convex && curr2v < 0 && vsf2v < 0;
}
return v_is_better ? v : v_so_far;
};
function vsub(a, b) {
return { x: a.x - b.x, y: a.y - b.y };
}
function dot_perp(a, b) {
return a.x * b.y - b.x * a.y;
}
;