1const conversions = require('./conversions'); 2 3/* 4 This function routes a model to all other models. 5 6 all functions that are routed have a property `.conversion` attached 7 to the returned synthetic function. This property is an array 8 of strings, each with the steps in between the 'from' and 'to' 9 color models (inclusive). 10 11 conversions that are not possible simply are not included. 12*/ 13 14function buildGraph() { 15 const graph = {}; 16 // https://jsperf.com/object-keys-vs-for-in-with-closure/3 17 const models = Object.keys(conversions); 18 19 for (let len = models.length, i = 0; i < len; i++) { 20 graph[models[i]] = { 21 // http://jsperf.com/1-vs-infinity 22 // micro-opt, but this is simple. 23 distance: -1, 24 parent: null 25 }; 26 } 27 28 return graph; 29} 30 31// https://en.wikipedia.org/wiki/Breadth-first_search 32function deriveBFS(fromModel) { 33 const graph = buildGraph(); 34 const queue = [fromModel]; // Unshift -> queue -> pop 35 36 graph[fromModel].distance = 0; 37 38 while (queue.length) { 39 const current = queue.pop(); 40 const adjacents = Object.keys(conversions[current]); 41 42 for (let len = adjacents.length, i = 0; i < len; i++) { 43 const adjacent = adjacents[i]; 44 const node = graph[adjacent]; 45 46 if (node.distance === -1) { 47 node.distance = graph[current].distance + 1; 48 node.parent = current; 49 queue.unshift(adjacent); 50 } 51 } 52 } 53 54 return graph; 55} 56 57function link(from, to) { 58 return function (args) { 59 return to(from(args)); 60 }; 61} 62 63function wrapConversion(toModel, graph) { 64 const path = [graph[toModel].parent, toModel]; 65 let fn = conversions[graph[toModel].parent][toModel]; 66 67 let cur = graph[toModel].parent; 68 while (graph[cur].parent) { 69 path.unshift(graph[cur].parent); 70 fn = link(conversions[graph[cur].parent][cur], fn); 71 cur = graph[cur].parent; 72 } 73 74 fn.conversion = path; 75 return fn; 76} 77 78module.exports = function (fromModel) { 79 const graph = deriveBFS(fromModel); 80 const conversion = {}; 81 82 const models = Object.keys(graph); 83 for (let len = models.length, i = 0; i < len; i++) { 84 const toModel = models[i]; 85 const node = graph[toModel]; 86 87 if (node.parent === null) { 88 // No possible conversion, or this node is the source model. 89 continue; 90 } 91 92 conversion[toModel] = wrapConversion(toModel, graph); 93 } 94 95 return conversion; 96}; 97 98