|
import * as is from '../../is'; |
|
import { defaults } from '../../util'; |
|
|
|
const hierholzerDefaults = defaults({ |
|
root: undefined, |
|
directed: false |
|
}); |
|
|
|
let elesfn = ({ |
|
hierholzer: function( options ){ |
|
if (!is.plainObject(options)) { |
|
let args = arguments; |
|
options = { root: args[0], directed: args[1] }; |
|
} |
|
let { root, directed } = hierholzerDefaults(options); |
|
let eles = this; |
|
let dflag = false; |
|
let oddIn; |
|
let oddOut; |
|
let startVertex; |
|
if (root) startVertex = is.string(root) ? this.filter(root)[0].id() : root[0].id(); |
|
let nodes = {}; |
|
let edges = {}; |
|
|
|
if (directed) { |
|
eles.forEach(function(ele){ |
|
let id = ele.id(); |
|
if(ele.isNode()) { |
|
let ind = ele.indegree(true); |
|
let outd = ele.outdegree(true); |
|
let d1 = ind - outd; |
|
let d2 = outd - ind; |
|
if (d1 == 1) { |
|
if (oddIn) dflag = true; |
|
else oddIn = id; |
|
} else if (d2 == 1) { |
|
if (oddOut) dflag = true; |
|
else oddOut = id; |
|
} else if ((d2 > 1) || (d1 > 1)) { |
|
dflag = true; |
|
} |
|
nodes[id] = []; |
|
ele.outgoers().forEach(e => { |
|
if (e.isEdge()) nodes[id].push(e.id()); |
|
}); |
|
} else { |
|
edges[id] = [undefined, ele.target().id()]; |
|
} |
|
}); |
|
} else { |
|
eles.forEach(function(ele){ |
|
let id = ele.id(); |
|
if(ele.isNode()) { |
|
let d = ele.degree(true); |
|
if (d%2) { |
|
if (!oddIn) oddIn = id; |
|
else if (!oddOut) oddOut = id; |
|
else dflag = true; |
|
} |
|
nodes[id] = []; |
|
ele.connectedEdges().forEach(e => nodes[id].push(e.id())); |
|
} else { |
|
edges[id] = [ele.source().id(), ele.target().id()]; |
|
} |
|
}); |
|
} |
|
|
|
let result = { |
|
found: false, |
|
trail: undefined |
|
}; |
|
|
|
if (dflag) return result; |
|
else if (oddOut && oddIn) { |
|
if (directed) { |
|
if (startVertex && (oddOut != startVertex)) { |
|
return result; |
|
} |
|
startVertex = oddOut; |
|
} else { |
|
if (startVertex && (oddOut != startVertex) && (oddIn != startVertex)) { |
|
return result; |
|
} else if (!startVertex) { |
|
startVertex = oddOut; |
|
} |
|
} |
|
} else { |
|
if (!startVertex) startVertex = eles[0].id(); |
|
} |
|
|
|
const walk = (v) => { |
|
let currentNode = v; |
|
let subtour = [v]; |
|
let adj, adjTail, adjHead; |
|
while (nodes[currentNode].length) { |
|
adj = nodes[currentNode].shift(); |
|
adjTail = edges[adj][0]; |
|
adjHead = edges[adj][1]; |
|
if (currentNode != adjHead) { |
|
nodes[adjHead] = nodes[adjHead].filter(e => e != adj); |
|
currentNode = adjHead; |
|
} else if (!directed && (currentNode != adjTail)) { |
|
nodes[adjTail] = nodes[adjTail].filter(e => e != adj); |
|
currentNode = adjTail; |
|
} |
|
subtour.unshift(adj); |
|
subtour.unshift(currentNode); |
|
} |
|
return subtour; |
|
}; |
|
|
|
let trail = []; |
|
let subtour = []; |
|
subtour = walk(startVertex); |
|
while (subtour.length != 1) { |
|
if (nodes[subtour[0]].length == 0) { |
|
trail.unshift(eles.getElementById(subtour.shift())); |
|
trail.unshift(eles.getElementById(subtour.shift())); |
|
} else { |
|
subtour = walk(subtour.shift()).concat(subtour); |
|
} |
|
} |
|
trail.unshift(eles.getElementById(subtour.shift())); |
|
|
|
for (let d in nodes) { |
|
if (nodes[d].length) { |
|
return result; |
|
} |
|
} |
|
result.found = true; |
|
result.trail = this.spawn( trail, true ); |
|
return result; |
|
}, |
|
}); |
|
|
|
export default elesfn; |
|
|