| 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; |
| }; |
| |