Math and (Quasi) Physics in Action Script 3

RGB and HSV

The RGB color model is commonly used in computers as a direct representation of additive color mixing which most display devices utilize, while HSV is a model to describe perceptual color relationshipsin more intuitive manner. RGB stands for red, green and blue, and HSV stands for hue, saturation and value

RGB values can be converted to HSV values and vice versa using the formulas below:

RGB to HSV

r, g, b ∈ [0,1]
max = max(r, g, b), min = min(r, g, b)

h =
0 | if max = min
(60 * (g - b) / (max - min) + 360) % 360 | if max = r
60 * (b - r) / (max - min) + 120 | if max = g
60 * (r - g) / (max - min) + 240 | if max = b

s =
0 | if max = 0
(max - min) / max | otherwise

v = max

HSV to RGB

h ∈ [0,360], s, v ∈ [0,1]

hi = floor(_h / 60) % 6
f = _h / 60 - floor(_h / 60)
p = v * (1 - s)
q = v * (1 - f * s)
t = v * (1 - (1 - f) * s)
			
(r,g, b) =
(v, t, p) | if hi = 0
(q, v, p) | if hi = 1
(p, v, t) | if hi = 2
(p, q, v) | if hi = 3
(t, p, v) | if hi = 4
(v, p, q) | if hi = 5

See ‘RGB color model - Wikipedia’ and ‘HSL and HSV - Wikipedia’ for more about RGB and HSV models.

Main.as

package {
  import flash.display.*;
  import flash.text.*;
  import flash.events.*;
  import fl.controls.*;
  import fl.events.*;
  public class Main extends Sprite {
    var r:uint = 0, g:uint = 0, b:uint = 0, h:Number = 0, s:Number = 0, v:Number = 0;
    var rSlider:Slider, gSlider:Slider, bSlider:Slider;
    var rVal:TextField, gVal:TextField, bVal:TextField;
    var hVal:TextField, sVal:TextField, vVal:TextField;
    var canvas1:Sprite,canvas2:Sprite;
    var indicatorW:uint = 340;
    
    public function Main() {
      addChild(rSlider = createSlider(220,60,255,0,255,1));
      addChild(gSlider = createSlider(220,100,255,0,255,1));
      addChild(bSlider = createSlider(220,140,255,0,255,1));
      addChild(createTextField(140,50,20,"R :"));
      addChild(createTextField(140,90,20,"G :"));
      addChild(createTextField(140,130,20,"B :"));
      addChild(rVal = createTextField(175,50,20,"0"));
      addChild(gVal = createTextField(175,90,20,"0"));
      addChild(bVal = createTextField(175,130,20,"0"));
      
      addChild(createTextField(140,210,20,"Hue :"));
      addChild(createTextField(140,290,20,"Saturation :"));
      addChild(createTextField(140,370,20,"Value :"));
      addChild(hVal = createTextField(245,210,20,"0"));
      addChild(sVal = createTextField(245,290,20,"0"));
      addChild(vVal = createTextField(245,370,20,"0"));
      
      addChild(canvas1 = new Sprite());
      addChild(canvas2 = new Sprite());
      
      drawMeters();
      drawIndicators();
    }
    
    private function h_change(evt:SliderEvent = null):void {
      rVal.text = String(r = rSlider.value);
      gVal.text = String(g = gSlider.value);
      bVal.text = String(b = bSlider.value);
      
      var cp = new ColorPoint(r, g, b);
      h = cp.h
      s = cp.s
      v = cp.v
      hVal.text = String(Math.floor(h));
      sVal.text = String(Math.floor(s * 100));
      vVal.text = String(Math.floor(v * 100));
      drawIndicators();
      drawMeters()
    }
    private function createSlider(px:uint, py:uint, w:uint,min:uint, max:uint, step:uint):Slider {
      var sld:Slider = new Slider();
      sld.x = px;
      sld.y = py;
      sld.width = w;
      sld.snapInterval = step;
      sld.minimum = min;
      sld.maximum = max;
      sld.addEventListener(SliderEvent.CHANGE, h_change);
      return sld
    }
    
    private function createTextField(px:uint, py:uint,size:uint, str:String):TextField {
      var tx:TextField = new TextField();
      var tf:TextFormat = new TextFormat();
      tf.size = size;
      tf.font = "_sans";
      tf.color = 0x666666;
      tx.autoSize = TextFieldAutoSize.LEFT;
      tx.selectable = false;
      tx.defaultTextFormat = tf;
      tx.x = px;
      tx.y = py;
      tx.text = str;
      return tx
    }
    
    
    private function drawIndicators():void {
      var gr:Graphics = canvas2.graphics;
      gr.clear();
      gr.lineStyle(1,0);
      gr.beginFill(r << 16 | g << 8 | b);
      gr.drawRect(10,40,120,120);
      gr.endFill();
      gr.lineStyle();
      gr.beginFill(0x000000);
      gr.drawRect(140 + h / 360 * indicatorW,250, 1, -10);
      gr.endFill();
      gr.beginFill(0x000000);
      gr.drawRect(140 + s * indicatorW,330, 1, -10);
      gr.endFill();
      gr.beginFill(0x000000);
      gr.drawRect(140 + v * indicatorW,410, 1, -10);
      gr.endFill();
    }
    
    private function drawMeters():void {
      var gr:Graphics = canvas1.graphics;
      var pos:Number;
      var cp:ColorPoint = new ColorPoint;
      gr.clear();
      for (var i = 0; i < indicatorW; i ++) {
        pos = i / indicatorW;
        cp.setHSV(Math.floor(360 * pos), s,v);
        gr.beginFill(cp.hex);
        gr.drawRect(140 + i, 250, 1,20);
        gr.endFill();
        
        cp.setHSV(h, pos,v);
        gr.beginFill(cp.hex);
        gr.drawRect(140 + i, 330, 1,20);
        gr.endFill();
        
        cp.setHSV(h, s,pos);
        gr.beginFill(cp.hex);
        gr.drawRect(140 + i, 410, 1,20);
        gr.endFill();
      }
      
      canvas1.cacheAsBitmap = true;
    }
  }
}

ColorPoint.as

package {
  public class ColorPoint {
    private var _r:uint,_g:uint,_b:uint,_hex:uint;
    private var _h:Number,_s:Number,_v:Number;
    public function ColorPoint(...args) {
      if (args.length == 3) {
        setRGB(args[0], args[1], args[2]);
      } else if (args.length == 1){
        setHex(args[0]);
      } else {
        setHex(0);
      }
    }
    public function get hex():uint { return _hex }
    public function get r():uint { return _r }
    public function get g():uint { return _g }
    public function get b():uint { return _b }
    
    public function get h():Number { return _h }
    public function get s():Number { return _s }
    public function get v():Number { return _v }
    
    
    public function RGBdistance(col:ColorPoint):Number {
        return Math.sqrt((r - col.r) * (r - col.r) + (g - col.g) * (g - col.g) + (b - col.b) * (b - col.b))
    }
    
    public function HSVdistance(col:ColorPoint):Number {
        var hDist = ((h - col.h) % 180);
        var sDist = s  * 180 - col.s  * 180;
        var vDist = v  * 180 - col.v  * 180;
        return Math.sqrt(hDist * hDist + sDist * sDist + vDist * vDist)
    }
    
    public function setRGB(__r:uint, __g:uint, __b:uint) {
      _r = __r;
      _g = __g;
      _b = __b;
      
      // hsv
      var tr:Number = _r / 255, tg:Number = _g / 255, tb:Number = _b / 255;
      var min:Number = Math.min(tr, tg, tb);
      var max:Number = Math.max(tr, tg, tb);
      // hue
      if (min == max) {
        _h = 0;
      } else if (max == tr) {
        _h = (60 * (tg - tb) / (max -min) + 360) % 360; 
      } else if (max == tg) {
        _h = 60 * (tb -tr) / (max - min) + 120;
      } else {
        _h = 60 * (tr  - tg) / (max - min) + 240;
      }
      // saturation
      if (max == 0) {
        _s = 0;
      } else {
        _s = (max - min) / max;
      }
      // value
      _v = max;
      // hex
      _hex = (_r << 16)| (_g <<8) | _b;
    }
    
    public function setHSV(__h:Number, __s:Number, __v:Number) {
      _h = __h;
      _s = __s;
      _v = __v;
      
      // rgb
      var hi:uint, f:Number, p:Number, q:Number, t:Number;
      var tr:Number, tg:Number, tb:Number;
      
      hi = Math.floor(_h / 60) % 6;
      f = _h / 60 - Math.floor(_h / 60);
      p = v * (1 - s);
      q = v * (1 - f * s);
      t = v * (1 - (1 - f) * s);
      
      switch (hi) {
        case 0:
        tr = v; tg = t; tb = p;
        break;
        case 1:
        tr = q; tg = v; tb = p;
        break;
        case 2:
        tr = p; tg = v; tb = t;
        break;
        case 3:
        tr = p; tg = q; tb = v;
        break;
        case 4:
        tr = t; tg = p; tb = v;
        break;
        case 5:
        tr = v; tg = p; tb = q;
        break;
      }
      _r = Math.floor(tr * 255);
      _g = Math.floor(tg * 255);
      _b = Math.floor(tb * 255);
      
      // hex
      _hex = (_r << 16)| (_g <<8) | _b;
    }
    
    public function setHex(__hex:uint):void {
      setRGB((__hex >> 16) & 0x0000ff, (__hex >> 8) & 0x0000ff, __hex & 0x0000ff);
    }
  }
}