Algorithm to find which two colors mix to form a third color (in JavaScript)
Asked Answered
H

1

4

I am looking for a library/algorithm/technique to take the 3 additive or subtractive colors and determine what combination of two results in some color x.

This is not a correct example (as I don't know much about color theory yet), but this is to illustrate the point. Say you have a color like greenish brown #45512b. The problem to solve is figure out -- for the two systems (additive and subtractive) -- the pair of colors that will generate greenish brown (the given color).

So you have the value "greenish brown" #45512b, and want to know what paints mix together to form this color. Maybe it is #fff123 and #bbb456 that combine subtractively to form greenish brown (just making this up). Then for additive mixing, figuring out the two shades of light of the 3 base colors of light used in computational models that combine to form greenish brown.

Basically just looking for this:

function additiveComponents(color) {
  return [ a, b ]
}

function subtractiveComponents(color) {
  return [ a, b ]
}

The input color doesn't need to be in hex format, any of the color spaces would work. But in the end I would like to convert the two output values back to a hex value.

One naive way I could imagine doing this is to take the reverse algorithm (algorithm to mix two colors into a third color), and try every combination of colors until you find the right one. But that would be super inefficient, wondering if there is a better way or standard technique.

Heresy answered 5/6, 2019 at 4:40 Comment(5)
Am I missing something, but are there not a multitude of colors that will add to create a single color?Vermicide
In any case - does xolor work for you?Vermicide
I have no idea lol, but I could see that.Heresy
Nice! It has this: " xolorObject.add(otherColor) - Returns the additive mix of the two colors. xolorObject.subtractive(otherColor) - Returns the subtractive mix of the two colors." However, still the same problem, how to do the reverse of that operation.Heresy
Alas, it doesn't have the functionality I'm looking for :/Heresy
V
2

use RGB color model. I am assuming you got 8 bit per channel color c0=(r0,g0,b0) and want to know c1,c2 that mix back to c0.

Additive mixing (light sources)

  1. chose one color c1

    to be able to mix up to c0 the c1 must be less or equal to c0 on per channel bases. So for example random lesser color:

    r1 = Random(r0);
    g1 = Random(g0);
    b1 = Random(b0);
    
  2. compute the missing color c2

    simply use the additive logic:

    c0 = c1 + c2
    

    so

    c2 = c0 - c1
    
    r2 = r0 - r1
    g2 = g0 - g1
    b2 = b0 - b1
    

Substractive mixing (filters, paint colors)

  1. chose one color c1

    this time c1 must be c1>=c0 so again random such color example:

    r1 = r0 + Random(255-r0);
    g1 = g0 + Random(255-g0);
    b1 = b0 + Random(255-b0);
    
  2. compute the missing color c2

    simply use the substractive logic:

    c0 = c1 - c2
    

    so

    c2 = c1 - c0
    
    r2 = r1 - r0
    g2 = g1 - g0
    b2 = b1 - b0
    

[Edit1] example

I just encoded a simple C++/VCL app to test this. So I got 3 sscrollbars to chose RGB of target color c0 and then some panels to show the c1,c2 and their mix to visually verify its working as should. Here the code:

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int _r=0; // channel order
const int _g=1;
const int _b=2;
const int _a=3;
union color
    {
    BYTE db[4]; // channel access
    DWORD dd;   // all 32 bit of color
    TColor c;   // VCL/GDI color  (you can ignore this)
    };
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    sb_rgbChange(this);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::sb_rgbChange(TObject *Sender)
    {
    int i;
    color c0,c1,c2,c;
    Randomize();
    // get c0 color from R,G,B sliders
    c0.db[_r]=255-sb_r->Position;
    c0.db[_g]=255-sb_g->Position;
    c0.db[_b]=255-sb_b->Position;
    c0.db[_a]=0;
    pan_c0->Color=c0.c;     // just show color on some panel
    // additive
    for (i=0;i<3;i++) c1.db[i]=Random(c0.db[i]);  c1.db[_a]=0;  // generate c1 <= c0
    for (i=0;i<3;i++) c2.db[i]=c0.db[i]-c1.db[i]; c2.db[_a]=0;  // compute c2 = c0 - c1
    for (i=0;i<3;i++)  c.db[i]=c1.db[i]+c2.db[i]; c.db[_a]=0;   // verify  c  = c1 + c2
    pan_add_c1->Color=c1.c; // just show colors on some panels
    pan_add_c2->Color=c2.c;
    pan_add_c ->Color= c.c;
    // substractive
    for (i=0;i<3;i++) c1.db[i]=c0.db[i]+Random(255-c0.db[i]); c1.db[_a]=0;  // generate c1 >= c0
    for (i=0;i<3;i++) c2.db[i]=c1.db[i]-c0.db[i];             c2.db[_a]=0;  // compute c2 = c1 - c0
    for (i=0;i<3;i++)  c.db[i]=c1.db[i]-c2.db[i];              c.db[_a]=0;  // verify  c  = c1 - c2
    pan_sub_c1->Color=c1.c; // just show colors on some panels
    pan_sub_c2->Color=c2.c;
    pan_sub_c ->Color= c.c;
    }
//---------------------------------------------------------------------------

And here a screenshot:

screenshot

The only important stuff in code is the sb_rgbChange event which is called on any change of any of the 3 scrollbars. It compute the c1,c2 colors for both additive and substractive logic and output the colors into pannels.

It works without any problem... btw You where right even my Random(x) generates x so the limit should be 255 (I already repaired the answer).

Here complete source code (BDS2006 C++/VCL/Win32) and Win32 binary:

Vannesavanness answered 5/6, 2019 at 8:48 Comment(10)
Holy cow I did not think this was going to be possible! Going to take me a little while to digest this! Thank you.Heresy
Wondering if you could explain at a higher level how this works :). So this is saying that you can mix any two colors and it will form the third color?Heresy
@LancePollard its simple linear equation ... A + B = C or A - B = C with the notion that A,B,C can not be negative that is why you need to select A as lower or bigger color than the target one C. Appart of that you can chose any A and compute B so they combine to C.Vannesavanness
Wondering if I got it right: jsfiddle.net/oh3bpcgw/1. Mainly wondering if using 255 is the correct choice instead of 256.Heresy
Also wondering if I could say, "Okay, I want to figure out what 2 colors generate x, and I want one of the components/colors to be this specific blue, so the other must be y", or if there is only a subset of colors from which I can choose (i.e. the startingWith value).Heresy
Also have the question, of how many color pairs there are that can mix into the third color, if it's just a few, or thousands, or millions even.Heresy
@LancePollard on integer you are limited by the possible counts to sum up to given colors (you know if you got 8bit per channel then there are only 256^3 of possible colors to chose from and due to the < or > constraint you can use only part of them ... the closer to White/Black the less colors to chose from...) but on floats you're virtually without limit until hitting the precision barrier of coarse.Vannesavanness
@LancePollard 255/256 depends on the Random implementation. The one I got in my C++ environment Random(X) is generating numbers <0,X-1> and never X hence 256 but if yours generate <0,X> then 255 is correct ... look in the documentation to your environment to clarify which one it is ... or simply generate and print 100 times Random(5) and look if it generated 5 or not ....Vannesavanness
Can you please check my fiddle, I am getting incorrect mixing results when I try this now.Heresy
@LancePollard See edit 1 so you got something to compare to ... I use no floats in there just integer math ... My bet is you are mixing float and integer math in a wrong way. Sadly I do not code in JAVASCRIPT so i can not debug but I would expect some entry point or events in the code or at least hard coded test case and some output but I see none of that in your code ...Vannesavanness

© 2022 - 2024 — McMap. All rights reserved.