Monday, March 25, 2013

Practical Color Matching

A friend posted this link on facebook recently. Its a really cool test. It got me thinking. its no wonder people have a hard time doing color matching. Its hard just to see that a color is in fact different, then to have decide why its different and compensate for it. Not to mention you then have to compensate for lighting, and finish (matte/gloss, etc.). So i dug up an old illustrator script that i wrote for a colleague. It helps do some of the heavy lifting for you when color matching for print. It makes new color chips based on your current selection in illustrator. Heres a brief video of it in action, with a special bonus at the end.




The color nazzis will say its evil because it uses the HSB color model to create the variations. But, the range of colors that are outside the hsb gamut is small.
It doesn't make sense to do something the hard way every time because its not going to work 10% of the time. Do it the easy way then if the color is out of gamut do it the hard way. You will save time in the long run by always doing it the easy way first. You just have to keep in mind that some colors are suspect to being out of the HSB gamut, and keep an eye out for them.

Okay, the script. The best way to operate it is by..
1) Opening a new document
2) Creating an object to hold the color you want to test.
3) Set the fill color of that object to the color you wish to test.
4) Set the document color mode to RGB if its not already.
5) Select the object and run the script.
You can skip steps 2 and 3 by copying an object from another file to make it easier, and if you forget step 4, the script will remind you.
Once you have run the script you will be given the option to adjust the amount of variation between swatches. The default of 10 might be too coarse for regular use but it works great for a demo :P
You can adjust it to get more or less variation between colors as needed.
Run it as many times as you need to fine tune your prints.


Here's the Script....
-------------------------

var docRef = activeDocument;
var sel = docRef.selection;
var ColorMode = docRef.documentColorSpace;
//alert(ColorMode);
var increment = prompt("Enter adjustment value/n0 = fine, 10=Coarse","10");

try{
 if(ColorMode != "DocumentColorSpace.RGB"){
  throw new Error("Document color mode must be set to RGB\nMenu: File > Document Color Mode > RGB Color");
 }
 if(sel[0].fillColor){
  var existingcolor = sel[0].fillColor;
 }else{
  throw new Error("You must select something");
 }
 if(isNaN(increment *1)){
  throw new Errow("Adjustment entered is not a number")
 }

var exR = existingcolor.red;
var exG = existingcolor.green;
var exB = existingcolor.blue;
var exHSV =  RGBtoHSV(exR,exG,exB);
var y=0;
var posy;

for(z=0; z<3; z++){
if(z==0){
   posy = 483;
   var zfill = ModHSV(exHSV, increment*-1, 0,0);
}else if(z==1){
   posy = 0;
   var zfill = ModHSV(exHSV, 0, 0, 0);
}else if(z==2){
   posy = -483;
   var zfill = ModHSV(exHSV, increment*1, 0,0);
}
for(x=0; x<=3; x++){
 //alert("new row "+x+", V="+exHSV[2])
 var xfill = ModHSV(zfill, 0, x*increment/100, 0);
 for(y=0; y<=3; y++){
  var yfill = ModHSV(xfill, 0, 0, y*increment*2.55);
  var swatchY = docRef.pathItems.rectangle(posy+0+(x*66), 0+(y*66), 48,48);
  swatchY.fillColor = HSVtoRGB(yfill); 
 }
}
for(x=0; x<=3; x++){
 //alert("new row "+x+", V="+exHSV[2])
 var xfill = ModHSV(zfill, 0, (x*increment/100)*-1, 0);
 for(y=0; y<=3; y++){
  var yfill = ModHSV(xfill, 0, 0, y*increment*2.55);
  var swatchY = docRef.pathItems.rectangle(posy+0+(x*66)*-1, 0+(y*66), 48,48);
  swatchY.fillColor = HSVtoRGB(yfill); 
 }
}
for(x=0; x<=3; x++){
 //alert("new row "+x+", V="+exHSV[2])
 var xfill = ModHSV(zfill, 0, (x*increment/100)*-1, 0);
 for(y=0; y<=3; y++){
  var yfill = ModHSV(xfill, 0, 0, (y*increment*2.55)*-1);
  var swatchY = docRef.pathItems.rectangle(posy+0+(x*66)*-1, 0+(y*66)*-1, 48,48);
  swatchY.fillColor = HSVtoRGB(yfill); 
 }
}
for(x=0; x<=3; x++){
 //alert("new row "+x+", V="+exHSV[2])
 var xfill = ModHSV(zfill, 0, x*increment/100, 0);
 for(y=0; y<=3; y++){
  var yfill = ModHSV(xfill, 0, 0, (y*increment*2.55)*-1);
  var swatchY = docRef.pathItems.rectangle(posy+0+(x*66), 0+(y*66)*-1, 48,48);
  swatchY.fillColor = HSVtoRGB(yfill); 
 }
}
}//end z loop
}catch(e){
 alert(e);
}


// - START RGBtoHSV - //
function RGBtoHSV(exR,exG,exB){
var HSVColor = new Array();
var cMin= myMin(exR,exG,exB);   
var V = myMax(exR,exG,exB);
var Delta = V - cMin;
var S, H; 
 // Calculate saturation: saturation is 0 if r, g and b are all 0
if( V== 0){
 S= 0;
 //alert("S=0")
}else{
 S = Delta / V;
 //alert("S="+S)
}
if(S== 0){
  H = 0
}else if(exR==V){
 H = 60 * (exG - exB) / Delta
 }else if(exG==V){
     H = 120 + 60 * (exB - exR) / Delta
 }else if(exB==V){
     H = 240 + 60 * (exR - exG) / Delta;
}else{
  alert("i don't know!")
}
 
 if(H < 0){
 H = H + 360
}
HSVColor[0] = H;
HSVColor[1] = S;
HSVColor[2] = V;
return HSVColor;
}
// -  end - //

// - START HSVtoRGB - //
function HSVtoRGB(HSVColor){
 var nRGBColor = new RGBColor();
 var V = HSVColor[2];
  //alert(V);
 var S = HSVColor[1];
  //alert(S); 
 var Hex = HSVColor[0]/60;
  //alert(HSVColor[0]+"/60="+Hex);
 var primclr = Math.floor(Hex);
  //alert(primclr);
 var secclr = Hex - primclr;
  //alert(secclr+"="+Hex+"-"+primclr);
 var a = (1-S)*V;
 var b = (1-(S*secclr))*V;
 var c = (1-(S*(1-secclr)))*V;
 //alert("ABC="+secclr+","+a+", "+b+", "+c) 
 //alert(primclr);
 switch(primclr){
  case 0:
   nRGBColor.red = V;
   nRGBColor.green = c;
   nRGBColor.blue = a;
   break;
  case 1:
   nRGBColor.red = b;
   nRGBColor.green = V;
   nRGBColor.blue = a;
   break;
  case 2:
   nRGBColor.red = a;
   nRGBColor.green = V;
   nRGBColor.blue = c;
   break;
  case 3:
   nRGBColor.red = a;
   nRGBColor.green = b;
   nRGBColor.blue = V;
   break;
  case 4:
   nRGBColor.red = c;
   nRGBColor.green = a;
   nRGBColor.blue = V;
   break;
  case 5:
   nRGBColor.red = V;
   nRGBColor.green = a;
   nRGBColor.blue = b;
   break;
  case 6:
   nRGBColor.red = V;
   nRGBColor.green = c;
   nRGBColor.blue = a;
   break;
 }
 return nRGBColor;
}
// - end - //

// START ModHSV - //
//ModHSV(exHSV, increment, (increment/100), (increment*2.549))
function ModHSV(xHSVColor, H, S, V){
 var nHSV = new Array();
 nHSV[0] = xHSVColor[0] + H;
 nHSV[1] = xHSVColor[1] + S;
 nHSV[2] = xHSVColor[2] + V;
 
 if(nHSV[0]>360 || nHSV[0]<0){
  if(nHSV[0]>360){
   nHSV[0] = nHSV[0]-360;
  }else if(nHSV[0]<0){
   nHSV[0] = nHSV[0]+360;
  }else{
  nHSV[0] = 0;
  }
 }
 if(nHSV[1] > 1 || nHSV[1] < 0){
  // set to gray
  nHSV[1] = 0;
  nHSV[2] = 204;
 }
 if(nHSV[2]>255 || nHSV[2]<0){
  nHSV[1] = 0;
  nHSV[2] = 204;
 }
 return nHSV;
}
// - end - //

function myMax(r,g,b){
  if (r >= g && r >= b){
   return r;
  }else if(g >= r && g >= b){
   return g;
  }else{
   return b;
  } 
}
// end

function myMin(a,b,c){
  if (a <= b && a <= c){
   return a;
  }else if(b <= a && b <= c){
   return b;
  }else{
   return c;
  } 
}
//end

Saturday, March 23, 2013

Hibernation

It's been 5 months or so since my last post. My daughter doesn't allow me to type while I'm feeding her anymore. So, posting has been difficult. Plus, it's not unusual for me to go into social hibernation during the winter as it is. I hate the cold and the snow and the shortage of sunlight. However durring those 5 months I haven't been inactive.
Shortly after my last post I stumbled upon a piece of software called "game editor". It's a standalone application for game development. It runs on windows but the games can be ported to iOS, Android, Mac, Linux, and PC. For a few years I've wanted to make an iOS game, but I simply can't afford to go out and buy a Mac. But, it looks like that limitation has been partially lifted. So, for the last 5 months in the slivers of spare time that I find, I've been developing a Zelda-esque Action RPG.
The software has predefined events and actions. But allows for a lot of custom programming in C. It's not without it's limitations. But, with all the areas for custom programming most things can be accomplished. The forum on the game editor website is very very helpful and friendly. Possibly the friendliest forum i've ever visited. I've never actually posted because most every question I've had had already been answered quite thoroughly.
At any rate. Level 1 should be ready to demo and bug test soon. The graphics are still only placeholders at the moment. I'm not exactly the best pixel artist.






Some day soon I will release a demo.
The bulk of the programming has been done. But, making the maps, cut scenes, and boss fights is tedious.
Not to mention the fact that my iphone crashed and i lost 4 months worth of notes that related to the games storyline... yet another delay.