tapspace
Version:
A zoomable user interface lib for web apps
91 lines (78 loc) • 2.22 kB
JavaScript
module.exports = (spaces, tracker, backtracker, id, maxDepth) => {
// Find a frontier towards the parent for the given root space id.
// Finds ancestors and siblings but avoids finding children of the root.
//
// Parameters:
// spaces
// tracker
// a function (id, space) -> array of id
// backtracker
// a function (id) -> parentId
// id
// a string, the root space id
// maxDepth
// a number
//
// Return
// an array: { id, space, depth }
//
if (!spaces[id]) {
return []
}
if (maxDepth < 0) {
return []
}
// Collect results here.
const frontier = []
// Iteration cursors. Space ids. Their spaces must exist.
const cursors = [id]
// Track and limit depth.
const depths = [0]
// Track already visited to avoid propagating back-and-forth.
// Visiting means that it has been a cursor.
const visits = {}
visits[id] = true
while (cursors.length > 0) {
const cursor = cursors.pop()
const depth = depths.pop()
const cursorSpace = spaces[cursor]
// Assert cursorSpace exists.
// Prevent expansion to root's children.
let nextIds = [backtracker(cursor)]
if (cursor !== id) {
const childIds = tracker(cursor, cursorSpace)
nextIds = nextIds.concat(childIds)
}
// Avoid expanding where already visited.
nextIds = nextIds.filter((nid) => {
if (visits[nid]) {
return false
}
return true
})
// Prevent cursor being pushed to frontier multiple times.
let cursorInFrontier = false
// Find if all nexts exist
for (let i = 0; i < nextIds.length; i += 1) {
const nid = nextIds[i]
const nextSpace = spaces[nid]
if (nextSpace) {
// Continue search with the next if they are not too far.
if (depth < maxDepth) {
cursors.push(nid)
depths.push(depth + 1)
visits[nid] = true
}
} else if (!cursorInFrontier) {
// Not every children is open: cursor belongs to the frontier.
frontier.push({
id: cursor,
space: cursorSpace,
depth: depth
})
cursorInFrontier = true
}
}
}
return frontier
}