RGB Color Cube
My friends and I had some discussions about mixing color theory and linear algebra. One of the things we talked about was having color components as base vectors in a coordinate system, and this is pretty much just a visualization of that.
Rendered in some faux-3d kind of way, nothing too interesting there.
package {
import flash.geom.Point;
import flash.filters.ConvolutionFilter;
import flash.filters.ColorMatrixFilter;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
[SWF(width = "465", height = "465", backgroundColor = "0x000000")]
public class FlashTest extends Sprite {
public function FlashTest() {
bmd = new BitmapData(400,400,true,0xff000000);
stage.addChild(new Bitmap(bmd));
stage.addEventListener(Event.ENTER_FRAME, doStuff);
}
private var bmd:BitmapData;
private var time:Number = 0;
private const TIMESTEP:Number = 0.01;
private const FRAMES_BETWEEN_ACTIONS:int = 0;
private var count:int = FRAMES_BETWEEN_ACTIONS;
private const BG_COLOR = 0xff222222;
private const BMD_HEIGHT:int = 400;
private const BMD_WIDTH:int = 400;
private const BMD_NUMPIX:int = 400*400;
private var pixels:Vector.<uint> = new Vector.<uint>(BMD_HEIGHT*BMD_WIDTH, true);
private var cmf:ColorMatrixFilter = new ColorMatrixFilter(
[0.9, 0, 0, 0, 0,
0, 0.9, 0, 0, 0,
0, 0, 0.9, 0, 0,
0, 0, 0, 0.97, 0]);
private function drawStuff(theta:Number):void {
// A density constant
var D:int = 10;
// Half width height and depth
var hW:int, hH:int, hD:int;
hW = hH = hD = D;
var X:Number, Y:Number, Z:Number;
var dispX:int, dispY:int, index:int, color:uint;
var sc:Number = 95/D;
var x:Number, y:Number, z:Number;
// Clear vector, or use trails?
if(true) {
var l:int = pixels.length;
while(--l >= 0) {
pixels[l] = BG_COLOR;
}
} else {
bmd.applyFilter(bmd, bmd.rect, new Point(), cmf);
pixels = bmd.getVector(bmd.rect);
}
for (X = -hW; X<=hW; ++X) {
for (Y = -hH; Y<=hH; ++Y) {
for (Z = -hD; Z<=hD; ++Z) {
// Set color based on coordinate
color = rgbaToHex((X+hW)/(2*hW), (Y+hH)/(2*hH), (Z+hD)/(2*hD), 0.9);
// Get transformed coordinate index
index = transPtIndex(X,Y,Z,theta,sc);
// Color a 2x2 square
if(index+BMD_WIDTH+1 < BMD_NUMPIX && index > 0) {
pixels[index] = color;
pixels[index+1] = color;
pixels[index+BMD_WIDTH] = color;
pixels[index+BMD_WIDTH+1] = color;
}
}
}
}
// Time to actually draw stuff
bmd.lock();
// Draw all colored points
bmd.setVector(bmd.rect, pixels);
// Define corners
var p1:Point = transPt(new Point(-hW, -hH), theta, sc, -hD);
var p2:Point = transPt(new Point(-hW, hH), theta, sc, -hD);
var p3:Point = transPt(new Point( hW, hH), theta, sc, -hD);
var p4:Point = transPt(new Point( hW, -hH), theta, sc, -hD);
var p5:Point = transPt(new Point(-hW, -hH), theta, sc, hD);
var p6:Point = transPt(new Point(-hW, hH), theta, sc, hD);
var p7:Point = transPt(new Point( hW, hH), theta, sc, hD);
var p8:Point = transPt(new Point( hW, -hH), theta, sc, hD);
// Draw lines between corners to create a boundary cube
efla(p1,p2,0xffffff,bmd);
efla(p2,p3,0xffffff,bmd);
efla(p3,p4,0xffffff,bmd);
efla(p4,p1,0xffffff,bmd);
efla(p5,p6,0xffffff,bmd);
efla(p6,p7,0xffffff,bmd);
efla(p7,p8,0xffffff,bmd);
efla(p8,p5,0xffffff,bmd);
efla(p1,p5,0xffffff,bmd);
efla(p2,p6,0xffffff,bmd);
efla(p3,p7,0xffffff,bmd);
efla(p4,p8,0xffffff,bmd);
bmd.unlock();
}
private function hslaToHex(h:Number,s:Number,l:Number,a:Number):uint {
var C:Number, X:Number, m:Number, H:Number;
m = 2*l-1;
m = m<0 ? -m : m;
C = (1-m)*s;
H = 6*h;
m = H%2 - 1;
m = m<0 ? -m : m;
X = C*(1-m);
m = l - C/2;
if(H <= 1) {
return rgbaToHex(C+m,X+m,m, a);
} else if (H <= 2) {
return rgbaToHex(X+m,C+m,m, a);
} else if (H <= 3) {
return rgbaToHex(m, C+m,X+m,a);
} else if (H <= 4) {
return rgbaToHex(m, X+m,C+m,a);
} else if (H <= 5) {
return rgbaToHex(X+m,m, C+m,a);
} else {
return rgbaToHex(C+m,m, X+m,a);
}
return 0x00000000;
}
private function hsvaToHex(h:Number,s:Number,v:Number,a:Number):uint {
var C:Number, X:Number, m:Number, H:Number;
C = v*s;
H = 6*h;
m = H%2 - 1;
m = m<0 ? -m : m;
X = C*(1-m);
m = v-C;
if(H <= 1) {
return rgbaToHex(C+m,X+m,m, a);
} else if (H <= 2) {
return rgbaToHex(X+m,C+m,m, a);
} else if (H <= 3) {
return rgbaToHex(m, C+m,X+m,a);
} else if (H <= 4) {
return rgbaToHex(m, X+m,C+m,a);
} else if (H <= 5) {
return rgbaToHex(X+m,m, C+m,a);
} else {
return rgbaToHex(C+m,m, X+m,a);
}
return 0x00000000;
}
private function rgbaToHex(r:Number,g:Number,b:Number,a:Number):uint {
return (0xff*a << 24) | (0xff*r << 16) | (0xff*g << 8) | 0xff*b;
}
// Function to transform a single point
private function transPt(p:Point, theta:Number, sc:Number, z:Number):Point {
var x:Number = p.x*Math.cos(theta) + p.y*Math.sin(theta);
var y:Number = p.y*Math.cos(theta) - p.x*Math.sin(theta);
z = -z;
p.x = 200+(x+y)*sc;
p.y = 200+((x-y)/3 + 1.2*z)*sc;
return p;
}
private function transPtIndex(x:Number, y:Number, z:Number, theta:Number, sc:Number):int {
var p:Point = transPt(new Point(x,y), theta, sc, z);
return int(p.x) + BMD_WIDTH*int(p.y);
}
private function doStuff(e:Event):void {
time += TIMESTEP;
if(++count <= FRAMES_BETWEEN_ACTIONS) {
return;
}
count = 0;
drawStuff(time);
}
private function efla(p1:Point, p2:Point, color:uint, bmd:BitmapData): void
{
var x:int = p1.x;
var y:int = p1.y;
var x2:int = p2.x;
var y2:int = p2.y;
var shortLen:int = y2-y;
var longLen:int = x2-x;
if ((shortLen ^ (shortLen >> 31)) - (shortLen >> 31) > (longLen ^ (longLen >> 31)) - (longLen >> 31))
{
shortLen ^= longLen;
longLen ^= shortLen;
shortLen ^= longLen;
var yLonger:Boolean = true;
}
else
{
yLonger = false;
}
var inc:int = longLen < 0 ? -1 : 1;
var multDiff:Number = longLen == 0 ? shortLen : shortLen / longLen;
if (yLonger)
{
for (var i:int = 0; i != longLen; i += inc)
{
bmd.setPixel(x + i*multDiff, y+i, color);
}
}
else
{
for (i = 0; i != longLen; i += inc)
{
bmd.setPixel(x+i, y+i*multDiff, color);
}
}
}
}
}