98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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;
 | |
| };
 | |
| 
 |