Thursday, December 17, 2009

My first PMatrix3D port to Processing.js

p.PMatrix3D = function PMatrix3D() {

this.set = function set( ) {
if ( arguments.length == 1 ) {
/*if (arguments[0] instanceof PMatrix2D) {
var src = arguments[0];
set ( src.m00, src.m01, 0, src.m02,
src.m10, src.m11, 0, src.m12,
0, 0, 1, 0,
0, 0, 0, 1 );
} else*/ if (arguments[0] instanceof PMatrix3D) {
var src = arguments[0];
set ( src.m00, src.m01, src.m02, src.m03,
src.m10, src.m11, src.m12, src.m13,
src.m20, src.m21, src.m22, src.m23,
src.m30, src.m31, src.m32, src.m33 );
} else if (arguments[0] instanceof Array) {
if (arguments[0].length == 6) {
var src = arguments[0];
set ( src[0], src[1], src[2],
src[3], src[4], src[5] );
} else if (arguments[0].length == 16) {
var src = arguments[0];
set ( src[0], src[1], src[2], src[3],
src[4], src[5], src[6], src[7],
src[8], src[9], src[10], src[11],
src[12], src[13], src[14], src[15] );
}
}
} else if ( arguments.length == 6 ) {
set ( arguments[0], arguments[1], 0, arguments[2],
arguments[3], arguments[4], 0, arguments[5],
0, 0, 1, 0,
0, 0, 0, 1 );
} else if ( arguments.length == 16 ) {
this.m00 = arguments[0]; this.m01 = arguments[1]; this.m02 = arguments[2]; this.m03 = arguments[3];
this.m10 = arguments[4]; this.m11 = arguments[5]; this.m12 = arguments[6]; this.m13 = arguments[7];
this.m20 = arguments[8]; this.m21 = arguments[9]; this.m22 = arguments[10]; this.m23 = arguments[11];
this.m30 = arguments[12]; this.m31 = arguments[13]; this.m32 = arguments[14]; this.m33 = arguments[15];
}
};

this.reset = function reset( ) {
this.set( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 );
};

if (arguments.length == 0) {
this.reset();
} else {
this.set(arguments);
}

this.translate = function translate() {
if (arguments.length == 2) {
translate(arguments[0], arguments[1], 0);
} else if (arguments.length == 3) {
m03 += arguments[0] * m00 + arguments[1] * m01 + arguments[2] * m02;
m13 += arguments[0] * m10 + arguments[1] * m11 + arguments[2] * m12;
m23 += arguments[0] * m20 + arguments[1] * m21 + arguments[2] * m22;
m33 += arguments[0] * m30 + arguments[1] * m31 + arguments[2] * m32;
}
};

this.rotate = function rotate() {
if ( arguments.length == 1 ) {
rotateZ(arguments[0]);
} else if ( arguments.length == 4 ) {
var c = cos(arguments[0]);
var s = sin(arguments[0]);
var t = 1 - c;

apply((t * arguments[1] * arguments[1]) + c, (t * arguments[1] * arguments[2]) - (s * arguments[3]),
(t * arguments[1] * arguments[3]) + (s * arguments[2]), 0, (t * arguments[1] * arguments[2]) + (s * arguments[3]),
(t * arguments[2] * arguments[2]) + c, (t * arguments[2] * arguments[3]) - (s * arguments[1]), 0,
(t * arguments[1] * arguments[3]) - (s * arguments[2]), (t * arguments[2] * arguments[3]) + (s * arguments[1]),
(t * arguments[3] * arguments[3]) + c, 0, 0, 0, 0, 1);
}
};

this.rotateX = function rotateX(angle) {
var c = cos(angle);
var s = sin(angle);
apply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1);
};

this.rotateY = function rotateY(angle) {
var c = cos(angle);
var s = sin(angle);
apply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1);
};

this.rotateZ = function rotateZ(angle) {
var c = cos(angle);
var s = sin(angle);
apply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
};

this.scale = function scale(sx, sy, sz) {
if (sx && !sy && !sz) {
sy = sz = sx;
} else if (sx && sy && !sz) {
sz = 1;
}
if (sx && sy && sz) {
m00 *= sx; m01 *= sy; m02 *= sz;
m10 *= sx; m11 *= sy; m12 *= sz;
m20 *= sx; m21 *= sy; m22 *= sz;
m30 *= sx; m31 *= sy; m32 *= sz;
}
};

this.skewX = function skewX(angle) {
var t = Math.tan(angle);
apply(1, t, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
};

this.skewY = function skewY(angle) {
var t = Math.tan(angle);
apply(1, 0, 0, 0,
t, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
};

this.apply = function apply() {
if (arguments.length == 1) {
if (arguments[0] instanceof PMatrix2D) {
apply(arguments[0].m00, arguments[0].m01, 0, arguments[0].m02,
arguments[0].m10, arguments[0].m11, 0, arguments[0].m12,
0, 0, 1, 0,
0, 0, 0, 1);
} else if (arguments[0] instanceof PMatrix3D) {
apply(arguments[0].m00, arguments[0].m01, arguments[0].m02, arguments[0].m03,
arguments[0].m10, arguments[0].m11, arguments[0].m12, arguments[0].m13,
arguments[0].m20, arguments[0].m21, arguments[0].m22, arguments[0].m23,
arguments[0].m30, arguments[0].m31, arguments[0].m32, arguments[0].m33);
}
} else if (arguments.length == 6) {
apply(arguments[0], arguments[1], 0, arguments[2],
arguments[3], arguments[4], 0, arguments[5],
0, 0, 1, 0,
0, 0, 0, 1);
} else if (arguments.length == 16) {
var n00 = arguments[0]; var n01 = arguments[1]; var n02 = arguments[2]; var n03 = arguments[3];
var n10 = arguments[4]; var n11 = arguments[5]; var n12 = arguments[6]; var n13 = arguments[7];
var n20 = arguments[8]; var n21 = arguments[9]; var n22 = arguments[10]; var n23 = arguments[11];
var n30 = arguments[12]; var n31 = arguments[13]; var n32 = arguments[14]; var n33 = arguments[15];

var r00 = m00 * n00 + m01 * n10 + m02 * n20 + m03 * n30;
var r01 = m00 * n01 + m01 * n11 + m02 * n21 + m03 * n31;
var r02 = m00 * n02 + m01 * n12 + m02 * n22 + m03 * n32;
var r03 = m00 * n03 + m01 * n13 + m02 * n23 + m03 * n33;

var r10 = m10 * n00 + m11 * n10 + m12 * n20 + m13 * n30;
var r11 = m10 * n01 + m11 * n11 + m12 * n21 + m13 * n31;
var r12 = m10 * n02 + m11 * n12 + m12 * n22 + m13 * n32;
var r13 = m10 * n03 + m11 * n13 + m12 * n23 + m13 * n33;

var r20 = m20 * n00 + m21 * n10 + m22 * n20 + m23 * n30;
var r21 = m20 * n01 + m21 * n11 + m22 * n21 + m23 * n31;
var r22 = m20 * n02 + m21 * n12 + m22 * n22 + m23 * n32;
var r23 = m20 * n03 + m21 * n13 + m22 * n23 + m23 * n33;

var r30 = m30 * n00 + m31 * n10 + m32 * n20 + m33 * n30;
var r31 = m30 * n01 + m31 * n11 + m32 * n21 + m33 * n31;
var r32 = m30 * n02 + m31 * n12 + m32 * n22 + m33 * n32;
var r33 = m30 * n03 + m31 * n13 + m32 * n23 + m33 * n33;

m00 = r00; m01 = r01; m02 = r02; m03 = r03;
m10 = r10; m11 = r11; m12 = r12; m13 = r13;
m20 = r20; m21 = r21; m22 = r22; m23 = r23;
m30 = r30; m31 = r31; m32 = r32; m33 = r33;
}
};

this.preApply = function preApply() {
apply(arguments);
};

this.mult = function mult(source, target) {
if (source != target && (source instanceof PVector || source instanceof Array)) {
var x, y, z, w;
var tx, ty, tz;
if (source instanceof PVector) {
x = source.x;
y = source.y;
z = source.z;
w = 1;
if (!target) {
target = new PVector();
}
} else if (source instanceof Array) {
x = source[0];
y = source[1];
z = source[2];
w = source[3] || 1;
if (target.length != 3 && target.length != 4) {
target = new Array();
}
}
target[source instanceof PVector ? x : 0] = m00 * x + m01 * y + m02 * z + m03 * w;
target[source instanceof PVector ? y : 1] = m10 * x + m11 * y + m12 * z + m13 * w;
target[source instanceof PVector ? z : 2] = m20 * x + m21 * y + m22 * z + m23 * w;
if (target.length == 4) {
target[3] = m30 * x + m31 * y + m32 * z + m33 * w;
}
}
return target;
};

this.multX = function multX(x, y, z, w) {
return m00 * x + m01 * y + (z ? m02 * z : 0) + (w ? m03 * w : m03);
};

this.multY = function multY(x, y, z, w) {
return m10 * x + m11 * y + (z ? m12 * z : 0) + (w ? m13 * w : m13);
};

this.multZ = function multZ(x, y, z, w) {
return m20 * x + m21 * y + (z ? m22 * z : 0) + (w ? m23 * w : m23);
};

this.multW = function multW(x, y, z, w) {
return m30 * x + m31 * y + m32 * z + (w ? m33 * w : m33);
};

this.transpose = function transpose() {
var temp;
temp = m01; m01 = m10; m10 = temp;
temp = m02; m02 = m20; m20 = temp;
temp = m03; m03 = m30; m30 = temp;
temp = m12; m12 = m21; m21 = temp;
temp = m13; m13 = m31; m31 = temp;
temp = m23; m23 = m32; m32 = temp;
};

this.invert = function invert() {
var determinant = determinant();
if (determinant == 0) {
return false;
}

// first row
var t00 = determinant3x3(m11, m12, m13, m21, m22, m23, m31, m32, m33);
var t01 = -determinant3x3(m10, m12, m13, m20, m22, m23, m30, m32, m33);
var t02 = determinant3x3(m10, m11, m13, m20, m21, m23, m30, m31, m33);
var t03 = -determinant3x3(m10, m11, m12, m20, m21, m22, m30, m31, m32);

// second row
var t10 = -determinant3x3(m01, m02, m03, m21, m22, m23, m31, m32, m33);
var t11 = determinant3x3(m00, m02, m03, m20, m22, m23, m30, m32, m33);
var t12 = -determinant3x3(m00, m01, m03, m20, m21, m23, m30, m31, m33);
var t13 = determinant3x3(m00, m01, m02, m20, m21, m22, m30, m31, m32);

// third row
var t20 = determinant3x3(m01, m02, m03, m11, m12, m13, m31, m32, m33);
var t21 = -determinant3x3(m00, m02, m03, m10, m12, m13, m30, m32, m33);
var t22 = determinant3x3(m00, m01, m03, m10, m11, m13, m30, m31, m33);
var t23 = -determinant3x3(m00, m01, m02, m10, m11, m12, m30, m31, m32);

// fourth row
var t30 = -determinant3x3(m01, m02, m03, m11, m12, m13, m21, m22, m23);
var t31 = determinant3x3(m00, m02, m03, m10, m12, m13, m20, m22, m23);
var t32 = -determinant3x3(m00, m01, m03, m10, m11, m13, m20, m21, m23);
var t33 = determinant3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22);

// transpose and divide by the determinant
m00 = t00 / determinant;
m01 = t10 / determinant;
m02 = t20 / determinant;
m03 = t30 / determinant;

m10 = t01 / determinant;
m11 = t11 / determinant;
m12 = t21 / determinant;
m13 = t31 / determinant;

m20 = t02 / determinant;
m21 = t12 / determinant;
m22 = t22 / determinant;
m23 = t32 / determinant;

m30 = t03 / determinant;
m31 = t13 / determinant;
m32 = t23 / determinant;
m33 = t33 / determinant;

return true;
}

var determinant3x3 = function determinant3x3(t00, t01, t02, t10, t11, t12, t20, t21, t22) {
return (t00 * (t11 * t22 - t12 * t21) +
t01 * (t12 * t20 - t10 * t22) +
t02 * (t10 * t21 - t11 * t20));
}

this.determinant = function determinant() {
var f = m00 * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32)
- m13 * m22 * m31
- m11 * m23 * m32
- m12 * m21 * m33);
f -= m01 * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32)
- m13 * m22 * m30
- m10 * m23 * m32
- m12 * m20 * m33);
f += m02 * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31)
- m13 * m21 * m30
- m10 * m23 * m31
- m11 * m20 * m33);
f -= m03 * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31)
- m12 * m21 * m30
- m10 * m22 * m31
- m11 * m20 * m32);
return f ;
}

var max = function max(a, b) {
return (a > b) ? a : b;
};

var abs = function abs(a) {
return (a < 0) ? -a : a;
};

var sin = function sin(angle) {
return Math.sin(angle);
};

var cos = function cos(angle) {
return Math.cos(angle);
};

};

0.3 Release

0.3 released.

In this release, I was planning to do rotate, translate, scale, and other features that relates to transformation of 3D stuffs. Unfortunately, I can't follow what the original Processing did for implementing these features. But at least, I found out they all rely on a PMatrix3D/PMatrix2D class to centralize codes (I guess). This PMatrix3D class is missing in Processing.js. Therefore, I decided to make PMatrix3D ready before moving forward to utilizing the class.

PMatrix3D is not listed as a feature in Processing/Processing.js reference. Therefore, I read through the original Processing's PMatrix3D class and ports the class to Processing.js

Here's the updated commit. And here's the code itself.

Wednesday, December 16, 2009

Working with Patches

Today, I am into creating a patch for FireFox. Following this exercise, I am able to make a change and do incremental builds as listed in the lab. It's just to type make in a folder that's inside the tree. But every time it returned with an error, and this time they were all the same error unlike last time I tried to build Firefox. I figured I should build it on another box because of my previous build experiences. At the moment, I only had a laptop available, and I can see the build lines flies up a lot slower than they were in the other box. I got it compiled with just 2 tries.

Now, running Nightly, every time I browse to another webpage, the console would generate a bunch of "Hello World". I just did a printf statement in a random function in a random .cpp file. Now that's something satifying =]

Creating a patch from the changes I made is an easy task. It only requires 1 command to do the work.
hg diff -p -U 8 . > patch.txt

Now to test the patch if it works, I reverted the changes by replacing the file with the backup file I made earlier. Using another command to apply the patch:
patch -p1 < patch.txt

It successfully changes the file to the one I modified.

The last command to try is backing out the patch with
patch -R -p1 < patch.txt

Again, 1 command and it worked. I liked this lab far better than the one I had before =].

Tuesday, December 15, 2009

"Simple" Firefox Build

A few days ago, I decided to build FireFox with my machine at home. Of course, I would not know where to approach this without being in an Open Source course. I thought it will be an easy task just like downloading the source and type 1 command asking Windows to compile, as described in this Simple FireFox Build tutorial.

Of course, Simple Firefox build does not equal to easy and fast. I had to install Win 7 SDK for Visual Studio which took a loooong time to install (a little less than 3GB installation file). In the mean while, I can simultaneously download the FireFox source code through the MozillaBuild package. At first, I didn't know how much time it will take to clone, I thought it was stuck and killed the process. (Stupid #1) When I clone it again, I thought it was stuck at the same place again, good thing I checked the size of the folder and realized the folder is growing. My gosh the FireFox source codes is 2GB of files. Both tasks together took over an hour to have them ready for the build! (BTW, my only ISP here, Rogers Communication Inc. has a monthly bandwidth of 60GB; there goes 10% of my monthly usage in 1 hour)

When I start to build and run the make file, another problem came. I tried couple of times to compile, and it returned an error. I did couple of searches for the error and they suggest that there should not be a space in between my build path. I was so sure that I didn't have a space in the path, but then suddenly, I remembered my username has a space in it. (yes I did read all instruction before downloading the MozillaBuild, Stupid #2).

Now, after fixing the path, the build process went a little bit further. This time, it says I'm trying to build for a newer platform than what the SDK version is capable of. I just downloaded the newest Win 7 SDK, why is this happening? All of the sudden, there's a Windows update icon poped up in the notification area, tell me that Visual Studio 2008 SP1 is ready to be installed. Oops, did I just assumed I have an updated Visual Studio? So I went ahead and applied the update. I read Win 7 SDK depends on Visual Studio 2008 SP1 and here comes Stupid #3. I didn't even think of retry the build before I uninstall/reinstall the SDK for the new VS. If it works even without this process, I could have saved an hour of time.

Finally, the build process looks good. It is bursting out alot of progress reports. Until it froze at one line. I wait and wait for it to move on, thinking that this file must be huge. I sat there and wait for 5 min, 10 min, 30 min, 1 hr, 2hr, 3hr!! Knowing that for a XP machine, it requires 2 hours to complete. I waited for 3 hours for that 1 line to complete thinking there must be something wrong. I finally gave up waiting and retried and it got stuck again but on another line. Of course, this time I retried the build right away. This happened about 10 times, and it finally finished building. It didn't cost 2 hours. More like 10 minutes. (Windows doesn't suck that much after all) Anyhow, my computer built the latest nightly, minefield v3.7alpre. Now that I think back the errors, it might be because the CPU is a new designed Athlon II x4, the processor might not be synchronizing its cores as the compiler wants it to.

Anyway, remember where I start? Simple Firefox Build. Simple it is, but it took me the whole night trying to figure out the cause of the errors that some of them could have been avoided if it's better documented. Like the size of Firefox source and the time it takes to build on different machines. Overall, this Firefox build experience is not a great one. But now, I can play around with the source.

Tuesday, November 24, 2009

Modified HashMap

var HashMap = function HashMap( map ) {
this.data = [];
this.curKey = 0;
this._size = 0;

if (arguments.length == 1) {
// copy map to data
}

this.put = function put( key, value ) {
var rv;
if (typeof key == "object") {
if(!key._HashMap_key){
key._HashMap_key = "_HashMapKey_" + this.curKey++;
}
rv = this.data[key._HashMap_key];
this.data[key._HashMap_key] = value;
} else {
rv = this.data[typeof key + key];
this.data[typeof key + key] = value;
}
this._size = rv ? this._size : this._size + 1;
return rv;
}

this.get = function get( key ) {
if (typeof key == "object") {
return this.data[key._HashMap_key];
}
return this.data[typeof key + key];
}
}

Original HashMap

HashMap = function(){
this._dict = {};
}
HashMap.prototype._shared = {id: 1};
HashMap.prototype.put = function put(key, value){
if(typeof key == "object"){
if(!key.hasOwnProperty._id){
key
.hasOwnProperty = function(key){
return Object.prototype.hasOwnProperty.call(this, key);
}
key
.hasOwnProperty._id = this._shared.id++;
}
this._dict[key.hasOwnProperty._id] = value;
}else{
this._dict[key] = value;
}
return this; // for chaining
}
HashMap.prototype.get = function get(key){
if(typeof key == "object"){
return this._dict[key.hasOwnProperty._id];
}
return this._dict[key];
}

Map Implementation

// linking the key-value-pairs is optional
// if no argument is provided, linkItems === undefined, i.e. !== false
// --> linking will be enabled
function Map(linkItems) {
this.current = undefined;
this.size = 0;

if(linkItems === false)
this.disableLinking();
}

Map.noop = function() {
return this;
};

Map.illegal = function() {
throw new Error("illegal operation for maps without linking");
};

// map initialisation from existing object
// doesn't add inherited properties if not explicitly instructed to:
// omitting foreignKeys means foreignKeys === undefined, i.e. == false
// --> inherited properties won't be added
Map.from = function(obj, foreignKeys) {
var map = new Map;

for(var prop in obj) {
if(foreignKeys || obj.hasOwnProperty(prop))
map
.put(prop, obj[prop]);
}

return map;
};

Map.prototype.disableLinking = function() {
this.link = Map.noop;
this.unlink = Map.noop;
this.disableLinking = Map.noop;
this.next = Map.illegal;
this.key = Map.illegal;
this.value = Map.illegal;
this.removeAll = Map.illegal;

return this;
};

// overwrite in Map instance if necessary
Map.prototype.hash = function(value) {
return (typeof value) + ' ' + (value instanceof Object ?
(value.__hash || (value.__hash = ++arguments.callee.current)) :
value
.toString());
};

Map.prototype.hash.current = 0;

// --- mapping functions

Map.prototype.get = function(key) {
var item = this[this.hash(key)];
return item === undefined ? undefined : item.value;
};

Map.prototype.put = function(key, value) {
var hash = this.hash(key);

if(this[hash] === undefined) {
var item = { key : key, value : value };
this[hash] = item;

this.link(item);
++this.size;
}
else this[hash].value = value;

return this;
};

Map.prototype.remove = function(key) {
var hash = this.hash(key);
var item = this[hash];

if(item !== undefined) {
--this.size;
this.unlink(item);

delete this[hash];
}

return this;
};

// only works if linked
Map.prototype.removeAll = function() {
while(this.size)
this.remove(this.key());

return this;
};

// --- linked list helper functions

Map.prototype.link = function(item) {
if(this.size == 0) {
item
.prev = item;
item
.next = item;
this.current = item;
}
else {
item
.prev = this.current.prev;
item
.prev.next = item;
item
.next = this.current;
this.current.prev = item;
}
};

Map.prototype.unlink = function(item) {
if(this.size == 0)
this.current = undefined;
else {
item
.prev.next = item.next;
item
.next.prev = item.prev;
if(item === this.current)
this.current = item.next;
}
};

// --- iterator functions - only work if map is linked

Map.prototype.next = function() {
this.current = this.current.next;
};

Map.prototype.key = function() {
return this.current.key;
};

Map.prototype.value = function() {
return this.current.value;
};