Major cleanup of JS sample code.
diff --git a/example/Tracer/dart/index.html b/example/Tracer/dart/index.html
index 65568d2..3d62186 100644
--- a/example/Tracer/dart/index.html
+++ b/example/Tracer/dart/index.html
@@ -4,18 +4,18 @@
<head>
<title>index</title>
</head>
-
- <body>
+
+ <body>
<canvas id="canvas" width="100" height="100" style="border:1px solid black"></canvas>
-
+
<div>
- Width: <input type="text" id="imageWidth" value="100">
+ Width: <input type="text" id="imageWidth" value="500">
</div>
<div>
- Height: <input type="text" id="imageHeight" value="100">
+ Height: <input type="text" id="imageHeight" value="500">
</div>
<div>
- Pixel Size: <input type="text" id="pixelSize" value="5,5">
+ Pixel Size: <input type="text" id="pixelSize" value="1,1">
</div>
<div>
Diffuse: <input type="checkbox" id="renderDiffuse" checked>
diff --git a/example/Tracer/js/Tracer.js b/example/Tracer/js/Tracer.js
index 5800e28..21475df 100644
--- a/example/Tracer/js/Tracer.js
+++ b/example/Tracer/js/Tracer.js
@@ -8,11 +8,13 @@
// untouched. This file also contains a copy of parts of the Prototype
// JavaScript framework which is used by the ray tracer.
+"use strict";
+var scope = this;
+
// Variable used to hold a number that can be used to verify that
// the scene was ray traced correctly.
var checkNumber;
-
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
@@ -24,20 +26,10 @@
// Prototype is freely distributable under the terms of an MIT-style license.
// For details, see the Prototype web site: http://prototype.conio.net/
-
-var Class = {
- create: function() {
- return function() {
- this.initialize.apply(this, arguments);
- }
- }
-};
-
-
Object.extend = function(destination, source) {
- for (var property in source) {
- destination[property] = source[property];
- }
+ Object.keys(source).forEach(function(p) {
+ destination[p] = source[p];
+ })
return destination;
};
@@ -64,224 +56,246 @@
// flog/background.js
// flog/engine.js
-
/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(scope.Flog) == 'undefined') {
+ var Flog = scope.Flog = { RayTracer: { Shape: {}, Material: {} } };
+}
+var Color =
Flog.RayTracer.Color = function(r, g, b) {
- this.red = r;
- this.green = g;
- this.blue = b;
+ this.red = (+r);
+ this.green = (+g);
+ this.blue = (+b);
+};
+Color.prototype = {
+ add: function(c){
+ return new Color(
+ +(this.red) + +(c.red),
+ +(this.green) + +(c.green),
+ +(this.blue) + +(c.blue)
+ );
+ },
+
+ addScalar_: function(s){
+ s = +s;
+ var result = new Color(
+ +(this.red) + s,
+ +(this.green) + s,
+ +(this.blue) + s);
+ result.limit();
+ return result;
+ },
+
+ subtract: function(c1, c2){
+ return new Color(
+ +(c1.red) - +(c2.red),
+ +(c1.green) - +(c2.green),
+ +(c1.blue) - +(c2.blue)
+ );
+ },
+
+ multiply_: function(c) {
+ return new Color(
+ +(this.red) * +(c.red),
+ +(this.green) * +(c.green),
+ +(this.blue) * +(c.blue)
+ );
+ },
+
+ multiplyScalar: function(f) {
+ f = +f;
+ return new Color(
+ +(this.red) * f,
+ +(this.green) * f,
+ +(this.blue) * f
+ );
+ },
+
+ divideFactor : function(c1, f) {
+ f = +f;
+ return new Color(
+ +(c1.red) / f,
+ +(c1.green) / f,
+ +(c1.blue) / f
+ );
+ },
+
+ limit: function() {
+ this.red = (this.red > 0.0) ?
+ ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
+ this.green = (this.green > 0.0) ?
+ ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
+ this.blue = (this.blue > 0.0) ?
+ ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
+ },
+
+ distance : function(color) {
+ var abs = Math.abs;
+ var d = +(abs(+(this.red) - +(color.red)) +
+ abs(+(this.green) - +(color.green)) +
+ abs(+(this.blue) - +(color.blue)));
+ return d;
+ },
+
+ blend: function(c, w){
+ return this.multiplyScalar(1-w).add(c.multiplyScalar(w));
+ },
+
+ brightness : function() {
+ var floor = Math.floor;
+ var r = floor(this.red*255);
+ var g = floor(this.green*255);
+ var b = floor(this.blue*255);
+ return (r * 77 + g * 150 + b * 29) >> 8;
+ },
+
+ toString : function () {
+ var floor = Math.floor;
+ var r = Math.floor(this.red*255);
+ var g = Math.floor(this.green*255);
+ var b = Math.floor(this.blue*255);
+ return "rgb("+ r +","+ g +","+ b +")";
+ }
};
-
-Flog.RayTracer.Color.prototype = {
- add : function(c1, c2){
- return new Flog.RayTracer.Color(c1.red + c2.red,
- c1.green + c2.green,
- c1.blue + c2.blue);
- },
-
- addScalar: function(c1, s){
- var result = new Flog.RayTracer.Color(
- c1.red + s, c1.green + s, c1.blue + s);
- result.limit();
- return result;
- },
-
- subtract: function(c1, c2){
- return new Flog.RayTracer.Color(c1.red - c2.red,
- c1.green - c2.green,
- c1.blue - c2.blue);
- },
-
- multiply : function(c1, c2) {
- return new Flog.RayTracer.Color(c1.red * c2.red,
- c1.green * c2.green,
- c1.blue * c2.blue);
- },
-
- multiplyScalar : function(c1, f) {
- return new Flog.RayTracer.Color(c1.red * f,
- c1.green * f,
- c1.blue * f);
- },
-
- divideFactor : function(c1, f) {
- return new Flog.RayTracer.Color(c1.red / f,
- c1.green / f,
- c1.blue / f);
- },
-
- limit: function() {
- this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
- this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
- this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
- },
-
- distance : function(color) {
- var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
- return d;
- },
-
- blend: function(c1, c2, w){
- result = Flog.RayTracer.Color.prototype.add(
- Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
- Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
- );
- return result;
- },
-
- brightness : function() {
- var r = Math.floor(this.red*255);
- var g = Math.floor(this.green*255);
- var b = Math.floor(this.blue*255);
- return (r * 77 + g * 150 + b * 29) >> 8;
- },
-
- toString : function () {
- var r = Math.floor(this.red*255);
- var g = Math.floor(this.green*255);
- var b = Math.floor(this.blue*255);
-
- return "rgb("+ r +","+ g +","+ b +")";
- }
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
+var Light =
Flog.RayTracer.Light = function (pos, color, intensity) {
this.position = pos;
this.color = color;
this.intensity = (intensity ? intensity : 10.0);
};
-
-Flog.RayTracer.Light.prototype = {
- toString : function () {
- return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
- }
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
-Flog.RayTracer.Vector = function (x, y, z) {
- this.x = x;
- this.y = y;
- this.z = z;
+Light.prototype = {
+ toString : function () {
+ return 'Light [' + this.position.x + ','
+ + this.position.y + ','
+ + this.position.z + ']';
+ }
};
+var Vector =
+Flog.RayTracer.Vector = function (x, y, z) {
+ this.x = +x;
+ this.y = +y;
+ this.z = +z;
+};
+Vector.prototype = {
+ copy: function(v){
+ this.x = +(v.x);
+ this.y = +(v.y);
+ this.z = +(v.z);
+ },
-Flog.RayTracer.Vector.prototype = {
- copy: function(vector){
- this.x = vector.x;
- this.y = vector.y;
- this.z = vector.z;
- },
+ normalize : function() {
+ var m = +(this.magnitude());
+ return new Vector(
+ +(+this.x / m),
+ +(+this.y / m),
+ +(+this.z / m)
+ );
+ },
- normalize : function() {
- var m = this.magnitude();
- return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
- },
+ magnitude : function() {
+ var x = +(this.x);
+ var y = +(this.y);
+ var z = +(this.z);
+ return +(Math.sqrt(+(x*x) + +(y*y) + +(z*z)));
+ },
- magnitude : function() {
- return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
- },
+ cross : function(w) {
+ return new Vector(
+ -this.z * w.y + this.y * w.z,
+ this.z * w.x - this.x * w.z,
+ -this.y * w.x + this.x * w.y);
+ },
- cross : function(w) {
- return new Flog.RayTracer.Vector(
- -this.z * w.y + this.y * w.z,
- this.z * w.x - this.x * w.z,
- -this.y * w.x + this.x * w.y);
- },
+ dot : function(w) {
+ return this.x * w.x + this.y * w.y + this.z * w.z;
+ },
- dot : function(w) {
- return this.x * w.x + this.y * w.y + this.z * w.z;
- },
+ add: function(w) {
+ var vx = +(this.x);
+ var vy = +(this.y);
+ var vz = +(this.z);
+ var wx = +(w.x);
+ var wy = +(w.y);
+ var wz = +(w.z);
+ return new Vector(+(wx + vx), +(wy + vy), +(wz + vz));
+ },
- add : function(v, w) {
- return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
- },
+ subtract: function(w) {
+ var vx = +(this.x);
+ var vy = +(this.y);
+ var vz = +(this.z);
+ var wx = +(w.x);
+ var wy = +(w.y);
+ var wz = +(w.z);
+ return new Vector(+(vx - wx), +(vy - wy), +(vz - wz));
+ },
- subtract : function(v, w) {
- if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
- return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
- },
+ multiplyVector_: function(w) {
+ return new Vector(+(this.x) * +(w.x),
+ +(this.y) * +(w.y),
+ +(this.z) * +(w.z));
+ },
- multiplyVector : function(v, w) {
- return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
- },
+ multiplyScalar: function(w) {
+ return new Vector(this.x * w,
+ this.y * w,
+ this.z * w);
+ },
- multiplyScalar : function(v, w) {
- return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
- },
+ toString : function () {
+ return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+ }
+};
- toString : function () {
- return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
- }
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
+var Ray =
Flog.RayTracer.Ray = function(pos, dir) {
this.position = pos;
this.direction = dir;
};
-
-
-Flog.RayTracer.Ray.prototype = {
- toString : function () {
- return 'Ray [' + this.position + ',' + this.direction + ']';
- }
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
-Flog.RayTracer.Scene = function() {
- this.camera = new Flog.RayTracer.Camera(
- new Flog.RayTracer.Vector(0,0,-5),
- new Flog.RayTracer.Vector(0,0,1),
- new Flog.RayTracer.Vector(0,1,0)
- );
- this.shapes = new Array();
- this.lights = new Array();
- this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
+Ray.prototype = {
+ toString : function () {
+ return 'Ray [' + this.position + ',' + this.direction + ']';
+ }
};
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
+var Scene =
+Flog.RayTracer.Scene = function() {
+ this.camera = new Camera(
+ new Vector(0,0,-5),
+ new Vector(0,0,1),
+ new Vector(0,1,0)
+ );
+ this.shapes = [];
+ this.lights = [];
+ this.background = new Background(new Color(0,0,0.5), 0.2);
+};
-Flog.RayTracer.Material.BaseMaterial = function (reflection, transparency, glass) {
+var BaseMaterial =
+Flog.RayTracer.Material.BaseMaterial = function(reflection,
+ transparency,
+ glass) {
this.gloss = glass;
this.transparency = transparency;
this.reflection = reflection;
this.refraction = 0.50;
this.hasTexture = false;
};
+BaseMaterial.prototype = {
+ wrapUp: function(t){
+ t = t % 2.0;
+ if(t < -1) t += 2.0;
+ if(t >= 1) t -= 2.0;
+ return t;
+ },
-Flog.RayTracer.Material.BaseMaterial.prototype = {
- wrapUp: function(t){
- t = t % 2.0;
- if(t < -1) t += 2.0;
- if(t >= 1) t -= 2.0;
- return t;
- },
-
- toString : function () {
- return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
- }
+ toString : function () {
+ return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+ }
}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
+var Solid =
Flog.RayTracer.Material.Solid = function(color, reflection, refraction, transparency, gloss) {
this.color = color;
this.reflection = reflection;
@@ -289,22 +303,27 @@
this.gloss = gloss;
this.hasTexture = false;
};
+Solid.prototype = Object.create(BaseMaterial.prototype, {
+ getColor: {
+ value: function() { return this.color; },
+ },
+ toString: {
+ value: function() {
+ return 'SolidMaterial [gloss=' + this.gloss +
+ ', transparency=' + this.transparency +
+ ', hasTexture=' + this.hasTexture +']';
+ }
+ }
+});
-
-Flog.RayTracer.Material.Solid.prototype = Object.create(Flog.RayTracer.Material.BaseMaterial.prototype);
-Flog.RayTracer.Material.Solid.prototype.getColor = function(u, v){
- return this.color;
-};
-
-Flog.RayTracer.Material.Solid.prototype.toString = function () {
- return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
-};
-
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
-Flog.RayTracer.Material.Chessboard = function(colorEven, colorOdd, reflection, transparency, gloss, density) {
+var Chessboard =
+Flog.RayTracer.Material.Chessboard = function(
+ colorEven,
+ colorOdd,
+ reflection,
+ transparency,
+ gloss,
+ density) {
this.colorEven = colorEven;
this.colorOdd = colorOdd;
this.reflection = reflection;
@@ -314,126 +333,113 @@
this.hasTexture = true;
};
-Flog.RayTracer.Material.Chessboard.prototype = Object.create(Flog.RayTracer.Material.BaseMaterial.prototype);
+Chessboard.prototype = Object.create(BaseMaterial.prototype, {
+ getColor: {
+ value: function(u, v){
+ var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+ if(t < 0.0) {
+ return this.colorEven;
+ } else {
+ return this.colorOdd;
+ }
+ }
+ },
+ toString: {
+ value: function(){
+ return 'ChessMaterial [gloss=' + this.gloss +
+ ', transparency=' + this.transparency +
+ ', hasTexture=' + this.hasTexture +']';
+ }
+ }
+});
-Flog.RayTracer.Material.Chessboard.prototype.getColor = function(u, v){
- var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
- if(t < 0.0)
- return this.colorEven;
- else
- return this.colorOdd;
-};
-
-Flog.RayTracer.Material.Chessboard.prototype.toString = function () {
- return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
-};
-
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
-
+var Sphere =
Flog.RayTracer.Shape.Sphere = function(pos, radius, material) {
this.radius = radius;
this.position = pos;
this.material = material;
};
+Sphere.prototype = {
-Flog.RayTracer.Shape.Sphere.prototype = {
- intersect: function(ray){
- var info = new Flog.RayTracer.IntersectionInfo();
- info.shape = this;
+ _foundIntersection: function(B, D, info, ray) {
+ info.isHit = true;
+ info.distance = (-B) - Math.sqrt(D);
+ info.position =
+ ray.position.add(ray.direction.multiplyScalar(info.distance));
+ info.normal = info.position.subtract(this.position).normalize();
+ info.color = this.material.getColor(0,0);
+ },
- var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
+ intersect: function(ray){
+ var info = new IntersectionInfo();
+ info.shape = this;
+ info.isHit = false;
- var B = dst.dot(ray.direction);
- var C = dst.dot(dst) - (this.radius * this.radius);
- var D = (B * B) - C;
+ var dst = ray.position.subtract(this.position);
+ var B = dst.dot(ray.direction);
+ var C = dst.dot(dst) - (this.radius * this.radius);
+ var D = (B * B) - C;
- if(D > 0){ // intersection!
- info.isHit = true;
- info.distance = (-B) - Math.sqrt(D);
- info.position = Flog.RayTracer.Vector.prototype.add(
- ray.position,
- Flog.RayTracer.Vector.prototype.multiplyScalar(
- ray.direction,
- info.distance
- )
- );
- info.normal = Flog.RayTracer.Vector.prototype.subtract(
- info.position,
- this.position
- ).normalize();
-
- info.color = this.material.getColor(0,0);
- } else {
- info.isHit = false;
- }
- return info;
- },
-
- toString : function () {
- return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+ if(D > 0) { // intersection!
+ this._foundIntersection(B, D, info, ray);
}
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+ return info;
+ },
-Flog.RayTracer.Shape.Plane = function(pos, d, material) {
+ toString : function () {
+ return 'Sphere [position=' + this.position +
+ ', radius=' + this.radius + ']';
+ }
+}
+
+var Plane =
+Plane = function(pos, d, material) {
this.position = pos;
this.d = d;
this.material = material;
};
+Plane.prototype = {
+ _hasTexture: function() {
+ },
+ intersect: function(ray){
+ var info = new IntersectionInfo();
+ var pos = this.position;
+ var mat = this.material;
-Flog.RayTracer.Shape.Plane.prototype = {
- intersect: function(ray){
- var info = new Flog.RayTracer.IntersectionInfo();
+ var Vd = pos.dot(ray.direction);
+ if(Vd == 0) return info; // no intersection
- var Vd = this.position.dot(ray.direction);
- if(Vd == 0) return info; // no intersection
+ var t = -(pos.dot(ray.position) + this.d) / Vd;
+ if(t <= 0) { return info; }
- var t = -(this.position.dot(ray.position) + this.d) / Vd;
- if(t <= 0) return info;
+ info.shape = this;
+ info.isHit = true;
+ info.position = ray.position.add(ray.direction.multiplyScalar(t));
+ info.normal = pos;
+ info.distance = t;
- info.shape = this;
- info.isHit = true;
- info.position = Flog.RayTracer.Vector.prototype.add(
- ray.position,
- Flog.RayTracer.Vector.prototype.multiplyScalar(
- ray.direction,
- t
- )
- );
- info.normal = this.position;
- info.distance = t;
-
- if(this.material.hasTexture){
- var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
- var vV = vU.cross(this.position);
- var u = info.position.dot(vU);
- var v = info.position.dot(vV);
- info.color = this.material.getColor(u,v);
- } else {
- info.color = this.material.getColor(0,0);
- }
-
- return info;
- },
-
- toString : function () {
- return 'Plane [' + this.position + ', d=' + this.d + ']';
+ if(mat.hasTexture){
+ var vU = new Vector(pos.y, pos.z, -pos.x);
+ var vV = vU.cross(pos);
+ var u = info.position.dot(vU);
+ var v = info.position.dot(vV);
+ info.color = mat.getColor(u,v);
+ } else {
+ info.color = mat.getColor(0,0);
}
-}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+ return info;
+ },
-Flog.RayTracer.IntersectionInfo = function() {
- this.color = new Flog.RayTracer.Color(0,0,0);
+ toString : function () {
+ return 'Plane [' + this.position + ', d=' + this.d + ']';
+ }
+}
+
+var IntersectionInfo =
+Flog.RayTracer.IntersectionInfo = function(shape) {
+ this.color = new Color(0,0,0);
this.isHit = false;
this.hitCount = 0;
this.shape = null;
@@ -442,61 +448,40 @@
this.color = null;
this.distance = null;
};
-
-Flog.RayTracer.IntersectionInfo.prototype = {
- toString : function () {
- return 'Intersection [' + this.position + ']';
- }
+IntersectionInfo.prototype = {
+ toString : function () {
+ return 'Intersection [' + this.position + ']';
+ }
}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+var Camera =
Flog.RayTracer.Camera = function(pos, lookAt, up) {
this.position = pos;
this.lookAt = lookAt;
this.up = up;
this.equator = lookAt.normalize().cross(this.up);
- this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
+ this.screen = this.position.add(this.lookAt);
};
+Camera.prototype = {
+ getRay: function(vx, vy){
+ var pos = this.screen.subtract(
+ this.equator.multiplyScalar(vx).subtract(this.up.multiplyScalar(vy))
+ );
+ pos.y = pos.y * -1;
+ var dir = pos.subtract(this.position);
+ return new Ray(pos, dir.normalize());
+ },
-Flog.RayTracer.Camera.prototype = {
- getRay: function(vx, vy){
- var pos = Flog.RayTracer.Vector.prototype.subtract(
- this.screen,
- Flog.RayTracer.Vector.prototype.subtract(
- Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
- Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
- )
- );
- pos.y = pos.y * -1;
- var dir = Flog.RayTracer.Vector.prototype.subtract(
- pos,
- this.position
- );
-
- var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
-
- return ray;
- },
-
- toString : function () {
- return 'Ray []';
- }
+ toString : function () { return 'Ray []'; }
}
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+var Background =
Flog.RayTracer.Background = function(color, ambience) {
this.color = color;
this.ambience = ambience;
};
-/* Fake a Flog.* namespace */
-if(typeof(Flog) == 'undefined') var Flog = {};
-if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
-
+var Engine =
Flog.RayTracer.Engine = function(options){
this.options = Object.extend({
canvasHeight: 100,
@@ -515,268 +500,241 @@
this.canvas = null;
/* TODO: dynamically include other scripts */
};
+Engine.prototype = {
+ setPixel: function(x, y, color){
+ var pxW, pxH;
+ pxW = this.options.pixelWidth;
+ pxH = this.options.pixelHeight;
+ if (this.canvas) {
+ this.canvas.fillStyle = color.toString();
+ this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
+ } else {
+ if (x === y) {
+ checkNumber += color.brightness();
+ }
+ // print(x * pxW, y * pxH, pxW, pxH);
+ }
+ },
-Flog.RayTracer.Engine.prototype = {
- setPixel: function(x, y, color){
- var pxW, pxH;
- pxW = this.options.pixelWidth;
- pxH = this.options.pixelHeight;
+ renderScene: function(scene, canvas){
+ checkNumber = 0;
+ /* Get canvas */
+ if (canvas) {
+ this.canvas = canvas.getContext("2d");
+ } else {
+ this.canvas = null;
+ }
- if (this.canvas) {
- this.canvas.fillStyle = color.toString();
- this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
- } else {
- if (x === y) {
- checkNumber += color.brightness();
- }
- // print(x * pxW, y * pxH, pxW, pxH);
+ var canvasHeight = this.options.canvasHeight;
+ var canvasWidth = this.options.canvasWidth;
+
+ for(var y=0; y < canvasHeight; y++){
+ for(var x=0; x < canvasWidth; x++){
+ var yp = y * 1.0 / canvasHeight * 2 - 1;
+
+ var xp = x * 1.0 / canvasWidth * 2 - 1;
+ var ray = scene.camera.getRay(xp, yp);
+ var color = this.getPixelColor(ray, scene);
+ this.setPixel(x, y, color);
+ }
+ }
+ if (canvas == null && checkNumber !== 2321) {
+ throw new Error("Scene rendered incorrectly");
+ }
+ },
+
+ getPixelColor: function(ray, scene){
+ var info = this.testIntersection(ray, scene, null);
+ if(info.isHit){
+ return this.rayTrace(info, ray, scene, 0);
+ }
+ return scene.background.color;
+ },
+
+ // FIXME(slightlyoff): HOT!
+ testIntersection: function(ray, scene, exclude){
+ var hits = 0;
+ var best = new IntersectionInfo();
+ best.distance = 2000;
+ var info;
+ var distance = 0|0;
+
+ var shapesLength = scene.shapes.length;
+ for(var i = 0; i < shapesLength; i++){
+ if(scene.shapes[i] !== exclude){
+ info = scene.shapes[i].intersect(ray);
+ distance = info.distance;
+ if(info.isHit && distance >= 0 && distance < best.distance) {
+ best = info;
+ hits++;
}
- },
+ }
+ }
+ best.hitCount = hits;
+ return best;
+ },
- renderScene: function(scene, canvas){
- checkNumber = 0;
- /* Get canvas */
- if (canvas) {
- this.canvas = canvas.getContext("2d");
- } else {
- this.canvas = null;
- }
+ getReflectionRay: function(P,N,V){
+ var c1 = -N.dot(V);
+ var R1 = N.multiplyScalar(2*c1).add(V);
+ return new Ray(P, R1);
+ },
- var canvasHeight = this.options.canvasHeight;
- var canvasWidth = this.options.canvasWidth;
+ rayTrace: function(info, ray, scene, depth) {
+ // Calc ambient
+ var color = info.color.multiplyScalar(scene.background.ambience);
+ var oldColor = color;
+ var shininess = Math.pow(10, info.shape.material.gloss + 1);
- for(var y=0; y < canvasHeight; y++){
- for(var x=0; x < canvasWidth; x++){
- var yp = y * 1.0 / canvasHeight * 2 - 1;
- var xp = x * 1.0 / canvasWidth * 2 - 1;
+ for(var i=0; i<scene.lights.length; i++) {
+ var light = scene.lights[i];
- var ray = scene.camera.getRay(xp, yp);
+ // Calc diffuse lighting
+ var v = light.position.subtract(info.position).normalize();
- var color = this.getPixelColor(ray, scene);
-
- this.setPixel(x, y, color);
- }
- }
- if (canvas == null && checkNumber !== 2321) {
- throw new Error("Scene rendered incorrectly");
- }
- },
-
- getPixelColor: function(ray, scene){
- var info = this.testIntersection(ray, scene, null);
- if(info.isHit){
- var color = this.rayTrace(info, ray, scene, 0);
- return color;
- }
- return scene.background.color;
- },
-
- testIntersection: function(ray, scene, exclude){
- var hits = 0;
- var best = new Flog.RayTracer.IntersectionInfo();
- best.distance = 2000;
-
- for(var i=0; i<scene.shapes.length; i++){
- var shape = scene.shapes[i];
-
- if(shape != exclude){
- var info = shape.intersect(ray);
- if(info.isHit && info.distance >= 0 && info.distance < best.distance){
- best = info;
- hits++;
- }
- }
- }
- best.hitCount = hits;
- return best;
- },
-
- getReflectionRay: function(P,N,V){
- var c1 = -N.dot(V);
- var R1 = Flog.RayTracer.Vector.prototype.add(
- Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
- V
- );
- return new Flog.RayTracer.Ray(P, R1);
- },
-
- rayTrace: function(info, ray, scene, depth){
- // Calc ambient
- var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
- var oldColor = color;
- var shininess = Math.pow(10, info.shape.material.gloss + 1);
-
- for(var i=0; i<scene.lights.length; i++){
- var light = scene.lights[i];
-
- // Calc diffuse lighting
- var v = Flog.RayTracer.Vector.prototype.subtract(
- light.position,
- info.position
- ).normalize();
-
- if(this.options.renderDiffuse){
- var L = v.dot(info.normal);
- if(L > 0.0){
- color = Flog.RayTracer.Color.prototype.add(
- color,
- Flog.RayTracer.Color.prototype.multiply(
- info.color,
- Flog.RayTracer.Color.prototype.multiplyScalar(
- light.color,
- L
- )
- )
- );
- }
- }
-
- // The greater the depth the more accurate the colours, but
- // this is exponentially (!) expensive
- if(depth <= this.options.rayDepth){
- // calculate reflection ray
- if(this.options.renderReflections && info.shape.material.reflection > 0)
- {
- var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
- var refl = this.testIntersection(reflectionRay, scene, info.shape);
-
- if (refl.isHit && refl.distance > 0){
- refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
- } else {
- refl.color = scene.background.color;
- }
-
- color = Flog.RayTracer.Color.prototype.blend(
- color,
- refl.color,
- info.shape.material.reflection
+ if(this.options.renderDiffuse){
+ var L = v.dot(info.normal);
+ if(L > 0){
+ color = color.add(
+ info.color.multiply_(
+ light.color.multiplyScalar(L)
+ )
);
+ }
+ }
+
+ // The greater the depth the more accurate the colours, but
+ // this is exponentially (!) expensive
+ if(depth <= this.options.rayDepth){
+ // calculate reflection ray
+ if(this.options.renderReflections &&
+ info.shape.material.reflection > 0) {
+
+ var reflectionRay = this.getReflectionRay(
+ info.position, info.normal, ray.direction);
+ var refl = this.testIntersection(reflectionRay, scene, info.shape);
+
+ if (refl.isHit && refl.distance > 0){
+ refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
+ } else {
+ refl.color = scene.background.color;
}
- // Refraction
- /* TODO */
- }
+ color = color.blend(
+ refl.color, info.shape.material.reflection
+ );
+ }
+ // Refraction
+ /* TODO */
+ }
- /* Render shadows and highlights */
+ /* Render shadows and highlights */
+ var shadowInfo = new IntersectionInfo();
- var shadowInfo = new Flog.RayTracer.IntersectionInfo();
+ if(this.options.renderShadows) {
+ var shadowRay = new Ray(info.position, v);
- if(this.options.renderShadows){
- var shadowRay = new Flog.RayTracer.Ray(info.position, v);
-
- shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
- if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
- var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
- var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
- color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
- }
- }
+ shadowInfo = this.testIntersection(shadowRay, scene,
+ info.shape);
+ if(shadowInfo.isHit && shadowInfo.shape != info.shape
+ /*&& shadowInfo.shape.type != 'PLANE'*/) {
+ var vA = color.multiplyScalar(0.5);
+ var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
+ color = vA.addScalar_(dB);
+ }
+ }
// Phong specular highlights
- if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
- var Lv = Flog.RayTracer.Vector.prototype.subtract(
- info.shape.position,
- light.position
- ).normalize();
-
- var E = Flog.RayTracer.Vector.prototype.subtract(
- scene.camera.position,
- info.shape.position
- ).normalize();
-
- var H = Flog.RayTracer.Vector.prototype.subtract(
- E,
- Lv
- ).normalize();
-
- var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
- color = Flog.RayTracer.Color.prototype.add(
- Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
- color
- );
+ if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0) {
+ var Lv = info.shape.position.subtract(light.position)
+ .normalize();
+ var E = scene.camera.position.subtract(info.shape.position)
+ .normalize();
+ var H = E.subtract(Lv).normalize();
+ var glossWeight =
+ Math.pow(Math.max(info.normal.dot(H), 0), shininess);
+ color = light.color.multiplyScalar(glossWeight).add(color);
}
- }
- color.limit();
- return color;
}
+ color.limit();
+ return color;
+ }
};
// 'event' null means that we are benchmarking
-function renderScene(event){
- var scene = new Flog.RayTracer.Scene();
+var renderScene =
+scope.renderScene = function(event){
+ var scene = new Scene();
- scene.camera = new Flog.RayTracer.Camera(
- new Flog.RayTracer.Vector(0, 0, -15),
- new Flog.RayTracer.Vector(-0.2, 0, 5),
- new Flog.RayTracer.Vector(0, 1, 0)
- );
+ scene.camera = new Camera(
+ new Vector(0, 0, -15),
+ new Vector(-0.2, 0, 5),
+ new Vector(0, 1, 0)
+ );
- scene.background = new Flog.RayTracer.Background(
- new Flog.RayTracer.Color(0.5, 0.5, 0.5),
- 0.4
- );
+ scene.background = new Background(new Color(0.5, 0.5, 0.5), 0.4 );
- var sphere = new Flog.RayTracer.Shape.Sphere(
- new Flog.RayTracer.Vector(-1.5, 1.5, 2),
- 1.5,
- new Flog.RayTracer.Material.Solid(
- new Flog.RayTracer.Color(0,0.5,0.5),
- 0.3,
- 0.0,
- 0.0,
- 2.0
- )
- );
+ var sphere = new Sphere(
+ new Vector(-1.5, 1.5, 2),
+ 1.5,
+ new Solid(
+ new Color(0,0.5,0.5),
+ 0.3,
+ 0.0,
+ 0.0,
+ 2.0
+ )
+ );
- var sphere1 = new Flog.RayTracer.Shape.Sphere(
- new Flog.RayTracer.Vector(1, 0.25, 1),
- 0.5,
- new Flog.RayTracer.Material.Solid(
- new Flog.RayTracer.Color(0.9,0.9,0.9),
- 0.1,
- 0.0,
- 0.0,
- 1.5
- )
- );
+ var sphere1 = new Sphere(
+ new Vector(1, 0.25, 1),
+ 0.5,
+ new Solid(
+ new Color(0.9,0.9,0.9),
+ 0.1,
+ 0.0,
+ 0.0,
+ 1.5
+ )
+ );
- var plane = new Flog.RayTracer.Shape.Plane(
- new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
- 1.2,
- new Flog.RayTracer.Material.Chessboard(
- new Flog.RayTracer.Color(1,1,1),
- new Flog.RayTracer.Color(0,0,0),
- 0.2,
- 0.0,
- 1.0,
- 0.7
- )
- );
+ var plane = new Plane(
+ new Vector(0.1, 0.9, -0.5).normalize(),
+ 1.2,
+ new Chessboard(
+ new Color(1,1,1),
+ new Color(0,0,0),
+ 0.2,
+ 0.0,
+ 1.0,
+ 0.7
+ )
+ );
- scene.shapes.push(plane);
- scene.shapes.push(sphere);
- scene.shapes.push(sphere1);
+ scene.shapes.push(plane);
+ scene.shapes.push(sphere);
+ scene.shapes.push(sphere1);
- var light = new Flog.RayTracer.Light(
- new Flog.RayTracer.Vector(5, 10, -1),
- new Flog.RayTracer.Color(0.8, 0.8, 0.8)
- );
+ var light = new Light(
+ new Vector(5, 10, -1),
+ new Color(0.8, 0.8, 0.8)
+ );
- var light1 = new Flog.RayTracer.Light(
- new Flog.RayTracer.Vector(-3, 5, -15),
- new Flog.RayTracer.Color(0.8, 0.8, 0.8),
- 100
- );
+ var light1 = new Light(
+ new Vector(-3, 5, -15),
+ new Color(0.8, 0.8, 0.8),
+ 100
+ );
- scene.lights.push(light);
- scene.lights.push(light1);
-
- var imageWidth, imageHeight, pixelSize;
- var renderDiffuse, renderShadows, renderHighlights, renderReflections;
- var canvas;
-
- if (typeof(event) == 'undefined' || event == null) {
+ scene.lights.push(light);
+ scene.lights.push(light1);
+
+ var imageWidth, imageHeight, pixelSize;
+ var renderDiffuse, renderShadows, renderHighlights, renderReflections;
+ var canvas;
+
+ if (typeof(event) == 'undefined' || event == null) {
imageWidth = 100;
imageHeight = 100;
pixelSize = "5,5".split(',');
@@ -795,22 +753,20 @@
renderReflections = document.getElementById('renderReflections').checked;
canvas = document.getElementById("canvas");
}
-
- var rayDepth = 2;//$F('rayDepth');
- var raytracer = new Flog.RayTracer.Engine(
- {
- canvasWidth: imageWidth,
- canvasHeight: imageHeight,
- pixelWidth: parseInt(pixelSize[0]),
- pixelHeight: parseInt(pixelSize[1]),
- "renderDiffuse": renderDiffuse,
- "renderHighlights": renderHighlights,
- "renderShadows": renderShadows,
- "renderReflections": renderReflections,
- "rayDepth": rayDepth
- }
- );
+ var rayDepth = 2;//$F('rayDepth');
- raytracer.renderScene(scene, canvas, 0);
-}
+ var raytracer = new Engine({
+ canvasWidth: imageWidth,
+ canvasHeight: imageHeight,
+ pixelWidth: parseInt(pixelSize[0]),
+ pixelHeight: parseInt(pixelSize[1]),
+ "renderDiffuse": renderDiffuse,
+ "renderHighlights": renderHighlights,
+ "renderShadows": renderShadows,
+ "renderReflections": renderReflections,
+ "rayDepth": rayDepth
+ });
+
+ raytracer.renderScene(scene, canvas, 0);
+};
diff --git a/example/Tracer/js/app.js b/example/Tracer/js/app.js
index ccead77..52cabec 100644
--- a/example/Tracer/js/app.js
+++ b/example/Tracer/js/app.js
@@ -1,12 +1,17 @@
+(function() {
+
var button = document.getElementById('render');
var canvas = document.getElementById('canvas');
var time = document.getElementById('time');
-
- button.addEventListener('click', function (e) {
+
+ var run = function(e) {
canvas.width = parseInt(document.getElementById('imageWidth').value);
canvas.height = parseInt(document.getElementById('imageHeight').value);
- var start = new Date();
+ canvas.getContext("2d").clearRect(0,0,canvas.width,canvas.height)
+ var start = performance.now();
renderScene(e);
- var stop = new Date();
- time.innerHTML = (stop - start).toString();
- });
\ No newline at end of file
+ var stop = performance.now();
+ time.innerHTML = Number(stop - start).toFixed(2).toString();
+ };
+ button.addEventListener('click', run);
+})();
diff --git a/example/Tracer/js/bench.js b/example/Tracer/js/bench.js
index 6034d4a..dda992c 100644
--- a/example/Tracer/js/bench.js
+++ b/example/Tracer/js/bench.js
@@ -1,12 +1,13 @@
var Benchmark = {
measureFor: function(f, timeMinimum) {
+ "use asm";
var elapsed = 0;
var iterations = 0;
- var start = new Date();
+ var start = +(performance.now());
while (elapsed < timeMinimum) {
iterations++;
f();
- elapsed = new Date() - start;
+ elapsed = +(+(performance.now()) - start);
}
return 1000 * elapsed / iterations;
},
@@ -25,6 +26,6 @@
report: function(name, warmup, exercise) {
var score = this.measure(warmup, exercise);
- print(name + "(RunTime): " + score + " us.");
+ print(name + "(RunTime): " + Number(score).toFixed(2) + " us.");
}
};
diff --git a/example/Tracer/js/index.html b/example/Tracer/js/index.html
index 6981f68..3b6fe8d 100644
--- a/example/Tracer/js/index.html
+++ b/example/Tracer/js/index.html
@@ -4,18 +4,18 @@
<head>
<title>index</title>
</head>
-
- <body>
+
+ <body>
<canvas id="canvas" width="100" height="100" style="border:1px solid black"></canvas>
-
+
<div>
- Width: <input type="text" id="imageWidth" value="100">
+ Width: <input type="text" id="imageWidth" value="500">
</div>
<div>
- Height: <input type="text" id="imageHeight" value="100">
+ Height: <input type="text" id="imageHeight" value="500">
</div>
<div>
- Pixel Size: <input type="text" id="pixelSize" value="5,5">
+ Pixel Size: <input type="text" id="pixelSize" value="1,1">
</div>
<div>
Diffuse: <input type="checkbox" id="renderDiffuse" checked>