views:

460

answers:

6

How would one create a deterministic Javascript HTML colour picker which given arguments of how many colours are desired returns an array of HTML hex colour codes, ie:

 function createColours(numColours) {
  return [/* colours array of size numColours */]  
 }

The colours themselves can be chosen / generated randomly, but the method must guarantee that colours chosen are always the same between calls and always in the same order in series.

For example, if the series of colours decided on by the function started with the following 8:

 "#47092E", "#CC2A43", "#00C66B", "#BC1300", "#667E00", "#795800", "#FFD245", "#6EFFAD", etc, etc

The function would behave with the following consistent responses across separate method invocations on the client

 ["#47092E", "#CC2A43"] == createColours(2);
 ["#47092E", "#CC2A43", "#00C66B", "#BC1300", "#667E00"] == createColours(5);
 ["#47092E"] == createColours(1);  
 ["#47092E", "#CC2A43", "#00C66B", "#BC1300", "#667E00", "#795800", "#FFD245", "#6EFFAD", #and 49 others#] == createColours(57);

Note: The colours are not defined as a variable in advance; the method might be asked for 345 colours, all of which it would be required to generate by whatever suitable means.

The problem to be solved is - first and foremost - how would you create a capability within the method to generate the n HEX colour values consistently the same each time also preserving the sequence

A: 

Maybe this:

function createColours(numColours){
   var all  = ["#47092E", "#CC2A43", "#00C66B", .... ];
   var selected = [];
   for(var i=0;i<numColours;i++){
     selected.push(all[i]);
   }
   return selected;
}
david.mchonechase
Thats missing the point.. I am not defining the array 'all'.. if i had hundreds of colours, I want the method to generate the Hex codes themselves. If they were a constant value I wouldn't have cause to set the question :)
j pimmel
A: 

See this article. It describes how all functions in javascript are actually objects.

So here's a quick prototype:

function createColours(num) {
  if ( typeof createColors.colors == 'undefined' ) {
    // We've not yet created any colors...
    createColors.colors = new array();
   }
  if ( createColors.colors.length >= num ) {
     // Create new colors and add them to the array here...

  }
  // Otherwise, return the array...
  return createColors.colors;
}
jqs
Thanks, I'm aware of your point about Functions beings objects. Your solution does nothing to solve the primary problem of generating N colours by Hex Value using some kind of quasi-random means. All your solution provides is array handling.
j pimmel
Ah sorry, I assumed the bigger issue was keep the same result set rather than generating the random content...
jqs
No worries, thanks for the effort :)
j pimmel
A: 

This is how I would do it.

(function(){
var colours = [];
var x = "0123456789ABCDEF".split('');
var addColour = function() {
  var s='#';
  for(var i=6; i-->0;)
    s+=x[Math.round(Math.random()*15)];
  colours.push(s);
};
createColours = function(numColours) {
  for(var i=numColours-colours.length; i-->0;) addColour();
  return colours.slice(0,numColours);
}
})();
sunsean
Interesting.. will give it a spin
j pimmel
Thanks. Had a go with it; it generates completely random colours every call being different to the last and also generates values of HEX which aren't legal for colours. Is useful for reference in combination with PRNG answer
j pimmel
+1  A: 

Each pair of a hexidecimal color represents an RGB value. Given #AABBCC, one can deduce that AA represents Red, BB representds Green and CC represents Blue. To obtain the 'value' for Red, you would first need to convert A from Hexidecimal to a decimal format.

A represents 10 in decimal. When you multiply the value pairs together from the hexidecimal color, you get each value for RBG. So when you convert the previously stated hex color into RGB, you'll get Red = A*A, Green = B*B and Blue = C*C or Red = 10*10 = 100, Green = 11*11 = 121 and Blue = 12*12 = 144.

So #AABBCC is an RGB of (100,121,144). I think that should be enough info to reverse the process... since you won't be needing hexidecimal values with a length of more than 2 hex characters for each color, you won't be needing a fancy algorithm to convert Decimal to Hexidecimal and back. RGB only goes from 0 - 255 so you can either convert between bases using a built in function or by making table from the resources on the page:

Wikipedia page for Hexidecmal

Dalin Seivewright
+3  A: 

You need to use a pseudo random number generator (PRNG), with a fixed start seed. The Math.random method in javascript is a PRNG, but you can't set the seed like you can in other languages.

A PRNG is reasonably simple to create - lots of code on the internet. Given a fixed start seed it will always generate the same sequence of random numbers, which is what you're asking for.

Whether those colors look pleasing or not, is another question entirely...

One javascript implementation with seeding capability:
http://www.erikoest.dk/rng2.htm

You'll also find C implementations (easy to convert to javascript) and other details of building your own PRNG of the Lehmer type here:
http://www.google.com/search?q=Lehmer%20random%20number%20generator

Another one with code (Mersenne twister):
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/JAVASCRIPT/java-script.html

This question covers several types of PRNG in javascript:
http://stackoverflow.com/questions/424292/how-to-create-my-own-javascript-random-number-generator-that-i-can-also-set-the-s

If you give the same starting seed to the PRNG at the beginning of the function, it will always return the same number sequence with each successive call.

Convert that number into a color, and you're all set.

Adam Davis
+1  A: 

Using the answers provided above, and the RNG code here, I ended up with (assumes using of Prototype library for class creation):

 var ColourPicker = Class.create({
     initialize: function() {
         this.hash = '#';
         this.rngRed = new RNG(5872323); // using same seed always guarantees number order response same
         this.rngGreen = new RNG(332233);
         this.rngBlue = new RNG(3442178);
     },
     numberToHex: function(number, padding) {
         var hex = number.toString(16);
         return hex;
     },
     createColours: function(numColours) {
         var colours = [];
         for (var i = numColours - colours.length; i-- > 0;) {
             var r = this.rngRed.nextRange(5,240);
             var g = this.rngGreen.nextRange(10,250);
             var b = this.rngBlue.nextRange(0,230);
             colours.push(this.hash + this.numberToHex(r) + this.numberToHex(g) + this.numberToHex(b));
         }
         return colours;
     }
 });

 var colourPicker = new ColourPicker();
 var colours = colourPicker.createColours(10);

 for (var i = 0 ; i < colours.length ; i++) {
  alert(colours[i]);
 }
j pimmel