function rgbToCmyk({ r, g, b }, maxChannels = 2, step = 0.01, difference = d3.differenceCiede2000) {
if (maxChannels < 1 || maxChannels > 4 || !Number.isInteger(maxChannels)) {
throw new Error("maxChannels must be an integer from one through 4");
}
const precision = -Math.log10(step);
if (maxChannels === 4) {
return rgbToFullCmyk({ r, g, b }, precision);
}
const channels = ["c", "m", "y", "k"];
const R = r / 255;
const G = g / 255;
const B = b / 255;
let bestDistance = Infinity;
let bestCMYK = { c: 0, m: 0, y: 0, k: 0 };
const combinations = getCombinations(channels, maxChannels);
for (const combo of combinations) {
const valueSpace = getValues(combo.length, step);
for (const values of valueSpace) {
const candidate = Object.fromEntries(channels.map(ch => [ch, 0]));
combo.forEach((ch, i) => {
candidate[ch] = values[i];
});
const dist = difference(
rgbToString(cmykToRgb(candidate)),
rgbToString({r, g, b})
);
if (dist < bestDistance) {
bestDistance = dist;
bestCMYK = { ...candidate };
}
}
}
return Object.fromEntries(
Object.entries(bestCMYK).map(([ch, v]) => [ch, +v.toFixed(precision)])
);
}