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