UNPKG

pick-distinct-colors

Version:

A collection of algorithms and utilities for analyzing and selecting maximally distinct colors. Now includes a unified pickDistinctColors API for easy color selection.

121 lines (101 loc) 4.59 kB
import { rgb2lab, deltaE, sortColors, mulberry32 } from '../utils/colorUtils.js'; export function maxSumDistancesGlobal(colors, selectCount) { return new Promise((resolve, reject) => { // Create worker code with utility functions in scope const workerCode = ` // Import utility functions from parent const rgb2lab = ${rgb2lab.toString()}; const deltaE = ${deltaE.toString()}; const sortColors = ${sortColors.toString()}; // Main worker function function maxSumDistancesGlobal(colors, selectCount) { const start = performance.now(); const labColors = colors.map(rgb2lab); // Calculate total distances from each color to all other colors const totalDistances = colors.map((_, i) => { let sum = 0; for (let j = 0; j < colors.length; j++) { if (i !== j) { sum += deltaE(labColors[i], labColors[j]); } } return { index: i, sum }; }); // Sort colors by their total distance to all other colors totalDistances.sort((a, b) => b.sum - a.sum); // Take the top selectCount colors that have the highest total distances const selectedIndices = totalDistances.slice(0, selectCount).map(item => item.index); const selectedColors = selectedIndices.map(i => colors[i]); return { colors: sortColors(selectedColors), time: performance.now() - start }; } // Worker message handler self.onmessage = function(e) { const { colors, selectCount } = e.data; try { const result = maxSumDistancesGlobal(colors, selectCount); self.postMessage({ type: 'complete', result }); } catch (error) { self.postMessage({ type: 'error', error: error.message }); } }; `; // Create blob and worker const blob = new Blob([workerCode], { type: 'application/javascript' }); const worker = new Worker(URL.createObjectURL(blob)); // Set up worker message handlers worker.onmessage = function(e) { if (e.data.type === 'complete') { resolve(e.data.result); } else if (e.data.type === 'error') { reject(new Error(e.data.error)); } worker.terminate(); }; worker.onerror = function(error) { reject(error); worker.terminate(); }; // Start the worker worker.postMessage({ colors, selectCount }); }); } export function maxSumDistancesSequential(colors, selectCount, seed) { console.log('Starting Maximum Sum (Sequential) calculation...'); const start = performance.now(); const labColors = colors.map(rgb2lab); const selected = []; const available = Array.from({length: colors.length}, (_, i) => i); // Use seeded PRNG if seed is provided const prng = typeof seed === 'number' ? mulberry32(seed) : Math.random; // Helper function to calculate total distance from a point to selected points function calculateTotalDistance(index) { return selected.reduce((sum, selectedIndex) => sum + deltaE(labColors[index], labColors[selectedIndex]), 0); } // Select first point randomly const firstIndex = Math.floor(prng() * available.length); selected.push(available[firstIndex]); available.splice(firstIndex, 1); // Select remaining points while (selected.length < selectCount) { let bestIndex = 0; let bestDistance = -Infinity; // Find point with maximum sum of distances to selected points for (let i = 0; i < available.length; i++) { const totalDistance = calculateTotalDistance(available[i]); if (totalDistance > bestDistance) { bestDistance = totalDistance; bestIndex = i; } } selected.push(available[bestIndex]); available.splice(bestIndex, 1); } return { colors: sortColors(selected.map(i => colors[i])), time: performance.now() - start }; }