Spaces:
Running
Running
const conversions = require('./conversions'); | |
/* | |
This function routes a model to all other models. | |
all functions that are routed have a property `.conversion` attached | |
to the returned synthetic function. This property is an array | |
of strings, each with the steps in between the 'from' and 'to' | |
color models (inclusive). | |
conversions that are not possible simply are not included. | |
*/ | |
function buildGraph() { | |
const graph = {}; | |
// https://jsperf.com/object-keys-vs-for-in-with-closure/3 | |
const models = Object.keys(conversions); | |
for (let len = models.length, i = 0; i < len; i++) { | |
graph[models[i]] = { | |
// http://jsperf.com/1-vs-infinity | |
// micro-opt, but this is simple. | |
distance: -1, | |
parent: null | |
}; | |
} | |
return graph; | |
} | |
// https://en.wikipedia.org/wiki/Breadth-first_search | |
function deriveBFS(fromModel) { | |
const graph = buildGraph(); | |
const queue = [fromModel]; // Unshift -> queue -> pop | |
graph[fromModel].distance = 0; | |
while (queue.length) { | |
const current = queue.pop(); | |
const adjacents = Object.keys(conversions[current]); | |
for (let len = adjacents.length, i = 0; i < len; i++) { | |
const adjacent = adjacents[i]; | |
const node = graph[adjacent]; | |
if (node.distance === -1) { | |
node.distance = graph[current].distance + 1; | |
node.parent = current; | |
queue.unshift(adjacent); | |
} | |
} | |
} | |
return graph; | |
} | |
function link(from, to) { | |
return function (args) { | |
return to(from(args)); | |
}; | |
} | |
function wrapConversion(toModel, graph) { | |
const path = [graph[toModel].parent, toModel]; | |
let fn = conversions[graph[toModel].parent][toModel]; | |
let cur = graph[toModel].parent; | |
while (graph[cur].parent) { | |
path.unshift(graph[cur].parent); | |
fn = link(conversions[graph[cur].parent][cur], fn); | |
cur = graph[cur].parent; | |
} | |
fn.conversion = path; | |
return fn; | |
} | |
module.exports = function (fromModel) { | |
const graph = deriveBFS(fromModel); | |
const conversion = {}; | |
const models = Object.keys(graph); | |
for (let len = models.length, i = 0; i < len; i++) { | |
const toModel = models[i]; | |
const node = graph[toModel]; | |
if (node.parent === null) { | |
// No possible conversion, or this node is the source model. | |
continue; | |
} | |
conversion[toModel] = wrapConversion(toModel, graph); | |
} | |
return conversion; | |
}; | |