"Get" as color detector?

Hello Forum :slight_smile:

On Processing, I love this program for detecting colors:

color c;

void setup (){
  size (200,200);
}

void draw() {
  background (0);
  fill (255);
  rect (0,0,100,200); 
  c = get (mouseX,mouseY);
  
  if (c == -1){
    print ("the color is white ");
  }  else if (c == -16777216){
    fill (0,255,0);
    print ("the color is black "); 
 }
}

But, I am seriously struggling to convert this simple program to p5js. It seems that ā€˜getā€™ works a bit differently with p5js.

Also, can anyone give me more context on what format the color codes are in this Processing sketch?

I think you have use some of the functions like red( c),green( c),blue( c),brightness( c) to find out the exact color.

Hello,

get() in P5.js reads the color into an array:
https://p5js.org/reference/#/p5/get

Some code I patched together to explore this:

Example
let c = [0, 0, 0, 0];
let c1 = [255, 255, 255, 255];
let c2 = [0, 0, 0, 255];

let state = false;

function setup() 
  {
  createCanvas(400, 400);
  print ("Begin...");    
  }

function draw() 
  {
  background (0);

  fill (255);
  rect (0, 0, 200, 200); 
  
  c = get(mouseX, mouseY);   
  print(c);
  
  state = isEqual1();
  //print(state);
  if (state)
    {
    print ("the color is white ");
    state=false;  
    }
    
  state = isEqual2();
  //print(state);
  if (state)
    {
    print ("the color is black ");
    state=false;  
    }    
  }
  
function isEqual1() 
  { 
  // comapring each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(c[i] != c1[i]) 
       {
       return false; 
       }        
    return true; 
    } 
  }   

function isEqual2() 
  { 
  // comapring each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(c[i] != c2[i]) 
       {
       return false; 
       }        
    return true; 
    } 
  }   

Example Update - Better function
let c = [0, 0, 0, 0];
let c1 = [255, 255, 255, 255];
let c2 = [0, 0, 0, 255];

let state = false;

function setup() 
  {
  createCanvas(400, 400);
  print ("Begin...");    
  }

function draw() 
  {
  background (0);

  fill (255);
  rect (0, 0, 200, 200); 
  
  c = get(mouseX, mouseY);   
  print(c);
  
  state = isEqual(c, c1);
  //print(state);
  if (state)
    {
    print ("the color is white ");
    state=false;  
    }
    
  state = isEqual(c, c2);
  //print(state);
  if (state)
    {
    print ("the color is black ");
    state=false;  
    }    
  }
  
function isEqual(col1, col2) 
  { 
  // comparing each element of array 
   for(var i=0;i<c.length;i++)
     {
     if(col1[i] != col2[i])       //If any one is != then it is false
       {
       return false; 
       }        
    return true; 
    } 
  }   

This was just an initial exploration and I compared arrays.
This can be done much betterā€¦

Borrowed some elements from here:
How to compare two arrays in JavaScript? - GeeksforGeeks

:)

2 Likes

get()

"Get a region of pixels, or a single pixel, from the canvas.

Returns an array of [R,G,B,A] values for any pixel or grabs a section of an image."

2 Likes

Actually very differently Iā€™m afraid! :cold_sweat:

Processingā€™s get() returns a 32-bit (4-byte) signed int aRGB color value:

While in p5js get() returns an array containing 4 values ordered as RGBa:

If you donā€™t mind using pixels[] in place of get():

Iā€™ve got this project which is already finished (but still untested & unpublished) where we can invoke getProcessingPixels() passing an image (or its corresponding pixels[]) as its argument, and then it returns an Int32Array proxy viewer of it:

That returned Int32Array proxy viewer should behave just like a Processingā€™s aRGB pixels[]:

So you can more easily convert Processingā€™s sketches relying on pixels[] to p5js: :partying_face:

// https://Discourse.processing.org/t/get-as-color-detector/19912/5
// GoToLoop (2020-Apr-19)

'use strict';

function getProcessingPixels(imgOrPixels) {
  const { buffer } = imgOrPixels.pixels && imgOrPixels.pixels || imgOrPixels;

  return new Proxy(
    new Int32Array(buffer), getProcessingPixels.INT32_aRGB_HANDLE
  );
}

getProcessingPixels.isLittleEndian = function () {
  return new Uint8Array(Uint32Array.of(0x12345678).buffer)[0] === 0x78;
};

getProcessingPixels.int32aRGBLittleEndianHandle = {
  set(obj, prop, val) { // aRGB -> aBGR (RGBa)
    const idx = +prop.toString();

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx)) {
      const red = val >> 0o20 & 0xff, blue = (val & 0xff) << 0o20;
      val = val & 0xff00ff00 | red | blue;
    }

    return obj[prop] = val, true;
  },

  get(obj, prop) { // aBGR (RGBa) -> aRGB
    const idx = +prop.toString(), val = obj[prop];

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx)) {
      const red = (val & 0xff) << 0o20, blue = val >> 0o20 & 0xff;
      return val & 0xff00ff00 | red | blue;
    }

    return typeof val === 'function' && val.bind(obj) || val;
  }
};

getProcessingPixels.int32aRGBBigEndianHandle = {
  set(obj, prop, val) { // aRGB -> RGBa
    const idx = +prop.toString();

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx))
      val = val >>> 0o30 | val << 0o10;

    return Reflect.set(obj, prop, val);
  },

  get(obj, prop) { // RGBa -> aRGB
    const idx = +prop.toString(), val = Reflect.get(obj, prop);

    if (idx >= 0 && idx < obj.length && Number.isInteger(idx))
      return val << 0o30 | val >>> 0o10;

    return typeof val === 'function' && val.bind(obj) || val;
  }
};

getProcessingPixels.LITTLE_ENDIAN = getProcessingPixels.isLittleEndian();
getProcessingPixels.BIG_ENDIAN = !getProcessingPixels.LITTLE_ENDIAN;

getProcessingPixels.INT32_aRGB_HANDLE =
  getProcessingPixels.LITTLE_ENDIAN &&
  getProcessingPixels.int32aRGBLittleEndianHandle ||
  getProcessingPixels.int32aRGBBigEndianHandle;
2 Likes

Processing and p5.js color work very differently, and that is surprising. There is a lot of great information on how / why above, but here is a walkthrough of a solution to your students using get() as a color detector in their projects.

In Processing (Java mode),

get() and color() return the same things (the color datatype, a 32-bit int representing ARGB), which can be compared using ==.

Letā€™s detect if the mouse is on white:

// check mouse color
void setup() {
  size(100,100);
  rect(25,25,50,50);
}

void draw() {
  int cwhite = color(255);
  int cmouse = get(mouseX, mouseY);
  if(cmouse==cwhite) {
    println(frameCount, cmouse);
  }
}

In p5.js (JavaScript),

get() and color() return DIFFERENT things, AND, those things canā€™t be compared with equals ā€“ to each other, OR to themselves(!!). get() returns an array of floats [R, G, B, A]. color() returns a p5.Color object, which contains multiple arrays, including one with four 0-255 values, called ā€œlevelsā€. In JavaScript, arrays and objects with the same contents are not found equal with ==.

To adapt the Processing sketch to p5.js, we need two things:

  1. define our color to check for as a flat array, [R, G, B, A], so it will match get(). Either declare it directly [] or use color(my_arguments).levels.
  2. create an equals function to compare two colors. There are many ways to do this, but weā€™ll use one that is easy to read and fast.
// check mouse color
function setup() {
  createCanvas(100, 100);
  rect(25,25,50,50);
}

function draw() {
  let cmouse = get(mouseX, mouseY); // [R, G, B, A]
  let cwhite = [255,255,255,255];
  // let cwhite = color(255).levels; 
  if(eqcolor(cmouse, cwhite)) {
    print(frameCount, cmouse);
  }
}

function eqcolor(a, b) {
  return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
}
<iframe src="https://editor.p5js.org/jeremydouglass/sketches/JsVeospiC" width="100%" height="510" allowfullscreen frameborder="0" marginwidth="0" marginheight="0"></iframe>
2 Likes

Thank you so much everyone!!! Wow!!!

This is so tremendously helpful for teaching! Thank you!

1 Like

Quick question about syntax/curly bracket structure :slight_smile:

brackets

Does the use of ā€œreturnā€ change the need for an opening curly bracket with the if conditional?

Sorry, if doesnā€™t need {} when the condition is only a single line. But that is confusing for learners ā€“ Iā€™ll fix in the examples above.

Also:

How to do it without an equality function.

Instead of a custom equality function:

if(eqcolor(cmouse, cwhite))

drop eqcolor and use .toString() instead:

if(cmouse == cwhite.toString())

Example sketch:

https://editor.p5js.org/jeremydouglass/sketches/wNZhvyugn

This works because in JavaScript,

print([1,2] == [1,2])  // false
print([1,2] == '1,2')  // true

Sigh. I honestly donā€™t like doing this because even though the code is shorter learners find it deeply, deeply confusing, so they forget the .toString() and get frustrated when it doesnā€™t work. But that might be a question of taste.

1 Like

Awesome - got to keep things consistent for myself and my students. Much appreciated!

Actually b/c the whole expression is evaluated as 1 boolean, we can simply use 1 return only for it:

function eqColor(a, b) {
  return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
}

So thereā€™s no need for 1-statement if nor else in this case.

Another alternative version using for:

function eqColor(a, b) {
  for (var i = 0; i < a.length; ++i)  if (a[i] != b[i])  return false;
  return true;
}
1 Like

Good point. Iā€™ll going to update the examples with that.

The for version is more versatile, and also fails-fast ā€“ but I wanted to make it explicit for learners that we expect to match exactly four term pairs.

2 Likes