Loading...
Logo
Processing Forum

Draggable color nodes

in General Discussion  •  Other  •  1 year ago  
I came across a nice fractal zooming web app the other day and made one like it. I have not included color in my own version and was wondering what would be a good way to make a draggable color selection like the one from this link:  http://www.carefullyconstructed.co.uk/projects/fractal/

To see what I am talking about in the link click on the downward pointing arrow, a menu will appear. I realize this is not Processing but I want to make a sketch of something like the color selection in Processing before I try to make it work with my web app.

I'm not sure how to go about starting something like this. Basically what I am looking to do is have an arbitrary number of nodes that are assigned a color and a x position between 0 and 1 to make a color palette. An image that is made of pixels with values between 0 and 1 will receive the appropriate color from this palette. Any help is appreciated.

Replies(11)

I recently made a sketch where you can drag cells in a grid. The cells hold letters, but it is easy to adapt it to hold colors...
http://bazaar.launchpad.net/~philho/+junk/Processing/files/head:/_SmallPrograms/GridOfLetters/

PhiLho, that's pretty cool. I'm not too worried about the actual drag and drop though. The problem I'm having is figuring out how to assign a color based on the positions of the nodes, the distance of each node from its neighboring nodes, and the actual color assigned to a node.

EDIT: Removed this section to avoid confusing readers and to make post shorter
Associate to color is easy: multiply by 10 and you get indexes in a 10 color palette. Adjust to the size of your palette, of course...
I am not sure to get the "position, neighbor distance" part.
EDIT: Removed this section to avoid confusing readers and to make post shorter
Here is a sketch that is stuck at the point I was trying to describe earlier. Right now I have click and draggable nodes that show a black / white color. Underneath the nodes I have an area that has random vertical lines that are black and white. That area underneath is in a temporary state and what I am stuck on. My goal is to instead have that area be vertical lines of smooth gradients based on the color of the nodes above. If there are 3 nodes then that area will have 2 smooth gradients in the space between the nodes. Here is what I have so far:
Copy code
  1. int numNodes = 3;
  2. int[] nodeColor = new int[numNodes];
  3. int[] nodePos = new int[numNodes];
  4. boolean somethingSelected;
  5. int selectedNode;

  6. void setup() {
  7.   size(800, 200, P2D);
  8.   for (int i = 0; i < numNodes; i++) {
  9.     nodeColor[i] = (int)random(256);
  10.     nodePos[i] = width/(numNodes-1)*i;
  11.   }
  12.   println("Click and drag the nodes along the line");
  13.   println("Press 'c' to randomize the color of the nodes");
  14.   println("Press 'p' to reset the position of the nodes");
  15. }

  16. void draw() {
  17.   if (somethingSelected) nodePos[selectedNode] = mouseX;
  18.   background(255);
  19.   line(0, 50, width, 50);
  20.   for (int i = 0; i < numNodes; i++) {
  21.     fill(nodeColor[i]);
  22.     rect(nodePos[i]-5, 25, 10, 50);
  23.   }
  24.   loadPixels();
  25.   for (int i = 0; i < width; i++) {
  26.     int val = (int)random(256);
  27.     for (int j = height/2; j < height; j++) pixels[i+j*width] = (val<<16)+(val<<8)+val;
  28.   }
  29.   updatePixels();
  30. }

  31. void mousePressed() {
  32.   somethingSelected = false;
  33.   for (int i = 0; i < numNodes; i++) {
  34.     if (mouseX > nodePos[i]-5 && mouseX < nodePos[i]+5 && mouseY > 25 && mouseY < 75) {
  35.       somethingSelected = true;
  36.       selectedNode = i;
  37.       break;
  38.     }
  39.   }
  40. }

  41. void mouseReleased() {
  42.   somethingSelected = false;
  43. }

  44. void keyPressed() {
  45.   if (key == 'c' || key == 'C') {
  46.     for (int i = 0; i < numNodes; i++) nodeColor[i] = (int)random(256);
  47.   }
  48.   if (key == 'p' || key == 'P') {
  49.     for (int i = 0; i < numNodes; i++) nodePos[i] = width/(numNodes-1)*i;
  50.   }
  51. }
Copy code
  1. int numNodes = 3;
  2. color[] nodeColor = new color[numNodes];
  3. int[] nodePos = new int[numNodes];
  4. boolean somethingSelected;
  5. int selectedNode;
  6.  
  7. void setup() {
  8.   size(800, 200, P2D);
  9.   for (int i = 0; i < numNodes; i++) {
  10.     nodeColor[i] = color(random(256), random(256), random(256));
  11.     nodePos[i] = width/(numNodes-1)*i;
  12.   }
  13.   println("Click and drag the nodes along the line");
  14.   println("Press 'c' to randomize the color of the nodes");
  15.   println("Press 'p' to reset the position of the nodes");
  16. }
  17.  
  18. void draw() {
  19.   if (somethingSelected) nodePos[selectedNode] = mouseX;
  20.   background(255);
  21.   line(0, 50, width, 50);
  22.   for (int i = 0; i < numNodes; i++) {
  23.     fill(nodeColor[i]);
  24.     rect(nodePos[i]-5, 25, 10, 50);
  25.   }
  26.   loadPixels();
  27.   int count = 0;
  28.   for (int i = 0; i < width; i++) {
  29.     float w = float(width);
  30.     if (i > nodePos[count+1])
  31.       count++;
  32.     float a = float(i - nodePos[count]) / (nodePos[count+1] - nodePos[count]);
  33.     color val = lerpColor(nodeColor[count], nodeColor[count+1], a);
  34.     for (int j = height/2; j < height; j++) pixels[i+j*width] = val;
  35.   }
  36.   updatePixels();
  37. }
Currently, don't move the left and right nodes... To be fixed, if needed (or make them fixed).
Thanks PhiLho, that helped me figure it out :)

I'll post a Processing version of it working with a fractal later
Ok, one last problem. I managed to get the sketch to work without lerpColor() because I don't think that exists in JavaScript and I'll want to do the same there later. But I used map() to get my sketch to work which apparently also doesn't exist in JavaScript (or is not supported in all browsers). Can you help me figure out how to assign val its value without using map()? I can't seem to figure it out. I highlighted the line in red.

On a side note, I'm using int instead of color to make the future translating to JavaScript easier. The sketch is in black and white again to avoid having to do all of my calculations for each color channel for now.

One more side note, I noticed with the sketch you wrote above that it doesn't do the gradients correctly if the nodes pass one another so I changed the code for finding the closest left and right nodes.
Copy code
  1. int numNodes = 5;
  2. int[] nodeColor = new int[numNodes];
  3. int[] nodePos = new int[numNodes];
  4. boolean somethingSelected;
  5. int selectedNode;

  6. void setup() {
  7.   size(800, 200, P2D);
  8.   for (int i = 0; i < numNodes; i++) {
  9.     nodeColor[i] = (int)random(256);
  10.     nodePos[i] = width/(numNodes-1)*i;
  11.   }
  12.   println("Click and drag the nodes along the line");
  13.   println("Press 'c' to randomize the color of the nodes");
  14.   println("Press 'p' to reset the position of the nodes");
  15. }

  16. void draw() {
  17.   // Display Hand if mouse is on a draggable node
  18.   boolean onNode = false;
  19.   for (int i = 1; i < numNodes-1; i++) {
  20.     if (mouseX > nodePos[i]-5 && mouseX < nodePos[i]+5 && mouseY > 25 && mouseY < 75) {
  21.       onNode = true;
  22.       break;
  23.     }
  24.   }
  25.   if (onNode) cursor(HAND);
  26.   else cursor(ARROW);

  27.   // Move selected node
  28.   if (somethingSelected) nodePos[selectedNode] = mouseX;

  29.   // Draw
  30.   background(255);
  31.   line(0, 50, width, 50);
  32.   for (int i = 0; i < numNodes; i++) {
  33.     fill(nodeColor[i]);
  34.     rect(nodePos[i]-5, 25, 10, 50);
  35.   }
  36.   loadPixels();
  37.   for (int i = 0; i < width; i++) {
  38.     // Closest left node info
  39.     int leftColor = nodeColor[0];
  40.     int leftDist = 2147483647;  // Infinity
  41.     int leftPos = nodePos[0];
  42.     
  43.     // Closest right node info
  44.     int rightColor = nodeColor[numNodes-1];
  45.     int rightDist = 2147483647;  // Infinity
  46.     int rightPos = nodePos[numNodes-1];
  47.     
  48.     // Get closest node positions and color
  49.     for (int j = 0; j < numNodes; j++) {
  50.       int currDist = abs(i-nodePos[j]);
  51.       if (i >= nodePos[j] && leftDist > currDist) {
  52.         leftColor = nodeColor[j];
  53.         leftDist = currDist;
  54.         leftPos = nodePos[j];
  55.       }
  56.       if (i < nodePos[j] && rightDist > currDist) {
  57.         rightColor = nodeColor[j];
  58.         rightDist = currDist;
  59.         rightPos = nodePos[j];
  60.       }
  61.     }
  62.     
  63.     // Assign a color based on closest node position and color
  64.     float percent = float(i-leftPos)/(rightPos-leftPos);
  65.     int val = (int)map(percent, 0, 1, leftColor, rightColor);
  66.     for (int j = height/2; j < height; j++) pixels[i+j*width] = (val<<16)+(val<<8)+val;
  67.   }
  68.   updatePixels();
  69. }

  70. void mousePressed() {
  71.   somethingSelected = false;
  72.   for (int i = 0; i < numNodes; i++) {
  73.     if (mouseX > nodePos[i]-5 && mouseX < nodePos[i]+5 && mouseY > 25 && mouseY < 75) {
  74.       somethingSelected = true;
  75.       selectedNode = i;
  76.       break;
  77.     }
  78.   }
  79. }

  80. void mouseReleased() {
  81.   somethingSelected = false;
  82. }

  83. void keyPressed() {
  84.   if (key == 'c' || key == 'C') {
  85.     for (int i = 0; i < numNodes; i++) nodeColor[i] = (int)random(256);
  86.   }
  87.   if (key == 'p' || key == 'P') {
  88.     for (int i = 0; i < numNodes; i++) nodePos[i] = width/(numNodes-1)*i;
  89.   }
  90. }
It is surprising that map() isn't implemented in all browsers. And you can see in the PApplet source, its definition is very simple! It is just a convenience function, like dist().
You can also find the code of lerpColor in http://code.google.com/p/processing/source/browse/trunk/processing/core/src/processing/core/PGraphics.java although the HSB mode uses a Java specific function, perhaps the reason it isn't in PJS.
Thanks again, here it is:
Copy code
  1. int val = (int)(leftColor+(rightColor-leftColor)*percent);
Got it working with the Mandelbrot Set. Unfortunately I am having trouble getting the lowest value and highest value to match the color palette. Other than that pretty happy with it:
Copy code
  1. // Fractal variables
  2. float xmin = -2.5;
  3. float ymin = -2.0;
  4. float wh = 4;
  5. int maxIterations = 1000;
  6. int screenSize = 600;

  7. // Node variables
  8. int nodeWidth = 10;
  9. int nodeHeight = 50;
  10. int numNodes = 7;
  11. int paletteHeight = 100;
  12. int[] nodeVal = new int[numNodes];
  13. int[] nodePos = new int[numNodes];
  14. boolean somethingSelected;
  15. int selectedNode;

  16. void setup() {
  17.   size(screenSize, screenSize+paletteHeight, P2D);
  18.   stroke(255, 0, 0);
  19.   for (int i = 0; i < numNodes; i++) {
  20.     nodeVal[i] = (int)random(256);
  21.     nodePos[i] = width/(numNodes-1)*i;
  22.   }
  23.   makeFractal();
  24.   println("Click and drag the nodes along the line");
  25.   println("Press 'c' to randomize the color of the nodes");
  26.   println("Press 'p' to reset the position of the nodes");
  27. }

  28. void draw() {
  29.   nodeCode();
  30. }

  31. void mousePressed() {
  32.   somethingSelected = false;
  33.   for (int i = 1; i < numNodes-1; i++) {
  34.     if (mouseX > nodePos[i]-nodeWidth/2 && mouseX < nodePos[i]+nodeWidth/2 && mouseY > screenSize+nodeHeight/2 && mouseY < height-nodeHeight/2) {
  35.       somethingSelected = true;
  36.       selectedNode = i;
  37.       break;
  38.     }
  39.   }
  40. }

  41. void mouseReleased() {
  42.   somethingSelected = false;
  43.   makeFractal();
  44. }

  45. void keyPressed() {
  46.   if (key == 'c' || key == 'C') {
  47.     for (int i = 0; i < numNodes; i++) nodeVal[i] = (int)random(256);
  48.     makeFractal();
  49.   }
  50.   if (key == 'p' || key == 'P') {
  51.     for (int i = 0; i < numNodes; i++) nodePos[i] = width/(numNodes-1)*i;
  52.     makeFractal();
  53.   }
  54. }

  55. void nodeCode() {
  56.   // Display Hand if mouse is on a draggable node
  57.   boolean onNode = false;
  58.   for (int i = 1; i < numNodes-1; i++) {
  59.     if (mouseX > nodePos[i]-nodeWidth/2 && mouseX < nodePos[i]+nodeWidth/2 && mouseY > screenSize+nodeHeight/2 && mouseY < height-nodeHeight/2) {
  60.       onNode = true;
  61.       break;
  62.     }
  63.   }
  64.   if (onNode) cursor(HAND);
  65.   else cursor(ARROW);

  66.   // Move selected node
  67.   if (somethingSelected) nodePos[selectedNode] = mouseX;

  68.   // Draw Palette
  69.   loadPixels();
  70.   for (int i = 0; i < width; i++) {
  71.     if (i == 0) for (int j = screenSize; j < height; j++) pixels[i+j*width] = (nodeVal[0]<<16)+(nodeVal[0]<<8)+nodeVal[0];
  72.     else if (i == width-1) for (int j = screenSize; j < height; j++) pixels[i+j*width] = (nodeVal[numNodes-1]<<16)+(nodeVal[numNodes-1]<<8)+nodeVal[numNodes-1];
  73.     else {
  74.       // Closest left node info
  75.       int leftVal = nodeVal[0];
  76.       int leftDist = 2147483647;  // Infinity
  77.       int leftPos = nodePos[0];

  78.       // Closest right node info
  79.       int rightVal = nodeVal[numNodes-1];
  80.       int rightDist = 2147483647;  // Infinity
  81.       int rightPos = nodePos[numNodes-1];

  82.       // Get closest node positions and color
  83.       for (int j = 0; j < numNodes; j++) {
  84.         int currDist = abs(i-nodePos[j]);
  85.         if (i > nodePos[j] && leftDist > currDist) {
  86.           leftVal = nodeVal[j];
  87.           leftDist = currDist;
  88.           leftPos = nodePos[j];
  89.         }
  90.         if (i <= nodePos[j] && rightDist > currDist) {
  91.           rightVal = nodeVal[j];
  92.           rightDist = currDist;
  93.           rightPos = nodePos[j];
  94.         }
  95.       }

  96.       // Assign a color based on closest node position and color
  97.       float percent = (float)(i-leftPos)/(rightPos-leftPos);
  98.       int val = (int)(leftVal+(rightVal-leftVal)*percent);
  99.       for (int j = screenSize; j < height; j++) pixels[i+j*width] = (val<<16)+(val<<8)+val;
  100.     }
  101.   }
  102.   updatePixels();

  103.   // Draw Nodes
  104.   line(0, screenSize, width, screenSize);
  105.   line(0, screenSize+paletteHeight/2, width, screenSize+paletteHeight/2);
  106.   for (int i = 0; i < numNodes; i++) {
  107.     fill(nodeVal[i]);
  108.     rect(nodePos[i]-nodeWidth/2, screenSize+nodeHeight/2, nodeWidth, nodeHeight);
  109.   }
  110. }

  111. void makeFractal() {
  112.   loadPixels();
  113.   float xmax = xmin+wh;
  114.   float ymax = ymin+wh;
  115.   float dx = (xmax-xmin)/screenSize;
  116.   float dy = (ymax-ymin)/screenSize;
  117.   float x = xmin;
  118.   for (int i = 0; i < screenSize; i++) {
  119.     float y = ymin;
  120.     for (int j = 0;  j < screenSize; j++) {
  121.       float zr = x;
  122.       float zi = y;
  123.       int n = 0;
  124.       while (n < maxIterations) {
  125.         float zrr = zr*zr;
  126.         float zii = zi*zi;
  127.         float twori = 2*zr*zi;
  128.         zr = zrr-zii+x;
  129.         zi = twori+y;
  130.         if (zrr+zii > 16) break;
  131.         n++;
  132.       }
  133.       float fn = n+1-log(log(sqrt(zr*zr+zi*zi)))/log(2);
  134.       float logColor = log(fn)/log(maxIterations);
  135.       int paletteColor = (int)palette(logColor);
  136.       pixels[i+j*width] = (paletteColor<<16)+(paletteColor<<8)+paletteColor;
  137.       y += dy;
  138.     }
  139.     x += dx;
  140.   }
  141.   updatePixels();
  142. }

  143. float palette(float in) {
  144.   in *= width;
  145.   int leftVal = nodeVal[0];
  146.   float leftDist = 2147483647;  // Infinity
  147.   int leftPos = nodePos[0];

  148.   int rightVal = nodeVal[numNodes-1];
  149.   float rightDist = 2147483647;  // Infinity
  150.   int rightPos = nodePos[numNodes-1];

  151.   for (int j = 0; j < numNodes; j++) {
  152.     float currDist = abs(in-nodePos[j]);
  153.     if (in > nodePos[j] && leftDist > currDist) {
  154.       leftVal = nodeVal[j];
  155.       leftDist = currDist;
  156.       leftPos = nodePos[j];
  157.     }
  158.     if (in <= nodePos[j] && rightDist > currDist) {
  159.       rightVal = nodeVal[j];
  160.       rightDist = currDist;
  161.       rightPos = nodePos[j];
  162.     }
  163.   }

  164.   float percent = (float)(in-leftPos)/(rightPos-leftPos);
  165.   int val = (int)(leftVal+(rightVal-leftVal)*percent);
  166.   return val;
  167. }