UNPKG

min-cycles

Version:

Minimal cycle basis for a planar graph

124 lines (104 loc) 3.73 kB
'use strict'; 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; }