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