You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					98 lines
				
				2.2 KiB
			
		
		
			
		
	
	
					98 lines
				
				2.2 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								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;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 |