diff options
Diffstat (limited to 'includes/js/dojox/gfx3d/object.js')
-rw-r--r-- | includes/js/dojox/gfx3d/object.js | 1092 |
1 files changed, 0 insertions, 1092 deletions
diff --git a/includes/js/dojox/gfx3d/object.js b/includes/js/dojox/gfx3d/object.js deleted file mode 100644 index e574c91..0000000 --- a/includes/js/dojox/gfx3d/object.js +++ /dev/null @@ -1,1092 +0,0 @@ -if(!dojo._hasResource["dojox.gfx3d.object"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojox.gfx3d.object"] = true; -dojo.provide("dojox.gfx3d.object"); - -dojo.require("dojox.gfx"); -dojo.require("dojox.gfx3d.lighting"); -dojo.require("dojox.gfx3d.scheduler"); -dojo.require("dojox.gfx3d.vector"); -dojo.require("dojox.gfx3d.gradient"); - -// FIXME: why the global "out" var here? -var out = function(o, x){ - if(arguments.length > 1){ - // console.debug("debug:", o); - o = x; - } - var e = {}; - for(var i in o){ - if(i in e){ continue; } - // console.debug("debug:", i, typeof o[i], o[i]); - } -}; - -dojo.declare("dojox.gfx3d.Object", null, { - constructor: function(){ - // summary: a Object object, which knows how to map - // 3D objects to 2D shapes. - - // object: Object: an abstract Object object - // (see dojox.gfx3d.defaultEdges, - // dojox.gfx3d.defaultTriangles, - // dojox.gfx3d.defaultQuads - // dojox.gfx3d.defaultOrbit - // dojox.gfx3d.defaultCube - // or dojox.gfx3d.defaultCylinder) - this.object = null; - - // matrix: dojox.gfx3d.matrix: world transform - this.matrix = null; - // cache: buffer for intermediate result, used late for draw() - this.cache = null; - // renderer: a reference for the Viewport - this.renderer = null; - // parent: a reference for parent, Scene or Viewport object - this.parent = null; - - // strokeStyle: Object: a stroke object - this.strokeStyle = null; - // fillStyle: Object: a fill object or texture object - this.fillStyle = null; - // shape: dojox.gfx.Shape: an underlying 2D shape - this.shape = null; - }, - - setObject: function(newObject){ - // summary: sets a Object object - // object: Object: an abstract Object object - // (see dojox.gfx3d.defaultEdges, - // dojox.gfx3d.defaultTriangles, - // dojox.gfx3d.defaultQuads - // dojox.gfx3d.defaultOrbit - // dojox.gfx3d.defaultCube - // or dojox.gfx3d.defaultCylinder) - this.object = dojox.gfx.makeParameters(this.object, newObject); - return this; - }, - - setTransform: function(matrix){ - // summary: sets a transformation matrix - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx3d.matrix.Matrix - // constructor for a list of acceptable arguments) - this.matrix = dojox.gfx3d.matrix.clone(matrix ? dojox.gfx3d.matrix.normalize(matrix) : dojox.gfx3d.identity, true); - return this; // self - }, - - // apply left & right transformation - - applyRightTransform: function(matrix){ - // summary: multiplies the existing matrix with an argument on right side - // (this.matrix * matrix) - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx.matrix.Matrix - // constructor for a list of acceptable arguments) - return matrix ? this.setTransform([this.matrix, matrix]) : this; // self - }, - applyLeftTransform: function(matrix){ - // summary: multiplies the existing matrix with an argument on left side - // (matrix * this.matrix) - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx.matrix.Matrix - // constructor for a list of acceptable arguments) - return matrix ? this.setTransform([matrix, this.matrix]) : this; // self - }, - - applyTransform: function(matrix){ - // summary: a shortcut for dojox.gfx.Shape.applyRightTransform - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx.matrix.Matrix - // constructor for a list of acceptable arguments) - return matrix ? this.setTransform([this.matrix, matrix]) : this; // self - }, - - setFill: function(fill){ - // summary: sets a fill object - // (the default implementation is to delegate to - // the underlying 2D shape). - // fill: Object: a fill object - // (see dojox.gfx.defaultLinearGradient, - // dojox.gfx.defaultRadialGradient, - // dojox.gfx.defaultPattern, - // dojo.Color - // or dojox.gfx.MODEL) - this.fillStyle = fill; - return this; - }, - - setStroke: function(stroke){ - // summary: sets a stroke object - // (the default implementation simply ignores it) - // stroke: Object: a stroke object - // (see dojox.gfx.defaultStroke) - this.strokeStyle = stroke; - return this; - }, - - toStdFill: function(lighting, normal){ - return (this.fillStyle && typeof this.fillStyle['type'] != "undefined") ? lighting[this.fillStyle.type](normal, this.fillStyle.finish, this.fillStyle.color) : this.fillStyle; - }, - - invalidate: function(){ - this.renderer.addTodo(this); - }, - - destroy: function(){ - if(this.shape){ - var p = this.shape.getParent(); - if(p){ - p.remove(this.shape); - } - this.shape = null; - } - }, - - // All the 3D objects need to override the following virtual functions: - // render, getZOrder, getOutline, draw, redraw if necessary. - - render: function(camera){ - throw "Pure virtual function, not implemented"; - }, - - draw: function(lighting){ - throw "Pure virtual function, not implemented"; - }, - - getZOrder: function(){ - return 0; - }, - - getOutline: function(){ - return null; - } - -}); - -dojo.declare("dojox.gfx3d.Scene", dojox.gfx3d.Object, { - // summary: the Scene is just a containter. - // note: we have the following assumption: - // all objects in the Scene are not overlapped with other objects - // outside of the scene. - constructor: function(){ - // summary: a containter of other 3D objects - this.objects= []; - this.todos = []; - this.schedule = dojox.gfx3d.scheduler.zOrder; - this._draw = dojox.gfx3d.drawer.conservative; - }, - - setFill: function(fill){ - this.fillStyle = fill; - dojo.forEach(this.objects, function(item){ - item.setFill(fill); - }); - return this; - }, - - setStroke: function(stroke){ - this.strokeStyle = stroke; - dojo.forEach(this.objects, function(item){ - item.setStroke(stroke); - }); - return this; - }, - - render: function(camera, deep){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - if(deep){ - this.todos = this.objects; - } - dojo.forEach(this.todos, function(item){ item.render(m, deep); }); - }, - - draw: function(lighting){ - this.objects = this.schedule(this.objects); - this._draw(this.todos, this.objects, this.renderer); - }, - - addTodo: function(newObject){ - // FIXME: use indexOf? - if(dojo.every(this.todos, function(item){ return item != newObject; })){ - this.todos.push(newObject); - this.invalidate(); - } - }, - - invalidate: function(){ - this.parent.addTodo(this); - }, - - getZOrder: function(){ - var zOrder = 0; - dojo.forEach(this.objects, function(item){ zOrder += item.getZOrder(); }); - return (this.objects.length > 1) ? zOrder / this.objects.length : 0; - } -}); - - -dojo.declare("dojox.gfx3d.Edges", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic edge in 3D viewport - this.object = dojo.clone(dojox.gfx3d.defaultEdges); - }, - - setObject: function(newObject, /* String, optional */ style){ - // summary: setup the object - // newObject: Array of points || Object - // style: String, optional - this.object = dojox.gfx.makeParameters(this.object, (newObject instanceof Array) ? { points: newObject, style: style } : newObject); - return this; - }, - - getZOrder: function(){ - var zOrder = 0; - dojo.forEach(this.cache, function(item){ zOrder += item.z;} ); - return (this.cache.length > 1) ? zOrder / this.cache.length : 0; - }, - - render: function(camera){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - this.cache = dojo.map(this.object.points, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - }, - - draw: function(){ - var c = this.cache; - if(this.shape){ - this.shape.setShape("") - }else{ - this.shape = this.renderer.createPath(); - } - var p = this.shape.setAbsoluteMode("absolute"); - - if(this.object.style == "strip" || this.object.style == "loop"){ - p.moveTo(c[0].x, c[0].y); - dojo.forEach(c.slice(1), function(item){ - p.lineTo(item.x, item.y); - }); - if(this.object.style == "loop"){ - p.closePath(); - } - }else{ - for(var i = 0; i < this.cache.length; ){ - p.moveTo(c[i].x, c[i].y); - i ++; - p.lineTo(c[i].x, c[i].y); - i ++; - } - } - // FIXME: doe setFill make sense here? - p.setStroke(this.strokeStyle); - } -}); - -dojo.declare("dojox.gfx3d.Orbit", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic edge in 3D viewport - this.object = dojo.clone(dojox.gfx3d.defaultOrbit); - }, - - render: function(camera){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - var angles = [0, Math.PI/4, Math.PI/3]; - var center = dojox.gfx3d.matrix.multiplyPoint(m, this.object.center); - var marks = dojo.map(angles, function(item){ - return {x: this.center.x + this.radius * Math.cos(item), - y: this.center.y + this.radius * Math.sin(item), z: this.center.z}; - }, this.object); - - marks = dojo.map(marks, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - - var normal = dojox.gfx3d.vector.normalize(marks); - - marks = dojo.map(marks, function(item){ - return dojox.gfx3d.vector.substract(item, center); - }); - - // Use the algorithm here: - // http://www.3dsoftware.com/Math/PlaneCurves/EllipseAlgebra/ - // After we normalize the marks, the equation is: - // a x^2 + 2b xy + cy^2 + f = 0: let a = 1 - // so the final equation is: - // [ xy, y^2, 1] * [2b, c, f]' = [ -x^2 ]' - - var A = { - xx: marks[0].x * marks[0].y, xy: marks[0].y * marks[0].y, xz: 1, - yx: marks[1].x * marks[1].y, yy: marks[1].y * marks[1].y, yz: 1, - zx: marks[2].x * marks[2].y, zy: marks[2].y * marks[2].y, zz: 1, - dx: 0, dy: 0, dz: 0 - }; - var b = dojo.map(marks, function(item){ - return -Math.pow(item.x, 2); - }); - - // X is 2b, c, f - var X = dojox.gfx3d.matrix.multiplyPoint(dojox.gfx3d.matrix.invert(A),b[0], b[1], b[2]); - var theta = Math.atan2(X.x, 1 - X.y) / 2; - - // rotate the marks back to the canonical form - var probes = dojo.map(marks, function(item){ - return dojox.gfx.matrix.multiplyPoint(dojox.gfx.matrix.rotate(-theta), item.x, item.y); - }); - - // we are solving the equation: Ax = b - // A = [x^2, y^2] X = [1/a^2, 1/b^2]', b = [1, 1]' - // so rx = Math.sqrt(1/ ( inv(A)[1:] * b ) ); - // so ry = Math.sqrt(1/ ( inv(A)[2:] * b ) ); - - var a = Math.pow(probes[0].x, 2); - var b = Math.pow(probes[0].y, 2); - var c = Math.pow(probes[1].x, 2); - var d = Math.pow(probes[1].y, 2); - - // the invert matrix is - // 1/(ad -bc) [ d, -b; -c, a]; - var rx = Math.sqrt( (a*d - b*c)/ (d-b) ); - var ry = Math.sqrt( (a*d - b*c)/ (a-c) ); - - this.cache = {cx: center.x, cy: center.y, rx: rx, ry: ry, theta: theta, normal: normal}; - }, - - draw: function(lighting){ - if(this.shape){ - this.shape.setShape(this.cache); - } else { - this.shape = this.renderer.createEllipse(this.cache); - } - this.shape.applyTransform(dojox.gfx.matrix.rotateAt(this.cache.theta, this.cache.cx, this.cache.cy)) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, this.cache.normal)); - } -}); - -dojo.declare("dojox.gfx3d.Path3d", dojox.gfx3d.Object, { - // This object is still very immature ! - constructor: function(){ - // summary: a generic line - // (this is a helper object, which is defined for convenience) - this.object = dojo.clone(dojox.gfx3d.defaultPath3d); - this.segments = []; - this.absolute = true; - this.last = {}; - this.path = ""; - }, - - _collectArgs: function(array, args){ - // summary: converts an array of arguments to plain numeric values - // array: Array: an output argument (array of numbers) - // args: Array: an input argument (can be values of Boolean, Number, dojox.gfx.Point, or an embedded array of them) - for(var i = 0; i < args.length; ++i){ - var t = args[i]; - if(typeof(t) == "boolean"){ - array.push(t ? 1 : 0); - }else if(typeof(t) == "number"){ - array.push(t); - }else if(t instanceof Array){ - this._collectArgs(array, t); - }else if("x" in t && "y" in t){ - array.push(t.x); - array.push(t.y); - } - } - }, - - // a dictionary, which maps segment type codes to a number of their argemnts - _validSegments: {m: 3, l: 3, z: 0}, - - _pushSegment: function(action, args){ - // summary: adds a segment - // action: String: valid SVG code for a segment's type - // args: Array: a list of parameters for this segment - var group = this._validSegments[action.toLowerCase()]; - if(typeof(group) == "number"){ - if(group){ - if(args.length >= group){ - var segment = {action: action, args: args.slice(0, args.length - args.length % group)}; - this.segments.push(segment); - } - }else{ - var segment = {action: action, args: []}; - this.segments.push(segment); - } - } - }, - - moveTo: function(){ - // summary: formes a move segment - var args = []; - this._collectArgs(args, arguments); - this._pushSegment(this.absolute ? "M" : "m", args); - return this; // self - }, - lineTo: function(){ - // summary: formes a line segment - var args = []; - this._collectArgs(args, arguments); - this._pushSegment(this.absolute ? "L" : "l", args); - return this; // self - }, - - closePath: function(){ - // summary: closes a path - this._pushSegment("Z", []); - return this; // self - }, - - render: function(camera){ - // TODO: we need to get the ancestors' matrix - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - // iterate all the segments and convert them to 2D canvas - // TODO consider the relative mode - var path = "" - var _validSegments = this._validSegments; - dojo.forEach(this.segments, function(item){ - path += item.action; - for(var i = 0; i < item.args.length; i+= _validSegments[item.action.toLowerCase()] ){ - var pt = dojox.gfx3d.matrix.multiplyPoint(m, item.args[i], item.args[i+1], item.args[i+2]) - path += " " + pt.x + " " + pt.y; - } - }); - - this.cache = path; - }, - - _draw: function(){ - return this.parent.createPath(this.cache); - } -}); - -dojo.declare("dojox.gfx3d.Triangles", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic triangle - // (this is a helper object, which is defined for convenience) - this.object = dojo.clone(dojox.gfx3d.defaultTriangles); - }, - - setObject: function(newObject, /* String, optional */ style){ - // summary: setup the object - // newObject: Array of points || Object - // style: String, optional - if(newObject instanceof Array){ - this.object = dojox.gfx.makeParameters(this.object, { points: newObject, style: style } ); - } else { - this.object = dojox.gfx.makeParameters(this.object, newObject); - } - return this; - }, - render: function(camera){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - var c = dojo.map(this.object.points, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - this.cache = []; - var pool = c.slice(0, 2); - var center = c[0]; - if(this.object.style == "strip"){ - dojo.forEach(c.slice(2), function(item){ - pool.push(item); - pool.push(pool[0]); - this.cache.push(pool); - pool = pool.slice(1, 3); - }, this); - } else if(this.object.style == "fan"){ - dojo.forEach(c.slice(2), function(item){ - pool.push(item); - pool.push(center); - this.cache.push(pool); - pool = [center, item]; - }, this); - } else { - for(var i = 0; i < c.length; ){ - this.cache.push( [ c[i], c[i+1], c[i+2], c[i] ]); - i += 3; - } - } - }, - - draw: function(lighting){ - // use the BSP to schedule - this.cache = dojox.gfx3d.scheduler.bsp(this.cache, function(it){ return it; }); - if(this.shape){ - this.shape.clear(); - } else { - this.shape = this.renderer.createGroup(); - } - dojo.forEach(this.cache, function(item){ - this.shape.createPolyline(item) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.vector.normalize(item))); - }, this); - }, - - getZOrder: function(){ - var zOrder = 0; - dojo.forEach(this.cache, function(item){ - zOrder += (item[0].z + item[1].z + item[2].z) / 3; }); - return (this.cache.length > 1) ? zOrder / this.cache.length : 0; - } -}); - -dojo.declare("dojox.gfx3d.Quads", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic triangle - // (this is a helper object, which is defined for convenience) - this.object = dojo.clone(dojox.gfx3d.defaultQuads); - }, - - setObject: function(newObject, /* String, optional */ style){ - // summary: setup the object - // newObject: Array of points || Object - // style: String, optional - this.object = dojox.gfx.makeParameters(this.object, (newObject instanceof Array) ? { points: newObject, style: style } : newObject ); - return this; - }, - render: function(camera){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - var c = dojo.map(this.object.points, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - this.cache = []; - if(this.object.style == "strip"){ - var pool = c.slice(0, 2); - for(var i = 2; i < c.length; ){ - pool = pool.concat( [ c[i], c[i+1], pool[0] ] ); - this.cache.push(pool); - pool = pool.slice(2,4); - i += 2; - } - }else{ - for(var i = 0; i < c.length; ){ - this.cache.push( [c[i], c[i+1], c[i+2], c[i+3], c[i] ] ); - i += 4; - } - } - }, - - draw: function(lighting){ - // use the BSP to schedule - this.cache = dojox.gfx3d.scheduler.bsp(this.cache, function(it){ return it; }); - if(this.shape){ - this.shape.clear(); - }else{ - this.shape = this.renderer.createGroup(); - } - // using naive iteration to speed things up a bit by avoiding function call overhead - for(var x=0; x<this.cache.length; x++){ - this.shape.createPolyline(this.cache[x]) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.vector.normalize(this.cache[x]))); - } - /* - dojo.forEach(this.cache, function(item){ - this.shape.createPolyline(item) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.vector.normalize(item))); - }, this); - */ - }, - - getZOrder: function(){ - var zOrder = 0; - // using naive iteration to speed things up a bit by avoiding function call overhead - for(var x=0; x<this.cache.length; x++){ - var i = this.cache[x]; - zOrder += (i[0].z + i[1].z + i[2].z + i[3].z) / 4; - } - /* - dojo.forEach(this.cache, function(item){ - zOrder += (item[0].z + item[1].z + item[2].z + item[3].z) / 4; }); - */ - return (this.cache.length > 1) ? zOrder / this.cache.length : 0; - } -}); - -dojo.declare("dojox.gfx3d.Polygon", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic triangle - // (this is a helper object, which is defined for convenience) - this.object = dojo.clone(dojox.gfx3d.defaultPolygon); - }, - - setObject: function(newObject){ - // summary: setup the object - // newObject: Array of points || Object - this.object = dojox.gfx.makeParameters(this.object, (newObject instanceof Array) ? {path: newObject} : newObject) - return this; - }, - - render: function(camera){ - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - this.cache = dojo.map(this.object.path, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - // add the first point to close the polyline - this.cache.push(this.cache[0]); - }, - - draw: function(lighting){ - if(this.shape){ - this.shape.setShape({points: this.cache}); - }else{ - this.shape = this.renderer.createPolyline({points: this.cache}); - } - - this.shape.setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.matrix.normalize(this.cache))); - }, - - getZOrder: function(){ - var zOrder = 0; - // using naive iteration to speed things up a bit by avoiding function call overhead - for(var x=0; x<this.cache.length; x++){ - zOrder += this.cache[x].z; - } - return (this.cache.length > 1) ? zOrder / this.cache.length : 0; - }, - - getOutline: function(){ - return this.cache.slice(0, 3); - } -}); - -dojo.declare("dojox.gfx3d.Cube", dojox.gfx3d.Object, { - constructor: function(){ - // summary: a generic triangle - // (this is a helper object, which is defined for convenience) - this.object = dojo.clone(dojox.gfx3d.defaultCube); - this.polygons = []; - }, - - setObject: function(newObject){ - // summary: setup the object - // newObject: Array of points || Object - this.object = dojox.gfx.makeParameters(this.object, newObject); - }, - - render: function(camera){ - // parse the top, bottom to get 6 polygons: - var a = this.object.top; - var g = this.object.bottom; - var b = {x: g.x, y: a.y, z: a.z}; - var c = {x: g.x, y: g.y, z: a.z}; - var d = {x: a.x, y: g.y, z: a.z}; - var e = {x: a.x, y: a.y, z: g.z}; - var f = {x: g.x, y: a.y, z: g.z}; - var h = {x: a.x, y: g.y, z: g.z}; - var polygons = [a, b, c, d, e, f, g, h]; - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - var p = dojo.map(polygons, function(item){ - return dojox.gfx3d.matrix.multiplyPoint(m, item); - }); - a = p[0]; b = p[1]; c = p[2]; d = p[3]; e = p[4]; f = p[5]; g = p[6]; h = p[7]; - this.cache = [[a, b, c, d, a], [e, f, g, h, e], [a, d, h, e, a], [d, c, g, h, d], [c, b, f, g, c], [b, a, e, f, b]]; - }, - - draw: function(lighting){ - // use bsp to sort. - this.cache = dojox.gfx3d.scheduler.bsp(this.cache, function(it){ return it; }); - // only the last 3 polys are visible. - var cache = this.cache.slice(3); - - if(this.shape){ - this.shape.clear(); - }else{ - this.shape = this.renderer.createGroup(); - } - for(var x=0; x<cache.length; x++){ - this.shape.createPolyline(cache[x]) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.vector.normalize(cache[x]))); - } - /* - dojo.forEach(cache, function(item){ - this.shape.createPolyline(item) - .setStroke(this.strokeStyle) - .setFill(this.toStdFill(lighting, dojox.gfx3d.vector.normalize(item))); - }, this); - */ - }, - - getZOrder: function(){ - var top = this.cache[0][0]; - var bottom = this.cache[1][2]; - return (top.z + bottom.z) / 2; - } -}); - - -dojo.declare("dojox.gfx3d.Cylinder", dojox.gfx3d.Object, { - constructor: function(){ - this.object = dojo.clone(dojox.gfx3d.defaultCylinder); - }, - - render: function(camera){ - // get the bottom surface first - var m = dojox.gfx3d.matrix.multiply(camera, this.matrix); - var angles = [0, Math.PI/4, Math.PI/3]; - var center = dojox.gfx3d.matrix.multiplyPoint(m, this.object.center); - var marks = dojo.map(angles, function(item){ - return {x: this.center.x + this.radius * Math.cos(item), - y: this.center.y + this.radius * Math.sin(item), z: this.center.z}; - }, this.object); - - marks = dojo.map(marks, function(item){ - return dojox.gfx3d.vector.substract(dojox.gfx3d.matrix.multiplyPoint(m, item), center); - }); - - // Use the algorithm here: - // http://www.3dsoftware.com/Math/PlaneCurves/EllipseAlgebra/ - // After we normalize the marks, the equation is: - // a x^2 + 2b xy + cy^2 + f = 0: let a = 1 - // so the final equation is: - // [ xy, y^2, 1] * [2b, c, f]' = [ -x^2 ]' - - var A = { - xx: marks[0].x * marks[0].y, xy: marks[0].y * marks[0].y, xz: 1, - yx: marks[1].x * marks[1].y, yy: marks[1].y * marks[1].y, yz: 1, - zx: marks[2].x * marks[2].y, zy: marks[2].y * marks[2].y, zz: 1, - dx: 0, dy: 0, dz: 0 - }; - var b = dojo.map(marks, function(item){ - return -Math.pow(item.x, 2); - }); - - // X is 2b, c, f - var X = dojox.gfx3d.matrix.multiplyPoint(dojox.gfx3d.matrix.invert(A), b[0], b[1], b[2]); - var theta = Math.atan2(X.x, 1 - X.y) / 2; - - // rotate the marks back to the canonical form - var probes = dojo.map(marks, function(item){ - return dojox.gfx.matrix.multiplyPoint(dojox.gfx.matrix.rotate(-theta), item.x, item.y); - }); - - // we are solving the equation: Ax = b - // A = [x^2, y^2] X = [1/a^2, 1/b^2]', b = [1, 1]' - // so rx = Math.sqrt(1/ ( inv(A)[1:] * b ) ); - // so ry = Math.sqrt(1/ ( inv(A)[2:] * b ) ); - - var a = Math.pow(probes[0].x, 2); - var b = Math.pow(probes[0].y, 2); - var c = Math.pow(probes[1].x, 2); - var d = Math.pow(probes[1].y, 2); - - // the invert matrix is - // 1/(ad - bc) [ d, -b; -c, a]; - var rx = Math.sqrt((a * d - b * c) / (d - b)); - var ry = Math.sqrt((a * d - b * c) / (a - c)); - if(rx < ry){ - var t = rx; - rx = ry; - ry = t; - theta -= Math.PI/2; - } - - var top = dojox.gfx3d.matrix.multiplyPoint(m, - dojox.gfx3d.vector.sum(this.object.center, {x: 0, y:0, z: this.object.height})); - - var gradient = this.fillStyle.type == "constant" ? this.fillStyle.color - : dojox.gfx3d.gradient(this.renderer.lighting, this.fillStyle, this.object.center, this.object.radius, Math.PI, 2 * Math.PI, m); - if(isNaN(rx) || isNaN(ry) || isNaN(theta)){ - // in case the cap is invisible (parallel to the incident vector) - rx = this.object.radius, ry = 0, theta = 0; - } - this.cache = {center: center, top: top, rx: rx, ry: ry, theta: theta, gradient: gradient}; - }, - - draw: function(){ - var c = this.cache, v = dojox.gfx3d.vector, m = dojox.gfx.matrix, - centers = [c.center, c.top], normal = v.substract(c.top, c.center); - if(v.dotProduct(normal, this.renderer.lighting.incident) > 0){ - centers = [c.top, c.center]; - normal = v.substract(c.center, c.top); - } - - var color = this.renderer.lighting[this.fillStyle.type](normal, this.fillStyle.finish, this.fillStyle.color), - d = Math.sqrt( Math.pow(c.center.x - c.top.x, 2) + Math.pow(c.center.y - c.top.y, 2) ); - - if(this.shape){ - this.shape.clear(); - }else{ - this.shape = this.renderer.createGroup(); - } - - this.shape.createPath("") - .moveTo(0, -c.rx) - .lineTo(d, -c.rx) - .lineTo(d, c.rx) - .lineTo(0, c.rx) - .arcTo(c.ry, c.rx, 0, true, true, 0, -c.rx) - .setFill(c.gradient).setStroke(this.strokeStyle) - .setTransform([m.translate(centers[0]), - m.rotate(Math.atan2(centers[1].y - centers[0].y, centers[1].x - centers[0].x))]); - - if(c.rx > 0 && c.ry > 0){ - this.shape.createEllipse({cx: centers[1].x, cy: centers[1].y, rx: c.rx, ry: c.ry}) - .setFill(color).setStroke(this.strokeStyle) - .applyTransform(m.rotateAt(c.theta, centers[1])); - } - } -}); - - -// the ultimate container of 3D world -dojo.declare("dojox.gfx3d.Viewport", dojox.gfx.Group, { - constructor: function(){ - // summary: a viewport/container for 3D objects, which knows - // the camera and lightings - - // matrix: dojox.gfx3d.matrix: world transform - // dimension: Object: the dimension of the canvas - this.dimension = null; - - // objects: Array: all 3d Objects - this.objects = []; - // todos: Array: all 3d Objects that needs to redraw - this.todos = []; - - // FIXME: memory leak? - this.renderer = this; - // Using zOrder as the default scheduler - this.schedule = dojox.gfx3d.scheduler.zOrder; - this.draw = dojox.gfx3d.drawer.conservative; - // deep: boolean, true means the whole viewport needs to re-render, redraw - this.deep = false; - - // lights: Array: an array of light objects - this.lights = []; - this.lighting = null; - }, - - setCameraTransform: function(matrix){ - // summary: sets a transformation matrix - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx.matrix.Matrix - // constructor for a list of acceptable arguments) - this.camera = dojox.gfx3d.matrix.clone(matrix ? dojox.gfx3d.matrix.normalize(matrix) : dojox.gfx3d.identity, true); - this.invalidate(); - return this; // self - }, - - applyCameraRightTransform: function(matrix){ - // summary: multiplies the existing matrix with an argument on right side - // (this.matrix * matrix) - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx3d.matrix.Matrix - // constructor for a list of acceptable arguments) - return matrix ? this.setCameraTransform([this.camera, matrix]) : this; // self - }, - - applyCameraLeftTransform: function(matrix){ - // summary: multiplies the existing matrix with an argument on left side - // (matrix * this.matrix) - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx3d.matrix.Matrix - // constructor for a list of acceptable arguments) - return matrix ? this.setCameraTransform([matrix, this.camera]) : this; // self - }, - - applyCameraTransform: function(matrix){ - // summary: a shortcut for dojox.gfx3d.Object.applyRightTransform - // matrix: dojox.gfx3d.matrix.Matrix: a matrix or a matrix-like object - // (see an argument of dojox.gfx3d.matrix.Matrix - // constructor for a list of acceptable arguments) - return this.applyCameraRightTransform(matrix); // self - }, - - setLights: function(/* Array || Object */lights, /* Color, optional */ ambient, - /* Color, optional */ specular){ - // summary: set the lights - // lights: Array: an array of light object - // or lights object - // ambient: Color: an ambient object - // specular: Color: an specular object - this.lights = (lights instanceof Array) ? {sources: lights, ambient: ambient, specular: specular} : lights; - var view = {x: 0, y: 0, z: 1}; - - this.lighting = new dojox.gfx3d.lighting.Model(view, this.lights.sources, - this.lights.ambient, this.lights.specular); - this.invalidate(); - return this; - }, - - addLights: function(lights){ - // summary: add new light/lights to the viewport. - // lights: Array || light object: light object(s) - return this.setLights(this.lights.sources.concat(lights)); - }, - - addTodo: function(newObject){ - // NOTE: Viewport implements almost the same addTodo, - // except calling invalidate, since invalidate is used as - // any modification needs to redraw the object itself, call invalidate. - // then call render. - if(dojo.every(this.todos, - function(item){ - return item != newObject; - } - )){ - this.todos.push(newObject); - } - }, - - invalidate: function(){ - this.deep = true; - this.todos = this.objects; - }, - - setDimensions: function(dim){ - if(dim){ - this.dimension = { - width: dojo.isString(dim.width) ? parseInt(dim.width) : dim.width, - height: dojo.isString(dim.height) ? parseInt(dim.height) : dim.height - }; - }else{ - this.dimension = null; - } - }, - - render: function(){ - // summary: iterate all children and call their render callback function. - if(!this.todos.length){ return; } - // console.debug("Viewport::render"); - var m = dojox.gfx3d.matrix; - - // Iterate the todos and call render to prepare the rendering: - for(var x=0; x<this.todos.length; x++){ - this.todos[x].render(dojox.gfx3d.matrix.normalize([ - m.cameraRotateXg(180), - m.cameraTranslate(0, this.dimension.height, 0), - this.camera, - ]), this.deep); - } - - this.objects = this.schedule(this.objects); - this.draw(this.todos, this.objects, this); - this.todos = []; - this.deep = false; - } - -}); - -//FIXME: Viewport cannot masquerade as a Group -dojox.gfx3d.Viewport.nodeType = dojox.gfx.Group.nodeType; - -dojox.gfx3d._creators = { - // summary: object creators - createEdges: function(edges, style){ - // summary: creates an edge object - // line: Object: a edge object (see dojox.gfx3d.defaultPath) - return this.create3DObject(dojox.gfx3d.Edges, edges, style); // dojox.gfx3d.Edge - }, - createTriangles: function(tris, style){ - // summary: creates an edge object - // line: Object: a edge object (see dojox.gfx3d.defaultPath) - return this.create3DObject(dojox.gfx3d.Triangles, tris, style); // dojox.gfx3d.Edge - }, - createQuads: function(quads, style){ - // summary: creates an edge object - // line: Object: a edge object (see dojox.gfx3d.defaultPath) - return this.create3DObject(dojox.gfx3d.Quads, quads, style); // dojox.gfx3d.Edge - }, - createPolygon: function(points){ - // summary: creates an triangle object - // points: Array of points || Object - return this.create3DObject(dojox.gfx3d.Polygon, points); // dojox.gfx3d.Polygon - }, - - createOrbit: function(orbit){ - // summary: creates an triangle object - // points: Array of points || Object - return this.create3DObject(dojox.gfx3d.Orbit, orbit); // dojox.gfx3d.Cube - }, - - createCube: function(cube){ - // summary: creates an triangle object - // points: Array of points || Object - return this.create3DObject(dojox.gfx3d.Cube, cube); // dojox.gfx3d.Cube - }, - - createCylinder: function(cylinder){ - // summary: creates an triangle object - // points: Array of points || Object - return this.create3DObject(dojox.gfx3d.Cylinder, cylinder); // dojox.gfx3d.Cube - }, - - createPath3d: function(path){ - // summary: creates an edge object - // line: Object: a edge object (see dojox.gfx3d.defaultPath) - return this.create3DObject(dojox.gfx3d.Path3d, path); // dojox.gfx3d.Edge - }, - createScene: function(){ - // summary: creates an triangle object - // line: Object: a triangle object (see dojox.gfx3d.defaultPath) - return this.create3DObject(dojox.gfx3d.Scene); // dojox.gfx3d.Scene - }, - - create3DObject: function(objectType, rawObject, style){ - // summary: creates an instance of the passed shapeType class - // shapeType: Function: a class constructor to create an instance of - // rawShape: Object: properties to be passed in to the classes "setShape" method - var obj = new objectType(); - this.adopt(obj); - if(rawObject){ obj.setObject(rawObject, style); } - return obj; // dojox.gfx3d.Object - }, - // todo : override the add/remove if necessary - adopt: function(obj){ - // summary: adds a shape to the list - // shape: dojox.gfx.Shape: a shape - obj.renderer = this.renderer; // obj._setParent(this, null); more TODOs HERER? - obj.parent = this; - this.objects.push(obj); - this.addTodo(obj); - return this; - }, - abandon: function(obj, silently){ - // summary: removes a shape from the list - // silently: Boolean?: if true, do not redraw a picture yet - for(var i = 0; i < this.objects.length; ++i){ - if(this.objects[i] == obj){ - this.objects.splice(i, 1); - } - } - // if(this.rawNode == shape.rawNode.parentNode){ - // this.rawNode.removeChild(shape.rawNode); - // } - // obj._setParent(null, null); - obj.parent = null; - return this; // self - }, - - - setScheduler: function(scheduler){ - this.schedule = scheduler; - }, - - setDrawer: function(drawer){ - this.draw = drawer; - } -}; - -dojo.extend(dojox.gfx3d.Viewport, dojox.gfx3d._creators); -dojo.extend(dojox.gfx3d.Scene, dojox.gfx3d._creators); -delete dojox.gfx3d._creators; - - -//FIXME: extending dojox.gfx.Surface and masquerading Viewport as Group is hacky! - -// Add createViewport to dojox.gfx.Surface -dojo.extend(dojox.gfx.Surface, { - createViewport: function(){ - //FIXME: createObject is non-public method! - var viewport = this.createObject(dojox.gfx3d.Viewport, null, true); - //FIXME: this may not work with dojox.gfx.Group !! - viewport.setDimensions(this.getDimensions()); - return viewport; - } -}); - -} |