diff --git a/.gitignore b/.gitignore index 1e4ac1d96..0a74f86e1 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,9 @@ player/static/mfr/* !.gitkeep .cache + +# JS +####################### + +node_modules +node_modules/* diff --git a/AUTHORS.rst b/AUTHORS.rst index fbcecc79a..4f9754228 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -33,3 +33,4 @@ Contributors - Addison Schiller `@AddisonSchiller `_ - Longze Chen `@cslzchen https://github.com/cslzchen`_ - Jonathon Love `@jonathon-love https://github.com/jonathon-love`_ +- Joshua Bird `@birdbrained https://github.com/birdbrained`_ diff --git a/Dockerfile b/Dockerfile index 14475dd6e..c99a80efa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,6 +64,10 @@ ENV GIT_COMMIT ${GIT_COMMIT} RUN python setup.py develop +RUN apt-get install nodejs \ + apt-get install npm \ + && npm run build + EXPOSE 7778 CMD ["gosu", "www-data", "invoke", "server"] diff --git a/README.md b/README.md index 4e6881f86..b98eb5c8d 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,14 @@ invoke install invoke server ``` +Also set up node for frontend packages. The recommended package manager is `yarn`. +Webpack is used to build the packages. A script is included in `package.json` for convenience. + +```bash +yarn +yarn build +``` + ### Configuring MFR configuration is done through a JSON file (`mfr-test.json`) that lives in the `.cos` directory of your home directory. If this is your first time setting up MFR or its sister project, [WaterButler](https://github.com/CenterForOpenScience/waterbutler/), you probably do not have this directory and will need to create it: diff --git a/mfr/extensions/audio/templates/viewer.mako b/mfr/extensions/audio/templates/viewer.mako index ed3020932..1597f1937 100644 --- a/mfr/extensions/audio/templates/viewer.mako +++ b/mfr/extensions/audio/templates/viewer.mako @@ -3,5 +3,4 @@ Your browser does not support the audio tag. - - + diff --git a/mfr/extensions/codepygments/static/css/default.css b/mfr/extensions/codepygments/static/css/default.css index 3f4e04963..c870dbfc7 100644 --- a/mfr/extensions/codepygments/static/css/default.css +++ b/mfr/extensions/codepygments/static/css/default.css @@ -66,7 +66,7 @@ pre { overflow: auto; display: block; padding: 9.5px; - margin: 0px 0px 10px; + margin: 0px 0px 0px 0px; font-size: 13px; line-height: 1.42857; background-color: #F5F5F5; @@ -80,4 +80,4 @@ code, kbd, pre, samp { * { box-sizing: border-box; -} \ No newline at end of file +} diff --git a/mfr/extensions/codepygments/templates/viewer.mako b/mfr/extensions/codepygments/templates/viewer.mako index e6e451fc1..6cf7aabf7 100644 --- a/mfr/extensions/codepygments/templates/viewer.mako +++ b/mfr/extensions/codepygments/templates/viewer.mako @@ -1,8 +1,7 @@ - +
${body}
- - + diff --git a/mfr/extensions/docx/templates/viewer.mako b/mfr/extensions/docx/templates/viewer.mako index 36d6ac0ec..bc8bbc7c9 100644 --- a/mfr/extensions/docx/templates/viewer.mako +++ b/mfr/extensions/docx/templates/viewer.mako @@ -2,5 +2,4 @@ ${body} - - + diff --git a/mfr/extensions/image/templates/viewer.mako b/mfr/extensions/image/templates/viewer.mako index 2e1aa6f84..a0b164daa 100644 --- a/mfr/extensions/image/templates/viewer.mako +++ b/mfr/extensions/image/templates/viewer.mako @@ -1,4 +1,3 @@ - - + diff --git a/mfr/extensions/ipynb/templates/viewer.mako b/mfr/extensions/ipynb/templates/viewer.mako index a5ab47fb0..fe8d10c25 100644 --- a/mfr/extensions/ipynb/templates/viewer.mako +++ b/mfr/extensions/ipynb/templates/viewer.mako @@ -1,15 +1,14 @@ - - - + +
${body | n}
+ - - + - + diff --git a/mfr/extensions/jasp/templates/viewer.mako b/mfr/extensions/jasp/templates/viewer.mako index a0637ea35..7788b70e8 100644 --- a/mfr/extensions/jasp/templates/viewer.mako +++ b/mfr/extensions/jasp/templates/viewer.mako @@ -2,5 +2,4 @@ ${body} - - + diff --git a/mfr/extensions/jsc3d/templates/viewer.mako b/mfr/extensions/jsc3d/templates/viewer.mako index fc476ce9e..8649053fa 100644 --- a/mfr/extensions/jsc3d/templates/viewer.mako +++ b/mfr/extensions/jsc3d/templates/viewer.mako @@ -1,11 +1,13 @@ - - - + + - + - - + + + - + - - + + % if ext in ['.3ds']: - + % elif ext in ['.ctm']: - + % endif - var _getLoader = JSC3D.LoaderSelector.getLoader; - JSC3D.LoaderSelector.getLoader = function (fileExtName) { - var loader = _getLoader(fileExtName); - if (!loader) { - return viewer.reportProgress('Unable to load renderer for file of type \'' + fileExtName + '\'', 0); - } - var _loadFromUrl = loader.loadFromUrl; - loader.loadFromUrl = _loadFromUrl.bind(loader, '${url}'); - return loader; - }; - viewer.init(); - viewer.update(); - }); - diff --git a/mfr/extensions/md/templates/viewer.mako b/mfr/extensions/md/templates/viewer.mako index d7e166caf..86e0c369d 100644 --- a/mfr/extensions/md/templates/viewer.mako +++ b/mfr/extensions/md/templates/viewer.mako @@ -4,5 +4,4 @@ ${body} - - + diff --git a/mfr/extensions/pdb/templates/viewer.mako b/mfr/extensions/pdb/templates/viewer.mako index 6430cd538..33ed1d77d 100644 --- a/mfr/extensions/pdb/templates/viewer.mako +++ b/mfr/extensions/pdb/templates/viewer.mako @@ -1,35 +1,34 @@
- - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - @@ -45,13 +49,13 @@ http://sourceforge.net/adobe/cmap/wiki/License/
- - -
@@ -64,26 +68,35 @@ http://sourceforge.net/adobe/cmap/wiki/License/
+
- - - - - - - - - - + diff --git a/mfr/extensions/video/templates/viewer.mako b/mfr/extensions/video/templates/viewer.mako index e0058e4a4..120c7c7eb 100644 --- a/mfr/extensions/video/templates/viewer.mako +++ b/mfr/extensions/video/templates/viewer.mako @@ -3,8 +3,7 @@ Your browser does not support the video tag. - - + \ No newline at end of file + diff --git a/mfr/extensions/zip/templates/viewer.mako b/mfr/extensions/zip/templates/viewer.mako index f8b8b5438..5c1c21beb 100644 --- a/mfr/extensions/zip/templates/viewer.mako +++ b/mfr/extensions/zip/templates/viewer.mako @@ -1,6 +1,11 @@ +

Zip File: @@ -27,5 +32,4 @@

- - + diff --git a/mfr/server/app.py b/mfr/server/app.py index 808034793..6ec990c44 100644 --- a/mfr/server/app.py +++ b/mfr/server/app.py @@ -17,7 +17,6 @@ from mfr.server.handlers.status import StatusHandler from mfr.server.handlers.exporters import ExportersHandler from mfr.server.handlers.renderers import RenderersHandler -from mfr.server.handlers.core import ExtensionsStaticFileHandler logger = logging.getLogger(__name__) @@ -36,8 +35,7 @@ def stop_loop(): def make_app(debug): app = tornado.web.Application( [ - (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': server_settings.STATIC_PATH}), - (r'/assets/(.*?)/(.*\..*)', ExtensionsStaticFileHandler), + (r'/assets/(.*)', tornado.web.StaticFileHandler, {'path': server_settings.STATIC_PATH}), (r'/export', ExportHandler), (r'/exporters', ExportersHandler), (r'/render', RenderHandler), diff --git a/mfr/server/handlers/core.py b/mfr/server/handlers/core.py index 68b5cabc6..54e6770cd 100644 --- a/mfr/server/handlers/core.py +++ b/mfr/server/handlers/core.py @@ -268,28 +268,3 @@ def _all_metrics(self): metrics['error'] = getattr(self, 'error_metrics') if hasattr(self, 'error_metrics') else None return metrics - -class ExtensionsStaticFileHandler(tornado.web.StaticFileHandler, CorsMixin): - """Extensions static path definitions - """ - - def initialize(self): - namespace = 'mfr.renderers' - module_path = 'mfr.extensions' - self.modules = { - ep.module_name.replace(module_path + '.', ''): os.path.join(ep.dist.location, 'mfr', 'extensions', ep.module_name.replace(module_path + '.', ''), 'static') - for ep in list(pkg_resources.iter_entry_points(namespace)) - } - - async def get(self, module_name, path): - try: - super().initialize(self.modules[module_name]) - return await super().get(path) - except Exception: - self.set_status(404) - - try: - super().initialize(settings.STATIC_PATH) - return await super().get(path) - except Exception: - self.set_status(404) diff --git a/mfr/server/settings.py b/mfr/server/settings.py index 7085878da..56c35a189 100644 --- a/mfr/server/settings.py +++ b/mfr/server/settings.py @@ -7,7 +7,7 @@ config = settings.child('SERVER_CONFIG') -STATIC_PATH = config.get('STATIC_PATH', os.path.join(os.path.dirname(__file__), 'static')) +STATIC_PATH = config.get('STATIC_PATH', os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'dist')) ADDRESS = config.get('ADDRESS', 'localhost') PORT = config.get('PORT', 7778) diff --git a/mfr/server/static/css/mfr.css b/mfr/server/static/css/mfr.css deleted file mode 100644 index 5c8cc2d2c..000000000 --- a/mfr/server/static/css/mfr.css +++ /dev/null @@ -1,72 +0,0 @@ -.mfr-logo-spin { - -webkit-animation: mfr-spin 3s infinite linear, mfr-opacity 3s infinite linear; - -moz-animation: mfr-spin 3s infinite linear mfr-opacity 3s infinite linear; - animation: mfr-spin 3s infinite linear, mfr-opacity 3s infinite linear; - position: absolute; - top: 0; - left: 50%; - z-index: -1; -} -@-moz-keyframes mfr-spin { - from { - -moz-transform: rotate(0deg); - } - to { - -moz-transform: rotate(360deg); - } -} -@-webkit-keyframes mfr-spin { - from { - -webkit-transform: rotate(0deg); - } - to { - -webkit-transform: rotate(360deg); - } -} -@keyframes mfr-spin { - from { - -webkit-transform: rotate(0deg); - transform:rotate(0deg); - } - to { - -webkit-transform: rotate(360deg); - transform:rotate(360deg); - } -} -@-moz-keyframes mfr-opacity { - 0% { - opacity : 0.1 - } - 50% { - opacity: 1 - } - 100% { - opacity: 0.1 - } -} -@-webkit-keyframes mfr-opacity { - 0% { - opacity : 0.1 - } - 50% { - opacity: 1 - } - 100% { - opacity: 0.1 - } -} -@keyframes mfr-opacity { - 0% { - opacity : 0.1 - } - 50% { - opacity: 1 - } - 100% { - opacity: 0.1 - } -} - -.embed-responsive-pdf { - padding-bottom: 95%; -} \ No newline at end of file diff --git a/mfr/server/static/js/mfr.js b/mfr/server/static/js/mfr.js deleted file mode 100644 index 317e7d39f..000000000 --- a/mfr/server/static/js/mfr.js +++ /dev/null @@ -1,118 +0,0 @@ -/*! pym.js - v1.3.1 - 2017-07-25 */ -!function(a){"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&module.exports?module.exports=a():window.pym=a.call(this)}(function(){var a="xPYMx",b={},c=function(a){var b=document.createEvent("Event");b.initEvent("pym:"+a,!0,!0),document.dispatchEvent(b)},d=function(a){var b=new RegExp("[\\?&]"+a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]")+"=([^&#]*)"),c=b.exec(location.search);return null===c?"":decodeURIComponent(c[1].replace(/\+/g," "))},e=function(a,b){if(("*"===b.xdomain||a.origin.match(new RegExp(b.xdomain+"$")))&&"string"==typeof a.data)return!0},f=function(b,c,d){var e=["pym",b,c,d];return e.join(a)},g=function(b){var c=["pym",b,"(\\S+)","(.*)"];return new RegExp("^"+c.join(a)+"$")},h=Date.now||function(){return(new Date).getTime()},i=function(a,b,c){var d,e,f,g=null,i=0;c||(c={});var j=function(){i=c.leading===!1?0:h(),g=null,f=a.apply(d,e),g||(d=e=null)};return function(){var k=h();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,l<=0||l>b?(g&&(clearTimeout(g),g=null),i=k,f=a.apply(d,e),g||(d=e=null)):g||c.trailing===!1||(g=setTimeout(j,l)),f}},j=function(){for(var a=b.autoInitInstances.length,c=a-1;c>=0;c--){var d=b.autoInitInstances[c];d.el.getElementsByTagName("iframe").length&&d.el.getElementsByTagName("iframe")[0].contentWindow||b.autoInitInstances.splice(c,1)}};return b.autoInitInstances=[],b.autoInit=function(a){var d=document.querySelectorAll("[data-pym-src]:not([data-pym-auto-initialized])"),e=d.length;j();for(var f=0;f-1&&(b=this.url.substring(c,this.url.length),this.url=this.url.substring(0,c)),this.url.indexOf("?")<0?this.url+="?":this.url+="&",this.iframe.src=this.url+"initialWidth="+a+"&childId="+this.id,this.settings.optionalparams&&(this.iframe.src+="&parentTitle="+encodeURIComponent(document.title),this.iframe.src+="&"+this.settings.parenturlparam+"="+encodeURIComponent(this.settings.parenturlvalue)),this.iframe.src+=b,this.iframe.setAttribute("width","100%"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("marginheight","0"),this.iframe.setAttribute("frameborder","0"),this.settings.title&&this.iframe.setAttribute("title",this.settings.title),void 0!==this.settings.allowfullscreen&&this.settings.allowfullscreen!==!1&&this.iframe.setAttribute("allowfullscreen",""),void 0!==this.settings.sandbox&&"string"==typeof this.settings.sandbox&&this.iframe.setAttribute("sandbox",this.settings.sandbox),this.settings.id&&(document.getElementById(this.settings.id)||this.iframe.setAttribute("id",this.settings.id)),this.settings.name&&this.iframe.setAttribute("name",this.settings.name);this.el.firstChild;)this.el.removeChild(this.el.firstChild);this.el.appendChild(this.iframe),window.addEventListener("resize",this._onResize),this.settings.trackscroll&&window.addEventListener("scroll",this._throttleOnScroll)},this._onResize=function(){this.sendWidth(),this.settings.trackscroll&&this.sendViewportAndIFramePosition()}.bind(this),this._onScroll=function(){this.sendViewportAndIFramePosition()}.bind(this),this._fire=function(a,b){if(a in this.messageHandlers)for(var c=0;c protein.sheet[j][3]) continue; + atom.ss = 's'; + if (atom.resi == protein.sheet[j][1]) atom.ssbegin = true; + if (atom.resi == protein.sheet[j][3]) atom.ssend = true; + } + for (j = 0; j < protein.helix.length; j++) { + if (atom.chain != protein.helix[j][0]) continue; + if (atom.resi < protein.helix[j][1]) continue; + if (atom.resi > protein.helix[j][3]) continue; + atom.ss = 'h'; + if (atom.resi == protein.helix[j][1]) atom.ssbegin = true; + else if (atom.resi == protein.helix[j][3]) atom.ssend = true; + } + } + protein.smallMolecule = false; + return true; +}; + +// Catmull-Rom subdivision +GLmol.prototype.subdivide = function(_points, DIV) { // points as Vector3 + var ret = []; + var points = _points; + points = new Array(); // Smoothing test + points.push(_points[0]); + for (var i = 1, lim = _points.length - 1; i < lim; i++) { + var p1 = _points[i], p2 = _points[i + 1]; + if (p1.smoothen) points.push(new TV3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2)); + else points.push(p1); + } + points.push(_points[_points.length - 1]); + + for (var i = -1, size = points.length; i <= size - 3; i++) { + var p0 = points[(i == -1) ? 0 : i]; + var p1 = points[i + 1], p2 = points[i + 2]; + var p3 = points[(i == size - 3) ? size - 1 : i + 3]; + var v0 = new TV3().sub(p2, p0).multiplyScalar(0.5); + var v1 = new TV3().sub(p3, p1).multiplyScalar(0.5); + for (var j = 0; j < DIV; j++) { + var t = 1.0 / DIV * j; + var x = p1.x + t * v0.x + + t * t * (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x) + + t * t * t * (2 * p1.x - 2 * p2.x + v0.x + v1.x); + var y = p1.y + t * v0.y + + t * t * (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y) + + t * t * t * (2 * p1.y - 2 * p2.y + v0.y + v1.y); + var z = p1.z + t * v0.z + + t * t * (-3 * p1.z + 3 * p2.z - 2 * v0.z - v1.z) + + t * t * t * (2 * p1.z - 2 * p2.z + v0.z + v1.z); + ret.push(new TV3(x, y, z)); + } + } + ret.push(points[points.length - 1]); + return ret; +}; + +GLmol.prototype.drawAtomsAsSphere = function(group, atomlist, defaultRadius, forceDefault, scale) { + var sphereGeometry = new THREE.SphereGeometry(1, this.sphereQuality, this.sphereQuality); // r, seg, ring + for (var i = 0; i < atomlist.length; i++) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + var sphereMaterial = new THREE.MeshLambertMaterial({color: atom.color}); + var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); + group.add(sphere); + var r = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius; + if (!forceDefault && scale) r *= scale; + sphere.scale.x = sphere.scale.y = sphere.scale.z = r; + sphere.position.x = atom.x; + sphere.position.y = atom.y; + sphere.position.z = atom.z; + } +}; + +// about two times faster than sphere when div = 2 +GLmol.prototype.drawAtomsAsIcosahedron = function(group, atomlist, defaultRadius, forceDefault) { + var geo = this.IcosahedronGeometry(); + for (var i = 0; i < atomlist.length; i++) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + var mat = new THREE.MeshLambertMaterial({color: atom.color}); + var sphere = new THREE.Mesh(geo, mat); + sphere.scale.x = sphere.scale.y = sphere.scale.z = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius; + group.add(sphere); + sphere.position.x = atom.x; + sphere.position.y = atom.y; + sphere.position.z = atom.z; + } +}; + +GLmol.prototype.isConnected = function(atom1, atom2) { + var s = atom1.bonds.indexOf(atom2.serial); + if (s != -1) return atom1.bondOrder[s]; + + if (this.protein.smallMolecule && (atom1.hetflag || atom2.hetflag)) return 0; // CHECK: or should I ? + + var distSquared = (atom1.x - atom2.x) * (atom1.x - atom2.x) + + (atom1.y - atom2.y) * (atom1.y - atom2.y) + + (atom1.z - atom2.z) * (atom1.z - atom2.z); + +// if (atom1.altLoc != atom2.altLoc) return false; + if (isNaN(distSquared)) return 0; + if (distSquared < 0.5) return 0; // maybe duplicate position. + + if (distSquared > 1.3 && (atom1.elem == 'H' || atom2.elem == 'H' || atom1.elem == 'D' || atom2.elem == 'D')) return 0; + if (distSquared < 3.42 && (atom1.elem == 'S' || atom2.elem == 'S')) return 1; + if (distSquared > 2.78) return 0; + return 1; +}; + +GLmol.prototype.drawBondAsStickSub = function(group, atom1, atom2, bondR, order) { + var delta, tmp; + if (order > 1) delta = this.calcBondDelta(atom1, atom2, bondR * 2.3); + var p1 = new TV3(atom1.x, atom1.y, atom1.z); + var p2 = new TV3(atom2.x, atom2.y, atom2.z); + var mp = p1.clone().addSelf(p2).multiplyScalar(0.5); + + var c1 = new TCo(atom1.color), c2 = new TCo(atom2.color); + if (order == 1 || order == 3) { + this.drawCylinder(group, p1, mp, bondR, atom1.color); + this.drawCylinder(group, p2, mp, bondR, atom2.color); + } + if (order > 1) { + tmp = mp.clone().addSelf(delta); + this.drawCylinder(group, p1.clone().addSelf(delta), tmp, bondR, atom1.color); + this.drawCylinder(group, p2.clone().addSelf(delta), tmp, bondR, atom2.color); + tmp = mp.clone().subSelf(delta); + this.drawCylinder(group, p1.clone().subSelf(delta), tmp, bondR, atom1.color); + this.drawCylinder(group, p2.clone().subSelf(delta), tmp, bondR, atom2.color); + } +}; + +GLmol.prototype.drawBondsAsStick = function(group, atomlist, bondR, atomR, ignoreNonbonded, multipleBonds, scale) { + var sphereGeometry = new THREE.SphereGeometry(1, this.sphereQuality, this.sphereQuality); + var nAtoms = atomlist.length, mp; + var forSpheres = []; + if (!!multipleBonds) bondR /= 2.5; + for (var _i = 0; _i < nAtoms; _i++) { + var i = atomlist[_i]; + var atom1 = this.atoms[i]; + if (atom1 == undefined) continue; + for (var _j = _i + 1; _j < _i + 30 && _j < nAtoms; _j++) { + var j = atomlist[_j]; + var atom2 = this.atoms[j]; + if (atom2 == undefined) continue; + var order = this.isConnected(atom1, atom2); + if (order == 0) continue; + atom1.connected = atom2.connected = true; + this.drawBondAsStickSub(group, atom1, atom2, bondR, (!!multipleBonds) ? order : 1); + } + for (var _j = 0; _j < atom1.bonds.length; _j++) { + var j = atom1.bonds[_j]; + if (j < i + 30) continue; // be conservative! + if (atomlist.indexOf(j) == -1) continue; + var atom2 = this.atoms[j]; + if (atom2 == undefined) continue; + atom1.connected = atom2.connected = true; + this.drawBondAsStickSub(group, atom1, atom2, bondR, (!!multipleBonds) ? atom1.bondOrder[_j] : 1); + } + if (atom1.connected) forSpheres.push(i); + } + this.drawAtomsAsSphere(group, forSpheres, atomR, !scale, scale); +}; + +GLmol.prototype.defineCell = function() { + var p = this.protein; + if (p.a == undefined) return; + + p.ax = p.a; + p.ay = 0; + p.az = 0; + p.bx = p.b * Math.cos(Math.PI / 180.0 * p.gamma); + p.by = p.b * Math.sin(Math.PI / 180.0 * p.gamma); + p.bz = 0; + p.cx = p.c * Math.cos(Math.PI / 180.0 * p.beta); + p.cy = p.c * (Math.cos(Math.PI / 180.0 * p.alpha) - + Math.cos(Math.PI / 180.0 * p.gamma) + * Math.cos(Math.PI / 180.0 * p.beta) + / Math.sin(Math.PI / 180.0 * p.gamma)); + p.cz = Math.sqrt(p.c * p.c * Math.sin(Math.PI / 180.0 * p.beta) + * Math.sin(Math.PI / 180.0 * p.beta) - p.cy * p.cy); +}; + +GLmol.prototype.drawUnitcell = function(group) { + var p = this.protein; + if (p.a == undefined) return; + + var vertices = [[0, 0, 0], [p.ax, p.ay, p.az], [p.bx, p.by, p.bz], [p.ax + p.bx, p.ay + p.by, p.az + p.bz], + [p.cx, p.cy, p.cz], [p.cx + p.ax, p.cy + p.ay, p.cz + p.az], [p.cx + p.bx, p.cy + p.by, p.cz + p.bz], [p.cx + p.ax + p.bx, p.cy + p.ay + p.by, p.cz + p.az + p.bz]]; + var edges = [0, 1, 0, 2, 1, 3, 2, 3, 4, 5, 4, 6, 5, 7, 6, 7, 0, 4, 1, 5, 2, 6, 3, 7]; + + var geo = new THREE.Geometry(); + for (var i = 0; i < edges.length; i++) { + geo.vertices.push(new TV3(vertices[edges[i]][0], vertices[edges[i]][1], vertices[edges[i]][2])); + } + var lineMaterial = new THREE.LineBasicMaterial({linewidth: 1, color: 0xcccccc}); + var line = new THREE.Line(geo, lineMaterial); + line.type = THREE.LinePieces; + group.add(line); +}; + +// TODO: Find inner side of a ring +GLmol.prototype.calcBondDelta = function(atom1, atom2, sep) { + var dot; + var axis = new TV3(atom1.x - atom2.x, atom1.y - atom2.y, atom1.z - atom2.z).normalize(); + var found = null; + for (var i = 0; i < atom1.bonds.length && !found; i++) { + var atom = this.atoms[atom1.bonds[i]]; if (!atom) continue; + if (atom.serial != atom2.serial && atom.elem != 'H') found = atom; + } + for (var i = 0; i < atom2.bonds.length && !found; i++) { + var atom = this.atoms[atom2.bonds[i]]; if (!atom) continue; + if (atom.serial != atom1.serial && atom.elem != 'H') found = atom; + } + if (found) { + var tmp = new TV3(atom1.x - found.x, atom1.y - found.y, atom1.z - found.z).normalize(); + dot = tmp.dot(axis); + delta = new TV3(tmp.x - axis.x * dot, tmp.y - axis.y * dot, tmp.z - axis.z * dot); + } + if (!found || Math.abs(dot - 1) < 0.001 || Math.abs(dot + 1) < 0.001) { + if (axis.x < 0.01 && axis.y < 0.01) { + delta = new TV3(0, -axis.z, axis.y); + } else { + delta = new TV3(-axis.y, axis.x, 0); + } + } + delta.normalize().multiplyScalar(sep); + return delta; +}; + +GLmol.prototype.drawBondsAsLineSub = function(geo, atom1, atom2, order) { + var delta, tmp, vs = geo.vertices, cs = geo.colors; + if (order > 1) delta = this.calcBondDelta(atom1, atom2, 0.15); + var p1 = new TV3(atom1.x, atom1.y, atom1.z); + var p2 = new TV3(atom2.x, atom2.y, atom2.z); + var mp = p1.clone().addSelf(p2).multiplyScalar(0.5); + + var c1 = new TCo(atom1.color), c2 = new TCo(atom2.color); + if (order == 1 || order == 3) { + vs.push(p1); cs.push(c1); vs.push(mp); cs.push(c1); + vs.push(p2); cs.push(c2); vs.push(mp); cs.push(c2); + } + if (order > 1) { + vs.push(p1.clone().addSelf(delta)); cs.push(c1); + vs.push(tmp = mp.clone().addSelf(delta)); cs.push(c1); + vs.push(p2.clone().addSelf(delta)); cs.push(c2); + vs.push(tmp); cs.push(c2); + vs.push(p1.clone().subSelf(delta)); cs.push(c1); + vs.push(tmp = mp.clone().subSelf(delta)); cs.push(c1); + vs.push(p2.clone().subSelf(delta)); cs.push(c2); + vs.push(tmp); cs.push(c2); + } +}; + +GLmol.prototype.drawBondsAsLine = function(group, atomlist, lineWidth) { + var geo = new THREE.Geometry(); + var nAtoms = atomlist.length; + + for (var _i = 0; _i < nAtoms; _i++) { + var i = atomlist[_i]; + var atom1 = this.atoms[i]; + if (atom1 == undefined) continue; + for (var _j = _i + 1; _j < _i + 30 && _j < nAtoms; _j++) { + var j = atomlist[_j]; + var atom2 = this.atoms[j]; + if (atom2 == undefined) continue; + var order = this.isConnected(atom1, atom2); + if (order == 0) continue; + + this.drawBondsAsLineSub(geo, atom1, atom2, order); + } + for (var _j = 0; _j < atom1.bonds.length; _j++) { + var j = atom1.bonds[_j]; + if (j < i + 30) continue; // be conservative! + if (atomlist.indexOf(j) == -1) continue; + var atom2 = this.atoms[j]; + if (atom2 == undefined) continue; + this.drawBondsAsLineSub(geo, atom1, atom2, atom1.bondOrder[_j]); + } + } + var lineMaterial = new THREE.LineBasicMaterial({linewidth: lineWidth}); + lineMaterial.vertexColors = true; + + var line = new THREE.Line(geo, lineMaterial); + line.type = THREE.LinePieces; + group.add(line); +}; + +GLmol.prototype.drawSmoothCurve = function(group, _points, width, colors, div) { + if (_points.length == 0) return; + + div = (div == undefined) ? 5 : div; + + var geo = new THREE.Geometry(); + var points = this.subdivide(_points, div); + + for (var i = 0; i < points.length; i++) { + geo.vertices.push(points[i]); + geo.colors.push(new TCo(colors[(i == 0) ? 0 : Math.round((i - 1) / div)])); + } + var lineMaterial = new THREE.LineBasicMaterial({linewidth: width}); + lineMaterial.vertexColors = true; + var line = new THREE.Line(geo, lineMaterial); + line.type = THREE.LineStrip; + group.add(line); +}; + +GLmol.prototype.drawAsCross = function(group, atomlist, delta) { + var geo = new THREE.Geometry(); + var points = [[delta, 0, 0], [-delta, 0, 0], [0, delta, 0], [0, -delta, 0], [0, 0, delta], [0, 0, -delta]]; + + for (var i = 0, lim = atomlist.length; i < lim; i++) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + var c = new TCo(atom.color); + for (var j = 0; j < 6; j++) { + geo.vertices.push(new TV3(atom.x + points[j][0], atom.y + points[j][1], atom.z + points[j][2])); + geo.colors.push(c); + } + } + var lineMaterial = new THREE.LineBasicMaterial({linewidth: this.lineWidth}); + lineMaterial.vertexColors = true; + var line = new THREE.Line(geo, lineMaterial, THREE.LinePieces); + group.add(line); +}; + +// FIXME: Winkled... +GLmol.prototype.drawSmoothTube = function(group, _points, colors, radii) { + if (_points.length < 2) return; + + var circleDiv = this.tubeDIV, axisDiv = this.axisDIV; + var geo = new THREE.Geometry(); + var points = this.subdivide(_points, axisDiv); + var prevAxis1 = new TV3(), prevAxis2; + + for (var i = 0, lim = points.length; i < lim; i++) { + var r, idx = (i - 1) / axisDiv; + if (i == 0) r = radii[0]; + else { + if (idx % 1 == 0) r = radii[idx]; + else { + var floored = Math.floor(idx); + var tmp = idx - floored; + r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp); + } + } + var delta, axis1, axis2; + + if (i < lim - 1) { + delta = new TV3().sub(points[i], points[i + 1]); + axis1 = new TV3(0, - delta.z, delta.y).normalize().multiplyScalar(r); + axis2 = new TV3().cross(delta, axis1).normalize().multiplyScalar(r); +// var dir = 1, offset = 0; + if (prevAxis1.dot(axis1) < 0) { + axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv; + } + prevAxis1 = axis1; prevAxis2 = axis2; + } else { + axis1 = prevAxis1; axis2 = prevAxis2; + } + + for (var j = 0; j < circleDiv; j++) { + var angle = 2 * Math.PI / circleDiv * j; //* dir + offset; + var c = Math.cos(angle), s = Math.sin(angle); + geo.vertices.push(new TV3( + points[i].x + c * axis1.x + s * axis2.x, + points[i].y + c * axis1.y + s * axis2.y, + points[i].z + c * axis1.z + s * axis2.z)); + } + } + + var offset = 0; + for (var i = 0, lim = points.length - 1; i < lim; i++) { + var c = new TCo(colors[Math.round((i - 1)/ axisDiv)]); + + var reg = 0; + var r1 = new TV3().sub(geo.vertices[offset], geo.vertices[offset + circleDiv]).lengthSq(); + var r2 = new TV3().sub(geo.vertices[offset], geo.vertices[offset + circleDiv + 1]).lengthSq(); + if (r1 > r2) {r1 = r2; reg = 1;}; + for (var j = 0; j < circleDiv; j++) { + geo.faces.push(new TF3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv)); + geo.faces.push(new TF3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv)); + geo.faces[geo.faces.length -2].color = c; + geo.faces[geo.faces.length -1].color = c; + } + offset += circleDiv; + } + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + var mat = new THREE.MeshLambertMaterial(); + mat.vertexColors = THREE.FaceColors; + var mesh = new THREE.Mesh(geo, mat); + mesh.doubleSided = true; + group.add(mesh); +}; + + +GLmol.prototype.drawMainchainCurve = function(group, atomlist, curveWidth, atomName, div) { + var points = [], colors = []; + var currentChain, currentResi; + if (div == undefined) div = 5; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + if ((atom.atom == atomName) && !atom.hetflag) { + if (currentChain != atom.chain || currentResi + 1 != atom.resi) { + this.drawSmoothCurve(group, points, curveWidth, colors, div); + points = []; + colors = []; + } + points.push(new TV3(atom.x, atom.y, atom.z)); + colors.push(atom.color); + currentChain = atom.chain; + currentResi = atom.resi; + } + } + this.drawSmoothCurve(group, points, curveWidth, colors, div); +}; + +GLmol.prototype.drawMainchainTube = function(group, atomlist, atomName, radius) { + var points = [], colors = [], radii = []; + var currentChain, currentResi; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + if ((atom.atom == atomName) && !atom.hetflag) { + if (currentChain != atom.chain || currentResi + 1 != atom.resi) { + this.drawSmoothTube(group, points, colors, radii); + points = []; colors = []; radii = []; + } + points.push(new TV3(atom.x, atom.y, atom.z)); + if (radius == undefined) { + radii.push((atom.b > 0) ? atom.b / 100 : 0.3); + } else { + radii.push(radius); + } + colors.push(atom.color); + currentChain = atom.chain; + currentResi = atom.resi; + } + } + this.drawSmoothTube(group, points, colors, radii); +}; + +GLmol.prototype.drawStrip = function(group, p1, p2, colors, div, thickness) { + if ((p1.length) < 2) return; + div = div || this.axisDIV; + p1 = this.subdivide(p1, div); + p2 = this.subdivide(p2, div); + if (!thickness) return this.drawThinStrip(group, p1, p2, colors, div); + + var geo = new THREE.Geometry(); + var vs = geo.vertices, fs = geo.faces; + var axis, p1v, p2v, a1v, a2v; + for (var i = 0, lim = p1.length; i < lim; i++) { + vs.push(p1v = p1[i]); // 0 + vs.push(p1v); // 1 + vs.push(p2v = p2[i]); // 2 + vs.push(p2v); // 3 + if (i < lim - 1) { + var toNext = p1[i + 1].clone().subSelf(p1[i]); + var toSide = p2[i].clone().subSelf(p1[i]); + axis = toSide.crossSelf(toNext).normalize().multiplyScalar(thickness); + } + vs.push(a1v = p1[i].clone().addSelf(axis)); // 4 + vs.push(a1v); // 5 + vs.push(a2v = p2[i].clone().addSelf(axis)); // 6 + vs.push(a2v); // 7 + } + var faces = [[0, 2, -6, -8], [-4, -2, 6, 4], [7, 3, -5, -1], [-3, -7, 1, 5]]; + for (var i = 1, lim = p1.length; i < lim; i++) { + var offset = 8 * i, color = new TCo(colors[Math.round((i - 1)/ div)]); + for (var j = 0; j < 4; j++) { + var f = new THREE.Face4(offset + faces[j][0], offset + faces[j][1], offset + faces[j][2], offset + faces[j][3], undefined, color); + fs.push(f); + } + } + var vsize = vs.length - 8; // Cap + for (var i = 0; i < 4; i++) {vs.push(vs[i * 2]); vs.push(vs[vsize + i * 2])}; + vsize += 8; + fs.push(new THREE.Face4(vsize, vsize + 2, vsize + 6, vsize + 4, undefined, fs[0].color)); + fs.push(new THREE.Face4(vsize + 1, vsize + 5, vsize + 7, vsize + 3, undefined, fs[fs.length - 3].color)); + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + var material = new THREE.MeshLambertMaterial(); + material.vertexColors = THREE.FaceColors; + var mesh = new THREE.Mesh(geo, material); + mesh.doubleSided = true; + group.add(mesh); +}; + + +GLmol.prototype.drawThinStrip = function(group, p1, p2, colors, div) { + var geo = new THREE.Geometry(); + for (var i = 0, lim = p1.length; i < lim; i++) { + geo.vertices.push(p1[i]); // 2i + geo.vertices.push(p2[i]); // 2i + 1 + } + for (var i = 1, lim = p1.length; i < lim; i++) { + var f = new THREE.Face4(2 * i, 2 * i + 1, 2 * i - 1, 2 * i - 2); + f.color = new TCo(colors[Math.round((i - 1)/ div)]); + geo.faces.push(f); + } + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + var material = new THREE.MeshLambertMaterial(); + material.vertexColors = THREE.FaceColors; + var mesh = new THREE.Mesh(geo, material); + mesh.doubleSided = true; + group.add(mesh); +}; + + +GLmol.prototype.IcosahedronGeometry = function() { + if (!this.icosahedron) this.icosahedron = new THREE.IcosahedronGeometry(1); + return this.icosahedron; +}; + +GLmol.prototype.drawCylinder = function(group, from, to, radius, color, cap) { + if (!from || !to) return; + + var midpoint = new TV3().add(from, to).multiplyScalar(0.5); + var color = new TCo(color); + + if (!this.cylinderGeometry) { + this.cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, this.cylinderQuality, 1, !cap); + this.cylinderGeometry.faceUvs = []; + this.faceVertexUvs = []; + } + var cylinderMaterial = new THREE.MeshLambertMaterial({color: color.getHex()}); + var cylinder = new THREE.Mesh(this.cylinderGeometry, cylinderMaterial); + cylinder.position = midpoint; + cylinder.lookAt(from); + cylinder.updateMatrix(); + cylinder.matrixAutoUpdate = false; + var m = new THREE.Matrix4().makeScale(radius, radius, from.distanceTo(to)); + m.rotateX(Math.PI / 2); + cylinder.matrix.multiplySelf(m); + group.add(cylinder); +}; + +// FIXME: transition! +GLmol.prototype.drawHelixAsCylinder = function(group, atomlist, radius) { + var start = null; + var currentChain, currentResi; + + var others = [], beta = []; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined || atom.hetflag) continue; + if ((atom.ss != 'h' && atom.ss != 's') || atom.ssend || atom.ssbegin) others.push(atom.serial); + if (atom.ss == 's') beta.push(atom.serial); + if (atom.atom != 'CA') continue; + + if (atom.ss == 'h' && atom.ssend) { + if (start != null) this.drawCylinder(group, new TV3(start.x, start.y, start.z), new TV3(atom.x, atom.y, atom.z), radius, atom.color, true); + start = null; + } + currentChain = atom.chain; + currentResi = atom.resi; + if (start == null && atom.ss == 'h' && atom.ssbegin) start = atom; + } + if (start != null) this.drawCylinder(group, new TV3(start.x, start.y, start.z), new TV3(atom.x, atom.y, atom.z), radius, atom.color); + this.drawMainchainTube(group, others, "CA", 0.3); + this.drawStrand(group, beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.thickness * 2); +}; + +GLmol.prototype.drawCartoon = function(group, atomlist, doNotSmoothen, thickness) { + this.drawStrand(group, atomlist, 2, undefined, true, undefined, undefined, doNotSmoothen, thickness); +}; + +GLmol.prototype.drawStrand = function(group, atomlist, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness) { + num = num || this.strandDIV; + div = div || this.axisDIV; + coilWidth = coilWidth || this.coilWidth; + doNotSmoothen == (doNotSmoothen == undefined) ? false : doNotSmoothen; + helixSheetWidth = helixSheetWidth || this.helixSheetWidth; + var points = []; for (var k = 0; k < num; k++) points[k] = []; + var colors = []; + var currentChain, currentResi, currentCA; + var prevCO = null, ss=null, ssborder = false; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + if ((atom.atom == 'O' || atom.atom == 'CA') && !atom.hetflag) { + if (atom.atom == 'CA') { + if (currentChain != atom.chain || currentResi + 1 != atom.resi) { + for (var j = 0; !thickness && j < num; j++) + this.drawSmoothCurve(group, points[j], 1 ,colors, div); + if (fill) this.drawStrip(group, points[0], points[num - 1], colors, div, thickness); + var points = []; for (var k = 0; k < num; k++) points[k] = []; + colors = []; + prevCO = null; ss = null; ssborder = false; + } + currentCA = new TV3(atom.x, atom.y, atom.z); + currentChain = atom.chain; + currentResi = atom.resi; + ss = atom.ss; ssborder = atom.ssstart || atom.ssend; + colors.push(atom.color); + } else { // O + var O = new TV3(atom.x, atom.y, atom.z); + O.subSelf(currentCA); + O.normalize(); // can be omitted for performance + O.multiplyScalar((ss == 'c') ? coilWidth : helixSheetWidth); + if (prevCO != undefined && O.dot(prevCO) < 0) O.negate(); + prevCO = O; + for (var j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + var v = new TV3(currentCA.x + prevCO.x * delta, + currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta); + if (!doNotSmoothen && ss == 's') v.smoothen = true; + points[j].push(v); + } + } + } + } + for (var j = 0; !thickness && j < num; j++) + this.drawSmoothCurve(group, points[j], 1 ,colors, div); + if (fill) this.drawStrip(group, points[0], points[num - 1], colors, div, thickness); +}; + +GLmol.prototype.drawNucleicAcidLadderSub = function(geo, lineGeo, atoms, color) { +// color.r *= 0.9; color.g *= 0.9; color.b *= 0.9; + if (atoms[0] != undefined && atoms[1] != undefined && atoms[2] != undefined && + atoms[3] != undefined && atoms[4] != undefined && atoms[5] != undefined) { + var baseFaceId = geo.vertices.length; + for (var i = 0; i <= 5; i++) geo.vertices.push(atoms[i]); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 1, baseFaceId + 2)); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 2, baseFaceId + 3)); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 3, baseFaceId + 4)); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 4, baseFaceId + 5)); + for (var j = geo.faces.length - 4, lim = geo.faces.length; j < lim; j++) geo.faces[j].color = color; + } + if (atoms[4] != undefined && atoms[3] != undefined && atoms[6] != undefined && + atoms[7] != undefined && atoms[8] != undefined) { + var baseFaceId = geo.vertices.length; + geo.vertices.push(atoms[4]); + geo.vertices.push(atoms[3]); + geo.vertices.push(atoms[6]); + geo.vertices.push(atoms[7]); + geo.vertices.push(atoms[8]); + for (var i = 0; i <= 4; i++) geo.colors.push(color); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 1, baseFaceId + 2)); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 2, baseFaceId + 3)); + geo.faces.push(new TF3(baseFaceId, baseFaceId + 3, baseFaceId + 4)); + for (var j = geo.faces.length - 3, lim = geo.faces.length; j < lim; j++) geo.faces[j].color = color; + } +}; + +GLmol.prototype.drawNucleicAcidLadder = function(group, atomlist) { + var geo = new THREE.Geometry(); + var lineGeo = new THREE.Geometry(); + var baseAtoms = ["N1", "C2", "N3", "C4", "C5", "C6", "N9", "C8", "N7"]; + var currentChain, currentResi, currentComponent = new Array(baseAtoms.length); + var color = new TCo(0xcc0000); + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined || atom.hetflag) continue; + + if (atom.resi != currentResi || atom.chain != currentChain) { + this.drawNucleicAcidLadderSub(geo, lineGeo, currentComponent, color); + currentComponent = new Array(baseAtoms.length); + } + var pos = baseAtoms.indexOf(atom.atom); + if (pos != -1) currentComponent[pos] = new TV3(atom.x, atom.y, atom.z); + if (atom.atom == 'O3\'') color = new TCo(atom.color); + currentResi = atom.resi; currentChain = atom.chain; + } + this.drawNucleicAcidLadderSub(geo, lineGeo, currentComponent, color); + geo.computeFaceNormals(); + var mat = new THREE.MeshLambertMaterial(); + mat.vertexColors = THREE.VertexColors; + var mesh = new THREE.Mesh(geo, mat); + mesh.doubleSided = true; + group.add(mesh); +}; + +GLmol.prototype.drawNucleicAcidStick = function(group, atomlist) { + var currentChain, currentResi, start = null, end = null; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined || atom.hetflag) continue; + + if (atom.resi != currentResi || atom.chain != currentChain) { + if (start != null && end != null) + this.drawCylinder(group, new TV3(start.x, start.y, start.z), + new TV3(end.x, end.y, end.z), 0.3, start.color, true); + start = null; end = null; + } + if (atom.atom == 'O3\'') start = atom; + if (atom.resn == ' A' || atom.resn == ' G' || atom.resn == ' DA' || atom.resn == ' DG') { + if (atom.atom == 'N1') end = atom; // N1(AG), N3(CTU) + } else if (atom.atom == 'N3') { + end = atom; + } + currentResi = atom.resi; currentChain = atom.chain; + } + if (start != null && end != null) + this.drawCylinder(group, new TV3(start.x, start.y, start.z), + new TV3(end.x, end.y, end.z), 0.3, start.color, true); +}; + +GLmol.prototype.drawNucleicAcidLine = function(group, atomlist) { + var currentChain, currentResi, start = null, end = null; + var geo = new THREE.Geometry(); + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined || atom.hetflag) continue; + + if (atom.resi != currentResi || atom.chain != currentChain) { + if (start != null && end != null) { + geo.vertices.push(new TV3(start.x, start.y, start.z)); + geo.colors.push(new TCo(start.color)); + geo.vertices.push(new TV3(end.x, end.y, end.z)); + geo.colors.push(new TCo(start.color)); + } + start = null; end = null; + } + if (atom.atom == 'O3\'') start = atom; + if (atom.resn == ' A' || atom.resn == ' G' || atom.resn == ' DA' || atom.resn == ' DG') { + if (atom.atom == 'N1') end = atom; // N1(AG), N3(CTU) + } else if (atom.atom == 'N3') { + end = atom; + } + currentResi = atom.resi; currentChain = atom.chain; + } + if (start != null && end != null) { + geo.vertices.push(new TV3(start.x, start.y, start.z)); + geo.colors.push(new TCo(start.color)); + geo.vertices.push(new TV3(end.x, end.y, end.z)); + geo.colors.push(new TCo(start.color)); + } + var mat = new THREE.LineBasicMaterial({linewidth: 1, linejoin: false}); + mat.linewidth = 1.5; mat.vertexColors = true; + var line = new THREE.Line(geo, mat, THREE.LinePieces); + group.add(line); +}; + +GLmol.prototype.drawCartoonNucleicAcid = function(group, atomlist, div, thickness) { + this.drawStrandNucleicAcid(group, atomlist, 2, div, true, undefined, thickness); +}; + +GLmol.prototype.drawStrandNucleicAcid = function(group, atomlist, num, div, fill, nucleicAcidWidth, thickness) { + nucleicAcidWidth = nucleicAcidWidth || this.nucleicAcidWidth; + div = div || this.axisDIV; + num = num || this.nucleicAcidStrandDIV; + var points = []; for (var k = 0; k < num; k++) points[k] = []; + var colors = []; + var currentChain, currentResi, currentO3; + var prevOO = null; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; + if (atom == undefined) continue; + + if ((atom.atom == 'O3\'' || atom.atom == 'OP2') && !atom.hetflag) { + if (atom.atom == 'O3\'') { // to connect 3' end. FIXME: better way to do? + if (currentChain != atom.chain || currentResi + 1 != atom.resi) { + if (currentO3) { + for (var j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + points[j].push(new TV3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + } + if (fill) this.drawStrip(group, points[0], points[1], colors, div, thickness); + for (var j = 0; !thickness && j < num; j++) + this.drawSmoothCurve(group, points[j], 1 ,colors, div); + var points = []; for (var k = 0; k < num; k++) points[k] = []; + colors = []; + prevOO = null; + } + currentO3 = new TV3(atom.x, atom.y, atom.z); + currentChain = atom.chain; + currentResi = atom.resi; + colors.push(atom.color); + } else { // OP2 + if (!currentO3) {prevOO = null; continue;} // for 5' phosphate (e.g. 3QX3) + var O = new TV3(atom.x, atom.y, atom.z); + O.subSelf(currentO3); + O.normalize().multiplyScalar(nucleicAcidWidth); // TODO: refactor + if (prevOO != undefined && O.dot(prevOO) < 0) { + O.negate(); + } + prevOO = O; + for (var j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + points[j].push(new TV3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + currentO3 = null; + } + } + } + if (currentO3) { + for (var j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + points[j].push(new TV3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + } + if (fill) this.drawStrip(group, points[0], points[1], colors, div, thickness); + for (var j = 0; !thickness && j < num; j++) + this.drawSmoothCurve(group, points[j], 1 ,colors, div); +}; + +GLmol.prototype.drawDottedLines = function(group, points, color) { + var geo = new THREE.Geometry(); + var step = 0.3; + + for (var i = 0, lim = Math.floor(points.length / 2); i < lim; i++) { + var p1 = points[2 * i], p2 = points[2 * i + 1]; + var delta = p2.clone().subSelf(p1); + var dist = delta.length(); + delta.normalize().multiplyScalar(step); + var jlim = Math.floor(dist / step); + for (var j = 0; j < jlim; j++) { + var p = new TV3(p1.x + delta.x * j, p1.y + delta.y * j, p1.z + delta.z * j); + geo.vertices.push(p); + } + if (jlim % 2 == 1) geo.vertices.push(p2); + } + + var mat = new THREE.LineBasicMaterial({'color': color.getHex()}); + mat.linewidth = 2; + var line = new THREE.Line(geo, mat, THREE.LinePieces); + group.add(line); +}; + +GLmol.prototype.getAllAtoms = function() { + var ret = []; + for (var i in this.atoms) { + ret.push(this.atoms[i].serial); + } + return ret; +}; + +// Probably I can refactor using higher-order functions. +GLmol.prototype.getHetatms = function(atomlist) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag) ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.removeSolvents = function(atomlist) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.resn != 'HOH') ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.getProteins = function(atomlist) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (!atom.hetflag) ret.push(atom.serial); + } + return ret; +}; + +// TODO: Test +GLmol.prototype.excludeAtoms = function(atomlist, deleteList) { + var ret = []; + var blackList = new Object(); + for (var _i in deleteList) blackList[deleteList[_i]] = true; + + for (var _i in atomlist) { + var i = atomlist[_i]; + + if (!blackList[i]) ret.push(i); + } + return ret; +}; + +GLmol.prototype.getSidechains = function(atomlist) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag) continue; + if (atom.atom == 'C' || atom.atom == 'O' || (atom.atom == 'N' && atom.resn != "PRO")) continue; + ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.getAtomsWithin = function(atomlist, extent) { + var ret = []; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.x < extent[0][0] || atom.x > extent[1][0]) continue; + if (atom.y < extent[0][1] || atom.y > extent[1][1]) continue; + if (atom.z < extent[0][2] || atom.z > extent[1][2]) continue; + ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.getExtent = function(atomlist) { + var xmin = ymin = zmin = 9999; + var xmax = ymax = zmax = -9999; + var xsum = ysum = zsum = cnt = 0; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + cnt++; + xsum += atom.x; ysum += atom.y; zsum += atom.z; + + xmin = (xmin < atom.x) ? xmin : atom.x; + ymin = (ymin < atom.y) ? ymin : atom.y; + zmin = (zmin < atom.z) ? zmin : atom.z; + xmax = (xmax > atom.x) ? xmax : atom.x; + ymax = (ymax > atom.y) ? ymax : atom.y; + zmax = (zmax > atom.z) ? zmax : atom.z; + } + return [[xmin, ymin, zmin], [xmax, ymax, zmax], [xsum / cnt, ysum / cnt, zsum / cnt]]; +}; + +GLmol.prototype.getResiduesById = function(atomlist, resi) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (resi.indexOf(atom.resi) != -1) ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.getResidueBySS = function(atomlist, ss) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (ss.indexOf(atom.ss) != -1) ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.getChain = function(atomlist, chain) { + var ret = [], chains = {}; + chain = chain.toString(); // concat if Array + for (var i = 0, lim = chain.length; i < lim; i++) chains[chain.substr(i, 1)] = true; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (chains[atom.chain]) ret.push(atom.serial); + } + return ret; +}; + +// for HETATM only +GLmol.prototype.getNonbonded = function(atomlist, chain) { + var ret = []; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag && atom.bonds.length == 0) ret.push(atom.serial); + } + return ret; +}; + +GLmol.prototype.colorByAtom = function(atomlist, colors) { + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + var c = colors[atom.elem]; + if (c == undefined) c = this.ElementColors[atom.elem]; + if (c == undefined) c = this.defaultColor; + atom.color = c; + } +}; + + +// MEMO: Color only CA. maybe I should add atom.cartoonColor. +GLmol.prototype.colorByStructure = function(atomlist, helixColor, sheetColor, colorSidechains) { + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (!colorSidechains && (atom.atom != 'CA' || atom.hetflag)) continue; + if (atom.ss[0] == 's') atom.color = sheetColor; + else if (atom.ss[0] == 'h') atom.color = helixColor; + } +}; + +GLmol.prototype.colorByBFactor = function(atomlist, colorSidechains) { + var minB = 1000, maxB = -1000; + + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag) continue; + if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') { + if (minB > atom.b) minB = atom.b; + if (maxB < atom.b) maxB = atom.b; + } + } + + var mid = (maxB + minB) / 2; + + var range = (maxB - minB) / 2; + if (range < 0.01 && range > -0.01) return; + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag) continue; + if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') { + var color = new TCo(0); + if (atom.b < mid) + color.setHSV(0.667, (mid - atom.b) / range, 1); + else + color.setHSV(0, (atom.b - mid) / range, 1); + atom.color = color.getHex(); + } + } +}; + +GLmol.prototype.colorByChain = function(atomlist, colorSidechains) { + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if (atom.hetflag) continue; + if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') { + var color = new TCo(0); + color.setHSV((atom.chain.charCodeAt(0) * 5) % 17 / 17.0, 1, 0.9); + atom.color = color.getHex(); + } + } +}; + +GLmol.prototype.colorByResidue = function(atomlist, residueColors) { + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + c = residueColors[atom.resn] + if (c != undefined) atom.color = c; + } +}; + +GLmol.prototype.colorAtoms = function(atomlist, c) { + for (var i in atomlist) { + var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + atom.color = c; + } +}; + +GLmol.prototype.colorByPolarity = function(atomlist, polar, nonpolar) { + var polarResidues = ['ARG', 'HIS', 'LYS', 'ASP', 'GLU', 'SER', 'THR', 'ASN', 'GLN', 'CYS']; + var nonPolarResidues = ['GLY', 'PRO', 'ALA', 'VAL', 'LEU', 'ILE', 'MET', 'PHE', 'TYR', 'TRP']; + var colorMap = {}; + for (var i in polarResidues) colorMap[polarResidues[i]] = polar; + for (i in nonPolarResidues) colorMap[nonPolarResidues[i]] = nonpolar; + this.colorByResidue(atomlist, colorMap); +}; + +// TODO: Add near(atomlist, neighbor, distanceCutoff) +// TODO: Add expandToResidue(atomlist) + +GLmol.prototype.colorChainbow = function(atomlist, colorSidechains) { + var cnt = 0; + var atom, i; + for (i in atomlist) { + atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if ((colorSidechains || atom.atom != 'CA' || atom.atom != 'O3\'') && !atom.hetflag) + cnt++; + } + + var total = cnt; + cnt = 0; + for (i in atomlist) { + atom = this.atoms[atomlist[i]]; if (atom == undefined) continue; + + if ((colorSidechains || atom.atom != 'CA' || atom.atom != 'O3\'') && !atom.hetflag) { + var color = new TCo(0); + color.setHSV(240.0 / 360 * (1 - cnt / total), 1, 0.9); + atom.color = color.getHex(); + cnt++; + } + } +}; + +GLmol.prototype.drawSymmetryMates2 = function(group, asu, matrices) { + if (matrices == undefined) return; + asu.matrixAutoUpdate = false; + + var cnt = 1; + this.protein.appliedMatrix = new THREE.Matrix4(); + for (var i = 0; i < matrices.length; i++) { + var mat = matrices[i]; + if (mat == undefined || mat.isIdentity()) continue; + console.log(mat); + var symmetryMate = THREE.SceneUtils.cloneObject(asu); + symmetryMate.matrix = mat; + group.add(symmetryMate); + for (var j = 0; j < 16; j++) this.protein.appliedMatrix.elements[j] += mat.elements[j]; + cnt++; + } + this.protein.appliedMatrix.multiplyScalar(cnt); +}; + + +GLmol.prototype.drawSymmetryMatesWithTranslation2 = function(group, asu, matrices) { + if (matrices == undefined) return; + var p = this.protein; + asu.matrixAutoUpdate = false; + + for (var i = 0; i < matrices.length; i++) { + var mat = matrices[i]; + if (mat == undefined) continue; + + for (var a = -1; a <=0; a++) { + for (var b = -1; b <= 0; b++) { + for (var c = -1; c <= 0; c++) { + var translationMat = new THREE.Matrix4().makeTranslation( + p.ax * a + p.bx * b + p.cx * c, + p.ay * a + p.by * b + p.cy * c, + p.az * a + p.bz * b + p.cz * c); + var symop = mat.clone().multiplySelf(translationMat); + if (symop.isIdentity()) continue; + var symmetryMate = THREE.SceneUtils.cloneObject(asu); + symmetryMate.matrix = symop; + group.add(symmetryMate); + } + } + } + } +}; + +GLmol.prototype.defineRepresentation = function() { + var all = this.getAllAtoms(); + var hetatm = this.removeSolvents(this.getHetatms(all)); + this.colorByAtom(all, {}); + this.colorByChain(all); + + this.drawAtomsAsSphere(this.modelGroup, hetatm, this.sphereRadius); + this.drawMainchainCurve(this.modelGroup, all, this.curveWidth, 'P'); + this.drawCartoon(this.modelGroup, all, this.curveWidth); +}; + +GLmol.prototype.getView = function() { + if (!this.modelGroup) return [0, 0, 0, 0, 0, 0, 0, 1]; + var pos = this.modelGroup.position; + var q = this.rotationGroup.quaternion; + return [pos.x, pos.y, pos.z, this.rotationGroup.position.z, q.x, q.y, q.z, q.w]; +}; + +GLmol.prototype.setView = function(arg) { + if (!this.modelGroup || !this.rotationGroup) return; + this.modelGroup.position.x = arg[0]; + this.modelGroup.position.y = arg[1]; + this.modelGroup.position.z = arg[2]; + this.rotationGroup.position.z = arg[3]; + this.rotationGroup.quaternion.x = arg[4]; + this.rotationGroup.quaternion.y = arg[5]; + this.rotationGroup.quaternion.z = arg[6]; + this.rotationGroup.quaternion.w = arg[7]; + this.show(); +}; + +GLmol.prototype.setBackground = function(hex, a) { + a = a | 1.0; + this.bgColor = hex; + this.renderer.setClearColorHex(hex, a); + this.scene.fog.color = new TCo(hex); +}; + +GLmol.prototype.initializeScene = function() { + // CHECK: Should I explicitly call scene.deallocateObject? + this.scene = new THREE.Scene(); + this.scene.fog = new THREE.Fog(this.bgColor, 100, 200); + + this.modelGroup = new THREE.Object3D(); + this.rotationGroup = new THREE.Object3D(); + this.rotationGroup.useQuaternion = true; + this.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0); + this.rotationGroup.add(this.modelGroup); + + this.scene.add(this.rotationGroup); + this.setupLights(this.scene); +}; + +GLmol.prototype.zoomInto = function(atomlist, keepSlab) { + var tmp = this.getExtent(atomlist); + var center = new TV3(tmp[2][0], tmp[2][1], tmp[2][2]);//(tmp[0][0] + tmp[1][0]) / 2, (tmp[0][1] + tmp[1][1]) / 2, (tmp[0][2] + tmp[1][2]) / 2); + if (this.protein.appliedMatrix) {center = this.protein.appliedMatrix.multiplyVector3(center);} + this.modelGroup.position = center.multiplyScalar(-1); + var x = tmp[1][0] - tmp[0][0], y = tmp[1][1] - tmp[0][1], z = tmp[1][2] - tmp[0][2]; + + var maxD = Math.sqrt(x * x + y * y + z * z); + if (maxD < 25) maxD = 25; + + if (!keepSlab) { + this.slabNear = -maxD / 1.9; + this.slabFar = maxD / 3; + } + + this.rotationGroup.position.z = maxD * 0.35 / Math.tan(Math.PI / 180.0 * this.camera.fov / 2) - 150; + this.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0); +}; + +GLmol.prototype.rebuildScene = function() { + time = new Date(); + + var view = this.getView(); + this.initializeScene(); + this.defineRepresentation(); + this.setView(view); + + console.log("builded scene in " + (+new Date() - time) + "ms"); +}; + +GLmol.prototype.loadMolecule = function(repressZoom) { + this.loadMoleculeStr(repressZoom, $('#' + this.id + '_src').val()); +}; + +GLmol.prototype.loadMoleculeStr = function(repressZoom, source) { + var time = new Date(); + + this.protein = {sheet: [], helix: [], biomtChains: '', biomtMatrices: [], symMat: [], pdbID: '', title: ''}; + this.atoms = []; + + this.parsePDB2(source); + if (!this.parseSDF(source)) this.parseXYZ(source); + console.log("parsed in " + (+new Date() - time) + "ms"); + + var title = $('#' + this.id + '_pdbTitle'); + var titleStr = ''; + if (this.protein.pdbID != '') titleStr += '' + this.protein.pdbID + ''; + if (this.protein.title != '') titleStr += '
' + this.protein.title; + title.html(titleStr); + + this.rebuildScene(true); + if (repressZoom == undefined || !repressZoom) this.zoomInto(this.getAllAtoms()); + + this.show(); + }; + +GLmol.prototype.setSlabAndFog = function() { + var center = this.rotationGroup.position.z - this.camera.position.z; + if (center < 1) center = 1; + this.camera.near = center + this.slabNear; + if (this.camera.near < 1) this.camera.near = 1; + this.camera.far = center + this.slabFar; + if (this.camera.near + 1 > this.camera.far) this.camera.far = this.camera.near + 1; + if (this.camera instanceof THREE.PerspectiveCamera) { + this.camera.fov = this.fov; + } else { + this.camera.right = center * Math.tan(Math.PI / 180 * this.fov); + this.camera.left = - this.camera.right; + this.camera.top = this.camera.right / this.ASPECT; + this.camera.bottom = - this.camera.top; + } + this.camera.updateProjectionMatrix(); + this.scene.fog.near = this.camera.near + this.fogStart * (this.camera.far - this.camera.near); +// if (this.scene.fog.near > center) this.scene.fog.near = center; + this.scene.fog.far = this.camera.far; +}; + +GLmol.prototype.enableMouse = function() { + var me = this, glDOM = $(this.renderer.domElement); + + // TODO: Better touch panel support. + // Contribution is needed as I don't own any iOS or Android device with WebGL support. + glDOM.bind('mousedown touchstart', function(ev) { + ev.preventDefault(); + if (!me.scene) return; + var x = ev.pageX, y = ev.pageY; + if (ev.originalEvent.targetTouches && ev.originalEvent.targetTouches[0]) { + x = ev.originalEvent.targetTouches[0].pageX; + y = ev.originalEvent.targetTouches[0].pageY; + } + if (x == undefined) return; + me.isDragging = true; + me.mouseButton = ev.which; + me.mouseStartX = x; + me.mouseStartY = y; + me.cq = me.rotationGroup.quaternion; + me.cz = me.rotationGroup.position.z; + me.currentModelPos = me.modelGroup.position.clone(); + me.cslabNear = me.slabNear; + me.cslabFar = me.slabFar; + }); + + glDOM.bind('DOMMouseScroll mousewheel', function(ev) { // Zoom + ev.preventDefault(); + if (!me.scene) return; + var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85; + if (ev.originalEvent.detail) { // Webkit + me.rotationGroup.position.z += scaleFactor * ev.originalEvent.detail / 10; + } else if (ev.originalEvent.wheelDelta) { // Firefox + me.rotationGroup.position.z -= scaleFactor * ev.originalEvent.wheelDelta / 400; + } + console.log(ev.originalEvent.wheelDelta, ev.originalEvent.detail, me.rotationGroup.position.z); + me.show(); + }); + glDOM.bind("contextmenu", function(ev) {ev.preventDefault();}); + $('body').bind('mouseup touchend', function(ev) { + me.isDragging = false; + }); + + glDOM.bind('mousemove touchmove', function(ev) { // touchmove + ev.preventDefault(); + if (!me.scene) return; + if (!me.isDragging) return; + var mode = 0; + var modeRadio = $('input[name=' + me.id + '_mouseMode]:checked'); + if (modeRadio.length > 0) mode = parseInt(modeRadio.val()); + + var x = ev.pageX, y = ev.pageY; + if (ev.originalEvent.targetTouches && ev.originalEvent.targetTouches[0]) { + x = ev.originalEvent.targetTouches[0].pageX; + y = ev.originalEvent.targetTouches[0].pageY; + } + if (x == undefined) return; + var dx = (x - me.mouseStartX) / me.WIDTH; + var dy = (y - me.mouseStartY) / me.HEIGHT; + var r = Math.sqrt(dx * dx + dy * dy); + if (mode == 3 || (me.mouseButton == 3 && ev.ctrlKey)) { // Slab + me.slabNear = me.cslabNear + dx * 100; + me.slabFar = me.cslabFar + dy * 100; + } else if (mode == 2 || me.mouseButton == 3 || ev.shiftKey) { // Zoom + var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85; + if (scaleFactor < 80) scaleFactor = 80; + me.rotationGroup.position.z = me.cz - dy * scaleFactor; + } else if (mode == 1 || me.mouseButton == 2 || ev.ctrlKey) { // Translate + var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85; + if (scaleFactor < 20) scaleFactor = 20; + var translationByScreen = new TV3(- dx * scaleFactor, - dy * scaleFactor, 0); + var q = me.rotationGroup.quaternion; + var qinv = new THREE.Quaternion(q.x, q.y, q.z, q.w).inverse().normalize(); + var translation = qinv.multiplyVector3(translationByScreen); + me.modelGroup.position.x = me.currentModelPos.x + translation.x; + me.modelGroup.position.y = me.currentModelPos.y + translation.y; + me.modelGroup.position.z = me.currentModelPos.z + translation.z; + } else if ((mode == 0 || me.mouseButton == 1) && r != 0) { // Rotate + var rs = Math.sin(r * Math.PI) / r; + me.dq.x = Math.cos(r * Math.PI); + me.dq.y = 0; + me.dq.z = rs * dx; + me.dq.w = rs * dy; + me.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0); + me.rotationGroup.quaternion.multiplySelf(me.dq); + me.rotationGroup.quaternion.multiplySelf(me.cq); + } + me.show(); + }); +}; + + +GLmol.prototype.show = function() { + if (!this.scene) return; + + var time = new Date(); + this.setSlabAndFog(); + this.renderer.render(this.scene, this.camera); + console.log("rendered in " + (+new Date() - time) + "ms"); +}; + +// For scripting +GLmol.prototype.doFunc = function(func) { + func(this); +}; + +return GLmol; +}()); diff --git a/src/pdb/Three49custom.js b/src/pdb/Three49custom.js new file mode 100644 index 000000000..0f84d4420 --- /dev/null +++ b/src/pdb/Three49custom.js @@ -0,0 +1,6803 @@ + // Three.js - http://github.com/mrdoob/three.js +'use strict'; +var THREE = THREE || {REVISION: "49"}; +self.Int32Array || (self.Int32Array = Array, self.Float32Array = Array); +(function() { + for (var a = 0, b = ["ms", "moz", "webkit", "o"], c = 0; c < b.length && !window.requestAnimationFrame; ++c) { + window.requestAnimationFrame = window[b[c] + "RequestAnimationFrame"]; + window.cancelAnimationFrame = window[b[c] + "CancelAnimationFrame"] || window[b[c] + "CancelRequestAnimationFrame"] + } + if (!window.requestAnimationFrame) + window.requestAnimationFrame = function(b) { + var c = Date.now(), f = Math.max(0, 16 - (c - a)), h = window.setTimeout(function() { + b(c + f) + }, f); + a = c + f; + return h + }; + if (!window.cancelAnimationFrame) + window.cancelAnimationFrame = + function(a) { + clearTimeout(a) + } +})(); +THREE.Clock = function(a) { + this.autoStart = a !== void 0 ? a : true; + this.elapsedTime = this.oldTime = this.startTime = 0; + this.running = false +}; +THREE.Clock.prototype.start = function() { + this.oldTime = this.startTime = Date.now(); + this.running = true +}; +THREE.Clock.prototype.stop = function() { + this.getElapsedTime(); + this.running = false +}; +THREE.Clock.prototype.getElapsedTime = function() { + return this.elapsedTime = this.elapsedTime + this.getDelta() +}; +THREE.Clock.prototype.getDelta = function() { + var a = 0; + this.autoStart && !this.running && this.start(); + if (this.running) { + var b = Date.now(), a = 0.001 * (b - this.oldTime); + this.oldTime = b; + this.elapsedTime = this.elapsedTime + a + } + return a +}; +THREE.Color = function(a) { + a !== void 0 && this.setHex(a); + return this +}; +THREE.Color.prototype = {constructor: THREE.Color,r: 1,g: 1,b: 1,copy: function(a) { + this.r = a.r; + this.g = a.g; + this.b = a.b; + return this + },copyGammaToLinear: function(a) { + this.r = a.r * a.r; + this.g = a.g * a.g; + this.b = a.b * a.b; + return this + },copyLinearToGamma: function(a) { + this.r = Math.sqrt(a.r); + this.g = Math.sqrt(a.g); + this.b = Math.sqrt(a.b); + return this + },convertGammaToLinear: function() { + var a = this.r, b = this.g, c = this.b; + this.r = a * a; + this.g = b * b; + this.b = c * c; + return this + },convertLinearToGamma: function() { + this.r = Math.sqrt(this.r); + this.g = Math.sqrt(this.g); + this.b = Math.sqrt(this.b); + return this + },setRGB: function(a, b, c) { + this.r = a; + this.g = b; + this.b = c; + return this + },setHSV: function(a, b, c) { + var d, e, f; + if (c === 0) + this.r = this.g = this.b = 0; + else { + d = Math.floor(a * 6); + e = a * 6 - d; + a = c * (1 - b); + f = c * (1 - b * e); + b = c * (1 - b * (1 - e)); + switch (d) { + case 1: + this.r = f; + this.g = c; + this.b = a; + break; + case 2: + this.r = a; + this.g = c; + this.b = b; + break; + case 3: + this.r = a; + this.g = f; + this.b = c; + break; + case 4: + this.r = b; + this.g = a; + this.b = c; + break; + case 5: + this.r = c; + this.g = a; + this.b = f; + break; + case 6: + case 0: + this.r = c; + this.g = b; + this.b = a + } + } + return this + },setHex: function(a) { + a = + Math.floor(a); + this.r = (a >> 16 & 255) / 255; + this.g = (a >> 8 & 255) / 255; + this.b = (a & 255) / 255; + return this + },lerpSelf: function(a, b) { + this.r = this.r + (a.r - this.r) * b; + this.g = this.g + (a.g - this.g) * b; + this.b = this.b + (a.b - this.b) * b; + return this + },getHex: function() { + return Math.floor(this.r * 255) << 16 ^ Math.floor(this.g * 255) << 8 ^ Math.floor(this.b * 255) + },getContextStyle: function() { + return "rgb(" + Math.floor(this.r * 255) + "," + Math.floor(this.g * 255) + "," + Math.floor(this.b * 255) + ")" + },clone: function() { + return (new THREE.Color).setRGB(this.r, this.g, this.b) + }}; +THREE.Vector2 = function(a, b) { + this.x = a || 0; + this.y = b || 0 +}; +THREE.Vector2.prototype = {constructor: THREE.Vector2,set: function(a, b) { + this.x = a; + this.y = b; + return this + },copy: function(a) { + this.x = a.x; + this.y = a.y; + return this + },add: function(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + return this + },addSelf: function(a) { + this.x = this.x + a.x; + this.y = this.y + a.y; + return this + },sub: function(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + return this + },subSelf: function(a) { + if (a) { + this.x = this.x - a.x; + this.y = this.y - a.y; + } + return this + },multiplyScalar: function(a) { + this.x = this.x * a; + this.y = this.y * a; + return this + },divideScalar: function(a) { + if (a) { + this.x = + this.x / a; + this.y = this.y / a + } else + this.set(0, 0); + return this + },negate: function() { + return this.multiplyScalar(-1) + },dot: function(a) { + return this.x * a.x + this.y * a.y + },lengthSq: function() { + return this.x * this.x + this.y * this.y + },length: function() { + return Math.sqrt(this.lengthSq()) + },normalize: function() { + return this.divideScalar(this.length()) + },distanceTo: function(a) { + return Math.sqrt(this.distanceToSquared(a)) + },distanceToSquared: function(a) { + var b = this.x - a.x, a = this.y - a.y; + return b * b + a * a + },setLength: function(a) { + return this.normalize().multiplyScalar(a) + }, + lerpSelf: function(a, b) { + this.x = this.x + (a.x - this.x) * b; + this.y = this.y + (a.y - this.y) * b; + return this + },equals: function(a) { + return a.x === this.x && a.y === this.y + },isZero: function() { + return this.lengthSq() < 1.0E-4 + },clone: function() { + return new THREE.Vector2(this.x, this.y) + }}; +THREE.Vector3 = function(a, b, c) { + this.x = a || 0; + this.y = b || 0; + this.z = c || 0 +}; +THREE.Vector3.prototype = {constructor: THREE.Vector3,set: function(a, b, c) { + this.x = a; + this.y = b; + this.z = c; + return this + },setX: function(a) { + this.x = a; + return this + },setY: function(a) { + this.y = a; + return this + },setZ: function(a) { + this.z = a; + return this + },copy: function(a) { + this.x = a.x; + this.y = a.y; + this.z = a.z; + return this + },add: function(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + return this + },addSelf: function(a) { + this.x = this.x + a.x; + this.y = this.y + a.y; + this.z = this.z + a.z; + return this + },addScalar: function(a) { + if (a) { + this.x = this.x + a; + this.y = this.y + a; + this.z = this.z + a; + } + return this + },sub: function(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + return this + },subSelf: function(a) { + if (a) { + this.x = this.x - a.x; + this.y = this.y - a.y; + this.z = this.z - a.z; + } + return this + },multiply: function(a, b) { + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + return this + },multiplySelf: function(a) { + this.x = this.x * a.x; + this.y = this.y * a.y; + this.z = this.z * a.z; + return this + },multiplyScalar: function(a) { + this.x = this.x * a; + this.y = this.y * a; + this.z = this.z * a; + return this + },divideSelf: function(a) { + this.x = this.x / a.x; + this.y = + this.y / a.y; + this.z = this.z / a.z; + return this + },divideScalar: function(a) { + if (a) { + this.x = this.x / a; + this.y = this.y / a; + this.z = this.z / a + } else + this.z = this.y = this.x = 0; + return this + },negate: function() { + return this.multiplyScalar(-1) + },dot: function(a) { + return this.x * a.x + this.y * a.y + this.z * a.z + },lengthSq: function() { + return this.x * this.x + this.y * this.y + this.z * this.z + },length: function() { + return Math.sqrt(this.lengthSq()) + },lengthManhattan: function() { + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + },normalize: function() { + return this.divideScalar(this.length()) + }, + setLength: function(a) { + return this.normalize().multiplyScalar(a) + },lerpSelf: function(a, b) { + this.x = this.x + (a.x - this.x) * b; + this.y = this.y + (a.y - this.y) * b; + this.z = this.z + (a.z - this.z) * b; + return this + },cross: function(a, b) { + this.x = a.y * b.z - a.z * b.y; + this.y = a.z * b.x - a.x * b.z; + this.z = a.x * b.y - a.y * b.x; + return this + },crossSelf: function(a) { + var b = this.x, c = this.y, d = this.z; + this.x = c * a.z - d * a.y; + this.y = d * a.x - b * a.z; + this.z = b * a.y - c * a.x; + return this + },distanceTo: function(a) { + return Math.sqrt(this.distanceToSquared(a)) + },distanceToSquared: function(a) { + return (new THREE.Vector3).sub(this, + a).lengthSq() + },getPositionFromMatrix: function(a) { + this.x = a.elements[12]; + this.y = a.elements[13]; + this.z = a.elements[14]; + return this + },getRotationFromMatrix: function(a, b) { + var c = b ? b.x : 1, d = b ? b.y : 1, e = b ? b.z : 1, f = a.elements[0] / c, h = a.elements[4] / d, c = a.elements[1] / c, d = a.elements[5] / d, i = a.elements[9] / e, l = a.elements[10] / e; + this.y = Math.asin(a.elements[8] / e); + e = Math.cos(this.y); + if (Math.abs(e) > 1.0E-5) { + this.x = Math.atan2(-i / e, l / e); + this.z = Math.atan2(-h / e, f / e) + } else { + this.x = 0; + this.z = Math.atan2(c, d) + } + return this + },getScaleFromMatrix: function(a) { + var b = + this.set(a.elements[0], a.elements[1], a.elements[2]).length(), c = this.set(a.elements[4], a.elements[5], a.elements[6]).length(), a = this.set(a.elements[8], a.elements[9], a.elements[10]).length(); + this.x = b; + this.y = c; + this.z = a + },equals: function(a) { + return a.x === this.x && a.y === this.y && a.z === this.z + },isZero: function() { + return this.lengthSq() < 1.0E-4 + },clone: function() { + return new THREE.Vector3(this.x, this.y, this.z) + }}; +THREE.Vector4 = function(a, b, c, d) { + this.x = a || 0; + this.y = b || 0; + this.z = c || 0; + this.w = d !== void 0 ? d : 1 +}; +THREE.Vector4.prototype = {constructor: THREE.Vector4,set: function(a, b, c, d) { + this.x = a; + this.y = b; + this.z = c; + this.w = d; + return this + },copy: function(a) { + this.x = a.x; + this.y = a.y; + this.z = a.z; + this.w = a.w !== void 0 ? a.w : 1; + return this + },add: function(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + return this + },addSelf: function(a) { + this.x = this.x + a.x; + this.y = this.y + a.y; + this.z = this.z + a.z; + this.w = this.w + a.w; + return this + },sub: function(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + return this + },subSelf: function(a) { + this.x = + this.x - a.x; + this.y = this.y - a.y; + this.z = this.z - a.z; + this.w = this.w - a.w; + return this + },multiplyScalar: function(a) { + this.x = this.x * a; + this.y = this.y * a; + this.z = this.z * a; + this.w = this.w * a; + return this + },divideScalar: function(a) { + if (a) { + this.x = this.x / a; + this.y = this.y / a; + this.z = this.z / a; + this.w = this.w / a + } else { + this.z = this.y = this.x = 0; + this.w = 1 + } + return this + },negate: function() { + return this.multiplyScalar(-1) + },dot: function(a) { + return this.x * a.x + this.y * a.y + this.z * a.z + this.w * a.w + },lengthSq: function() { + return this.dot(this) + },length: function() { + return Math.sqrt(this.lengthSq()) + }, + normalize: function() { + return this.divideScalar(this.length()) + },setLength: function(a) { + return this.normalize().multiplyScalar(a) + },lerpSelf: function(a, b) { + this.x = this.x + (a.x - this.x) * b; + this.y = this.y + (a.y - this.y) * b; + this.z = this.z + (a.z - this.z) * b; + this.w = this.w + (a.w - this.w) * b; + return this + },clone: function() { + return new THREE.Vector4(this.x, this.y, this.z, this.w) + }}; +THREE.Frustum = function() { + this.planes = [new THREE.Vector4, new THREE.Vector4, new THREE.Vector4, new THREE.Vector4, new THREE.Vector4, new THREE.Vector4] +}; +THREE.Frustum.prototype.setFromMatrix = function(a) { + var b, c = this.planes, d = a.elements, a = d[0]; + b = d[1]; + var e = d[2], f = d[3], h = d[4], i = d[5], l = d[6], k = d[7], j = d[8], m = d[9], n = d[10], o = d[11], s = d[12], p = d[13], q = d[14], d = d[15]; + c[0].set(f - a, k - h, o - j, d - s); + c[1].set(f + a, k + h, o + j, d + s); + c[2].set(f + b, k + i, o + m, d + p); + c[3].set(f - b, k - i, o - m, d - p); + c[4].set(f - e, k - l, o - n, d - q); + c[5].set(f + e, k + l, o + n, d + q); + for (a = 0; a < 6; a++) { + b = c[a]; + b.divideScalar(Math.sqrt(b.x * b.x + b.y * b.y + b.z * b.z)) + } +}; +THREE.Frustum.prototype.contains = function(a) { + for (var b = this.planes, c = a.matrixWorld, d = c.elements, c = -a.geometry.boundingSphere.radius * c.getMaxScaleOnAxis(), e = 0; e < 6; e++) { + a = b[e].x * d[12] + b[e].y * d[13] + b[e].z * d[14] + b[e].w; + if (a <= c) + return false + } + return true +}; +THREE.Frustum.__v1 = new THREE.Vector3; +THREE.Ray = function(a, b) { + function c(a, b, c) { + s.sub(c, a); + w = s.dot(b); + A = p.add(a, q.copy(b).multiplyScalar(w)); + return y = c.distanceTo(A) + } + function d(a, b, c, d) { + s.sub(d, b); + p.sub(c, b); + q.sub(a, b); + u = s.dot(s); + H = s.dot(p); + B = s.dot(q); + K = p.dot(p); + N = p.dot(q); + Y = 1 / (u * K - H * H); + ca = (K * B - H * N) * Y; + I = (u * N - H * B) * Y; + return ca >= 0 && I >= 0 && ca + I < 1 + } + this.origin = a || new THREE.Vector3; + this.direction = b || new THREE.Vector3; + var e = 1.0E-4; + this.setPrecision = function(a) { + e = a + }; + var f = new THREE.Vector3, h = new THREE.Vector3, i = new THREE.Vector3, l = new THREE.Vector3, + k = new THREE.Vector3, j = new THREE.Vector3, m = new THREE.Vector3, n = new THREE.Vector3, o = new THREE.Vector3; + this.intersectObject = function(a) { + var b, s = []; + if (a instanceof THREE.Particle) { + var p = c(this.origin, this.direction, a.matrixWorld.getPosition()); + if (p > a.scale.x) + return []; + b = {distance: p,point: a.position,face: null,object: a}; + s.push(b) + } else if (a instanceof THREE.Mesh) { + var p = c(this.origin, this.direction, a.matrixWorld.getPosition()), g = THREE.Frustum.__v1.set(a.matrixWorld.getColumnX().length(), a.matrixWorld.getColumnY().length(), + a.matrixWorld.getColumnZ().length()); + if (p > a.geometry.boundingSphere.radius * Math.max(g.x, Math.max(g.y, g.z))) + return s; + var q, u, y = a.geometry, w = y.vertices, A; + a.matrixRotationWorld.extractRotation(a.matrixWorld); + p = 0; + for (g = y.faces.length; p < g; p++) { + b = y.faces[p]; + k.copy(this.origin); + j.copy(this.direction); + A = a.matrixWorld; + m = A.multiplyVector3(m.copy(b.centroid)).subSelf(k); + n = a.matrixRotationWorld.multiplyVector3(n.copy(b.normal)); + q = j.dot(n); + if (!(Math.abs(q) < e)) { + u = n.dot(m) / q; + if (!(u < 0) && (a.doubleSided || (a.flipSided ? + q > 0 : q < 0))) { + o.add(k, j.multiplyScalar(u)); + if (b instanceof THREE.Face3) { + f = A.multiplyVector3(f.copy(w[b.a])); + h = A.multiplyVector3(h.copy(w[b.b])); + i = A.multiplyVector3(i.copy(w[b.c])); + if (d(o, f, h, i)) { + b = {distance: k.distanceTo(o),point: o.clone(),face: b,object: a}; + s.push(b) + } + } else if (b instanceof THREE.Face4) { + f = A.multiplyVector3(f.copy(w[b.a])); + h = A.multiplyVector3(h.copy(w[b.b])); + i = A.multiplyVector3(i.copy(w[b.c])); + l = A.multiplyVector3(l.copy(w[b.d])); + if (d(o, f, h, l) || d(o, h, i, l)) { + b = {distance: k.distanceTo(o),point: o.clone(), + face: b,object: a}; + s.push(b) + } + } + } + } + } + } + return s + }; + this.intersectObjects = function(a) { + for (var b = [], c = 0, d = a.length; c < d; c++) + Array.prototype.push.apply(b, this.intersectObject(a[c])); + b.sort(function(a, b) { + return a.distance - b.distance + }); + return b + }; + var s = new THREE.Vector3, p = new THREE.Vector3, q = new THREE.Vector3, w, A, y, u, H, B, K, N, Y, ca, I +}; +THREE.Rectangle = function() { + function a() { + f = d - b; + h = e - c + } + var b, c, d, e, f, h, i = true; + this.getX = function() { + return b + }; + this.getY = function() { + return c + }; + this.getWidth = function() { + return f + }; + this.getHeight = function() { + return h + }; + this.getLeft = function() { + return b + }; + this.getTop = function() { + return c + }; + this.getRight = function() { + return d + }; + this.getBottom = function() { + return e + }; + this.set = function(f, h, j, m) { + i = false; + b = f; + c = h; + d = j; + e = m; + a() + }; + this.addPoint = function(f, h) { + if (i) { + i = false; + b = f; + c = h; + d = f; + e = h + } else { + b = b < f ? b : f; + c = c < h ? c : h; + d = d > f ? d : f; + e = e > h ? + e : h + } + a() + }; + this.add3Points = function(f, h, j, m, n, o) { + if (i) { + i = false; + b = f < j ? f < n ? f : n : j < n ? j : n; + c = h < m ? h < o ? h : o : m < o ? m : o; + d = f > j ? f > n ? f : n : j > n ? j : n; + e = h > m ? h > o ? h : o : m > o ? m : o + } else { + b = f < j ? f < n ? f < b ? f : b : n < b ? n : b : j < n ? j < b ? j : b : n < b ? n : b; + c = h < m ? h < o ? h < c ? h : c : o < c ? o : c : m < o ? m < c ? m : c : o < c ? o : c; + d = f > j ? f > n ? f > d ? f : d : n > d ? n : d : j > n ? j > d ? j : d : n > d ? n : d; + e = h > m ? h > o ? h > e ? h : e : o > e ? o : e : m > o ? m > e ? m : e : o > e ? o : e + } + a() + }; + this.addRectangle = function(f) { + if (i) { + i = false; + b = f.getLeft(); + c = f.getTop(); + d = f.getRight(); + e = f.getBottom() + } else { + b = b < f.getLeft() ? b : f.getLeft(); + c = c < f.getTop() ? c : f.getTop(); + d = d > f.getRight() ? d : f.getRight(); + e = e > f.getBottom() ? e : f.getBottom() + } + a() + }; + this.inflate = function(f) { + b = b - f; + c = c - f; + d = d + f; + e = e + f; + a() + }; + this.minSelf = function(f) { + b = b > f.getLeft() ? b : f.getLeft(); + c = c > f.getTop() ? c : f.getTop(); + d = d < f.getRight() ? d : f.getRight(); + e = e < f.getBottom() ? e : f.getBottom(); + a() + }; + this.intersects = function(a) { + return d < a.getLeft() || b > a.getRight() || e < a.getTop() || c > a.getBottom() ? false : true + }; + this.empty = function() { + i = true; + e = d = c = b = 0; + a() + }; + this.isEmpty = function() { + return i + } +}; +THREE.Math = {clamp: function(a, b, c) { + return a < b ? b : a > c ? c : a + },clampBottom: function(a, b) { + return a < b ? b : a + },mapLinear: function(a, b, c, d, e) { + return d + (a - b) * (e - d) / (c - b) + },random16: function() { + return (65280 * Math.random() + 255 * Math.random()) / 65535 + },randInt: function(a, b) { + return a + Math.floor(Math.random() * (b - a + 1)) + },randFloat: function(a, b) { + return a + Math.random() * (b - a) + },randFloatSpread: function(a) { + return a * (0.5 - Math.random()) + },sign: function(a) { + return a < 0 ? -1 : a > 0 ? 1 : 0 + }}; +THREE.Matrix3 = function() { + this.elements = new Float32Array(9) +}; +THREE.Matrix3.prototype = {constructor: THREE.Matrix3,getInverse: function(a) { + var b = a.elements, a = b[10] * b[5] - b[6] * b[9], c = -b[10] * b[1] + b[2] * b[9], d = b[6] * b[1] - b[2] * b[5], e = -b[10] * b[4] + b[6] * b[8], f = b[10] * b[0] - b[2] * b[8], h = -b[6] * b[0] + b[2] * b[4], i = b[9] * b[4] - b[5] * b[8], l = -b[9] * b[0] + b[1] * b[8], k = b[5] * b[0] - b[1] * b[4], b = b[0] * a + b[1] * e + b[2] * i; + b === 0 && console.warn("Matrix3.getInverse(): determinant == 0"); + var b = 1 / b, j = this.elements; + j[0] = b * a; + j[1] = b * c; + j[2] = b * d; + j[3] = b * e; + j[4] = b * f; + j[5] = b * h; + j[6] = b * i; + j[7] = b * l; + j[8] = b * k; + return this + }, + transpose: function() { + var a, b = this.elements; + a = b[1]; + b[1] = b[3]; + b[3] = a; + a = b[2]; + b[2] = b[6]; + b[6] = a; + a = b[5]; + b[5] = b[7]; + b[7] = a; + return this + },transposeIntoArray: function(a) { + var b = this.m; + a[0] = b[0]; + a[1] = b[3]; + a[2] = b[6]; + a[3] = b[1]; + a[4] = b[4]; + a[5] = b[7]; + a[6] = b[2]; + a[7] = b[5]; + a[8] = b[8]; + return this + }}; +THREE.Matrix4 = function(a, b, c, d, e, f, h, i, l, k, j, m, n, o, s, p) { + this.elements = new Float32Array(16); + this.set(a !== void 0 ? a : 1, b || 0, c || 0, d || 0, e || 0, f !== void 0 ? f : 1, h || 0, i || 0, l || 0, k || 0, j !== void 0 ? j : 1, m || 0, n || 0, o || 0, s || 0, p !== void 0 ? p : 1) +}; +THREE.Matrix4.prototype = {constructor: THREE.Matrix4,set: function(a, b, c, d, e, f, h, i, l, k, j, m, n, o, s, p) { + var q = this.elements; + q[0] = a; + q[4] = b; + q[8] = c; + q[12] = d; + q[1] = e; + q[5] = f; + q[9] = h; + q[13] = i; + q[2] = l; + q[6] = k; + q[10] = j; + q[14] = m; + q[3] = n; + q[7] = o; + q[11] = s; + q[15] = p; + return this + },identity: function() { + this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return this + },copy: function(a) { + a = a.elements; + this.set(a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7], a[11], a[15]); + return this + },lookAt: function(a, b, c) { + var d = this.elements, + e = THREE.Matrix4.__v1, f = THREE.Matrix4.__v2, h = THREE.Matrix4.__v3; + h.sub(a, b).normalize(); + if (h.length() === 0) + h.z = 1; + e.cross(c, h).normalize(); + if (e.length() === 0) { + h.x = h.x + 1.0E-4; + e.cross(c, h).normalize() + } + f.cross(h, e); + d[0] = e.x; + d[4] = f.x; + d[8] = h.x; + d[1] = e.y; + d[5] = f.y; + d[9] = h.y; + d[2] = e.z; + d[6] = f.z; + d[10] = h.z; + return this + },multiply: function(a, b) { + var c = a.elements, d = b.elements, e = this.elements, f = c[0], h = c[4], i = c[8], l = c[12], k = c[1], j = c[5], m = c[9], n = c[13], o = c[2], s = c[6], p = c[10], q = c[14], w = c[3], A = c[7], y = c[11], c = c[15], u = d[0], H = d[4], + B = d[8], K = d[12], N = d[1], Y = d[5], ca = d[9], I = d[13], ba = d[2], ja = d[6], ya = d[10], D = d[14], g = d[3], Na = d[7], za = d[11], d = d[15]; + e[0] = f * u + h * N + i * ba + l * g; + e[4] = f * H + h * Y + i * ja + l * Na; + e[8] = f * B + h * ca + i * ya + l * za; + e[12] = f * K + h * I + i * D + l * d; + e[1] = k * u + j * N + m * ba + n * g; + e[5] = k * H + j * Y + m * ja + n * Na; + e[9] = k * B + j * ca + m * ya + n * za; + e[13] = k * K + j * I + m * D + n * d; + e[2] = o * u + s * N + p * ba + q * g; + e[6] = o * H + s * Y + p * ja + q * Na; + e[10] = o * B + s * ca + p * ya + q * za; + e[14] = o * K + s * I + p * D + q * d; + e[3] = w * u + A * N + y * ba + c * g; + e[7] = w * H + A * Y + y * ja + c * Na; + e[11] = w * B + A * ca + y * ya + c * za; + e[15] = w * K + A * I + y * D + c * d; + return this + },multiplySelf: function(a) { + return this.multiply(this, + a) + },multiplyToArray: function(a, b, c) { + var d = this.elements; + this.multiply(a, b); + c[0] = d[0]; + c[1] = d[1]; + c[2] = d[2]; + c[3] = d[3]; + c[4] = d[4]; + c[5] = d[5]; + c[6] = d[6]; + c[7] = d[7]; + c[8] = d[8]; + c[9] = d[9]; + c[10] = d[10]; + c[11] = d[11]; + c[12] = d[12]; + c[13] = d[13]; + c[14] = d[14]; + c[15] = d[15]; + return this + },multiplyScalar: function(a) { + var b = this.elements; + b[0] = b[0] * a; + b[4] = b[4] * a; + b[8] = b[8] * a; + b[12] = b[12] * a; + b[1] = b[1] * a; + b[5] = b[5] * a; + b[9] = b[9] * a; + b[13] = b[13] * a; + b[2] = b[2] * a; + b[6] = b[6] * a; + b[10] = b[10] * a; + b[14] = b[14] * a; + b[3] = b[3] * a; + b[7] = b[7] * a; + b[11] = b[11] * a; + b[15] = + b[15] * a; + return this + },multiplyVector3: function(a) { + var b = this.elements, c = a.x, d = a.y, e = a.z, f = 1 / (b[3] * c + b[7] * d + b[11] * e + b[15]); + a.x = (b[0] * c + b[4] * d + b[8] * e + b[12]) * f; + a.y = (b[1] * c + b[5] * d + b[9] * e + b[13]) * f; + a.z = (b[2] * c + b[6] * d + b[10] * e + b[14]) * f; + return a + },multiplyVector4: function(a) { + var b = this.elements, c = a.x, d = a.y, e = a.z, f = a.w; + a.x = b[0] * c + b[4] * d + b[8] * e + b[12] * f; + a.y = b[1] * c + b[5] * d + b[9] * e + b[13] * f; + a.z = b[2] * c + b[6] * d + b[10] * e + b[14] * f; + a.w = b[3] * c + b[7] * d + b[11] * e + b[15] * f; + return a + },rotateAxis: function(a) { + var b = this.elements, c = a.x, + d = a.y, e = a.z; + a.x = c * b[0] + d * b[4] + e * b[8]; + a.y = c * b[1] + d * b[5] + e * b[9]; + a.z = c * b[2] + d * b[6] + e * b[10]; + a.normalize(); + return a + },crossVector: function(a) { + var b = this.elements, c = new THREE.Vector4; + c.x = b[0] * a.x + b[4] * a.y + b[8] * a.z + b[12] * a.w; + c.y = b[1] * a.x + b[5] * a.y + b[9] * a.z + b[13] * a.w; + c.z = b[2] * a.x + b[6] * a.y + b[10] * a.z + b[14] * a.w; + c.w = a.w ? b[3] * a.x + b[7] * a.y + b[11] * a.z + b[15] * a.w : 1; + return c + },determinant: function() { + var a = this.elements, b = a[0], c = a[4], d = a[8], e = a[12], f = a[1], h = a[5], i = a[9], l = a[13], k = a[2], j = a[6], m = a[10], n = a[14], o = a[3], s = a[7], + p = a[11], a = a[15]; + return e * i * j * o - d * l * j * o - e * h * m * o + c * l * m * o + d * h * n * o - c * i * n * o - e * i * k * s + d * l * k * s + e * f * m * s - b * l * m * s - d * f * n * s + b * i * n * s + e * h * k * p - c * l * k * p - e * f * j * p + b * l * j * p + c * f * n * p - b * h * n * p - d * h * k * a + c * i * k * a + d * f * j * a - b * i * j * a - c * f * m * a + b * h * m * a + },transpose: function() { + var a = this.elements, b; + b = a[1]; + a[1] = a[4]; + a[4] = b; + b = a[2]; + a[2] = a[8]; + a[8] = b; + b = a[6]; + a[6] = a[9]; + a[9] = b; + b = a[3]; + a[3] = a[12]; + a[12] = b; + b = a[7]; + a[7] = a[13]; + a[13] = b; + b = a[11]; + a[11] = a[14]; + a[14] = b; + return this + },flattenToArray: function(a) { + var b = this.elements; + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; + a[3] = b[3]; + a[4] = b[4]; + a[5] = b[5]; + a[6] = b[6]; + a[7] = b[7]; + a[8] = b[8]; + a[9] = b[9]; + a[10] = b[10]; + a[11] = b[11]; + a[12] = b[12]; + a[13] = b[13]; + a[14] = b[14]; + a[15] = b[15]; + return a + },flattenToArrayOffset: function(a, b) { + var c = this.elements; + a[b] = c[0]; + a[b + 1] = c[1]; + a[b + 2] = c[2]; + a[b + 3] = c[3]; + a[b + 4] = c[4]; + a[b + 5] = c[5]; + a[b + 6] = c[6]; + a[b + 7] = c[7]; + a[b + 8] = c[8]; + a[b + 9] = c[9]; + a[b + 10] = c[10]; + a[b + 11] = c[11]; + a[b + 12] = c[12]; + a[b + 13] = c[13]; + a[b + 14] = c[14]; + a[b + 15] = c[15]; + return a + },getPosition: function() { + var a = this.elements; + return THREE.Matrix4.__v1.set(a[12], a[13], + a[14]) + },setPosition: function(a) { + var b = this.elements; + b[12] = a.x; + b[13] = a.y; + b[14] = a.z; + return this + },getColumnX: function() { + var a = this.elements; + return THREE.Matrix4.__v1.set(a[0], a[1], a[2]) + },getColumnY: function() { + var a = this.elements; + return THREE.Matrix4.__v1.set(a[4], a[5], a[6]) + },getColumnZ: function() { + var a = this.elements; + return THREE.Matrix4.__v1.set(a[8], a[9], a[10]) + },getInverse: function(a) { + var b = this.elements, c = a.elements, d = c[0], e = c[4], f = c[8], h = c[12], i = c[1], l = c[5], k = c[9], j = c[13], m = c[2], n = c[6], o = c[10], s = + c[14], p = c[3], q = c[7], w = c[11], c = c[15]; + b[0] = k * s * q - j * o * q + j * n * w - l * s * w - k * n * c + l * o * c; + b[4] = h * o * q - f * s * q - h * n * w + e * s * w + f * n * c - e * o * c; + b[8] = f * j * q - h * k * q + h * l * w - e * j * w - f * l * c + e * k * c; + b[12] = h * k * n - f * j * n - h * l * o + e * j * o + f * l * s - e * k * s; + b[1] = j * o * p - k * s * p - j * m * w + i * s * w + k * m * c - i * o * c; + b[5] = f * s * p - h * o * p + h * m * w - d * s * w - f * m * c + d * o * c; + b[9] = h * k * p - f * j * p - h * i * w + d * j * w + f * i * c - d * k * c; + b[13] = f * j * m - h * k * m + h * i * o - d * j * o - f * i * s + d * k * s; + b[2] = l * s * p - j * n * p + j * m * q - i * s * q - l * m * c + i * n * c; + b[6] = h * n * p - e * s * p - h * m * q + d * s * q + e * m * c - d * n * c; + b[10] = e * j * p - h * l * p + h * i * q - d * j * q - e * i * c + d * l * c; + b[14] = h * l * m - + e * j * m - h * i * n + d * j * n + e * i * s - d * l * s; + b[3] = k * n * p - l * o * p - k * m * q + i * o * q + l * m * w - i * n * w; + b[7] = e * o * p - f * n * p + f * m * q - d * o * q - e * m * w + d * n * w; + b[11] = f * l * p - e * k * p - f * i * q + d * k * q + e * i * w - d * l * w; + b[15] = e * k * m - f * l * m + f * i * n - d * k * n - e * i * o + d * l * o; + this.multiplyScalar(1 / a.determinant()); + return this + },setRotationFromEuler: function(a, b) { + var c = this.elements, d = a.x, e = a.y, f = a.z, h = Math.cos(d), d = Math.sin(d), i = Math.cos(e), e = Math.sin(e), l = Math.cos(f), f = Math.sin(f); + switch (b) { + case "YXZ": + var k = i * l, j = i * f, m = e * l, n = e * f; + c[0] = k + n * d; + c[4] = m * d - j; + c[8] = h * e; + c[1] = h * f; + c[5] = h * + l; + c[9] = -d; + c[2] = j * d - m; + c[6] = n + k * d; + c[10] = h * i; + break; + case "ZXY": + k = i * l; + j = i * f; + m = e * l; + n = e * f; + c[0] = k - n * d; + c[4] = -h * f; + c[8] = m + j * d; + c[1] = j + m * d; + c[5] = h * l; + c[9] = n - k * d; + c[2] = -h * e; + c[6] = d; + c[10] = h * i; + break; + case "ZYX": + k = h * l; + j = h * f; + m = d * l; + n = d * f; + c[0] = i * l; + c[4] = m * e - j; + c[8] = k * e + n; + c[1] = i * f; + c[5] = n * e + k; + c[9] = j * e - m; + c[2] = -e; + c[6] = d * i; + c[10] = h * i; + break; + case "YZX": + k = h * i; + j = h * e; + m = d * i; + n = d * e; + c[0] = i * l; + c[4] = n - k * f; + c[8] = m * f + j; + c[1] = f; + c[5] = h * l; + c[9] = -d * l; + c[2] = -e * l; + c[6] = j * f + m; + c[10] = k - n * f; + break; + case "XZY": + k = h * i; + j = h * e; + m = d * i; + n = d * e; + c[0] = i * l; + c[4] = -f; + c[8] = e * l; + c[1] = k * f + n; + c[5] = h * l; + c[9] = j * f - m; + c[2] = m * f - j; + c[6] = d * l; + c[10] = n * f + k; + break; + default: + k = h * l; + j = h * f; + m = d * l; + n = d * f; + c[0] = i * l; + c[4] = -i * f; + c[8] = e; + c[1] = j + m * e; + c[5] = k - n * e; + c[9] = -d * i; + c[2] = n - k * e; + c[6] = m + j * e; + c[10] = h * i + } + return this + },setRotationFromQuaternion: function(a) { + var b = this.elements, c = a.x, d = a.y, e = a.z, f = a.w, h = c + c, i = d + d, l = e + e, a = c * h, k = c * i, c = c * l, j = d * i, d = d * l, e = e * l, h = f * h, i = f * i, f = f * l; + b[0] = 1 - (j + e); + b[4] = k - f; + b[8] = c + i; + b[1] = k + f; + b[5] = 1 - (a + e); + b[9] = d - h; + b[2] = c - i; + b[6] = d + h; + b[10] = 1 - (a + j); + return this + },compose: function(a, b, c) { + var d = this.elements, + e = THREE.Matrix4.__m1, f = THREE.Matrix4.__m2; + e.identity(); + e.setRotationFromQuaternion(b); + f.makeScale(c.x, c.y, c.z); + this.multiply(e, f); + d[12] = a.x; + d[13] = a.y; + d[14] = a.z; + return this + },decompose: function(a, b, c) { + var d = this.elements, e = THREE.Matrix4.__v1, f = THREE.Matrix4.__v2, h = THREE.Matrix4.__v3; + e.set(d[0], d[1], d[2]); + f.set(d[4], d[5], d[6]); + h.set(d[8], d[9], d[10]); + a = a instanceof THREE.Vector3 ? a : new THREE.Vector3; + b = b instanceof THREE.Quaternion ? b : new THREE.Quaternion; + c = c instanceof THREE.Vector3 ? c : new THREE.Vector3; + c.x = e.length(); + c.y = f.length(); + c.z = h.length(); + a.x = d[12]; + a.y = d[13]; + a.z = d[14]; + d = THREE.Matrix4.__m1; + d.copy(this); + d.elements[0] = d.elements[0] / c.x; + d.elements[1] = d.elements[1] / c.x; + d.elements[2] = d.elements[2] / c.x; + d.elements[4] = d.elements[4] / c.y; + d.elements[5] = d.elements[5] / c.y; + d.elements[6] = d.elements[6] / c.y; + d.elements[8] = d.elements[8] / c.z; + d.elements[9] = d.elements[9] / c.z; + d.elements[10] = d.elements[10] / c.z; + b.setFromRotationMatrix(d); + return [a, b, c] + },extractPosition: function(a) { + var b = this.elements, a = a.elements; + b[12] = a[12]; + b[13] = a[13]; + b[14] = a[14]; + return this + },extractRotation: function(a) { + var b = this.elements, a = a.elements, c = THREE.Matrix4.__v1, d = 1 / c.set(a[0], a[1], a[2]).length(), e = 1 / c.set(a[4], a[5], a[6]).length(), c = 1 / c.set(a[8], a[9], a[10]).length(); + b[0] = a[0] * d; + b[1] = a[1] * d; + b[2] = a[2] * d; + b[4] = a[4] * e; + b[5] = a[5] * e; + b[6] = a[6] * e; + b[8] = a[8] * c; + b[9] = a[9] * c; + b[10] = a[10] * c; + return this + },translate: function(a) { + var b = this.elements, c = a.x, d = a.y, a = a.z; + b[12] = b[0] * c + b[4] * d + b[8] * a + b[12]; + b[13] = b[1] * c + b[5] * d + b[9] * a + b[13]; + b[14] = b[2] * c + b[6] * + d + b[10] * a + b[14]; + b[15] = b[3] * c + b[7] * d + b[11] * a + b[15]; + return this + },rotateX: function(a) { + var b = this.elements, c = b[4], d = b[5], e = b[6], f = b[7], h = b[8], i = b[9], l = b[10], k = b[11], j = Math.cos(a), a = Math.sin(a); + b[4] = j * c + a * h; + b[5] = j * d + a * i; + b[6] = j * e + a * l; + b[7] = j * f + a * k; + b[8] = j * h - a * c; + b[9] = j * i - a * d; + b[10] = j * l - a * e; + b[11] = j * k - a * f; + return this + },rotateY: function(a) { + var b = this.elements, c = b[0], d = b[1], e = b[2], f = b[3], h = b[8], i = b[9], l = b[10], k = b[11], j = Math.cos(a), a = Math.sin(a); + b[0] = j * c - a * h; + b[1] = j * d - a * i; + b[2] = j * e - a * l; + b[3] = j * f - a * k; + b[8] = j * h + a * c; + b[9] = + j * i + a * d; + b[10] = j * l + a * e; + b[11] = j * k + a * f; + return this + },rotateZ: function(a) { + var b = this.elements, c = b[0], d = b[1], e = b[2], f = b[3], h = b[4], i = b[5], l = b[6], k = b[7], j = Math.cos(a), a = Math.sin(a); + b[0] = j * c + a * h; + b[1] = j * d + a * i; + b[2] = j * e + a * l; + b[3] = j * f + a * k; + b[4] = j * h - a * c; + b[5] = j * i - a * d; + b[6] = j * l - a * e; + b[7] = j * k - a * f; + return this + },rotateByAxis: function(a, b) { + var c = this.elements; + if (a.x === 1 && a.y === 0 && a.z === 0) + return this.rotateX(b); + if (a.x === 0 && a.y === 1 && a.z === 0) + return this.rotateY(b); + if (a.x === 0 && a.y === 0 && a.z === 1) + return this.rotateZ(b); + var d = a.x, + e = a.y, f = a.z, h = Math.sqrt(d * d + e * e + f * f), d = d / h, e = e / h, f = f / h, h = d * d, i = e * e, l = f * f, k = Math.cos(b), j = Math.sin(b), m = 1 - k, n = d * e * m, o = d * f * m, m = e * f * m, d = d * j, s = e * j, j = f * j, f = h + (1 - h) * k, h = n + j, e = o - s, n = n - j, i = i + (1 - i) * k, j = m + d, o = o + s, m = m - d, l = l + (1 - l) * k, k = c[0], d = c[1], s = c[2], p = c[3], q = c[4], w = c[5], A = c[6], y = c[7], u = c[8], H = c[9], B = c[10], K = c[11]; + c[0] = f * k + h * q + e * u; + c[1] = f * d + h * w + e * H; + c[2] = f * s + h * A + e * B; + c[3] = f * p + h * y + e * K; + c[4] = n * k + i * q + j * u; + c[5] = n * d + i * w + j * H; + c[6] = n * s + i * A + j * B; + c[7] = n * p + i * y + j * K; + c[8] = o * k + m * q + l * u; + c[9] = o * d + m * w + l * H; + c[10] = o * s + m * A + l * B; + c[11] = + o * p + m * y + l * K; + return this + },scale: function(a) { + var b = this.elements, c = a.x, d = a.y, a = a.z; + b[0] = b[0] * c; + b[4] = b[4] * d; + b[8] = b[8] * a; + b[1] = b[1] * c; + b[5] = b[5] * d; + b[9] = b[9] * a; + b[2] = b[2] * c; + b[6] = b[6] * d; + b[10] = b[10] * a; + b[3] = b[3] * c; + b[7] = b[7] * d; + b[11] = b[11] * a; + return this + },getMaxScaleOnAxis: function() { + var a = this.elements; + return Math.sqrt(Math.max(a[0] * a[0] + a[1] * a[1] + a[2] * a[2], Math.max(a[4] * a[4] + a[5] * a[5] + a[6] * a[6], a[8] * a[8] + a[9] * a[9] + a[10] * a[10]))) + },makeTranslation: function(a, b, c) { + this.set(1, 0, 0, a, 0, 1, 0, b, 0, 0, 1, c, 0, 0, 0, 1); + return this + }, + makeRotationX: function(a) { + var b = Math.cos(a), a = Math.sin(a); + this.set(1, 0, 0, 0, 0, b, -a, 0, 0, a, b, 0, 0, 0, 0, 1); + return this + },makeRotationY: function(a) { + var b = Math.cos(a), a = Math.sin(a); + this.set(b, 0, a, 0, 0, 1, 0, 0, -a, 0, b, 0, 0, 0, 0, 1); + return this + },makeRotationZ: function(a) { + var b = Math.cos(a), a = Math.sin(a); + this.set(b, -a, 0, 0, a, b, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return this + },makeRotationAxis: function(a, b) { + var c = Math.cos(b), d = Math.sin(b), e = 1 - c, f = a.x, h = a.y, i = a.z, l = e * f, k = e * h; + this.set(l * f + c, l * h - d * i, l * i + d * h, 0, l * h + d * i, k * h + c, k * i - d * f, 0, l * i - + d * h, k * i + d * f, e * i * i + c, 0, 0, 0, 0, 1); + return this + },makeScale: function(a, b, c) { + this.set(a, 0, 0, 0, 0, b, 0, 0, 0, 0, c, 0, 0, 0, 0, 1); + return this + },makeFrustum: function(a, b, c, d, e, f) { + var h = this.elements; + h[0] = 2 * e / (b - a); + h[4] = 0; + h[8] = (b + a) / (b - a); + h[12] = 0; + h[1] = 0; + h[5] = 2 * e / (d - c); + h[9] = (d + c) / (d - c); + h[13] = 0; + h[2] = 0; + h[6] = 0; + h[10] = -(f + e) / (f - e); + h[14] = -2 * f * e / (f - e); + h[3] = 0; + h[7] = 0; + h[11] = -1; + h[15] = 0; + return this + },makePerspective: function(a, b, c, d) { + var a = c * Math.tan(a * Math.PI / 360), e = -a; + return this.makeFrustum(e * b, a * b, e, a, c, d) + },makeOrthographic: function(a, + b, c, d, e, f) { + var h = this.elements, i = b - a, l = c - d, k = f - e; + h[0] = 2 / i; + h[4] = 0; + h[8] = 0; + h[12] = -((b + a) / i); + h[1] = 0; + h[5] = 2 / l; + h[9] = 0; + h[13] = -((c + d) / l); + h[2] = 0; + h[6] = 0; + h[10] = -2 / k; + h[14] = -((f + e) / k); + h[3] = 0; + h[7] = 0; + h[11] = 0; + h[15] = 1; + return this + },clone: function() { + var a = this.elements; + return new THREE.Matrix4(a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7], a[11], a[15]) + }}; +THREE.Matrix4.__v1 = new THREE.Vector3; +THREE.Matrix4.__v2 = new THREE.Vector3; +THREE.Matrix4.__v3 = new THREE.Vector3; +THREE.Matrix4.__m1 = new THREE.Matrix4; +THREE.Matrix4.__m2 = new THREE.Matrix4; +THREE.Object3D = function() { + this.id = THREE.Object3DCount++; + this.name = ""; + this.parent = void 0; + this.children = []; + this.up = new THREE.Vector3(0, 1, 0); + this.position = new THREE.Vector3; + this.rotation = new THREE.Vector3; + this.eulerOrder = "XYZ"; + this.scale = new THREE.Vector3(1, 1, 1); + this.flipSided = this.doubleSided = false; + this.renderDepth = null; + this.rotationAutoUpdate = true; + this.matrix = new THREE.Matrix4; + this.matrixWorld = new THREE.Matrix4; + this.matrixRotationWorld = new THREE.Matrix4; + this.matrixWorldNeedsUpdate = this.matrixAutoUpdate = + true; + this.quaternion = new THREE.Quaternion; + this.useQuaternion = false; + this.boundRadius = 0; + this.boundRadiusScale = 1; + this.visible = true; + this.receiveShadow = this.castShadow = false; + this.frustumCulled = true; + this._vector = new THREE.Vector3 +}; +THREE.Object3D.prototype = {constructor: THREE.Object3D,applyMatrix: function(a) { + this.matrix.multiply(a, this.matrix); + this.scale.getScaleFromMatrix(this.matrix); + this.rotation.getRotationFromMatrix(this.matrix, this.scale); + this.position.getPositionFromMatrix(this.matrix) + },translate: function(a, b) { + this.matrix.rotateAxis(b); + this.position.addSelf(b.multiplyScalar(a)) + },translateX: function(a) { + this.translate(a, this._vector.set(1, 0, 0)) + },translateY: function(a) { + this.translate(a, this._vector.set(0, 1, 0)) + },translateZ: function(a) { + this.translate(a, + this._vector.set(0, 0, 1)) + },lookAt: function(a) { + this.matrix.lookAt(a, this.position, this.up); + this.rotationAutoUpdate && this.rotation.getRotationFromMatrix(this.matrix) + },add: function(a) { + if (a === this) + console.warn("THREE.Object3D.add: An object can't be added as a child of itself."); + else if (a instanceof THREE.Object3D) { + a.parent !== void 0 && a.parent.remove(a); + a.parent = this; + this.children.push(a); + for (var b = this; b.parent !== void 0; ) + b = b.parent; + b !== void 0 && b instanceof THREE.Scene && b.__addObject(a) + } + },remove: function(a) { + var b = + this.children.indexOf(a); + if (b !== -1) { + a.parent = void 0; + this.children.splice(b, 1); + for (b = this; b.parent !== void 0; ) + b = b.parent; + b !== void 0 && b instanceof THREE.Scene && b.__removeObject(a) + } + },getChildByName: function(a, b) { + var c, d, e; + c = 0; + for (d = this.children.length; c < d; c++) { + e = this.children[c]; + if (e.name === a) + return e; + if (b) { + e = e.getChildByName(a, b); + if (e !== void 0) + return e + } + } + },updateMatrix: function() { + this.matrix.setPosition(this.position); + this.useQuaternion ? this.matrix.setRotationFromQuaternion(this.quaternion) : this.matrix.setRotationFromEuler(this.rotation, + this.eulerOrder); + if (this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1) { + this.matrix.scale(this.scale); + this.boundRadiusScale = Math.max(this.scale.x, Math.max(this.scale.y, this.scale.z)) + } + this.matrixWorldNeedsUpdate = true + },updateMatrixWorld: function(a) { + this.matrixAutoUpdate && this.updateMatrix(); + if (this.matrixWorldNeedsUpdate || a) { + this.parent ? this.matrixWorld.multiply(this.parent.matrixWorld, this.matrix) : this.matrixWorld.copy(this.matrix); + this.matrixWorldNeedsUpdate = false; + a = true + } + for (var b = 0, c = this.children.length; b < + c; b++) + this.children[b].updateMatrixWorld(a) + }}; +THREE.Object3DCount = 0; +THREE.Projector = function() { + function a() { + var a = l[i] = l[i] || new THREE.RenderableVertex; + i++; + return a + } + function b(a, b) { + return b.z - a.z + } + function c(a, b) { + var c = 0, d = 1, g = a.z + a.w, e = b.z + b.w, f = -a.z + a.w, h = -b.z + b.w; + if (g >= 0 && e >= 0 && f >= 0 && h >= 0) + return true; + if (g < 0 && e < 0 || f < 0 && h < 0) + return false; + g < 0 ? c = Math.max(c, g / (g - e)) : e < 0 && (d = Math.min(d, g / (g - e))); + f < 0 ? c = Math.max(c, f / (f - h)) : h < 0 && (d = Math.min(d, f / (f - h))); + if (d < c) + return false; + a.lerpSelf(b, c); + b.lerpSelf(a, 1 - d); + return true + } + var d, e, f = [], h, i, l = [], k, j, m = [], n, o = [], s, p, q = [], w, A, y = [], + u = {objects: [],sprites: [],lights: [],elements: []}, H = new THREE.Vector3, B = new THREE.Vector4, K = new THREE.Matrix4, N = new THREE.Matrix4, Y = new THREE.Frustum, ca = new THREE.Vector4, I = new THREE.Vector4; + this.projectVector = function(a, b) { + b.matrixWorldInverse.getInverse(b.matrixWorld); + K.multiply(b.projectionMatrix, b.matrixWorldInverse); + K.multiplyVector3(a); + return a + }; + this.unprojectVector = function(a, b) { + b.projectionMatrixInverse.getInverse(b.projectionMatrix); + K.multiply(b.matrixWorld, b.projectionMatrixInverse); + K.multiplyVector3(a); + return a + }; + this.pickingRay = function(a, b) { + var c; + a.z = -1; + c = new THREE.Vector3(a.x, a.y, 1); + this.unprojectVector(a, b); + this.unprojectVector(c, b); + c.subSelf(a).normalize(); + return new THREE.Ray(a, c) + }; + this.projectGraph = function(a, c) { + e = 0; + u.objects.length = 0; + u.sprites.length = 0; + u.lights.length = 0; + var h = function(a) { + if (a.visible !== false) { + if ((a instanceof THREE.Mesh || a instanceof THREE.Line) && (a.frustumCulled === false || Y.contains(a))) { + H.copy(a.matrixWorld.getPosition()); + K.multiplyVector3(H); + var b = f[e] = f[e] || new THREE.RenderableObject; + e++; + d = b; + d.object = a; + d.z = H.z; + u.objects.push(d) + } else + a instanceof THREE.Light && u.lights.push(a); + for (var b = 0, c = a.children.length; b < c; b++) + h(a.children[b]) + } + }; + h(a); + c && u.objects.sort(b); + return u + }; + this.projectScene = function(d, e, f) { + var D = e.near, g = e.far, H = false, za, Da, $, R, J, Z, Q, pa, O, sa, Ga, Ha, La, Oa, Ta; + A = p = n = j = 0; + u.elements.length = 0; + if (e.parent === void 0) { + console.warn("DEPRECATED: Camera hasn't been added to a Scene. Adding it..."); + d.add(e) + } + d.updateMatrixWorld(); + e.matrixWorldInverse.getInverse(e.matrixWorld); + K.multiply(e.projectionMatrix, + e.matrixWorldInverse); + Y.setFromMatrix(K); + u = this.projectGraph(d, false); + d = 0; + for (za = u.objects.length; d < za; d++) { + O = u.objects[d].object; + sa = O.matrixWorld; + i = 0; + if (O instanceof THREE.Mesh) { + Ga = O.geometry; + Ha = O.geometry.materials; + R = Ga.vertices; + La = Ga.faces; + Oa = Ga.faceVertexUvs; + Ga = O.matrixRotationWorld.extractRotation(sa); + Da = 0; + for ($ = R.length; Da < $; Da++) { + h = a(); + h.positionWorld.copy(R[Da]); + sa.multiplyVector3(h.positionWorld); + h.positionScreen.copy(h.positionWorld); + K.multiplyVector4(h.positionScreen); + h.positionScreen.x = + h.positionScreen.x / h.positionScreen.w; + h.positionScreen.y = h.positionScreen.y / h.positionScreen.w; + h.visible = h.positionScreen.z > D && h.positionScreen.z < g + } + R = 0; + for (Da = La.length; R < Da; R++) { + $ = La[R]; + if ($ instanceof THREE.Face3) { + J = l[$.a]; + Z = l[$.b]; + Q = l[$.c]; + if (J.visible && Z.visible && Q.visible) { + H = (Q.positionScreen.x - J.positionScreen.x) * (Z.positionScreen.y - J.positionScreen.y) - (Q.positionScreen.y - J.positionScreen.y) * (Z.positionScreen.x - J.positionScreen.x) < 0; + if (O.doubleSided || H != O.flipSided) { + pa = m[j] = m[j] || new THREE.RenderableFace3; + j++; + k = pa; + k.v1.copy(J); + k.v2.copy(Z); + k.v3.copy(Q) + } else + continue + } else + continue + } else if ($ instanceof THREE.Face4) { + J = l[$.a]; + Z = l[$.b]; + Q = l[$.c]; + pa = l[$.d]; + if (J.visible && Z.visible && Q.visible && pa.visible) { + H = (pa.positionScreen.x - J.positionScreen.x) * (Z.positionScreen.y - J.positionScreen.y) - (pa.positionScreen.y - J.positionScreen.y) * (Z.positionScreen.x - J.positionScreen.x) < 0 || (Z.positionScreen.x - Q.positionScreen.x) * (pa.positionScreen.y - Q.positionScreen.y) - (Z.positionScreen.y - Q.positionScreen.y) * (pa.positionScreen.x - + Q.positionScreen.x) < 0; + if (O.doubleSided || H != O.flipSided) { + Ta = o[n] = o[n] || new THREE.RenderableFace4; + n++; + k = Ta; + k.v1.copy(J); + k.v2.copy(Z); + k.v3.copy(Q); + k.v4.copy(pa) + } else + continue + } else + continue + } + k.normalWorld.copy($.normal); + !H && (O.flipSided || O.doubleSided) && k.normalWorld.negate(); + Ga.multiplyVector3(k.normalWorld); + k.centroidWorld.copy($.centroid); + sa.multiplyVector3(k.centroidWorld); + k.centroidScreen.copy(k.centroidWorld); + K.multiplyVector3(k.centroidScreen); + Q = $.vertexNormals; + J = 0; + for (Z = Q.length; J < Z; J++) { + pa = k.vertexNormalsWorld[J]; + pa.copy(Q[J]); + !H && (O.flipSided || O.doubleSided) && pa.negate(); + Ga.multiplyVector3(pa) + } + J = 0; + for (Z = Oa.length; J < Z; J++) + if (Ta = Oa[J][R]) { + Q = 0; + for (pa = Ta.length; Q < pa; Q++) + k.uvs[J][Q] = Ta[Q] + } + k.material = O.material; + k.faceMaterial = $.materialIndex !== null ? Ha[$.materialIndex] : null; + k.z = k.centroidScreen.z; + u.elements.push(k) + } + } else if (O instanceof THREE.Line) { + N.multiply(K, sa); + R = O.geometry.vertices; + J = a(); + J.positionScreen.copy(R[0]); + N.multiplyVector4(J.positionScreen); + sa = O.type === THREE.LinePieces ? 2 : 1; + Da = 1; + for ($ = R.length; Da < + $; Da++) { + J = a(); + J.positionScreen.copy(R[Da]); + N.multiplyVector4(J.positionScreen); + if (!((Da + 1) % sa > 0)) { + Z = l[i - 2]; + ca.copy(J.positionScreen); + I.copy(Z.positionScreen); + if (c(ca, I)) { + ca.multiplyScalar(1 / ca.w); + I.multiplyScalar(1 / I.w); + Ha = q[p] = q[p] || new THREE.RenderableLine; + p++; + s = Ha; + s.v1.positionScreen.copy(ca); + s.v2.positionScreen.copy(I); + s.z = Math.max(ca.z, I.z); + s.material = O.material; + u.elements.push(s) + } + } + } + } + } + d = 0; + for (za = u.sprites.length; d < za; d++) { + O = u.sprites[d].object; + sa = O.matrixWorld; + if (O instanceof THREE.Particle) { + B.set(sa.elements[12], + sa.elements[13], sa.elements[14], 1); + K.multiplyVector4(B); + B.z = B.z / B.w; + if (B.z > 0 && B.z < 1) { + D = y[A] = y[A] || new THREE.RenderableParticle; + A++; + w = D; + w.x = B.x / B.w; + w.y = B.y / B.w; + w.z = B.z; + w.rotation = O.rotation.z; + w.scale.x = O.scale.x * Math.abs(w.x - (B.x + e.projectionMatrix.elements[0]) / (B.w + e.projectionMatrix.elements[12])); + w.scale.y = O.scale.y * Math.abs(w.y - (B.y + e.projectionMatrix.elements[5]) / (B.w + e.projectionMatrix.elements[13])); + w.material = O.material; + u.elements.push(w) + } + } + } + f && u.elements.sort(b); + return u + } +}; +THREE.Quaternion = function(a, b, c, d) { + this.x = a || 0; + this.y = b || 0; + this.z = c || 0; + this.w = d !== void 0 ? d : 1 +}; +THREE.Quaternion.prototype = {constructor: THREE.Quaternion,set: function(a, b, c, d) { + this.x = a; + this.y = b; + this.z = c; + this.w = d; + return this + },copy: function(a) { + this.x = a.x; + this.y = a.y; + this.z = a.z; + this.w = a.w; + return this + },setFromEuler: function(a) { + var b = Math.PI / 360, c = a.x * b, d = a.y * b, e = a.z * b, a = Math.cos(d), d = Math.sin(d), b = Math.cos(-e), e = Math.sin(-e), f = Math.cos(c), c = Math.sin(c), h = a * b, i = d * e; + this.w = h * f - i * c; + this.x = h * c + i * f; + this.y = d * b * f + a * e * c; + this.z = a * e * f - d * b * c; + return this + },setFromAxisAngle: function(a, b) { + var c = b / 2, d = Math.sin(c); + this.x = a.x * d; + this.y = a.y * d; + this.z = a.z * d; + this.w = Math.cos(c); + return this + },setFromRotationMatrix: function(a) { + var b = Math.pow(a.determinant(), 1 / 3); + this.w = Math.sqrt(Math.max(0, b + a.elements[0] + a.elements[5] + a.elements[10])) / 2; + this.x = Math.sqrt(Math.max(0, b + a.elements[0] - a.elements[5] - a.elements[10])) / 2; + this.y = Math.sqrt(Math.max(0, b - a.elements[0] + a.elements[5] - a.elements[10])) / 2; + this.z = Math.sqrt(Math.max(0, b - a.elements[0] - a.elements[5] + a.elements[10])) / 2; + this.x = a.elements[6] - a.elements[9] < 0 ? -Math.abs(this.x) : + Math.abs(this.x); + this.y = a.elements[8] - a.elements[2] < 0 ? -Math.abs(this.y) : Math.abs(this.y); + this.z = a.elements[1] - a.elements[4] < 0 ? -Math.abs(this.z) : Math.abs(this.z); + this.normalize(); + return this + },calculateW: function() { + this.w = -Math.sqrt(Math.abs(1 - this.x * this.x - this.y * this.y - this.z * this.z)); + return this + },inverse: function() { + this.x = this.x * -1; + this.y = this.y * -1; + this.z = this.z * -1; + return this + },length: function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w) + },normalize: function() { + var a = + Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + if (a === 0) + this.w = this.z = this.y = this.x = 0; + else { + a = 1 / a; + this.x = this.x * a; + this.y = this.y * a; + this.z = this.z * a; + this.w = this.w * a + } + return this + },multiply: function(a, b) { + this.x = a.x * b.w + a.y * b.z - a.z * b.y + a.w * b.x; + this.y = -a.x * b.z + a.y * b.w + a.z * b.x + a.w * b.y; + this.z = a.x * b.y - a.y * b.x + a.z * b.w + a.w * b.z; + this.w = -a.x * b.x - a.y * b.y - a.z * b.z + a.w * b.w; + return this + },multiplySelf: function(a) { + var b = this.x, c = this.y, d = this.z, e = this.w, f = a.x, h = a.y, i = a.z, a = a.w; + this.x = b * a + e * f + c * i - d * h; + this.y = + c * a + e * h + d * f - b * i; + this.z = d * a + e * i + b * h - c * f; + this.w = e * a - b * f - c * h - d * i; + return this + },multiplyVector3: function(a, b) { + b || (b = a); + var c = a.x, d = a.y, e = a.z, f = this.x, h = this.y, i = this.z, l = this.w, k = l * c + h * e - i * d, j = l * d + i * c - f * e, m = l * e + f * d - h * c, c = -f * c - h * d - i * e; + b.x = k * l + c * -f + j * -i - m * -h; + b.y = j * l + c * -h + m * -f - k * -i; + b.z = m * l + c * -i + k * -h - j * -f; + return b + },clone: function() { + return new THREE.Quaternion(this.x, this.y, this.z, this.w) + }}; +THREE.Quaternion.slerp = function(a, b, c, d) { + var e = a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z; + if (e < 0) { + c.w = -b.w; + c.x = -b.x; + c.y = -b.y; + c.z = -b.z; + e = -e + } else + c.copy(b); + if (Math.abs(e) >= 1) { + c.w = a.w; + c.x = a.x; + c.y = a.y; + c.z = a.z; + return c + } + var f = Math.acos(e), e = Math.sqrt(1 - e * e); + if (Math.abs(e) < 0.001) { + c.w = 0.5 * (a.w + b.w); + c.x = 0.5 * (a.x + b.x); + c.y = 0.5 * (a.y + b.y); + c.z = 0.5 * (a.z + b.z); + return c + } + b = Math.sin((1 - d) * f) / e; + d = Math.sin(d * f) / e; + c.w = a.w * b + c.w * d; + c.x = a.x * b + c.x * d; + c.y = a.y * b + c.y * d; + c.z = a.z * b + c.z * d; + return c +}; +THREE.Vertex = function() { + console.warn("THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.") +}; +THREE.Face3 = function(a, b, c, d, e, f) { + this.a = a; + this.b = b; + this.c = c; + this.normal = d instanceof THREE.Vector3 ? d : new THREE.Vector3; + this.vertexNormals = d instanceof Array ? d : []; + this.color = e instanceof THREE.Color ? e : new THREE.Color; + this.vertexColors = e instanceof Array ? e : []; + this.vertexTangents = []; + this.materialIndex = f; + this.centroid = new THREE.Vector3 +}; +THREE.Face3.prototype = {constructor: THREE.Face3,clone: function() { + var a = new THREE.Face3(this.a, this.b, this.c); + a.normal.copy(this.normal); + a.color.copy(this.color); + a.centroid.copy(this.centroid); + a.materialIndex = this.materialIndex; + var b, c; + b = 0; + for (c = this.vertexNormals.length; b < c; b++) + a.vertexNormals[b] = this.vertexNormals[b].clone(); + b = 0; + for (c = this.vertexColors.length; b < c; b++) + a.vertexColors[b] = this.vertexColors[b].clone(); + b = 0; + for (c = this.vertexTangents.length; b < c; b++) + a.vertexTangents[b] = this.vertexTangents[b].clone(); + return a + }}; +THREE.Face4 = function(a, b, c, d, e, f, h) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.normal = e instanceof THREE.Vector3 ? e : new THREE.Vector3; + this.vertexNormals = e instanceof Array ? e : []; + this.color = f instanceof THREE.Color ? f : new THREE.Color; + this.vertexColors = f instanceof Array ? f : []; + this.vertexTangents = []; + this.materialIndex = h; + this.centroid = new THREE.Vector3 +}; +THREE.Face4.prototype = {constructor: THREE.Face4,clone: function() { + var a = new THREE.Face4(this.a, this.b, this.c, this.d); + a.normal.copy(this.normal); + a.color.copy(this.color); + a.centroid.copy(this.centroid); + a.materialIndex = this.materialIndex; + var b, c; + b = 0; + for (c = this.vertexNormals.length; b < c; b++) + a.vertexNormals[b] = this.vertexNormals[b].clone(); + b = 0; + for (c = this.vertexColors.length; b < c; b++) + a.vertexColors[b] = this.vertexColors[b].clone(); + b = 0; + for (c = this.vertexTangents.length; b < c; b++) + a.vertexTangents[b] = this.vertexTangents[b].clone(); + return a + }}; +THREE.UV = function(a, b) { + this.u = a || 0; + this.v = b || 0 +}; +THREE.UV.prototype = {constructor: THREE.UV,set: function(a, b) { + this.u = a; + this.v = b; + return this + },copy: function(a) { + this.u = a.u; + this.v = a.v; + return this + },lerpSelf: function(a, b) { + this.u = this.u + (a.u - this.u) * b; + this.v = this.v + (a.v - this.v) * b; + return this + },clone: function() { + return new THREE.UV(this.u, this.v) + }}; +THREE.Geometry = function() { + this.id = THREE.GeometryCount++; + this.vertices = []; + this.colors = []; + this.materials = []; + this.faces = []; + this.faceUvs = [[]]; + this.faceVertexUvs = [[]]; + this.morphTargets = []; + this.morphColors = []; + this.morphNormals = []; + this.skinWeights = []; + this.skinIndices = []; + this.boundingSphere = this.boundingBox = null; + this.dynamic = this.hasTangents = false +}; +THREE.Geometry.prototype = {constructor: THREE.Geometry,applyMatrix: function(a) { + var b = new THREE.Matrix4; + b.extractRotation(a); + for (var c = 0, d = this.vertices.length; c < d; c++) + a.multiplyVector3(this.vertices[c]); + c = 0; + for (d = this.faces.length; c < d; c++) { + var e = this.faces[c]; + b.multiplyVector3(e.normal); + for (var f = 0, h = e.vertexNormals.length; f < h; f++) + b.multiplyVector3(e.vertexNormals[f]); + a.multiplyVector3(e.centroid) + } + },computeCentroids: function() { + var a, b, c; + a = 0; + for (b = this.faces.length; a < b; a++) { + c = this.faces[a]; + c.centroid.set(0, + 0, 0); + if (c instanceof THREE.Face3) { + c.centroid.addSelf(this.vertices[c.a]); + c.centroid.addSelf(this.vertices[c.b]); + c.centroid.addSelf(this.vertices[c.c]); + c.centroid.divideScalar(3) + } else if (c instanceof THREE.Face4) { + c.centroid.addSelf(this.vertices[c.a]); + c.centroid.addSelf(this.vertices[c.b]); + c.centroid.addSelf(this.vertices[c.c]); + c.centroid.addSelf(this.vertices[c.d]); + c.centroid.divideScalar(4) + } + } + },computeFaceNormals: function() { + var a, b, c, d, e, f, h = new THREE.Vector3, i = new THREE.Vector3; + a = 0; + for (b = this.faces.length; a < + b; a++) { + c = this.faces[a]; + d = this.vertices[c.a]; + e = this.vertices[c.b]; + f = this.vertices[c.c]; + h.sub(f, e); + i.sub(d, e); + h.crossSelf(i); + h.isZero() || h.normalize(); + c.normal.copy(h) + } + },computeVertexNormals: function() { + var a, b, c, d; + if (this.__tmpVertices === void 0) { + d = this.__tmpVertices = Array(this.vertices.length); + a = 0; + for (b = this.vertices.length; a < b; a++) + d[a] = new THREE.Vector3; + a = 0; + for (b = this.faces.length; a < b; a++) { + c = this.faces[a]; + if (c instanceof THREE.Face3) + c.vertexNormals = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3]; + else if (c instanceof THREE.Face4) + c.vertexNormals = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3] + } + } else { + d = this.__tmpVertices; + a = 0; + for (b = this.vertices.length; a < b; a++) + d[a].set(0, 0, 0) + } + a = 0; + for (b = this.faces.length; a < b; a++) { + c = this.faces[a]; + if (c instanceof THREE.Face3) { + d[c.a].addSelf(c.normal); + d[c.b].addSelf(c.normal); + d[c.c].addSelf(c.normal) + } else if (c instanceof THREE.Face4) { + d[c.a].addSelf(c.normal); + d[c.b].addSelf(c.normal); + d[c.c].addSelf(c.normal); + d[c.d].addSelf(c.normal) + } + } + a = 0; + for (b = this.vertices.length; a < b; a++) + d[a].normalize(); + a = 0; + for (b = this.faces.length; a < b; a++) { + c = this.faces[a]; + if (c instanceof THREE.Face3) { + c.vertexNormals[0].copy(d[c.a]); + c.vertexNormals[1].copy(d[c.b]); + c.vertexNormals[2].copy(d[c.c]) + } else if (c instanceof THREE.Face4) { + c.vertexNormals[0].copy(d[c.a]); + c.vertexNormals[1].copy(d[c.b]); + c.vertexNormals[2].copy(d[c.c]); + c.vertexNormals[3].copy(d[c.d]) + } + } + },computeMorphNormals: function() { + var a, b, c, d, e; + c = 0; + for (d = this.faces.length; c < d; c++) { + e = this.faces[c]; + e.__originalFaceNormal ? + e.__originalFaceNormal.copy(e.normal) : e.__originalFaceNormal = e.normal.clone(); + if (!e.__originalVertexNormals) + e.__originalVertexNormals = []; + a = 0; + for (b = e.vertexNormals.length; a < b; a++) + e.__originalVertexNormals[a] ? e.__originalVertexNormals[a].copy(e.vertexNormals[a]) : e.__originalVertexNormals[a] = e.vertexNormals[a].clone() + } + var f = new THREE.Geometry; + f.faces = this.faces; + a = 0; + for (b = this.morphTargets.length; a < b; a++) { + if (!this.morphNormals[a]) { + this.morphNormals[a] = {}; + this.morphNormals[a].faceNormals = []; + this.morphNormals[a].vertexNormals = + []; + var h = this.morphNormals[a].faceNormals, i = this.morphNormals[a].vertexNormals, l, k; + c = 0; + for (d = this.faces.length; c < d; c++) { + e = this.faces[c]; + l = new THREE.Vector3; + k = e instanceof THREE.Face3 ? {a: new THREE.Vector3,b: new THREE.Vector3,c: new THREE.Vector3} : {a: new THREE.Vector3,b: new THREE.Vector3,c: new THREE.Vector3,d: new THREE.Vector3}; + h.push(l); + i.push(k) + } + } + h = this.morphNormals[a]; + f.vertices = this.morphTargets[a].vertices; + f.computeFaceNormals(); + f.computeVertexNormals(); + c = 0; + for (d = this.faces.length; c < d; c++) { + e = this.faces[c]; + l = h.faceNormals[c]; + k = h.vertexNormals[c]; + l.copy(e.normal); + if (e instanceof THREE.Face3) { + k.a.copy(e.vertexNormals[0]); + k.b.copy(e.vertexNormals[1]); + k.c.copy(e.vertexNormals[2]) + } else { + k.a.copy(e.vertexNormals[0]); + k.b.copy(e.vertexNormals[1]); + k.c.copy(e.vertexNormals[2]); + k.d.copy(e.vertexNormals[3]) + } + } + } + c = 0; + for (d = this.faces.length; c < d; c++) { + e = this.faces[c]; + e.normal = e.__originalFaceNormal; + e.vertexNormals = e.__originalVertexNormals + } + },computeTangents: function() { + function a(a, b, c, d, g, e, f) { + i = a.vertices[b]; + l = a.vertices[c]; + k = a.vertices[d]; + j = h[g]; + m = h[e]; + n = h[f]; + o = l.x - i.x; + s = k.x - i.x; + p = l.y - i.y; + q = k.y - i.y; + w = l.z - i.z; + A = k.z - i.z; + y = m.u - j.u; + u = n.u - j.u; + H = m.v - j.v; + B = n.v - j.v; + K = 1 / (y * B - u * H); + I.set((B * o - H * s) * K, (B * p - H * q) * K, (B * w - H * A) * K); + ba.set((y * s - u * o) * K, (y * q - u * p) * K, (y * A - u * w) * K); + Y[b].addSelf(I); + Y[c].addSelf(I); + Y[d].addSelf(I); + ca[b].addSelf(ba); + ca[c].addSelf(ba); + ca[d].addSelf(ba) + } + var b, c, d, e, f, h, i, l, k, j, m, n, o, s, p, q, w, A, y, u, H, B, K, N, Y = [], ca = [], I = new THREE.Vector3, ba = new THREE.Vector3, ja = new THREE.Vector3, ya = new THREE.Vector3, D = new THREE.Vector3; + b = 0; + for (c = this.vertices.length; b < c; b++) { + Y[b] = new THREE.Vector3; + ca[b] = new THREE.Vector3 + } + b = 0; + for (c = this.faces.length; b < c; b++) { + f = this.faces[b]; + h = this.faceVertexUvs[0][b]; + if (f instanceof THREE.Face3) + a(this, f.a, f.b, f.c, 0, 1, 2); + else if (f instanceof THREE.Face4) { + a(this, f.a, f.b, f.d, 0, 1, 3); + a(this, f.b, f.c, f.d, 1, 2, 3) + } + } + var g = ["a", "b", "c", "d"]; + b = 0; + for (c = this.faces.length; b < c; b++) { + f = this.faces[b]; + for (d = 0; d < f.vertexNormals.length; d++) { + D.copy(f.vertexNormals[d]); + e = f[g[d]]; + N = Y[e]; + ja.copy(N); + ja.subSelf(D.multiplyScalar(D.dot(N))).normalize(); + ya.cross(f.vertexNormals[d], N); + e = ya.dot(ca[e]); + e = e < 0 ? -1 : 1; + f.vertexTangents[d] = new THREE.Vector4(ja.x, ja.y, ja.z, e) + } + } + this.hasTangents = true + },computeBoundingBox: function() { + if (!this.boundingBox) + this.boundingBox = {min: new THREE.Vector3,max: new THREE.Vector3}; + if (this.vertices.length > 0) { + var a; + a = this.vertices[0]; + this.boundingBox.min.copy(a); + this.boundingBox.max.copy(a); + for (var b = this.boundingBox.min, c = this.boundingBox.max, d = 1, e = this.vertices.length; d < e; d++) { + a = this.vertices[d]; + if (a.x < b.x) + b.x = a.x; + else if (a.x > + c.x) + c.x = a.x; + if (a.y < b.y) + b.y = a.y; + else if (a.y > c.y) + c.y = a.y; + if (a.z < b.z) + b.z = a.z; + else if (a.z > c.z) + c.z = a.z + } + } else { + this.boundingBox.min.set(0, 0, 0); + this.boundingBox.max.set(0, 0, 0) + } + },computeBoundingSphere: function() { + if (!this.boundingSphere) + this.boundingSphere = {radius: 0}; + for (var a, b = 0, c = 0, d = this.vertices.length; c < d; c++) { + a = this.vertices[c].length(); + a > b && (b = a) + } + this.boundingSphere.radius = b + },mergeVertices: function() { + var a = {}, b = [], c = [], d, e = Math.pow(10, 4), f, h, i; + f = 0; + for (h = this.vertices.length; f < h; f++) { + d = this.vertices[f]; + d = [Math.round(d.x * e), Math.round(d.y * e), Math.round(d.z * e)].join("_"); + if (a[d] === void 0) { + a[d] = f; + b.push(this.vertices[f]); + c[f] = b.length - 1 + } else + c[f] = c[a[d]] + } + f = 0; + for (h = this.faces.length; f < h; f++) { + e = this.faces[f]; + if (e instanceof THREE.Face3) { + e.a = c[e.a]; + e.b = c[e.b]; + e.c = c[e.c] + } else if (e instanceof THREE.Face4) { + e.a = c[e.a]; + e.b = c[e.b]; + e.c = c[e.c]; + e.d = c[e.d]; + d = [e.a, e.b, e.c, e.d]; + for (a = 3; a > 0; a--) + if (d.indexOf(e["abcd"[a]]) != a) { + d.splice(a, 1); + this.faces[f] = new THREE.Face3(d[0], d[1], d[2]); + e = 0; + for (d = this.faceVertexUvs.length; e < + d; e++) + (i = this.faceVertexUvs[e][f]) && i.splice(a, 1); + break + } + } + } + c = this.vertices.length - b.length; + this.vertices = b; + return c + }}; +THREE.GeometryCount = 0; +THREE.Spline = function(a) { + function b(a, b, c, d, e, f, h) { + a = (c - a) * 0.5; + d = (d - b) * 0.5; + return (2 * (b - c) + a + d) * h + (-3 * (b - c) - 2 * a - d) * f + a * e + b + } + this.points = a; + var c = [], d = {x: 0,y: 0,z: 0}, e, f, h, i, l, k, j, m, n; + this.initFromArray = function(a) { + this.points = []; + for (var b = 0; b < a.length; b++) + this.points[b] = {x: a[b][0],y: a[b][1],z: a[b][2]} + }; + this.getPoint = function(a) { + e = (this.points.length - 1) * a; + f = Math.floor(e); + h = e - f; + c[0] = f === 0 ? f : f - 1; + c[1] = f; + c[2] = f > this.points.length - 2 ? this.points.length - 1 : f + 1; + c[3] = f > this.points.length - 3 ? this.points.length - 1 : + f + 2; + k = this.points[c[0]]; + j = this.points[c[1]]; + m = this.points[c[2]]; + n = this.points[c[3]]; + i = h * h; + l = h * i; + d.x = b(k.x, j.x, m.x, n.x, h, i, l); + d.y = b(k.y, j.y, m.y, n.y, h, i, l); + d.z = b(k.z, j.z, m.z, n.z, h, i, l); + return d + }; + this.getControlPointsArray = function() { + var a, b, c = this.points.length, d = []; + for (a = 0; a < c; a++) { + b = this.points[a]; + d[a] = [b.x, b.y, b.z] + } + return d + }; + this.getLength = function(a) { + var b, c, d, e = b = b = 0, f = new THREE.Vector3, h = new THREE.Vector3, i = [], k = 0; + i[0] = 0; + a || (a = 100); + c = this.points.length * a; + f.copy(this.points[0]); + for (a = 1; a < c; a++) { + b = + a / c; + d = this.getPoint(b); + h.copy(d); + k = k + h.distanceTo(f); + f.copy(d); + b = (this.points.length - 1) * b; + b = Math.floor(b); + if (b != e) { + i[b] = k; + e = b + } + } + i[i.length] = k; + return {chunks: i,total: k} + }; + this.reparametrizeByArcLength = function(a) { + var b, c, d, e, f, h, i = [], k = new THREE.Vector3, j = this.getLength(); + i.push(k.copy(this.points[0]).clone()); + for (b = 1; b < this.points.length; b++) { + c = j.chunks[b] - j.chunks[b - 1]; + h = Math.ceil(a * c / j.total); + e = (b - 1) / (this.points.length - 1); + f = b / (this.points.length - 1); + for (c = 1; c < h - 1; c++) { + d = e + c * (1 / h) * (f - e); + d = this.getPoint(d); + i.push(k.copy(d).clone()) + } + i.push(k.copy(this.points[b]).clone()) + } + this.points = i + } +}; +THREE.Camera = function() { + THREE.Object3D.call(this); + this.matrixWorldInverse = new THREE.Matrix4; + this.projectionMatrix = new THREE.Matrix4; + this.projectionMatrixInverse = new THREE.Matrix4 +}; +THREE.Camera.prototype = new THREE.Object3D; +THREE.Camera.prototype.constructor = THREE.Camera; +THREE.Camera.prototype.lookAt = function(a) { + this.matrix.lookAt(this.position, a, this.up); + this.rotationAutoUpdate && this.rotation.getRotationFromMatrix(this.matrix) +}; +THREE.OrthographicCamera = function(a, b, c, d, e, f) { + THREE.Camera.call(this); + this.left = a; + this.right = b; + this.top = c; + this.bottom = d; + this.near = e !== void 0 ? e : 0.1; + this.far = f !== void 0 ? f : 2E3; + this.updateProjectionMatrix() +}; +THREE.OrthographicCamera.prototype = new THREE.Camera; +THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function() { + this.projectionMatrix.makeOrthographic(this.left, this.right, this.top, this.bottom, this.near, this.far) +}; +THREE.PerspectiveCamera = function(a, b, c, d) { + THREE.Camera.call(this); + this.fov = a !== void 0 ? a : 50; + this.aspect = b !== void 0 ? b : 1; + this.near = c !== void 0 ? c : 0.1; + this.far = d !== void 0 ? d : 2E3; + this.updateProjectionMatrix() +}; +THREE.PerspectiveCamera.prototype = new THREE.Camera; +THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; +THREE.PerspectiveCamera.prototype.setLens = function(a, b) { + this.fov = 2 * Math.atan((b !== void 0 ? b : 24) / (a * 2)) * (180 / Math.PI); + this.updateProjectionMatrix() +}; +THREE.PerspectiveCamera.prototype.setViewOffset = function(a, b, c, d, e, f) { + this.fullWidth = a; + this.fullHeight = b; + this.x = c; + this.y = d; + this.width = e; + this.height = f; + this.updateProjectionMatrix() +}; +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function() { + if (this.fullWidth) { + var a = this.fullWidth / this.fullHeight, b = Math.tan(this.fov * Math.PI / 360) * this.near, c = -b, d = a * c, a = Math.abs(a * b - d), c = Math.abs(b - c); + this.projectionMatrix.makeFrustum(d + this.x * a / this.fullWidth, d + (this.x + this.width) * a / this.fullWidth, b - (this.y + this.height) * c / this.fullHeight, b - this.y * c / this.fullHeight, this.near, this.far) + } else + this.projectionMatrix.makePerspective(this.fov, this.aspect, this.near, this.far) +}; +THREE.Light = function(a) { + THREE.Object3D.call(this); + this.color = new THREE.Color(a) +}; +THREE.Light.prototype = new THREE.Object3D; +THREE.Light.prototype.constructor = THREE.Light; +THREE.Light.prototype.supr = THREE.Object3D.prototype; +THREE.AmbientLight = function(a) { + THREE.Light.call(this, a) +}; +THREE.AmbientLight.prototype = new THREE.Light; +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; +THREE.DirectionalLight = function(a, b, c) { + THREE.Light.call(this, a); + this.position = new THREE.Vector3(0, 1, 0); + this.target = new THREE.Object3D; + this.intensity = b !== void 0 ? b : 1; + this.distance = c !== void 0 ? c : 0; + this.onlyShadow = this.castShadow = false; + this.shadowCameraNear = 50; + this.shadowCameraFar = 5E3; + this.shadowCameraLeft = -500; + this.shadowCameraTop = this.shadowCameraRight = 500; + this.shadowCameraBottom = -500; + this.shadowCameraVisible = false; + this.shadowBias = 0; + this.shadowDarkness = 0.5; + this.shadowMapHeight = this.shadowMapWidth = 512; + this.shadowCascade = false; + this.shadowCascadeOffset = new THREE.Vector3(0, 0, -1E3); + this.shadowCascadeCount = 2; + this.shadowCascadeBias = [0, 0, 0]; + this.shadowCascadeWidth = [512, 512, 512]; + this.shadowCascadeHeight = [512, 512, 512]; + this.shadowCascadeNearZ = [-1, 0.99, 0.998]; + this.shadowCascadeFarZ = [0.99, 0.998, 1]; + this.shadowCascadeArray = []; + this.shadowMatrix = this.shadowCamera = this.shadowMapSize = this.shadowMap = null +}; +THREE.DirectionalLight.prototype = new THREE.Light; +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; +THREE.PointLight = function(a, b, c) { + THREE.Light.call(this, a); + this.position = new THREE.Vector3(0, 0, 0); + this.intensity = b !== void 0 ? b : 1; + this.distance = c !== void 0 ? c : 0 +}; +THREE.PointLight.prototype = new THREE.Light; +THREE.PointLight.prototype.constructor = THREE.PointLight; +THREE.SpotLight = function(a, b, c, d, e) { + THREE.Light.call(this, a); + this.position = new THREE.Vector3(0, 1, 0); + this.target = new THREE.Object3D; + this.intensity = b !== void 0 ? b : 1; + this.distance = c !== void 0 ? c : 0; + this.angle = d !== void 0 ? d : Math.PI / 2; + this.exponent = e !== void 0 ? e : 10; + this.onlyShadow = this.castShadow = false; + this.shadowCameraNear = 50; + this.shadowCameraFar = 5E3; + this.shadowCameraFov = 50; + this.shadowCameraVisible = false; + this.shadowBias = 0; + this.shadowDarkness = 0.5; + this.shadowMapHeight = this.shadowMapWidth = 512; + this.shadowMatrix = + this.shadowCamera = this.shadowMapSize = this.shadowMap = null +}; +THREE.SpotLight.prototype = new THREE.Light; +THREE.SpotLight.prototype.constructor = THREE.SpotLight; +THREE.Material = function(a) { + a = a || {}; + this.id = THREE.MaterialCount++; + this.name = ""; + this.opacity = a.opacity !== void 0 ? a.opacity : 1; + this.transparent = a.transparent !== void 0 ? a.transparent : false; + this.blending = a.blending !== void 0 ? a.blending : THREE.NormalBlending; + this.blendSrc = a.blendSrc !== void 0 ? a.blendSrc : THREE.SrcAlphaFactor; + this.blendDst = a.blendDst !== void 0 ? a.blendDst : THREE.OneMinusSrcAlphaFactor; + this.blendEquation = a.blendEquation !== void 0 ? a.blendEquation : THREE.AddEquation; + this.depthTest = a.depthTest !== void 0 ? + a.depthTest : true; + this.depthWrite = a.depthWrite !== void 0 ? a.depthWrite : true; + this.polygonOffset = a.polygonOffset !== void 0 ? a.polygonOffset : false; + this.polygonOffsetFactor = a.polygonOffsetFactor !== void 0 ? a.polygonOffsetFactor : 0; + this.polygonOffsetUnits = a.polygonOffsetUnits !== void 0 ? a.polygonOffsetUnits : 0; + this.alphaTest = a.alphaTest !== void 0 ? a.alphaTest : 0; + this.overdraw = a.overdraw !== void 0 ? a.overdraw : false; + this.needsUpdate = this.visible = true +}; +THREE.MaterialCount = 0; +THREE.NoShading = 0; +THREE.FlatShading = 1; +THREE.SmoothShading = 2; +THREE.NoColors = 0; +THREE.FaceColors = 1; +THREE.VertexColors = 2; +THREE.NoBlending = 0; +THREE.NormalBlending = 1; +THREE.AdditiveBlending = 2; +THREE.SubtractiveBlending = 3; +THREE.MultiplyBlending = 4; +THREE.AdditiveAlphaBlending = 5; +THREE.CustomBlending = 6; +THREE.AddEquation = 100; +THREE.SubtractEquation = 101; +THREE.ReverseSubtractEquation = 102; +THREE.ZeroFactor = 200; +THREE.OneFactor = 201; +THREE.SrcColorFactor = 202; +THREE.OneMinusSrcColorFactor = 203; +THREE.SrcAlphaFactor = 204; +THREE.OneMinusSrcAlphaFactor = 205; +THREE.DstAlphaFactor = 206; +THREE.OneMinusDstAlphaFactor = 207; +THREE.DstColorFactor = 208; +THREE.OneMinusDstColorFactor = 209; +THREE.SrcAlphaSaturateFactor = 210; +THREE.LineBasicMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.linewidth = a.linewidth !== void 0 ? a.linewidth : 1; + this.linecap = a.linecap !== void 0 ? a.linecap : "round"; + this.linejoin = a.linejoin !== void 0 ? a.linejoin : "round"; + this.vertexColors = a.vertexColors ? a.vertexColors : false; + this.fog = a.fog !== void 0 ? a.fog : true +}; +THREE.LineBasicMaterial.prototype = new THREE.Material; +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; +THREE.MeshBasicMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.map = a.map !== void 0 ? a.map : null; + this.lightMap = a.lightMap !== void 0 ? a.lightMap : null; + this.envMap = a.envMap !== void 0 ? a.envMap : null; + this.combine = a.combine !== void 0 ? a.combine : THREE.MultiplyOperation; + this.reflectivity = a.reflectivity !== void 0 ? a.reflectivity : 1; + this.refractionRatio = a.refractionRatio !== void 0 ? a.refractionRatio : 0.98; + this.fog = a.fog !== void 0 ? a.fog : + true; + this.shading = a.shading !== void 0 ? a.shading : THREE.SmoothShading; + this.wireframe = a.wireframe !== void 0 ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth !== void 0 ? a.wireframeLinewidth : 1; + this.wireframeLinecap = a.wireframeLinecap !== void 0 ? a.wireframeLinecap : "round"; + this.wireframeLinejoin = a.wireframeLinejoin !== void 0 ? a.wireframeLinejoin : "round"; + this.vertexColors = a.vertexColors !== void 0 ? a.vertexColors : THREE.NoColors; + this.skinning = a.skinning !== void 0 ? a.skinning : false; + this.morphTargets = a.morphTargets !== + void 0 ? a.morphTargets : false +}; +THREE.MeshBasicMaterial.prototype = new THREE.Material; +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; +THREE.MeshLambertMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.ambient = a.ambient !== void 0 ? new THREE.Color(a.ambient) : new THREE.Color(16777215); + this.emissive = a.emissive !== void 0 ? new THREE.Color(a.emissive) : new THREE.Color(0); + this.wrapAround = a.wrapAround !== void 0 ? a.wrapAround : false; + this.wrapRGB = new THREE.Vector3(1, 1, 1); + this.map = a.map !== void 0 ? a.map : null; + this.lightMap = a.lightMap !== void 0 ? a.lightMap : null; + this.envMap = + a.envMap !== void 0 ? a.envMap : null; + this.combine = a.combine !== void 0 ? a.combine : THREE.MultiplyOperation; + this.reflectivity = a.reflectivity !== void 0 ? a.reflectivity : 1; + this.refractionRatio = a.refractionRatio !== void 0 ? a.refractionRatio : 0.98; + this.fog = a.fog !== void 0 ? a.fog : true; + this.shading = a.shading !== void 0 ? a.shading : THREE.SmoothShading; + this.wireframe = a.wireframe !== void 0 ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth !== void 0 ? a.wireframeLinewidth : 1; + this.wireframeLinecap = a.wireframeLinecap !== void 0 ? + a.wireframeLinecap : "round"; + this.wireframeLinejoin = a.wireframeLinejoin !== void 0 ? a.wireframeLinejoin : "round"; + this.vertexColors = a.vertexColors !== void 0 ? a.vertexColors : THREE.NoColors; + this.skinning = a.skinning !== void 0 ? a.skinning : false; + this.morphTargets = a.morphTargets !== void 0 ? a.morphTargets : false; + this.morphNormals = a.morphNormals !== void 0 ? a.morphNormals : false +}; +THREE.MeshLambertMaterial.prototype = new THREE.Material; +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; +THREE.MeshPhongMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.ambient = a.ambient !== void 0 ? new THREE.Color(a.ambient) : new THREE.Color(16777215); + this.emissive = a.emissive !== void 0 ? new THREE.Color(a.emissive) : new THREE.Color(0); + this.specular = a.specular !== void 0 ? new THREE.Color(a.specular) : new THREE.Color(1118481); + this.shininess = a.shininess !== void 0 ? a.shininess : 30; + this.metal = a.metal !== void 0 ? a.metal : false; + this.perPixel = + a.perPixel !== void 0 ? a.perPixel : false; + this.wrapAround = a.wrapAround !== void 0 ? a.wrapAround : false; + this.wrapRGB = new THREE.Vector3(1, 1, 1); + this.map = a.map !== void 0 ? a.map : null; + this.lightMap = a.lightMap !== void 0 ? a.lightMap : null; + this.envMap = a.envMap !== void 0 ? a.envMap : null; + this.combine = a.combine !== void 0 ? a.combine : THREE.MultiplyOperation; + this.reflectivity = a.reflectivity !== void 0 ? a.reflectivity : 1; + this.refractionRatio = a.refractionRatio !== void 0 ? a.refractionRatio : 0.98; + this.fog = a.fog !== void 0 ? a.fog : true; + this.shading = + a.shading !== void 0 ? a.shading : THREE.SmoothShading; + this.wireframe = a.wireframe !== void 0 ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth !== void 0 ? a.wireframeLinewidth : 1; + this.wireframeLinecap = a.wireframeLinecap !== void 0 ? a.wireframeLinecap : "round"; + this.wireframeLinejoin = a.wireframeLinejoin !== void 0 ? a.wireframeLinejoin : "round"; + this.vertexColors = a.vertexColors !== void 0 ? a.vertexColors : THREE.NoColors; + this.skinning = a.skinning !== void 0 ? a.skinning : false; + this.morphTargets = a.morphTargets !== void 0 ? + a.morphTargets : false; + this.morphNormals = a.morphNormals !== void 0 ? a.morphNormals : false +}; +THREE.MeshPhongMaterial.prototype = new THREE.Material; +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; +THREE.MeshDepthMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.shading = a.shading !== void 0 ? a.shading : THREE.SmoothShading; + this.wireframe = a.wireframe !== void 0 ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth !== void 0 ? a.wireframeLinewidth : 1 +}; +THREE.MeshDepthMaterial.prototype = new THREE.Material; +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; +THREE.MeshNormalMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.shading = a.shading ? a.shading : THREE.FlatShading; + this.wireframe = a.wireframe ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth ? a.wireframeLinewidth : 1 +}; +THREE.MeshNormalMaterial.prototype = new THREE.Material; +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; +THREE.MeshFaceMaterial = function() { +}; +THREE.ParticleBasicMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.map = a.map !== void 0 ? a.map : null; + this.size = a.size !== void 0 ? a.size : 1; + this.sizeAttenuation = a.sizeAttenuation !== void 0 ? a.sizeAttenuation : true; + this.vertexColors = a.vertexColors !== void 0 ? a.vertexColors : false; + this.fog = a.fog !== void 0 ? a.fog : true +}; +THREE.ParticleBasicMaterial.prototype = new THREE.Material; +THREE.ParticleBasicMaterial.prototype.constructor = THREE.ParticleBasicMaterial; +THREE.ShaderMaterial = function(a) { + THREE.Material.call(this, a); + a = a || {}; + this.fragmentShader = a.fragmentShader !== void 0 ? a.fragmentShader : "void main() {}"; + this.vertexShader = a.vertexShader !== void 0 ? a.vertexShader : "void main() {}"; + this.uniforms = a.uniforms !== void 0 ? a.uniforms : {}; + this.attributes = a.attributes; + this.shading = a.shading !== void 0 ? a.shading : THREE.SmoothShading; + this.wireframe = a.wireframe !== void 0 ? a.wireframe : false; + this.wireframeLinewidth = a.wireframeLinewidth !== void 0 ? a.wireframeLinewidth : 1; + this.fog = + a.fog !== void 0 ? a.fog : false; + this.lights = a.lights !== void 0 ? a.lights : false; + this.vertexColors = a.vertexColors !== void 0 ? a.vertexColors : THREE.NoColors; + this.skinning = a.skinning !== void 0 ? a.skinning : false; + this.morphTargets = a.morphTargets !== void 0 ? a.morphTargets : false; + this.morphNormals = a.morphNormals !== void 0 ? a.morphNormals : false +}; +THREE.ShaderMaterial.prototype = new THREE.Material; +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; +THREE.Texture = function(a, b, c, d, e, f, h, i) { + this.id = THREE.TextureCount++; + this.image = a; + this.mapping = b !== void 0 ? b : new THREE.UVMapping; + this.wrapS = c !== void 0 ? c : THREE.ClampToEdgeWrapping; + this.wrapT = d !== void 0 ? d : THREE.ClampToEdgeWrapping; + this.magFilter = e !== void 0 ? e : THREE.LinearFilter; + this.minFilter = f !== void 0 ? f : THREE.LinearMipMapLinearFilter; + this.format = h !== void 0 ? h : THREE.RGBAFormat; + this.type = i !== void 0 ? i : THREE.UnsignedByteType; + this.offset = new THREE.Vector2(0, 0); + this.repeat = new THREE.Vector2(1, 1); + this.generateMipmaps = + true; + this.needsUpdate = this.premultiplyAlpha = false; + this.onUpdate = null +}; +THREE.Texture.prototype = {constructor: THREE.Texture,clone: function() { + var a = new THREE.Texture(this.image, this.mapping, this.wrapS, this.wrapT, this.magFilter, this.minFilter, this.format, this.type); + a.offset.copy(this.offset); + a.repeat.copy(this.repeat); + return a + }}; +THREE.TextureCount = 0; +THREE.MultiplyOperation = 0; +THREE.MixOperation = 1; +THREE.UVMapping = function() { +}; +THREE.CubeReflectionMapping = function() { +}; +THREE.CubeRefractionMapping = function() { +}; +THREE.SphericalReflectionMapping = function() { +}; +THREE.SphericalRefractionMapping = function() { +}; +THREE.RepeatWrapping = 0; +THREE.ClampToEdgeWrapping = 1; +THREE.MirroredRepeatWrapping = 2; +THREE.NearestFilter = 3; +THREE.NearestMipMapNearestFilter = 4; +THREE.NearestMipMapLinearFilter = 5; +THREE.LinearFilter = 6; +THREE.LinearMipMapNearestFilter = 7; +THREE.LinearMipMapLinearFilter = 8; +THREE.ByteType = 9; +THREE.UnsignedByteType = 10; +THREE.ShortType = 11; +THREE.UnsignedShortType = 12; +THREE.IntType = 13; +THREE.UnsignedIntType = 14; +THREE.FloatType = 15; +THREE.AlphaFormat = 16; +THREE.RGBFormat = 17; +THREE.RGBAFormat = 18; +THREE.LuminanceFormat = 19; +THREE.LuminanceAlphaFormat = 20; +THREE.DataTexture = function(a, b, c, d, e, f, h, i, l, k) { + THREE.Texture.call(this, null, f, h, i, l, k, d, e); + this.image = {data: a,width: b,height: c} +}; +THREE.DataTexture.prototype = new THREE.Texture; +THREE.DataTexture.prototype.constructor = THREE.DataTexture; +THREE.DataTexture.prototype.clone = function() { + var a = new THREE.DataTexture(this.image.data, this.image.width, this.image.height, this.format, this.type, this.mapping, this.wrapS, this.wrapT, this.magFilter, this.minFilter); + a.offset.copy(this.offset); + a.repeat.copy(this.repeat); + return a +}; +THREE.Particle = function(a) { + THREE.Object3D.call(this); + this.material = a +}; +THREE.Particle.prototype = new THREE.Object3D; +THREE.Particle.prototype.constructor = THREE.Particle; +THREE.ParticleSystem = function(a, b) { + THREE.Object3D.call(this); + this.geometry = a; + this.material = b !== void 0 ? b : new THREE.ParticleBasicMaterial({color: Math.random() * 16777215}); + this.sortParticles = false; + if (this.geometry) { + this.geometry.boundingSphere || this.geometry.computeBoundingSphere(); + this.boundRadius = a.boundingSphere.radius + } + this.frustumCulled = false +}; +THREE.ParticleSystem.prototype = new THREE.Object3D; +THREE.ParticleSystem.prototype.constructor = THREE.ParticleSystem; +THREE.Line = function(a, b, c) { + THREE.Object3D.call(this); + this.geometry = a; + this.material = b !== void 0 ? b : new THREE.LineBasicMaterial({color: Math.random() * 16777215}); + this.type = c !== void 0 ? c : THREE.LineStrip; + this.geometry && (this.geometry.boundingSphere || this.geometry.computeBoundingSphere()) +}; +THREE.LineStrip = 0; +THREE.LinePieces = 1; +THREE.Line.prototype = new THREE.Object3D; +THREE.Line.prototype.constructor = THREE.Line; +THREE.Mesh = function(a, b) { + THREE.Object3D.call(this); + this.geometry = a; + this.material = b !== void 0 ? b : new THREE.MeshBasicMaterial({color: Math.random() * 16777215,wireframe: true}); + if (this.geometry) { + this.geometry.boundingSphere || this.geometry.computeBoundingSphere(); + this.boundRadius = a.boundingSphere.radius; + if (this.geometry.morphTargets.length) { + this.morphTargetBase = -1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + for (var c = 0; c < this.geometry.morphTargets.length; c++) { + this.morphTargetInfluences.push(0); + this.morphTargetDictionary[this.geometry.morphTargets[c].name] = c + } + } + } +}; +THREE.Mesh.prototype = new THREE.Object3D; +THREE.Mesh.prototype.constructor = THREE.Mesh; +THREE.Mesh.prototype.supr = THREE.Object3D.prototype; +THREE.Mesh.prototype.getMorphTargetIndexByName = function(a) { + if (this.morphTargetDictionary[a] !== void 0) + return this.morphTargetDictionary[a]; + console.log("THREE.Mesh.getMorphTargetIndexByName: morph target " + a + " does not exist. Returning 0."); + return 0 +}; +THREE.Ribbon = function(a, b) { + THREE.Object3D.call(this); + this.geometry = a; + this.material = b +}; +THREE.Ribbon.prototype = new THREE.Object3D; +THREE.Ribbon.prototype.constructor = THREE.Ribbon; +THREE.LOD = function() { + THREE.Object3D.call(this); + this.LODs = [] +}; +THREE.LOD.prototype = new THREE.Object3D; +THREE.LOD.prototype.constructor = THREE.LOD; +THREE.LOD.prototype.supr = THREE.Object3D.prototype; +THREE.LOD.prototype.addLevel = function(a, b) { + b === void 0 && (b = 0); + for (var b = Math.abs(b), c = 0; c < this.LODs.length; c++) + if (b < this.LODs[c].visibleAtDistance) + break; + this.LODs.splice(c, 0, {visibleAtDistance: b,object3D: a}); + this.add(a) +}; +THREE.LOD.prototype.update = function(a) { + if (this.LODs.length > 1) { + a.matrixWorldInverse.getInverse(a.matrixWorld); + a = a.matrixWorldInverse; + a = -(a.elements[2] * this.matrixWorld.elements[12] + a.elements[6] * this.matrixWorld.elements[13] + a.elements[10] * this.matrixWorld.elements[14] + a.elements[14]); + this.LODs[0].object3D.visible = true; + for (var b = 1; b < this.LODs.length; b++) + if (a >= this.LODs[b].visibleAtDistance) { + this.LODs[b - 1].object3D.visible = false; + this.LODs[b].object3D.visible = true + } else + break; + for (; b < this.LODs.length; b++) + this.LODs[b].object3D.visible = + false + } +}; +THREE.Sprite = function(a) { + THREE.Object3D.call(this); + this.color = a.color !== void 0 ? new THREE.Color(a.color) : new THREE.Color(16777215); + this.map = a.map !== void 0 ? a.map : new THREE.Texture; + this.blending = a.blending !== void 0 ? a.blending : THREE.NormalBlending; + this.blendSrc = a.blendSrc !== void 0 ? a.blendSrc : THREE.SrcAlphaFactor; + this.blendDst = a.blendDst !== void 0 ? a.blendDst : THREE.OneMinusSrcAlphaFactor; + this.blendEquation = a.blendEquation !== void 0 ? a.blendEquation : THREE.AddEquation; + this.useScreenCoordinates = a.useScreenCoordinates !== void 0 ? + a.useScreenCoordinates : true; + this.mergeWith3D = a.mergeWith3D !== void 0 ? a.mergeWith3D : !this.useScreenCoordinates; + this.affectedByDistance = a.affectedByDistance !== void 0 ? a.affectedByDistance : !this.useScreenCoordinates; + this.scaleByViewport = a.scaleByViewport !== void 0 ? a.scaleByViewport : !this.affectedByDistance; + this.alignment = a.alignment instanceof THREE.Vector2 ? a.alignment : THREE.SpriteAlignment.center; + this.rotation3d = this.rotation; + this.rotation = 0; + this.opacity = 1; + this.uvOffset = new THREE.Vector2(0, 0); + this.uvScale = + new THREE.Vector2(1, 1) +}; +THREE.Sprite.prototype = new THREE.Object3D; +THREE.Sprite.prototype.constructor = THREE.Sprite; +THREE.Sprite.prototype.updateMatrix = function() { + this.matrix.setPosition(this.position); + this.rotation3d.set(0, 0, this.rotation); + this.matrix.setRotationFromEuler(this.rotation3d); + if (this.scale.x !== 1 || this.scale.y !== 1) { + this.matrix.scale(this.scale); + this.boundRadiusScale = Math.max(this.scale.x, this.scale.y) + } + this.matrixWorldNeedsUpdate = true +}; +THREE.SpriteAlignment = {}; +THREE.SpriteAlignment.topLeft = new THREE.Vector2(1, -1); +THREE.SpriteAlignment.topCenter = new THREE.Vector2(0, -1); +THREE.SpriteAlignment.topRight = new THREE.Vector2(-1, -1); +THREE.SpriteAlignment.centerLeft = new THREE.Vector2(1, 0); +THREE.SpriteAlignment.center = new THREE.Vector2(0, 0); +THREE.SpriteAlignment.centerRight = new THREE.Vector2(-1, 0); +THREE.SpriteAlignment.bottomLeft = new THREE.Vector2(1, 1); +THREE.SpriteAlignment.bottomCenter = new THREE.Vector2(0, 1); +THREE.SpriteAlignment.bottomRight = new THREE.Vector2(-1, 1); +THREE.Scene = function() { + THREE.Object3D.call(this); + this.overrideMaterial = this.fog = null; + this.matrixAutoUpdate = false; + this.__objects = []; + this.__lights = []; + this.__objectsAdded = []; + this.__objectsRemoved = [] +}; +THREE.Scene.prototype = new THREE.Object3D; +THREE.Scene.prototype.constructor = THREE.Scene; +THREE.Scene.prototype.__addObject = function(a) { + if (a instanceof THREE.Light) + this.__lights.indexOf(a) === -1 && this.__lights.push(a); + else if (!(a instanceof THREE.Camera) && this.__objects.indexOf(a) === -1) { + this.__objects.push(a); + this.__objectsAdded.push(a); + var b = this.__objectsRemoved.indexOf(a); + b !== -1 && this.__objectsRemoved.splice(b, 1) + } + for (b = 0; b < a.children.length; b++) + this.__addObject(a.children[b]) +}; +THREE.Scene.prototype.__removeObject = function(a) { + if (a instanceof THREE.Light) { + var b = this.__lights.indexOf(a); + b !== -1 && this.__lights.splice(b, 1) + } else if (!(a instanceof THREE.Camera)) { + b = this.__objects.indexOf(a); + if (b !== -1) { + this.__objects.splice(b, 1); + this.__objectsRemoved.push(a); + b = this.__objectsAdded.indexOf(a); + b !== -1 && this.__objectsAdded.splice(b, 1) + } + } + for (b = 0; b < a.children.length; b++) + this.__removeObject(a.children[b]) +}; +THREE.Fog = function(a, b, c) { + this.color = new THREE.Color(a); + this.near = b !== void 0 ? b : 1; + this.far = c !== void 0 ? c : 1E3 +}; +THREE.FogExp2 = function(a, b) { + this.color = new THREE.Color(a); + this.density = b !== void 0 ? b : 2.5E-4 +}; +THREE.ShaderChunk = {fog_pars_fragment: "#ifdef USE_FOG\nuniform vec3 fogColor;\n#ifdef FOG_EXP2\nuniform float fogDensity;\n#else\nuniform float fogNear;\nuniform float fogFar;\n#endif\n#endif",fog_fragment: "#ifdef USE_FOG\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n#ifdef FOG_EXP2\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n#else\nfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n#endif\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n#endif",envmap_pars_fragment: "#ifdef USE_ENVMAP\nvarying vec3 vReflect;\nuniform float reflectivity;\nuniform samplerCube envMap;\nuniform float flipEnvMap;\nuniform int combine;\n#endif", + envmap_fragment: "#ifdef USE_ENVMAP\n#ifdef DOUBLE_SIDED\nfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\nvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * vReflect.x, vReflect.yz ) );\n#else\nvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * vReflect.x, vReflect.yz ) );\n#endif\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\nif ( combine == 1 ) {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, reflectivity );\n} else {\ngl_FragColor.xyz = gl_FragColor.xyz * cubeColor.xyz;\n}\n#endif", + envmap_pars_vertex: "#ifdef USE_ENVMAP\nvarying vec3 vReflect;\nuniform float refractionRatio;\nuniform bool useRefract;\n#endif",envmap_vertex: "#ifdef USE_ENVMAP\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\nvec3 nWorld = mat3( objectMatrix[ 0 ].xyz, objectMatrix[ 1 ].xyz, objectMatrix[ 2 ].xyz ) * normal;\nif ( useRefract ) {\nvReflect = refract( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ), refractionRatio );\n} else {\nvReflect = reflect( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ) );\n}\n#endif", + map_particle_pars_fragment: "#ifdef USE_MAP\nuniform sampler2D map;\n#endif",map_particle_fragment: "#ifdef USE_MAP\ngl_FragColor = gl_FragColor * texture2D( map, gl_PointCoord );\n#endif",map_pars_vertex: "#ifdef USE_MAP\nvarying vec2 vUv;\nuniform vec4 offsetRepeat;\n#endif",map_pars_fragment: "#ifdef USE_MAP\nvarying vec2 vUv;\nuniform sampler2D map;\n#endif",map_vertex: "#ifdef USE_MAP\nvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif",map_fragment: "#ifdef USE_MAP\n#ifdef GAMMA_INPUT\nvec4 texelColor = texture2D( map, vUv );\ntexelColor.xyz *= texelColor.xyz;\ngl_FragColor = gl_FragColor * texelColor;\n#else\ngl_FragColor = gl_FragColor * texture2D( map, vUv );\n#endif\n#endif", + lightmap_pars_fragment: "#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\nuniform sampler2D lightMap;\n#endif",lightmap_pars_vertex: "#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\n#endif",lightmap_fragment: "#ifdef USE_LIGHTMAP\ngl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n#endif",lightmap_vertex: "#ifdef USE_LIGHTMAP\nvUv2 = uv2;\n#endif",lights_lambert_pars_vertex: "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngle[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif", + lights_lambert_vertex: "vLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\nvLightBack = vec3( 0.0 );\n#endif\ntransformedNormal = normalize( transformedNormal );\n#if MAX_DIR_LIGHTS > 0\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, dirVector );\nvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\ndirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\ndirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n#ifdef DOUBLE_SIDED\nvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n#endif\n}\n#endif\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\npointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\npointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n#ifdef DOUBLE_SIDED\nvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nlVector = normalize( lVector );\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - mPosition.xyz ) );\nif ( spotEffect > spotLightAngle[ i ] ) {\nspotEffect = pow( spotEffect, spotLightExponent[ i ] );\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\nspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\nspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n#ifdef DOUBLE_SIDED\nvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n#endif\n}\n}\n#endif\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n#ifdef DOUBLE_SIDED\nvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n#endif", + lights_phong_pars_vertex: "#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvarying vec3 vWorldPosition;\n#endif",lights_phong_vertex: "#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nvPointLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nvSpotLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvWorldPosition = mPosition.xyz;\n#endif", + lights_phong_pars_fragment: "uniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#else\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngle[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#else\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\nvarying vec3 vWorldPosition;\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;", + lights_phong_fragment: "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#ifdef DOUBLE_SIDED\nnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n#endif\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vPointLight[ i ].xyz );\nfloat lDistance = vPointLight[ i ].w;\n#endif\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n#endif\npointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\nvec3 pointHalfVector = normalize( lVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = max( pow( pointDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n#else\npointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vSpotLight[ i ].xyz );\nfloat lDistance = vSpotLight[ i ].w;\n#endif\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngle[ i ] ) {\nspotEffect = pow( spotEffect, spotLightExponent[ i ] );\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n#endif\nspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\nvec3 spotHalfVector = normalize( lVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = max( pow( spotDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, dirVector );\n#ifdef WRAP_AROUND\nfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n#endif\ndirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = max( pow( dirDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n#endif", + color_pars_fragment: "#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_fragment: "#ifdef USE_COLOR\ngl_FragColor = gl_FragColor * vec4( vColor, opacity );\n#endif",color_pars_vertex: "#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_vertex: "#ifdef USE_COLOR\n#ifdef GAMMA_INPUT\nvColor = color * color;\n#else\nvColor = color;\n#endif\n#endif",skinning_pars_vertex: "#ifdef USE_SKINNING\nuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n#endif",skinning_vertex: "#ifdef USE_SKINNING\ngl_Position = ( boneGlobalMatrices[ int( skinIndex.x ) ] * skinVertexA ) * skinWeight.x;\ngl_Position += ( boneGlobalMatrices[ int( skinIndex.y ) ] * skinVertexB ) * skinWeight.y;\ngl_Position = projectionMatrix * modelViewMatrix * gl_Position;\n#endif", + morphtarget_pars_vertex: "#ifdef USE_MORPHTARGETS\n#ifndef USE_MORPHNORMALS\nuniform float morphTargetInfluences[ 8 ];\n#else\nuniform float morphTargetInfluences[ 4 ];\n#endif\n#endif",morphtarget_vertex: "#ifdef USE_MORPHTARGETS\nvec3 morphed = vec3( 0.0 );\nmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\nmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\nmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\nmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n#ifndef USE_MORPHNORMALS\nmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\nmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\nmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\nmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n#endif\nmorphed += position;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( morphed, 1.0 );\n#endif", + default_vertex: "#ifndef USE_MORPHTARGETS\n#ifndef USE_SKINNING\ngl_Position = projectionMatrix * mvPosition;\n#endif\n#endif",morphnormal_vertex: "#ifdef USE_MORPHNORMALS\nvec3 morphedNormal = vec3( 0.0 );\nmorphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\nmorphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\nmorphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\nmorphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\nmorphedNormal += normal;\nvec3 transformedNormal = normalMatrix * morphedNormal;\n#else\nvec3 transformedNormal = normalMatrix * normal;\n#endif", + shadowmap_pars_fragment: "#ifdef USE_SHADOWMAP\nuniform sampler2D shadowMap[ MAX_SHADOWS ];\nuniform vec2 shadowMapSize[ MAX_SHADOWS ];\nuniform float shadowDarkness[ MAX_SHADOWS ];\nuniform float shadowBias[ MAX_SHADOWS ];\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nfloat unpackDepth( const in vec4 rgba_depth ) {\nconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\nfloat depth = dot( rgba_depth, bit_shift );\nreturn depth;\n}\n#endif",shadowmap_fragment: "#ifdef USE_SHADOWMAP\n#ifdef SHADOWMAP_DEBUG\nvec3 frustumColors[3];\nfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\nfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\nfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n#endif\n#ifdef SHADOWMAP_CASCADE\nint inFrustumCount = 0;\n#endif\nfloat fDepth;\nvec3 shadowColor = vec3( 1.0 );\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\nbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\nbool inFrustum = all( inFrustumVec );\n#ifdef SHADOWMAP_CASCADE\ninFrustumCount += int( inFrustum );\nbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n#else\nbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n#endif\nbool frustumTest = all( frustumTestVec );\nif ( frustumTest ) {\nshadowCoord.z += shadowBias[ i ];\n#ifdef SHADOWMAP_SOFT\nfloat shadow = 0.0;\nconst float shadowDelta = 1.0 / 9.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.25 * xPixelOffset;\nfloat dy0 = -1.25 * yPixelOffset;\nfloat dx1 = 1.25 * xPixelOffset;\nfloat dy1 = 1.25 * yPixelOffset;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#else\nvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\nfloat fDepth = unpackDepth( rgbaDepth );\nif ( fDepth < shadowCoord.z )\nshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n#endif\n}\n#ifdef SHADOWMAP_DEBUG\n#ifdef SHADOWMAP_CASCADE\nif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n#else\nif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n#endif\n#endif\n}\n#ifdef GAMMA_OUTPUT\nshadowColor *= shadowColor;\n#endif\ngl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n#endif", + shadowmap_pars_vertex: "#ifdef USE_SHADOWMAP\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n#endif",shadowmap_vertex: "#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n#ifdef USE_MORPHTARGETS\nvShadowCoord[ i ] = shadowMatrix[ i ] * objectMatrix * vec4( morphed, 1.0 );\n#else\nvShadowCoord[ i ] = shadowMatrix[ i ] * objectMatrix * vec4( position, 1.0 );\n#endif\n}\n#endif",alphatest_fragment: "#ifdef ALPHATEST\nif ( gl_FragColor.a < ALPHATEST ) discard;\n#endif", + linear_to_gamma_fragment: "#ifdef GAMMA_OUTPUT\ngl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n#endif"}; +THREE.UniformsUtils = {merge: function(a) { + var b, c, d, e = {}; + for (b = 0; b < a.length; b++) { + d = this.clone(a[b]); + for (c in d) + e[c] = d[c] + } + return e + },clone: function(a) { + var b, c, d, e = {}; + for (b in a) { + e[b] = {}; + for (c in a[b]) { + d = a[b][c]; + e[b][c] = d instanceof THREE.Color || d instanceof THREE.Vector2 || d instanceof THREE.Vector3 || d instanceof THREE.Vector4 || d instanceof THREE.Matrix4 || d instanceof THREE.Texture ? d.clone() : d instanceof Array ? d.slice() : d + } + } + return e + }}; +THREE.UniformsLib = {common: {diffuse: {type: "c",value: new THREE.Color(15658734)},opacity: {type: "f",value: 1},map: {type: "t",value: 0,texture: null},offsetRepeat: {type: "v4",value: new THREE.Vector4(0, 0, 1, 1)},lightMap: {type: "t",value: 2,texture: null},envMap: {type: "t",value: 1,texture: null},flipEnvMap: {type: "f",value: -1},useRefract: {type: "i",value: 0},reflectivity: {type: "f",value: 1},refractionRatio: {type: "f",value: 0.98},combine: {type: "i",value: 0},morphTargetInfluences: {type: "f",value: 0}},fog: {fogDensity: {type: "f", + value: 2.5E-4},fogNear: {type: "f",value: 1},fogFar: {type: "f",value: 2E3},fogColor: {type: "c",value: new THREE.Color(16777215)}},lights: {ambientLightColor: {type: "fv",value: []},directionalLightDirection: {type: "fv",value: []},directionalLightColor: {type: "fv",value: []},pointLightColor: {type: "fv",value: []},pointLightPosition: {type: "fv",value: []},pointLightDistance: {type: "fv1",value: []},spotLightColor: {type: "fv",value: []},spotLightPosition: {type: "fv",value: []},spotLightDirection: {type: "fv",value: []},spotLightDistance: {type: "fv1", + value: []},spotLightAngle: {type: "fv1",value: []},spotLightExponent: {type: "fv1",value: []}},particle: {psColor: {type: "c",value: new THREE.Color(15658734)},opacity: {type: "f",value: 1},size: {type: "f",value: 1},scale: {type: "f",value: 1},map: {type: "t",value: 0,texture: null},fogDensity: {type: "f",value: 2.5E-4},fogNear: {type: "f",value: 1},fogFar: {type: "f",value: 2E3},fogColor: {type: "c",value: new THREE.Color(16777215)}},shadowmap: {shadowMap: {type: "tv",value: 6,texture: []},shadowMapSize: {type: "v2v",value: []},shadowBias: {type: "fv1", + value: []},shadowDarkness: {type: "fv1",value: []},shadowMatrix: {type: "m4v",value: []}}}; +THREE.ShaderLib = {depth: {uniforms: {mNear: {type: "f",value: 1},mFar: {type: "f",value: 2E3},opacity: {type: "f",value: 1}},vertexShader: "void main() {\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader: "uniform float mNear;\nuniform float mFar;\nuniform float opacity;\nvoid main() {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat color = 1.0 - smoothstep( mNear, mFar, depth );\ngl_FragColor = vec4( vec3( color ), opacity );\n}"},normal: {uniforms: {opacity: {type: "f", + value: 1}},vertexShader: "varying vec3 vNormal;\nvoid main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvNormal = normalMatrix * normal;\ngl_Position = projectionMatrix * mvPosition;\n}",fragmentShader: "uniform float opacity;\nvarying vec3 vNormal;\nvoid main() {\ngl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );\n}"},basic: {uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.shadowmap]),vertexShader: [THREE.ShaderChunk.map_pars_vertex, + THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", THREE.ShaderChunk.map_vertex, THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.morphtarget_vertex, + THREE.ShaderChunk.default_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),fragmentShader: ["uniform vec3 diffuse;\nuniform float opacity;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, "void main() {\ngl_FragColor = vec4( diffuse, opacity );", THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, + THREE.ShaderChunk.lightmap_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")},lambert: {uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {ambient: {type: "c",value: new THREE.Color(16777215)},emissive: {type: "c",value: new THREE.Color(0)},wrapRGB: {type: "v3",value: new THREE.Vector3(1, + 1, 1)}}]),vertexShader: ["varying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif", THREE.ShaderChunk.map_pars_vertex, THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.lights_lambert_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", THREE.ShaderChunk.map_vertex, + THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.morphnormal_vertex, "#ifndef USE_ENVMAP\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\n#endif", THREE.ShaderChunk.lights_lambert_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.default_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),fragmentShader: ["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif", + THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );", THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, "#ifdef DOUBLE_SIDED\nif ( gl_FrontFacing )\ngl_FragColor.xyz *= vLightFront;\nelse\ngl_FragColor.xyz *= vLightBack;\n#else\ngl_FragColor.xyz *= vLightFront;\n#endif", + THREE.ShaderChunk.lightmap_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")},phong: {uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {ambient: {type: "c",value: new THREE.Color(16777215)},emissive: {type: "c",value: new THREE.Color(0)},specular: {type: "c",value: new THREE.Color(1118481)}, + shininess: {type: "f",value: 30},wrapRGB: {type: "v3",value: new THREE.Vector3(1, 1, 1)}}]),vertexShader: ["varying vec3 vViewPosition;\nvarying vec3 vNormal;", THREE.ShaderChunk.map_pars_vertex, THREE.ShaderChunk.lightmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.lights_phong_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + THREE.ShaderChunk.map_vertex, THREE.ShaderChunk.lightmap_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.color_vertex, "#ifndef USE_ENVMAP\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\n#endif\nvViewPosition = -mvPosition.xyz;", THREE.ShaderChunk.morphnormal_vertex, "vNormal = transformedNormal;", THREE.ShaderChunk.lights_phong_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.default_vertex, THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"), + fragmentShader: ["uniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;", THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );", + THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lightmap_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.envmap_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")},particle_basic: {uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.particle, THREE.UniformsLib.shadowmap]),vertexShader: ["uniform float size;\nuniform float scale;", + THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {", THREE.ShaderChunk.color_vertex, "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n#ifdef USE_SIZEATTENUATION\ngl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n#else\ngl_PointSize = size;\n#endif\ngl_Position = projectionMatrix * mvPosition;", THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n"),fragmentShader: ["uniform vec3 psColor;\nuniform float opacity;", THREE.ShaderChunk.color_pars_fragment, + THREE.ShaderChunk.map_particle_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, "void main() {\ngl_FragColor = vec4( psColor, opacity );", THREE.ShaderChunk.map_particle_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n")},depthRGBA: {uniforms: {},vertexShader: [THREE.ShaderChunk.morphtarget_pars_vertex, "void main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.default_vertex, "}"].join("\n"),fragmentShader: "vec4 pack_depth( const in float depth ) {\nconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\nconst vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\nvec4 res = fract( depth * bit_shift );\nres -= res.xxyz * bit_mask;\nreturn res;\n}\nvoid main() {\ngl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n}"}}; +THREE.WebGLRenderer = function(a) { + function b(a, b) { + var c = a.vertices.length, d = b.material; + if (d.attributes) { + if (a.__webglCustomAttributesList === void 0) + a.__webglCustomAttributesList = []; + for (var e in d.attributes) { + var f = d.attributes[e]; + if (!f.__webglInitialized || f.createUniqueBuffers) { + f.__webglInitialized = true; + var h = 1; + f.type === "v2" ? h = 2 : f.type === "v3" ? h = 3 : f.type === "v4" ? h = 4 : f.type === "c" && (h = 3); + f.size = h; + f.array = new Float32Array(c * h); + f.buffer = g.createBuffer(); + f.buffer.belongsToAttribute = e; + f.needsUpdate = true + } + a.__webglCustomAttributesList.push(f) + } + } + } + function c(a, b) { + if (a.material && !(a.material instanceof THREE.MeshFaceMaterial)) + return a.material; + if (b.materialIndex >= 0) + return a.geometry.materials[b.materialIndex] + } + function d(a) { + return a instanceof THREE.MeshBasicMaterial && !a.envMap || a instanceof THREE.MeshDepthMaterial ? false : a && a.shading !== void 0 && a.shading === THREE.SmoothShading ? THREE.SmoothShading : THREE.FlatShading + } + function e(a) { + return a.map || a.lightMap || a instanceof THREE.ShaderMaterial ? true : false + } + function f(a, b, c) { + var d, e, f, h, i = a.vertices; + h = i.length; + var k = a.colors, j = k.length, l = a.__vertexArray, n = a.__colorArray, o = a.__sortArray, m = a.verticesNeedUpdate, p = a.colorsNeedUpdate, s = a.__webglCustomAttributesList; + if (c.sortParticles) { + Ub.copy(qb); + Ub.multiplySelf(c.matrixWorld); + for (d = 0; d < h; d++) { + e = i[d]; + Ra.copy(e); + Ub.multiplyVector3(Ra); + o[d] = [Ra.z, d] + } + o.sort(function(a, b) { + return b[0] - a[0] + }); + for (d = 0; d < h; d++) { + e = i[o[d][1]]; + f = d * 3; + l[f] = e.x; + l[f + 1] = e.y; + l[f + 2] = e.z + } + for (d = 0; d < j; d++) { + f = d * 3; + e = k[o[d][1]]; + n[f] = e.r; + n[f + 1] = e.g; + n[f + 2] = e.b + } + if (s) { + k = 0; + for (j = s.length; k < j; k++) { + i = s[k]; + if (i.boundTo === void 0 || i.boundTo === "vertices") { + f = 0; + e = i.value.length; + if (i.size === 1) + for (d = 0; d < e; d++) { + h = o[d][1]; + i.array[d] = i.value[h] + } + else if (i.size === 2) + for (d = 0; d < e; d++) { + h = o[d][1]; + h = i.value[h]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + f = f + 2 + } + else if (i.size === 3) + if (i.type === "c") + for (d = 0; d < e; d++) { + h = o[d][1]; + h = i.value[h]; + i.array[f] = h.r; + i.array[f + 1] = h.g; + i.array[f + 2] = h.b; + f = f + 3 + } + else + for (d = 0; d < e; d++) { + h = o[d][1]; + h = i.value[h]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + i.array[f + 2] = h.z; + f = f + 3 + } + else if (i.size === 4) + for (d = 0; d < e; d++) { + h = o[d][1]; + h = i.value[h]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + i.array[f + 2] = h.z; + i.array[f + 3] = h.w; + f = f + 4 + } + } + } + } + } else { + if (m) + for (d = 0; d < h; d++) { + e = i[d]; + f = d * 3; + l[f] = e.x; + l[f + 1] = e.y; + l[f + 2] = e.z + } + if (p) + for (d = 0; d < j; d++) { + e = k[d]; + f = d * 3; + n[f] = e.r; + n[f + 1] = e.g; + n[f + 2] = e.b + } + if (s) { + k = 0; + for (j = s.length; k < j; k++) { + i = s[k]; + if (i.needsUpdate && (i.boundTo === void 0 || i.boundTo === "vertices")) { + e = i.value.length; + f = 0; + if (i.size === 1) + for (d = 0; d < e; d++) + i.array[d] = i.value[d]; + else if (i.size === 2) + for (d = 0; d < e; d++) { + h = i.value[d]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + f = f + 2 + } + else if (i.size === + 3) + if (i.type === "c") + for (d = 0; d < e; d++) { + h = i.value[d]; + i.array[f] = h.r; + i.array[f + 1] = h.g; + i.array[f + 2] = h.b; + f = f + 3 + } + else + for (d = 0; d < e; d++) { + h = i.value[d]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + i.array[f + 2] = h.z; + f = f + 3 + } + else if (i.size === 4) + for (d = 0; d < e; d++) { + h = i.value[d]; + i.array[f] = h.x; + i.array[f + 1] = h.y; + i.array[f + 2] = h.z; + i.array[f + 3] = h.w; + f = f + 4 + } + } + } + } + } + if (m || c.sortParticles) { + g.bindBuffer(g.ARRAY_BUFFER, a.__webglVertexBuffer); + g.bufferData(g.ARRAY_BUFFER, l, b) + } + if (p || c.sortParticles) { + g.bindBuffer(g.ARRAY_BUFFER, a.__webglColorBuffer); + g.bufferData(g.ARRAY_BUFFER, + n, b) + } + if (s) { + k = 0; + for (j = s.length; k < j; k++) { + i = s[k]; + if (i.needsUpdate || c.sortParticles) { + g.bindBuffer(g.ARRAY_BUFFER, i.buffer); + g.bufferData(g.ARRAY_BUFFER, i.array, b) + } + } + } + } + function h(a, b) { + return b.z - a.z + } + function i(a, b, c) { + if (a.length) + for (var d = 0, e = a.length; d < e; d++) { + J = za = null; + $ = R = pa = Q = Oa = La = O = -1; + rb = true; + a[d].render(b, c, jc, kc); + J = za = null; + $ = R = pa = Q = Oa = La = O = -1; + rb = true + } + } + function l(a, b, c, d, e, g, f, h) { + var i, k, j, l; + if (b) { + k = a.length - 1; + l = b = -1 + } else { + k = 0; + b = a.length; + l = 1 + } + for (var n = k; n !== b; n = n + l) { + i = a[n]; + if (i.render) { + k = i.object; + j = + i.buffer; + if (h) + i = h; + else { + i = i[c]; + if (!i) + continue; + f && D.setBlending(i.blending, i.blendEquation, i.blendSrc, i.blendDst); + D.setDepthTest(i.depthTest); + D.setDepthWrite(i.depthWrite); + p(i.polygonOffset, i.polygonOffsetFactor, i.polygonOffsetUnits) + } + D.setObjectFaces(k); + j instanceof THREE.BufferGeometry ? D.renderBufferDirect(d, e, g, i, j, k) : D.renderBuffer(d, e, g, i, j, k) + } + } + } + function k(a, b, c, d, e, g, f) { + for (var h, i, k = 0, j = a.length; k < j; k++) { + h = a[k]; + i = h.object; + if (i.visible) { + if (f) + h = f; + else { + h = h[b]; + if (!h) + continue; + g && D.setBlending(h.blending, + h.blendEquation, h.blendSrc, h.blendDst); + D.setDepthTest(h.depthTest); + D.setDepthWrite(h.depthWrite); + p(h.polygonOffset, h.polygonOffsetFactor, h.polygonOffsetUnits) + } + D.renderImmediateObject(c, d, e, h, i) + } + } + } + function j(a, b, c) { + a.push({buffer: b,object: c,opaque: null,transparent: null}) + } + function m(a) { + for (var b in a.attributes) + if (a.attributes[b].needsUpdate) + return true; + return false + } + function n(a) { + for (var b in a.attributes) + a.attributes[b].needsUpdate = false + } + function o(a, b, c, d, e) { + if (!d.program || d.needsUpdate) { + D.initMaterial(d, + b, c, e); + d.needsUpdate = false + } + if (d.morphTargets && !e.__webglMorphTargetInfluences) { + e.__webglMorphTargetInfluences = new Float32Array(D.maxMorphTargets); + for (var f = 0, h = D.maxMorphTargets; f < h; f++) + e.__webglMorphTargetInfluences[f] = 0 + } + var i = false, f = d.program, h = f.uniforms, k = d.uniforms; + if (f !== za) { + g.useProgram(f); + za = f; + i = true + } + if (d.id !== $) { + $ = d.id; + i = true + } + if (i || a !== J) { + g.uniformMatrix4fv(h.projectionMatrix, false, a._projectionMatrixArray); + a !== J && (J = a) + } + if (i) { + if (c && d.fog) { + k.fogColor.value = c.color; + if (c instanceof THREE.Fog) { + k.fogNear.value = + c.near; + k.fogFar.value = c.far + } else if (c instanceof THREE.FogExp2) + k.fogDensity.value = c.density + } + if (d instanceof THREE.MeshPhongMaterial || d instanceof THREE.MeshLambertMaterial || d.lights) { + if (rb) { + for (var j, l = 0, n = 0, o = 0, m, p, s, q = lc, y = q.directional.colors, A = q.directional.positions, B = q.point.colors, K = q.point.positions, N = q.point.distances, Y = q.spot.colors, O = q.spot.positions, ba = q.spot.distances, ja = q.spot.directions, ca = q.spot.angles, Q = q.spot.exponents, Z = 0, I = 0, R = 0, la = s = 0, c = la = 0, i = b.length; c < i; c++) { + j = b[c]; + if (!j.onlyShadow) { + m = + j.color; + p = j.intensity; + s = j.distance; + if (j instanceof THREE.AmbientLight) + if (D.gammaInput) { + l = l + m.r * m.r; + n = n + m.g * m.g; + o = o + m.b * m.b + } else { + l = l + m.r; + n = n + m.g; + o = o + m.b + } + else if (j instanceof THREE.DirectionalLight) { + s = Z * 3; + if (D.gammaInput) { + y[s] = m.r * m.r * p * p; + y[s + 1] = m.g * m.g * p * p; + y[s + 2] = m.b * m.b * p * p + } else { + y[s] = m.r * p; + y[s + 1] = m.g * p; + y[s + 2] = m.b * p + } + Ca.copy(j.matrixWorld.getPosition()); + Ca.subSelf(j.target.matrixWorld.getPosition()); + Ca.normalize(); + A[s] = Ca.x; + A[s + 1] = Ca.y; + A[s + 2] = Ca.z; + Z = Z + 1 + } else if (j instanceof THREE.PointLight) { + la = I * 3; + if (D.gammaInput) { + B[la] = m.r * m.r * p * p; + B[la + 1] = m.g * m.g * p * p; + B[la + 2] = m.b * m.b * p * p + } else { + B[la] = m.r * p; + B[la + 1] = m.g * p; + B[la + 2] = m.b * p + } + m = j.matrixWorld.getPosition(); + K[la] = m.x; + K[la + 1] = m.y; + K[la + 2] = m.z; + N[I] = s; + I = I + 1 + } else if (j instanceof THREE.SpotLight) { + la = R * 3; + if (D.gammaInput) { + Y[la] = m.r * m.r * p * p; + Y[la + 1] = m.g * m.g * p * p; + Y[la + 2] = m.b * m.b * p * p + } else { + Y[la] = m.r * p; + Y[la + 1] = m.g * p; + Y[la + 2] = m.b * p + } + m = j.matrixWorld.getPosition(); + O[la] = m.x; + O[la + 1] = m.y; + O[la + 2] = m.z; + ba[R] = s; + Ca.copy(m); + Ca.subSelf(j.target.matrixWorld.getPosition()); + Ca.normalize(); + ja[la] = Ca.x; + ja[la + 1] = Ca.y; + ja[la + 2] = Ca.z; + ca[R] = Math.cos(j.angle); + Q[R] = j.exponent; + R = R + 1 + } + } + } + c = Z * 3; + for (i = y.length; c < i; c++) + y[c] = 0; + c = I * 3; + for (i = B.length; c < i; c++) + B[c] = 0; + c = R * 3; + for (i = Y.length; c < i; c++) + Y[c] = 0; + q.directional.length = Z; + q.point.length = I; + q.spot.length = R; + q.ambient[0] = l; + q.ambient[1] = n; + q.ambient[2] = o; + rb = false + } + c = lc; + k.ambientLightColor.value = c.ambient; + k.directionalLightColor.value = c.directional.colors; + k.directionalLightDirection.value = c.directional.positions; + k.pointLightColor.value = c.point.colors; + k.pointLightPosition.value = + c.point.positions; + k.pointLightDistance.value = c.point.distances; + k.spotLightColor.value = c.spot.colors; + k.spotLightPosition.value = c.spot.positions; + k.spotLightDistance.value = c.spot.distances; + k.spotLightDirection.value = c.spot.directions; + k.spotLightAngle.value = c.spot.angles; + k.spotLightExponent.value = c.spot.exponents + } + if (d instanceof THREE.MeshBasicMaterial || d instanceof THREE.MeshLambertMaterial || d instanceof THREE.MeshPhongMaterial) { + k.opacity.value = d.opacity; + D.gammaInput ? k.diffuse.value.copyGammaToLinear(d.color) : + k.diffuse.value = d.color; + (k.map.texture = d.map) && k.offsetRepeat.value.set(d.map.offset.x, d.map.offset.y, d.map.repeat.x, d.map.repeat.y); + k.lightMap.texture = d.lightMap; + k.envMap.texture = d.envMap; + k.flipEnvMap.value = d.envMap instanceof THREE.WebGLRenderTargetCube ? 1 : -1; + k.reflectivity.value = d.reflectivity; + k.refractionRatio.value = d.refractionRatio; + k.combine.value = d.combine; + k.useRefract.value = d.envMap && d.envMap.mapping instanceof THREE.CubeRefractionMapping + } + if (d instanceof THREE.LineBasicMaterial) { + k.diffuse.value = + d.color; + k.opacity.value = d.opacity + } else if (d instanceof THREE.ParticleBasicMaterial) { + k.psColor.value = d.color; + k.opacity.value = d.opacity; + k.size.value = d.size; + k.scale.value = H.height / 2; + k.map.texture = d.map + } else if (d instanceof THREE.MeshPhongMaterial) { + k.shininess.value = d.shininess; + if (D.gammaInput) { + k.ambient.value.copyGammaToLinear(d.ambient); + k.emissive.value.copyGammaToLinear(d.emissive); + k.specular.value.copyGammaToLinear(d.specular) + } else { + k.ambient.value = d.ambient; + k.emissive.value = d.emissive; + k.specular.value = + d.specular + } + d.wrapAround && k.wrapRGB.value.copy(d.wrapRGB) + } else if (d instanceof THREE.MeshLambertMaterial) { + if (D.gammaInput) { + k.ambient.value.copyGammaToLinear(d.ambient); + k.emissive.value.copyGammaToLinear(d.emissive) + } else { + k.ambient.value = d.ambient; + k.emissive.value = d.emissive + } + d.wrapAround && k.wrapRGB.value.copy(d.wrapRGB) + } else if (d instanceof THREE.MeshDepthMaterial) { + k.mNear.value = a.near; + k.mFar.value = a.far; + k.opacity.value = d.opacity + } else if (d instanceof THREE.MeshNormalMaterial) + k.opacity.value = d.opacity; + if (e.receiveShadow && !d._shadowPass && k.shadowMatrix) { + i = c = 0; + for (j = b.length; i < j; i++) { + l = b[i]; + if (l.castShadow && (l instanceof THREE.SpotLight || l instanceof THREE.DirectionalLight && !l.shadowCascade)) { + k.shadowMap.texture[c] = l.shadowMap; + k.shadowMapSize.value[c] = l.shadowMapSize; + k.shadowMatrix.value[c] = l.shadowMatrix; + k.shadowDarkness.value[c] = l.shadowDarkness; + k.shadowBias.value[c] = l.shadowBias; + c++ + } + } + } + b = d.uniformsList; + k = 0; + for (c = b.length; k < c; k++) + if (l = f.uniforms[b[k][1]]) { + i = b[k][0]; + n = i.type; + j = i.value; + switch (n) { + case "i": + g.uniform1i(l, + j); + break; + case "f": + g.uniform1f(l, j); + break; + case "v2": + g.uniform2f(l, j.x, j.y); + break; + case "v3": + g.uniform3f(l, j.x, j.y, j.z); + break; + case "v4": + g.uniform4f(l, j.x, j.y, j.z, j.w); + break; + case "c": + g.uniform3f(l, j.r, j.g, j.b); + break; + case "fv1": + g.uniform1fv(l, j); + break; + case "fv": + g.uniform3fv(l, j); + break; + case "v2v": + if (!i._array) + i._array = new Float32Array(2 * j.length); + n = 0; + for (o = j.length; n < o; n++) { + q = n * 2; + i._array[q] = j[n].x; + i._array[q + 1] = j[n].y + } + g.uniform2fv(l, i._array); + break; + case "v3v": + if (!i._array) + i._array = new Float32Array(3 * j.length); + n = 0; + for (o = j.length; n < o; n++) { + q = n * 3; + i._array[q] = j[n].x; + i._array[q + 1] = j[n].y; + i._array[q + 2] = j[n].z + } + g.uniform3fv(l, i._array); + break; + case "v4v": + if (!i._array) + i._array = new Float32Array(4 * j.length); + n = 0; + for (o = j.length; n < o; n++) { + q = n * 4; + i._array[q] = j[n].x; + i._array[q + 1] = j[n].y; + i._array[q + 2] = j[n].z; + i._array[q + 3] = j[n].w + } + g.uniform4fv(l, i._array); + break; + case "m4": + if (!i._array) + i._array = new Float32Array(16); + j.flattenToArray(i._array); + g.uniformMatrix4fv(l, false, i._array); + break; + case "m4v": + if (!i._array) + i._array = new Float32Array(16 * + j.length); + n = 0; + for (o = j.length; n < o; n++) + j[n].flattenToArrayOffset(i._array, n * 16); + g.uniformMatrix4fv(l, false, i._array); + break; + case "t": + g.uniform1i(l, j); + l = i.texture; + if (!l) + continue; + if (l.image instanceof Array && l.image.length === 6) { + i = l; + if (i.image.length === 6) + if (i.needsUpdate) { + if (!i.image.__webglTextureCube) + i.image.__webglTextureCube = g.createTexture(); + g.activeTexture(g.TEXTURE0 + j); + g.bindTexture(g.TEXTURE_CUBE_MAP, i.image.__webglTextureCube); + j = []; + for (l = 0; l < 6; l++) { + n = j; + o = l; + if (D.autoScaleCubemaps) { + q = i.image[l]; + A = yc; + if (!(q.width <= A && q.height <= A)) { + B = Math.max(q.width, q.height); + y = Math.floor(q.width * A / B); + A = Math.floor(q.height * A / B); + B = document.createElement("canvas"); + B.width = y; + B.height = A; + B.getContext("2d").drawImage(q, 0, 0, q.width, q.height, 0, 0, y, A); + q = B + } + } else + q = i.image[l]; + n[o] = q + } + l = j[0]; + n = (l.width & l.width - 1) === 0 && (l.height & l.height - 1) === 0; + o = u(i.format); + q = u(i.type); + w(g.TEXTURE_CUBE_MAP, i, n); + for (l = 0; l < 6; l++) + g.texImage2D(g.TEXTURE_CUBE_MAP_POSITIVE_X + l, 0, o, o, q, j[l]); + i.generateMipmaps && n && g.generateMipmap(g.TEXTURE_CUBE_MAP); + i.needsUpdate = false; + if (i.onUpdate) + i.onUpdate() + } else { + g.activeTexture(g.TEXTURE0 + j); + g.bindTexture(g.TEXTURE_CUBE_MAP, i.image.__webglTextureCube) + } + } else if (l instanceof THREE.WebGLRenderTargetCube) { + i = l; + g.activeTexture(g.TEXTURE0 + j); + g.bindTexture(g.TEXTURE_CUBE_MAP, i.__webglTexture) + } else + D.setTexture(l, j); + break; + case "tv": + if (!i._array) { + i._array = []; + n = 0; + for (o = i.texture.length; n < o; n++) + i._array[n] = j + n + } + g.uniform1iv(l, i._array); + n = 0; + for (o = i.texture.length; n < o; n++) + (l = i.texture[n]) && D.setTexture(l, i._array[n]) + } + } + if ((d instanceof + THREE.ShaderMaterial || d instanceof THREE.MeshPhongMaterial || d.envMap) && h.cameraPosition !== null) { + b = a.matrixWorld.getPosition(); + g.uniform3f(h.cameraPosition, b.x, b.y, b.z) + } + (d instanceof THREE.MeshPhongMaterial || d instanceof THREE.MeshLambertMaterial || d instanceof THREE.ShaderMaterial || d.skinning) && h.viewMatrix !== null && g.uniformMatrix4fv(h.viewMatrix, false, a._viewMatrixArray); + d.skinning && g.uniformMatrix4fv(h.boneGlobalMatrices, false, e.boneMatrices) + } + g.uniformMatrix4fv(h.modelViewMatrix, false, e._modelViewMatrix.elements); + h.normalMatrix && g.uniformMatrix3fv(h.normalMatrix, false, e._normalMatrix.elements); + h.objectMatrix !== null && g.uniformMatrix4fv(h.objectMatrix, false, e.matrixWorld.elements); + return f + } + function s(a, b) { + a._modelViewMatrix.multiply(b.matrixWorldInverse, a.matrixWorld); + a._normalMatrix.getInverse(a._modelViewMatrix); + a._normalMatrix.transpose() + } + function p(a, b, c) { + if (Ta !== a) { + a ? g.enable(g.POLYGON_OFFSET_FILL) : g.disable(g.POLYGON_OFFSET_FILL); + Ta = a + } + if (a && (Vb !== b || Wb !== c)) { + g.polygonOffset(b, c); + Vb = b; + Wb = c + } + } + function q(a, + b) { + var c; + a === "fragment" ? c = g.createShader(g.FRAGMENT_SHADER) : a === "vertex" && (c = g.createShader(g.VERTEX_SHADER)); + g.shaderSource(c, b); + g.compileShader(c); + if (!g.getShaderParameter(c, g.COMPILE_STATUS)) { + console.error(g.getShaderInfoLog(c)); + console.error(b); + return null + } + return c + } + function w(a, b, c) { + if (c) { + g.texParameteri(a, g.TEXTURE_WRAP_S, u(b.wrapS)); + g.texParameteri(a, g.TEXTURE_WRAP_T, u(b.wrapT)); + g.texParameteri(a, g.TEXTURE_MAG_FILTER, u(b.magFilter)); + g.texParameteri(a, g.TEXTURE_MIN_FILTER, u(b.minFilter)) + } else { + g.texParameteri(a, + g.TEXTURE_WRAP_S, g.CLAMP_TO_EDGE); + g.texParameteri(a, g.TEXTURE_WRAP_T, g.CLAMP_TO_EDGE); + g.texParameteri(a, g.TEXTURE_MAG_FILTER, y(b.magFilter)); + g.texParameteri(a, g.TEXTURE_MIN_FILTER, y(b.minFilter)) + } + } + function A(a, b) { + g.bindRenderbuffer(g.RENDERBUFFER, a); + if (b.depthBuffer && !b.stencilBuffer) { + g.renderbufferStorage(g.RENDERBUFFER, g.DEPTH_COMPONENT16, b.width, b.height); + g.framebufferRenderbuffer(g.FRAMEBUFFER, g.DEPTH_ATTACHMENT, g.RENDERBUFFER, a) + } else if (b.depthBuffer && b.stencilBuffer) { + g.renderbufferStorage(g.RENDERBUFFER, + g.DEPTH_STENCIL, b.width, b.height); + g.framebufferRenderbuffer(g.FRAMEBUFFER, g.DEPTH_STENCIL_ATTACHMENT, g.RENDERBUFFER, a) + } else + g.renderbufferStorage(g.RENDERBUFFER, g.RGBA4, b.width, b.height) + } + function y(a) { + switch (a) { + case THREE.NearestFilter: + case THREE.NearestMipMapNearestFilter: + case THREE.NearestMipMapLinearFilter: + return g.NEAREST; + default: + return g.LINEAR + } + } + function u(a) { + switch (a) { + case THREE.RepeatWrapping: + return g.REPEAT; + case THREE.ClampToEdgeWrapping: + return g.CLAMP_TO_EDGE; + case THREE.MirroredRepeatWrapping: + return g.MIRRORED_REPEAT; + case THREE.NearestFilter: + return g.NEAREST; + case THREE.NearestMipMapNearestFilter: + return g.NEAREST_MIPMAP_NEAREST; + case THREE.NearestMipMapLinearFilter: + return g.NEAREST_MIPMAP_LINEAR; + case THREE.LinearFilter: + return g.LINEAR; + case THREE.LinearMipMapNearestFilter: + return g.LINEAR_MIPMAP_NEAREST; + case THREE.LinearMipMapLinearFilter: + return g.LINEAR_MIPMAP_LINEAR; + case THREE.ByteType: + return g.BYTE; + case THREE.UnsignedByteType: + return g.UNSIGNED_BYTE; + case THREE.ShortType: + return g.SHORT; + case THREE.UnsignedShortType: + return g.UNSIGNED_SHORT; + case THREE.IntType: + return g.INT; + case THREE.UnsignedIntType: + return g.UNSIGNED_INT; + case THREE.FloatType: + return g.FLOAT; + case THREE.AlphaFormat: + return g.ALPHA; + case THREE.RGBFormat: + return g.RGB; + case THREE.RGBAFormat: + return g.RGBA; + case THREE.LuminanceFormat: + return g.LUMINANCE; + case THREE.LuminanceAlphaFormat: + return g.LUMINANCE_ALPHA; + case THREE.AddEquation: + return g.FUNC_ADD; + case THREE.SubtractEquation: + return g.FUNC_SUBTRACT; + case THREE.ReverseSubtractEquation: + return g.FUNC_REVERSE_SUBTRACT; + case THREE.ZeroFactor: + return g.ZERO; + case THREE.OneFactor: + return g.ONE; + case THREE.SrcColorFactor: + return g.SRC_COLOR; + case THREE.OneMinusSrcColorFactor: + return g.ONE_MINUS_SRC_COLOR; + case THREE.SrcAlphaFactor: + return g.SRC_ALPHA; + case THREE.OneMinusSrcAlphaFactor: + return g.ONE_MINUS_SRC_ALPHA; + case THREE.DstAlphaFactor: + return g.DST_ALPHA; + case THREE.OneMinusDstAlphaFactor: + return g.ONE_MINUS_DST_ALPHA; + case THREE.DstColorFactor: + return g.DST_COLOR; + case THREE.OneMinusDstColorFactor: + return g.ONE_MINUS_DST_COLOR; + case THREE.SrcAlphaSaturateFactor: + return g.SRC_ALPHA_SATURATE + } + return 0 + } + console.log("THREE.WebGLRenderer", THREE.REVISION); + var a = a || {}, H = a.canvas !== void 0 ? a.canvas : document.createElement("canvas"), B = a.precision !== void 0 ? a.precision : "highp", K = a.alpha !== void 0 ? a.alpha : true, N = a.premultipliedAlpha !== void 0 ? a.premultipliedAlpha : true, Y = a.antialias !== void 0 ? a.antialias : false, ca = a.stencil !== void 0 ? a.stencil : true, I = a.preserveDrawingBuffer !== void 0 ? a.preserveDrawingBuffer : false, ba = a.clearColor !== void 0 ? new THREE.Color(a.clearColor) : new THREE.Color(0), ja = a.clearAlpha !== void 0 ? a.clearAlpha : + 0, ya = a.maxLights !== void 0 ? a.maxLights : 4; + this.domElement = H; + this.context = null; + this.autoUpdateScene = this.autoUpdateObjects = this.sortObjects = this.autoClearStencil = this.autoClearDepth = this.autoClearColor = this.autoClear = true; + this.shadowMapEnabled = this.physicallyBasedShading = this.gammaOutput = this.gammaInput = false; + this.shadowMapCullFrontFaces = this.shadowMapSoft = this.shadowMapAutoUpdate = true; + this.shadowMapCascade = this.shadowMapDebug = false; + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + this.autoScaleCubemaps = + true; + this.renderPluginsPre = []; + this.renderPluginsPost = []; + this.info = {memory: {programs: 0,geometries: 0,textures: 0},render: {calls: 0,vertices: 0,faces: 0,points: 0}}; + var D = this, g, Na = [], za = null, Da = null, $ = -1, R = null, J = null, Z = 0, Q = -1, pa = -1, O = -1, sa = -1, Ga = -1, Ha = -1, La = -1, Oa = -1, Ta = null, Vb = null, Wb = null, Db = null, Eb = 0, Fb = 0, Xb = 0, Gb = 0, jc = 0, kc = 0, Yb = new THREE.Frustum, qb = new THREE.Matrix4, Ub = new THREE.Matrix4, Ra = new THREE.Vector4, Ca = new THREE.Vector3, rb = true, lc = {ambient: [0, 0, 0],directional: {length: 0,colors: [],positions: []}, + point: {length: 0,colors: [],positions: [],distances: []},spot: {length: 0,colors: [],positions: [],distances: [],directions: [],angles: [],exponents: []}}; + g = function() { + var a; + try { + if (!(a = H.getContext("experimental-webgl", {alpha: K,premultipliedAlpha: N,antialias: Y,stencil: ca,preserveDrawingBuffer: I}))) + throw "Error creating WebGL context."; + } catch (b) { + console.error(b) + } + a.getExtension("OES_texture_float") || console.log("THREE.WebGLRenderer: Float textures not supported."); + return a + }(); + g.clearColor(0, 0, 0, 1); + g.clearDepth(1); + g.clearStencil(0); + g.enable(g.DEPTH_TEST); + g.depthFunc(g.LEQUAL); + g.frontFace(g.CCW); + g.cullFace(g.BACK); + g.enable(g.CULL_FACE); + g.enable(g.BLEND); + g.blendEquation(g.FUNC_ADD); + g.blendFunc(g.SRC_ALPHA, g.ONE_MINUS_SRC_ALPHA); + g.clearColor(ba.r, ba.g, ba.b, ja); + this.context = g; + var Zb = g.getParameter(g.MAX_VERTEX_TEXTURE_IMAGE_UNITS); + g.getParameter(g.MAX_TEXTURE_SIZE); + var yc = g.getParameter(g.MAX_CUBE_MAP_TEXTURE_SIZE); + this.getContext = function() { + return g + }; + this.supportsVertexTextures = function() { + return Zb > 0 + }; + this.setSize = + function(a, b) { + H.width = a; + H.height = b; + this.setViewport(0, 0, H.width, H.height) + }; + this.setViewport = function(a, b, c, d) { + Eb = a; + Fb = b; + Xb = c; + Gb = d; + g.viewport(Eb, Fb, Xb, Gb) + }; + this.setScissor = function(a, b, c, d) { + g.scissor(a, b, c, d) + }; + this.enableScissorTest = function(a) { + a ? g.enable(g.SCISSOR_TEST) : g.disable(g.SCISSOR_TEST) + }; + this.setClearColorHex = function(a, b) { + ba.setHex(a); + ja = b; + g.clearColor(ba.r, ba.g, ba.b, ja) + }; + this.setClearColor = function(a, b) { + ba.copy(a); + ja = b; + g.clearColor(ba.r, ba.g, ba.b, ja) + }; + this.getClearColor = function() { + return ba + }; + this.getClearAlpha = function() { + return ja + }; + this.clear = function(a, b, c) { + var d = 0; + if (a === void 0 || a) + d = d | g.COLOR_BUFFER_BIT; + if (b === void 0 || b) + d = d | g.DEPTH_BUFFER_BIT; + if (c === void 0 || c) + d = d | g.STENCIL_BUFFER_BIT; + g.clear(d) + }; + this.clearTarget = function(a, b, c, d) { + this.setRenderTarget(a); + this.clear(b, c, d) + }; + this.addPostPlugin = function(a) { + a.init(this); + this.renderPluginsPost.push(a) + }; + this.addPrePlugin = function(a) { + a.init(this); + this.renderPluginsPre.push(a) + }; + this.deallocateObject = function(a) { + if (a.__webglInit) { + a.__webglInit = + false; + delete a._modelViewMatrix; + delete a._normalMatrix; + delete a._normalMatrixArray; + delete a._modelViewMatrixArray; + delete a._objectMatrixArray; + if (a instanceof THREE.Mesh) + for (var b in a.geometry.geometryGroups) { + var c = a.geometry.geometryGroups[b]; + g.deleteBuffer(c.__webglVertexBuffer); + g.deleteBuffer(c.__webglNormalBuffer); + g.deleteBuffer(c.__webglTangentBuffer); + g.deleteBuffer(c.__webglColorBuffer); + g.deleteBuffer(c.__webglUVBuffer); + g.deleteBuffer(c.__webglUV2Buffer); + g.deleteBuffer(c.__webglSkinVertexABuffer); + g.deleteBuffer(c.__webglSkinVertexBBuffer); + g.deleteBuffer(c.__webglSkinIndicesBuffer); + g.deleteBuffer(c.__webglSkinWeightsBuffer); + g.deleteBuffer(c.__webglFaceBuffer); + g.deleteBuffer(c.__webglLineBuffer); + var d = void 0, e = void 0; + if (c.numMorphTargets) { + d = 0; + for (e = c.numMorphTargets; d < e; d++) + g.deleteBuffer(c.__webglMorphTargetsBuffers[d]) + } + if (c.numMorphNormals) { + d = 0; + for (e = c.numMorphNormals; d < e; d++) + g.deleteBuffer(c.__webglMorphNormalsBuffers[d]) + } + if (c.__webglCustomAttributesList) { + d = void 0; + for (d in c.__webglCustomAttributesList) + g.deleteBuffer(c.__webglCustomAttributesList[d].buffer) + } + D.info.memory.geometries-- + } + else if (a instanceof + THREE.Line) { + a = a.geometry; + g.deleteBuffer(a.__webglVertexBuffer); + g.deleteBuffer(a.__webglColorBuffer); + D.info.memory.geometries-- + } + } + }; + this.deallocateTexture = function(a) { + if (a.__webglInit) { + a.__webglInit = false; + g.deleteTexture(a.__webglTexture); + D.info.memory.textures-- + } + }; + this.deallocateRenderTarget = function(a) { + if (a && a.__webglTexture) { + g.deleteTexture(a.__webglTexture); + if (a instanceof THREE.WebGLRenderTargetCube) + for (var b = 0; b < 6; b++) { + g.deleteFramebuffer(a.__webglFramebuffer[b]); + g.deleteRenderbuffer(a.__webglRenderbuffer[b]) + } + else { + g.deleteFramebuffer(a.__webglFramebuffer); + g.deleteRenderbuffer(a.__webglRenderbuffer) + } + } + }; + this.updateShadowMap = function(a, b) { + za = null; + $ = R = Oa = La = O = -1; + rb = true; + pa = Q = -1; + this.shadowMapPlugin.update(a, b) + }; + this.renderBufferImmediate = function(a, b, c) { + if (!a.__webglVertexBuffer) + a.__webglVertexBuffer = g.createBuffer(); + if (!a.__webglNormalBuffer) + a.__webglNormalBuffer = g.createBuffer(); + if (a.hasPos) { + g.bindBuffer(g.ARRAY_BUFFER, a.__webglVertexBuffer); + g.bufferData(g.ARRAY_BUFFER, a.positionArray, g.DYNAMIC_DRAW); + g.enableVertexAttribArray(b.attributes.position); + g.vertexAttribPointer(b.attributes.position, 3, g.FLOAT, false, 0, 0) + } + if (a.hasNormal) { + g.bindBuffer(g.ARRAY_BUFFER, a.__webglNormalBuffer); + if (c === THREE.FlatShading) { + var d, e, f, h, i, k, j, l, n, m, o = a.count * 3; + for (m = 0; m < o; m = m + 9) { + c = a.normalArray; + d = c[m]; + e = c[m + 1]; + f = c[m + 2]; + h = c[m + 3]; + k = c[m + 4]; + l = c[m + 5]; + i = c[m + 6]; + j = c[m + 7]; + n = c[m + 8]; + d = (d + h + i) / 3; + e = (e + k + j) / 3; + f = (f + l + n) / 3; + c[m] = d; + c[m + 1] = e; + c[m + 2] = f; + c[m + 3] = d; + c[m + 4] = e; + c[m + 5] = f; + c[m + 6] = d; + c[m + 7] = e; + c[m + 8] = f + } + } + g.bufferData(g.ARRAY_BUFFER, a.normalArray, g.DYNAMIC_DRAW); + g.enableVertexAttribArray(b.attributes.normal); + g.vertexAttribPointer(b.attributes.normal, 3, g.FLOAT, false, 0, 0) + } + g.drawArrays(g.TRIANGLES, 0, a.count); + a.count = 0 + }; + this.renderBufferDirect = function(a, b, c, d, e, f) { + if (d.visible !== false) { + c = o(a, b, c, d, f); + a = c.attributes; + b = false; + d = e.id * 16777215 + c.id * 2 + (d.wireframe ? 1 : 0); + if (d !== R) { + R = d; + b = true + } + if (f instanceof THREE.Mesh) { + f = e.offsets; + d = 0; + for (c = f.length; d < c; ++d) { + if (b) { + g.bindBuffer(g.ARRAY_BUFFER, e.vertexPositionBuffer); + g.vertexAttribPointer(a.position, e.vertexPositionBuffer.itemSize, g.FLOAT, false, 0, f[d].index * 12); + if (a.normal >= + 0 && e.vertexNormalBuffer) { + g.bindBuffer(g.ARRAY_BUFFER, e.vertexNormalBuffer); + g.vertexAttribPointer(a.normal, e.vertexNormalBuffer.itemSize, g.FLOAT, false, 0, f[d].index * 12) + } + if (a.uv >= 0 && e.vertexUvBuffer) + if (e.vertexUvBuffer) { + g.bindBuffer(g.ARRAY_BUFFER, e.vertexUvBuffer); + g.vertexAttribPointer(a.uv, e.vertexUvBuffer.itemSize, g.FLOAT, false, 0, f[d].index * 8); + g.enableVertexAttribArray(a.uv) + } else + g.disableVertexAttribArray(a.uv); + if (a.color >= 0 && e.vertexColorBuffer) { + g.bindBuffer(g.ARRAY_BUFFER, e.vertexColorBuffer); + g.vertexAttribPointer(a.color, e.vertexColorBuffer.itemSize, g.FLOAT, false, 0, f[d].index * 16) + } + g.bindBuffer(g.ELEMENT_ARRAY_BUFFER, e.vertexIndexBuffer) + } + g.drawElements(g.TRIANGLES, f[d].count, g.UNSIGNED_SHORT, f[d].start * 2); + D.info.render.calls++; + D.info.render.vertices = D.info.render.vertices + f[d].count; + D.info.render.faces = D.info.render.faces + f[d].count / 3 + } + } + } + }; + this.renderBuffer = function(a, b, c, d, e, f) { + if (d.visible !== false) { + var h, i, c = o(a, b, c, d, f), b = c.attributes, a = false, c = e.id * 16777215 + c.id * 2 + (d.wireframe ? 1 : 0); + if (c !== R) { + R = c; + a = true + } + if (!d.morphTargets && b.position >= 0) { + if (a) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglVertexBuffer); + g.vertexAttribPointer(b.position, 3, g.FLOAT, false, 0, 0) + } + } else if (f.morphTargetBase) { + c = d.program.attributes; + if (f.morphTargetBase !== -1) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[f.morphTargetBase]); + g.vertexAttribPointer(c.position, 3, g.FLOAT, false, 0, 0) + } else if (c.position >= 0) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglVertexBuffer); + g.vertexAttribPointer(c.position, 3, g.FLOAT, false, 0, + 0) + } + if (f.morphTargetForcedOrder.length) { + h = 0; + var k = f.morphTargetForcedOrder; + for (i = f.morphTargetInfluences; h < d.numSupportedMorphTargets && h < k.length; ) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[k[h]]); + g.vertexAttribPointer(c["morphTarget" + h], 3, g.FLOAT, false, 0, 0); + if (d.morphNormals) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglMorphNormalsBuffers[k[h]]); + g.vertexAttribPointer(c["morphNormal" + h], 3, g.FLOAT, false, 0, 0) + } + f.__webglMorphTargetInfluences[h] = i[k[h]]; + h++ + } + } else { + var k = [], j = -1, l = 0; + i = f.morphTargetInfluences; + var n, m = i.length; + h = 0; + for (f.morphTargetBase !== -1 && (k[f.morphTargetBase] = true); h < d.numSupportedMorphTargets; ) { + for (n = 0; n < m; n++) + if (!k[n] && i[n] > j) { + l = n; + j = i[l] + } + g.bindBuffer(g.ARRAY_BUFFER, e.__webglMorphTargetsBuffers[l]); + g.vertexAttribPointer(c["morphTarget" + h], 3, g.FLOAT, false, 0, 0); + if (d.morphNormals) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglMorphNormalsBuffers[l]); + g.vertexAttribPointer(c["morphNormal" + h], 3, g.FLOAT, false, 0, 0) + } + f.__webglMorphTargetInfluences[h] = j; + k[l] = 1; + j = -1; + h++ + } + } + d.program.uniforms.morphTargetInfluences !== + null && g.uniform1fv(d.program.uniforms.morphTargetInfluences, f.__webglMorphTargetInfluences) + } + if (a) { + if (e.__webglCustomAttributesList) { + h = 0; + for (i = e.__webglCustomAttributesList.length; h < i; h++) { + c = e.__webglCustomAttributesList[h]; + if (b[c.buffer.belongsToAttribute] >= 0) { + g.bindBuffer(g.ARRAY_BUFFER, c.buffer); + g.vertexAttribPointer(b[c.buffer.belongsToAttribute], c.size, g.FLOAT, false, 0, 0) + } + } + } + if (b.color >= 0) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglColorBuffer); + g.vertexAttribPointer(b.color, 3, g.FLOAT, false, 0, 0) + } + if (b.normal >= + 0) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglNormalBuffer); + g.vertexAttribPointer(b.normal, 3, g.FLOAT, false, 0, 0) + } + if (b.tangent >= 0) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglTangentBuffer); + g.vertexAttribPointer(b.tangent, 4, g.FLOAT, false, 0, 0) + } + if (b.uv >= 0) + if (e.__webglUVBuffer) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglUVBuffer); + g.vertexAttribPointer(b.uv, 2, g.FLOAT, false, 0, 0); + g.enableVertexAttribArray(b.uv) + } else + g.disableVertexAttribArray(b.uv); + if (b.uv2 >= 0) + if (e.__webglUV2Buffer) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglUV2Buffer); + g.vertexAttribPointer(b.uv2, 2, g.FLOAT, false, 0, 0); + g.enableVertexAttribArray(b.uv2) + } else + g.disableVertexAttribArray(b.uv2); + if (d.skinning && b.skinVertexA >= 0 && b.skinVertexB >= 0 && b.skinIndex >= 0 && b.skinWeight >= 0) { + g.bindBuffer(g.ARRAY_BUFFER, e.__webglSkinVertexABuffer); + g.vertexAttribPointer(b.skinVertexA, 4, g.FLOAT, false, 0, 0); + g.bindBuffer(g.ARRAY_BUFFER, e.__webglSkinVertexBBuffer); + g.vertexAttribPointer(b.skinVertexB, 4, g.FLOAT, false, 0, 0); + g.bindBuffer(g.ARRAY_BUFFER, e.__webglSkinIndicesBuffer); + g.vertexAttribPointer(b.skinIndex, + 4, g.FLOAT, false, 0, 0); + g.bindBuffer(g.ARRAY_BUFFER, e.__webglSkinWeightsBuffer); + g.vertexAttribPointer(b.skinWeight, 4, g.FLOAT, false, 0, 0) + } + } + if (f instanceof THREE.Mesh) { + if (d.wireframe) { + d = d.wireframeLinewidth; + if (d !== Db) { + g.lineWidth(d); + Db = d + } + a && g.bindBuffer(g.ELEMENT_ARRAY_BUFFER, e.__webglLineBuffer); + g.drawElements(g.LINES, e.__webglLineCount, g.UNSIGNED_SHORT, 0) + } else { + a && g.bindBuffer(g.ELEMENT_ARRAY_BUFFER, e.__webglFaceBuffer); + g.drawElements(g.TRIANGLES, e.__webglFaceCount, g.UNSIGNED_SHORT, 0) + } + D.info.render.calls++; + D.info.render.vertices = D.info.render.vertices + e.__webglFaceCount; + D.info.render.faces = D.info.render.faces + e.__webglFaceCount / 3 + } else if (f instanceof THREE.Line) { + f = f.type === THREE.LineStrip ? g.LINE_STRIP : g.LINES; + d = d.linewidth; + if (d !== Db) { + g.lineWidth(d); + Db = d + } + g.drawArrays(f, 0, e.__webglLineCount); + D.info.render.calls++ + } + } + }; + this.render = function(a, b, c, d) { + var e, f, j, n, m = a.__lights, o = a.fog; + $ = -1; + rb = true; + if (b.parent === void 0) { + console.warn("DEPRECATED: Camera hasn't been added to a Scene. Adding it..."); + a.add(b) + } + this.autoUpdateScene && + a.updateMatrixWorld(); + if (!b._viewMatrixArray) + b._viewMatrixArray = new Float32Array(16); + if (!b._projectionMatrixArray) + b._projectionMatrixArray = new Float32Array(16); + b.matrixWorldInverse.getInverse(b.matrixWorld); + b.matrixWorldInverse.flattenToArray(b._viewMatrixArray); + b.projectionMatrix.flattenToArray(b._projectionMatrixArray); + qb.multiply(b.projectionMatrix, b.matrixWorldInverse); + Yb.setFromMatrix(qb); + this.autoUpdateObjects && this.initWebGLObjects(a); + i(this.renderPluginsPre, a, b); + D.info.render.calls = 0; + D.info.render.vertices = + 0; + D.info.render.faces = 0; + D.info.render.points = 0; + this.setRenderTarget(c); + (this.autoClear || d) && this.clear(this.autoClearColor, this.autoClearDepth, this.autoClearStencil); + n = a.__webglObjects; + d = 0; + for (e = n.length; d < e; d++) { + f = n[d]; + j = f.object; + f.render = false; + if (j.visible && (!(j instanceof THREE.Mesh || j instanceof THREE.ParticleSystem) || !j.frustumCulled || Yb.contains(j))) { + s(j, b); + var q = f, u = q.object, y = q.buffer, w = void 0, w = w = void 0, w = u.material; + if (w instanceof THREE.MeshFaceMaterial) { + w = y.materialIndex; + if (w >= 0) { + w = u.geometry.materials[w]; + if (w.transparent) { + q.transparent = w; + q.opaque = null + } else { + q.opaque = w; + q.transparent = null + } + } + } else if (w) + if (w.transparent) { + q.transparent = w; + q.opaque = null + } else { + q.opaque = w; + q.transparent = null + } + f.render = true; + if (this.sortObjects) + if (j.renderDepth) + f.z = j.renderDepth; + else { + Ra.copy(j.matrixWorld.getPosition()); + qb.multiplyVector3(Ra); + f.z = Ra.z + } + } + } + this.sortObjects && n.sort(h); + n = a.__webglObjectsImmediate; + d = 0; + for (e = n.length; d < e; d++) { + f = n[d]; + j = f.object; + if (j.visible) { + s(j, b); + j = f.object.material; + if (j.transparent) { + f.transparent = j; + f.opaque = + null + } else { + f.opaque = j; + f.transparent = null + } + } + } + if (a.overrideMaterial) { + d = a.overrideMaterial; + this.setBlending(d.blending, d.blendEquation, d.blendSrc, d.blendDst); + this.setDepthTest(d.depthTest); + this.setDepthWrite(d.depthWrite); + p(d.polygonOffset, d.polygonOffsetFactor, d.polygonOffsetUnits); + l(a.__webglObjects, false, "", b, m, o, true, d); + k(a.__webglObjectsImmediate, "", b, m, o, false, d) + } else { + this.setBlending(THREE.NormalBlending); + l(a.__webglObjects, true, "opaque", b, m, o, false); + k(a.__webglObjectsImmediate, "opaque", b, m, o, false); + l(a.__webglObjects, false, "transparent", b, m, o, true); + k(a.__webglObjectsImmediate, "transparent", b, m, o, true) + } + i(this.renderPluginsPost, a, b); + if (c && c.generateMipmaps && c.minFilter !== THREE.NearestFilter && c.minFilter !== THREE.LinearFilter) + if (c instanceof THREE.WebGLRenderTargetCube) { + g.bindTexture(g.TEXTURE_CUBE_MAP, c.__webglTexture); + g.generateMipmap(g.TEXTURE_CUBE_MAP); + g.bindTexture(g.TEXTURE_CUBE_MAP, null) + } else { + g.bindTexture(g.TEXTURE_2D, c.__webglTexture); + g.generateMipmap(g.TEXTURE_2D); + g.bindTexture(g.TEXTURE_2D, + null) + } + this.setDepthTest(true); + this.setDepthWrite(true) + }; + this.renderImmediateObject = function(a, b, c, d, e) { + var f = o(a, b, c, d, e); + R = -1; + D.setObjectFaces(e); + e.immediateRenderCallback ? e.immediateRenderCallback(f, g, Yb) : e.render(function(a) { + D.renderBufferImmediate(a, f, d.shading) + }) + }; + this.initWebGLObjects = function(a) { + if (!a.__webglObjects) { + a.__webglObjects = []; + a.__webglObjectsImmediate = []; + a.__webglSprites = []; + a.__webglFlares = [] + } + for (; a.__objectsAdded.length; ) { + var h = a.__objectsAdded[0], i = a, k = void 0, l = void 0, o = void 0; + if (!h.__webglInit) { + h.__webglInit = true; + h._modelViewMatrix = new THREE.Matrix4; + h._normalMatrix = new THREE.Matrix3; + if (h instanceof THREE.Mesh) { + l = h.geometry; + if (l instanceof THREE.Geometry) { + if (l.geometryGroups === void 0) { + var q = l, p = void 0, s = void 0, u = void 0, w = void 0, y = void 0, A = void 0, B = void 0, H = {}, K = q.morphTargets.length, Y = q.morphNormals.length; + q.geometryGroups = {}; + p = 0; + for (s = q.faces.length; p < s; p++) { + u = q.faces[p]; + w = u.materialIndex; + A = w !== void 0 ? w : -1; + H[A] === void 0 && (H[A] = {hash: A,counter: 0}); + B = H[A].hash + "_" + H[A].counter; + q.geometryGroups[B] === void 0 && (q.geometryGroups[B] = {faces3: [],faces4: [],materialIndex: w,vertices: 0,numMorphTargets: K,numMorphNormals: Y}); + y = u instanceof THREE.Face3 ? 3 : 4; + if (q.geometryGroups[B].vertices + y > 65535) { + H[A].counter = H[A].counter + 1; + B = H[A].hash + "_" + H[A].counter; + q.geometryGroups[B] === void 0 && (q.geometryGroups[B] = {faces3: [],faces4: [],materialIndex: w,vertices: 0,numMorphTargets: K,numMorphNormals: Y}) + } + u instanceof THREE.Face3 ? q.geometryGroups[B].faces3.push(p) : q.geometryGroups[B].faces4.push(p); + q.geometryGroups[B].vertices = + q.geometryGroups[B].vertices + y + } + q.geometryGroupsList = []; + var J = void 0; + for (J in q.geometryGroups) { + q.geometryGroups[J].id = Z++; + q.geometryGroupsList.push(q.geometryGroups[J]) + } + } + for (k in l.geometryGroups) { + o = l.geometryGroups[k]; + if (!o.__webglVertexBuffer) { + var N = o; + N.__webglVertexBuffer = g.createBuffer(); + N.__webglNormalBuffer = g.createBuffer(); + N.__webglTangentBuffer = g.createBuffer(); + N.__webglColorBuffer = g.createBuffer(); + N.__webglUVBuffer = g.createBuffer(); + N.__webglUV2Buffer = g.createBuffer(); + N.__webglSkinVertexABuffer = + g.createBuffer(); + N.__webglSkinVertexBBuffer = g.createBuffer(); + N.__webglSkinIndicesBuffer = g.createBuffer(); + N.__webglSkinWeightsBuffer = g.createBuffer(); + N.__webglFaceBuffer = g.createBuffer(); + N.__webglLineBuffer = g.createBuffer(); + var O = void 0, R = void 0; + if (N.numMorphTargets) { + N.__webglMorphTargetsBuffers = []; + O = 0; + for (R = N.numMorphTargets; O < R; O++) + N.__webglMorphTargetsBuffers.push(g.createBuffer()) + } + if (N.numMorphNormals) { + N.__webglMorphNormalsBuffers = []; + O = 0; + for (R = N.numMorphNormals; O < R; O++) + N.__webglMorphNormalsBuffers.push(g.createBuffer()) + } + D.info.memory.geometries++; + var I = o, ba = h, ja = ba.geometry, ca = I.faces3, $ = I.faces4, Q = ca.length * 3 + $.length * 4, za = ca.length * 1 + $.length * 2, pa = ca.length * 3 + $.length * 4, ya = c(ba, I), Na = e(ya), la = d(ya), Da = ya.vertexColors ? ya.vertexColors : false; + I.__vertexArray = new Float32Array(Q * 3); + if (la) + I.__normalArray = new Float32Array(Q * 3); + if (ja.hasTangents) + I.__tangentArray = new Float32Array(Q * 4); + if (Da) + I.__colorArray = new Float32Array(Q * 3); + if (Na) { + if (ja.faceUvs.length > 0 || ja.faceVertexUvs.length > 0) + I.__uvArray = new Float32Array(Q * 2); + if (ja.faceUvs.length > 1 || ja.faceVertexUvs.length > + 1) + I.__uv2Array = new Float32Array(Q * 2) + } + if (ba.geometry.skinWeights.length && ba.geometry.skinIndices.length) { + I.__skinVertexAArray = new Float32Array(Q * 4); + I.__skinVertexBArray = new Float32Array(Q * 4); + I.__skinIndexArray = new Float32Array(Q * 4); + I.__skinWeightArray = new Float32Array(Q * 4) + } + I.__faceArray = new Uint16Array(za * 3); + I.__lineArray = new Uint16Array(pa * 2); + var sa = void 0, Ca = void 0; + if (I.numMorphTargets) { + I.__morphTargetsArrays = []; + sa = 0; + for (Ca = I.numMorphTargets; sa < Ca; sa++) + I.__morphTargetsArrays.push(new Float32Array(Q * + 3)) + } + if (I.numMorphNormals) { + I.__morphNormalsArrays = []; + sa = 0; + for (Ca = I.numMorphNormals; sa < Ca; sa++) + I.__morphNormalsArrays.push(new Float32Array(Q * 3)) + } + I.__webglFaceCount = za * 3; + I.__webglLineCount = pa * 2; + if (ya.attributes) { + if (I.__webglCustomAttributesList === void 0) + I.__webglCustomAttributesList = []; + var Ga = void 0; + for (Ga in ya.attributes) { + var La = ya.attributes[Ga], Ma = {}, Oa; + for (Oa in La) + Ma[Oa] = La[Oa]; + if (!Ma.__webglInitialized || Ma.createUniqueBuffers) { + Ma.__webglInitialized = true; + var Ha = 1; + Ma.type === "v2" ? Ha = 2 : Ma.type === + "v3" ? Ha = 3 : Ma.type === "v4" ? Ha = 4 : Ma.type === "c" && (Ha = 3); + Ma.size = Ha; + Ma.array = new Float32Array(Q * Ha); + Ma.buffer = g.createBuffer(); + Ma.buffer.belongsToAttribute = Ga; + La.needsUpdate = true; + Ma.__original = La + } + I.__webglCustomAttributesList.push(Ma) + } + } + I.__inittedArrays = true; + l.verticesNeedUpdate = true; + l.morphTargetsNeedUpdate = true; + l.elementsNeedUpdate = true; + l.uvsNeedUpdate = true; + l.normalsNeedUpdate = true; + l.tangetsNeedUpdate = true; + l.colorsNeedUpdate = true + } + } + } + } else if (h instanceof THREE.Line) { + l = h.geometry; + if (!l.__webglVertexBuffer) { + var Ta = + l; + Ta.__webglVertexBuffer = g.createBuffer(); + Ta.__webglColorBuffer = g.createBuffer(); + D.info.memory.geometries++; + var Ra = l, rb = h, qb = Ra.vertices.length; + Ra.__vertexArray = new Float32Array(qb * 3); + Ra.__colorArray = new Float32Array(qb * 3); + Ra.__webglLineCount = qb; + b(Ra, rb); + l.verticesNeedUpdate = true; + l.colorsNeedUpdate = true + } + } else if (h instanceof THREE.ParticleSystem) { + l = h.geometry; + if (!l.__webglVertexBuffer) { + var Db = l; + Db.__webglVertexBuffer = g.createBuffer(); + Db.__webglColorBuffer = g.createBuffer(); + D.info.geometries++; + var Mb = + l, Ub = h, Eb = Mb.vertices.length; + Mb.__vertexArray = new Float32Array(Eb * 3); + Mb.__colorArray = new Float32Array(Eb * 3); + Mb.__sortArray = []; + Mb.__webglParticleCount = Eb; + b(Mb, Ub); + l.verticesNeedUpdate = true; + l.colorsNeedUpdate = true + } + } + } + if (!h.__webglActive) { + if (h instanceof THREE.Mesh) { + l = h.geometry; + if (l instanceof THREE.BufferGeometry) + j(i.__webglObjects, l, h); + else + for (k in l.geometryGroups) { + o = l.geometryGroups[k]; + j(i.__webglObjects, o, h) + } + } else if (h instanceof THREE.Line) { + l = h.geometry; + j(i.__webglObjects, l, h) + } + h.__webglActive = + true + } + a.__objectsAdded.splice(0, 1) + } + for (; a.__objectsRemoved.length; ) { + var mc = a.__objectsRemoved[0]; + if (mc instanceof THREE.Mesh || mc instanceof THREE.Line) + for (var Fb = a.__webglObjects, Xb = mc, nc = Fb.length - 1; nc >= 0; nc--) + Fb[nc].object === Xb && Fb.splice(nc, 1); + mc.__webglActive = false; + a.__objectsRemoved.splice(0, 1) + } + for (var Gb = 0, Yb = a.__webglObjects.length; Gb < Yb; Gb++) { + var Va = a.__webglObjects[Gb].object, V = Va.geometry, $b = void 0, Nb = void 0, Ea = void 0; + if (Va instanceof THREE.Mesh) + if (V instanceof THREE.BufferGeometry) { + V.verticesNeedUpdate = + false; + V.elementsNeedUpdate = false; + V.uvsNeedUpdate = false; + V.normalsNeedUpdate = false; + V.colorsNeedUpdate = false + } else { + for (var zc = 0, jc = V.geometryGroupsList.length; zc < jc; zc++) { + $b = V.geometryGroupsList[zc]; + Ea = c(Va, $b); + Nb = Ea.attributes && m(Ea); + if (V.verticesNeedUpdate || V.morphTargetsNeedUpdate || V.elementsNeedUpdate || V.uvsNeedUpdate || V.normalsNeedUpdate || V.colorsNeedUpdate || V.tangetsNeedUpdate || Nb) { + var M = $b, kc = Va, Ia = g.DYNAMIC_DRAW, lc = !V.dynamic, Hb = Ea; + if (M.__inittedArrays) { + var Vb = d(Hb), Ac = Hb.vertexColors ? Hb.vertexColors : + false, Wb = e(Hb), oc = Vb === THREE.SmoothShading, v = void 0, C = void 0, Sa = void 0, z = void 0, Ob = void 0, sb = void 0, Ua = void 0, pc = void 0, lb = void 0, Pb = void 0, Qb = void 0, E = void 0, F = void 0, G = void 0, W = void 0, Wa = void 0, Xa = void 0, Ya = void 0, ac = void 0, Za = void 0, $a = void 0, ab = void 0, bc = void 0, bb = void 0, cb = void 0, db = void 0, cc = void 0, eb = void 0, fb = void 0, gb = void 0, dc = void 0, hb = void 0, ib = void 0, jb = void 0, ec = void 0, tb = void 0, ub = void 0, vb = void 0, qc = void 0, wb = void 0, xb = void 0, yb = void 0, rc = void 0, S = void 0, Zb = void 0, zb = void 0, Rb = void 0, + Sb = void 0, ta = void 0, Ic = void 0, qa = void 0, ra = void 0, Ab = void 0, mb = void 0, ka = 0, oa = 0, nb = 0, ob = 0, Pa = 0, xa = 0, X = 0, Aa = 0, ma = 0, x = 0, L = 0, t = 0, Ja = void 0, ua = M.__vertexArray, fc = M.__uvArray, gc = M.__uv2Array, Qa = M.__normalArray, da = M.__tangentArray, va = M.__colorArray, ea = M.__skinVertexAArray, fa = M.__skinVertexBArray, ga = M.__skinIndexArray, ha = M.__skinWeightArray, Bc = M.__morphTargetsArrays, Cc = M.__morphNormalsArrays, Dc = M.__webglCustomAttributesList, r = void 0, kb = M.__faceArray, Ka = M.__lineArray, Ba = kc.geometry, yc = Ba.elementsNeedUpdate, + Jc = Ba.uvsNeedUpdate, Nc = Ba.normalsNeedUpdate, Oc = Ba.tangetsNeedUpdate, Pc = Ba.colorsNeedUpdate, Qc = Ba.morphTargetsNeedUpdate, Ib = Ba.vertices, T = M.faces3, U = M.faces4, na = Ba.faces, Ec = Ba.faceVertexUvs[0], Fc = Ba.faceVertexUvs[1], Jb = Ba.skinVerticesA, Kb = Ba.skinVerticesB, Lb = Ba.skinIndices, Bb = Ba.skinWeights, Cb = Ba.morphTargets, sc = Ba.morphNormals; + if (Ba.verticesNeedUpdate) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + E = Ib[z.a]; + F = Ib[z.b]; + G = Ib[z.c]; + ua[oa] = E.x; + ua[oa + 1] = E.y; + ua[oa + 2] = E.z; + ua[oa + 3] = F.x; + ua[oa + 4] = F.y; + ua[oa + 5] = F.z; + ua[oa + + 6] = G.x; + ua[oa + 7] = G.y; + ua[oa + 8] = G.z; + oa = oa + 9 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + E = Ib[z.a]; + F = Ib[z.b]; + G = Ib[z.c]; + W = Ib[z.d]; + ua[oa] = E.x; + ua[oa + 1] = E.y; + ua[oa + 2] = E.z; + ua[oa + 3] = F.x; + ua[oa + 4] = F.y; + ua[oa + 5] = F.z; + ua[oa + 6] = G.x; + ua[oa + 7] = G.y; + ua[oa + 8] = G.z; + ua[oa + 9] = W.x; + ua[oa + 10] = W.y; + ua[oa + 11] = W.z; + oa = oa + 12 + } + g.bindBuffer(g.ARRAY_BUFFER, M.__webglVertexBuffer); + g.bufferData(g.ARRAY_BUFFER, ua, Ia) + } + if (Qc) { + ta = 0; + for (Ic = Cb.length; ta < Ic; ta++) { + v = L = 0; + for (C = T.length; v < C; v++) { + Ab = T[v]; + z = na[Ab]; + E = Cb[ta].vertices[z.a]; + F = Cb[ta].vertices[z.b]; + G = Cb[ta].vertices[z.c]; + qa = Bc[ta]; + qa[L] = E.x; + qa[L + 1] = E.y; + qa[L + 2] = E.z; + qa[L + 3] = F.x; + qa[L + 4] = F.y; + qa[L + 5] = F.z; + qa[L + 6] = G.x; + qa[L + 7] = G.y; + qa[L + 8] = G.z; + if (Hb.morphNormals) { + if (oc) { + mb = sc[ta].vertexNormals[Ab]; + Za = mb.a; + $a = mb.b; + ab = mb.c + } else + ab = $a = Za = sc[ta].faceNormals[Ab]; + ra = Cc[ta]; + ra[L] = Za.x; + ra[L + 1] = Za.y; + ra[L + 2] = Za.z; + ra[L + 3] = $a.x; + ra[L + 4] = $a.y; + ra[L + 5] = $a.z; + ra[L + 6] = ab.x; + ra[L + 7] = ab.y; + ra[L + 8] = ab.z + } + L = L + 9 + } + v = 0; + for (C = U.length; v < C; v++) { + Ab = U[v]; + z = na[Ab]; + E = Cb[ta].vertices[z.a]; + F = Cb[ta].vertices[z.b]; + G = Cb[ta].vertices[z.c]; + W = + Cb[ta].vertices[z.d]; + qa = Bc[ta]; + qa[L] = E.x; + qa[L + 1] = E.y; + qa[L + 2] = E.z; + qa[L + 3] = F.x; + qa[L + 4] = F.y; + qa[L + 5] = F.z; + qa[L + 6] = G.x; + qa[L + 7] = G.y; + qa[L + 8] = G.z; + qa[L + 9] = W.x; + qa[L + 10] = W.y; + qa[L + 11] = W.z; + if (Hb.morphNormals) { + if (oc) { + mb = sc[ta].vertexNormals[Ab]; + Za = mb.a; + $a = mb.b; + ab = mb.c; + bc = mb.d + } else + bc = ab = $a = Za = sc[ta].faceNormals[Ab]; + ra = Cc[ta]; + ra[L] = Za.x; + ra[L + 1] = Za.y; + ra[L + 2] = Za.z; + ra[L + 3] = $a.x; + ra[L + 4] = $a.y; + ra[L + 5] = $a.z; + ra[L + 6] = ab.x; + ra[L + 7] = ab.y; + ra[L + 8] = ab.z; + ra[L + 9] = bc.x; + ra[L + 10] = bc.y; + ra[L + 11] = bc.z + } + L = L + 12 + } + g.bindBuffer(g.ARRAY_BUFFER, + M.__webglMorphTargetsBuffers[ta]); + g.bufferData(g.ARRAY_BUFFER, Bc[ta], Ia); + if (Hb.morphNormals) { + g.bindBuffer(g.ARRAY_BUFFER, M.__webglMorphNormalsBuffers[ta]); + g.bufferData(g.ARRAY_BUFFER, Cc[ta], Ia) + } + } + } + if (Bb.length) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + eb = Bb[z.a]; + fb = Bb[z.b]; + gb = Bb[z.c]; + ha[x] = eb.x; + ha[x + 1] = eb.y; + ha[x + 2] = eb.z; + ha[x + 3] = eb.w; + ha[x + 4] = fb.x; + ha[x + 5] = fb.y; + ha[x + 6] = fb.z; + ha[x + 7] = fb.w; + ha[x + 8] = gb.x; + ha[x + 9] = gb.y; + ha[x + 10] = gb.z; + ha[x + 11] = gb.w; + hb = Lb[z.a]; + ib = Lb[z.b]; + jb = Lb[z.c]; + ga[x] = hb.x; + ga[x + 1] = hb.y; + ga[x + 2] = + hb.z; + ga[x + 3] = hb.w; + ga[x + 4] = ib.x; + ga[x + 5] = ib.y; + ga[x + 6] = ib.z; + ga[x + 7] = ib.w; + ga[x + 8] = jb.x; + ga[x + 9] = jb.y; + ga[x + 10] = jb.z; + ga[x + 11] = jb.w; + tb = Jb[z.a]; + ub = Jb[z.b]; + vb = Jb[z.c]; + ea[x] = tb.x; + ea[x + 1] = tb.y; + ea[x + 2] = tb.z; + ea[x + 3] = 1; + ea[x + 4] = ub.x; + ea[x + 5] = ub.y; + ea[x + 6] = ub.z; + ea[x + 7] = 1; + ea[x + 8] = vb.x; + ea[x + 9] = vb.y; + ea[x + 10] = vb.z; + ea[x + 11] = 1; + wb = Kb[z.a]; + xb = Kb[z.b]; + yb = Kb[z.c]; + fa[x] = wb.x; + fa[x + 1] = wb.y; + fa[x + 2] = wb.z; + fa[x + 3] = 1; + fa[x + 4] = xb.x; + fa[x + 5] = xb.y; + fa[x + 6] = xb.z; + fa[x + 7] = 1; + fa[x + 8] = yb.x; + fa[x + 9] = yb.y; + fa[x + 10] = yb.z; + fa[x + 11] = 1; + x = x + 12 + } + v = 0; + for (C = + U.length; v < C; v++) { + z = na[U[v]]; + eb = Bb[z.a]; + fb = Bb[z.b]; + gb = Bb[z.c]; + dc = Bb[z.d]; + ha[x] = eb.x; + ha[x + 1] = eb.y; + ha[x + 2] = eb.z; + ha[x + 3] = eb.w; + ha[x + 4] = fb.x; + ha[x + 5] = fb.y; + ha[x + 6] = fb.z; + ha[x + 7] = fb.w; + ha[x + 8] = gb.x; + ha[x + 9] = gb.y; + ha[x + 10] = gb.z; + ha[x + 11] = gb.w; + ha[x + 12] = dc.x; + ha[x + 13] = dc.y; + ha[x + 14] = dc.z; + ha[x + 15] = dc.w; + hb = Lb[z.a]; + ib = Lb[z.b]; + jb = Lb[z.c]; + ec = Lb[z.d]; + ga[x] = hb.x; + ga[x + 1] = hb.y; + ga[x + 2] = hb.z; + ga[x + 3] = hb.w; + ga[x + 4] = ib.x; + ga[x + 5] = ib.y; + ga[x + 6] = ib.z; + ga[x + 7] = ib.w; + ga[x + 8] = jb.x; + ga[x + 9] = jb.y; + ga[x + 10] = jb.z; + ga[x + 11] = jb.w; + ga[x + 12] = ec.x; + ga[x + + 13] = ec.y; + ga[x + 14] = ec.z; + ga[x + 15] = ec.w; + tb = Jb[z.a]; + ub = Jb[z.b]; + vb = Jb[z.c]; + qc = Jb[z.d]; + ea[x] = tb.x; + ea[x + 1] = tb.y; + ea[x + 2] = tb.z; + ea[x + 3] = 1; + ea[x + 4] = ub.x; + ea[x + 5] = ub.y; + ea[x + 6] = ub.z; + ea[x + 7] = 1; + ea[x + 8] = vb.x; + ea[x + 9] = vb.y; + ea[x + 10] = vb.z; + ea[x + 11] = 1; + ea[x + 12] = qc.x; + ea[x + 13] = qc.y; + ea[x + 14] = qc.z; + ea[x + 15] = 1; + wb = Kb[z.a]; + xb = Kb[z.b]; + yb = Kb[z.c]; + rc = Kb[z.d]; + fa[x] = wb.x; + fa[x + 1] = wb.y; + fa[x + 2] = wb.z; + fa[x + 3] = 1; + fa[x + 4] = xb.x; + fa[x + 5] = xb.y; + fa[x + 6] = xb.z; + fa[x + 7] = 1; + fa[x + 8] = yb.x; + fa[x + 9] = yb.y; + fa[x + 10] = yb.z; + fa[x + 11] = 1; + fa[x + 12] = rc.x; + fa[x + 13] = rc.y; + fa[x + + 14] = rc.z; + fa[x + 15] = 1; + x = x + 16 + } + if (x > 0) { + g.bindBuffer(g.ARRAY_BUFFER, M.__webglSkinVertexABuffer); + g.bufferData(g.ARRAY_BUFFER, ea, Ia); + g.bindBuffer(g.ARRAY_BUFFER, M.__webglSkinVertexBBuffer); + g.bufferData(g.ARRAY_BUFFER, fa, Ia); + g.bindBuffer(g.ARRAY_BUFFER, M.__webglSkinIndicesBuffer); + g.bufferData(g.ARRAY_BUFFER, ga, Ia); + g.bindBuffer(g.ARRAY_BUFFER, M.__webglSkinWeightsBuffer); + g.bufferData(g.ARRAY_BUFFER, ha, Ia) + } + } + if (Pc && Ac) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + Ua = z.vertexColors; + pc = z.color; + if (Ua.length === 3 && Ac === + THREE.VertexColors) { + bb = Ua[0]; + cb = Ua[1]; + db = Ua[2] + } else + db = cb = bb = pc; + va[ma] = bb.r; + va[ma + 1] = bb.g; + va[ma + 2] = bb.b; + va[ma + 3] = cb.r; + va[ma + 4] = cb.g; + va[ma + 5] = cb.b; + va[ma + 6] = db.r; + va[ma + 7] = db.g; + va[ma + 8] = db.b; + ma = ma + 9 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + Ua = z.vertexColors; + pc = z.color; + if (Ua.length === 4 && Ac === THREE.VertexColors) { + bb = Ua[0]; + cb = Ua[1]; + db = Ua[2]; + cc = Ua[3] + } else + cc = db = cb = bb = pc; + va[ma] = bb.r; + va[ma + 1] = bb.g; + va[ma + 2] = bb.b; + va[ma + 3] = cb.r; + va[ma + 4] = cb.g; + va[ma + 5] = cb.b; + va[ma + 6] = db.r; + va[ma + 7] = db.g; + va[ma + 8] = db.b; + va[ma + 9] = cc.r; + va[ma + 10] = cc.g; + va[ma + 11] = cc.b; + ma = ma + 12 + } + if (ma > 0) { + g.bindBuffer(g.ARRAY_BUFFER, M.__webglColorBuffer); + g.bufferData(g.ARRAY_BUFFER, va, Ia) + } + } + if (Oc && Ba.hasTangents) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + lb = z.vertexTangents; + Wa = lb[0]; + Xa = lb[1]; + Ya = lb[2]; + da[X] = Wa.x; + da[X + 1] = Wa.y; + da[X + 2] = Wa.z; + da[X + 3] = Wa.w; + da[X + 4] = Xa.x; + da[X + 5] = Xa.y; + da[X + 6] = Xa.z; + da[X + 7] = Xa.w; + da[X + 8] = Ya.x; + da[X + 9] = Ya.y; + da[X + 10] = Ya.z; + da[X + 11] = Ya.w; + X = X + 12 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + lb = z.vertexTangents; + Wa = lb[0]; + Xa = lb[1]; + Ya = lb[2]; + ac = lb[3]; + da[X] = Wa.x; + da[X + 1] = Wa.y; + da[X + 2] = Wa.z; + da[X + 3] = Wa.w; + da[X + 4] = Xa.x; + da[X + 5] = Xa.y; + da[X + 6] = Xa.z; + da[X + 7] = Xa.w; + da[X + 8] = Ya.x; + da[X + 9] = Ya.y; + da[X + 10] = Ya.z; + da[X + 11] = Ya.w; + da[X + 12] = ac.x; + da[X + 13] = ac.y; + da[X + 14] = ac.z; + da[X + 15] = ac.w; + X = X + 16 + } + g.bindBuffer(g.ARRAY_BUFFER, M.__webglTangentBuffer); + g.bufferData(g.ARRAY_BUFFER, da, Ia) + } + if (Nc && Vb) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + Ob = z.vertexNormals; + sb = z.normal; + if (Ob.length === 3 && oc) + for (S = 0; S < 3; S++) { + zb = Ob[S]; + Qa[xa] = zb.x; + Qa[xa + 1] = zb.y; + Qa[xa + 2] = zb.z; + xa = xa + 3 + } + else + for (S = 0; S < 3; S++) { + Qa[xa] = + sb.x; + Qa[xa + 1] = sb.y; + Qa[xa + 2] = sb.z; + xa = xa + 3 + } + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + Ob = z.vertexNormals; + sb = z.normal; + if (Ob.length === 4 && oc) + for (S = 0; S < 4; S++) { + zb = Ob[S]; + Qa[xa] = zb.x; + Qa[xa + 1] = zb.y; + Qa[xa + 2] = zb.z; + xa = xa + 3 + } + else + for (S = 0; S < 4; S++) { + Qa[xa] = sb.x; + Qa[xa + 1] = sb.y; + Qa[xa + 2] = sb.z; + xa = xa + 3 + } + } + g.bindBuffer(g.ARRAY_BUFFER, M.__webglNormalBuffer); + g.bufferData(g.ARRAY_BUFFER, Qa, Ia) + } + if (Jc && Ec && Wb) { + v = 0; + for (C = T.length; v < C; v++) { + Sa = T[v]; + z = na[Sa]; + Pb = Ec[Sa]; + if (Pb !== void 0) + for (S = 0; S < 3; S++) { + Rb = Pb[S]; + fc[nb] = Rb.u; + fc[nb + 1] = Rb.v; + nb = nb + 2 + } + } + v = 0; + for (C = U.length; v < C; v++) { + Sa = U[v]; + z = na[Sa]; + Pb = Ec[Sa]; + if (Pb !== void 0) + for (S = 0; S < 4; S++) { + Rb = Pb[S]; + fc[nb] = Rb.u; + fc[nb + 1] = Rb.v; + nb = nb + 2 + } + } + if (nb > 0) { + g.bindBuffer(g.ARRAY_BUFFER, M.__webglUVBuffer); + g.bufferData(g.ARRAY_BUFFER, fc, Ia) + } + } + if (Jc && Fc && Wb) { + v = 0; + for (C = T.length; v < C; v++) { + Sa = T[v]; + z = na[Sa]; + Qb = Fc[Sa]; + if (Qb !== void 0) + for (S = 0; S < 3; S++) { + Sb = Qb[S]; + gc[ob] = Sb.u; + gc[ob + 1] = Sb.v; + ob = ob + 2 + } + } + v = 0; + for (C = U.length; v < C; v++) { + Sa = U[v]; + z = na[Sa]; + Qb = Fc[Sa]; + if (Qb !== void 0) + for (S = 0; S < 4; S++) { + Sb = Qb[S]; + gc[ob] = Sb.u; + gc[ob + 1] = Sb.v; + ob = + ob + 2 + } + } + if (ob > 0) { + g.bindBuffer(g.ARRAY_BUFFER, M.__webglUV2Buffer); + g.bufferData(g.ARRAY_BUFFER, gc, Ia) + } + } + if (yc) { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + kb[Pa] = ka; + kb[Pa + 1] = ka + 1; + kb[Pa + 2] = ka + 2; + Pa = Pa + 3; + Ka[Aa] = ka; + Ka[Aa + 1] = ka + 1; + Ka[Aa + 2] = ka; + Ka[Aa + 3] = ka + 2; + Ka[Aa + 4] = ka + 1; + Ka[Aa + 5] = ka + 2; + Aa = Aa + 6; + ka = ka + 3 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + kb[Pa] = ka; + kb[Pa + 1] = ka + 1; + kb[Pa + 2] = ka + 3; + kb[Pa + 3] = ka + 1; + kb[Pa + 4] = ka + 2; + kb[Pa + 5] = ka + 3; + Pa = Pa + 6; + Ka[Aa] = ka; + Ka[Aa + 1] = ka + 1; + Ka[Aa + 2] = ka; + Ka[Aa + 3] = ka + 3; + Ka[Aa + 4] = ka + 1; + Ka[Aa + 5] = ka + 2; + Ka[Aa + 6] = ka + + 2; + Ka[Aa + 7] = ka + 3; + Aa = Aa + 8; + ka = ka + 4 + } + g.bindBuffer(g.ELEMENT_ARRAY_BUFFER, M.__webglFaceBuffer); + g.bufferData(g.ELEMENT_ARRAY_BUFFER, kb, Ia); + g.bindBuffer(g.ELEMENT_ARRAY_BUFFER, M.__webglLineBuffer); + g.bufferData(g.ELEMENT_ARRAY_BUFFER, Ka, Ia) + } + if (Dc) { + S = 0; + for (Zb = Dc.length; S < Zb; S++) { + r = Dc[S]; + if (r.__original.needsUpdate) { + t = 0; + if (r.size === 1) + if (r.boundTo === void 0 || r.boundTo === "vertices") { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + r.array[t] = r.value[z.a]; + r.array[t + 1] = r.value[z.b]; + r.array[t + 2] = r.value[z.c]; + t = t + 3 + } + v = 0; + for (C = + U.length; v < C; v++) { + z = na[U[v]]; + r.array[t] = r.value[z.a]; + r.array[t + 1] = r.value[z.b]; + r.array[t + 2] = r.value[z.c]; + r.array[t + 3] = r.value[z.d]; + t = t + 4 + } + } else { + if (r.boundTo === "faces") { + v = 0; + for (C = T.length; v < C; v++) { + Ja = r.value[T[v]]; + r.array[t] = Ja; + r.array[t + 1] = Ja; + r.array[t + 2] = Ja; + t = t + 3 + } + v = 0; + for (C = U.length; v < C; v++) { + Ja = r.value[U[v]]; + r.array[t] = Ja; + r.array[t + 1] = Ja; + r.array[t + 2] = Ja; + r.array[t + 3] = Ja; + t = t + 4 + } + } + } + else if (r.size === 2) + if (r.boundTo === void 0 || r.boundTo === "vertices") { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + E = r.value[z.a]; + F = + r.value[z.b]; + G = r.value[z.c]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = F.x; + r.array[t + 3] = F.y; + r.array[t + 4] = G.x; + r.array[t + 5] = G.y; + t = t + 6 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + E = r.value[z.a]; + F = r.value[z.b]; + G = r.value[z.c]; + W = r.value[z.d]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = F.x; + r.array[t + 3] = F.y; + r.array[t + 4] = G.x; + r.array[t + 5] = G.y; + r.array[t + 6] = W.x; + r.array[t + 7] = W.y; + t = t + 8 + } + } else { + if (r.boundTo === "faces") { + v = 0; + for (C = T.length; v < C; v++) { + G = F = E = Ja = r.value[T[v]]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = F.x; + r.array[t + 3] = F.y; + r.array[t + 4] = G.x; + r.array[t + 5] = G.y; + t = t + 6 + } + v = 0; + for (C = U.length; v < C; v++) { + W = G = F = E = Ja = r.value[U[v]]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = F.x; + r.array[t + 3] = F.y; + r.array[t + 4] = G.x; + r.array[t + 5] = G.y; + r.array[t + 6] = W.x; + r.array[t + 7] = W.y; + t = t + 8 + } + } + } + else if (r.size === 3) { + var P; + P = r.type === "c" ? ["r", "g", "b"] : ["x", "y", "z"]; + if (r.boundTo === void 0 || r.boundTo === "vertices") { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + E = r.value[z.a]; + F = r.value[z.b]; + G = r.value[z.c]; + r.array[t] = E[P[0]]; + r.array[t + 1] = E[P[1]]; + r.array[t + + 2] = E[P[2]]; + r.array[t + 3] = F[P[0]]; + r.array[t + 4] = F[P[1]]; + r.array[t + 5] = F[P[2]]; + r.array[t + 6] = G[P[0]]; + r.array[t + 7] = G[P[1]]; + r.array[t + 8] = G[P[2]]; + t = t + 9 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + E = r.value[z.a]; + F = r.value[z.b]; + G = r.value[z.c]; + W = r.value[z.d]; + r.array[t] = E[P[0]]; + r.array[t + 1] = E[P[1]]; + r.array[t + 2] = E[P[2]]; + r.array[t + 3] = F[P[0]]; + r.array[t + 4] = F[P[1]]; + r.array[t + 5] = F[P[2]]; + r.array[t + 6] = G[P[0]]; + r.array[t + 7] = G[P[1]]; + r.array[t + 8] = G[P[2]]; + r.array[t + 9] = W[P[0]]; + r.array[t + 10] = W[P[1]]; + r.array[t + 11] = W[P[2]]; + t = t + 12 + } + } else if (r.boundTo === + "faces") { + v = 0; + for (C = T.length; v < C; v++) { + G = F = E = Ja = r.value[T[v]]; + r.array[t] = E[P[0]]; + r.array[t + 1] = E[P[1]]; + r.array[t + 2] = E[P[2]]; + r.array[t + 3] = F[P[0]]; + r.array[t + 4] = F[P[1]]; + r.array[t + 5] = F[P[2]]; + r.array[t + 6] = G[P[0]]; + r.array[t + 7] = G[P[1]]; + r.array[t + 8] = G[P[2]]; + t = t + 9 + } + v = 0; + for (C = U.length; v < C; v++) { + W = G = F = E = Ja = r.value[U[v]]; + r.array[t] = E[P[0]]; + r.array[t + 1] = E[P[1]]; + r.array[t + 2] = E[P[2]]; + r.array[t + 3] = F[P[0]]; + r.array[t + 4] = F[P[1]]; + r.array[t + 5] = F[P[2]]; + r.array[t + 6] = G[P[0]]; + r.array[t + 7] = G[P[1]]; + r.array[t + 8] = G[P[2]]; + r.array[t + + 9] = W[P[0]]; + r.array[t + 10] = W[P[1]]; + r.array[t + 11] = W[P[2]]; + t = t + 12 + } + } + } else if (r.size === 4) + if (r.boundTo === void 0 || r.boundTo === "vertices") { + v = 0; + for (C = T.length; v < C; v++) { + z = na[T[v]]; + E = r.value[z.a]; + F = r.value[z.b]; + G = r.value[z.c]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = E.z; + r.array[t + 3] = E.w; + r.array[t + 4] = F.x; + r.array[t + 5] = F.y; + r.array[t + 6] = F.z; + r.array[t + 7] = F.w; + r.array[t + 8] = G.x; + r.array[t + 9] = G.y; + r.array[t + 10] = G.z; + r.array[t + 11] = G.w; + t = t + 12 + } + v = 0; + for (C = U.length; v < C; v++) { + z = na[U[v]]; + E = r.value[z.a]; + F = r.value[z.b]; + G = r.value[z.c]; + W = r.value[z.d]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = E.z; + r.array[t + 3] = E.w; + r.array[t + 4] = F.x; + r.array[t + 5] = F.y; + r.array[t + 6] = F.z; + r.array[t + 7] = F.w; + r.array[t + 8] = G.x; + r.array[t + 9] = G.y; + r.array[t + 10] = G.z; + r.array[t + 11] = G.w; + r.array[t + 12] = W.x; + r.array[t + 13] = W.y; + r.array[t + 14] = W.z; + r.array[t + 15] = W.w; + t = t + 16 + } + } else if (r.boundTo === "faces") { + v = 0; + for (C = T.length; v < C; v++) { + G = F = E = Ja = r.value[T[v]]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = E.z; + r.array[t + 3] = E.w; + r.array[t + 4] = F.x; + r.array[t + 5] = F.y; + r.array[t + 6] = F.z; + r.array[t + + 7] = F.w; + r.array[t + 8] = G.x; + r.array[t + 9] = G.y; + r.array[t + 10] = G.z; + r.array[t + 11] = G.w; + t = t + 12 + } + v = 0; + for (C = U.length; v < C; v++) { + W = G = F = E = Ja = r.value[U[v]]; + r.array[t] = E.x; + r.array[t + 1] = E.y; + r.array[t + 2] = E.z; + r.array[t + 3] = E.w; + r.array[t + 4] = F.x; + r.array[t + 5] = F.y; + r.array[t + 6] = F.z; + r.array[t + 7] = F.w; + r.array[t + 8] = G.x; + r.array[t + 9] = G.y; + r.array[t + 10] = G.z; + r.array[t + 11] = G.w; + r.array[t + 12] = W.x; + r.array[t + 13] = W.y; + r.array[t + 14] = W.z; + r.array[t + 15] = W.w; + t = t + 16 + } + } + g.bindBuffer(g.ARRAY_BUFFER, r.buffer); + g.bufferData(g.ARRAY_BUFFER, r.array, Ia) + } + } + } + if (lc) { + delete M.__inittedArrays; + delete M.__colorArray; + delete M.__normalArray; + delete M.__tangentArray; + delete M.__uvArray; + delete M.__uv2Array; + delete M.__faceArray; + delete M.__vertexArray; + delete M.__lineArray; + delete M.__skinVertexAArray; + delete M.__skinVertexBArray; + delete M.__skinIndexArray; + delete M.__skinWeightArray + } + } + } + } + V.verticesNeedUpdate = false; + V.morphTargetsNeedUpdate = false; + V.elementsNeedUpdate = false; + V.uvsNeedUpdate = false; + V.normalsNeedUpdate = false; + V.colorsNeedUpdate = false; + V.tangetsNeedUpdate = false; + Ea.attributes && n(Ea) + } + else if (Va instanceof + THREE.Line) { + Ea = c(Va, $b); + Nb = Ea.attributes && m(Ea); + if (V.verticesNeedUpdate || V.colorsNeedUpdate || Nb) { + var pb = V, Gc = g.DYNAMIC_DRAW, hc = void 0, ic = void 0, tc = void 0, ia = void 0, uc = void 0, Kc = pb.vertices, Lc = pb.colors, Rc = Kc.length, Sc = Lc.length, vc = pb.__vertexArray, wc = pb.__colorArray, Tc = pb.colorsNeedUpdate, Hc = pb.__webglCustomAttributesList, xc = void 0, Mc = void 0, wa = void 0, Tb = void 0, Fa = void 0, aa = void 0; + if (pb.verticesNeedUpdate) { + for (hc = 0; hc < Rc; hc++) { + tc = Kc[hc]; + ia = hc * 3; + vc[ia] = tc.x; + vc[ia + 1] = tc.y; + vc[ia + 2] = tc.z + } + g.bindBuffer(g.ARRAY_BUFFER, + pb.__webglVertexBuffer); + g.bufferData(g.ARRAY_BUFFER, vc, Gc) + } + if (Tc) { + for (ic = 0; ic < Sc; ic++) { + uc = Lc[ic]; + ia = ic * 3; + wc[ia] = uc.r; + wc[ia + 1] = uc.g; + wc[ia + 2] = uc.b + } + g.bindBuffer(g.ARRAY_BUFFER, pb.__webglColorBuffer); + g.bufferData(g.ARRAY_BUFFER, wc, Gc) + } + if (Hc) { + xc = 0; + for (Mc = Hc.length; xc < Mc; xc++) { + aa = Hc[xc]; + if (aa.needsUpdate && (aa.boundTo === void 0 || aa.boundTo === "vertices")) { + ia = 0; + Tb = aa.value.length; + if (aa.size === 1) + for (wa = 0; wa < Tb; wa++) + aa.array[wa] = aa.value[wa]; + else if (aa.size === 2) + for (wa = 0; wa < Tb; wa++) { + Fa = aa.value[wa]; + aa.array[ia] = + Fa.x; + aa.array[ia + 1] = Fa.y; + ia = ia + 2 + } + else if (aa.size === 3) + if (aa.type === "c") + for (wa = 0; wa < Tb; wa++) { + Fa = aa.value[wa]; + aa.array[ia] = Fa.r; + aa.array[ia + 1] = Fa.g; + aa.array[ia + 2] = Fa.b; + ia = ia + 3 + } + else + for (wa = 0; wa < Tb; wa++) { + Fa = aa.value[wa]; + aa.array[ia] = Fa.x; + aa.array[ia + 1] = Fa.y; + aa.array[ia + 2] = Fa.z; + ia = ia + 3 + } + else if (aa.size === 4) + for (wa = 0; wa < Tb; wa++) { + Fa = aa.value[wa]; + aa.array[ia] = Fa.x; + aa.array[ia + 1] = Fa.y; + aa.array[ia + 2] = Fa.z; + aa.array[ia + 3] = Fa.w; + ia = ia + 4 + } + g.bindBuffer(g.ARRAY_BUFFER, aa.buffer); + g.bufferData(g.ARRAY_BUFFER, aa.array, Gc) + } + } + } + } + V.verticesNeedUpdate = + false; + V.colorsNeedUpdate = false; + Ea.attributes && n(Ea) + } else if (Va instanceof THREE.ParticleSystem) { + Ea = c(Va, $b); + Nb = Ea.attributes && m(Ea); + (V.verticesNeedUpdate || V.colorsNeedUpdate || Va.sortParticles || Nb) && f(V, g.DYNAMIC_DRAW, Va); + V.verticesNeedUpdate = false; + V.colorsNeedUpdate = false; + Ea.attributes && n(Ea) + } + } + }; + this.initMaterial = function(a, b, c, d) { + var e, f, h; + a instanceof THREE.MeshDepthMaterial ? h = "depth" : a instanceof THREE.MeshNormalMaterial ? h = "normal" : a instanceof THREE.MeshBasicMaterial ? h = "basic" : a instanceof THREE.MeshLambertMaterial ? + h = "lambert" : a instanceof THREE.MeshPhongMaterial ? h = "phong" : a instanceof THREE.LineBasicMaterial ? h = "basic" : a instanceof THREE.ParticleBasicMaterial && (h = "particle_basic"); + if (h) { + var i = THREE.ShaderLib[h]; + a.uniforms = THREE.UniformsUtils.clone(i.uniforms); + a.vertexShader = i.vertexShader; + a.fragmentShader = i.fragmentShader + } + var k, j, l, n, m; + k = n = m = i = 0; + for (j = b.length; k < j; k++) { + l = b[k]; + if (!l.onlyShadow) { + l instanceof THREE.DirectionalLight && n++; + l instanceof THREE.PointLight && m++; + l instanceof THREE.SpotLight && i++ + } + } + if (m + + i + n <= ya) { + j = n; + l = m; + n = i + } else { + j = Math.ceil(ya * n / (m + n)); + n = l = ya - j + } + var o = 0, i = 0; + for (m = b.length; i < m; i++) { + k = b[i]; + if (k.castShadow) { + k instanceof THREE.SpotLight && o++; + k instanceof THREE.DirectionalLight && !k.shadowCascade && o++ + } + } + var p; + a: { + m = a.fragmentShader; + k = a.vertexShader; + var i = a.uniforms, b = a.attributes, c = {map: !!a.map,envMap: !!a.envMap,lightMap: !!a.lightMap,vertexColors: a.vertexColors,fog: c,useFog: a.fog,sizeAttenuation: a.sizeAttenuation,skinning: a.skinning,maxBones: 50,morphTargets: a.morphTargets,morphNormals: a.morphNormals, + maxMorphTargets: this.maxMorphTargets,maxMorphNormals: this.maxMorphNormals,maxDirLights: j,maxPointLights: l,maxSpotLights: n,maxShadows: o,shadowMapEnabled: this.shadowMapEnabled && d.receiveShadow,shadowMapSoft: this.shadowMapSoft,shadowMapDebug: this.shadowMapDebug,shadowMapCascade: this.shadowMapCascade,alphaTest: a.alphaTest,metal: a.metal,perPixel: a.perPixel,wrapAround: a.wrapAround,doubleSided: d && d.doubleSided}, s, d = []; + if (h) + d.push(h); + else { + d.push(m); + d.push(k) + } + for (s in c) { + d.push(s); + d.push(c[s]) + } + h = d.join(); + s = 0; + for (d = Na.length; s < d; s++) + if (Na[s].code === h) { + p = Na[s].program; + break a + } + s = g.createProgram(); + d = ["precision " + B + " float;", Zb > 0 ? "#define VERTEX_TEXTURES" : "", D.gammaInput ? "#define GAMMA_INPUT" : "", D.gammaOutput ? "#define GAMMA_OUTPUT" : "", D.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", "#define MAX_DIR_LIGHTS " + c.maxDirLights, "#define MAX_POINT_LIGHTS " + c.maxPointLights, "#define MAX_SPOT_LIGHTS " + c.maxSpotLights, "#define MAX_SHADOWS " + c.maxShadows, "#define MAX_BONES " + c.maxBones, c.map ? "#define USE_MAP" : + "", c.envMap ? "#define USE_ENVMAP" : "", c.lightMap ? "#define USE_LIGHTMAP" : "", c.vertexColors ? "#define USE_COLOR" : "", c.skinning ? "#define USE_SKINNING" : "", c.morphTargets ? "#define USE_MORPHTARGETS" : "", c.morphNormals ? "#define USE_MORPHNORMALS" : "", c.perPixel ? "#define PHONG_PER_PIXEL" : "", c.wrapAround ? "#define WRAP_AROUND" : "", c.doubleSided ? "#define DOUBLE_SIDED" : "", c.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", c.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", c.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", c.shadowMapCascade ? + "#define SHADOWMAP_CASCADE" : "", c.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", "uniform mat4 objectMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\nattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\nattribute vec3 morphTarget0;\nattribute vec3 morphTarget1;\nattribute vec3 morphTarget2;\nattribute vec3 morphTarget3;\n#ifdef USE_MORPHNORMALS\nattribute vec3 morphNormal0;\nattribute vec3 morphNormal1;\nattribute vec3 morphNormal2;\nattribute vec3 morphNormal3;\n#else\nattribute vec3 morphTarget4;\nattribute vec3 morphTarget5;\nattribute vec3 morphTarget6;\nattribute vec3 morphTarget7;\n#endif\n#endif\n#ifdef USE_SKINNING\nattribute vec4 skinVertexA;\nattribute vec4 skinVertexB;\nattribute vec4 skinIndex;\nattribute vec4 skinWeight;\n#endif\n"].join("\n"); + j = ["precision " + B + " float;", "#define MAX_DIR_LIGHTS " + c.maxDirLights, "#define MAX_POINT_LIGHTS " + c.maxPointLights, "#define MAX_SPOT_LIGHTS " + c.maxSpotLights, "#define MAX_SHADOWS " + c.maxShadows, c.alphaTest ? "#define ALPHATEST " + c.alphaTest : "", D.gammaInput ? "#define GAMMA_INPUT" : "", D.gammaOutput ? "#define GAMMA_OUTPUT" : "", D.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", c.useFog && c.fog ? "#define USE_FOG" : "", c.useFog && c.fog instanceof THREE.FogExp2 ? "#define FOG_EXP2" : "", c.map ? "#define USE_MAP" : + "", c.envMap ? "#define USE_ENVMAP" : "", c.lightMap ? "#define USE_LIGHTMAP" : "", c.vertexColors ? "#define USE_COLOR" : "", c.metal ? "#define METAL" : "", c.perPixel ? "#define PHONG_PER_PIXEL" : "", c.wrapAround ? "#define WRAP_AROUND" : "", c.doubleSided ? "#define DOUBLE_SIDED" : "", c.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", c.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", c.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", c.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", "uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n"); + g.attachShader(s, q("fragment", j + m)); + g.attachShader(s, q("vertex", d + k)); + g.linkProgram(s); + g.getProgramParameter(s, g.LINK_STATUS) || console.error("Could not initialise shader\nVALIDATE_STATUS: " + g.getProgramParameter(s, g.VALIDATE_STATUS) + ", gl error [" + g.getError() + "]"); + s.uniforms = {}; + s.attributes = {}; + var u, d = ["viewMatrix", "modelViewMatrix", "projectionMatrix", "normalMatrix", "objectMatrix", "cameraPosition", "boneGlobalMatrices", "morphTargetInfluences"]; + for (u in i) + d.push(u); + u = d; + d = 0; + for (i = u.length; d < i; d++) { + m = + u[d]; + s.uniforms[m] = g.getUniformLocation(s, m) + } + d = ["position", "normal", "uv", "uv2", "tangent", "color", "skinVertexA", "skinVertexB", "skinIndex", "skinWeight"]; + for (u = 0; u < c.maxMorphTargets; u++) + d.push("morphTarget" + u); + for (u = 0; u < c.maxMorphNormals; u++) + d.push("morphNormal" + u); + for (p in b) + d.push(p); + p = d; + u = 0; + for (b = p.length; u < b; u++) { + c = p[u]; + s.attributes[c] = g.getAttribLocation(s, c) + } + s.id = Na.length; + Na.push({program: s,code: h}); + D.info.memory.programs = Na.length; + p = s + } + a.program = p; + p = a.program.attributes; + p.position >= 0 && g.enableVertexAttribArray(p.position); + p.color >= 0 && g.enableVertexAttribArray(p.color); + p.normal >= 0 && g.enableVertexAttribArray(p.normal); + p.tangent >= 0 && g.enableVertexAttribArray(p.tangent); + if (a.skinning && p.skinVertexA >= 0 && p.skinVertexB >= 0 && p.skinIndex >= 0 && p.skinWeight >= 0) { + g.enableVertexAttribArray(p.skinVertexA); + g.enableVertexAttribArray(p.skinVertexB); + g.enableVertexAttribArray(p.skinIndex); + g.enableVertexAttribArray(p.skinWeight) + } + if (a.attributes) + for (f in a.attributes) + p[f] !== void 0 && p[f] >= 0 && g.enableVertexAttribArray(p[f]); + if (a.morphTargets) { + a.numSupportedMorphTargets = + 0; + s = "morphTarget"; + for (f = 0; f < this.maxMorphTargets; f++) { + u = s + f; + if (p[u] >= 0) { + g.enableVertexAttribArray(p[u]); + a.numSupportedMorphTargets++ + } + } + } + if (a.morphNormals) { + a.numSupportedMorphNormals = 0; + s = "morphNormal"; + for (f = 0; f < this.maxMorphNormals; f++) { + u = s + f; + if (p[u] >= 0) { + g.enableVertexAttribArray(p[u]); + a.numSupportedMorphNormals++ + } + } + } + a.uniformsList = []; + for (e in a.uniforms) + a.uniformsList.push([a.uniforms[e], e]) + }; + this.setFaceCulling = function(a, b) { + if (a) { + !b || b === "ccw" ? g.frontFace(g.CCW) : g.frontFace(g.CW); + a === "back" ? g.cullFace(g.BACK) : + a === "front" ? g.cullFace(g.FRONT) : g.cullFace(g.FRONT_AND_BACK); + g.enable(g.CULL_FACE) + } else + g.disable(g.CULL_FACE) + }; + this.setObjectFaces = function(a) { + if (Q !== a.doubleSided) { + a.doubleSided ? g.disable(g.CULL_FACE) : g.enable(g.CULL_FACE); + Q = a.doubleSided + } + if (pa !== a.flipSided) { + a.flipSided ? g.frontFace(g.CW) : g.frontFace(g.CCW); + pa = a.flipSided + } + }; + this.setDepthTest = function(a) { + if (La !== a) { + a ? g.enable(g.DEPTH_TEST) : g.disable(g.DEPTH_TEST); + La = a + } + }; + this.setDepthWrite = function(a) { + if (Oa !== a) { + g.depthMask(a); + Oa = a + } + }; + this.setBlending = + function(a, b, c, d) { + if (a !== O) { + switch (a) { + case THREE.NoBlending: + g.disable(g.BLEND); + break; + case THREE.AdditiveBlending: + g.enable(g.BLEND); + g.blendEquation(g.FUNC_ADD); + g.blendFunc(g.SRC_ALPHA, g.ONE); + break; + case THREE.SubtractiveBlending: + g.enable(g.BLEND); + g.blendEquation(g.FUNC_ADD); + g.blendFunc(g.ZERO, g.ONE_MINUS_SRC_COLOR); + break; + case THREE.MultiplyBlending: + g.enable(g.BLEND); + g.blendEquation(g.FUNC_ADD); + g.blendFunc(g.ZERO, g.SRC_COLOR); + break; + case THREE.CustomBlending: + g.enable(g.BLEND); + break; + default: + g.enable(g.BLEND); + g.blendEquationSeparate(g.FUNC_ADD, g.FUNC_ADD); + g.blendFuncSeparate(g.SRC_ALPHA, g.ONE_MINUS_SRC_ALPHA, g.ONE, g.ONE_MINUS_SRC_ALPHA) + } + O = a + } + if (a === THREE.CustomBlending) { + if (b !== sa) { + g.blendEquation(u(b)); + sa = b + } + if (c !== Ga || d !== Ha) { + g.blendFunc(u(c), u(d)); + Ga = c; + Ha = d + } + } else + Ha = Ga = sa = null + }; + this.setTexture = function(a, b) { + if (a.needsUpdate) { + if (!a.__webglInit) { + a.__webglInit = true; + a.__webglTexture = g.createTexture(); + D.info.memory.textures++ + } + g.activeTexture(g.TEXTURE0 + b); + g.bindTexture(g.TEXTURE_2D, a.__webglTexture); + g.pixelStorei(g.UNPACK_PREMULTIPLY_ALPHA_WEBGL, + a.premultiplyAlpha); + var c = a.image, d = (c.width & c.width - 1) === 0 && (c.height & c.height - 1) === 0, e = u(a.format), f = u(a.type); + w(g.TEXTURE_2D, a, d); + a instanceof THREE.DataTexture ? g.texImage2D(g.TEXTURE_2D, 0, e, c.width, c.height, 0, e, f, c.data) : g.texImage2D(g.TEXTURE_2D, 0, e, e, f, a.image); + a.generateMipmaps && d && g.generateMipmap(g.TEXTURE_2D); + a.needsUpdate = false; + if (a.onUpdate) + a.onUpdate() + } else { + g.activeTexture(g.TEXTURE0 + b); + g.bindTexture(g.TEXTURE_2D, a.__webglTexture) + } + }; + this.setRenderTarget = function(a) { + var b = a instanceof + THREE.WebGLRenderTargetCube; + if (a && !a.__webglFramebuffer) { + if (a.depthBuffer === void 0) + a.depthBuffer = true; + if (a.stencilBuffer === void 0) + a.stencilBuffer = true; + a.__webglTexture = g.createTexture(); + var c = (a.width & a.width - 1) === 0 && (a.height & a.height - 1) === 0, d = u(a.format), e = u(a.type); + if (b) { + a.__webglFramebuffer = []; + a.__webglRenderbuffer = []; + g.bindTexture(g.TEXTURE_CUBE_MAP, a.__webglTexture); + w(g.TEXTURE_CUBE_MAP, a, c); + for (var f = 0; f < 6; f++) { + a.__webglFramebuffer[f] = g.createFramebuffer(); + a.__webglRenderbuffer[f] = g.createRenderbuffer(); + g.texImage2D(g.TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, d, a.width, a.height, 0, d, e, null); + var h = a, i = g.TEXTURE_CUBE_MAP_POSITIVE_X + f; + g.bindFramebuffer(g.FRAMEBUFFER, a.__webglFramebuffer[f]); + g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, i, h.__webglTexture, 0); + A(a.__webglRenderbuffer[f], a) + } + c && g.generateMipmap(g.TEXTURE_CUBE_MAP) + } else { + a.__webglFramebuffer = g.createFramebuffer(); + a.__webglRenderbuffer = g.createRenderbuffer(); + g.bindTexture(g.TEXTURE_2D, a.__webglTexture); + w(g.TEXTURE_2D, a, c); + g.texImage2D(g.TEXTURE_2D, + 0, d, a.width, a.height, 0, d, e, null); + d = g.TEXTURE_2D; + g.bindFramebuffer(g.FRAMEBUFFER, a.__webglFramebuffer); + g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, d, a.__webglTexture, 0); + A(a.__webglRenderbuffer, a); + c && g.generateMipmap(g.TEXTURE_2D) + } + b ? g.bindTexture(g.TEXTURE_CUBE_MAP, null) : g.bindTexture(g.TEXTURE_2D, null); + g.bindRenderbuffer(g.RENDERBUFFER, null); + g.bindFramebuffer(g.FRAMEBUFFER, null) + } + if (a) { + b = b ? a.__webglFramebuffer[a.activeCubeFace] : a.__webglFramebuffer; + c = a.width; + a = a.height; + e = d = 0 + } else { + b = null; + c = Xb; + a = Gb; + d = Eb; + e = Fb + } + if (b !== Da) { + g.bindFramebuffer(g.FRAMEBUFFER, b); + g.viewport(d, e, c, a); + Da = b + } + jc = c; + kc = a + } +}; +THREE.WebGLRenderTarget = function(a, b, c) { + this.width = a; + this.height = b; + c = c || {}; + this.wrapS = c.wrapS !== void 0 ? c.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = c.wrapT !== void 0 ? c.wrapT : THREE.ClampToEdgeWrapping; + this.magFilter = c.magFilter !== void 0 ? c.magFilter : THREE.LinearFilter; + this.minFilter = c.minFilter !== void 0 ? c.minFilter : THREE.LinearMipMapLinearFilter; + this.offset = new THREE.Vector2(0, 0); + this.repeat = new THREE.Vector2(1, 1); + this.format = c.format !== void 0 ? c.format : THREE.RGBAFormat; + this.type = c.type !== void 0 ? c.type : + THREE.UnsignedByteType; + this.depthBuffer = c.depthBuffer !== void 0 ? c.depthBuffer : true; + this.stencilBuffer = c.stencilBuffer !== void 0 ? c.stencilBuffer : true; + this.generateMipmaps = true +}; +THREE.WebGLRenderTarget.prototype.clone = function() { + var a = new THREE.WebGLRenderTarget(this.width, this.height); + a.wrapS = this.wrapS; + a.wrapT = this.wrapT; + a.magFilter = this.magFilter; + a.minFilter = this.minFilter; + a.offset.copy(this.offset); + a.repeat.copy(this.repeat); + a.format = this.format; + a.type = this.type; + a.depthBuffer = this.depthBuffer; + a.stencilBuffer = this.stencilBuffer; + return a +}; +THREE.WebGLRenderTargetCube = function(a, b, c) { + THREE.WebGLRenderTarget.call(this, a, b, c); + this.activeCubeFace = 0 +}; +THREE.WebGLRenderTargetCube.prototype = new THREE.WebGLRenderTarget; +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; +THREE.RenderableVertex = function() { + this.positionWorld = new THREE.Vector3; + this.positionScreen = new THREE.Vector4; + this.visible = true +}; +THREE.RenderableVertex.prototype.copy = function(a) { + this.positionWorld.copy(a.positionWorld); + this.positionScreen.copy(a.positionScreen) +}; +THREE.RenderableFace3 = function() { + this.v1 = new THREE.RenderableVertex; + this.v2 = new THREE.RenderableVertex; + this.v3 = new THREE.RenderableVertex; + this.centroidWorld = new THREE.Vector3; + this.centroidScreen = new THREE.Vector3; + this.normalWorld = new THREE.Vector3; + this.vertexNormalsWorld = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3]; + this.faceMaterial = this.material = null; + this.uvs = [[]]; + this.z = null +}; +THREE.RenderableFace4 = function() { + this.v1 = new THREE.RenderableVertex; + this.v2 = new THREE.RenderableVertex; + this.v3 = new THREE.RenderableVertex; + this.v4 = new THREE.RenderableVertex; + this.centroidWorld = new THREE.Vector3; + this.centroidScreen = new THREE.Vector3; + this.normalWorld = new THREE.Vector3; + this.vertexNormalsWorld = [new THREE.Vector3, new THREE.Vector3, new THREE.Vector3, new THREE.Vector3]; + this.faceMaterial = this.material = null; + this.uvs = [[]]; + this.z = null +}; +THREE.RenderableObject = function() { + this.z = this.object = null +}; +THREE.RenderableLine = function() { + this.z = null; + this.v1 = new THREE.RenderableVertex; + this.v2 = new THREE.RenderableVertex; + this.material = null +}; +THREE.GeometryUtils = {merge: function(a, b) { + for (var c, d, e = a.vertices.length, f = b instanceof THREE.Mesh ? b.geometry : b, h = a.vertices, i = f.vertices, l = a.faces, k = f.faces, j = a.faceVertexUvs[0], m = f.faceVertexUvs[0], n = {}, o = 0; o < a.materials.length; o++) + n[a.materials[o].id] = o; + if (b instanceof THREE.Mesh) { + b.matrixAutoUpdate && b.updateMatrix(); + c = b.matrix; + d = new THREE.Matrix4; + d.extractRotation(c, b.scale) + } + for (var o = 0, s = i.length; o < s; o++) { + var p = i[o].clone(); + c && c.multiplyVector3(p); + h.push(p) + } + o = 0; + for (s = k.length; o < s; o++) { + var h = + k[o], q, w, A = h.vertexNormals, y = h.vertexColors; + h instanceof THREE.Face3 ? q = new THREE.Face3(h.a + e, h.b + e, h.c + e) : h instanceof THREE.Face4 && (q = new THREE.Face4(h.a + e, h.b + e, h.c + e, h.d + e)); + q.normal.copy(h.normal); + d && d.multiplyVector3(q.normal); + i = 0; + for (p = A.length; i < p; i++) { + w = A[i].clone(); + d && d.multiplyVector3(w); + q.vertexNormals.push(w) + } + q.color.copy(h.color); + i = 0; + for (p = y.length; i < p; i++) { + w = y[i]; + q.vertexColors.push(w.clone()) + } + if (h.materialIndex !== void 0) { + i = f.materials[h.materialIndex]; + p = i.id; + y = n[p]; + if (y === void 0) { + y = + a.materials.length; + n[p] = y; + a.materials.push(i) + } + q.materialIndex = y + } + q.centroid.copy(h.centroid); + c && c.multiplyVector3(q.centroid); + l.push(q) + } + o = 0; + for (s = m.length; o < s; o++) { + c = m[o]; + d = []; + i = 0; + for (p = c.length; i < p; i++) + d.push(new THREE.UV(c[i].u, c[i].v)); + j.push(d) + } + },clone: function(a) { + var b = new THREE.Geometry, c, d = a.vertices, e = a.faces, f = a.faceVertexUvs[0]; + if (a.materials) + b.materials = a.materials.slice(); + a = 0; + for (c = d.length; a < c; a++) + b.vertices.push(d[a].clone()); + a = 0; + for (c = e.length; a < c; a++) + b.faces.push(e[a].clone()); + a = 0; + for (c = f.length; a < c; a++) { + for (var d = f[a], e = [], h = 0, i = d.length; h < i; h++) + e.push(new THREE.UV(d[h].u, d[h].v)); + b.faceVertexUvs[0].push(e) + } + return b + },randomPointInTriangle: function(a, b, c) { + var d, e, f, h = new THREE.Vector3, i = THREE.GeometryUtils.__v1; + d = THREE.GeometryUtils.random(); + e = THREE.GeometryUtils.random(); + if (d + e > 1) { + d = 1 - d; + e = 1 - e + } + f = 1 - d - e; + h.copy(a); + h.multiplyScalar(d); + i.copy(b); + i.multiplyScalar(e); + h.addSelf(i); + i.copy(c); + i.multiplyScalar(f); + h.addSelf(i); + return h + },randomPointInFace: function(a, b, c) { + var d, e, f; + if (a instanceof + THREE.Face3) { + d = b.vertices[a.a]; + e = b.vertices[a.b]; + f = b.vertices[a.c]; + return THREE.GeometryUtils.randomPointInTriangle(d, e, f) + } + if (a instanceof THREE.Face4) { + d = b.vertices[a.a]; + e = b.vertices[a.b]; + f = b.vertices[a.c]; + var b = b.vertices[a.d], h; + if (c) + if (a._area1 && a._area2) { + c = a._area1; + h = a._area2 + } else { + c = THREE.GeometryUtils.triangleArea(d, e, b); + h = THREE.GeometryUtils.triangleArea(e, f, b); + a._area1 = c; + a._area2 = h + } + else { + c = THREE.GeometryUtils.triangleArea(d, e, b); + h = THREE.GeometryUtils.triangleArea(e, f, b) + } + return THREE.GeometryUtils.random() * + (c + h) < c ? THREE.GeometryUtils.randomPointInTriangle(d, e, b) : THREE.GeometryUtils.randomPointInTriangle(e, f, b) + } + },randomPointsInGeometry: function(a, b) { + function c(a) { + function b(c, d) { + if (d < c) + return c; + var e = c + Math.floor((d - c) / 2); + return k[e] > a ? b(c, e - 1) : k[e] < a ? b(e + 1, d) : e + } + return b(0, k.length - 1) + } + var d, e, f = a.faces, h = a.vertices, i = f.length, l = 0, k = [], j, m, n, o; + for (e = 0; e < i; e++) { + d = f[e]; + if (d instanceof THREE.Face3) { + j = h[d.a]; + m = h[d.b]; + n = h[d.c]; + d._area = THREE.GeometryUtils.triangleArea(j, m, n) + } else if (d instanceof THREE.Face4) { + j = + h[d.a]; + m = h[d.b]; + n = h[d.c]; + o = h[d.d]; + d._area1 = THREE.GeometryUtils.triangleArea(j, m, o); + d._area2 = THREE.GeometryUtils.triangleArea(m, n, o); + d._area = d._area1 + d._area2 + } + l = l + d._area; + k[e] = l + } + d = []; + for (e = 0; e < b; e++) { + h = THREE.GeometryUtils.random() * l; + h = c(h); + d[e] = THREE.GeometryUtils.randomPointInFace(f[h], a, true) + } + return d + },triangleArea: function(a, b, c) { + var d, e = THREE.GeometryUtils.__v1; + e.sub(a, b); + d = e.length(); + e.sub(a, c); + a = e.length(); + e.sub(b, c); + c = e.length(); + b = 0.5 * (d + a + c); + return Math.sqrt(b * (b - d) * (b - a) * (b - c)) + },center: function(a) { + a.computeBoundingBox(); + var b = a.boundingBox, c = new THREE.Vector3; + c.add(b.min, b.max); + c.multiplyScalar(-0.5); + a.applyMatrix((new THREE.Matrix4).makeTranslation(c.x, c.y, c.z)); + a.computeBoundingBox(); + return c + },normalizeUVs: function(a) { + for (var a = a.faceVertexUvs[0], b = 0, c = a.length; b < c; b++) + for (var d = a[b], e = 0, f = d.length; e < f; e++) { + if (d[e].u !== 1) + d[e].u = d[e].u - Math.floor(d[e].u); + if (d[e].v !== 1) + d[e].v = d[e].v - Math.floor(d[e].v) + } + },triangulateQuads: function(a) { + var b, c, d, e, f = [], h = [], i = []; + b = 0; + for (c = a.faceUvs.length; b < c; b++) + h[b] = []; + b = 0; + for (c = a.faceVertexUvs.length; b < + c; b++) + i[b] = []; + b = 0; + for (c = a.faces.length; b < c; b++) { + d = a.faces[b]; + if (d instanceof THREE.Face4) { + e = d.a; + var l = d.b, k = d.c, j = d.d, m = new THREE.Face3, n = new THREE.Face3; + m.color.copy(d.color); + n.color.copy(d.color); + m.materialIndex = d.materialIndex; + n.materialIndex = d.materialIndex; + m.a = e; + m.b = l; + m.c = j; + n.a = l; + n.b = k; + n.c = j; + if (d.vertexColors.length === 4) { + m.vertexColors[0] = d.vertexColors[0].clone(); + m.vertexColors[1] = d.vertexColors[1].clone(); + m.vertexColors[2] = d.vertexColors[3].clone(); + n.vertexColors[0] = d.vertexColors[1].clone(); + n.vertexColors[1] = d.vertexColors[2].clone(); + n.vertexColors[2] = d.vertexColors[3].clone() + } + f.push(m, n); + d = 0; + for (e = a.faceVertexUvs.length; d < e; d++) + if (a.faceVertexUvs[d].length) { + m = a.faceVertexUvs[d][b]; + l = m[1]; + k = m[2]; + j = m[3]; + m = [m[0].clone(), l.clone(), j.clone()]; + l = [l.clone(), k.clone(), j.clone()]; + i[d].push(m, l) + } + d = 0; + for (e = a.faceUvs.length; d < e; d++) + if (a.faceUvs[d].length) { + l = a.faceUvs[d][b]; + h[d].push(l, l) + } + } else { + f.push(d); + d = 0; + for (e = a.faceUvs.length; d < e; d++) + h[d].push(a.faceUvs[d]); + d = 0; + for (e = a.faceVertexUvs.length; d < + e; d++) + i[d].push(a.faceVertexUvs[d]) + } + } + a.faces = f; + a.faceUvs = h; + a.faceVertexUvs = i; + a.computeCentroids(); + a.computeFaceNormals(); + a.computeVertexNormals(); + a.hasTangents && a.computeTangents() + },explode: function(a) { + for (var b = [], c = 0, d = a.faces.length; c < d; c++) { + var e = b.length, f = a.faces[c]; + if (f instanceof THREE.Face4) { + var h = f.a, i = f.b, l = f.c, h = a.vertices[h], i = a.vertices[i], l = a.vertices[l], k = a.vertices[f.d]; + b.push(h.clone()); + b.push(i.clone()); + b.push(l.clone()); + b.push(k.clone()); + f.a = e; + f.b = e + 1; + f.c = e + 2; + f.d = e + 3 + } else { + h = f.a; + i = f.b; + l = f.c; + h = a.vertices[h]; + i = a.vertices[i]; + l = a.vertices[l]; + b.push(h.clone()); + b.push(i.clone()); + b.push(l.clone()); + f.a = e; + f.b = e + 1; + f.c = e + 2 + } + } + a.vertices = b; + delete a.__tmpVertices + },tessellate: function(a, b) { + var c, d, e, f, h, i, l, k, j, m, n, o, s, p, q, w, A, y, u, H = [], B = []; + c = 0; + for (d = a.faceVertexUvs.length; c < d; c++) + B[c] = []; + c = 0; + for (d = a.faces.length; c < d; c++) { + e = a.faces[c]; + if (e instanceof THREE.Face3) { + f = e.a; + h = e.b; + i = e.c; + k = a.vertices[f]; + j = a.vertices[h]; + m = a.vertices[i]; + o = k.distanceTo(j); + s = j.distanceTo(m); + n = k.distanceTo(m); + if (o > + b || s > b || n > b) { + l = a.vertices.length; + y = e.clone(); + u = e.clone(); + if (o >= s && o >= n) { + k = k.clone(); + k.lerpSelf(j, 0.5); + y.a = f; + y.b = l; + y.c = i; + u.a = l; + u.b = h; + u.c = i; + if (e.vertexNormals.length === 3) { + f = e.vertexNormals[0].clone(); + f.lerpSelf(e.vertexNormals[1], 0.5); + y.vertexNormals[1].copy(f); + u.vertexNormals[0].copy(f) + } + if (e.vertexColors.length === 3) { + f = e.vertexColors[0].clone(); + f.lerpSelf(e.vertexColors[1], 0.5); + y.vertexColors[1].copy(f); + u.vertexColors[0].copy(f) + } + e = 0 + } else if (s >= o && s >= n) { + k = j.clone(); + k.lerpSelf(m, 0.5); + y.a = f; + y.b = h; + y.c = + l; + u.a = l; + u.b = i; + u.c = f; + if (e.vertexNormals.length === 3) { + f = e.vertexNormals[1].clone(); + f.lerpSelf(e.vertexNormals[2], 0.5); + y.vertexNormals[2].copy(f); + u.vertexNormals[0].copy(f); + u.vertexNormals[1].copy(e.vertexNormals[2]); + u.vertexNormals[2].copy(e.vertexNormals[0]) + } + if (e.vertexColors.length === 3) { + f = e.vertexColors[1].clone(); + f.lerpSelf(e.vertexColors[2], 0.5); + y.vertexColors[2].copy(f); + u.vertexColors[0].copy(f); + u.vertexColors[1].copy(e.vertexColors[2]); + u.vertexColors[2].copy(e.vertexColors[0]) + } + e = 1 + } else { + k = k.clone(); + k.lerpSelf(m, 0.5); + y.a = f; + y.b = h; + y.c = l; + u.a = l; + u.b = h; + u.c = i; + if (e.vertexNormals.length === 3) { + f = e.vertexNormals[0].clone(); + f.lerpSelf(e.vertexNormals[2], 0.5); + y.vertexNormals[2].copy(f); + u.vertexNormals[0].copy(f) + } + if (e.vertexColors.length === 3) { + f = e.vertexColors[0].clone(); + f.lerpSelf(e.vertexColors[2], 0.5); + y.vertexColors[2].copy(f); + u.vertexColors[0].copy(f) + } + e = 2 + } + H.push(y, u); + a.vertices.push(k); + f = 0; + for (h = a.faceVertexUvs.length; f < h; f++) + if (a.faceVertexUvs[f].length) { + k = a.faceVertexUvs[f][c]; + u = k[0]; + i = k[1]; + y = k[2]; + if (e === + 0) { + j = u.clone(); + j.lerpSelf(i, 0.5); + k = [u.clone(), j.clone(), y.clone()]; + i = [j.clone(), i.clone(), y.clone()] + } else if (e === 1) { + j = i.clone(); + j.lerpSelf(y, 0.5); + k = [u.clone(), i.clone(), j.clone()]; + i = [j.clone(), y.clone(), u.clone()] + } else { + j = u.clone(); + j.lerpSelf(y, 0.5); + k = [u.clone(), i.clone(), j.clone()]; + i = [j.clone(), i.clone(), y.clone()] + } + B[f].push(k, i) + } + } else { + H.push(e); + f = 0; + for (h = a.faceVertexUvs.length; f < h; f++) + B[f].push(a.faceVertexUvs[f][c]) + } + } else { + f = e.a; + h = e.b; + i = e.c; + l = e.d; + k = a.vertices[f]; + j = a.vertices[h]; + m = a.vertices[i]; + n = + a.vertices[l]; + o = k.distanceTo(j); + s = j.distanceTo(m); + p = m.distanceTo(n); + q = k.distanceTo(n); + if (o > b || s > b || p > b || q > b) { + w = a.vertices.length; + A = a.vertices.length + 1; + y = e.clone(); + u = e.clone(); + if (o >= s && o >= p && o >= q || p >= s && p >= o && p >= q) { + o = k.clone(); + o.lerpSelf(j, 0.5); + j = m.clone(); + j.lerpSelf(n, 0.5); + y.a = f; + y.b = w; + y.c = A; + y.d = l; + u.a = w; + u.b = h; + u.c = i; + u.d = A; + if (e.vertexNormals.length === 4) { + f = e.vertexNormals[0].clone(); + f.lerpSelf(e.vertexNormals[1], 0.5); + h = e.vertexNormals[2].clone(); + h.lerpSelf(e.vertexNormals[3], 0.5); + y.vertexNormals[1].copy(f); + y.vertexNormals[2].copy(h); + u.vertexNormals[0].copy(f); + u.vertexNormals[3].copy(h) + } + if (e.vertexColors.length === 4) { + f = e.vertexColors[0].clone(); + f.lerpSelf(e.vertexColors[1], 0.5); + h = e.vertexColors[2].clone(); + h.lerpSelf(e.vertexColors[3], 0.5); + y.vertexColors[1].copy(f); + y.vertexColors[2].copy(h); + u.vertexColors[0].copy(f); + u.vertexColors[3].copy(h) + } + e = 0 + } else { + o = j.clone(); + o.lerpSelf(m, 0.5); + j = n.clone(); + j.lerpSelf(k, 0.5); + y.a = f; + y.b = h; + y.c = w; + y.d = A; + u.a = A; + u.b = w; + u.c = i; + u.d = l; + if (e.vertexNormals.length === 4) { + f = e.vertexNormals[1].clone(); + f.lerpSelf(e.vertexNormals[2], 0.5); + h = e.vertexNormals[3].clone(); + h.lerpSelf(e.vertexNormals[0], 0.5); + y.vertexNormals[2].copy(f); + y.vertexNormals[3].copy(h); + u.vertexNormals[0].copy(h); + u.vertexNormals[1].copy(f) + } + if (e.vertexColors.length === 4) { + f = e.vertexColors[1].clone(); + f.lerpSelf(e.vertexColors[2], 0.5); + h = e.vertexColors[3].clone(); + h.lerpSelf(e.vertexColors[0], 0.5); + y.vertexColors[2].copy(f); + y.vertexColors[3].copy(h); + u.vertexColors[0].copy(h); + u.vertexColors[1].copy(f) + } + e = 1 + } + H.push(y, u); + a.vertices.push(o, j); + f = 0; + for (h = a.faceVertexUvs.length; f < h; f++) + if (a.faceVertexUvs[f].length) { + k = a.faceVertexUvs[f][c]; + u = k[0]; + i = k[1]; + y = k[2]; + k = k[3]; + if (e === 0) { + j = u.clone(); + j.lerpSelf(i, 0.5); + m = y.clone(); + m.lerpSelf(k, 0.5); + u = [u.clone(), j.clone(), m.clone(), k.clone()]; + i = [j.clone(), i.clone(), y.clone(), m.clone()] + } else { + j = i.clone(); + j.lerpSelf(y, 0.5); + m = k.clone(); + m.lerpSelf(u, 0.5); + u = [u.clone(), i.clone(), j.clone(), m.clone()]; + i = [m.clone(), j.clone(), y.clone(), k.clone()] + } + B[f].push(u, i) + } + } else { + H.push(e); + f = 0; + for (h = a.faceVertexUvs.length; f < h; f++) + B[f].push(a.faceVertexUvs[f][c]) + } + } + } + a.faces = + H; + a.faceVertexUvs = B + }}; +THREE.GeometryUtils.random = THREE.Math.random16; +THREE.GeometryUtils.__v1 = new THREE.Vector3; +THREE.ImageUtils = {crossOrigin: "anonymous",loadTexture: function(a, b, c) { + var d = new Image, e = new THREE.Texture(d, b); + d.onload = function() { + e.needsUpdate = true; + c && c(this) + }; + d.crossOrigin = this.crossOrigin; + d.src = a; + return e + },loadTextureCube: function(a, b, c) { + var d, e = [], f = new THREE.Texture(e, b), b = e.loadCount = 0; + for (d = a.length; b < d; ++b) { + e[b] = new Image; + e[b].onload = function() { + e.loadCount = e.loadCount + 1; + if (e.loadCount === 6) + f.needsUpdate = true; + c && c(this) + }; + e[b].crossOrigin = this.crossOrigin; + e[b].src = a[b] + } + return f + },getNormalMap: function(a, + b) { + var c = function(a) { + var b = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); + return [a[0] / b, a[1] / b, a[2] / b] + }, b = b | 1, d = a.width, e = a.height, f = document.createElement("canvas"); + f.width = d; + f.height = e; + var h = f.getContext("2d"); + h.drawImage(a, 0, 0); + for (var i = h.getImageData(0, 0, d, e).data, l = h.createImageData(d, e), k = l.data, j = 0; j < d; j++) + for (var m = 0; m < e; m++) { + var n = m - 1 < 0 ? 0 : m - 1, o = m + 1 > e - 1 ? e - 1 : m + 1, s = j - 1 < 0 ? 0 : j - 1, p = j + 1 > d - 1 ? d - 1 : j + 1, q = [], w = [0, 0, i[(m * d + j) * 4] / 255 * b]; + q.push([-1, 0, i[(m * d + s) * 4] / 255 * b]); + q.push([-1, -1, i[(n * d + s) * 4] / 255 * b]); + q.push([0, + -1, i[(n * d + j) * 4] / 255 * b]); + q.push([1, -1, i[(n * d + p) * 4] / 255 * b]); + q.push([1, 0, i[(m * d + p) * 4] / 255 * b]); + q.push([1, 1, i[(o * d + p) * 4] / 255 * b]); + q.push([0, 1, i[(o * d + j) * 4] / 255 * b]); + q.push([-1, 1, i[(o * d + s) * 4] / 255 * b]); + n = []; + s = q.length; + for (o = 0; o < s; o++) { + var p = q[o], A = q[(o + 1) % s], p = [p[0] - w[0], p[1] - w[1], p[2] - w[2]], A = [A[0] - w[0], A[1] - w[1], A[2] - w[2]]; + n.push(c([p[1] * A[2] - p[2] * A[1], p[2] * A[0] - p[0] * A[2], p[0] * A[1] - p[1] * A[0]])) + } + q = [0, 0, 0]; + for (o = 0; o < n.length; o++) { + q[0] = q[0] + n[o][0]; + q[1] = q[1] + n[o][1]; + q[2] = q[2] + n[o][2] + } + q[0] = q[0] / n.length; + q[1] = + q[1] / n.length; + q[2] = q[2] / n.length; + w = (m * d + j) * 4; + k[w] = (q[0] + 1) / 2 * 255 | 0; + k[w + 1] = (q[1] + 0.5) * 255 | 0; + k[w + 2] = q[2] * 255 | 0; + k[w + 3] = 255 + } + h.putImageData(l, 0, 0); + return f + },generateDataTexture: function(a, b, c) { + for (var d = a * b, e = new Uint8Array(3 * d), f = Math.floor(c.r * 255), h = Math.floor(c.g * 255), c = Math.floor(c.b * 255), i = 0; i < d; i++) { + e[i * 3] = f; + e[i * 3 + 1] = h; + e[i * 3 + 2] = c + } + a = new THREE.DataTexture(e, a, b, THREE.RGBFormat); + a.needsUpdate = true; + return a + }}; +THREE.SceneUtils = {showHierarchy: function(a, b) { + THREE.SceneUtils.traverseHierarchy(a, function(a) { + a.visible = b + }) + },traverseHierarchy: function(a, b) { + var c, d, e = a.children.length; + for (d = 0; d < e; d++) { + c = a.children[d]; + b(c); + THREE.SceneUtils.traverseHierarchy(c, b) + } + },createMultiMaterialObject: function(a, b) { + var c, d = b.length, e = new THREE.Object3D; + for (c = 0; c < d; c++) { + var f = new THREE.Mesh(a, b[c]); + e.add(f) + } + return e + },cloneObject: function(a) { + var b; + if (a instanceof THREE.Mesh) + b = new THREE.Mesh(a.geometry, a.material); + else if (a instanceof + THREE.Line) + b = new THREE.Line(a.geometry, a.material, a.type); + else if (a instanceof THREE.Ribbon) + b = new THREE.Ribbon(a.geometry, a.material); + else if (a instanceof THREE.ParticleSystem) { + b = new THREE.ParticleSystem(a.geometry, a.material); + b.sortParticles = a.sortParticles + } else if (a instanceof THREE.Particle) + b = new THREE.Particle(a.material); + else if (a instanceof THREE.Sprite) { + b = new THREE.Sprite({}); + b.color.copy(a.color); + b.map = a.map; + b.blending = a.blending; + b.useScreenCoordinates = a.useScreenCoordinates; + b.mergeWith3D = + a.mergeWith3D; + b.affectedByDistance = a.affectedByDistance; + b.scaleByViewport = a.scaleByViewport; + b.alignment = a.alignment; + b.rotation3d.copy(a.rotation3d); + b.rotation = a.rotation; + b.opacity = a.opacity; + b.uvOffset.copy(a.uvOffset); + b.uvScale.copy(a.uvScale) + } else + a instanceof THREE.LOD ? b = new THREE.LOD : a instanceof THREE.Object3D && (b = new THREE.Object3D); + b.name = a.name; + b.parent = a.parent; + b.up.copy(a.up); + b.position.copy(a.position); + b.rotation instanceof THREE.Vector3 && b.rotation.copy(a.rotation); + b.eulerOrder = a.eulerOrder; + b.scale.copy(a.scale); + b.dynamic = a.dynamic; + b.doubleSided = a.doubleSided; + b.flipSided = a.flipSided; + b.renderDepth = a.renderDepth; + b.rotationAutoUpdate = a.rotationAutoUpdate; + b.matrix.copy(a.matrix); + b.matrixWorld.copy(a.matrixWorld); + b.matrixRotationWorld.copy(a.matrixRotationWorld); + b.matrixAutoUpdate = a.matrixAutoUpdate; + b.matrixWorldNeedsUpdate = a.matrixWorldNeedsUpdate; + b.quaternion.copy(a.quaternion); + b.useQuaternion = a.useQuaternion; + b.boundRadius = a.boundRadius; + b.boundRadiusScale = a.boundRadiusScale; + b.visible = a.visible; + b.castShadow = a.castShadow; + b.receiveShadow = a.receiveShadow; + b.frustumCulled = a.frustumCulled; + for (var c = 0; c < a.children.length; c++) { + var d = THREE.SceneUtils.cloneObject(a.children[c]); + b.children[c] = d; + d.parent = b + } + if (a instanceof THREE.LOD) + for (c = 0; c < a.LODs.length; c++) + b.LODs[c] = {visibleAtDistance: a.LODs[c].visibleAtDistance,object3D: b.children[c]}; + return b + },detach: function(a, b, c) { + a.applyMatrix(b.matrixWorld); + b.remove(a); + c.add(a) + },attach: function(a, b, c) { + var d = new THREE.Matrix4; + d.getInverse(c.matrixWorld); + a.applyMatrix(d); + b.remove(a); + c.add(a) + }}; +THREE.WebGLRenderer && (THREE.ShaderUtils = {lib: {fresnel: {uniforms: {mRefractionRatio: {type: "f",value: 1.02},mFresnelBias: {type: "f",value: 0.1},mFresnelPower: {type: "f",value: 2},mFresnelScale: {type: "f",value: 1},tCube: {type: "t",value: 1,texture: null}},fragmentShader: "uniform samplerCube tCube;\nvarying vec3 vReflect;\nvarying vec3 vRefract[3];\nvarying float vReflectionFactor;\nvoid main() {\nvec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\nvec4 refractedColor = vec4( 1.0, 1.0, 1.0, 1.0 );\nrefractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;\nrefractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;\nrefractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;\nrefractedColor.a = 1.0;\ngl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );\n}",vertexShader: "uniform float mRefractionRatio;\nuniform float mFresnelBias;\nuniform float mFresnelScale;\nuniform float mFresnelPower;\nvarying vec3 vReflect;\nvarying vec3 vRefract[3];\nvarying float vReflectionFactor;\nvoid main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\nvec3 nWorld = normalize ( mat3( objectMatrix[0].xyz, objectMatrix[1].xyz, objectMatrix[2].xyz ) * normal );\nvec3 I = mPosition.xyz - cameraPosition;\nvReflect = reflect( I, nWorld );\nvRefract[0] = refract( normalize( I ), nWorld, mRefractionRatio );\nvRefract[1] = refract( normalize( I ), nWorld, mRefractionRatio * 0.99 );\nvRefract[2] = refract( normalize( I ), nWorld, mRefractionRatio * 0.98 );\nvReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), nWorld ), mFresnelPower );\ngl_Position = projectionMatrix * mvPosition;\n}"}, + normal: {uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.fog, THREE.UniformsLib.lights, THREE.UniformsLib.shadowmap, {enableAO: {type: "i",value: 0},enableDiffuse: {type: "i",value: 0},enableSpecular: {type: "i",value: 0},enableReflection: {type: "i",value: 0},tDiffuse: {type: "t",value: 0,texture: null},tCube: {type: "t",value: 1,texture: null},tNormal: {type: "t",value: 2,texture: null},tSpecular: {type: "t",value: 3,texture: null},tAO: {type: "t",value: 4,texture: null},tDisplacement: {type: "t",value: 5,texture: null},uNormalScale: {type: "f", + value: 1},uDisplacementBias: {type: "f",value: 0},uDisplacementScale: {type: "f",value: 1},uDiffuseColor: {type: "c",value: new THREE.Color(16777215)},uSpecularColor: {type: "c",value: new THREE.Color(1118481)},uAmbientColor: {type: "c",value: new THREE.Color(16777215)},uShininess: {type: "f",value: 30},uOpacity: {type: "f",value: 1},uReflectivity: {type: "f",value: 0.5},uOffset: {type: "v2",value: new THREE.Vector2(0, 0)},uRepeat: {type: "v2",value: new THREE.Vector2(1, 1)},wrapRGB: {type: "v3",value: new THREE.Vector3(1, 1, 1)}}]), + fragmentShader: ["uniform vec3 uAmbientColor;\nuniform vec3 uDiffuseColor;\nuniform vec3 uSpecularColor;\nuniform float uShininess;\nuniform float uOpacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform float uNormalScale;\nuniform float uReflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vViewPosition;", + THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.fog_pars_fragment, "void main() {\ngl_FragColor = vec4( vec3( 1.0 ), uOpacity );\nvec3 specularTex = vec3( 1.0 );\nvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\nnormalTex.xy *= uNormalScale;\nnormalTex = normalize( normalTex );\nif( enableDiffuse ) {\n#ifdef GAMMA_INPUT\nvec4 texelColor = texture2D( tDiffuse, vUv );\ntexelColor.xyz *= texelColor.xyz;\ngl_FragColor = gl_FragColor * texelColor;\n#else\ngl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n#endif\n}\nif( enableAO ) {\n#ifdef GAMMA_INPUT\nvec4 aoColor = texture2D( tAO, vUv );\naoColor.xyz *= aoColor.xyz;\ngl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n#endif\n}\nif( enableSpecular )\nspecularTex = texture2D( tSpecular, vUv ).xyz;\nmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\nvec3 finalNormal = tsb * normalTex;\nvec3 normal = normalize( finalNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec3 pointVector = normalize( vPointLight[ i ].xyz );\nfloat pointDistance = vPointLight[ i ].w;\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n#endif\npointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;\nvec3 pointHalfVector = normalize( pointVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n#else\npointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\n#ifdef WRAP_AROUND\nfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\nfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n#endif\ndirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor) + totalSpecular;\nif ( enableReflection ) {\nvec3 wPos = cameraPosition - vViewPosition;\nvec3 vReflect = reflect( normalize( wPos ), normal );\nvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );\n}", + THREE.ShaderChunk.shadowmap_fragment, THREE.ShaderChunk.linear_to_gamma_fragment, THREE.ShaderChunk.fog_fragment, "}"].join("\n"),vertexShader: ["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\n#ifdef VERTEX_TEXTURES\nuniform sampler2D tDisplacement;\nuniform float uDisplacementScale;\nuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\nvarying vec3 vViewPosition;", + THREE.ShaderChunk.shadowmap_pars_vertex, "void main() {\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvViewPosition = -mvPosition.xyz;\nvNormal = normalMatrix * normal;\nvTangent = normalMatrix * tangent.xyz;\nvBinormal = cross( vNormal, vTangent ) * tangent.w;\nvUv = uv * uRepeat + uOffset;\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nvPointLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#ifdef VERTEX_TEXTURES\nvec3 dv = texture2D( tDisplacement, uv ).xyz;\nfloat df = uDisplacementScale * dv.x + uDisplacementBias;\nvec4 displacedPosition = vec4( normalize( vNormal.xyz ) * df, 0.0 ) + mvPosition;\ngl_Position = projectionMatrix * displacedPosition;\n#else\ngl_Position = projectionMatrix * mvPosition;\n#endif", + THREE.ShaderChunk.shadowmap_vertex, "}"].join("\n")},cube: {uniforms: {tCube: {type: "t",value: 1,texture: null},tFlip: {type: "f",value: -1}},vertexShader: "varying vec3 vViewPosition;\nvoid main() {\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\nvViewPosition = cameraPosition - mPosition.xyz;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader: "uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vViewPosition;\nvoid main() {\nvec3 wPos = cameraPosition - vViewPosition;\ngl_FragColor = textureCube( tCube, vec3( tFlip * wPos.x, wPos.yz ) );\n}"}}}); +THREE.BufferGeometry = function() { + this.id = THREE.GeometryCount++; + this.vertexColorArray = this.vertexUvArray = this.vertexNormalArray = this.vertexPositionArray = this.vertexIndexArray = this.vertexColorBuffer = this.vertexUvBuffer = this.vertexNormalBuffer = this.vertexPositionBuffer = this.vertexIndexBuffer = null; + this.dynamic = false; + this.boundingSphere = this.boundingBox = null; + this.morphTargets = [] +}; +THREE.BufferGeometry.prototype = {constructor: THREE.BufferGeometry,computeBoundingBox: function() { + },computeBoundingSphere: function() { + }}; +THREE.CubeGeometry = function(a, b, c, d, e, f, h, i) { + function l(a, b, c, h, i, j, l, n) { + var m, o = d || 1, p = e || 1, q = i / 2, g = j / 2, s = k.vertices.length; + if (a === "x" && b === "y" || a === "y" && b === "x") + m = "z"; + else if (a === "x" && b === "z" || a === "z" && b === "x") { + m = "y"; + p = f || 1 + } else if (a === "z" && b === "y" || a === "y" && b === "z") { + m = "x"; + o = f || 1 + } + var w = o + 1, y = p + 1, A = i / o, R = j / p, J = new THREE.Vector3; + J[m] = l > 0 ? 1 : -1; + for (i = 0; i < y; i++) + for (j = 0; j < w; j++) { + var Z = new THREE.Vector3; + Z[a] = (j * A - q) * c; + Z[b] = (i * R - g) * h; + Z[m] = l; + k.vertices.push(Z) + } + for (i = 0; i < p; i++) + for (j = 0; j < o; j++) { + a = new THREE.Face4(j + + w * i + s, j + w * (i + 1) + s, j + 1 + w * (i + 1) + s, j + 1 + w * i + s); + a.normal.copy(J); + a.vertexNormals.push(J.clone(), J.clone(), J.clone(), J.clone()); + a.materialIndex = n; + k.faces.push(a); + k.faceVertexUvs[0].push([new THREE.UV(j / o, i / p), new THREE.UV(j / o, (i + 1) / p), new THREE.UV((j + 1) / o, (i + 1) / p), new THREE.UV((j + 1) / o, i / p)]) + } + } + THREE.Geometry.call(this); + var k = this, j = a / 2, m = b / 2, n = c / 2, o, s, p, q, w, A; + if (h !== void 0) { + if (h instanceof Array) + this.materials = h; + else { + this.materials = []; + for (o = 0; o < 6; o++) + this.materials.push(h) + } + o = 0; + q = 1; + s = 2; + w = 3; + p = 4; + A = 5 + } else + this.materials = + []; + this.sides = {px: true,nx: true,py: true,ny: true,pz: true,nz: true}; + if (i != void 0) + for (var y in i) + this.sides[y] !== void 0 && (this.sides[y] = i[y]); + this.sides.px && l("z", "y", -1, -1, c, b, j, o); + this.sides.nx && l("z", "y", 1, -1, c, b, -j, q); + this.sides.py && l("x", "z", 1, 1, a, c, m, s); + this.sides.ny && l("x", "z", 1, -1, a, c, -m, w); + this.sides.pz && l("x", "y", 1, -1, a, b, n, p); + this.sides.nz && l("x", "y", -1, -1, a, b, -n, A); + this.computeCentroids(); + this.mergeVertices() +}; +THREE.CubeGeometry.prototype = new THREE.Geometry; +THREE.CubeGeometry.prototype.constructor = THREE.CubeGeometry; +THREE.CylinderGeometry = function(a, b, c, d, e, f) { + THREE.Geometry.call(this); + var a = a !== void 0 ? a : 20, b = b !== void 0 ? b : 20, c = c !== void 0 ? c : 100, h = c / 2, d = d || 8, e = e || 1, i, l, k = [], j = []; + for (l = 0; l <= e; l++) { + var m = [], n = [], o = l / e, s = o * (b - a) + a; + for (i = 0; i <= d; i++) { + var p = i / d, q = new THREE.Vector3; + q.x = s * Math.sin(p * Math.PI * 2); + q.y = -o * c + h; + q.z = s * Math.cos(p * Math.PI * 2); + this.vertices.push(q); + m.push(this.vertices.length - 1); + n.push(new THREE.UV(p, o)) + } + k.push(m); + j.push(n) + } + c = (b - a) / c; + for (i = 0; i < d; i++) { + if (a !== 0) { + m = this.vertices[k[0][i]].clone(); + n = this.vertices[k[0][i + + 1]].clone() + } else { + m = this.vertices[k[1][i]].clone(); + n = this.vertices[k[1][i + 1]].clone() + } + m.setY(Math.sqrt(m.x * m.x + m.z * m.z) * c).normalize(); + n.setY(Math.sqrt(n.x * n.x + n.z * n.z) * c).normalize(); + for (l = 0; l < e; l++) { + var o = k[l][i], s = k[l + 1][i], p = k[l + 1][i + 1], q = k[l][i + 1], w = m.clone(), A = m.clone(), y = n.clone(), u = n.clone(), H = j[l][i].clone(), B = j[l + 1][i].clone(), K = j[l + 1][i + 1].clone(), N = j[l][i + 1].clone(); + this.faces.push(new THREE.Face4(o, s, p, q, [w, A, y, u])); + this.faceVertexUvs[0].push([H, B, K, N]) + } + } + if (!f && a > 0) { + this.vertices.push(new THREE.Vector3(0, + h, 0)); + for (i = 0; i < d; i++) { + o = k[0][i]; + s = k[0][i + 1]; + p = this.vertices.length - 1; + w = new THREE.Vector3(0, 1, 0); + A = new THREE.Vector3(0, 1, 0); + y = new THREE.Vector3(0, 1, 0); + H = j[0][i].clone(); + B = j[0][i + 1].clone(); + K = new THREE.UV(B.u, 0); + this.faces.push(new THREE.Face3(o, s, p, [w, A, y])); + this.faceVertexUvs[0].push([H, B, K]) + } + } + if (!f && b > 0) { + this.vertices.push(new THREE.Vector3(0, -h, 0)); + for (i = 0; i < d; i++) { + o = k[l][i + 1]; + s = k[l][i]; + p = this.vertices.length - 1; + w = new THREE.Vector3(0, -1, 0); + A = new THREE.Vector3(0, -1, 0); + y = new THREE.Vector3(0, -1, 0); + H = j[l][i + 1].clone(); + B = j[l][i].clone(); + K = new THREE.UV(B.u, 1); + this.faces.push(new THREE.Face3(o, s, p, [w, A, y])); + this.faceVertexUvs[0].push([H, B, K]) + } + } + this.computeCentroids(); + this.computeFaceNormals() +}; +THREE.CylinderGeometry.prototype = new THREE.Geometry; +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; +THREE.PlaneGeometry = function(a, b, c, d) { + THREE.Geometry.call(this); + for (var e = a / 2, f = b / 2, c = c || 1, d = d || 1, h = c + 1, i = d + 1, l = a / c, k = b / d, j = new THREE.Vector3(0, 1, 0), a = 0; a < i; a++) + for (b = 0; b < h; b++) + this.vertices.push(new THREE.Vector3(b * l - e, 0, a * k - f)); + for (a = 0; a < d; a++) + for (b = 0; b < c; b++) { + e = new THREE.Face4(b + h * a, b + h * (a + 1), b + 1 + h * (a + 1), b + 1 + h * a); + e.normal.copy(j); + e.vertexNormals.push(j.clone(), j.clone(), j.clone(), j.clone()); + this.faces.push(e); + this.faceVertexUvs[0].push([new THREE.UV(b / c, a / d), new THREE.UV(b / c, (a + 1) / d), new THREE.UV((b + + 1) / c, (a + 1) / d), new THREE.UV((b + 1) / c, a / d)]) + } + this.computeCentroids() +}; +THREE.PlaneGeometry.prototype = new THREE.Geometry; +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; +THREE.SphereGeometry = function(a, b, c, d, e, f, h) { + THREE.Geometry.call(this); + var a = a || 50, d = d !== void 0 ? d : 0, e = e !== void 0 ? e : Math.PI * 2, f = f !== void 0 ? f : 0, h = h !== void 0 ? h : Math.PI, b = Math.max(3, Math.floor(b) || 8), c = Math.max(2, Math.floor(c) || 6), i, l, k = [], j = []; + for (l = 0; l <= c; l++) { + var m = [], n = []; + for (i = 0; i <= b; i++) { + var o = i / b, s = l / c, p = new THREE.Vector3; + p.x = -a * Math.cos(d + o * e) * Math.sin(f + s * h); + p.y = a * Math.cos(f + s * h); + p.z = a * Math.sin(d + o * e) * Math.sin(f + s * h); + this.vertices.push(p); + m.push(this.vertices.length - 1); + n.push(new THREE.UV(o, + s)) + } + k.push(m); + j.push(n) + } + for (l = 0; l < c; l++) + for (i = 0; i < b; i++) { + var d = k[l][i + 1], e = k[l][i], f = k[l + 1][i], h = k[l + 1][i + 1], m = this.vertices[d].clone().normalize(), n = this.vertices[e].clone().normalize(), o = this.vertices[f].clone().normalize(), s = this.vertices[h].clone().normalize(), p = j[l][i + 1].clone(), q = j[l][i].clone(), w = j[l + 1][i].clone(), A = j[l + 1][i + 1].clone(); + if (Math.abs(this.vertices[d].y) == a) { + this.faces.push(new THREE.Face3(d, f, h, [m, o, s])); + this.faceVertexUvs[0].push([p, w, A]) + } else if (Math.abs(this.vertices[f].y) == + a) { + this.faces.push(new THREE.Face3(d, e, f, [m, n, o])); + this.faceVertexUvs[0].push([p, q, w]) + } else { + this.faces.push(new THREE.Face4(d, e, f, h, [m, n, o, s])); + this.faceVertexUvs[0].push([p, q, w, A]) + } + } + this.computeCentroids(); + this.computeFaceNormals(); + this.boundingSphere = {radius: a} +}; +THREE.SphereGeometry.prototype = new THREE.Geometry; +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; +THREE.PolyhedronGeometry = function(a, b, c, d) { + function e(a) { + var b = a.normalize().clone(); + b.index = l.vertices.push(b) - 1; + var c = Math.atan2(a.z, -a.x) / 2 / Math.PI + 0.5, a = Math.atan2(-a.y, Math.sqrt(a.x * a.x + a.z * a.z)) / Math.PI + 0.5; + b.uv = new THREE.UV(c, a); + return b + } + function f(a, b, c, d) { + if (d < 1) { + d = new THREE.Face3(a.index, b.index, c.index, [a.clone(), b.clone(), c.clone()]); + d.centroid.addSelf(a).addSelf(b).addSelf(c).divideScalar(3); + d.normal = d.centroid.clone().normalize(); + l.faces.push(d); + d = Math.atan2(d.centroid.z, -d.centroid.x); + l.faceVertexUvs[0].push([i(a.uv, a, d), i(b.uv, b, d), i(c.uv, c, d)]) + } else { + d = d - 1; + f(a, h(a, b), h(a, c), d); + f(h(a, b), b, h(b, c), d); + f(h(a, c), h(b, c), c, d); + f(h(a, b), h(b, c), h(a, c), d) + } + } + function h(a, b) { + m[a.index] || (m[a.index] = []); + m[b.index] || (m[b.index] = []); + var c = m[a.index][b.index]; + c === void 0 && (m[a.index][b.index] = m[b.index][a.index] = c = e((new THREE.Vector3).add(a, b).divideScalar(2))); + return c + } + function i(a, b, c) { + c < 0 && a.u === 1 && (a = new THREE.UV(a.u - 1, a.v)); + b.x === 0 && b.z === 0 && (a = new THREE.UV(c / 2 / Math.PI + 0.5, a.v)); + return a + } + THREE.Geometry.call(this); + for (var c = c || 1, d = d || 0, l = this, k = 0, j = a.length; k < j; k++) + e(new THREE.Vector3(a[k][0], a[k][1], a[k][2])); + for (var m = [], a = this.vertices, k = 0, j = b.length; k < j; k++) + f(a[b[k][0]], a[b[k][1]], a[b[k][2]], d); + this.mergeVertices(); + k = 0; + for (j = this.vertices.length; k < j; k++) + this.vertices[k].multiplyScalar(c); + this.computeCentroids(); + this.boundingSphere = {radius: c} +}; +THREE.PolyhedronGeometry.prototype = new THREE.Geometry; +THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; +THREE.IcosahedronGeometry = function(a, b) { + var c = (1 + Math.sqrt(5)) / 2; + THREE.PolyhedronGeometry.call(this, [[-1, c, 0], [1, c, 0], [-1, -c, 0], [1, -c, 0], [0, -1, c], [0, 1, c], [0, -1, -c], [0, 1, -c], [c, 0, -1], [c, 0, 1], [-c, 0, -1], [-c, 0, 1]], [[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]], a, b) +}; +THREE.IcosahedronGeometry.prototype = new THREE.Geometry; +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; diff --git a/src/pdb/animation.js b/src/pdb/animation.js new file mode 100644 index 000000000..bd4d2a9ef --- /dev/null +++ b/src/pdb/animation.js @@ -0,0 +1,135 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +// base for all animations, e.g. position transitions, slerping etc. +function Animation(from, to, duration) { + this._from = from; + this._to = to; + this._duration = duration; + this._left = duration; + this._start = Date.now(); + this._looping = false; + this._finished = false; +} + +Animation.prototype.setLooping = function(looping) { + this._looping = looping; +}; + +Animation.prototype.step = function() { + var now = Date.now(); + var elapsed = now - this._start; + var t; + if (this._duration === 0) { + t = 1.0; + } else { + if (this._looping) { + var times = Math.floor(elapsed/this._duration); + t = (elapsed - times * this._duration)/this._duration; + } else { + elapsed = Math.min(this._duration, elapsed); + t = elapsed/this._duration; + this._finished = t === 1.0; + } + } + return this._setTo(t); +}; + +Animation.prototype._setTo = function(t) { + var smoothInterval = (1 - Math.cos(t * Math.PI ) ) / 2; + this._current = this._from * (1-smoothInterval) + this._to * smoothInterval; + return this._current; +}; + +Animation.prototype.finished = function() { + return this._finished; +}; + + + +function Move(from, to, duration) { + Animation.prototype.constructor.call(this, vec3.clone(from), + vec3.clone(to), duration); + this._current = vec3.clone(from); +} + +derive(Move, Animation); + +Move.prototype._setTo = function(t) { + var smoothInterval = (1 - Math.cos(t * Math.PI ) ) / 2; + vec3.lerp(this._current, this._from, this._to, smoothInterval); + return this._current; +}; + +function Rotate(initialRotation, destinationRotation, duration) { + var initial = mat3.create(); + var to = mat3.create(); + mat3.fromMat4(initial, initialRotation); + mat3.fromMat4(to, destinationRotation); + var initialQuat = quat.create(); + var toQuat = quat.create(); + quat.fromMat3(initialQuat, initial); + quat.fromMat3(toQuat, to); + this._current = mat3.create(); + Animation.prototype.constructor.call(this, initialQuat, toQuat, duration); +} + +derive(Rotate, Animation); + +Rotate.prototype._setTo = (function() { + var quatRot = quat.create(); + + return function(t) { + quat.slerp(quatRot, this._from, this._to, t); + mat3.fromQuat(this._current, quatRot); + return this._current; + }; +})(); + +function RockAndRoll(rotation, axis, duration) { + var initial = mat3.create(); + mat3.fromMat4(initial, rotation); + Animation.prototype.constructor.call(this, initial, null, duration); + this._axis = vec3.clone(axis); + this.setLooping(true); + this._current = mat3.create(); +} + +derive(RockAndRoll, Animation); + +RockAndRoll.prototype._setTo = (function() { + var axisRot = mat3.create(); + return function(t) { + var angle = 0.2 * Math.sin(2 * t * Math.PI); + geom.axisRotation(axisRot, this._axis, angle); + mat3.mul(this._current, this._from, axisRot); + return this._current; + }; +})(); + +exports.Move = Move; +exports.Rotate = Rotate; +exports.RockAndRoll = RockAndRoll; +exports.Animation = Animation; +return true; +})(this); diff --git a/src/pdb/buffer-allocators.js b/src/pdb/buffer-allocators.js new file mode 100644 index 000000000..d0b5490c9 --- /dev/null +++ b/src/pdb/buffer-allocators.js @@ -0,0 +1,73 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +// contains classes for two kinds of typed array allocation schemes. +// PoolAllocator stores every typed array allocation in a list and tries +// to reuse unused buffers whenever possible. The NativeAllocator just +// news the typed arrays every time they are used. +function PoolAllocator(bufferType) { + this._freeArrays = []; + this._bufferType = bufferType; +} + +PoolAllocator.prototype.request = function(requestedLength) { + var bestIndex = -1; + var bestLength = null; + for (var i = 0; i < this._freeArrays.length; ++i) { + var free = this._freeArrays[i]; + var length = free.length; + if (length >= requestedLength && + (bestLength === null || length < bestLength)) { + bestLength = length; + bestIndex = i; + } + } + if (bestIndex !== -1) { + var result = this._freeArrays[bestIndex]; + this._freeArrays.splice(bestIndex, 1); + return result; + } + return new this._bufferType(requestedLength); + +}; + +PoolAllocator.prototype.release = function(buffer) { + this._freeArrays.push(buffer); +}; + +function NativeAllocator(bufferType) { + this._bufferType = bufferType; +} + +NativeAllocator.prototype.request = function(length) { + return new this._bufferType(length); +}; + +NativeAllocator.prototype.release = function(buffer) { +}; + +exports.PoolAllocator = PoolAllocator; +exports.NativeAllocator = NativeAllocator; + +return true; +})(this); diff --git a/src/pdb/cam.js b/src/pdb/cam.js new file mode 100644 index 000000000..708a10eca --- /dev/null +++ b/src/pdb/cam.js @@ -0,0 +1,277 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { + +"use strict"; + +// A camera, providing us with a view into the 3D worlds. Handles projection, +// and modelview matrices and controls the global render parameters such as + // shader and fog. +function Cam(gl) { + this._projection = mat4.create(); + this._camModelView = mat4.create(); + this._modelView = mat4.create(); + this._rotation = mat4.create(); + this._translation = mat4.create(); + this._near = 0.10; + this._far = 4000.0; + this._fogNear = -5; + this._fogFar = 50; + this._fog = true; + this._fovY = Math.PI * 45.0 / 180.0; + this._paramsChanged = false; + this._fogColor = vec3.fromValues(1, 1, 1); + this._outlineColor = vec3.fromValues(0.1, 0.1, 0.1); + this._center = vec3.create(); + this._zoom = 50; + this._updateMat = true; + this._gl = gl; + this._currentShader = null; + this.setViewportSize(gl.viewportWidth, gl.viewportHeight); +} + + +Cam.prototype.setRotation = function(rot) { + if (rot.length === 16) { + mat4.copy(this._rotation, rot); + } else { + this._rotation[0] = rot[0]; + this._rotation[1] = rot[1]; + this._rotation[2] = rot[2]; + this._rotation[3] = 0.0; + this._rotation[4] = rot[3]; + this._rotation[5] = rot[4]; + this._rotation[6] = rot[5]; + this._rotation[7] = 0.0; + this._rotation[8] = rot[6]; + this._rotation[9] = rot[7]; + this._rotation[10] = rot[8]; + this._rotation[11] = 0.0; + this._rotation[12] = 0.0; + this._rotation[13] = 0.0; + this._rotation[14] = 0.0; + this._rotation[15] = 1.0; + } + this._updateMat = true; +}; + +// returns the 3 main axes of the current camera rotation +Cam.prototype.mainAxes = function() { + return[ + vec3.fromValues(this._rotation[0], this._rotation[4], this._rotation[8]), + vec3.fromValues(this._rotation[1], this._rotation[5], this._rotation[9]), + vec3.fromValues(this._rotation[2], this._rotation[6], this._rotation[10]) + ]; +}; + +Cam.prototype.fieldOfViewY = function() { + return this._fovY; +}; + +Cam.prototype.aspectRatio = function() { + return this._width / this._height; +}; + +Cam.prototype.rotation = function() { + return this._rotation; +}; + +Cam.prototype._updateIfRequired = function() { + if (!this._updateMat) { + return false; + } + mat4.identity(this._camModelView); + mat4.translate(this._camModelView, this._camModelView, + [ -this._center[0], -this._center[1], -this._center[2] ]); + mat4.mul(this._camModelView, this._rotation, this._camModelView); + mat4.identity(this._translation); + mat4.translate(this._translation, this._translation, [ 0, 0, -this._zoom ]); + mat4.mul(this._camModelView, this._translation, this._camModelView); + mat4.identity(this._projection); + mat4.perspective(this._projection, this._fovY, this._width / this._height, + this._near, this._far); + this._updateMat = false; + return true; +}; + +Cam.prototype.setViewportSize = function(width, height) { + this._updateMat = true; + this._width = width; + this._height = height; +}; + +Cam.prototype.setCenter = function(point) { + this._updateMat = true; + vec3.copy(this._center, point); +}; + +Cam.prototype.fog = function(value) { + if (value !== undefined) { + this._fog = value; + this._paramsChanged = true; + } + return this._fog; +}; + +Cam.prototype.rotateZ = (function() { + var tm = mat4.create(); + return function(delta) { + mat4.identity(tm); + this._updateMat = true; + mat4.rotate(tm, tm, delta, [ 0, 0, 1 ]); + mat4.mul(this._rotation, tm, this._rotation); + }; +})(); + +Cam.prototype.rotateX= (function(){ + var tm = mat4.create(); + return function(delta) { + mat4.identity(tm); + this._updateMat = true; + mat4.rotate(tm, tm, delta, [ 1, 0, 0 ]); + mat4.mul(this._rotation, tm, this._rotation); + }; +})(); + +Cam.prototype.rotateY = (function() { + var tm = mat4.create(); + return function(delta) { + mat4.identity(tm); + this._updateMat = true; + mat4.rotate(tm, tm, delta, [ 0, 1, 0 ]); + mat4.mul(this._rotation, tm, this._rotation); + }; +})(); + +Cam.prototype.panX = function(delta) { + return this.panXY(delta, 0); +}; + +Cam.prototype.panY = function(delta) { + return this.panXY(0, delta); +}; + +Cam.prototype.panXY = (function () { + var invertRotation = mat4.create(); + var newCenter = vec3.create(); + return function(deltaX, deltaY) { + mat4.transpose(invertRotation, this._rotation); + this._updateMat = true; + vec3.set(newCenter, -deltaX, deltaY, 0); + vec3.transformMat4(newCenter, newCenter, invertRotation); + vec3.add(newCenter, newCenter, this._center); + this.setCenter(newCenter); + }; +})(); + +Cam.prototype.nearOffset = function() { return this._near; }; +Cam.prototype.farOffset = function() { return this._far; }; + + +Cam.prototype.setNearFar = function(near, far) { + if (near === this._near && far === this._far) { + return; + } + this._near = near; + this._far = far; + this._updateMat = true; +}; + +Cam.prototype.setFogNearFar = function(near, far) { + this._fogNear = near; + this._fogFar = far; + this._updateMat = true; +}; + +Cam.prototype.setZoom = function(zoom) { + this._updateMat = true; + this._zoom = zoom; + return this._zoom; +}; + +Cam.prototype.zoom = function(delta) { + if (delta === undefined) { + return this._zoom; + } + this._updateMat = true; + var factor = 1.0 + delta * 0.1; + this._zoom = Math.min(1000.0, Math.max(2.0, factor * this._zoom)); + return this._zoom; +}; + +Cam.prototype.center = function() { + return this._center; +}; + +Cam.prototype.currentShader = function() { + return this._currentShader; +}; + +// sets all OpenGL parameters to make this camera active. +// +// among other things, it sets the follow uniforms on the shader: +// +// - projectionMat - the 4x4 projection matrix +// - modelviewMat - the 4x4 modelview matrix +// - rotationMat - the rotational part of the modelview matrix +// - fog - boolean indicating whether fog is enabled +// - fogNear,fogFar - near and far offset of fog +// - fogColor - the color of fog +// - outlineColor - color to be used for the outline shader +Cam.prototype.bind = function(shader, additionalTransform) { + var shaderChanged = false; + if (this._currentShader !== shader) { + this._currentShader = shader; + this._gl.useProgram(shader); + shaderChanged = true; + } + shaderChanged = this._updateIfRequired() || shaderChanged; + + // in case additionalTransform is given, multiply camera model view + // with the matrix and use the product as the model view matrix. + if (additionalTransform) { + mat4.mul(this._modelView, this._camModelView, additionalTransform); + this._gl.uniformMatrix4fv(shader.modelview, false, this._modelView); + } else { + this._gl.uniformMatrix4fv(shader.modelview, false, this._camModelView); + } + + // in case nothing changed, there is no need for us to set any other + // parameters. + if (!shaderChanged && !this._paramsChanged) { + return; + } + this._paramsChanged = false; + this._gl.uniformMatrix4fv(shader.projection, false, this._projection); + if (shader.rotation) { + this._gl.uniformMatrix4fv(shader.rotation, false, this._rotation); + } + this._gl.uniform1i(shader.fog, this._fog); + var nearOffset = this._zoom ; + this._gl.uniform1f(shader.fogFar, this._fogFar + nearOffset); + this._gl.uniform1f(shader.fogNear, this._fogNear + nearOffset); + this._gl.uniform3fv(shader.fogColor, this._fogColor); + this._gl.uniform3fv(shader.outlineColor, this._outlineColor); +}; + +exports.Cam = Cam; +})(this); + diff --git a/src/pdb/chain-data.js b/src/pdb/chain-data.js new file mode 100644 index 000000000..483ac8286 --- /dev/null +++ b/src/pdb/chain-data.js @@ -0,0 +1,64 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +(function(exports) { +"use strict"; + +// LineChainData and MeshChainData are two internal classes that add molecule- +// specific attributes and functionality to the IndexedVertexArray and +// VertexArray classes. +function LineChainData(chain, gl, numVerts, float32Allocator) { + VertexArray.prototype.constructor.call(this, gl, numVerts, float32Allocator); + this._chain = chain; +} + +derive(LineChainData, VertexArray); + +LineChainData.prototype.chain = function() { return this._chain; }; + + +function MeshChainData(chain, gl, numVerts, numIndices, float32Allocator, + uint16Allocator) { + IndexedVertexArray.prototype.constructor.call(this, gl, numVerts, numIndices, + float32Allocator, + uint16Allocator); + this._chain = chain; +} + +MeshChainData.prototype.chain = function() { return this._chain; }; + +LineChainData.prototype.drawSymmetryRelated = function(cam, shader, transforms) { + this.bind(shader); + for (var i = 0; i < transforms.length; ++i) { + cam.bind(shader, transforms[i]); + this._gl.uniform1i(shader.symId, i); + this.draw(); + } + this.releaseAttribs(shader); +}; + +derive(MeshChainData, IndexedVertexArray); + +MeshChainData.prototype.drawSymmetryRelated = LineChainData.prototype.drawSymmetryRelated; +exports.LineChainData = LineChainData; +exports.MeshChainData = MeshChainData; + +return true; +})(this); diff --git a/src/pdb/core.js b/src/pdb/core.js new file mode 100644 index 000000000..9e63c790b --- /dev/null +++ b/src/pdb/core.js @@ -0,0 +1,150 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +exports.derive = function(subclass, baseclass) { + for (var prop in baseclass.prototype) { + subclass.prototype[prop] = baseclass.prototype[prop]; + } +}; + +exports.copy = function(src) { + src = src || {}; + var cloned = {}; + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + cloned[prop] = src[prop]; + } + } + return cloned; +}; + +function defaultComp(lhs, rhs) { + return lhs < rhs; +} + +// returns the index into the values array for the first value identical to +// *value*. +exports.binarySearch = function(values, value, comp) { + if (values.length === 0) { + return -1; + } + comp = comp || defaultComp; + var low = 0, high = values.length; + var mid = (low + high) >> 1; + while (true) { + var midValue = values[mid]; + if (comp(value, midValue)) { + high = mid; + } else if (comp(midValue, value)) { + low = mid; + } else { + return mid; + } + var newMid = (low + high) >> 1; + if (newMid === mid) { + return -1; + } + mid = newMid; + } + return -1; +}; + +// returns the index of the first item in the list whose value is +// larger or equal than *value*. +exports.indexFirstLargerEqualThan = function(values, value, comp) { + comp = comp || defaultComp; + if (values.length === 0 || comp(value, values[0])) { + return -1; + } + var low = 0, high = values.length; + var mid = (low + high) >> 1; + while (true) { + var midValue = values[mid]; + if (comp(value, midValue)) { + // there might be other values larger than value with an index + // lower than mid. + high = mid; + } else if (comp(midValue, value)) { + low = mid; + } else { + high = mid+1; + } + var newMid = (low + high) >> 1; + if (newMid === mid) { + return mid; + } + mid = newMid; + } +}; + +exports.indexLastSmallerThan = function(values, value, comp) { + comp = comp || defaultComp; + if (values.length === 0 || comp(values[values.length-1], value)) { + return values.length-1; + } + var low = 0, high = values.length; + var mid = (low + high) >> 1; + var cnt = 0; + while (true) { + var midValue = values[mid]; + if (comp(value, midValue)) { + high = mid; + } else { + low = mid; + } + var newMid = (low + high) >> 1; + if (newMid === mid) { + return mid-1; + } + mid = newMid; + } +}; + +exports.indexLastSmallerEqualThan = function(values, value, comp) { + comp = comp || defaultComp; + if (values.length === 0 || comp(values[values.length-1], value)) { + return values.length-1; + } + if (comp(value, values[0])) { + return -1; + } + var low = 0, high = values.length; + var mid = (low + high) >> 1; + var cnt = 0; + while (true) { + var midValue = values[mid]; + if (comp(value, midValue)) { + high = mid; + } else { + low = mid; + } + var newMid = (low + high) >> 1; + if (newMid === mid) { + return mid; + } + mid = newMid; + } +}; + +return true; +})(this); diff --git a/src/pdb/framebuffer.js b/src/pdb/framebuffer.js new file mode 100644 index 000000000..8edec7d78 --- /dev/null +++ b/src/pdb/framebuffer.js @@ -0,0 +1,105 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { + +"use strict"; + +function FrameBuffer(gl, options) { + this._width = options.width; + this._height = options.height; + this._colorBufferWidth = this._width; + this._colorBufferHeight = this._height; + this._gl = gl; + this._colorHandle = this._gl.createFramebuffer(); + this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._colorHandle); + this._depthHandle = this._gl.createRenderbuffer(); + this._gl.bindRenderbuffer(this._gl.RENDERBUFFER, this._depthHandle); + this._gl.renderbufferStorage(this._gl.RENDERBUFFER, this._gl.DEPTH_COMPONENT16, + this._width, this._height); + this._gl.framebufferRenderbuffer(this._gl.FRAMEBUFFER, + this._gl.DEPTH_ATTACHMENT, + this._gl.RENDERBUFFER, this._depthHandle); + this._colorTexture = this._gl.createTexture(); + this._initColorBuffer(); +} + +FrameBuffer.prototype.width = function() { return this._width; }; +FrameBuffer.prototype.height = function() { return this._height; }; + +FrameBuffer.prototype.bind = function() { + this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._colorHandle); + this._gl.bindRenderbuffer(this._gl.RENDERBUFFER, this._depthHandle); + if (this._colorBufferWidth !== this._width || + this._colorBufferHeight !== this._height) { + this._resizeBuffers(); + } + this._gl.viewport(0, 0, this._width, this._height); +}; + +FrameBuffer.prototype._initColorBuffer = function() { + this.bind(); + var gl = this._gl; + gl.bindTexture(gl.TEXTURE_2D, this._colorTexture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this._width, this._height, 0, + gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, this._colorTexture, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + + this.release(); +}; + +FrameBuffer.prototype._resizeBuffers = function() { + this._gl.bindRenderbuffer(this._gl.RENDERBUFFER, this._depthHandle); + this._gl.renderbufferStorage(this._gl.RENDERBUFFER, this._gl.DEPTH_COMPONENT16, + this._width, this._height); + this._gl.bindTexture(this._gl.TEXTURE_2D, this._colorTexture); + this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._width, + this._height, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, + null); + this._gl.framebufferTexture2D(this._gl.FRAMEBUFFER, this._gl.COLOR_ATTACHMENT0, + this._gl.TEXTURE_2D, this._colorTexture, 0); + this._gl.framebufferRenderbuffer(this._gl.FRAMEBUFFER, + this._gl.DEPTH_ATTACHMENT, + this._gl.RENDERBUFFER, this._depthHandle); + this._gl.bindTexture(this._gl.TEXTURE_2D, null); + this._colorBufferWidth = this._width; + this._colorBufferHeight = this._height; +}; + +FrameBuffer.prototype.resize = function(width, height) { + this._width = width; + this._height = height; +}; + +FrameBuffer.prototype.release = function() { + this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null); + this._gl.bindRenderbuffer(this._gl.RENDERBUFFER, null); +}; + +exports.FrameBuffer = FrameBuffer; + +})(this); + diff --git a/src/pdb/geom-builders.js b/src/pdb/geom-builders.js new file mode 100644 index 000000000..667dd047c --- /dev/null +++ b/src/pdb/geom-builders.js @@ -0,0 +1,233 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// contains classes for constructing geometry for spheres, cylinders and tubes. +(function(exports) { +"use strict"; + +function ProtoSphere(stacks, arcs) { + this._arcs = arcs; + this._stacks = stacks; + this._indices = new Uint16Array(3 * arcs * stacks * 2); + this._verts = new Float32Array(3 * arcs * stacks); + var vert_angle = Math.PI / (stacks - 1); + var horz_angle = Math.PI * 2.0 / arcs; + var i, j; + for (i = 0; i < this._stacks; ++i) { + var radius = Math.sin(i * vert_angle); + var z = Math.cos(i * vert_angle); + for (j = 0; j < this._arcs; ++j) { + var nx = radius * Math.cos(j * horz_angle); + var ny = radius * Math.sin(j * horz_angle); + this._verts[3 * (j + i * this._arcs)] = nx; + this._verts[3 * (j + i * this._arcs) + 1] = ny; + this._verts[3 * (j + i * this._arcs) + 2] = z; + } + } + var index = 0; + for (i = 0; i < this._stacks - 1; ++i) { + for (j = 0; j < this._arcs; ++j) { + this._indices[index] = (i) * this._arcs + j; + this._indices[index + 1] = (i) * this._arcs + ((j + 1) % this._arcs); + this._indices[index + 2] = (i + 1) * this._arcs + j; + + index += 3; + + this._indices[index] = (i) * this._arcs + ((j + 1) % this._arcs); + this._indices[index + 1] = + (i + 1) * this._arcs + ((j + 1) % this._arcs); + this._indices[index + 2] = (i + 1) * this._arcs + j; + index += 3; + } + } +} + +ProtoSphere.prototype.addTransformed = (function() { + + var pos = vec3.create(), normal = vec3.create(); + + return function(va, center, radius, color, objId) { + var baseIndex = va.numVerts(); + for (var i = 0; i < this._stacks * this._arcs; ++i) { + vec3.set(normal, this._verts[3 * i], this._verts[3 * i + 1], + this._verts[3 * i + 2]); + vec3.copy(pos, normal); + vec3.scale(pos, pos, radius); + vec3.add(pos, pos, center); + va.addVertex(pos, normal, color, objId); + } + for (i = 0; i < this._indices.length / 3; ++i) { + va.addTriangle(baseIndex + this._indices[i * 3], + baseIndex + this._indices[i * 3 + 1], + baseIndex + this._indices[i * 3 + 2]); + } + }; +})(); + +ProtoSphere.prototype.numIndices = function() { + return this._indices.length; +}; + +ProtoSphere.prototype.numVerts = function() { + return this._verts.length / 3; +}; + +// A tube profile is a cross-section of a tube, e.g. a circle or a 'flat' +// square. +// They are used to control the style of helices, strands and coils for the +// cartoon render mode. +function TubeProfile(points, num, strength) { + var interpolated = + geom.catmullRomSpline(points, points.length / 3, num, strength, true); + + this._indices = new Uint16Array(interpolated.length * 2); + this._verts = interpolated; + this._normals = new Float32Array(interpolated.length); + this._arcs = interpolated.length / 3; + + var normal = vec3.create(), pos = vec3.create(); + + for (var i = 0; i < this._arcs; ++i) { + var i_prev = i === 0 ? this._arcs - 1 : i - 1; + var i_next = i === this._arcs - 1 ? 0 : i + 1; + normal[0] = this._verts[3 * i_next + 1] - this._verts[3 * i_prev + 1]; + normal[1] = this._verts[3 * i_prev] - this._verts[3 * i_next]; + vec3.normalize(normal, normal); + this._normals[3 * i] = normal[0]; + this._normals[3 * i + 1] = normal[1]; + this._normals[3 * i + 2] = normal[2]; + } + + for (i = 0; i < this._arcs; ++i) { + this._indices[6 * i] = i; + this._indices[6 * i + 1] = i + this._arcs; + this._indices[6 * i + 2] = ((i + 1) % this._arcs) + this._arcs; + this._indices[6 * i + 3] = i; + this._indices[6 * i + 4] = ((i + 1) % this._arcs) + this._arcs; + this._indices[6 * i + 5] = (i + 1) % this._arcs; + } +} + +TubeProfile.prototype.addTransformed = (function() { + var pos = vec3.create(), normal = vec3.create(); + return function(vertArray, center, radius, rotation, color, first, offset, objId) { + var baseIndex = vertArray.numVerts() - this._arcs; + for (var i = 0; i < this._arcs; ++i) { + vec3.set(pos, radius * this._verts[3 * i], + radius * this._verts[3 * i + 1], 0.0); + vec3.transformMat3(pos, pos, rotation); + vec3.add(pos, pos, center); + vec3.set(normal, this._normals[3 * i], this._normals[3 * i + 1], 0.0); + vec3.transformMat3(normal, normal, rotation); + vertArray.addVertex(pos, normal, color, objId); + } + if (first) { + return; + } + if (offset === 0) { + // that's what happens most of the time, thus is has been optimized. + for (i = 0; i < this._indices.length / 3; ++i) { + vertArray.addTriangle(baseIndex + this._indices[i * 3], + baseIndex + this._indices[i * 3 + 1], + baseIndex + this._indices[i * 3 + 2]); + } + return; + } + for (i = 0; i < this._arcs; ++i) { + vertArray.addTriangle(baseIndex + ((i + offset) % this._arcs), + baseIndex + i + this._arcs, + baseIndex + ((i + 1) % this._arcs) + this._arcs); + vertArray.addTriangle(baseIndex + (i + offset) % this._arcs, + baseIndex + ((i + 1) % this._arcs) + this._arcs, + baseIndex + ((i + 1 + offset) % this._arcs)); + } + + }; +})(); + +function ProtoCylinder(arcs) { + this._arcs = arcs; + this._indices = new Uint16Array(arcs * 3 * 2); + this._verts = new Float32Array(3 * arcs * 2); + this._normals = new Float32Array(3 * arcs * 2); + var angle = Math.PI * 2 / this._arcs; + for (var i = 0; i < this._arcs; ++i) { + var cos_angle = Math.cos(angle * i); + var sin_angle = Math.sin(angle * i); + this._verts[3 * i] = cos_angle; + this._verts[3 * i + 1] = sin_angle; + this._verts[3 * i + 2] = -0.5; + this._verts[3 * arcs + 3 * i] = cos_angle; + this._verts[3 * arcs + 3 * i + 1] = sin_angle; + this._verts[3 * arcs + 3 * i + 2] = 0.5; + this._normals[3 * i] = cos_angle; + this._normals[3 * i + 1] = sin_angle; + this._normals[3 * arcs + 3 * i] = cos_angle; + this._normals[3 * arcs + 3 * i + 1] = sin_angle; + } + for (i = 0; i < this._arcs; ++i) { + this._indices[6 * i] = (i) % this._arcs; + this._indices[6 * i + 1] = arcs + ((i + 1) % this._arcs); + this._indices[6 * i + 2] = (i + 1) % this._arcs; + + this._indices[6 * i + 3] = (i) % this._arcs; + this._indices[6 * i + 4] = arcs + ((i) % this._arcs); + this._indices[6 * i + 5] = arcs + ((i + 1) % this._arcs); + } +} + +ProtoCylinder.prototype.numVerts = function() { + return this._verts.length / 3; +}; + +ProtoCylinder.prototype.numIndices = function() { + return this._indices.length; +}; + +ProtoCylinder.prototype.addTransformed = (function() { + var pos = vec3.create(), normal = vec3.create(); + return function(va, center, length, radius, rotation, colorOne, colorTwo, + idOne, idTwo) { + var baseIndex = va.numVerts(); + for (var i = 0; i < 2 * this._arcs; ++i) { + vec3.set(pos, radius * this._verts[3 * i], radius * this._verts[3 * i + 1], + length * this._verts[3 * i + 2]); + vec3.transformMat3(pos, pos, rotation); + vec3.add(pos, pos, center); + vec3.set(normal, this._normals[3 * i], this._normals[3 * i + 1], + this._normals[3 * i + 2]); + vec3.transformMat3(normal, normal, rotation); + var objId = i < this._arcs ? idOne : idTwo; + va.addVertex(pos, normal, i < this._arcs ? colorOne : colorTwo, objId); + } + for (i = 0; i < this._indices.length / 3; ++i) { + va.addTriangle(baseIndex + this._indices[i * 3], + baseIndex + this._indices[i * 3 + 1], + baseIndex + this._indices[i * 3 + 2]); + } + }; +})(); + +exports.TubeProfile = TubeProfile; +exports.ProtoSphere = ProtoSphere; +exports.ProtoCylinder = ProtoCylinder; + +})(this); + diff --git a/src/pdb/geom.js b/src/pdb/geom.js new file mode 100644 index 000000000..357c558ec --- /dev/null +++ b/src/pdb/geom.js @@ -0,0 +1,202 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +var geom = (function() { + "use strict"; + // calculates the signed angle of vectors a and b with respect to + // the reference normal c. +var signedAngle = (function() { + var tmp = vec3.create(); + return function(a, b, c) { + vec3.cross(tmp, a, b); + return Math.atan2(vec3.dot(tmp, c), vec3.dot(a, b)); + }; +})(); + +// calculate a vector orthogonal to an input vector +var ortho = (function() { + var tmp = vec3.create(); + return function(out, vec) { + vec3.copy(tmp, vec); + if (Math.abs(vec[0]) < Math.abs(vec[1])) { + if (Math.abs(vec[0]) < Math.abs(vec[2])) { + tmp[0] += 1; + } else { + tmp[2] += 1; + } + } else { + if (Math.abs(vec[1]) < Math.abs(vec[2])) { + tmp[1] += 1; + } else { + tmp[2] += 1; + } + } + return vec3.cross(out, vec, tmp); + }; +})(); + +// assumes that axis is normalized. don't expect to get meaningful +// results when it's not +var axisRotation = function(out, axis, angle) { + var sa = Math.sin(angle), ca = Math.cos(angle), x = axis[0], y = axis[1], + z = axis[2], xx = x * x, xy = x * y, xz = x * z, yy = y * y, yz = y * z, + zz = z * z; + + out[0] = xx + ca - xx * ca; + out[1] = xy - ca * xy - sa * z; + out[2] = xz - ca * xz + sa * y; + out[3] = xy - ca * xy + sa * z; + out[4] = yy + ca - ca * yy; + out[5] = yz - ca * yz - sa * x; + out[6] = xz - ca * xz - sa * y; + out[7] = yz - ca * yz + sa * x; + out[8] = zz + ca - ca * zz; + return out; +}; + +var cubicHermiteInterpolate = (function() { + var p = vec3.create(); + return function(out, p_k, m_k, p_kp1, m_kp1, t, index) { + var tt = t * t; + var three_minus_two_t = 3.0 - 2.0 * t; + var h01 = tt * three_minus_two_t; + var h00 = 1.0 - h01; + var h10 = tt * (t - 2.0) + t; + var h11 = tt * (t - 1.0); + vec3.copy(p, p_k); + vec3.scale(p, p, h00); + vec3.scaleAndAdd(p, p, m_k, h10); + vec3.scaleAndAdd(p, p, p_kp1, h01); + vec3.scaleAndAdd(p, p, m_kp1, h11); + out[index] = p[0]; + out[index + 1] = p[1]; + out[index + 2] = p[2]; +}; +})(); + +// returns the number of interpolation points for the given settings +function catmullRomSplineNumPoints(numPoints, subdiv, circular) { + if (circular) { + return numPoints * subdiv; + } else { + return subdiv * (numPoints - 1) + 1; + } +} +// interpolates the given list of points (stored in a Float32Array) with a +// Cubic Hermite spline using the method of Catmull and Rom to calculate the +// tangents. +function catmullRomSpline(points, numPoints, num, strength, circular, + float32BufferPool) { + circular = circular || false; + strength = strength || 0.5; + var out = null; + var outLength = catmullRomSplineNumPoints(numPoints, num, circular) * 3; + if (float32BufferPool) { + out = float32BufferPool.request(outLength); + } else { + out = new Float32Array(outLength); + } + var index = 0; + var delta_t = 1.0 / num; + var m_k = vec3.create(), m_kp1 = vec3.create(); // tangents at k-1 and k+1 + var p_k = vec3.create(), p_kp1 = vec3.create(), p_kp2 = vec3.create(), + p_kp3 = vec3.create(); + var i, j, e; + + vec3.set(p_kp1, points[0], points[1], points[2]); + vec3.set(p_kp2, points[3], points[4], points[5]); + if (circular) { + vec3.set(p_k, points[points.length - 3], points[points.length - 2], + points[points.length - 1]); + vec3.sub(m_k, p_kp2, p_k); + vec3.scale(m_k, m_k, strength); + } else { + vec3.set(p_k, points[0], points[1], points[2]); + vec3.set(m_k, 0, 0, 0); + } + for (i = 1, e = numPoints - 1; i < e; ++i) { + vec3.set(p_kp3, points[3 * (i + 1)], points[3 * (i + 1) + 1], + points[3 * (i + 1) + 2]); + vec3.sub(m_kp1, p_kp3, p_kp1); + vec3.scale(m_kp1, m_kp1, strength); + for (j = 0; j < num; ++j) { + cubicHermiteInterpolate(out, p_kp1, m_k, p_kp2, m_kp1, delta_t * j, + index); + index += 3; + } + vec3.copy(p_k, p_kp1); + vec3.copy(p_kp1, p_kp2); + vec3.copy(p_kp2, p_kp3); + vec3.copy(m_k, m_kp1); + } + if (circular) { + vec3.set(p_kp3, points[0], points[1], points[3]); + vec3.sub(m_kp1, p_kp3, p_kp1); + vec3.scale(m_kp1, m_kp1, strength); + } else { + vec3.set(m_kp1, 0, 0, 0); + } + for (j = 0; j < num; ++j) { + cubicHermiteInterpolate(out, p_kp1, m_k, p_kp2, m_kp1, delta_t * j, index); + index += 3; + } + if (!circular) { + out[index] = points[3 * (numPoints - 1) + 0]; + out[index + 1] = points[3 * (numPoints - 1) + 1]; + out[index + 2] = points[3 * (numPoints - 1) + 2]; + return out; + } + vec3.copy(p_k, p_kp1); + vec3.copy(p_kp1, p_kp2); + vec3.copy(p_kp2, p_kp3); + vec3.copy(m_k, m_kp1); + vec3.set(p_kp3, points[3], points[4], points[5]); + vec3.sub(m_kp1, p_kp3, p_kp1); + vec3.scale(m_kp1, m_kp1, strength); + for (j = 0; j < num; ++j) { + cubicHermiteInterpolate(out, p_kp1, m_k, p_kp2, m_kp1, delta_t * j, index); + index += 3; + } + return out; +} + +function Sphere(center, radius) { + this._center = center || vec3.create(); + this._radius = radius || 1.0; +} + +Sphere.prototype.center = function() { return this._center; }; +Sphere.prototype.radius = function() { return this._radius; }; + +return { + signedAngle : signedAngle, + axisRotation : axisRotation, + ortho : ortho, + catmullRomSpline : catmullRomSpline, + cubicHermiteInterpolate : cubicHermiteInterpolate, + catmullRomSplineNumPoints : catmullRomSplineNumPoints, + Sphere : Sphere +}; + +})(); + +if(typeof(exports) !== 'undefined') { + module.exports = geom; +} diff --git a/src/pdb/gl-matrix.js b/src/pdb/gl-matrix.js new file mode 100755 index 000000000..7d83623d3 --- /dev/null +++ b/src/pdb/gl-matrix.js @@ -0,0 +1,3057 @@ +/** + * @fileoverview gl-matrix - High performance matrix and vector operations + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 2.2.0 + */ + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +(function(_global) { + "use strict"; + + var shim = {}; + if (typeof(exports) === 'undefined') { + if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + shim.exports = {}; + define(function() { + return shim.exports; + }); + } else { + // gl-matrix lives in a browser, define its namespaces in global + shim.exports = typeof(window) !== 'undefined' ? window : _global; + } + } + else { + // gl-matrix lives in commonjs, define its namespaces in exports + shim.exports = exports; + } + + (function(exports) { + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +if(!GLMAT_ARRAY_TYPE) { + var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; +} + +if(!GLMAT_RANDOM) { + var GLMAT_RANDOM = Math.random; +} + +/** + * @class Common utilities + * @name glMatrix + */ +var glMatrix = {}; + +/** + * Sets the type of array used when creating new vectors and matricies + * + * @param {Type} type Array type, such as Float32Array or Array + */ +glMatrix.setMatrixArrayType = function(type) { + GLMAT_ARRAY_TYPE = type; +} + +if(typeof(exports) !== 'undefined') { + exports.glMatrix = glMatrix; +} +; +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 3 Dimensional Vector + * @name vec3 + */ + +var vec3 = {}; + +/** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ +vec3.create = function() { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = 0; + out[1] = 0; + out[2] = 0; + return out; +}; + +/** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ +vec3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ +vec3.fromValues = function(x, y, z) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ +vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ +vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +}; + +/** + * Subtracts vector b from vector a + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; +}; + +/** + * Alias for {@link vec3.subtract} + * @function + */ +vec3.sub = vec3.subtract; + +/** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; +}; + +/** + * Alias for {@link vec3.multiply} + * @function + */ +vec3.mul = vec3.multiply; + +/** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; +}; + +/** + * Alias for {@link vec3.divide} + * @function + */ +vec3.div = vec3.divide; + +/** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; +}; + +/** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; +}; + +/** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec3} out + */ +vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +}; + +/** + * Adds two vec3's after scaling the second operand by a scalar value + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec3} out + */ +vec3.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + return out; +}; + +/** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ +vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Alias for {@link vec3.distance} + * @function + */ +vec3.dist = vec3.distance; + +/** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ +vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; +}; + +/** + * Alias for {@link vec3.squaredDistance} + * @function + */ +vec3.sqrDist = vec3.squaredDistance; + +/** + * Calculates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ +vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Alias for {@link vec3.length} + * @function + */ +vec3.len = vec3.length; + +/** + * Calculates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; +}; + +/** + * Alias for {@link vec3.squaredLength} + * @function + */ +vec3.sqrLen = vec3.squaredLength; + +/** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ +vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; +}; + +/** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ +vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +}; + +/** + * Calculates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ +vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +}; + +/** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +}; + +/** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ +vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; +}; + +/** + * Generates a random vector with the given scale + * + * @param {vec3} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec3} out + */ +vec3.random = function (out, scale) { + scale = scale || 1.0; + + var r = GLMAT_RANDOM() * 2.0 * Math.PI; + var z = (GLMAT_RANDOM() * 2.0) - 1.0; + var zScale = Math.sqrt(1.0-z*z) * scale; + + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; +}; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return out; +}; + +/** + * Transforms the vec3 with a mat3. + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m the 3x3 matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat3 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; +}; + +/** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ +vec3.transformQuat = function(out, a, q) { + // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations + + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ +vec3.forEach = (function() { + var vec = vec3.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec3 = vec3; +} +; +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4 Dimensional Vector + * @name vec4 + */ + +var vec4 = {}; + +/** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ +vec4.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + return out; +}; + +/** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ +vec4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ +vec4.fromValues = function(x, y, z, w) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ +vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ +vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; +}; + +/** + * Subtracts vector b from vector a + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; +}; + +/** + * Alias for {@link vec4.subtract} + * @function + */ +vec4.sub = vec4.subtract; + +/** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; +}; + +/** + * Alias for {@link vec4.multiply} + * @function + */ +vec4.mul = vec4.multiply; + +/** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; +}; + +/** + * Alias for {@link vec4.divide} + * @function + */ +vec4.div = vec4.divide; + +/** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; +}; + +/** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; +}; + +/** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec4} out + */ +vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; +}; + +/** + * Adds two vec4's after scaling the second operand by a scalar value + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec4} out + */ +vec4.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + out[3] = a[3] + (b[3] * scale); + return out; +}; + +/** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ +vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Alias for {@link vec4.distance} + * @function + */ +vec4.dist = vec4.distance; + +/** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ +vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Alias for {@link vec4.squaredDistance} + * @function + */ +vec4.sqrDist = vec4.squaredDistance; + +/** + * Calculates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ +vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Alias for {@link vec4.length} + * @function + */ +vec4.len = vec4.length; + +/** + * Calculates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Alias for {@link vec4.squaredLength} + * @function + */ +vec4.sqrLen = vec4.squaredLength; + +/** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ +vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; +}; + +/** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ +vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + out[3] = a[3] * len; + } + return out; +}; + +/** + * Calculates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ +vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +}; + +/** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ +vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; +}; + +/** + * Generates a random vector with the given scale + * + * @param {vec4} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec4} out + */ +vec4.random = function (out, scale) { + scale = scale || 1.0; + + //TODO: This is a pretty awful way of doing this. Find something better. + out[0] = GLMAT_RANDOM(); + out[1] = GLMAT_RANDOM(); + out[2] = GLMAT_RANDOM(); + out[3] = GLMAT_RANDOM(); + vec4.normalize(out, out); + vec4.scale(out, out, scale); + return out; +}; + +/** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ +vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; +}; + +/** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ +vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ +vec4.forEach = (function() { + var vec = vec4.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec4 = vec4; +} +; +var mat3 = {}; + +/** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ +mat3.create = function() { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Copies the upper-left 3x3 values into the given mat3. + * + * @param {mat3} out the receiving 3x3 matrix + * @param {mat4} a the source 4x4 matrix + * @returns {mat3} out + */ +mat3.fromMat4 = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[4]; + out[4] = a[5]; + out[5] = a[6]; + out[6] = a[8]; + out[7] = a[9]; + out[8] = a[10]; + return out; +}; + +/** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ +mat3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ +mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; +}; + +/** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; +}; + +/** + * Calculates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; +}; + +/** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ +mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); +}; + +/** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ +mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; +}; + +/** + * Alias for {@link mat3.multiply} + * @function + */ +mat3.mul = mat3.multiply; + +/** + * Translate a mat3 by the given vector + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to translate + * @param {vec2} v vector to translate by + * @returns {mat3} out + */ +mat3.translate = function(out, a, v) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + x = v[0], y = v[1]; + + out[0] = a00; + out[1] = a01; + out[2] = a02; + + out[3] = a10; + out[4] = a11; + out[5] = a12; + + out[6] = x * a00 + y * a10 + a20; + out[7] = x * a01 + y * a11 + a21; + out[8] = x * a02 + y * a12 + a22; + return out; +}; + +/** + * Rotates a mat3 by the given angle + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ +mat3.rotate = function (out, a, rad) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + s = Math.sin(rad), + c = Math.cos(rad); + + out[0] = c * a00 + s * a10; + out[1] = c * a01 + s * a11; + out[2] = c * a02 + s * a12; + + out[3] = c * a10 - s * a00; + out[4] = c * a11 - s * a01; + out[5] = c * a12 - s * a02; + + out[6] = a20; + out[7] = a21; + out[8] = a22; + return out; +}; + +/** +* Calculates a 3x3 matrix from the given quaternion +* +* @param {mat3} out mat3 receiving operation result +* @param {quat} q Quaternion to create matrix from +* +* @returns {mat3} out +*/ +mat3.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[3] = xy + wz; + out[6] = xz - wy; + + out[1] = xy - wz; + out[4] = 1 - (xx + zz); + out[7] = yz + wx; + + out[2] = xz + wy; + out[5] = yz - wx; + out[8] = 1 - (xx + yy); + + return out; +}; + +/** +* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix +* +* @param {mat3} out mat3 receiving operation result +* @param {mat4} a Mat4 to derive the normal matrix from +* +* @returns {mat3} out +*/ +mat3.normalFromMat4 = function (out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + + out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + + out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + + return out; +}; + +/** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat3 = mat3; +} +; +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class 4x4 Matrix + * @name mat4 + */ + +var mat4 = {}; + +/** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ +mat4.create = function() { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +mat4.fromValues = function(m00, m10, m20, m30, + m01, m11, m21, m31, + m02, m12, m22, m32, + m03, m13, m23, m33) { + var out = new GLMAT_ARRAY_TYPE(16); + out[ 0] = m00; out[ 1] = m10; out[ 2] = m20, out[ 3] = m30; + out[ 4] = m01; out[ 5] = m11; out[ 6] = m21; out[ 7] = m31; + out[ 8] = m02; out[ 9] = m12; out[10] = m22; out[11] = m32; + out[12] = m03; out[13] = m13; out[14] = m23; out[15] = m33; + return out; +}; +/** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ +mat4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +/** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; +}; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; + +/** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; +}; + +/** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ +mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +}; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; + +/** + * Alias for {@link mat4.multiply} + * @function + */ +mat4.mul = mat4.multiply; + +/** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ +mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; +}; + +/** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ +mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < GLMAT_EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; + +/** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +}; + +/** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; +}; + +/** +* Calculates a 4x4 matrix from the given quaternion +* +* @param {mat4} out mat4 receiving operation result +* @param {quat} q Quaternion to create matrix from +* +* @returns {mat4} out +*/ +mat4.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; +}; + +/** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < GLMAT_EPSILON && + Math.abs(eyey - centery) < GLMAT_EPSILON && + Math.abs(eyez - centerz) < GLMAT_EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; + +/** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat4 = mat4; +} +; +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * @class Quaternion + * @name quat + */ + +var quat = {}; + +/** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ +quat.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quaternion to represent the shortest rotation from one + * vector to another. + * + * Both vectors are assumed to be unit length. + * + * @param {quat} out the receiving quaternion. + * @param {vec3} a the initial vector + * @param {vec3} b the destination vector + * @returns {quat} out + */ +quat.rotationTo = (function() { + var tmpvec3 = vec3.create(); + var xUnitVec3 = vec3.fromValues(1,0,0); + var yUnitVec3 = vec3.fromValues(0,1,0); + + return function(out, a, b) { + var dot = vec3.dot(a, b); + if (dot < -0.999999) { + vec3.cross(tmpvec3, xUnitVec3, a); + if (vec3.length(tmpvec3) < 0.000001) + vec3.cross(tmpvec3, yUnitVec3, a); + vec3.normalize(tmpvec3, tmpvec3); + quat.setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + vec3.cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return quat.normalize(out, out); + } + }; +})(); + +/** + * Sets the specified quaternion with values corresponding to the given + * axes. Each axis is a vec3 and is expected to be unit length and + * perpendicular to all other specified axes. + * + * @param {vec3} view the vector representing the viewing direction + * @param {vec3} right the vector representing the local "right" direction + * @param {vec3} up the vector representing the local "up" direction + * @returns {quat} out + */ +quat.setAxes = (function() { + var matr = mat3.create(); + + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + + matr[2] = view[0]; + matr[5] = view[1]; + matr[8] = view[2]; + + return quat.normalize(out, quat.fromMat3(out, matr)); + }; +})(); + +/** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + * @function + */ +quat.clone = vec4.clone; + +/** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + * @function + */ +quat.fromValues = vec4.fromValues; + +/** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + * @function + */ +quat.copy = vec4.copy; + +/** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + * @function + */ +quat.set = vec4.set; + +/** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ +quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ +quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +}; + +/** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + * @function + */ +quat.add = vec4.add; + +/** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +}; + +/** + * Alias for {@link quat.multiply} + * @function + */ +quat.mul = quat.multiply; + +/** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {quat} out + * @function + */ +quat.scale = vec4.scale; + +/** + * Rotates a quaternion by the given angle about the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; +}; + +/** + * Rotates a quaternion by the given angle about the Y axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; +}; + +/** + * Rotates a quaternion by the given angle about the Z axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; +}; + +/** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ +quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; +}; + +/** + * Calculates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + * @function + */ +quat.dot = vec4.dot; + +/** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + * @function + */ +quat.lerp = vec4.lerp; + +/** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.slerp = function (out, a, b, t) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + var omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = ax * bx + ay * by + az * bz + aw * bw; + // adjust signs (if necessary) + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + // calculate coefficients + if ( (1.0 - cosom) > 0.000001 ) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + + return out; +}; + +/** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ +quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +}; + +/** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ +quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Calculates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + * @function + */ +quat.length = vec4.length; + +/** + * Alias for {@link quat.length} + * @function + */ +quat.len = quat.length; + +/** + * Calculates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + * @function + */ +quat.squaredLength = vec4.squaredLength; + +/** + * Alias for {@link quat.squaredLength} + * @function + */ +quat.sqrLen = quat.squaredLength; + +/** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + * @function + */ +quat.normalize = vec4.normalize; + +/** + * Creates a quaternion from the given 3x3 rotation matrix. + * + * NOTE: The resultant quaternion is not normalized, so you should be sure + * to renormalize the quaternion yourself where necessary. + * + * @param {quat} out the receiving quaternion + * @param {mat3} m rotation matrix + * @returns {quat} out + * @function + */ +quat.fromMat3 = (function() { + // benchmarks: + // http://jsperf.com/typed-array-access-speed + // http://jsperf.com/conversion-of-3x3-matrix-to-quaternion + + var s_iNext = (typeof(Int8Array) !== 'undefined' ? new Int8Array([1,2,0]) : [1,2,0]); + + return function(out, m) { + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + + if ( fTrace > 0.0 ) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; // 1/(4w) + out[0] = (m[7]-m[5])*fRoot; + out[1] = (m[2]-m[6])*fRoot; + out[2] = (m[3]-m[1])*fRoot; + } else { + // |w| <= 1/2 + var i = 0; + if ( m[4] > m[0] ) + i = 1; + if ( m[8] > m[i*3+i] ) + i = 2; + var j = s_iNext[i]; + var k = s_iNext[j]; + + fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[k*3+j] - m[j*3+k]) * fRoot; + out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; + out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; + } + + return out; + }; +})(); + +/** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.quat = quat; +} +; + + })(shim.exports); +})(this); diff --git a/src/pdb/indexed-vertex-array.js b/src/pdb/indexed-vertex-array.js new file mode 100644 index 000000000..88f41afc8 --- /dev/null +++ b/src/pdb/indexed-vertex-array.js @@ -0,0 +1,149 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +(function(exports) { +"use strict"; + +function IndexedVertexArray(gl, numVerts, numIndices, + float32Allocator, uint16Allocator) { + VertexArrayBase.prototype.constructor.call(this, gl, numVerts, + float32Allocator); + this._indexBuffer = gl.createBuffer(); + this._uint16Allocator = uint16Allocator; + this._numVerts = 0; + this._maxVerts = numVerts; + this._numTriangles = 0; + this._indexData = uint16Allocator.request(numIndices); +} + +derive(IndexedVertexArray, VertexArrayBase); + +IndexedVertexArray.prototype.destroy = function() { + VertexArrayBase.prototype.destroy.call(this); + this._gl.deleteBuffer(this._indexBuffer); + this._uint16Allocator.release(this._indexData); +}; + +IndexedVertexArray.prototype.numVerts = function() { return this._numVerts; }; +IndexedVertexArray.prototype.maxVerts = function() { return this._maxVerts; }; +IndexedVertexArray.prototype.numIndices = function() { return this._numTriangles * 3; }; + +IndexedVertexArray.prototype.addVertex = function(pos, normal, color, objId) { + var i = this._numVerts * this._FLOATS_PER_VERT; + this._vertData[i++] = pos[0]; + this._vertData[i++] = pos[1]; + this._vertData[i++] = pos[2]; + this._vertData[i++] = normal[0]; + this._vertData[i++] = normal[1]; + this._vertData[i++] = normal[2]; + this._vertData[i++] = color[0]; + this._vertData[i++] = color[1]; + this._vertData[i++] = color[2]; + this._vertData[i++] = color[3]; + this._vertData[i++] = objId; + this._numVerts += 1; + this._ready = false; +}; + +IndexedVertexArray.prototype._FLOATS_PER_VERT = 11; +IndexedVertexArray.prototype._OBJID_OFFSET = 10; +IndexedVertexArray.prototype._COLOR_OFFSET = 6; +IndexedVertexArray.prototype._NORMAL_OFFSET = 3; +IndexedVertexArray.prototype._POS_OFFSET = 0; + + +IndexedVertexArray.prototype.addTriangle = function(idx1, idx2, idx3) { + var index = 3 * this._numTriangles; + if (index >= this._indexData.length) { + return; + } + this._indexData[index++] = idx1; + this._indexData[index++] = idx2; + this._indexData[index++] = idx3; + this._numTriangles += 1; + this._ready = false; +}; + +IndexedVertexArray.prototype.bindBuffers = function() { + var ready = this._ready; + VertexArrayBase.prototype.bindBuffers.call(this); + this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + if (ready) { + return; + } + this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, this._indexData, + this._gl.STATIC_DRAW); +}; + +IndexedVertexArray.prototype.bindAttribs = function(shader) { + this._gl.enableVertexAttribArray(shader.posAttrib); + this._gl.vertexAttribPointer(shader.posAttrib, 3, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, this._POS_OFFSET * 4); + + if (shader.normalAttrib !== -1) { + this._gl.enableVertexAttribArray(shader.normalAttrib); + this._gl.vertexAttribPointer(shader.normalAttrib, 3, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, + this._NORMAL_OFFSET * 4); + } + + if (shader.colorAttrib !== -1) { + this._gl.vertexAttribPointer(shader.colorAttrib, 4, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, + this._COLOR_OFFSET * 4); + this._gl.enableVertexAttribArray(shader.colorAttrib); + } + if (shader.objIdAttrib !== -1) { + this._gl.vertexAttribPointer(shader.objIdAttrib, 1, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, this._OBJID_OFFSET * 4); + this._gl.enableVertexAttribArray(shader.objIdAttrib); + } +}; + +IndexedVertexArray.prototype.releaseAttribs = function(shader) { + + this._gl.disableVertexAttribArray(shader.posAttrib); + if (shader.colorAttrib !== -1) { + this._gl.disableVertexAttribArray(shader.colorAttrib); + } + if (shader.normalAttrib !== -1) { + this._gl.disableVertexAttribArray(shader.normalAttrib); + } + if (shader.objIdAttrib !== -1) { + this._gl.disableVertexAttribArray(shader.objIdAttrib); + } +}; + +IndexedVertexArray.prototype.bind = function(shader) { + this.bindBuffers(); + this.bindAttribs(shader); +}; + +// draws all triangles contained in the indexed vertex array using the provided +// shader. requires a call to bind() first. +IndexedVertexArray.prototype.draw = function() { + this._gl.drawElements(this._gl.TRIANGLES, this._numTriangles * 3, + this._gl.UNSIGNED_SHORT, 0); +}; + +exports.IndexedVertexArray = IndexedVertexArray; + +return true; +})(this); diff --git a/src/pdb/io.js b/src/pdb/io.js new file mode 100644 index 000000000..7ea3de204 --- /dev/null +++ b/src/pdb/io.js @@ -0,0 +1,256 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { + + +function Remark350Reader() { + this._assemblies = {}; + this._current = null; +} + +Remark350Reader.prototype.assemblies = function() { + var assemblies = []; + for (var c in this._assemblies) { + assemblies.push(this._assemblies[c]); + } + return assemblies; +}; + +Remark350Reader.prototype.assembly = function(id) { + return this._assemblies[id]; +}; + + +Remark350Reader.prototype.nextLine = function(line) { + line = line.substr(11); + if (line[0] === 'B' && line.substr(0, 12) === 'BIOMOLECULE:') { + var name = line.substr(13).trim(); + this._currentAssembly = new Assembly(name); + this._assemblies[name] = this._currentAssembly; + return; + } + if (line.substr(0, 30) === 'APPLY THE FOLLOWING TO CHAINS:' || + line.substr(0, 30) === ' AND CHAINS:') { + var chains = line.substr(30).split(','); + if (line[0] === 'A') { + this._currentSymGen = new SymGenerator(); + this._currentAssembly.addGenerator(this._currentSymGen); + } + this._currentMatrix = mat4.create(); + for (var i = 0; i < chains.length; ++i) { + var trimmedChainName = chains[i].trim(); + if (trimmedChainName.length) { + this._currentSymGen.addChain(trimmedChainName); + } + } + return; + } + if (line.substr(0, 7) === ' BIOMT') { + var col = parseInt(line[7], 10) - 1; + var offset = 0; + // for PDB files with 100 or more BIOMT matrices, the columns are + // shifted to the right by one digit (see PDB entry 1m4x, for + // example). The offset increases by one for every additional + // digit. + while (line[12 + offset] !== ' ') { + offset += 1; + } + var x = parseFloat(line.substr(13 + offset, 9)); + var y = parseFloat(line.substr(23 + offset, 9)); + var z = parseFloat(line.substr(33 + offset, 9)); + var w = parseFloat(line.substr(43 + offset, 14)); + var l = x*x + y*y + z*z; + this._currentMatrix[4*0+col] = x; + this._currentMatrix[4*1+col] = y; + this._currentMatrix[4*2+col] = z; + this._currentMatrix[4*3+col] = w; + if (col === 2) { + this._currentSymGen.addMatrix(this._currentMatrix); + this._currentMatrix = mat4.create(); + } + return; + } +}; + +function PDBReader() { + this._helices = []; + this._sheets = []; + this._structure = new mol.Mol(); + this._remark350Reader = new Remark350Reader(); + this._currChain = null; + this._currRes = null; + this._currAtom = null; +} + +PDBReader.prototype.parseHelixRecord = function(line) { + var frstNum = parseInt(line.substr(21, 4), 10); + var frstInsCode = line[25] === ' ' ? '\0' : line[25]; + var lastNum = parseInt(line.substr(33, 4), 10); + var lastInsCode = line[37] === ' ' ? '\0' : line[37]; + var chainName = line[19]; + this._helices.push({ first : [frstNum, frstInsCode], + last : [lastNum, lastInsCode], chainName : chainName + }); + return true; +}; + +PDBReader.prototype.parseSheetRecord = function(line) { + var frstNum = parseInt(line.substr(22, 4), 10); + var frstInsCode = line[26] === ' ' ? '\0' : line[26]; + var lastNum = parseInt(line.substr(33, 4), 10); + var lastInsCode = line[37] === ' ' ? '\0' : line[37]; + var chainName = line[21]; + this._sheets.push({ + first : [frstNum, frstInsCode], + last : [lastNum, lastInsCode], + chainName : chainName + }); + return true; +}; + +PDBReader.prototype.parseAndAddAtom = function(line, hetatm) { + var alt_loc = line[16]; + if (alt_loc !== ' ' && alt_loc !== 'A') { + return true; + } + var chainName = line[21]; + var resName = line.substr(17, 3).trim(); + var atomName = line.substr(12, 4).trim(); + var rnumNum = parseInt(line.substr(22, 4), 10); + var insCode = line[26] === ' ' ? '\0' : line[26]; + var updateResidue = false; + var updateChain = false; + if (!this._currChain || this._currChain.name() !== chainName) { + updateChain = true; + updateResidue = true; + } + if (!this._currRes || this._currRes.num() !== rnumNum || + this._currRes.insCode() !== insCode) { + updateResidue = true; + } + if (updateChain) { + // residues of one chain might appear interspersed with residues from + // other chains. + this._currChain = this._structure.chain(chainName) || + this._structure.addChain(chainName); + } + if (updateResidue) { + this._currRes = this._currChain.addResidue(resName, rnumNum, insCode); + } + var pos = vec3.create(); + for (var i=0;i<3;++i) { + pos[i] = (parseFloat(line.substr(30+i*8, 8))); + } + this._currRes.addAtom(atomName, pos, line.substr(76, 2).trim()); + return true; +}; + +PDBReader.prototype.processLine = function(line) { + var recordName = line.substr(0, 6); + if (recordName === 'ATOM ') { + return this.parseAndAddAtom(line, false); + } + if (recordName === 'HETATM') { + return this.parseAndAddAtom(line, true); + } + if (recordName === 'REMARK') { + // for now we are only interested in the biological assembly information + // contained in remark 350. + var remarkNumber = line.substr(7, 3); + if (remarkNumber === '350') { + this._remark350Reader.nextLine(line); + } + return true; + } + if (recordName === 'HELIX ') { + return this.parseHelixRecord(line); + } + if (recordName === 'SHEET ') { + return this.parseSheetRecord(line); + } + if (recordName === 'END ' || recordName === 'ENDMDL') { + return false; + } + return true; +}; + +// assigns the secondary structure information found in the helix sheet records, +// derives connectivity and assigns assembly information. +PDBReader.prototype.finish = function() { + var chain = null; + for (i = 0; i < this._sheets.length; ++i) { + var sheet = this._sheets[i]; + chain = this._structure.chain(sheet.chainName); + if (chain) { + chain.assignSS(sheet.first, sheet.last, 'E'); + } + } + for (i = 0; i < this._helices.length; ++i) { + var helix = this._helices[i]; + chain = this._structure.chain(helix.chainName); + if (chain) { + chain.assignSS(helix.first, helix.last, 'H'); + } + } + this._structure.setAssemblies(this._remark350Reader.assemblies()); + this._structure.deriveConnectivity(); + console.log('imported', this._structure.chains().length, 'chain(s),', + this._structure.residueCount(), 'residue(s)'); + return this._structure; +}; + +// a truly minimalistic PDB parser. It will die as soon as the input is +// not well-formed. it only reads ATOM, HETATM, HELIX, SHEET and REMARK +// 350 records, everything else is ignored. in case of multi-model +// files, only the first model is read. +// +// FIXME: load PDB currently spends a substantial amount of time creating +// the vec3 instances for the atom positions. it's possible that it's +// cheaper to initialize a bulk buffer once and create buffer views to +// that data for each atom position. since the atom's lifetime is bound to +// the parent structure, the buffer could be managed on that level and +// released once the structure is deleted. +function pdb(text) { + console.time('pdb'); + var reader = new PDBReader(); + var lines = text.split(/\r\n|\r|\n/g); + var i = 0; + for (i = 0; i < lines.length; i++) { + if (!reader.processLine(lines[i])) { + break; + } + } + var structure = reader.finish(); + console.timeEnd('pdb'); + return structure; +} + + +exports.io = {}; +exports.io.pdb = pdb; +exports.io.Remark350Reader = Remark350Reader; + + +}(this)); + + + + diff --git a/src/pdb/mol.js b/src/pdb/mol.js new file mode 100644 index 000000000..a6e84ec05 --- /dev/null +++ b/src/pdb/mol.js @@ -0,0 +1,1216 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +// atom covalent radii by element derived from Cambrige Structural Database. +// Source: http://profmokeur.ca/chemistry/covalent_radii.htm +var ELEMENT_COVALENT_RADII = { + H : 0.31, HE : 0.28, LI : 1.28, BE : 0.96, B : 0.84, C : 0.76, N : 0.71, + O : 0.66, F : 0.57, NE : 0.58, NA : 1.66, MG : 1.41, AL : 1.21, SI : 1.11, + P : 1.07, S : 1.05, CL : 1.02, AR : 1.06, K : 2.03, CA : 1.76, SC : 1.70, +TI : 1.60, V : 1.53, CR : 1.39, MN : 1.39, FE : 1.32, CO : 1.26, NI : 1.24, +CU : 1.32, ZN : 1.22, GA : 1.22, GE : 1.20, AS : 1.19, SE : 1.20, BR : 1.20, +KR : 1.16, RB : 2.20, SR : 1.95, Y : 1.90, ZR : 1.75, NB : 1.64, MO : 1.54, +TC : 1.47, RU : 1.46, RH : 1.42, PD : 1.39, AG : 1.45, CD : 1.44, IN : 1.42, +SN : 1.39, SB : 1.39, TE : 1.38, I : 1.39, XE : 1.40, CS : 2.44, BA : 2.15, +LA : 2.07, CE : 2.04, PR : 2.03, ND : 2.01, PM : 1.99, SM : 1.98, EU : 1.98, +GD : 1.96, TB : 1.94, DY : 1.92, HO : 1.92, ER : 1.89, TM : 1.90, YB : 1.87, +LU : 1.87, HF : 1.75, TA : 1.70, W : 1.62, RE : 1.51, OS : 1.44, IR : 1.41, +PT : 1.36, AU : 1.36, HG : 1.32, TL : 1.45, PB : 1.46, BI : 1.48, PO : 1.40, +AT : 1.50, RN : 1.50, FR : 2.60, RA : 2.21, AC : 2.15, TH : 2.06, PA : 2.00, + U : 1.96, NP : 1.90, PU : 1.87, AM : 1.80, CM : 1.69 +}; + +function covalentRadius(ele) { + var r = ELEMENT_COVALENT_RADII[ele.toUpperCase()]; + if (r !== undefined) { + return r; + } + return 1.5; +} + +// combines the numeric part of the residue number with the insertion +// code and returns a single number. Note that this is completely safe +// and we do not have to worry about overflows, as for PDB files the +// range of permitted residue numbers is quite limited anyway. +function rnumInsCodeHash(num, insCode) { + return num << 8 | insCode.charCodeAt(0); +} + +//----------------------------------------------------------------------------- +// MolBase, ChainBase, ResidueBase, AtomBase +//----------------------------------------------------------------------------- + +function MolBase() { +} + +MolBase.prototype.eachResidue = function(callback) { + for (var i = 0; i < this._chains.length; i+=1) { + if (this._chains[i].eachResidue(callback) === false) { + return false; + } + } +}; + +MolBase.prototype.eachAtom = function(callback, index) { + index |= 0; + for (var i = 0; i < this._chains.length; i+=1) { + index = this._chains[i].eachAtom(callback, index); + if (index === false) { + return false; + } + } +}; + +MolBase.prototype.residueCount = function () { + var chains = this.chains(); + var count = 0; + for (var ci = 0; ci < chains.length; ++ci) { + count += chains[ci].residues().length; + } + return count; +}; + +MolBase.prototype.eachChain = function(callback) { + var chains = this.chains(); + for (var i = 0; i < chains.length; ++i) { + if (callback(chains[i]) === false) { + return; + } + } +}; + +MolBase.prototype.atomCount = function() { + var chains = this.chains(); + var count = 0; + for (var ci = 0; ci < chains.length; ++ci) { + count += chains[ci].atomCount(); + } + return count; +}; + +MolBase.prototype.atoms = function() { + var atoms = []; + this.eachAtom(function(atom) { atoms.push(atom); }); + return atoms; +}; + +MolBase.prototype.atom = function(name) { + var parts = name.split('.'); + var chain = this.chain(parts[0]); + if (chain === null) { + return null; + } + var residue = chain.residueByRnum(parseInt(parts[1], 10)); + if (residue === null) { + return null; + } + return residue.atom(parts[2]); +}; + +MolBase.prototype.center = function() { + var sum = vec3.create(); + var count = 0; + this.eachAtom(function(atom) { + vec3.add(sum, sum, atom.pos()); + count+=1; + }); + if (count) { + vec3.scale(sum, sum, 1/count); + } + return sum; +}; + +// returns a sphere containing all atoms part of this structure. This will not +// calculate the minimal bounding sphere, just a good-enough approximation. +MolBase.prototype.boundingSphere = function() { + var center = this.center(); + var radiusSquare = 0.0; + this.eachAtom(function(atom) { + radiusSquare = Math.max(radiusSquare, vec3.sqrDist(center, atom.pos())); + }); + return new Sphere(center, radiusSquare); +}; + +// returns all backbone traces of all chains of this structure +MolBase.prototype.backboneTraces = function() { + var chains = this.chains(); + var traces = []; + for (var i = 0; i < chains.length; ++i) { + Array.prototype.push.apply(traces, chains[i].backboneTraces()); + } + return traces; +}; + + +MolBase.prototype.select = function(what) { + + if (what === 'protein') { + return this.residueSelect(function(r) { return r.isAminoacid(); }); + } + if (what === 'water') { + return this.residueSelect(function(r) { return r.isWater(); }); + } + if (what === 'ligand') { + return this.residueSelect(function(r) { + return !r.isAminoacid() && !r.isWater(); + }); + } + // when what is not one of the simple strings above, we assume what + // is a dictionary containing predicates which have to be fulfilled. + return this._dictSelect(what); +}; + +MolBase.prototype.selectWithin = (function() { + var dist = vec3.create(); + return function(mol, options) { + console.time('Mol.selectWithin'); + options = options || {}; + var radius = options.radius || 4.0; + var radiusSqr = radius*radius; + var matchResidues = !!options.matchResidues; + var targetAtoms = []; + mol.eachAtom(function(a) { targetAtoms.push(a); }); + + var view = new MolView(this.full()); + var addedRes = null, addedChain = null; + var chains = this.chains(); + var skipResidue = false; + for (var ci = 0; ci < chains.length; ++ci) { + var residues = chains[ci].residues(); + addedChain = null; + for (var ri = 0; ri < residues.length; ++ri) { + addedRes = null; + skipResidue = false; + var atoms = residues[ri].atoms(); + for (var ai = 0; ai < atoms.length; ++ai) { + if (skipResidue) { + break; + } + for (var wi = 0; wi < targetAtoms.length; ++wi) { + vec3.sub(dist, atoms[ai].pos(), targetAtoms[wi].pos()); + if (vec3.sqrLen(dist) > radiusSqr) { + continue; + } + if (!addedChain) { + addedChain = view.addChain(chains[ci].full(), false); + } + if (!addedRes) { + addedRes = + addedChain.addResidue(residues[ri].full(), matchResidues); + } + if (matchResidues) { + skipResidue = true; + break; + } + addedRes.addAtom(atoms[ai].full()); + } + } + } + } + console.timeEnd('Mol.selectWithin'); + return view; + }; +})(); + +MolBase.prototype.residueSelect = function(predicate) { + console.time('Mol.residueSelect'); + var view = new MolView(this.full()); + for (var ci = 0; ci < this._chains.length; ++ci) { + var chain = this._chains[ci]; + var chainView = null; + var residues = chain.residues(); + for (var ri = 0; ri < residues.length; ++ri) { + if (predicate(residues[ri])) { + if (!chainView) { + chainView = view.addChain(chain, false); + } + chainView.addResidue(residues[ri], true); + } + } + } + console.timeEnd('Mol.residueSelect'); + return view; +}; + +MolBase.prototype._atomPredicates = function(dict) { + + var predicates = []; + if (dict.aname !== undefined) { + predicates.push(function(a) { return a.name() === dict.aname; }); + } + if (dict.anames !== undefined) { + predicates.push(function(a) { + var n = a.name(); + for (var k = 0; k < dict.anames.length; ++k) { + if (n === dict.anames[k]) { + return true; + } + } + return false; + }); + } + return predicates; +}; + +// extracts the residue predicates from the dictionary. +// ignores rindices, rindexRange because they are handled separately. +MolBase.prototype._residuePredicates = function(dict) { + + var predicates = []; + if (dict.rname !== undefined) { + predicates.push(function(r) { return r.name() === dict.rname; }); + } + if (dict.rnames !== undefined) { + predicates.push(function(r) { + var n = r.name(); + for (var k = 0; k < dict.rnames.length; ++k) { + if (n === dict.rnames[k]) { + return true; + } + } + return false; + }); + } + if (dict.rnums !== undefined) { + var num_set = {}; + for (var i = 0; i < dict.rnums.length; ++i) { + num_set[dict.rnums[i]] = true; + } + predicates.push(function(r) { + var n = r.num(); + return num_set[n] === true; + }); + } + return predicates; +}; + +MolBase.prototype._chainPredicates = function(dict) { + var predicates = []; + if (dict.cname !== undefined) { + dict.chain = dict.cname; + } + if (dict.cnames !== undefined) { + dict.chains = dict.cnames; + } + if (dict.chain !== undefined) { + predicates.push(function(c) { return c.name() === dict.chain; }); + } + if (dict.chains !== undefined) { + predicates.push(function(c) { + var n = c.name(); + for (var k = 0; k < dict.chains.length; ++k) { + if (n === dict.chains[k]) { + return true; + } + } + return false; + }); + } + return predicates; +}; + +function fulfillsPredicates(obj, predicates) { + for (var i = 0; i < predicates.length; ++i) { + if (!predicates[i](obj)) { + return false; + } + } + return true; +} + +MolBase.prototype._dictSelect = function(dict) { + var view = new MolView(this); + var residuePredicates = this._residuePredicates(dict); + var atomPredicates = this._atomPredicates(dict); + var chainPredicates = this._chainPredicates(dict); + + for (var ci = 0; ci < this._chains.length; ++ci) { + var chain = this._chains[ci]; + if (!fulfillsPredicates(chain, chainPredicates)) { + continue; + } + var chainView = null; + var residues = chain.residues(); + if (dict.rindex) { + dict.rindices = [dict.rindex]; + } + if (dict.rnumRange) { + residues = + chain.residuesInRnumRange(dict.rnumRange[0], dict.rnumRange[1]); + } + var selResidues = [], i, e; + if (dict.rindexRange !== undefined) { + for (i = dict.rindexRange[0], + e = Math.min(residues.length, dict.rindexRange[1]); + i < e; ++i) { + selResidues.push(residues[i]); + } + residues = selResidues; + } else if (dict.rindices) { + if (dict.rindices.length !== undefined) { + selResidues = []; + for (i = 0; i < dict.rindices.length; ++i) { + selResidues.push(residues[dict.rindices[i]]); + } + residues = selResidues; + } + } + for (var ri = 0; ri < residues.length; ++ri) { + if (!fulfillsPredicates(residues[ri], residuePredicates)) { + continue; + } + if (!chainView) { + chainView = view.addChain(chain, false); + } + var residueView = null; + var atoms = residues[ri].atoms(); + for (var ai = 0; ai < atoms.length; ++ai) { + if (!fulfillsPredicates(atoms[ai], atomPredicates)) { + continue; + } + if (!residueView) { + residueView = chainView.addResidue(residues[ri], false); + } + residueView.addAtom(atoms[ai]); + } + } + } + return view; +}; + +function rnumComp(lhs, rhs) { + return lhs.num() < rhs.num(); +} + +function numify(val) { + return { num : function() { return val; }}; +} + + + +MolBase.prototype.assembly = function(id) { + var assemblies = this.assemblies(); + for (var i = 0; i < assemblies.length; ++i) { + if (assemblies[i].name() === id) { + return assemblies[i]; + } + } + return null; +}; + + +function ChainBase() { + +} + +ChainBase.prototype.eachAtom = function(callback, index) { + index |= 0; + for (var i = 0; i< this._residues.length; i+=1) { + index = this._residues[i].eachAtom(callback, index); + if (index === false) { + return false; + } + } + return index; +}; + +ChainBase.prototype.atomCount = function() { + var count = 0; + var residues = this.residues(); + for (var ri = 0; ri < residues.length; ++ri) { + count+= residues[ri].atoms().length; + } + return count; +}; + +ChainBase.prototype.eachResidue = function(callback) { + for (var i = 0; i < this._residues.length; i+=1) { + if (callback(this._residues[i]) === false) { + return false; + } + } +}; + + + +ChainBase.prototype.residues = function() { return this._residues; }; + +ChainBase.prototype.structure = function() { return this._structure; }; + + +ChainBase.prototype.asView = function() { + var view = new MolView(this.structure().full()); + view.addChain(this, true); + return view; + +}; + +ChainBase.prototype.residueByRnum = function(rnum) { + var residues = this.residues(); + if (this._rnumsOrdered) { + var index = binarySearch(residues, numify(rnum), rnumComp); + if (index === -1) { + return null; + } + return residues[index]; + } else { + for (var i = 0; i < residues.length; ++i) { + if (residues[i].num() === rnum) { + return residues[i]; + } + } + return null; + } +}; + + +ChainBase.prototype.prop = function(propName) { + return this[propName](); +}; + +function ResidueBase() { + +} + +ResidueBase.prototype.prop = function(propName) { + return this[propName](); +}; + +ResidueBase.prototype.isWater = function() { + return this.name() === 'HOH' || this.name() === 'DOD'; +}; + +ResidueBase.prototype.eachAtom = function(callback, index) { + index |= 0; + for (var i =0; i< this._atoms.length; i+=1) { + if (callback(this._atoms[i], index) === false) { + return false; + } + index +=1; + } + return index; +}; + +ResidueBase.prototype.qualifiedName = function() { + return this.chain().name()+'.'+this.name()+this.num(); +}; + +ResidueBase.prototype.atom = function(index_or_name) { + if (typeof index_or_name === 'string') { + for (var i =0; i < this._atoms.length; ++i) { + if (this._atoms[i].name() === index_or_name) { + return this._atoms[i]; + } + } + return null; + } + if (index_or_name >= this._atoms.length && index_or_name < 0) { + return null; + } + return this._atoms[index_or_name]; +}; + +// CA for amino acids, P for nucleotides, nucleosides +ResidueBase.prototype.centralAtom = function() { + if (this.isAminoacid()) { + return this.atom('CA'); + } + if (this.isNucleotide()) { + return this.atom('C3\''); + } + return null; +}; + + +ResidueBase.prototype.center = function() { + var count = 0; + var c = vec3.create(); + this.eachAtom(function(atom) { + vec3.add(c, c, atom.pos()); + count += 1; + }); + if (count > 0) { + vec3.scale(c, c, 1.0/count); + } + return c; +}; + +ResidueBase.prototype.isAminoacid = function() { + return this._isAminoacid; +}; + +ResidueBase.prototype.isNucleotide = function() { + return this._isNucleotide; +}; + + +function AtomBase() { +} + +AtomBase.prototype.name = function() { return this._name; }; +AtomBase.prototype.pos = function() { return this._pos; }; +AtomBase.prototype.element = function() { return this._element; }; +AtomBase.prototype.index = function() { return this._index; }; + +AtomBase.prototype.prop = function(propName) { + return this[propName](); +}; + +AtomBase.prototype.bondCount = function() { return this.bonds().length; }; +AtomBase.prototype.eachBond = function(callback) { + var bonds = this.bonds(); + for (var i = 0, e = bonds.length; i < e; ++i) { + callback(bonds[i]); + } +}; + +//----------------------------------------------------------------------------- +// Mol, Chain, Residue, Atom, Bond +//----------------------------------------------------------------------------- + +function Mol(pv) { + MolBase.prototype.constructor.call(this); + this._chains = []; + this._assemblies = []; + this._pv = pv; + this._nextAtomIndex = 0; +} + +derive(Mol, MolBase); + + + +Mol.prototype.addAssembly = function(assembly) { + this._assemblies.push(assembly); +}; + +Mol.prototype.setAssemblies = function(assemblies) { + this._assemblies = assemblies; +}; + +Mol.prototype.assemblies = function() { return this._assemblies; }; + +Mol.prototype.chains = function() { return this._chains; }; + +Mol.prototype.full = function() { return this; }; + +Mol.prototype.containsResidue = function(residue) { + return residue.full().structure() === this; +}; + +Mol.prototype.chain = function(name) { + for (var i = 0; i < this._chains.length; ++i) { + if (this._chains[i].name() === name) { + return this._chains[i]; + } + } + return null; +}; + +Mol.prototype.nextAtomIndex = function() { + var nextIndex = this._nextAtomIndex; + this._nextAtomIndex+=1; + return nextIndex; +}; + +Mol.prototype.addChain = function(name) { + var chain = new Chain(this, name); + this._chains.push(chain); + return chain; +}; + + +Mol.prototype.connect = function(atom_a, atom_b) { + var bond = new Bond(atom_a, atom_b); + atom_a.addBond(bond); + atom_b.addBond(bond); + return bond; +}; + + +function connectPeptides(structure, left, right) { + var cAtom = left.atom('C'); + var nAtom = right.atom('N'); + if (cAtom && nAtom) { + var sqrDist = vec3.sqrDist(cAtom.pos(), nAtom.pos()); + if (sqrDist < 1.6*1.6) { + structure.connect(nAtom, cAtom); + } + } +} + +function connectNucleotides(structure, left, right) { + var o3Prime = left.atom('O3\''); + var pAtom = right.atom('P'); + if (o3Prime && pAtom) { + var sqrDist = vec3.sqrDist(o3Prime.pos(), pAtom.pos()); + // FIXME: make sure 1.7 is a good threshold here... + if (sqrDist < 1.7*1.7) { + structure.connect(o3Prime, pAtom); + } + } +} + +// determine connectivity structure. for simplicity only connects atoms of the +// same residue and peptide bonds +Mol.prototype.deriveConnectivity = function() { + console.time('Mol.deriveConnectivity'); + var this_structure = this; + var prevResidue = null; + this.eachResidue(function(res) { + var sqr_dist; + var d = vec3.create(); + for (var i = 0; i < res.atoms().length; i+=1) { + var atomI = res.atom(i); + var covalentI = covalentRadius(atomI.element()); + for (var j = 0; j < i; j+=1) { + var atomJ = res.atom(j); + var covalentJ = covalentRadius(atomJ.element()); + sqr_dist = vec3.sqrDist(atomI.pos(), atomJ.pos()); + var lower = covalentI+covalentJ-0.30; + var upper = covalentI+covalentJ+0.30; + if (sqr_dist < upper*upper && sqr_dist > lower*lower) { + this_structure.connect(res.atom(i), res.atom(j)); + } + } + } + res._deduceType(); + if (prevResidue !== null) { + if (res.isAminoacid() && prevResidue.isAminoacid()) { + connectPeptides(this_structure, prevResidue, res); + } + if (res.isNucleotide() && prevResidue.isNucleotide()) { + connectNucleotides(this_structure, prevResidue, res); + } + } + prevResidue = res; + }); + console.timeEnd('Mol.deriveConnectivity'); +}; + +function Chain(structure, name) { + ChainBase.prototype.constructor.call(this); + this._structure = structure; + this._name = name; + this._cachedTraces = []; + this._residues = []; + this._rnumsOrdered = true; +} + +derive(Chain, ChainBase); + +Chain.prototype.name = function() { return this._name; }; + +Chain.prototype.full = function() { return this; }; + +Chain.prototype.addResidue = function(name, num, insCode) { + insCode = insCode || '\0'; + var residue = new Residue(this, name, num, insCode); + if (this._residues.length > 0 && this._rnumsOrdered) { + var combinedRNum = rnumInsCodeHash(num, insCode); + var last = this._residues[this._residues.length-1]; + var lastCombinedRNum = rnumInsCodeHash(last.num(),last.insCode()); + this._rnumsOrdered = lastCombinedRNum < combinedRNum; + } + this._residues.push(residue); + return residue; +}; + + +Chain.prototype.residuesInRnumRange = function(start, end) { + // FIXME: this currently only works with the numeric part, insertion + // codes are not honoured. + var matching = []; + var i, e; + if (this._rnumsOrdered === true) { + // binary search our way to heaven + var startIdx = indexFirstLargerEqualThan(this._residues, numify(start), + rnumComp); + if (startIdx === -1) { + return matching; + } + var endIdx = indexLastSmallerEqualThan(this._residues, numify(end), + rnumComp); + if (endIdx === -1) { + return matching; + } + for (i = startIdx; i <= endIdx; ++i) { + matching.push(this._residues[i]); + } + } else { + for (i = 0, e = this._residues.length; i !== e; ++i) { + var res = this._residues[i]; + if (res.num() >= start && res.num() <= end) { + matching.push(res); + } + } + } + return matching; +}; + +// assigns secondary structure to residues in range from_num to to_num. +Chain.prototype.assignSS = function(fromNumAndIns, toNumAndIns, ss) { + // FIXME: when the chain numbers are completely ordered, perform binary + // search to identify range of residues to assign secondary structure to. + var from = rnumInsCodeHash(fromNumAndIns[0], fromNumAndIns[1]); + var to = rnumInsCodeHash(toNumAndIns[0], toNumAndIns[1]); + for (var i = 1; i < this._residues.length-1; ++i) { + var res = this._residues[i]; + // FIXME: we currently don't set the secondary structure of the first and + // last residue of helices and sheets. that takes care of better + // transitions between coils and helices. ideally, this should be done + // in the cartoon renderer, NOT in this function. + var combined = rnumInsCodeHash(res.num(), res.insCode()); + if (combined <= from || combined >= to) { + continue; + } + res.setSS(ss); + } +}; + +// invokes a callback for each connected stretch of amino acids. these +// stretches are used for all trace-based rendering styles, e.g. sline, +// line_trace, tube, cartoon etc. +Chain.prototype.eachBackboneTrace = function(callback) { + this._cacheBackboneTraces(); + for (var i=0; i < this._cachedTraces.length; ++i) { + callback(this._cachedTraces[i]); + } +}; + +Chain.prototype._cacheBackboneTraces = function() { + if (this._cachedTraces.length > 0) { + return; + } + var stretch = new BackboneTrace(); + // true when the stretch consists of amino acid residues, false + // if the stretch consists of nucleotides. + var aaStretch = null; + for (var i = 0; i < this._residues.length; i+=1) { + var residue = this._residues[i]; + var isAminoacid = residue.isAminoacid(); + var isNucleotide = residue.isNucleotide(); + if ((aaStretch === true && !isAminoacid) || + (aaStretch === false && !isNucleotide) || + (aaStretch === null && !isNucleotide && !isAminoacid)) { + // a break in the trace: push stretch if there were enough residues + // in it and create new backbone trace. + if (stretch.length() > 1) { + this._cachedTraces.push(stretch); + } + aaStretch = null; + stretch = new BackboneTrace(); + continue; + } + if (stretch.length() === 0) { + stretch.push(residue); + aaStretch = residue.isAminoacid(); + continue; + } + // these checks are on purpose more relaxed than the checks we use in + // deriveConnectivity(). We don't really care about correctness of bond + // lengths here. The only thing that matters is that the residues are + // more or less close so that they could potentially/ be connected. + var prevAtom, thisAtom; + if (aaStretch) { + prevAtom = this._residues[i-1].atom('C'); + thisAtom = residue.atom('N'); + } else { + prevAtom = this._residues[i-1].atom('O3\''); + thisAtom = residue.atom('P'); + } + var sqrDist = vec3.sqrDist(prevAtom.pos(), thisAtom.pos()); + if (Math.abs(sqrDist - 1.5*1.5) < 1) { + stretch.push(residue); + } else { + if (stretch.length() > 1) { + this._cachedTraces.push(stretch); + stretch = new BackboneTrace(); + } + } + } + if (stretch.length() > 1) { + this._cachedTraces.push(stretch); + } +}; + + +// returns all connected stretches of amino acids found in this chain as +// a list. +Chain.prototype.backboneTraces = function() { + var traces = []; + this.eachBackboneTrace(function(trace) { traces.push(trace); }); + return traces; + +}; + +function Residue(chain, name, num, insCode) { + ResidueBase.prototype.constructor.call(this); + this._name = name; + this._num = num; + this._insCode = insCode; + this._atoms = []; + this._ss = 'C'; + this._chain = chain; + this._isAminoacid = false; + this._isNucleotide = false; + this._index = chain.residues().length; +} + +derive(Residue, ResidueBase); + +Residue.prototype._deduceType = function() { + this._isNucleotide = this.atom('P')!== null && this.atom('C3\'') !== null; + this._isAminoacid = this.atom('N') !== null && this.atom('CA') !== null && + this.atom('C') !== null && this.atom('O') !== null; +}; + +Residue.prototype.name = function() { return this._name; }; +Residue.prototype.insCode = function() { return this._insCode; }; + +Residue.prototype.num = function() { return this._num; }; + +Residue.prototype.full = function() { return this; }; + +Residue.prototype.addAtom = function(name, pos, element) { + var atom = new Atom(this, name, pos, element, this.structure().nextAtomIndex()); + this._atoms.push(atom); + return atom; +}; + +Residue.prototype.ss = function() { return this._ss; }; +Residue.prototype.setSS = function(ss) { this._ss = ss; }; +Residue.prototype.index = function() { return this._index; }; + +Residue.prototype.atoms = function() { return this._atoms; }; +Residue.prototype.chain = function() { return this._chain; }; + + +Residue.prototype.structure = function() { + return this._chain.structure(); +}; + +function Atom(residue, name, pos, element, index) { + AtomBase.prototype.constructor.call(this); + this._residue = residue; + this._bonds = []; + this._name = name; + this._pos = pos; + this._index = index; + this._element = element; +} + +derive(Atom, AtomBase); + +Atom.prototype.addBond = function(bond) { this._bonds.push(bond); }; +Atom.prototype.name = function() { return this._name; }; +Atom.prototype.bonds = function() { return this._bonds; }; +Atom.prototype.residue = function() { return this._residue; }; +Atom.prototype.structure = function() { return this._residue.structure(); }; +Atom.prototype.full = function() { return this; }; +Atom.prototype.qualifiedName = function() { + return this.residue().qualifiedName()+'.'+this.name(); +}; + +var Bond = function(atom_a, atom_b) { + var self = { + atom_one : atom_a, + atom_two : atom_b + }; + return { + atom_one : function() { return self.atom_one; }, + atom_two : function() { return self.atom_two; }, + + // calculates the mid-point between the two atom positions + mid_point : function(out) { + if (!out) { + out = vec3.create(); + } + vec3.add(out, self.atom_one.pos(), self.atom_two.pos()); + vec3.scale(out, out, 0.5); + return out; + } + }; +}; + +//----------------------------------------------------------------------------- +// MolView, ChainView, ResidueView, AtomView +//----------------------------------------------------------------------------- + +function MolView(mol) { + MolBase.prototype.constructor.call(this); + this._mol = mol; + this._chains = []; +} + +derive(MolView, MolBase); + +MolView.prototype.full = function() { return this._mol; }; + +MolView.prototype.assemblies = function() { return this._mol.assemblies(); }; + +// add chain to view +MolView.prototype.addChain = function(chain, recurse) { + var chainView = new ChainView(this, chain.full()); + this._chains.push(chainView); + if (recurse) { + var residues = chain.residues(); + for (var i = 0; i< residues.length; ++i) { + chainView.addResidue(residues[i], true); + } + } + return chainView; +}; + + +MolView.prototype.containsResidue = function(residue) { + if (!residue) { + return false; + } + var chain = this.chain(residue.chain().name()); + if (!chain) { + return false; + } + return chain.containsResidue(residue); +}; + + +MolView.prototype.chains = function() { return this._chains; }; + + +MolView.prototype.chain = function(name) { + for (var i = 0; i < this._chains.length; ++i) { + if (this._chains[i].name() === name) { + return this._chains[i]; + } + } + return null; +}; + +function ChainView(molView, chain) { + ChainBase.prototype.constructor.call(this); + this._chain = chain; + this._residues = []; + this._molView = molView; + this._residueMap = {}; +} + + +derive(ChainView, ChainBase); + +ChainView.prototype.addResidue = function(residue, recurse) { + var resView = new ResidueView(this, residue.full()); + this._residues.push(resView); + this._residueMap[residue.full().index()] = resView; + if (recurse) { + var atoms = residue.atoms(); + for (var i = 0; i < atoms.length; ++i) { + resView.addAtom(atoms[i].full()); + } + } + return resView; +}; + +ChainView.prototype.containsResidue = function(residue) { + var resView = this._residueMap[residue.full().index()]; + if (resView === undefined) { + return false; + } + return resView.full() === residue.full(); +}; + + +ChainView.prototype.eachBackboneTrace = function(callback) { + // backbone traces for the view must be based on the the full + // traces for the following reasons: + // - we must be able to display subsets with one residue in length, + // when they are part of a larger trace. + // - when a trace residue is not at the end, e.g. the C-terminal or + // N-terminal end of the full trace, the trace residue starts + // midway between the residue and the previous, and ends midway + // between the residue and the next. + // - the tangents for the Catmull-Rom spline depend on the residues + // before and after. Thus, to get the same curvature for a + // trace subset, the residues before and after must be taken + // into account. + var fullTraces = this._chain.backboneTraces(); + var traceSubsets = []; + for (var i = 0; i < fullTraces.length; ++i) { + var subsets = fullTraces[i].subsets(this._residues); + for (var j = 0; j < subsets.length; ++j) { + callback(subsets[j]); + } + } +}; + +ChainView.prototype.backboneTraces = function() { + var traces = []; + this.eachBackboneTrace(function(trace) { traces.push(trace); }); + return traces; +}; + +ChainView.prototype.full = function() { return this._chain; }; + +ChainView.prototype.name = function () { return this._chain.name(); }; + +ChainView.prototype.structure = function() { return this._molView; }; + +function ResidueView(chainView, residue) { + ResidueBase.prototype.constructor.call(this); + this._chainView = chainView; + this._atoms = []; + this._residue = residue; +} + + +derive(ResidueView, ResidueBase); + +ResidueView.prototype.addAtom = function(atom) { + var atomView = new AtomView(this, atom.full()); + this._atoms.push(atomView); +}; + +ResidueView.prototype.full = function() { return this._residue; }; +ResidueView.prototype.num = function() { return this._residue.num(); }; + +ResidueView.prototype.insCode = function() { + return this._residue.insCode(); +}; +ResidueView.prototype.ss = function() { return this._residue.ss(); }; +ResidueView.prototype.index = function() { return this._residue.index(); }; +ResidueView.prototype.chain = function() { return this._chainView; }; +ResidueView.prototype.name = function() { return this._residue.name(); }; + +ResidueView.prototype.atoms = function() { return this._atoms; }; +ResidueView.prototype.qualifiedName = function() { + return this._residue.qualifiedName(); +}; + +ResidueView.prototype.containsResidue = function(residue) { + return this._residue.full() === residue.full(); +}; + + + +function AtomView(resView, atom) { + AtomBase.prototype.constructor.call(this); + this._resView = resView; + this._atom = atom; + this._bonds = []; +} + + +derive(AtomView, AtomBase); + +AtomView.prototype.full = function() { return this._atom; }; +AtomView.prototype.name = function() { return this._atom.name(); }; +AtomView.prototype.pos = function() { return this._atom.pos(); }; +AtomView.prototype.element = function() { return this._atom.element(); }; +AtomView.prototype.residue = function() { return this._resView; }; +AtomView.prototype.bonds = function() { return this._atom.bonds(); }; +AtomView.prototype.index = function() { return this._atom.index(); }; +AtomView.prototype.qualifiedName = function() { + return this._atom.qualifiedName(); +}; + + + +var zhangSkolnickSS = (function() { + var posOne = vec3.create(); + var posTwo = vec3.create(); + return function(trace, i, distances, delta) { + for (var j = Math.max(0, i-2); j <= i; ++j) { + for (var k = 2; k < 5; ++k) { + if (j+k >= trace.length()) { + continue; + } + var d = vec3.dist(trace.posAt(posOne, j), + trace.posAt(posTwo, j+k)); + if (Math.abs(d - distances[k-2]) > delta) { + return false; + } + } + } + return true; + }; +})(); + +var isHelical = function(trace, i) { + var helixDistances = [5.45, 5.18, 6.37]; + var helixDelta = 2.1; + return zhangSkolnickSS(trace, i, helixDistances, helixDelta); +}; + +var isSheet = function(trace, i) { + var sheetDistances = [6.1, 10.4, 13.0]; + var sheetDelta = 1.42; + return zhangSkolnickSS(trace, i, sheetDistances, sheetDelta); +}; + +function traceAssignHelixSheet(trace) { + for (var i = 0; i < trace.length(); ++i) { + if (isHelical(trace, i)) { + trace.residueAt(i).setSS('H'); + continue; + } + if (isSheet(trace, i)) { + trace.residueAt(i).setSS('E'); + continue; + } + trace.residueAt(i).setSS('C'); + } +} + + +// assigns secondary structure information based on a simple and very fast +// algorithm published by Zhang and Skolnick in their TM-align paper. +// Reference: +// +// TM-align: a protein structure alignment algorithm based on the Tm-score +// (2005) NAR, 33(7) 2302-2309 +function assignHelixSheet(structure) { + console.time('mol.assignHelixSheet'); + var chains = structure.chains(); + for (var ci = 0; ci < chains.length; ++ci) { + var chain = chains[ci]; + chain.eachBackboneTrace(traceAssignHelixSheet); + } + console.timeEnd('mol.assignHelixSheet'); +} + +exports.mol = {}; + +exports.mol.Mol = Mol; +exports.mol.Chain = Chain; +exports.mol.Residue = Residue; +exports.mol.Atom = Atom; + +exports.mol.MolView = MolView; +exports.mol.ChainView = ChainView; +exports.mol.ResidueView = ResidueView; +exports.mol.AtomView = AtomView; +exports.mol.assignHelixSheet = assignHelixSheet; + +return true; + +})(this); diff --git a/src/pdb/render.js b/src/pdb/render.js new file mode 100644 index 000000000..5b925ca6e --- /dev/null +++ b/src/pdb/render.js @@ -0,0 +1,1061 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +var render = (function() { + "use strict"; + + var exports = {}; + + var R = 0.7071; + var COIL_POINTS = [ -R, -R, 0, R, -R, 0, R, R, 0, -R, R, 0 ]; + + var HELIX_POINTS = [ + -6 * R, + -1.0 * R, + 0, + 6 * R, + -1.0 * R, + 0, + 6 * R, + 1.0 * R, + 0, + -6 * R, + 1.0 * R, + 0 + ]; + var ARROW_POINTS = [ + -10 * R, + -1.0 * R, + 0, + 10 * R, + -1.0 * R, + 0, + 10 * R, + 1.0 * R, + 0, + -10 * R, + 1.0 * R, + 0 + ]; + +// performs an in-place smoothing over 3 consecutive positions. +var inplaceStrandSmoothing = (function() { + var bf = vec3.create(), af = vec3.create(), cf = vec3.create(); + return function(p, from, to, length) { + from = Math.max(from, 1); + to = Math.min(length - 1, to); + var startIndex = 3 * (from - 1); + vec3.set(bf, p[startIndex], p[startIndex + 1], p[startIndex + 2]); + vec3.set(cf, p[3 * from], p[3 * from + 1], p[3 * from + 2]); + for (var i = from; i < to; ++i) { + startIndex = 3 * (i + 1); + vec3.set(af, p[startIndex], p[startIndex + 1], p[startIndex + 2]); + p[3 * i + 0] = af[0] * 0.25 + cf[0] * 0.50 + bf[0] * 0.25; + p[3 * i + 1] = af[1] * 0.25 + cf[1] * 0.50 + bf[1] * 0.25; + p[3 * i + 2] = af[2] * 0.25 + cf[2] * 0.50 + bf[2] * 0.25; + vec3.copy(bf, cf); + vec3.copy(cf, af); + } + }; +})(); + +// derive a rotation matrix which rotates the z-axis onto tangent. when +// left is given and use_hint is true, x-axis is chosen to be as close +// as possible to left. +// +// upon returning, left will be modified to contain the updated left +// direction. +var buildRotation = (function() { + return function(rotation, tangent, left, up, use_left_hint) { + if (use_left_hint) { vec3.cross(up, tangent, left); + } else { + geom.ortho(up, tangent); + } + + vec3.cross(left, up, tangent); + vec3.normalize(up, up); + vec3.normalize(left, left); + rotation[0] = left[0]; + rotation[1] = left[1]; + rotation[2] = left[2]; + + rotation[3] = up[0]; + rotation[4] = up[1]; + rotation[5] = up[2]; + + rotation[6] = tangent[0]; + rotation[7] = tangent[1]; + rotation[8] = tangent[2]; +} +; +})(); + +var spheresForChain = (function() { + var color = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + + return function(meshGeom, vertAssoc, options, chain) { + var atomCount = chain.atomCount(); + var idRange = options.idPool.getContinuousRange(atomCount); + var vertsPerSphere = options.protoSphere.numVerts(); + var indicesPerSphere = options.protoSphere.numIndices(); + var radius = 1.5 * options.radiusMultiplier; + meshGeom.addIdRange(idRange); + meshGeom.addChainVertArray(chain, vertsPerSphere*atomCount, + indicesPerSphere*atomCount); + chain.eachAtom(function(atom) { + var va = meshGeom.vertArrayWithSpaceFor(vertsPerSphere); + options.color.colorFor(atom, color, 0); + var vertStart = va.numVerts(); + var objId = idRange.nextId({ geom: meshGeom, atom : atom }); + options.protoSphere.addTransformed(va, atom.pos(), radius, color, objId); + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(atom, va, vertStart, vertEnd); + }); + }; +})(); + +exports.spheres = function(structure, gl, options) { + console.time('spheres'); + var protoSphere = new ProtoSphere(options.sphereDetail, options.sphereDetail); + options.protoSphere = protoSphere; + var geom = new MeshGeom(gl, options.float32Allocator, options.uint16Allocator); + var vertAssoc = new AtomVertexAssoc(structure, true); + geom.addVertAssoc(vertAssoc); + geom.setShowRelated(options.showRelated); + options.color.begin(structure); + structure.eachChain(function(chain) { + spheresForChain(geom, vertAssoc, options, chain); + }); + options.color.end(structure); + console.timeEnd('spheres'); + return geom; +}; + + +var ballsAndSticksForChain = (function() { + var midPoint = vec3.create(), dir = vec3.create(); + var color = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + var left = vec3.create(), up = vec3.create(); + var rotation = mat3.create(); + return function(meshGeom, vertAssoc, options, chain) { + // determine required number of vertices and indices for this chain + var atomCount = chain.atomCount(); + var bondCount = 0; + chain.eachAtom(function(a) { bondCount += a.bonds().length; }); + var numVerts = atomCount * options.protoSphere.numVerts() + + bondCount * options.protoCyl.numVerts(); + var numIndices = atomCount * options.protoSphere.numIndices() + + bondCount * options.protoCyl.numIndices(); + meshGeom.addChainVertArray(chain, numVerts, numIndices); + var idRange = options.idPool.getContinuousRange(atomCount); + meshGeom.addIdRange(idRange); + // generate geometry for each atom + chain.eachAtom(function(atom) { + var atomVerts = options.protoSphere.numVerts() + + atom.bondCount() * options.protoCyl.numVerts(); + var va = meshGeom.vertArrayWithSpaceFor(atomVerts); + var vertStart = va.numVerts(); + var objId = idRange.nextId({ geom: meshGeom, atom : atom }); + + options.color.colorFor(atom, color, 0); + options.protoSphere.addTransformed(va, atom.pos(), options.radius, color, + objId); + atom.eachBond(function(bond) { + bond.mid_point(midPoint); + vec3.sub(dir, atom.pos(), midPoint); + var length = vec3.length(dir); + + vec3.scale(dir, dir, 1.0/length); + + buildRotation(rotation, dir, left, up, false); + + vec3.add(midPoint, midPoint, atom.pos()); + vec3.scale(midPoint, midPoint, 0.5); + options.protoCyl.addTransformed(va, midPoint, length, options.radius, + rotation, color, color, objId, objId); + }); + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(atom, va, vertStart, vertEnd); + }); + }; +})(); + +exports.ballsAndSticks = function(structure, gl, options) { + console.time('ballsAndSticks'); + var vertAssoc = new AtomVertexAssoc(structure, true); + var protoSphere = new ProtoSphere(options.sphereDetail, options.sphereDetail); + var protoCyl = new ProtoCylinder(options.arcDetail); + options.protoSphere = protoSphere; + options.protoCyl = protoCyl; + var meshGeom = new MeshGeom(gl, options.float32Allocator, + options.uint16Allocator); + meshGeom.addVertAssoc(vertAssoc); + meshGeom.setShowRelated(options.showRelated); + options.color.begin(structure); + structure.eachChain(function(chain) { + ballsAndSticksForChain(meshGeom, vertAssoc, options, chain); + }); + options.color.end(structure); + console.timeEnd('ballsAndSticks'); + return meshGeom; +}; + +var linesForChain = (function () { + var mp = vec3.create(); + var clr = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + return function(lineGeom, vertAssoc, chain, options) { + var lineCount = 0; + var atomCount = chain.atomCount(); + var idRange = options.idPool.getContinuousRange(atomCount); + lineGeom.addIdRange(idRange); + // determine number of required lines to draw the full structure + chain.eachAtom(function(atom) { + var numBonds = atom.bonds().length; + if (numBonds) { + lineCount += numBonds; + } else { + lineCount += 3; + } + }); + var va = lineGeom.addChainVertArray(chain, lineCount * 2); + chain.eachAtom(function(atom) { + // for atoms without bonds, we draw a small cross, otherwise these atoms + // would be invisible on the screen. + var vertStart = va.numVerts(); + var objId = idRange.nextId({ geom : lineGeom, atom: atom }); + if (atom.bonds().length) { + atom.eachBond(function(bond) { + bond.mid_point(mp); + options.color.colorFor(atom, clr, 0); + va.addLine(atom.pos(), clr, mp, clr, objId, objId); + }); + } else { + var cs = 0.2; + var pos = atom.pos(); + options.color.colorFor(atom, clr, 0); + va.addLine([ pos[0] - cs, pos[1], pos[2] ], clr, + [ pos[0] + cs, pos[1], pos[2] ], clr, objId, objId); + va.addLine([ pos[0], pos[1] - cs, pos[2] ], clr, + [ pos[0], pos[1] + cs, pos[2] ], clr, objId, objId); + va.addLine([ pos[0], pos[1], pos[2] - cs ], clr, + [ pos[0], pos[1], pos[2] + cs ], clr, objId, objId); + } + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(atom, va, vertStart, vertEnd); + }); + + }; +})(); + + +exports.lines = function(structure, gl, options) { + console.time('lines'); + var vertAssoc = new AtomVertexAssoc(structure, true); + options.color.begin(structure); + var lineCount = 0; + var lineGeom = new LineGeom(gl, options.float32Allocator); + lineGeom.setLineWidth(options.lineWidth); + var va = lineGeom.vertArray(); + lineGeom.addVertAssoc(vertAssoc); + lineGeom.setShowRelated(options.showRelated); + structure.eachChain(function(chain) { + linesForChain(lineGeom, vertAssoc, chain, options); + }); + options.color.end(structure); + console.timeEnd('lines'); + return lineGeom; +}; + +var _lineTraceNumVerts = function(traces) { + var numVerts = 0; + for (var i = 0; i < traces.length; ++i) { + numVerts += 2 * (traces[i].length() - 1); + } + return numVerts; +}; + +var makeLineTrace = (function() { + var colorOne = vec4.fromValues(0.0, 0.0, 0.0, 1.0), + colorTwo = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + var posOne = vec3.create(), posTwo = vec3.create(); + + return function makeLineTrace(lineGeom, vertAssoc, va, traceIndex, + trace, options) { + vertAssoc.addAssoc(traceIndex, va, 0, va.numVerts(), + va.numVerts() + 1); + + var colors = options.float32Allocator.request(trace.length() * 4); + var idRange = options.idPool.getContinuousRange(trace.length()); + var idOne = idRange.nextId({ geom: lineGeom, + atom : trace.centralAtomAt(0) }); + var idTwo; + lineGeom.addIdRange(idRange); + for (var i = 1; i < trace.length(); ++i) { + + options.color.colorFor(trace.centralAtomAt(i - 1), colorOne, 0); + colors[(i - 1) * 4 + 0] = colorOne[0]; + colors[(i - 1) * 4 + 1] = colorOne[1]; + colors[(i - 1) * 4 + 2] = colorOne[2]; + colors[(i - 1) * 4 + 3] = colorOne[3]; + options.color.colorFor(trace.centralAtomAt(i), colorTwo, 0); + trace.posAt(posOne, i - 1); + trace.posAt(posTwo, i); + idTwo = idRange.nextId({ + geom: lineGeom, atom : trace.centralAtomAt(i)}); + va.addLine(posOne, colorOne, posTwo, colorTwo, idOne, idTwo); + idOne = idTwo; + idTwo = null; + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(traceIndex, va, i, vertEnd - 1, + vertEnd + ((i === trace.length() - 1) ? 0 : 1)); + } + colors[trace.length() * 4 - 4] = colorTwo[0]; + colors[trace.length() * 4 - 3] = colorTwo[1]; + colors[trace.length() * 4 - 2] = colorTwo[2]; + colors[trace.length() * 4 - 1] = colorTwo[3]; + vertAssoc.setPerResidueColors(traceIndex, colors); + return traceIndex + 1; + }; +})(); + +var lineTraceForChain = function(lineGeom, vertAssoc, options, traceIndex, + chain) { + var backboneTraces = chain.backboneTraces(); + var numVerts = _lineTraceNumVerts(backboneTraces); + var va = lineGeom.addChainVertArray(chain, numVerts); + for (var i = 0; i < backboneTraces.length; ++i) { + traceIndex = makeLineTrace(lineGeom, vertAssoc, va, traceIndex, + backboneTraces[i], options); + } + return traceIndex; + + +}; +//-------------------------------------------------------------------------- +// Some thoughts on trace-based render styles +// +// * Backbone traces must be determined from the complete structure (Chain +// as opposed to ChainView). +// +// * For subsets, the trace must start midway between the residue before +// the visible part, and end midway after the last visible residue. +// +// * Curvature of trace subsets must be based on the full backbone trace. +//-------------------------------------------------------------------------- +exports.lineTrace = function(structure, gl, options) { + + + console.time('lineTrace'); + var vertAssoc = new TraceVertexAssoc(structure, 1, true); + options.color.begin(structure); + var chains = structure.chains(); + var lineGeom = new LineGeom(gl, options.float32Allocator); + lineGeom.setLineWidth(options.lineWidth); + var traceIndex = 0; + structure.eachChain(function(chain) { + traceIndex = lineTraceForChain(lineGeom, vertAssoc, options, + traceIndex, chain); + }); + lineGeom.addVertAssoc(vertAssoc); + lineGeom.setShowRelated(options.showRelated); + options.color.end(structure); + console.timeEnd('lineTrace'); + return lineGeom; +}; + +var _slineNumVerts = function(traces, splineDetail) { + var numVerts = 0; + for (var i = 0; i < traces.length; ++i) { + numVerts += 2 * (splineDetail * (traces[i].length() - 1) + 1); + } + return numVerts; +}; + +var slineMakeTrace = (function(trace) { + var posOne = vec3.create(), posTwo = vec3.create(); + var colorOne = vec4.fromValues(0.0, 0.0, 0.0, 1.0), colorTwo = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + return function(lineGeom, vertAssoc, va, options, traceIndex, trace) { + var firstSlice = trace.fullTraceIndex(0); + var positions = options.float32Allocator.request(trace.length() * 3); + var colors = options.float32Allocator.request(trace.length() * 4); + var objIds = []; + var i, e; + var idRange = options.idPool.getContinuousRange(trace.length()); + lineGeom.addIdRange(idRange); + for (i = 0; i < trace.length(); ++i) { + var atom = trace.centralAtomAt(i); + trace.smoothPosAt(posOne, i, options.strength); + options.color.colorFor(atom, colors, 4 * i); + positions[i * 3] = posOne[0]; + positions[i * 3 + 1] = posOne[1]; + positions[i * 3 + 2] = posOne[2]; + objIds.push(idRange.nextId({ geom : lineGeom, atom : atom })); + } + var idStart = objIds[0], idEnd = 0; + var sdiv = geom.catmullRomSpline(positions, trace.length(), + options.splineDetail, options.strength, + false, options.float32Allocator); + var interpColors = interpolateColor(colors, options.splineDetail); + var vertStart = va.numVerts(); + vertAssoc.addAssoc(traceIndex, va, firstSlice, vertStart, vertStart + 1); + var halfSplineDetail = Math.floor(options.splineDetail / 2); + var steps = geom.catmullRomSplineNumPoints(trace.length(), + options.splineDetail, false); + for (i = 1; i < steps; ++i) { + posOne[0] = sdiv[3 * (i - 1)]; + posOne[1] = sdiv[3 * (i - 1) + 1]; + posOne[2] = sdiv[3 * (i - 1) + 2]; + posTwo[0] = sdiv[3 * (i - 0)]; + posTwo[1] = sdiv[3 * (i - 0) + 1]; + posTwo[2] = sdiv[3 * (i - 0) + 2]; + + colorOne[0] = interpColors[4 * (i - 1) + 0]; + colorOne[1] = interpColors[4 * (i - 1) + 1]; + colorOne[2] = interpColors[4 * (i - 1) + 2]; + colorOne[3] = interpColors[4 * (i - 1) + 3]; + + colorTwo[0] = interpColors[4 * (i - 0) + 0]; + colorTwo[1] = interpColors[4 * (i - 0) + 1]; + colorTwo[2] = interpColors[4 * (i - 0) + 2]; + colorTwo[3] = interpColors[4 * (i - 0) + 3]; + var index = Math.floor((i + halfSplineDetail) / options.splineDetail); + idEnd = objIds[Math.min(objIds.length - 1, index)]; + va.addLine(posOne, colorOne, posTwo, colorTwo, idStart, idEnd); + idStart = idEnd; + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(traceIndex, va, firstSlice + i, vertEnd - 1, + vertEnd + ((i === trace.length - 1) ? 0 : 1)); + } + vertAssoc.setPerResidueColors(traceIndex, colors); + options.float32Allocator.release(positions); + options.float32Allocator.release(sdiv); + return traceIndex + 1; + }; +})(); + +var slineForChain = function(lineGeom, vertAssoc, options, chain, traceIndex) { + var backboneTraces = chain.backboneTraces(); + var numVerts = _slineNumVerts(backboneTraces, options.splineDetail); + var va = lineGeom.addChainVertArray(chain, numVerts); + for (var i = 0; i < backboneTraces.length; ++i) { + traceIndex = slineMakeTrace(lineGeom, vertAssoc, va, options, + traceIndex, backboneTraces[i]); + } + return traceIndex; +}; + +exports.sline = function(structure, gl, options) { + console.time('sline'); + options.color.begin(structure); + var vertAssoc = + new TraceVertexAssoc(structure, options.splineDetail, 1, true); + var lineGeom = new LineGeom(gl, options.float32Allocator); + lineGeom.setLineWidth(options.lineWidth); + lineGeom.setShowRelated(options.showRelated); + var traceIndex = 0; + structure.eachChain(function(chain) { + traceIndex = slineForChain(lineGeom, vertAssoc, options, chain, traceIndex); + }); + lineGeom.addVertAssoc(vertAssoc); + options.color.end(structure); + console.timeEnd('sline'); + return lineGeom; +}; + +var _traceNumVerts = function(traces, sphereNumVerts, cylNumVerts) { + var numVerts = 0; + for (var i = 0; i < traces.length; ++i) { + numVerts += traces[i].length() * sphereNumVerts; + numVerts += (traces[i].length() - 1) * cylNumVerts; + } + return numVerts; +}; + +var _traceNumIndices = function(traces, sphereNumIndices, cylNumIndices) { + var numIndices = 0; + for (var i = 0; i < traces.length; ++i) { + numIndices += traces[i].length() * sphereNumIndices; + numIndices += (traces[i].length() - 1) * cylNumIndices; + } + return numIndices; +}; + +var traceForChain = function(meshGeom, vertAssoc, options, traceIndex, chain) { + // determine number of verts required to render the traces + var traces = chain.backboneTraces(); + var numVerts = _traceNumVerts(traces, options.protoSphere.numVerts(), + options.protoCyl.numVerts()); + var numIndices = _traceNumIndices(traces, options.protoSphere.numIndices(), + options.protoCyl.numIndices()); + meshGeom.addChainVertArray(chain, numVerts, numIndices); + for (var ti = 0; ti < traces.length; ++ti) { + _renderSingleTrace(meshGeom, vertAssoc, traces[ti], traceIndex, options); + traceIndex++; + } + return traceIndex; +}; + +exports.trace = function(structure, gl, options) { + console.time('trace'); + + options.protoCyl = new ProtoCylinder(options.arcDetail); + options.protoSphere = + new ProtoSphere(options.sphereDetail, options.sphereDetail); + + var meshGeom = new MeshGeom(gl, options.float32Allocator, + options.uint16Allocator); + var vertAssoc = new TraceVertexAssoc(structure, 1, true); + meshGeom.addVertAssoc(vertAssoc); + meshGeom.setShowRelated(options.showRelated); + + options.color.begin(structure); + var traceIndex = 0; + structure.eachChain(function(chain) { + traceIndex = traceForChain(meshGeom, vertAssoc, options, traceIndex, chain); + }); + options.color.end(structure); + + console.timeEnd('trace'); + return meshGeom; +}; + +// calculates the number of vertices required for the cartoon and +// tube render styles +var _cartoonNumVerts = function(traces, vertsPerSlice, splineDetail) { + var numVerts = 0; + for (var i = 0; i < traces.length; ++i) { + numVerts += ((traces[i].length() - 1) * splineDetail + 1) * vertsPerSlice; + // triangles for capping the tube + numVerts += 2; + } + return numVerts; +}; + +var _cartoonNumIndices = function(traces, vertsPerSlice, splineDetail) { + var numIndices = 0; + for (var i = 0; i < traces.length; ++i) { + numIndices += (traces[i].length() * splineDetail - 1) * vertsPerSlice * 6; + // triangles for capping the tube + numIndices += 2 * 3 * vertsPerSlice; + } + return numIndices; +}; + +// creates the capped cylinders for DNA/RNA pointing towards the end of the bases. +var _addNucleotideSticks = (function() { + var rotation = mat3.create(); + var up = vec3.create(), left = vec3.create(), dir = vec3.create(); + var center = vec3.create(); + var color = vec4.create(); + return function(meshGeom, vertAssoc, traces, options) { + var radius = options.radius * 1.8; + for (var i = 0; i < traces.length; ++i) { + var trace = traces[i]; + var idRange = options.idPool.getContinuousRange(trace.length()); + for (var j = 0; j < trace.length(); ++j) { + var atomVerts = options.protoCyl.numVerts(); + var va = meshGeom.vertArrayWithSpaceFor(atomVerts); + var vertStart = va.numVerts(); + var residue = trace.residueAt(j); + var resName = residue.name(); + var startAtom = residue.atom('C3\''); + var endAtom = null; + if (resName === 'A' || resName === 'G' || resName === 'DA' || resName === 'DG') { + endAtom = residue.atom('N1'); + } else { + endAtom = residue.atom('N3'); + } + if (endAtom === null || startAtom === null) { + continue; + } + var objId = idRange.nextId({ geom: meshGeom, atom : endAtom }); + vec3.add(center, startAtom.pos(), endAtom.pos()); + vec3.scale(center, center, 0.5); + + options.color.colorFor(endAtom, color, 0); + vec3.sub(dir, endAtom.pos(), startAtom.pos()); + var length = vec3.length(dir); + vec3.scale(dir, dir, 1.0/length); + buildRotation(rotation, dir, left, up, false); + + options.protoCyl.addTransformed(va, center, length, radius, + rotation, color, color, objId, objId); + options.protoSphere.addTransformed(va, endAtom.pos(), radius, + color, objId); + options.protoSphere.addTransformed(va, startAtom.pos(), radius, + color, objId); + var vertEnd = va.numVerts(); + vertAssoc.addAssoc(endAtom, va, vertStart, vertEnd); + } + } + }; +})(); + +// generates the mesh geometry for displaying a single chain as either cartoon +// or tube (options.forceTube === true). +var cartoonForChain = function(meshGeom, vertAssoc, nucleotideAssoc, options, + traceIndex, chain) { + var traces = chain.backboneTraces(); + var numVerts = _cartoonNumVerts(traces, options.arcDetail * 4, + options.splineDetail); + var numIndices = _cartoonNumIndices(traces, options.arcDetail * 4, + options.splineDetail); + // figure out which of the traces consist of nucleic acids. They require additional + // space for rendering the sticks. + var nucleicAcidTraces = []; + var vertForBaseSticks = options.protoCyl.numVerts() + + 2 * options.protoSphere.numVerts(); + var indicesForBaseSticks = options.protoCyl.numIndices() + + 2 * options.protoSphere.numIndices(); + for (var i = 0; i < traces.length; ++i) { + var trace = traces[i]; + if (trace.residueAt(0).isNucleotide()) { + nucleicAcidTraces.push(trace); + // each DNA/RNA base gets a double-capped cylinder + numVerts += trace.length() * vertForBaseSticks; + numIndices += trace.length() * indicesForBaseSticks; + } + } + meshGeom.addChainVertArray(chain, numVerts, numIndices); + for (var ti = 0; ti < traces.length; ++ti) { + _cartoonForSingleTrace(meshGeom, vertAssoc, traces[ti], traceIndex, + options); + traceIndex++; + } + _addNucleotideSticks(meshGeom, nucleotideAssoc, nucleicAcidTraces, options); + return traceIndex; +}; + +exports.cartoon = function(structure, gl, options) { + console.time('cartoon'); + options.arrowSkip = Math.floor(options.splineDetail * 3 / 4); + options.coilProfile = new TubeProfile(COIL_POINTS, options.arcDetail, 1.0); + options.helixProfile = new TubeProfile(HELIX_POINTS, options.arcDetail, 0.1); + options.strandProfile = new TubeProfile(HELIX_POINTS, options.arcDetail, 0.1); + options.arrowProfile = new TubeProfile(ARROW_POINTS, options.arcDetail, 0.1); + options.protoCyl = new ProtoCylinder(options.arcDetail * 4); + options.protoSphere = new ProtoSphere(options.arcDetail * 4, options.arcDetail * 4); + + var meshGeom = new MeshGeom(gl, options.float32Allocator, + options.uint16Allocator); + var vertAssoc = new TraceVertexAssoc(structure, options.splineDetail, true); + meshGeom.addVertAssoc(vertAssoc); + meshGeom.setShowRelated(options.showRelated); + + options.color.begin(structure); + + var traceIndex = 0; + // the following vert-assoc is for rendering of DNA/RNA. Create vertex assoc + // from N1/N3 atoms only, this will speed up recoloring later on, which when + // performed on the complete structure, is slower than recalculating the + // whole geometry. + var selection = structure.select({anames: ['N1', 'N3']}); + var nucleotideAssoc = new AtomVertexAssoc(selection, true); + meshGeom.addVertAssoc(nucleotideAssoc); + structure.eachChain(function(chain) { + traceIndex = cartoonForChain(meshGeom, vertAssoc, nucleotideAssoc, options, + traceIndex, chain); + }); + + options.color.end(structure); + console.timeEnd('cartoon'); + return meshGeom; +}; + +exports.surface = (function() { + var pos = vec3.create(), normal = vec3.create(), + color = vec4.fromValues(0.8, 0.8, 0.8, 1.0); + return function(data, gl, options) { + var offset = 0; + var version = data.getUint32(0); + offset += 4; + var numVerts = data.getUint32(offset); + offset += 4; + var vertexStride = 4 * 6; + var facesDataStart = vertexStride * numVerts + offset; + var numFaces = data.getUint32(facesDataStart); + var meshGeom = new MeshGeom(gl, options.float32Allocator, + options.uint16Allocator); + meshGeom.setShowRelated('asym'); + var va = meshGeom.addVertArray(numVerts, numFaces * 3); + var i; + for (i = 0 ; i < numVerts; ++i) { + vec3.set(pos, data.getFloat32(offset + 0), data.getFloat32(offset + 4), + data.getFloat32(offset + 8)); + offset += 12; + vec3.set(normal, data.getFloat32(offset + 0), data.getFloat32(offset + 4), + data.getFloat32(offset + 8)); + offset += 12; + va.addVertex(pos, normal, color, 0); + } + offset = facesDataStart + 4; + for (i = 0 ; i < numFaces; ++i) { + var idx0 = data.getUint32(offset + 0), + idx1 = data.getUint32(offset + 4), + idx2 = data.getUint32(offset + 8); + offset += 12; + va.addTriangle(idx0 - 1, idx2 -1, idx1 - 1); + } + return meshGeom; + }; +})(); + +var _cartoonAddTube = (function() { + var rotation = mat3.create(); + var up = vec3.create(); + + return function(vertArray, pos, left, ss, tangent, color, radius, first, options, + offset, objId) { + var prof = options.coilProfile; + if (ss !== 'C' && !options.forceTube) { + if (ss === 'H') { + prof = options.helixProfile; + } else if (ss === 'E') { + prof = options.strandProfile; + } else if (ss === 'A') { + prof = options.arrowProfile; + } + } else { + if (first) { + geom.ortho(left, tangent); + } else { + vec3.cross(left, up, tangent); + } + } + + buildRotation(rotation, tangent, left, up, true); + prof.addTransformed(vertArray, pos, radius, rotation, color, first, + offset, objId); + }; +})(); + +// INTERNAL: fills positions, normals and colors from the information found in +// trace. The 3 arrays must already have the correct size (3*trace.length). +var _colorPosNormalsFromTrace = (function() { + var pos = vec3.create(); + var normal = vec3.create(), lastNormal = vec3.create(); + + return function(meshGeom, trace, colors, positions, normals, objIds, pool, + options) { + var strand_start = null, strand_end = null; + var trace_length = trace.length(); + vec3.set(lastNormal, 0.0, 0.0, 0.0); + for (var i = 0; i < trace_length; ++i) { + objIds.push(pool.nextId({ geom : meshGeom, + atom : trace.centralAtomAt(i)})); + trace.smoothPosAt(pos, i, options.strength); + positions[i * 3] = pos[0]; + positions[i * 3 + 1] = pos[1]; + positions[i * 3 + 2] = pos[2]; + + trace.smoothNormalAt(normal, i, options.strength); + + var atom = trace.centralAtomAt(i); + options.color.colorFor(atom, colors, i * 4); + + if (vec3.dot(normal, lastNormal) < 0) { + vec3.scale(normal, normal, -1); + } + if (trace.residueAt(i).ss() === 'E' && !options.forceTube) { + if (strand_start === null) { + strand_start = i; + } + strand_end = i; + } + if (trace.residueAt(i).ss() === 'C' && strand_start !== null) { + inplaceStrandSmoothing(positions, strand_start, strand_end, trace_length); + inplaceStrandSmoothing(normals, strand_start, strand_end, trace_length); + strand_start = null; + strand_end = null; + } + normals[i * 3] = positions[3 * i] + normal[0] + lastNormal[0]; + normals[i * 3 + 1] = positions[3 * i + 1] + normal[1] + lastNormal[1]; + normals[i * 3 + 2] = positions[3 * i + 2] + normal[2] + lastNormal[2]; + vec3.copy(lastNormal, normal); + } + }; +})(); + + +function capTubeStart(va, baseIndex, numTubeVerts) { + for (var i = 0; i < numTubeVerts - 1; ++i) { + va.addTriangle(baseIndex, baseIndex + 1 + i, baseIndex + 2 + i); + } + va.addTriangle(baseIndex, baseIndex + numTubeVerts, baseIndex + 1); +} + +function capTubeEnd(va, baseIndex, numTubeVerts) { + var center = baseIndex + numTubeVerts; + for (var i = 0; i < numTubeVerts - 1; ++i) { + va.addTriangle(center, baseIndex + i + 1, baseIndex + i); + } + va.addTriangle(center, baseIndex, baseIndex + numTubeVerts - 1); +} + +// constructs a cartoon representation for a single consecutive backbone +// trace. +var _cartoonForSingleTrace = (function() { + + var tangent = vec3.create(), pos = vec3.create(), left = vec3.create(), + color = vec4.fromValues(0.0, 0.0, 0.0, 1.0), normal = vec3.create(), normal2 = vec3.create(), + rot = mat3.create(); + + return function(meshGeom, vertAssoc, trace, traceIndex, options) { + var numVerts = + _cartoonNumVerts([trace], options.arcDetail * 4, options.splineDetail); + + var positions = options.float32Allocator.request(trace.length() * 3); + var colors = options.float32Allocator.request(trace.length() * 4); + var normals = options.float32Allocator.request(trace.length() * 3); + + var objIds = []; + var idRange = options.idPool.getContinuousRange(trace.length()); + _colorPosNormalsFromTrace(meshGeom, trace, colors, positions, normals, + objIds, idRange, options); + meshGeom.addIdRange(idRange); + var vertArray = meshGeom.vertArrayWithSpaceFor(numVerts); + var sdiv = geom.catmullRomSpline(positions, trace.length(), + options.splineDetail, options.strength, + false, options.float32Allocator); + var normalSdiv = geom.catmullRomSpline( + normals, trace.length(), options.splineDetail, options.strength, false, + options.float32Allocator); + vertAssoc.setPerResidueColors(traceIndex, colors); + var radius = options.radius * (trace.residueAt(0).isAminoacid() ? 1.0 : 1.8); + var interpColors = interpolateColor(colors, options.splineDetail); + // handle start of trace. this could be moved inside the for-loop, but + // at the expense of a conditional inside the loop. unrolling is + // slightly faster. + // + // we repeat the following steps for the start, central section and end + // of the profile: (a) assign position, normal, tangent and color, (b) + // add tube (or rectangular profile for helices and strands). + vec3.set(tangent, sdiv[3] - sdiv[0], sdiv[4] - sdiv[1], sdiv[5] - sdiv[2]); + vec3.set(pos, sdiv[0], sdiv[1], sdiv[2]); + vec3.set(normal, normalSdiv[0] - sdiv[0], normalSdiv[1] - sdiv[0], + normalSdiv[2] - sdiv[2]); + vec3.normalize(tangent, tangent); + vec3.normalize(normal, normal); + vec4.set(color, interpColors[0], interpColors[1], interpColors[2], + interpColors[3] ); + + var vertStart = vertArray.numVerts(); + vertArray.addVertex(pos, [-tangent[0], -tangent[1], -tangent[2]], + color, objIds[0]); + _cartoonAddTube(vertArray, pos, normal, trace.residueAt(0), tangent, + color, radius, true, options, 0, objIds[0]); + capTubeStart(vertArray, vertStart, options.arcDetail * 4); + var vertEnd = vertArray.numVerts(); + var slice = 0; + vertAssoc.addAssoc(traceIndex, vertArray, slice, vertStart, vertEnd); + slice += 1; + var halfSplineDetail = Math.floor(options.splineDetail / 2); + + // handle the bulk of the trace + var steps = geom.catmullRomSplineNumPoints(trace.length(), + options.splineDetail, false); + + for (var i = 1, e = steps; i < e; ++i) { + // compute 3*i, 3*(i-1), 3*(i+1) once and reuse + var ix3 = 3 * i, ix4 = 4 * i, ipox3 = 3 * (i + 1), imox3 = 3 * (i - 1); + + vec3.set(pos, sdiv[ix3], sdiv[ix3 + 1], sdiv[ix3 + 2]); + + if (i === e -1) { + vec3.set(tangent, sdiv[ix3] - sdiv[imox3], + sdiv[ix3 + 1] - sdiv[imox3 + 1], + sdiv[ix3 + 2] - sdiv[imox3 + 2]); + } else { + vec3.set(tangent, sdiv[ipox3] - sdiv[imox3], + sdiv[ipox3 + 1] - sdiv[imox3 + 1], + sdiv[ipox3 + 2] - sdiv[imox3 + 2]); + } + vec3.normalize(tangent, tangent); + vec4.set(color, interpColors[ix4], interpColors[ix4 + 1], + interpColors[ix4 + 2], interpColors[ix4 + 3]); + + var offset = 0; // <- set special handling of coil to helix,strand + // transitions. + var residueIndex = Math.floor(i / options.splineDetail); + var prevResidueIndex = Math.floor((i - 1) / options.splineDetail); + + // used to determine whether we have to add an arrow profile. when the + // current residue is the last strand residue, the arrow tip has to land + // exactly on the first slice of the next residue. Because we would like + // to have larger arrows we use multiple slices for the arrow (set to + // 3/4 of splineDetail). + var arrowEndIndex = Math.floor((i + options.arrowSkip) / options.splineDetail); + var drawArrow = false; + var thisSS = trace.residueAt(residueIndex).ss(); + if (!options.forceTube) { + if (residueIndex !== prevResidueIndex) { + // for helix and strand regions, we can't base the left vector + // of the current residue on the previous one, since it determines + // the orientation of the strand and helix profiles. + // + // frequently, the transition regions from coil to strand and helix + // contain strong twists which severely hamper visual quality. there + // is not problem however when transitioning from helix or strand + // to coil or inside a helix or strand. + // + // to avoid these visual artifacts, we calculate the best fit between + // the current normal and the normal "after" which gives us an offset + // for stitching the two parts together. + var prevSS = trace.residueAt(prevResidueIndex).ss(); + if (prevSS === 'C' && (thisSS === 'H' || thisSS === 'E')) { + // we don't want to generate holes, so we have to make sure + // the vertices of the rotated profile align with the previous + // profile. + vec3.set(normal2, normalSdiv[imox3] - sdiv[imox3], + normalSdiv[imox3 + 1] - sdiv[imox3 + 1], + normalSdiv[imox3 + 2] - sdiv[imox3 + 2]); + vec3.normalize(normal2, normal2); + var argAngle = 2 * Math.PI / (options.arcDetail * 4); + var signedAngle = geom.signedAngle(normal, normal2, tangent); + offset = Math.round(signedAngle / argAngle); + offset = (offset + options.arcDetail * 4) % (options.arcDetail * 4); + } + } + // figure out if we have to draw an arrow head + if (arrowEndIndex !== residueIndex && arrowEndIndex < trace.length()) { + var nextSS = trace.residueAt(arrowEndIndex).ss(); + if (nextSS === 'C' && thisSS === 'E') { + drawArrow = true; + } + } + } + // only set normal *after* handling the coil -> helix,strand + // transition, since we depend on the normal of the previous step. + vec3.set(normal, normalSdiv[3 * i] - sdiv[ix3], + normalSdiv[ix3 + 1] - sdiv[ix3 + 1], + normalSdiv[ix3 + 2] - sdiv[ix3 + 2]); + vec3.normalize(normal, normal); + vertStart = vertArray.numVerts(); + var objIndex = Math.floor((i + halfSplineDetail) / options.splineDetail); + var objId = objIds[Math.min(objIds.length - 1, objIndex)]; + _cartoonAddTube(vertArray, pos, normal, thisSS, + tangent, color, radius, false, options, offset, objId); + if (drawArrow) { + vertAssoc.addAssoc(traceIndex, vertArray, slice, vertStart, vertEnd); + // FIXME: arrow has completely wrong normals. Profile normals are + // generate perpendicular to the direction of the tube. The arrow + // normals are anti-parallel to the direction of the tube. + _cartoonAddTube(vertArray, pos, normal, 'A', + tangent, color, radius, false, options, 0, objId); + // We skip a few profiles to get a larger arrow. + i += options.arrowSkip; + } + vertEnd = vertArray.numVerts(); + if (i === e -1) { + vertEnd += 1; + } + vertAssoc.addAssoc(traceIndex, vertArray, slice, vertStart, vertEnd); + slice += 1; + if (drawArrow) { + slice += options.arrowSkip; + } + } + vertArray.addVertex(pos, tangent, color, objIds[objIds.length -1]); + capTubeEnd(vertArray, vertStart, options.arcDetail * 4); + options.float32Allocator.release(normals); + options.float32Allocator.release(positions); + }; +})(); + + +var _renderSingleTrace = (function() { + var rotation = mat3.create(); + var dir = vec3.create(), left = vec3.create(), up = vec3.create(), + midPoint = vec3.create(), caPrevPos = vec3.create(), + caThisPos = vec3.create(); + var colorOne = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + var colorTwo = vec4.fromValues(0.0, 0.0, 0.0, 1.0); + + return function(meshGeom, vertAssoc, trace, traceIndex, options) { + if (trace.length() === 0) { + return; + } + var idRange = options.idPool.getContinuousRange(trace.length()); + meshGeom.addIdRange(idRange); + options.color.colorFor(trace.centralAtomAt(0), colorOne, 0); + var numVerts = _traceNumVerts([trace], options.protoSphere.numVerts(), + options.protoCyl.numVerts()); + var va = meshGeom.vertArrayWithSpaceFor(numVerts); + var vertStart = va.numVerts(); + trace.posAt(caPrevPos, 0); + var idStart = idRange.nextId({ geom : meshGeom, + atom : trace.centralAtomAt(0)}), + idEnd = 0; + options.protoSphere.addTransformed(va, caPrevPos, options.radius, + colorOne, idStart); + var vertEnd = null; + vertAssoc.addAssoc(traceIndex, va, 0, vertStart, vertEnd); + var colors = options.float32Allocator.request(trace.length() * 4); + colors[0] = colorOne[0]; + colors[1] = colorOne[1]; + colors[2] = colorOne[2]; + colors[3] = colorOne[3]; + for (var i = 1; i < trace.length(); ++i) { + idEnd = idRange.nextId({ geom : meshGeom, atom : trace.centralAtomAt(i)}); + trace.posAt(caPrevPos, i - 1); + trace.posAt(caThisPos, i); + options.color.colorFor(trace.centralAtomAt(i), colorTwo, 0); + colors[i * 4 + 0] = colorTwo[0]; + colors[i * 4 + 1] = colorTwo[1]; + colors[i * 4 + 2] = colorTwo[2]; + colors[i * 4 + 3] = colorTwo[3]; + + vec3.sub(dir, caThisPos, caPrevPos); + var length = vec3.length(dir); + + vec3.scale(dir, dir, 1.0 / length); + + buildRotation(rotation, dir, left, up, false); + + vec3.copy(midPoint, caPrevPos); + vec3.add(midPoint, midPoint, caThisPos); + vec3.scale(midPoint, midPoint, 0.5); + var endSphere = va.numVerts(); + options.protoCyl.addTransformed(va, midPoint, length, + options.radius, rotation, colorOne, + colorTwo, idStart, idEnd); + vertEnd = va.numVerts(); + vertEnd = vertEnd - (vertEnd - endSphere) / 2; + + options.protoSphere.addTransformed(va, caThisPos, options.radius, + colorTwo, idEnd); + idStart = idEnd; + vertAssoc.addAssoc(traceIndex, va, i, vertStart, vertEnd); + vertStart = vertEnd; + vec3.copy(colorOne, colorTwo); + } + vertAssoc.setPerResidueColors(traceIndex, colors); + vertAssoc.addAssoc(traceIndex, va, trace.length() - 1, vertStart, + va.numVerts()); + }; +})(); + + +return exports; +})(); + +if(typeof(exports) !== 'undefined') { + module.exports = render; +} diff --git a/src/pdb/scene.js b/src/pdb/scene.js new file mode 100644 index 000000000..e14f83e63 --- /dev/null +++ b/src/pdb/scene.js @@ -0,0 +1,861 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +(function(exports) { + +"use strict"; + +function Range(min, max) { + if (min === undefined || max === undefined) { + this._empty = true; + this._min = this._max = null; + } else { + this._empty = false; + this._min = min; + this._max = max; + } +} + +Range.prototype.min = function() { + return this._min; +}; +Range.prototype.max = function() { + return this._max; +}; +Range.prototype.length = function() { + return this._max - this._min; +}; +Range.prototype.empty = function() { + return this._empty; +}; +Range.prototype.center = function() { + return (this._max + this._min) * 0.5; +}; + +Range.prototype.extend = function(amount) { + this._min -= amount; + this._max += amount; +}; + +Range.prototype.update = function(val) { + if (!this._empty) { + if (val < this._min) { + this._min = val; + } else if (val > this._max) { + this._max = val; + } + return; + } + this._min = this._max = val; + this._empty = false; +}; + +// A scene node holds a set of child nodes to be rendered on screen. Later on, +// the SceneNode might grow additional functionality commonly found in a scene +// graph, e.g. coordinate transformations. +function SceneNode(gl) { + this._children = []; + this._visible = true; + this._name = name || ''; + this._gl = gl; + this._order = 1; +} + +SceneNode.prototype.order = function(order) { + if (order !== undefined) { + this._order = order; + } + return this._order; +}; + +SceneNode.prototype.add = function(node) { + this._children.push(node); +}; + +SceneNode.prototype.draw = function(cam, shaderCatalog, style, pass) { + for (var i = 0, e = this._children.length; i !== e; ++i) { + this._children[i].draw(cam, shaderCatalog, style, pass); + } +}; + +SceneNode.prototype.show = function() { + this._visible = true; +}; + +SceneNode.prototype.hide = function() { + this._visible = false; +}; + +SceneNode.prototype.name = function(name) { + if (name !== undefined) { + this._name = name; + } + return this._name; +}; + +SceneNode.prototype.destroy = function() { + for (var i = 0; i < this._children.length; ++i) { + this._children[i].destroy(); + } +}; + +SceneNode.prototype.visible = function() { + return this._visible; +}; + +function BaseGeom(gl) { + SceneNode.prototype.constructor.call(this, gl); + this._idRanges = []; + this._vertAssocs = []; + this._showRelated = null; +} + +derive(BaseGeom, SceneNode); + +BaseGeom.prototype.setShowRelated = function(rel) { + this._showRelated = rel; + return rel; +}; + +BaseGeom.prototype.symWithIndex = function(index) { + if (this.showRelated() === 'asym') { + return null; + } + var assembly = this.structure().assembly(this.showRelated()); + if (!assembly) { + return null; + } + var gen = assembly.generators(); + for (var i = 0 ; i < gen.length; ++i) { + if (gen[i].matrices().length > index) { + return gen[i].matrix(index); + } + index -= gen[i].matrices().length; + } + return null; +}; + +BaseGeom.prototype.showRelated = function() { + return this._showRelated; +}; + +BaseGeom.prototype.select = function(what) { + return this.structure().select(what); +}; + +BaseGeom.prototype.structure = function() { + return this._vertAssocs[0]._structure; +}; + + +BaseGeom.prototype.getColorForAtom = function(atom, color) { + // FIXME: what to do in case there are multiple assocs? + return this._vertAssoc[0].getColorForAtom(atom, color); +}; + +BaseGeom.prototype.addIdRange = function(range) { + this._idRanges.push(range); +}; + +BaseGeom.prototype.destroy = function() { + SceneNode.prototype.destroy.call(this); + for (var i = 0; i < this._idRanges.length; ++i) { + this._idRanges[i].recycle(); + } +}; + + +BaseGeom.prototype.addVertAssoc = function(assoc) { + this._vertAssocs.push(assoc); +}; + +// returns all vertex arrays that contain geometry for one of the specified +// chain names. Typically, there will only be one array for a given chain, +// but for larger chains with mesh geometries a single chain may be split +// across multiple vertex arrays. +BaseGeom.prototype._vertArraysInvolving = function(chains) { + var vertArrays = this.vertArrays(); + var selectedArrays = []; + var set = {}; + for (var ci = 0; ci < chains.length; ++ci) { + set[chains[ci]] = true; + } + for (var i = 0; i < vertArrays.length; ++i) { + if (set[vertArrays[i].chain()] === true) { + selectedArrays.push(vertArrays[i]); + } + } + return selectedArrays; +}; + + +// draws vertex arrays by using the symmetry generators contained in assembly +BaseGeom.prototype._drawSymmetryRelated = function(cam, shader, assembly) { + var gens = assembly.generators(); + for (var i = 0; i < gens.length; ++i) { + var gen = gens[i]; + var affectedVertArrays = this._vertArraysInvolving(gen.chains()); + this._drawVertArrays(cam, shader, affectedVertArrays, gen.matrices()); + } +}; + +BaseGeom.prototype._updateProjectionIntervalsAsym = + function(xAxis, yAxis, zAxis, xInterval, yInterval, zInterval) { + var vertArrays = this.vertArrays(); + for (var i = 0; i < vertArrays.length; ++i) { + vertArrays[i].updateProjectionIntervals(xAxis, yAxis, zAxis, xInterval, + yInterval, zInterval); + } +}; + +BaseGeom.prototype.updateProjectionIntervals = + function(xAxis, yAxis, zAxis, xInterval, yInterval, zInterval) { + if (!this._visible) { + return; + } + var showRelated = this.showRelated(); + if (showRelated === 'asym') { + return this._updateProjectionIntervalsAsym(xAxis, yAxis, zAxis, xInterval, + yInterval, zInterval); + } + var assembly = this.structure().assembly(showRelated); + // in case there is no assembly, fallback to asymmetric unit and bail out. + if (!assembly) { + console.error('no assembly', showRelated, + 'found. Falling back to asymmetric unit'); + return this._updateProjectionIntervalsAsym(xAxis, yAxis, zAxis, xInterval, + yInterval, zInterval); + } + var gens = assembly.generators(); + for (var i = 0; i < gens.length; ++i) { + var gen = gens[i]; + var affectedVertArrays = this._vertArraysInvolving(gen.chains()); + for (var j = 0; j < gen.matrices().length; ++j) { + for (var k = 0; k < affectedVertArrays.length; ++k) { + var transform = gen.matrix(j); + affectedVertArrays[k].updateProjectionIntervals(xAxis, yAxis, zAxis, + xInterval, yInterval, + zInterval, transform); + } + } + } +}; + + +// FIXME: investigate the performance cost of sharing code between updateSquaredSphereRadius +// and updateProjectionIntervals +BaseGeom.prototype._updateSquaredSphereRadiusAsym = function(center, radius) { + var vertArrays = this.vertArrays(); + for (var i = 0; i < vertArrays.length; ++i) { + radius = vertArrays[i].updateSquaredSphereRadius(center, radius); + } + return radius; +}; + +BaseGeom.prototype.updateSquaredSphereRadius = function(center, radius) { + if (!this._visible) { + return radius; + } + var showRelated = this.showRelated(); + if (showRelated === 'asym') { + return this._updateSquaredSphereRadiusAsym(center, radius); + } + var assembly = this.structure().assembly(showRelated); + // in case there is no assembly, fallback to asymmetric unit and bail out. + if (!assembly) { + console.error('no assembly', showRelated, + 'found. Falling back to asymmetric unit'); + return this._updateSquaredSphereRadiusAsym(center, radius); + } + var gens = assembly.generators(); + for (var i = 0; i < gens.length; ++i) { + var gen = gens[i]; + var affectedVertArrays = this._vertArraysInvolving(gen.chains()); + for (var j = 0; j < gen.matrices().length; ++j) { + for (var k = 0; k < affectedVertArrays.length; ++k) { + var transform = gen.matrix(j); + radius = affectedVertArrays[k].updateSquaredSphereRadius(center, radius); + } + } + } + return radius; +}; + +BaseGeom.prototype.draw = function(cam, shaderCatalog, style, pass) { + + if (!this._visible) { + return; + } + + var shader = this.shaderForStyleAndPass(shaderCatalog, style, pass); + + if (!shader) { + return; + } + var showRelated = this.showRelated(); + if (showRelated === 'asym') { + return this._drawVertArrays(cam, shader, this.vertArrays(), null); + } + + var assembly = this.structure().assembly(showRelated); + // in case there is no assembly, fallback to asymmetric unit and bail out. + if (!assembly) { + console.error('no assembly', showRelated, + 'found. Falling back to asymmetric unit'); + return this._drawVertArrays(cam, shader, this.vertArrays(), null); + } + return this._drawSymmetryRelated(cam, shader, assembly); +}; + +BaseGeom.prototype.colorBy = function(colorFunc, view) { + console.time('BaseGeom.colorBy'); + this._ready = false; + view = view || this.structure(); + for (var i = 0; i < this._vertAssocs.length; ++i) { + this._vertAssocs[i].recolor(colorFunc, view); + } + console.timeEnd('BaseGeom.colorBy'); +}; + +BaseGeom.prototype.setOpacity = function(val, view) { + console.time('BaseGeom.setOpacity'); + this._ready = false; + view = view || this.structure(); + for (var i = 0; i < this._vertAssocs.length; ++i) { + this._vertAssocs[i].setOpacity(val, view); + } + console.timeEnd('BaseGeom.setOpacity'); +}; + +// Holds geometrical data for objects rendered as lines. For each vertex, +// the color and position is stored in an interleaved format. +function LineGeom(gl, float32Allocator) { + BaseGeom.prototype.constructor.call(this, gl); + this._vertArrays = []; + this._float32Allocator = float32Allocator; + this._vertAssocs = []; + this._lineWidth = 1.0; +} + +derive(LineGeom, BaseGeom); + + +LineGeom.prototype.addChainVertArray = function(chain, numVerts) { + var va = new LineChainData(chain.name(), this._gl, numVerts, + this._float32Allocator); + this._vertArrays.push(va); + return va; +}; + + +LineGeom.prototype.setLineWidth = function(width) { + this._lineWidth = width; +}; + +LineGeom.prototype.vertArrays = function() { + return this._vertArrays; +}; + +LineGeom.prototype.shaderForStyleAndPass = + function(shaderCatalog, style, pass) { + if (pass === 'outline') { + return null; + } + if (pass === 'select') { + return shaderCatalog.select; + } + return shaderCatalog.lines; +}; + +LineGeom.prototype.destroy = function() { + BaseGeom.prototype.destroy.call(this); + for (var i = 0; i < this._vertArrays.length; ++i) { + this._vertArrays[i].destroy(); + } + this._vertArrays = []; +}; + +LineGeom.prototype._drawVertArrays = function(cam, shader, vertArrays, + additionalTransforms) { + this._gl.lineWidth(this._lineWidth); + var i; + if (additionalTransforms) { + for (i = 0; i < vertArrays.length; ++i) { + vertArrays[i].drawSymmetryRelated(cam, shader, additionalTransforms); + } + } else { + this._gl.uniform1i(shader.symId, 255); + cam.bind(shader); + for (i = 0; i < vertArrays.length; ++i) { + vertArrays[i].bind(shader); + vertArrays[i].draw(); + vertArrays[i].releaseAttribs(shader); + } + } +}; + +LineGeom.prototype.vertArray = function() { return this._va; }; + + + +// an (indexed) mesh geometry container +// ------------------------------------------------------------------------ +// +// stores the vertex data in interleaved format. not doing so has severe +// performance penalties in WebGL, and severe means orders of magnitude +// slower than using an interleaved array. +// +// the vertex data is stored in the following format; +// +// Px Py Pz Nx Ny Nz Cr Cg Cb Ca Id +// +// , where P is the position, N the normal and C the color information +// of the vertex. +// +// Uint16 index buffer limit +// ----------------------------------------------------------------------- +// +// In WebGL, index arrays are restricted to uint16. The largest possible +// index value is smaller than the number of vertices required to display +// larger molecules. To work around this, MeshGeom allows to split the +// render geometry across multiple indexed vertex arrays. +function MeshGeom(gl, float32Allocator, uint16Allocator) { + BaseGeom.prototype.constructor.call(this, gl); + this._indexedVertArrays = [ ]; + this._float32Allocator = float32Allocator; + this._uint16Allocator = uint16Allocator; + this._remainingVerts = null; + this._remainingIndices = null; +} + +MeshGeom.prototype._boundedVertArraySize = function(size) { + return Math.min(65536, size); +}; + +MeshGeom.prototype.addChainVertArray = function(chain, numVerts, numIndices) { + this._remainingVerts = numVerts; + this._remainingIndices = numIndices; + var newVa = new MeshChainData(chain.name(), this._gl, + this._boundedVertArraySize(numVerts), + numIndices, + this._float32Allocator, + this._uint16Allocator); + this._indexedVertArrays.push(newVa); + return newVa; +}; + +MeshGeom.prototype.addVertArray = function(numVerts, numIndices) { + this._remainingVerts = numVerts; + this._remainingIndices = numIndices; + var newVa = new IndexedVertexArray( + this._gl, this._boundedVertArraySize(numVerts), numIndices, + this._float32Allocator, this._uint16Allocator); + + this._indexedVertArrays.push(newVa); + return newVa; +}; + +MeshGeom.prototype.vertArrayWithSpaceFor = function(numVerts) { + var currentVa = this._indexedVertArrays[this._indexedVertArrays.length - 1]; + var remaining = currentVa.maxVerts() - currentVa.numVerts(); + if (remaining >= numVerts) { + return currentVa; + } + this._remainingVerts = this._remainingVerts - currentVa.numVerts(); + this._remainingIndices = this._remainingIndices - currentVa.numIndices(); + numVerts = this._boundedVertArraySize(this._remainingVerts); + var newVa = null; + if (currentVa instanceof MeshChainData) { + newVa = new MeshChainData(currentVa.chain(), this._gl, numVerts, + this._remainingIndices, + this._float32Allocator, + this._uint16Allocator); + } else { + newVa = new IndexedVertexArray(this._gl, numVerts, this._remainingIndices, + this._float32Allocator, this._uint16Allocator); + } + this._indexedVertArrays.push(newVa); + return newVa; +}; + + +derive(MeshGeom, BaseGeom); + +MeshGeom.prototype.vertArray = function(index) { + return this._indexedVertArrays[index]; +}; + +MeshGeom.prototype.destroy = function() { + BaseGeom.prototype.destroy.call(this); + for (var i = 0; i < this._indexedVertArrays.length; ++i) { + this._indexedVertArrays[i].destroy(); + } + this._indexedVertArrays = []; +}; + +MeshGeom.prototype.numVerts = function() { + return this._indexedVertArrays[0].numVerts(); +}; + +MeshGeom.prototype.shaderForStyleAndPass = + function(shaderCatalog, style, pass) { + if (pass === 'outline') { + return shaderCatalog.outline; + } + if (pass === 'select') { + return shaderCatalog.select; + } + var shader = shaderCatalog[style]; + return shader !== undefined ? shader : null; +}; + +MeshGeom.prototype._drawVertArrays = function(cam, shader, indexedVertArrays, + additionalTransforms) { + var i; + if (additionalTransforms) { + for (i = 0; i < indexedVertArrays.length; ++i) { + indexedVertArrays[i].drawSymmetryRelated(cam, shader, additionalTransforms); + } + } else { + cam.bind(shader); + this._gl.uniform1i(shader.symId, 255); + for (i = 0; i < indexedVertArrays.length; ++i) { + indexedVertArrays[i].bind(shader); + indexedVertArrays[i].draw(); + indexedVertArrays[i].releaseAttribs(shader); + } + } +}; + +MeshGeom.prototype.vertArrays = function() { + return this._indexedVertArrays; +}; + +MeshGeom.prototype.addVertex = function(pos, normal, color, objId) { + var va = this._indexedVertArrays[0]; + va.addVertex(pos, normal, color, objId); +}; + +MeshGeom.prototype.addTriangle = function(idx1, idx2, idx3) { + var va = this._indexedVertArrays[0]; + va.addTriangle(idx1, idx2, idx3); +}; + + +function TextLabel(gl, canvas, context, pos, text) { + SceneNode.prototype.constructor.call(this, gl); + this._order = 100; + this._pos = pos; + this._interleavedBuffer = this._gl.createBuffer(); + this._interleavedData = new Float32Array(5 * 6); + + this._prepareText(canvas, context, text); + + var halfWidth = this._width / 2; + var halfHeight = this._height / 2; + this._interleavedData[0] = pos[0]; + this._interleavedData[1] = pos[1]; + this._interleavedData[2] = pos[2]; + this._interleavedData[3] = -halfWidth; + this._interleavedData[4] = -halfHeight; + + this._interleavedData[5] = pos[0]; + this._interleavedData[6] = pos[1]; + this._interleavedData[7] = pos[2]; + this._interleavedData[8] = halfWidth; + this._interleavedData[9] = halfHeight; + + this._interleavedData[10] = pos[0]; + this._interleavedData[11] = pos[1]; + this._interleavedData[12] = pos[2]; + this._interleavedData[13] = halfWidth; + this._interleavedData[14] = -halfHeight; + + this._interleavedData[15] = pos[0]; + this._interleavedData[16] = pos[1]; + this._interleavedData[17] = pos[2]; + this._interleavedData[18] = -halfWidth; + this._interleavedData[19] = -halfHeight; + + this._interleavedData[20] = pos[0]; + this._interleavedData[21] = pos[1]; + this._interleavedData[22] = pos[2]; + this._interleavedData[23] = -halfWidth; + this._interleavedData[24] = halfHeight; + + this._interleavedData[25] = pos[0]; + this._interleavedData[26] = pos[1]; + this._interleavedData[27] = pos[2]; + this._interleavedData[28] = halfWidth; + this._interleavedData[29] = halfHeight; +} + +TextLabel.prototype.updateProjectionIntervals = function() { + // text labels don't affect the projection interval. Don't do anything. +}; + +TextLabel.prototype.updateSquaredSphereRadius = function(center, radius) { + // text labels don't affect the bounding spheres. + return radius; +}; + +derive(TextLabel, SceneNode); + +TextLabel.prototype._setupTextParameters = function(ctx) { + ctx.fillStyle = 'black'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'bottom'; + ctx.font = '24px Verdana'; +}; + + +function smallestPowerOfTwo(size) { + var s = 1; + while (s < size) { + s *= 2; + } + return s; +} + +TextLabel.prototype._prepareText = function(canvas, ctx, text) { + this._setupTextParameters(ctx); + var estimatedWidth = ctx.measureText(text).width; + var estimatedHeight = 24; + canvas.width = smallestPowerOfTwo(estimatedWidth); + canvas.height = smallestPowerOfTwo(estimatedHeight); + this._setupTextParameters(ctx); + ctx.fillStyle = '#666'; + ctx.globalAlpha = 0.5; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.globalAlpha = 1.0; + ctx.fillStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.fillText(text, 0, canvas.height); + ctx.strokeText(text, 0, canvas.height); + this._texture = this._gl.createTexture(); + this._textureFromCanvas(this._texture, canvas); + this._xScale = estimatedWidth / canvas.width; + this._yScale = estimatedHeight / canvas.height; + this._width = estimatedWidth * 0.002; + this._height = estimatedHeight * 0.002; +}; + +TextLabel.prototype._textureFromCanvas = function(targetTexture, srcCanvas) { + this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true); + this._gl.bindTexture(this._gl.TEXTURE_2D, targetTexture); + this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, + this._gl.UNSIGNED_BYTE, srcCanvas); + this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, + this._gl.LINEAR); + this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, + this._gl.LINEAR_MIPMAP_LINEAR); + this._gl.generateMipmap(this._gl.TEXTURE_2D); + this._gl.bindTexture(this._gl.TEXTURE_2D, null); +}; + +TextLabel.prototype.bind = function() { + this._gl.bindBuffer(this._gl.ARRAY_BUFFER, this._interleavedBuffer); + this._gl.activeTexture(this._gl.TEXTURE0); + this._gl.bindTexture(this._gl.TEXTURE_2D, this._texture); + if (this._ready) { + return; + } + this._gl.bufferData(this._gl.ARRAY_BUFFER, this._interleavedData, + this._gl.STATIC_DRAW); + this._ready = true; +}; + +TextLabel.prototype.draw = function(cam, shaderCatalog, style, pass) { + if (!this._visible) { + return; + } + + if (pass !== 'normal') { + return; + } + var shader = shaderCatalog.text; + cam.bind(shader); + this.bind(); + this._gl.uniform1f(this._gl.getUniformLocation(shader, 'xScale'), + this._xScale); + this._gl.uniform1f(this._gl.getUniformLocation(shader, 'yScale'), + this._yScale); + this._gl.uniform1i(this._gl.getUniformLocation(shader, 'sampler'), 0); + var vertAttrib = this._gl.getAttribLocation(shader, 'attrCenter'); + this._gl.enableVertexAttribArray(vertAttrib); + this._gl.vertexAttribPointer(vertAttrib, 3, this._gl.FLOAT, false, 5 * 4, + 0 * 4); + var texAttrib = this._gl.getAttribLocation(shader, 'attrCorner'); + this._gl.vertexAttribPointer(texAttrib, 2, this._gl.FLOAT, false, 5 * 4, + 3 * 4); + this._gl.enableVertexAttribArray(texAttrib); + this._gl.enable(this._gl.BLEND); + this._gl.blendFunc(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA); + this._gl.drawArrays(this._gl.TRIANGLES, 0, 6); + this._gl.disableVertexAttribArray(vertAttrib); + this._gl.disableVertexAttribArray(texAttrib); + this._gl.disable(this._gl.BLEND); +}; + +// A continous range of object identifiers. +function ContinuousIdRange(pool, start, end) { + this._pool = pool; + this._start = start; + this._next = start; + this._end = end; +} + +ContinuousIdRange.prototype.nextId = function(obj) { + var id = this._next; + this._next++; + this._pool._objects[id] = obj; + return id; +}; +ContinuousIdRange.prototype.recycle = function() { + this._pool.recycle(this); +}; +ContinuousIdRange.prototype.length = function() { + return this._end - this._start; +}; + +function UniqueObjectIdPool() { + this._objects = {}; + this._unusedRangeStart = 0; + this._free = []; +} + +UniqueObjectIdPool.prototype.getContinuousRange = function(num) { + // FIXME: keep the "free" list sorted, so we can binary search it + // for a good match + var bestIndex = -1; + var bestLength = null; + for (var i = 0; i < this._free.length; ++i) { + var free = this._free[i]; + var length = free.length(); + if (length >= num && (bestLength === null || length < bestLength)) { + bestLength = length; + bestIndex = i; + } + } + if (bestIndex !== -1) { + var result = this._free[bestIndex]; + this._free.splice(bestIndex, 1); + return result; + } + var start = this._unusedRangeStart; + var end = start + num; + if (end > 65536) { + console.log('not enough free object ids.'); + return null; + } + this._unusedRangeStart = end; + return new ContinuousIdRange(this, start, end); +}; + +UniqueObjectIdPool.prototype.recycle = function(range) { + for (var i = range._start; i < range._next; ++i) { + delete this._objects[i]; + } + range._next = range._start; + this._free.push(range); +}; + +UniqueObjectIdPool.prototype.objectForId = function(id) { + return this._objects[id]; +}; + +function OrientedBoundingBox(gl, center, halfExtents, + float32Allocator, uint16Allocator) { + LineGeom.prototype.constructor.call(this, gl, 24, float32Allocator, + uint16Allocator); + var red = rgb.fromValues(1.0, 0.0, 0.0); + var green = rgb.fromValues(0.0, 1.0, 0.0); + var blue = rgb.fromValues(0.0, 0.0, 1.0); + var tf = mat3.create(); + tf[0] = halfExtents[0][0]; + tf[1] = halfExtents[0][1]; + tf[2] = halfExtents[0][2]; + + tf[3] = halfExtents[1][0]; + tf[4] = halfExtents[1][1]; + tf[5] = halfExtents[1][2]; + + tf[6] = halfExtents[2][0]; + tf[7] = halfExtents[2][1]; + tf[8] = halfExtents[2][2]; + var a = vec3.create(), b = vec3.create(); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ -1, -1, -1 ], tf)), red, + vec3.add(b, center, vec3.transformMat3(b, [ 1, -1, -1 ], tf)), red, -1); + + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, -1, -1 ], tf)), green, + vec3.add(b, center, vec3.transformMat3(b, [ 1, 1, -1 ], tf)), green, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, 1, -1 ], tf)), red, + vec3.add(b, center, vec3.transformMat3(b, [ -1, 1, -1 ], tf)), red, -1); + this.addLine(vec3.add(a, center, vec3.transformMat3(a, [ -1, 1, -1 ], tf)), + green, + vec3.add(b, center, vec3.transformMat3(b, [ -1, -1, -1 ], tf)), + green, -1); + + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ -1, -1, 1 ], tf)), red, + vec3.add(b, center, vec3.transformMat3(b, [ 1, -1, 1 ], tf)), red, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, -1, 1 ], tf)), green, + vec3.add(b, center, vec3.transformMat3(b, [ 1, 1, 1 ], tf)), green, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, 1, 1 ], tf)), red, + vec3.add(b, center, vec3.transformMat3(b, [ -1, 1, 1 ], tf)), red, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ -1, 1, 1 ], tf)), green, + vec3.add(b, center, vec3.transformMat3(b, [ -1, -1, 1 ], tf)), green, -1); + + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ -1, -1, -1 ], tf)), blue, + vec3.add(b, center, vec3.transformMat3(b, [ -1, -1, 1 ], tf)), blue, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, -1, -1 ], tf)), blue, + vec3.add(b, center, vec3.transformMat3(b, [ 1, -1, 1 ], tf)), blue, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ 1, 1, -1 ], tf)), blue, + vec3.add(b, center, vec3.transformMat3(b, [ 1, 1, 1 ], tf)), blue, -1); + this.addLine( + vec3.add(a, center, vec3.transformMat3(a, [ -1, 1, -1 ], tf)), blue, + vec3.add(b, center, vec3.transformMat3(b, [ -1, 1, 1 ], tf)), blue, -1); +} + +derive(OrientedBoundingBox, LineGeom); + +// don't do anything +OrientedBoundingBox.prototype.updateProjectionIntervals = function() {}; + +exports.SceneNode = SceneNode; +exports.OrientedBoundingBox = OrientedBoundingBox; +exports.AtomVertexAssoc = AtomVertexAssoc; +exports.TraceVertexAssoc = TraceVertexAssoc; +exports.MeshGeom = MeshGeom; +exports.LineGeom = LineGeom; +exports.TextLabel = TextLabel; +exports.UniqueObjectIdPool = UniqueObjectIdPool; +exports.Range = Range; +})(this); + diff --git a/src/pdb/shade.js b/src/pdb/shade.js new file mode 100644 index 000000000..baf5c85a4 --- /dev/null +++ b/src/pdb/shade.js @@ -0,0 +1,492 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +(function(exports) { +"use strict"; + +exports.rgb = {}; +var rgb = exports.rgb; + +exports.rgb.create = vec4.create; +exports.rgb.scale = vec4.scale; +exports.rgb.copy = vec4.copy; +exports.rgb.fromValues = vec4.fromValues; + +exports.rgb.mix = function(out, colorOne, colorTwo, t) { + var oneMinusT = 1.0 - t; + out[0] = colorOne[0]*t+colorTwo[0]*oneMinusT; + out[1] = colorOne[1]*t+colorTwo[1]*oneMinusT; + out[2] = colorOne[2]*t+colorTwo[2]*oneMinusT; + out[3] = colorOne[3]*t+colorTwo[3]*oneMinusT; + return out; +}; + +var COLORS = { + white : rgb.fromValues(1.0,1.0 ,1.0,1.0), + black : rgb.fromValues(0.0,0.0 ,0.0,1.0), + grey : rgb.fromValues(0.5,0.5 ,0.5,1.0), + lightgrey : rgb.fromValues(0.8,0.8 ,0.8,1.0), + darkgrey : rgb.fromValues(0.3,0.3 ,0.3,1.0), + red : rgb.fromValues(1.0,0.0 ,0.0,1.0), + darkred : rgb.fromValues(0.5,0.0 ,0.0,1.0), + lightred : rgb.fromValues(1.0,0.5 ,0.5,1.0), + green : rgb.fromValues(0.0,1.0 ,0.0,1.0), + darkgreen : rgb.fromValues(0.0,0.5 ,0.0,1.0), + lightgreen : rgb.fromValues(0.5,1.0 ,0.5,1.0), + blue : rgb.fromValues(0.0,0.0 ,1.0,1.0), + darkblue : rgb.fromValues(0.0,0.0 ,0.5,1.0), + lightblue : rgb.fromValues(0.5,0.5 ,1.0,1.0), + yellow : rgb.fromValues(1.0,1.0 ,0.0,1.0), + darkyellow : rgb.fromValues(0.5,0.5 ,0.0,1.0), + lightyellow : rgb.fromValues(1.0,1.0 ,0.5,1.0), + cyan : rgb.fromValues(0.0,1.0 ,1.0,1.0), + darkcyan : rgb.fromValues(0.0,0.5 ,0.5,1.0), + lightcyan : rgb.fromValues(0.5,1.0 ,1.0,1.0), + magenta : rgb.fromValues(1.0,0.0 ,1.0,1.0), + darkmagenta : rgb.fromValues(0.5,0.0 ,0.5,1.0), + lightmagenta : rgb.fromValues(1.0,0.5 ,1.0,1.0), + orange : rgb.fromValues(1.0,0.5 ,0.0,1.0), + darkorange : rgb.fromValues(0.5,0.25,0.0,1.0), + lightorange : rgb.fromValues(1.0,0.75,0.5,1.0) +}; + +// converts color strings to RGB. for now only supports color names. +// hex triples will need to be added. +exports.forceRGB = function(color) { + if (typeof color === 'string') { + var lookup = COLORS[color]; + if (lookup !== undefined) { + return lookup; + } + if (color.length > 0 && color[0] === '#') { + var r, g, b, a; + if (color.length === 4 || color.length === 5 ) { + r = parseInt(color[1], 16); + g = parseInt(color[2], 16); + b = parseInt(color[3], 16); + a = 15; + if(color.length===5) { + a = parseInt(color[4], 16); + } + var oneOver15 = 1/15.0; + return rgb.fromValues(oneOver15 * r, oneOver15 * g, oneOver15 * b, oneOver15 * a); + } + if (color.length === 7 || color.length === 9) { + r = parseInt(color.substr(1, 2), 16); + g = parseInt(color.substr(3, 2), 16); + b = parseInt(color.substr(5, 2), 16); + a = 255; + if(color.length===9) { + a = parseInt(color.substr(7, 2), 16); + } + var oneOver255 = 1/255.0; + return rgb.fromValues(oneOver255 * r, oneOver255 * g, oneOver255 * b, oneOver255 * a); + } + } + } + // in case no alpha component is provided, default alpha to 1.0 + if (color.length === 3) { + return [color[0], color[1], color[2], 1.0]; + } + return color; +}; + +function Gradient(colors, stops) { + this._colors = colors; + for (var i = 0; i < this._colors.length; ++i) { + this._colors[i] = exports.forceRGB(this._colors[i]); + } + this._stops = stops; +} + +Gradient.prototype.colorAt = function(out, value) { + if (value <= this._stops[0]) { + return vec4.copy(out, this._colors[0]); + } + if (value >= this._stops[this._stops.length-1]) { + return vec4.copy(out, this._colors[this._stops.length-1]); + } + // could use a binary search here, but since most gradients + // have a really small number of stops, that's not going to + // help much. + var lowerIndex = 0; + for (var i = 0; i < this._stops.length; ++i) { + if (this._stops[i] > value) { + break; + } + lowerIndex = i; + } + var upperIndex = lowerIndex+1; + var lowerStop = this._stops[lowerIndex]; + var upperStop = this._stops[upperIndex]; + var t = (value - lowerStop)/ (upperStop - lowerStop); + return rgb.mix(out, this._colors[upperIndex], this._colors[lowerIndex], t); +}; +var GRADIENTS = { }; +// creates a new gradient from the given set of colors. +// +// colors must be a valid list of colors. +// +// when stops is set to 'equal', then the color stops are +// assumed to be equi distant on the interval 0,1. otherwise, +// stops must be a list of floating point numbers with the +// same length than colors. +exports.gradient = function(colors, stops) { + if (typeof colors === 'string') { + return GRADIENTS[colors]; + } + stops = stops || 'equal'; + if (stops === 'equal') { + stops = []; + for (var i = 0; i < colors.length; ++i) { + stops.push(i*1.0/(colors.length-1)); + } + } + return new Gradient(colors, stops); +}; +var gradient = exports.gradient; + +GRADIENTS.rainbow =gradient(['red', 'yellow', 'green', 'blue']); +GRADIENTS.reds = gradient(['lightred', 'darkred']); +GRADIENTS.greens = gradient(['lightgreen', 'darkgreen']); +GRADIENTS.blues = gradient(['lightblue', 'darkblue']); +GRADIENTS.trafficlight = gradient(['green', 'yellow', 'red']); +GRADIENTS.heatmap = gradient(['red', 'white', 'blue']); + +function ColorOp(colorFunc, beginFunc, endFunc) { + this.colorFor = colorFunc; + this._beginFunc = beginFunc; + this._endFunc = endFunc; +} + +ColorOp.prototype.begin = function(obj) { + if (this._beginFunc) { + this._beginFunc(obj); + } +}; + + +ColorOp.prototype.end = function(obj) { + if (this._endFunc) { + this._endFunc(obj); + } +}; + +exports.color = {}; + +exports.ColorOp = ColorOp; + +exports.color.uniform = function(color) { + color = exports.forceRGB(color); + return new ColorOp(function(atom, out, index) { + out[index+0] = color[0]; + out[index+1] = color[1]; + out[index+2] = color[2]; + out[index+3] = color[3]; + }, null, null); +}; + +exports.color.byElement = function() { + return new ColorOp(function(atom, out, index) { + var ele = atom.element(); + if (ele === 'C') { + out[index] = 0.8; + out[index+1] = 0.8; + out[index+2] = 0.8; + out[index+3] = 1.0; + return out; + } + if (ele === 'N') { + out[index] = 0; + out[index+1] = 0; + out[index+2] = 1; + out[index+3] = 1.0; + return out; + } + if (ele === 'O') { + out[index] = 1; + out[index+1] = 0; + out[index+2] = 0; + out[index+3] = 1.0; + return out; + } + if (ele === 'S') { + out[index] = 0.8; + out[index+1] = 0.8; + out[index+2] = 0; + out[index+3] = 1.0; + return out; + } + if (ele === 'CA') { + out[index] = 0.533; + out[index+1] = 0.533; + out[index+2] = 0.666; + out[index+3] = 1.0; + return out; + } + out[index] = 1; + out[index+1] = 0; + out[index+2] = 1; + out[index+3] = 1.0; + return out; + }, null, null); +}; + +exports.color.bySS = function() { + + return new ColorOp(function(atom, out, index) { + switch (atom.residue().ss()) { + case 'C': + out[index] = 0.8; out[index+1] = 0.8; + out[index+2] = 0.8; out[index+3] = 1.0; + return; + case 'H': + out[index] = 0.6; out[index+1] = 0.6; + out[index+2] = 0.9; out[index+3] = 1.0; + return; + case 'E': + out[index] = 0.2; out[index+1] = 0.8; + out[index+2] = 0.2; out[index+3] = 1.0; + return; + } + }, null, null); +}; + +exports.color.rainbow = function(grad) { + if (!grad) { + grad = gradient('rainbow'); + } + var colorFunc = new ColorOp(function(a, out, index) { + var idx = a.residue().index(); + var limits = this.chainLimits[a.residue().chain().name()]; + var t = 0.0; + if (limits !== undefined) { + t = (idx - limits[0])/(limits[1]-limits[0]); + } + var x = [1,1,1,1]; + grad.colorAt(x, t); + out[index] = x[0]; + out[index+1] = x[1]; + out[index+2] = x[2]; + out[index+3] = x[3]; + }, function(obj) { + var chains = obj.chains(); + this.chainLimits = {}; + for (var i = 0; i < chains.length; ++i) { + var bb = chains[i].backboneTraces(); + if (bb.length === 0) { + continue; + } + var minIndex = bb[0].residueAt(0).index(), + maxIndex = bb[0].residueAt(bb[0].length()-1).index(); + for (var j = 1; j < bb.length; ++j) { + var bbj = bb[j]; + minIndex = Math.min(minIndex, bbj.residueAt(0).index()); + maxIndex = Math.max(maxIndex, bbj.residueAt(bbj.length()-1).index()); + } + this.chainLimits[chains[i].name()] = [minIndex, maxIndex]; + } + },function(obj) { + this.chainLimits = null; + }); + return colorFunc; +}; + +exports.color.ssSuccession = function(grad, coilColor) { + if (!grad) { + grad = gradient('rainbow'); + } + if (!coilColor) { + coilColor = forceRGB('lightgrey'); + } + var colorFunc = new ColorOp(function(a, out, index) { + var idx = a.residue().index(); + var limits = this.chainLimits[a.residue().chain().name()]; + var ssIndex = limits.indices[idx]; + if (ssIndex === -1) { + out[index] = coilColor[0]; + out[index+1] = coilColor[1]; + out[index+2] = coilColor[2]; + out[index+3] = coilColor[3]; + return; + } + var t = 0.0; + if (limits.max === null) { + } + if (limits.max !== null) { + t = ssIndex/(limits.max > 0 ? limits.max : 1); + } + var x = [0,0,0,0]; + grad.colorAt(x, t); + out[index] = x[0]; + out[index+1] = x[1]; + out[index+2] = x[2]; + out[index+3] = x[3]; + }, function(obj) { + var chains = obj.chains(); + this.chainLimits = {}; + for (var i = 0; i < chains.length; ++i) { + var residues = chains[i].residues(); + var maxIndex = null; + var indices = {}; + var ssIndex = 0; + var lastSS = 'C'; + for (var j = 0; j < residues.length; ++j) { + var ss = residues[j].ss(); + if (ss === 'C') { + if (lastSS !== 'C') { + ssIndex++; + } + indices[residues[j].index()] = -1; + } else { + maxIndex = ssIndex; + indices[residues[j].index()] = ssIndex; + } + lastSS = ss; + } + this.chainLimits[chains[i].name()] = { + indices : indices, + max: maxIndex + }; + } + },function(obj) { + this.chainLimits = null; + }); + return colorFunc; +}; + +exports.color.byChain = function(grad) { + if (!grad) { + grad = gradient('rainbow'); + } + var colorFunc = new ColorOp(function(a, out, index) { + var idx = a.residue().index(); + var chainIndex = this.chainIndices[a.residue().chain().name()]; + var t = chainIndex*this.scale; + var x = [0,0,0,0]; + grad.colorAt(x, t); + out[index+0] = x[0]; + out[index+1] = x[1]; + out[index+2] = x[2]; + out[index+3] = x[3]; + }, function(obj) { + var chains = obj.chains(); + this.chainIndices = {}; + for (var i = 0; i < chains.length; ++i) { + this.chainIndices[chains[i].name()] = i; + } + this.scale = chains.length > 1 ? 1.0/(chains.length-1) : 1.0; + },function(obj) { + this.chainIndices = null; + }); + return colorFunc; +}; + +function getMinMaxRange(obj, iter, propName) { + var min = null; + var max = null; + obj[iter](function(item) { + var value = item.prop(propName); + if (min === null && max === null) { + min = max = value; + return; + } + min = Math.min(min, value); + max = Math.max(max, value); + }); + return { min: min, max: max }; +} + +var gradColor = (function() { + var color = vec4.create(); + return function(out, index, grad, t) { + grad.colorAt(color, t); + out[index+0] = color[0]; + out[index+1] = color[1]; + out[index+2] = color[2]; + out[index+3] = color[3]; + }; +})(); + +function colorByItemProp(propName, grad, range, iter, item) { + if (!grad) { + grad = gradient('rainbow'); + } + return new ColorOp(function(a, out, index) { + var t = 0.0; + if (this._min !== this._max) { + t = (item(a).prop(propName) - this._min)/(this._max - this._min); + } + gradColor(out, index, grad, t); + }, + function(obj) { + if (range !== undefined) { + this._min = range[0]; + this._max = range[1]; + return; + } + range = getMinMaxRange(obj, iter, propName); + this._min = range.min; + this._max = range.max; + }, + function(obj) { } + ); +} + +exports.color.byAtomProp = function(propName, grad, range) { + return colorByItemProp(propName, grad, range, 'eachAtom', + function(a) {return a;}); +}; + +exports.color.byResidueProp = function(propName, grad, range) { + return colorByItemProp(propName, grad, range, 'eachResidue', + function(a) {return a.residue();}); +}; + +// linearly interpolates the array of colors and returns it as a Float32Array +// color must be an array containing a sequence of R,G,B triples. +exports.interpolateColor = function(colors, num) { + var out = new Float32Array((num*(colors.length/4-1) + 1)*4); + var index = 0; + var bf = vec4.create(), af = vec4.create(); + var delta = 1/num; + for (var i = 0; i < colors.length/4-1; ++i) { + vec4.set(bf, colors[4*i+0], colors[4*i+1], colors[4*i+2], colors[4*i+3]); + vec4.set(af, colors[4*i+4], colors[4*i+5], colors[4*i+6], colors[4*i+7]); + for (var j = 0; j < num; ++j) { + var t = delta * j; + out[index+0] = bf[0]*(1-t)+af[0]*t; + out[index+1] = bf[1]*(1-t)+af[1]*t; + out[index+2] = bf[2]*(1-t)+af[2]*t; + out[index+3] = bf[3]*(1-t)+af[3]*t; + index+=4; + } + } + out[index+0] = af[0]; + out[index+1] = af[1]; + out[index+2] = af[2]; + out[index+3] = af[3]; + return out; +}; + + + +return true; +})(this); diff --git a/src/pdb/shaders.js b/src/pdb/shaders.js new file mode 100644 index 000000000..74a1ca881 --- /dev/null +++ b/src/pdb/shaders.js @@ -0,0 +1,197 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { + +"use strict"; + +exports.shaders = {}; + +// line fragment shader, essentially uses the vertColor and adds some fog. +exports.shaders.LINES_FS = '\n\ +precision highp float;\n\ +\n\ +varying vec4 vertColor;\n\ +varying vec3 vertNormal;\n\ +uniform float fogNear;\n\ +uniform float fogFar;\n\ +uniform vec3 fogColor;\n\ +uniform bool fog;\n\ +\n\ +void main(void) {\n\ + gl_FragColor = vec4(vertColor);\n\ + if (gl_FragColor.a == 0.0) { discard; }\n\ + float depth = gl_FragCoord.z / gl_FragCoord.w;\n\ + if (fog) {\n\ + float fog_factor = smoothstep(fogNear, fogFar, depth);\n\ + gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w),\n\ + fog_factor);\n\ + }\n\ +}'; + +// hemilight fragment shader +exports.shaders.HEMILIGHT_FS = '\n\ +precision highp float;\n\ +\n\ +varying vec4 vertColor;\n\ +varying vec3 vertNormal;\n\ +uniform float fogNear;\n\ +uniform float fogFar;\n\ +uniform vec3 fogColor;\n\ +uniform bool fog;\n\ +\n\ +void main(void) {\n\ + float dp = dot(vertNormal, vec3(0.0, 0.0, 1.0));\n\ + float hemi = max(0.0, dp)*0.5+0.5;\n\ + hemi *= vertColor.a;\n\ + gl_FragColor = vec4(vertColor.rgb*hemi, vertColor.a);\n\ + if (gl_FragColor.a == 0.0) { discard; }\n\ + float depth = gl_FragCoord.z / gl_FragCoord.w;\n\ + if (fog) {\n\ + float fog_factor = smoothstep(fogNear, fogFar, depth);\n\ + gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w),\n\ + fog_factor);\n\ + }\n\ +}'; + +// hemilight vertex shader +exports.shaders.HEMILIGHT_VS = '\n\ +attribute vec3 attrPos;\n\ +attribute vec4 attrColor;\n\ +attribute vec3 attrNormal;\n\ +\n\ +uniform mat4 projectionMat;\n\ +uniform mat4 modelviewMat;\n\ +varying vec4 vertColor;\n\ +varying vec3 vertNormal;\n\ +void main(void) {\n\ + gl_Position = projectionMat * modelviewMat * vec4(attrPos, 1.0);\n\ + vec4 n = (modelviewMat * vec4(attrNormal, 0.0));\n\ + vertNormal = n.xyz;\n\ + vertColor = attrColor;\n\ +}'; + +// outline shader. mixes outlineColor with fogColor +exports.shaders.OUTLINE_FS = '\n\ +precision highp float;\n\ +varying float vertAlpha;\n\ +\n\ +uniform vec3 outlineColor;\n\ +uniform float fogNear;\n\ +uniform float fogFar;\n\ +uniform vec3 fogColor;\n\ +uniform bool fog;\n\ +\n\ +void main() {\n\ + gl_FragColor = vec4(outlineColor, vertAlpha);\n\ + if (gl_FragColor.a == 0.0) { discard; }\n\ + float depth = gl_FragCoord.z / gl_FragCoord.w;\n\ + if (fog) { \n\ + float fog_factor = smoothstep(fogNear, fogFar, depth);\n\ + gl_FragColor = mix(gl_FragColor, vec4(fogColor, vertAlpha),\n\ + fog_factor);\n\ + }\n\ +}'; + +// outline vertex shader. Expands vertices along the (in-screen) xy +// components of the normals. +exports.shaders.OUTLINE_VS = '\n\ +precision highp float;\n\ +\n\ +attribute vec3 attrPos;\n\ +attribute vec3 attrNormal;\n\ +attribute vec4 attrColor;\n\ + \n\ +uniform vec3 outlineColor;\n\ +uniform mat4 projectionMat;\n\ +uniform mat4 modelviewMat;\n\ +varying float vertAlpha;\n\ +\n\ +void main(void) {\n\ + gl_Position = projectionMat * modelviewMat * vec4(attrPos, 1.0);\n\ + vec4 normal = modelviewMat * vec4(attrNormal, 0.0);\n\ + vertAlpha = attrColor.a;\n\ + gl_Position.xy += normal.xy*0.200;\n\ +}'; + +exports.shaders.TEXT_VS = '\n\ +precision highp float;\n\ +\n\ +attribute vec3 attrCenter;\n\ +attribute vec2 attrCorner;\n\ +uniform mat4 projectionMat;\n\ +uniform mat4 modelviewMat;\n\ +uniform mat4 rotationMat;\n\ +varying vec2 vertTex;\n\ +void main() { \n\ + gl_Position = projectionMat* modelviewMat* vec4(attrCenter, 1.0);\n\ + gl_Position.xy += attrCorner*gl_Position.w; \n\ + gl_Position.z -= gl_Position.w*0.0005;\n\ + vertTex = (attrCorner+abs(attrCorner))/(2.0*abs(attrCorner)); \n\ +}'; + +exports.shaders.TEXT_FS = '\n\ +precision highp float;\n\ +\n\ +uniform mat4 projectionMat;\n\ +uniform mat4 modelviewMat;\n\ +uniform sampler2D sampler;\n\ +uniform float xScale;\n\ +uniform float yScale;\n\ +varying vec2 vertTex;\n\ +void main() { \n\ + gl_FragColor = texture2D(sampler, vec2(vertTex.x*xScale, vertTex.y*yScale));\n\ +}'; + +exports.shaders.SELECT_VS = '\n\ +precision highp float;\n\ +uniform mat4 projectionMat;\n\ +uniform mat4 modelviewMat;\n\ +attribute vec3 attrPos;\n\ +attribute float attrObjId;\n\ +\n\ +varying float objId;\n\ +\n\ +void main(void) {\n\ + gl_Position = projectionMat * modelviewMat * vec4(attrPos, 1.0);\n\ + objId = attrObjId;\n\ +}'; + +exports.shaders.SELECT_FS = '\n\ +precision highp float;\n\ +\n\ +varying float objId;\n\ +uniform int symId;\n\ +\n\ +int intMod(int x, int y) { \n\ + int z = x/y;\n\ + return x-y*z;\n\ +}\n\ +void main(void) {\n\ + // ints are only required to be 7bit...\n\ + int integralObjId = int(objId+0.5);\n\ + int red = intMod(integralObjId, 256);\n\ + integralObjId/=256;\n\ + int green = intMod(integralObjId, 256);\n\ + integralObjId/=256;\n\ + int blue = symId;\n\ + gl_FragColor = vec4(float(red), float(green), float(blue), 255.0)/255.0;\n\ +}'; +})(this); diff --git a/src/pdb/slab.js b/src/pdb/slab.js new file mode 100644 index 000000000..cc4ab4e1f --- /dev/null +++ b/src/pdb/slab.js @@ -0,0 +1,67 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +function Slab(near, far) { + this.near = near; + this.far = far; +} + +function FixedSlab(options) { + options = options || {}; + this._near = options.near || 0.1; + this._far = options.far || 400.0; +} + +FixedSlab.prototype.update = function() { + return new Slab(this._near, this._far); +}; + +function AutoSlab(options) { + this._far = 100.0; +} + +AutoSlab.prototype.update = function(objects, cam) { + var center = cam.center(); + var radius = null; + for (var i = 0; i < objects.length; ++i) { + var obj = objects[i]; + if (!obj.visible()) { + continue; + } + radius = obj.updateSquaredSphereRadius(center, radius); + } + if (radius === null) { + return null; + } + radius = Math.sqrt(radius); + var zoom = cam.zoom(); + var newFar = (radius + zoom) * 1.05; + var newNear = 0.1;//Math.max(0.1, zoom - radius); + return new Slab(newNear, newFar); +}; + +exports.FixedSlab = FixedSlab; +exports.AutoSlab = AutoSlab; +exports.Slab = Slab; + +})(this); diff --git a/src/pdb/symmetry.js b/src/pdb/symmetry.js new file mode 100644 index 000000000..42cb72730 --- /dev/null +++ b/src/pdb/symmetry.js @@ -0,0 +1,66 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +(function(exports) { +"use strict"; + +// a list of rotation/translation operators to be applied to certain chains, +// typically of the asymmetric unit. +function SymGenerator(chains, matrices) { + this._chains = chains || []; + this._matrices = matrices || []; +} + +SymGenerator.prototype.addChain = function(name) { + this._chains.push(name); +}; + +SymGenerator.prototype.chains = function() { return this._chains; }; + +SymGenerator.prototype.addMatrix = function(matrix) { + this._matrices.push(matrix); +}; + +SymGenerator.prototype.matrices = function() { return this._matrices; }; +SymGenerator.prototype.matrix = function(index) { return this._matrices[index]; }; + +// contains the definition for how to construct a biological assembly from +// an asymmetric unit. Essentially a list of rotation/translation operators +// to be applied to chains of the asymmetric unit. +function Assembly(name) { + this._name = name; + this._generators = []; +} + +Assembly.prototype.name = function() { return this._name; }; + +Assembly.prototype.generators = function() { return this._generators; }; +Assembly.prototype.generator = function(index) { return this._generators[index]; }; +Assembly.prototype.addGenerator = function(gen) { + this._generators.push(gen); +}; + +exports.SymGenerator = SymGenerator; +exports.Assembly = Assembly; + +return true; +})(this); + + diff --git a/src/pdb/trace.js b/src/pdb/trace.js new file mode 100644 index 000000000..c72cf4080 --- /dev/null +++ b/src/pdb/trace.js @@ -0,0 +1,223 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +function BackboneTrace() { this._trace = []; } + +if(typeof(exports) !== 'undefined') { + exports.backboneTrace = BackboneTrace; +} + +BackboneTrace.prototype.push = function(residue) { + this._trace.push(residue); +}; + +BackboneTrace.prototype.length = function() { + return this._trace.length; +}; + +BackboneTrace.prototype.residueAt = function(index) { + return this._trace[index]; +}; + +BackboneTrace.prototype.posAt = function(out, index) { + vec3.copy(out, this._trace[index].centralAtom().pos()); + return out; +}; + +BackboneTrace.prototype.normalAt = function(out, index) { + var residue = this._trace[index]; + if (residue.isAminoacid()) { + vec3.sub(out, residue.atom('O').pos(), residue.atom('C').pos()); + } + vec3.normalize(out, out); + return out; +}; + +// nothing needs to be done for the backbone trace. +BackboneTrace.prototype.smoothPosAt = BackboneTrace.prototype.posAt; +BackboneTrace.prototype.smoothNormalAt = BackboneTrace.prototype.normalAt; + +BackboneTrace.prototype.centralAtomAt = function(index) { + return this._trace[index].centralAtom(); +}; + +BackboneTrace.prototype.tangentAt = (function() { + var posBefore = vec3.create(); + var posAfter = vec3.create(); + return function(out, index) { + if (index > 0) { this.posAt(posBefore, index - 1); + } else { + this.posAt(posBefore, index); + } + if (index < this._trace.length-1) { + this.posAt(posAfter, index + 1); + } else { + this.posAt(posAfter, index); + } + vec3.sub(out, posAfter, posBefore); +} +; +})(); + +BackboneTrace.prototype.fullTraceIndex = function(index) { + return index; +}; + +BackboneTrace.prototype.subsets = function(residues) { + // we assume that the residue list is ordered from N- to C- + // terminus and we can traverse it in one go. + var fullTraceIdx = 0, listIdx = 0; + var subsets = []; + while (listIdx < residues.length && fullTraceIdx < this._trace.length) { + // increase pointer until we residue indices match. + var residueIndex = residues[listIdx].full().index(); + while (this._trace.length > fullTraceIdx && + this._trace[fullTraceIdx].index() < residueIndex) { + ++fullTraceIdx; + } + if (fullTraceIdx >= this._trace.length) { + break; + } + var traceIndex = this._trace[fullTraceIdx].index(); + while (residues.length > listIdx && + residues[listIdx].full().index() < traceIndex) { + ++listIdx; + } + if (listIdx >= residues.length) { + break; + } + var fullTraceBegin = fullTraceIdx; + var residueListBegin = listIdx; + while (residues.length > listIdx && this._trace.length > fullTraceIdx && + residues[listIdx].full().index() === + this._trace[fullTraceIdx].index()) { + ++listIdx; + ++fullTraceIdx; + } + var residueListEnd = listIdx; + var fullTraceEnd = fullTraceIdx; + subsets.push( + new TraceSubset(this, fullTraceBegin, fullTraceEnd, + residues.slice(residueListBegin, residueListEnd))); + } + return subsets; +}; + +// a trace subset, e.g. the part of a trace contained in a view. End regions +// are handled automatically depending on whether the beginning/end of the +// trace subset coincides with the C- and N-terminus of the full trace. +function TraceSubset(fullTrace, fullTraceBegin, fullTraceEnd, trace) { + this._fullTrace = fullTrace; + this._fullTraceBegin = fullTraceBegin; + this._fullTraceEnd = fullTraceEnd; + this._trace = trace; + this._isNTerminal = this._fullTraceBegin === 0; + this._isCTerminal = this._fullTrace.length() === this._fullTraceEnd; + var length = this._fullTraceEnd - this._fullTraceBegin; + if (!this._isCTerminal) { + ++length; + } + if (!this._isNTerminal) { + ++length; + this._fullTraceBegin -= 1; + } + this._length = length; +} + +TraceSubset.prototype.length = function() { + return this._length; +}; + +TraceSubset.prototype.residueAt = function(index) { + return this._fullTrace.residueAt(this._fullTraceBegin + index); +}; + +TraceSubset.prototype._interpolate = (function() { + var posOne = vec3.create(); + var tangentOne = vec3.create(); + var tangentTwo = vec3.create(); + return function(out, indexOne, indexTwo, strength) { + this.tangentAt(tangentOne, indexOne); + this.tangentAt(tangentTwo, indexTwo); + vec3.scale(tangentOne, tangentOne, strength); + vec3.scale(tangentTwo, tangentTwo, strength); + geom.cubicHermiteInterpolate(out, this.centralAtomAt(indexOne).pos(), + tangentOne, this.centralAtomAt(indexTwo).pos(), + tangentTwo, 0.5, 0); + return out; + }; +})(); + +// like posAt, but interpolates the position for the ends with a Catmull-Rom +// spline. +TraceSubset.prototype.smoothPosAt = (function() { + var posOne = vec3.create(); + var tangentOne = vec3.create(); + var tangentTwo = vec3.create(); + return function(out, index, strength) { + if (index === 0 && !this._isNTerminal) { + return this._interpolate(out, index, index + 1, strength); + } + if (index === this._length-1 && !this._isCTerminal) { + return this._interpolate(out, index - 1, index, strength); + } + var atom = this.centralAtomAt(index); + vec3.copy(out, atom.pos()); + return out; +} +; +})(); + + +TraceSubset.prototype.smoothNormalAt = (function() { + return function(out, index, strength) { + this._fullTrace.normalAt(out, index + this._fullTraceBegin); + return out; + }; +})(); + +TraceSubset.prototype.posAt = function(out, index) { + var atom = this.centralAtomAt(index); + var atom2 = null; + vec3.copy(out, atom.pos()); + if (index === 0 && !this._isNTerminal) { + atom2 = this.centralAtomAt(index + 1); + vec3.add(out, out, atom2.pos()); + vec3.scale(out, out, 0.5); + } + if (index === this._length - 1 && !this._isCTerminal) { + atom2 = this.centralAtomAt(index - 1); + vec3.add(out, out, atom2.pos()); + vec3.scale(out, out, 0.5); + } + return out; +}; + +TraceSubset.prototype.centralAtomAt = function(index) { + return this.residueAt(index).centralAtom(); +}; + +TraceSubset.prototype.fullTraceIndex = function(index) { + return this._fullTraceBegin + index; +}; +TraceSubset.prototype.tangentAt = function(out, index) { + return this._fullTrace.tangentAt(out, index + this._fullTraceBegin); +}; + diff --git a/src/pdb/vert-assoc.js b/src/pdb/vert-assoc.js new file mode 100644 index 000000000..2f6e73efd --- /dev/null +++ b/src/pdb/vert-assoc.js @@ -0,0 +1,218 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +// During recoloring of a render style, most of the vertex attributes, e.g. +// normals and positions do not change. Only the color information for each +// vertex needs to be adjusted. +// +// To do that efficiently, we need store an association between ranges of +// vertices and atoms in the original structure. Worse, we also need to +// support render styles for which colors need to be interpolated, e.g. +// the smooth line trace, tube and cartoon render modes. +// +// The vertex association data for the atom-based render styles is managed +// by AtomVertexAssoc, whereas the trace-based render styles are managed +// by the TraceVertexAssoc class. +function AtomVertexAssoc(structure, callColoringBeginEnd) { + this._structure = structure; + this._assocs = []; + this._callBeginEnd = callColoringBeginEnd; + +} + +if(typeof(exports) !== 'undefined') { + exports.AtomVertexAssoc = AtomVertexAssoc; +} + +AtomVertexAssoc.prototype.addAssoc = function(atom, va, vertStart, vertEnd) { + this._assocs.push({ + atom: atom, vertexArray : va, vertStart : vertStart, vertEnd : vertEnd + }); +}; + +AtomVertexAssoc.prototype.recolor = function(colorOp, view) { + // allocate buffer to hold all colors of the view. + var colorData = new Float32Array(view.atomCount()*4); + if (this._callBeginEnd) { + // FIXME: does this need to be called on the complete structure or the + // view? + colorOp.begin(this._structure); + } + + var atomMap = {}; + view.eachAtom(function(atom, index) { + atomMap[atom.index()] = index; + colorOp.colorFor(atom, colorData, index*4); + }); + if (this._callBeginEnd) { + colorOp.end(this._structure); + } + // apply the color to the actual interleaved vertex array. + for (var i = 0; i < this._assocs.length; ++i) { + var assoc = this._assocs[i]; + var ai = atomMap[assoc.atom.index()]; + if (ai === undefined) { + continue; + } + var r = colorData[ai*4+0], g = colorData[ai*4+1], + b = colorData[ai*4+2], a = colorData[ai*4+3]; + var va = assoc.vertexArray; + for (var j = assoc.vertStart ; j < assoc.vertEnd; ++j) { + va.setColor(j, r, g, b, a); + } + } +}; + +AtomVertexAssoc.prototype.getColorForAtom = function(atom, color) { + // FIXME: this can potentially get slow when called for many atoms + for (var i = 0; i < this._assocs.length; ++i) { + var assoc = this._assocs[i]; + if (assoc.atom.full() === atom.full()) { + // for atom-based color, the color for each atom is constant, so just + // use any vertex to the determine color. + return assoc.vertexArray.getColor(assoc.vertStart, color); + } + } + return null; +}; + +AtomVertexAssoc.prototype.setOpacity = function( val, view) { + // apply the color to the actual interleaved vertex array. + for (var i = 0; i < this._assocs.length; ++i) { + var assoc = this._assocs[i]; + var va = assoc.vertexArray; + for (var j = assoc.vertStart ; j < assoc.vertEnd; ++j) { + va.setOpacity(j, val); + } + } +}; + +// handles the association between a trace element, and sets of vertices. +function TraceVertexAssoc(structure, interpolation, callColoringBeginEnd, + perResidueColors) { + this._structure = structure; + this._assocs = []; + this._callBeginEnd = callColoringBeginEnd; + this._interpolation = interpolation || 1; + this._perResidueColors = {}; +} + +if(typeof(exports) !== 'undefined') { + exports.TraceVertexAssoc = TraceVertexAssoc; +} + +TraceVertexAssoc.prototype.setPerResidueColors = function(traceIndex, colors) { + this._perResidueColors[traceIndex] = colors; +}; + +TraceVertexAssoc.prototype.addAssoc = + function(traceIndex, vertexArray, slice, vertStart, vertEnd) { + this._assocs.push({ traceIndex: traceIndex, slice : slice, + vertStart : vertStart, vertEnd : vertEnd, + vertexArray : vertexArray}); +}; + +TraceVertexAssoc.prototype.recolor = function(colorOp, view) { + // FIXME: this function might create quite a few temporary buffers. Implement + // a buffer pool to avoid hitting the GC and having to go through the slow + // creation of typed arrays. + if (this._callBeginEnd) { + // FIXME: does this need to be called on the complete structure? + colorOp.begin(this._structure); + } + var colorData = []; + var i, j; + var traces = this._structure.backboneTraces(); + console.assert(this._perResidueColors, + "per-residue colors must be set for recoloring to work"); + for (i = 0; i < traces.length; ++i) { + // get current residue colors + var data = this._perResidueColors[i]; + console.assert(data, "no per-residue colors. Seriously, man?"); + var index = 0; + var trace = traces[i]; + for (j = 0; j < trace.length(); ++j) { + if (!view.containsResidue(trace.residueAt(j))) { + index+=4; + continue; + } + colorOp.colorFor(trace.centralAtomAt(j), data, index); + index+=4; + } + if (this._interpolation > 1) { + colorData.push(interpolateColor(data, this._interpolation)); + } else { + colorData.push(data); + } + } + + // store the color in the actual interleaved vertex array. + for (i = 0; i < this._assocs.length; ++i) { + var assoc = this._assocs[i]; + var ai = assoc.slice; + var newColors = colorData[assoc.traceIndex]; + var r = newColors[ai*4], g = newColors[ai*4+1], b = newColors[ai*4+2], a=newColors[ai*4+3]; + var va = assoc.vertexArray; + for (j = assoc.vertStart ; j < assoc.vertEnd; ++j) { + va.setColor(j, r, g, b, a); + } + } + if (this._callBeginEnd) { + colorOp.end(this._structure); + } +}; + +TraceVertexAssoc.prototype.getColorForAtom = function(atom, color) { + // FIXME: this can potentially get slow when called for many atoms + var i, j; + var traces = this._structure.backboneTraces(); + var residue = atom.full().residue(); + for (i = 0; i < traces.length; ++i) { + var data = this._perResidueColors[i]; + var index = 0; + var trace = traces[i]; + for (j = 0; j < trace.length(); ++j) { + if (residue === trace.residueAt(j).full()) { + color[0] = data[index + 0]; + color[1] = data[index + 1]; + color[2] = data[index + 2]; + color[3] = data[index + 3]; + return color; + } + index+=4; + } + } + return null; +}; + + +TraceVertexAssoc.prototype.setOpacity = function( val, view) { + // store the color in the actual interleaved vertex array. + for (i = 0; i < this._assocs.length; ++i) { + var assoc = this._assocs[i]; + var va = assoc.vertexArray; + for (j = assoc.vertStart ; j < assoc.vertEnd; ++j) { + va.setOpacity(j, val); + } + } + +}; + diff --git a/src/pdb/vertex-array-base.js b/src/pdb/vertex-array-base.js new file mode 100644 index 000000000..a71bc7803 --- /dev/null +++ b/src/pdb/vertex-array-base.js @@ -0,0 +1,156 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +(function(exports) { +"use strict"; + + +function VertexArrayBase(gl, numVerts, float32Allocator) { + this._gl = gl; + this._vertBuffer = gl.createBuffer(); + this._float32Allocator = float32Allocator || null; + this._ready = false; + this._boundingSphere = null; + var numFloats = this._FLOATS_PER_VERT * numVerts; + this._vertData = float32Allocator.request(numFloats); +} + +VertexArrayBase.prototype.setColor = function(index, r, g, b, a) { + var colorStart = index * this._FLOATS_PER_VERT + this._COLOR_OFFSET; + this._vertData[colorStart + 0] = r; + this._vertData[colorStart + 1] = g; + this._vertData[colorStart + 2] = b; + this._vertData[colorStart + 3] = a; + this._ready = false; +}; + +VertexArrayBase.prototype.getColor = function(index, color) { + var colorStart = index * this._FLOATS_PER_VERT + this._COLOR_OFFSET; + color[0] = this._vertData[colorStart + 0]; + color[1] = this._vertData[colorStart + 1]; + color[2] = this._vertData[colorStart + 2]; + color[3] = this._vertData[colorStart + 3]; + return color; +}; + +VertexArrayBase.prototype.setOpacity = function(index, a) { + var colorStart = index * this._FLOATS_PER_VERT + this._COLOR_OFFSET; + this._vertData[colorStart + 3] = a; + this._ready = false; +}; + + +VertexArrayBase.prototype.boundingSphere = function() { + if (!this._boundingSphere) { + this._boundingSphere = this._calculateBoundingSphere(); + } + return this._boundingSphere; +}; + + +VertexArrayBase.prototype._calculateBoundingSphere = function() { + var numVerts = this.numVerts(); + if (numVerts === 0) { + return null; + } + var center = vec3.create(); + var index, i; + for (i = 0; i < numVerts; ++i) { + index = i * this._FLOATS_PER_VERT; + center[0] += this._vertData[index + 0]; + center[1] += this._vertData[index + 1]; + center[2] += this._vertData[index + 2]; + } + vec3.scale(center, center, 1.0/numVerts); + var radiusSquare = 0.0; + for (i = 0; i < numVerts; ++i) { + index = i * this._FLOATS_PER_VERT; + var dx = center[0] - this._vertData[index + 0]; + var dy = center[1] - this._vertData[index + 1]; + var dz = center[2] - this._vertData[index + 2]; + radiusSquare = Math.max(radiusSquare, dx*dx + dy*dy + dz*dz); + } + return new geom.Sphere(center, Math.sqrt(radiusSquare)); +}; + +VertexArrayBase.prototype.destroy = function() { + this._gl.deleteBuffer(this._vertBuffer); + this._float32Allocator.release(this._vertData); +}; + +VertexArrayBase.prototype.bindBuffers = function() { + this._gl.bindBuffer(this._gl.ARRAY_BUFFER, this._vertBuffer); + if (this._ready) { + return; + } + this._gl.bufferData(this._gl.ARRAY_BUFFER, this._vertData, + this._gl.STATIC_DRAW); + this._ready = true; +}; + +// Helper method to calculate the squared bounding sphere radius of the sphere +// centered on "sphereCenter" over multiple vertex arrays. +VertexArrayBase.prototype.updateSquaredSphereRadius = (function() { + + var transformedCenter = vec3.create(); + return function(sphereCenter, radius, transform) { + var bounds = this.boundingSphere(); + if (!bounds) { + return radius; + } + // Note: Math.max(radius, null) returns the radius for positive values + // of radius, which is exactly what we want. + if (transform) { + vec3.transformMat4(transformedCenter, bounds.center(), transform); + return Math.max(vec3.sqrDist(transformedCenter, sphereCenter), radius); + } + + var sphereRadSquare = bounds.radius() * bounds.radius(); + return Math.max(vec3.sqrDist(bounds.center(), sphereCenter) + sphereRadSquare, radius); + }; +})(); + +VertexArrayBase.prototype.updateProjectionIntervals = (function() { + + var transformedCenter = vec3.create(); + return function(xAxis, yAxis, zAxis, xInterval, yInterval, + zInterval, transform) { + var bounds = this.boundingSphere(); + if (!bounds) { + return; + } + if (transform) { + vec3.transformMat4(transformedCenter, bounds.center(), transform); + } else { + vec3.copy(transformedCenter, bounds.center()); + } + var xProjected = vec3.dot(xAxis, transformedCenter); + var yProjected = vec3.dot(yAxis, transformedCenter); + var zProjected = vec3.dot(zAxis, transformedCenter); + xInterval.update(xProjected - bounds.radius()); + xInterval.update(xProjected + bounds.radius()); + yInterval.update(yProjected - bounds.radius()); + yInterval.update(yProjected + bounds.radius()); + zInterval.update(zProjected - bounds.radius()); + zInterval.update(zProjected + bounds.radius()); + }; +})(); +exports.VertexArrayBase = VertexArrayBase; + +})(this); diff --git a/src/pdb/vertex-array.js b/src/pdb/vertex-array.js new file mode 100644 index 000000000..340851a25 --- /dev/null +++ b/src/pdb/vertex-array.js @@ -0,0 +1,110 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +(function(exports) { +"use strict"; + +// (unindexed) vertex array for line-based geometries +function VertexArray(gl, numVerts, float32Allocator) { + VertexArrayBase.prototype.constructor.call(this, gl, numVerts, + float32Allocator); + this._numLines = 0; +} + +derive(VertexArray, VertexArrayBase); + +VertexArray.prototype._FLOATS_PER_VERT = 8; +VertexArray.prototype._POS_OFFSET = 0; +VertexArray.prototype._COLOR_OFFSET = 3; +VertexArray.prototype._ID_OFFSET = 7; + +VertexArray.prototype.numVerts = function() { return this._numLines * 2; }; + +VertexArray.prototype.addLine = function(startPos, startColor, endPos, + endColor, idOne, idTwo) { + var index = this._FLOATS_PER_VERT * this._numLines * 2; + this._vertData[index++] = startPos[0]; + this._vertData[index++] = startPos[1]; + this._vertData[index++] = startPos[2]; + this._vertData[index++] = startColor[0]; + this._vertData[index++] = startColor[1]; + this._vertData[index++] = startColor[2]; + this._vertData[index++] = startColor[3]; + this._vertData[index++] = idOne; + this._vertData[index++] = endPos[0]; + this._vertData[index++] = endPos[1]; + this._vertData[index++] = endPos[2]; + this._vertData[index++] = endColor[0]; + this._vertData[index++] = endColor[1]; + this._vertData[index++] = endColor[2]; + this._vertData[index++] = endColor[3]; + this._vertData[index++] = idTwo; + + this._numLines += 1; + this._ready = false; + this._boundingSpehre = null; +}; + + +VertexArray.prototype.bindAttribs = function(shader) { + this._gl.vertexAttribPointer(shader.posAttrib, 3, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, + this._POS_OFFSET * 4); + if (shader.colorAttrib !== -1) { + this._gl.vertexAttribPointer(shader.colorAttrib, 4, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, + this._COLOR_OFFSET * 4); + this._gl.enableVertexAttribArray(shader.colorAttrib); + } + this._gl.enableVertexAttribArray(shader.posAttrib); + if (shader.objIdAttrib !== -1) { + this._gl.vertexAttribPointer(shader.objIdAttrib, 1, this._gl.FLOAT, false, + this._FLOATS_PER_VERT * 4, + this._ID_OFFSET * 4); + this._gl.enableVertexAttribArray(shader.objIdAttrib); + } +}; + +VertexArray.prototype.releaseAttribs = function(shader) { + this._gl.disableVertexAttribArray(shader.posAttrib); + if (shader.colorAttrib !== -1) { + this._gl.disableVertexAttribArray(shader.colorAttrib); } + if (shader.objIdAttrib !== -1) { + this._gl.disableVertexAttribArray(shader.objIdAttrib); + } +}; + +VertexArray.prototype.bind = function(shader) { + this.bindBuffers(); + this.bindAttribs(shader); +}; + +// draws all triangles contained in the indexed vertex array using the provided +// shader. +VertexArray.prototype.draw = function(symId) { + this._gl.drawArrays(this._gl.LINES, 0, this._numLines * 2); +}; + + + +exports.VertexArray = VertexArray; + +return true; +})(this); diff --git a/src/pdb/viewer.js b/src/pdb/viewer.js new file mode 100644 index 000000000..12c81c649 --- /dev/null +++ b/src/pdb/viewer.js @@ -0,0 +1,1026 @@ +// Copyright (c) 2013-2014 Marco Biasini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +var pv = (function(){ + "use strict"; + + // FIXME: Browser vendors tend to block quite a few graphic cards. Instead + // of showing this very generic message, implement a per-browser + // diagnostic. For example, when we detect that we are running a recent + // Chrome and Webgl is not available, we should say that the user is + // supposed to check chrome://gpu for details on why WebGL is not + // available. Similar troubleshooting pages are available for other + // browsers. + var WEBGL_NOT_SUPPORTED = '\ +
\ +

Oink

Your browser does not support WebGL. \ +You might want to try Chrome, Firefox, IE 11, or newer versions of Safari\ +

\ +

If you are using a recent version of one of the above browsers, your \ +graphic card might be blocked. Check the browser documentation for details\ +

\ +
'; + + function bind(obj, fn) { + return function() { + return fn.apply(obj, arguments); + }; +} + + +var requestAnimFrame = (function(){ + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function slabModeToStrategy(mode, options) { + mode = mode || 'auto'; + if (mode === 'fixed') { + return new FixedSlab(options); + } + if (mode === 'auto') { + return new AutoSlab(options); + } + return null; +} + +function PV(domElement, opts) { + opts = opts || {}; + this._options = { + width : (opts.width || 500), + height : (opts.height || 500), + animateTime : (opts.animateTime || 0), + antialias : opts.antialias, + quality : opts.quality || 'low', + style : opts.style || 'hemilight', + background : opts.background ? forceRGB(opts.background) : vec3.fromValues(1,1,1), + slabMode : slabModeToStrategy(opts.slabMode), + atomClick: opts.atomClick || null, + fog : true, + atomDoubleClick : 'center', // option is handled below + }; + this._objects = []; + this._domElement = domElement; + this._redrawRequested = false; + this._resize = false; + this._lastTimestamp = null; + this.listenerMap = {}; + // NOTE: make sure to only request features supported by all browsers, + // not only browsers that support WebGL in this constructor. WebGL + // detection only happens in PV._initGL. Once this happened, we are + // save to use whatever feature pleases us, e.g. typed arrays, 2D + // contexts etc. + this._canvas = document.createElement('canvas'); + this._textureCanvas = document.createElement('canvas'); + this._textureCanvas.style.display = 'none'; + this._objectIdManager = new UniqueObjectIdPool(); + var parentRect = domElement.getBoundingClientRect(); + if (this._options.width === 'auto') { + this._options.width = parentRect.width; + } + if (this._options.height === 'auto') { + this._options.height = parentRect.height; + } + if ('outline' in opts) { + this._options.outline = opts.outline; + } else { + this._options.outline = true; + } + if ('atomDoubleClicked' in opts) { + this._options.atomDoubleClick = opts.atomDoubleClick; + } + if ('fog' in opts) { + this._options.fog = opts.fog; + } + this._ok = false; + this._camAnim = { + center : null, zoom : null, + rotation : null + }; + this.quality(this._options.quality); + this._canvas.width = this._options.width; + this._canvas.height = this._options.height; + this._domElement.appendChild(this._canvas); + this._domElement.appendChild(this._textureCanvas); + + if (document.readyState === "complete" || + document.readyState === "loaded" || + document.readyState === "interactive") { + this._initPV(); + } else { + document.addEventListener('DOMContentLoaded', bind(this, this._initPV)); + } + if (this._options.atomDoubleClick !== null) { + this.addListener('atomDoubleClicked', this._options.atomDoubleClick); + } + if (this._options.atomClick !== null) { + this.addListener('atomClicked', this._options.atomClick); + } +} + +PV.prototype._centerOnClicked = function(picked, originalEvent) { + if (picked === null) { + return; + } + var transformedPos = vec3.create(); + var newAtom = picked.object().atom; + var pos = newAtom.pos(); + if (picked.transform()) { + vec3.transformMat4(transformedPos, pos, picked.transform()); + this.setCenter(transformedPos, this._options.animateTime); + } else { + this.setCenter(pos, this._options.animateTime); + } +}; + + +// resizes the canvas, separated out from PV.resize because we want +// to call this function directly in a requestAnimationFrame together +// with rendering to avoid flickering. +PV.prototype._ensureSize = function() { + if (!this._resize) { + return; + } + this._resize = false; + this._options.realWidth = this._options.width * this._options.samples; + this._options.realHeight = this._options.height * this._options.samples; + this._gl.viewport(0, 0, this._options.realWidth, this._options._realHeight); + this._canvas.width = this._options.realWidth; + this._canvas.height = this._options.realHeight; + this._cam.setViewportSize(this._options.realWidth, this._options.realHeight); + if (this._options.samples > 1) { + this._initManualAntialiasing(this._options.samples); + } + this._pickBuffer.resize(this._options.width, this._options.height); +}; + +PV.prototype.resize = function(width, height) { + if (width === this._options.width && height === this._options.height) { + return; + } + this._resize = true; + this._options.width = width; + this._options.height = height; + this.requestRedraw(); +}; + +PV.prototype.fitParent = function() { + var parentRect = this._domElement.getBoundingClientRect(); + this.resize(parentRect.width, parentRect.height); +}; + +PV.prototype.gl = function() { + return this._gl; +}; + +PV.prototype.ok = function() { + return this._ok; +}; + +PV.prototype.options = function(optName, value) { + if (value !== undefined) { + if (optName === 'fog') { + this._cam.fog(value); + this._options.fog = value; + this.requestRedraw(); + } else { + this._options[optName] = value; + } + return value; + } + return this._options[optName]; +}; + +PV.prototype.quality = function(qual) { + this._options.quality = qual; + if (qual === 'high') { + this._options.arcDetail = 4; + this._options.sphereDetail = 16; + this._options.splineDetail = 8; + return; + } + if (qual === 'medium') { + this._options.arcDetail = 3; + this._options.sphereDetail = 10; + this._options.splineDetail = 4; + return; + } + if (qual === 'low') { + this._options.arcDetail = 2; + this._options.sphereDetail = 8; + this._options.splineDetail = 2; + return; + } + console.error('invalid quality argument', qual); +}; + +// returns the content of the WebGL context as a data URL element which can be +// inserted into an img element. This allows users to save a picture to disk +PV.prototype.imageData = function() { + return this._canvas.toDataURL(); +}; + +PV.prototype._initContext = function() { + try { + var contextOpts = { + antialias : this._options.antialias, + preserveDrawingBuffer : true // for image export + }; + this._gl = this._canvas.getContext('experimental-webgl', contextOpts); + } + catch (err) { + console.error('WebGL not supported', err); + return false; + } + if (!this._gl) { + console.error('WebGL not supported'); + return false; + } + return true; +}; + +PV.prototype._initManualAntialiasing = function(samples) { + var scale_factor = 1.0 / samples; + var trans_x = -(1 - scale_factor) * 0.5 * this._options.realWidth; + var trans_y = -(1 - scale_factor) * 0.5 * this._options.realHeight; + var translate = 'translate(' + trans_x + 'px, ' + trans_y + 'px)'; + var scale = 'scale(' + scale_factor + ', ' + scale_factor + ')'; + var transform = translate + ' ' + scale; + + this._canvas.style.webkitTransform = transform; + this._canvas.style.transform = transform; + this._canvas.style.ieTransform = transform; + this._canvas.width = this._options.realWidth; + this._canvas.height = this._options.realHeight; +}; + +PV.prototype._initPickBuffer = function() { + var fbOptions = { + width : this._options.width, height : this._options.height + }; + this._pickBuffer = new FrameBuffer(this._gl, fbOptions); +}; + +PV.prototype._initGL = function() { + var samples = 1; + if (!this._initContext()) { + return false; + } + + if (!this._gl.getContextAttributes().antialias && this._options.antialias) { + samples = 2; + } + this._options.realWidth = this._options.width * samples; + this._options.realHeight = this._options.height * samples; + this._options.samples = samples; + if (samples > 1) { + this._initManualAntialiasing(samples); + } + this._gl.viewportWidth = this._options.realWidth; + this._gl.viewportHeight = this._options.realHeight; + + this._gl.clearColor(this._options.background[0], this._options.background[1], this._options.background[2], 1.0); + this._gl.lineWidth(2.0); + this._gl.cullFace(this._gl.FRONT); + this._gl.enable(this._gl.CULL_FACE); + this._gl.enable(this._gl.DEPTH_TEST); + this._initPickBuffer(); + return true; +}; + +PV.prototype._shaderFromString = function(shader_code, type) { + var shader; + if (type === 'fragment') { + shader = this._gl.createShader(this._gl.FRAGMENT_SHADER); + } else if (type === 'vertex') { + shader = this._gl.createShader(this._gl.VERTEX_SHADER); + } else { + console.error('could not determine type for shader'); + return null; + } + this._gl.shaderSource(shader, shader_code); + this._gl.compileShader(shader); + if (!this._gl.getShaderParameter(shader, this._gl.COMPILE_STATUS)) { + console.error(this._gl.getShaderInfoLog(shader)); + return null; + } + return shader; +}; + +PV.prototype._initShader = function(vert_shader, frag_shader) { + var fs = this._shaderFromString(frag_shader, 'fragment'); + var vs = this._shaderFromString(vert_shader, 'vertex'); + var shaderProgram = this._gl.createProgram(); + this._gl.attachShader(shaderProgram, vs); + this._gl.attachShader(shaderProgram, fs); + this._gl.linkProgram(shaderProgram); + if (!this._gl.getProgramParameter(shaderProgram, this._gl.LINK_STATUS)) { + console.error('could not initialise shaders'); + console.error(this._gl.getShaderInfoLog(shaderProgram)); + return null; + } + this._gl.clearColor(this._options.background[0], this._options.background[1], this._options.background[2], 1.0); + this._gl.enable(this._gl.BLEND); + this._gl.blendFunc(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA); + this._gl.enable(this._gl.CULL_FACE); + this._gl.enable(this._gl.DEPTH_TEST); + + // get vertex attribute location for the shader once to + // avoid repeated calls to getAttribLocation/getUniformLocation + var getAttribLoc = bind(this._gl, this._gl.getAttribLocation); + var getUniformLoc = bind(this._gl, this._gl.getUniformLocation); + shaderProgram.posAttrib = getAttribLoc(shaderProgram, 'attrPos'); + shaderProgram.colorAttrib = getAttribLoc(shaderProgram, 'attrColor'); + shaderProgram.normalAttrib = getAttribLoc(shaderProgram, 'attrNormal'); + shaderProgram.objIdAttrib = getAttribLoc(shaderProgram, 'attrObjId'); + shaderProgram.symId = getUniformLoc(shaderProgram, 'symId'); + shaderProgram.projection = getUniformLoc(shaderProgram, 'projectionMat'); + shaderProgram.modelview = getUniformLoc(shaderProgram, 'modelviewMat'); + shaderProgram.rotation = getUniformLoc(shaderProgram, 'rotationMat'); + shaderProgram.fog = getUniformLoc(shaderProgram, 'fog'); + shaderProgram.fogFar = getUniformLoc(shaderProgram, 'fogFar'); + shaderProgram.fogNear = getUniformLoc(shaderProgram, 'fogNear'); + shaderProgram.fogColor = getUniformLoc(shaderProgram, 'fogColor'); + shaderProgram.outlineColor = getUniformLoc(shaderProgram, 'outlineColor'); + + return shaderProgram; +}; + +PV.prototype._mouseUp = function(event) { + this._canvas.removeEventListener('mousemove', this._mouseRotateListener, false); + this._canvas.removeEventListener('mousemove', this._mousePanListener, false); + this._canvas.removeEventListener('mouseup', this._mouseUpListener, false); + document.removeEventListener('mouseup', this._mouseUpListener, false); + document.removeEventListener('mousemove', this._mouseRotateListener); + document.removeEventListener('mousemove', this._mousePanListener); +}; + +PV.prototype._initPV = function() { + if (!this._initGL()) { + this._domElement.removeChild(this._canvas); + this._domElement.innerHTML = WEBGL_NOT_SUPPORTED; + this._domElement.style.width = this._options.width + 'px'; + this._domElement.style.height = this._options.height + 'px'; + return false; + } + this._ok = true; + this._2dcontext = this._textureCanvas.getContext('2d'); + this._float32Allocator = new PoolAllocator(Float32Array); + this._uint16Allocator = new PoolAllocator(Uint16Array); + this._cam = new Cam(this._gl); + this._cam.fog(this._options.fog); + this._shaderCatalog = { + hemilight : this._initShader(shaders.HEMILIGHT_VS, shaders.HEMILIGHT_FS), + outline : this._initShader(shaders.OUTLINE_VS, shaders.OUTLINE_FS), + lines : this._initShader(shaders.HEMILIGHT_VS, shaders.LINES_FS), + text : this._initShader(shaders.TEXT_VS, shaders.TEXT_FS), + select : this._initShader(shaders.SELECT_VS, shaders.SELECT_FS) + }; + + this._boundDraw = bind(this, this._draw); + + this._mousePanListener = bind(this, this._mousePan); + this._mouseRotateListener = bind(this, this._mouseRotate); + this._mouseUpListener = bind(this, this._mouseUp); + + // Firefox responds to the wheel event, whereas other browsers listen to + // the mousewheel event. Register different event handlers, depending on + // what properties are available. + if ('onwheel' in this._canvas) { + this._canvas.addEventListener('wheel', bind(this, this._mouseWheelFF), + false); + } else { + this._canvas.addEventListener('mousewheel', bind(this, this._mouseWheel), + false); + } + this._canvas.addEventListener('dblclick', bind(this, this._mouseDoubleClick), + false); + this._canvas.addEventListener('mousedown', bind(this, this._mouseDown), + false); + + return true; +}; + +PV.prototype.requestRedraw = function() { + if (this._redrawRequested) { + return; + } + this._redrawRequested = true; + requestAnimFrame(this._boundDraw); +}; + +PV.prototype._drawWithPass = function(pass) { + for (var i = 0, e = this._objects.length; i !== e; ++i) { + this._objects[i] + .draw(this._cam, this._shaderCatalog, this._options.style, pass); + } +}; + +PV.prototype.setCamera = function(rotation, center, zoom, ms) { + + ms |= 0; + if (ms === 0) { + this._cam.setCenter(center); + this._cam.setRotation(rotation); + this._cam.setZoom(zoom); + this.requestRedraw(); + return; + } + this._camAnim.center = new Move(this._cam.center(), + vec3.clone(center), ms); + this._camAnim.rotation = new Rotate(this._cam.rotation(), + mat4.clone(rotation), ms); + + this._camAnim.zoom = new Animation(this._cam.zoom(), + zoom, ms); + this.requestRedraw(); +}; + +// performs interpolation of current camera position +PV.prototype._animateCam = function() { + var anotherRedraw = false; + if (this._camAnim.center) { + this._cam.setCenter(this._camAnim.center.step()); + if (this._camAnim.center.finished()) { + this._camAnim.center = null; + } + anotherRedraw = true; + } + if (this._camAnim.rotation) { + this._cam.setRotation(this._camAnim.rotation.step()); + if (this._camAnim.rotation.finished()) { + this._camAnim.rotation = null; + } + anotherRedraw = true; + } + if (this._camAnim.zoom) { + this._cam.setZoom(this._camAnim.zoom.step()); + if (this._camAnim.zoom.finished()) { + this._camAnim.zoom = null; + } + anotherRedraw = true; + } + if (anotherRedraw) { + this.requestRedraw(); + } +}; + +PV.prototype._draw = function() { + this._redrawRequested = false; + this._ensureSize(); + this._animateCam(); + var newSlab = this._options.slabMode.update(this._objects, this._cam); + if (newSlab !== null) { + this._cam.setNearFar(newSlab.near, newSlab.far); + } + + this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT); + this._gl.viewport(0, 0, this._options.realWidth, this._options.realHeight); + this._gl.cullFace(this._gl.FRONT); + this._gl.enable(this._gl.CULL_FACE); + this._gl.enable(this._gl.BLEND); + this._drawWithPass('normal'); + if (!this._options.outline) { + return; + } + this._gl.cullFace(this._gl.BACK); + this._gl.enable(this._gl.CULL_FACE); + this._drawWithPass('outline'); +}; + +PV.prototype.setCenter = function(center, ms) { + ms |= 0; + if (ms === 0) { + this._cam.setCenter(center); + return; + } + this._camAnim.center = new Move(this._cam.center(), + vec3.clone(center), ms); + this.requestRedraw(); +}; + +PV.prototype.centerOn = function(what, ms) { + this.setCenter(what.center(), ms); +}; + + +PV.prototype.clear = function() { + for (var i = 0; i < this._objects.length; ++i) { + this._objects[i].destroy(); + } + this._objects = []; +}; + +PV.prototype._mouseWheel = function(event) { + this._cam.zoom(event.wheelDelta < 0 ? -1 : 1); + event.preventDefault(); + this.requestRedraw(); +}; + +PV.prototype._mouseWheelFF = function(event) { + this._cam.zoom(event.deltaY < 0 ? 1 : -1); + event.preventDefault(); + this.requestRedraw(); +}; + +PV.prototype._mouseDoubleClick = (function() { + return function(event) { + var rect = this._canvas.getBoundingClientRect(); + var picked = this.pick( + { x : event.clientX - rect.left, y : event.clientY - rect.top }); + this._dispatchPickedEvent(event, 'atomDoubleClicked', picked); + this.requestRedraw(); + }; +})(); + + +PV.prototype.addListener = function(eventName, callback) { + var callbacks = this.listenerMap[eventName]; + if (typeof callbacks === 'undefined') { + callbacks = []; + this.listenerMap[eventName] = callbacks; + } + if (callback === 'center') { + callbacks.push(bind(this, this._centerOnClicked)); + } else { + callbacks.push(callback); + } +}; + +PV.prototype._dispatchPickedEvent = function(event, newEventName, picked) { + var callbacks = this.listenerMap[newEventName]; + if (callbacks) { + + callbacks.forEach(function (callback) { + callback(picked, event); + }); + } +}; + +PV.prototype._mouseDown = function(event) { + if (event.button !== 0) { + return; + } + var currentTime = (new Date()).getTime(); + // make sure it isn't a double click + if (typeof this.lastClickTime === 'undefined' || (currentTime - this.lastClickTime > 300)) { + this.lastClickTime = currentTime; + var rect = this._canvas.getBoundingClientRect(); + var picked = this.pick( + { x : event.clientX - rect.left, y : event.clientY - rect.top }); + this._dispatchPickedEvent(event, 'atomClicked', picked); + } + event.preventDefault(); + if (event.shiftKey === true) { + this._canvas.addEventListener('mousemove', this._mousePanListener, false); + document.addEventListener('mousemove', this._mousePanListener, false); + } else { + this._canvas.addEventListener('mousemove', this._mouseRotateListener, + false); + document.addEventListener('mousemove', this._mouseRotateListener, false); + } + this._canvas.addEventListener('mouseup', this._mouseUpListener, false); + document.addEventListener('mouseup', this._mouseUpListener, false); + this._lastMousePos = { x : event.pageX, y : event.pageY }; +}; + +PV.prototype._mouseRotate = function(event) { + var newMousePos = { x : event.pageX, y : event.pageY }; + var delta = { + x : newMousePos.x - this._lastMousePos.x, + y : newMousePos.y - this._lastMousePos.y + }; + + var speed = 0.005; + this._cam.rotateX(speed * delta.y); + this._cam.rotateY(speed * delta.x); + this._lastMousePos = newMousePos; + this.requestRedraw(); +}; + +PV.prototype._mousePan = function(event) { + var newMousePos = { x : event.pageX, y : event.pageY }; + var delta = { + x : newMousePos.x - this._lastMousePos.x, + y : newMousePos.y - this._lastMousePos.y + }; + + var speed = 0.05; + this._cam.panXY(speed * delta.x, speed * delta.y); + this._lastMousePos = newMousePos; + this.requestRedraw(); +}; + +PV.prototype.RENDER_MODES = + [ 'sline', 'line', 'trace', 'lineTrace', 'cartoon', 'tube', 'spheres' ]; + +/// simple dispatcher which allows to render using a certain style. +// will bail out if the render mode does not exist. +PV.prototype.renderAs = function(name, structure, mode, opts) { + var found = false; + for (var i = 0; i < this.RENDER_MODES.length; ++i) { + if (this.RENDER_MODES[i] === mode) { + found = true; + break; + } + } + if (!found) { + console.error('render mode', mode, 'not supported'); + return; + } + return this[mode](name, structure, opts); +}; + + +PV.prototype._handleStandardOptions = function(opts) { + opts = copy(opts); + opts.float32Allocator = this._float32Allocator; + opts.uint16Allocator = this._uint16Allocator; + opts.idPool = this._objectIdManager; + opts.showRelated = opts.showRelated || 'asym'; + return opts; +}; + + +PV.prototype.lineTrace = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.uniform([ 1, 0, 1 ]); + options.lineWidth = options.lineWidth || 4.0; + + var obj = render.lineTrace(structure, this._gl, options); + return this.add(name, obj); +}; + +PV.prototype.spheres = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.byElement(); + options.sphereDetail = this.options('sphereDetail'); + options.radiusMultiplier = options.radiusMultiplier || 1.0; + + var obj = render.spheres(structure, this._gl, options); + return this.add(name, obj); +}; + +PV.prototype.sline = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.uniform([ 1, 0, 1 ]); + options.splineDetail = options.splineDetail || this.options('splineDetail'); + options.strength = options.strength || 1.0; + options.lineWidth = options.lineWidth || 4.0; + + var obj = render.sline(structure, this._gl, options); + return this.add(name, obj); +}; + +// internal method for debugging the auto-slabbing code. +// not meant to be used otherwise. Will probably be removed again. +PV.prototype.boundingSpheres = function(gl, obj, options) { + var vertArrays = obj.vertArrays(); + var mg = new MeshGeom(gl, options.float32Allocator, + options.uint16Allocator); + mg.order(100); + var protoSphere = new ProtoSphere(16, 16); + var vertsPerSphere = protoSphere.numVerts(); + var indicesPerSphere = protoSphere.numIndices(); + var vertAssoc = new AtomVertexAssoc(obj.structure()); + mg.setVertAssoc(vertAssoc); + mg.addChainVertArray({ name : function() { return "a"; }}, + vertArrays.length * vertsPerSphere, + indicesPerSphere * vertArrays.length); + mg.setShowRelated('asym'); + var color = [0.5, 0.5, 0.5, 0.2]; + var va = mg.vertArrayWithSpaceFor(vertsPerSphere * vertArrays.length); + for (var i = 0; i < vertArrays.length; ++i) { + var bs = vertArrays[i].boundingSphere(); + protoSphere.addTransformed(va, bs.center(), bs.radius(), color, 0); + } + return mg; +}; + +PV.prototype.cartoon = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.bySS(); + options.strength = options.strength || 1.0; + options.splineDetail = options.splineDetail || this.options('splineDetail'); + options.arcDetail = options.arcDetail || this.options('arcDetail'); + options.radius = options.radius || 0.3; + options.forceTube = options.forceTube || false; + var obj = render.cartoon(structure, this._gl, options); + var added = this.add(name, obj); + if (options.boundingSpheres) { + var boundingSpheres = this.boundingSpheres(this._gl, obj, options); + this.add(name+'.bounds', boundingSpheres); + } + return added; +}; + + +PV.prototype.surface = function(name, data, opts) { + var options = this._handleStandardOptions(opts); + var obj = render.surface(data, this._gl, options); + return this.add(name, obj); +}; + +// renders the protein using a smoothly interpolated tube, essentially +// identical to the cartoon render mode, but without special treatment for +// helices and strands. +PV.prototype.tube = function(name, structure, opts) { + opts = opts || {}; + opts.forceTube = true; + return this.cartoon(name, structure, opts); +}; + +PV.prototype.ballsAndSticks = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + + options.color = options.color || color.byElement(); + options.radius = options.radius || 0.3; + options.arcDetail = (options.arcDetail || this.options('arcDetail')) * 2; + options.sphereDetail = options.sphereDetail || this.options('sphereDetail'); + + var obj = render.ballsAndSticks(structure, this._gl, options); + return this.add(name, obj); +}; + +PV.prototype.lines = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.byElement(); + options.lineWidth = options.lineWidth || 4.0; + var obj = render.lines(structure, this._gl, options); + return this.add(name, obj); +}; + +PV.prototype.trace = function(name, structure, opts) { + var options = this._handleStandardOptions(opts); + options.color = options.color || color.uniform([ 1, 0, 0 ]); + options.radius = options.radius || 0.3; + options.arcDetail = (options.arcDetail || this.options('arcDetail')) * 2; + options.sphereDetail = options.sphereDetail || this.options('sphereDetail'); + + var obj = render.trace(structure, this._gl, options); + return this.add(name, obj); +}; + +PV.prototype.fitTo = function(what, slabMode) { + var axes = this._cam.mainAxes(); + slabMode = slabMode || this._options.slabMode; + var intervals = [ new Range(), new Range(), new Range() ]; + if (what instanceof SceneNode) { + what.updateProjectionIntervals(axes[0], axes[1], axes[2], intervals[0], + intervals[1], intervals[2]); + } else if (what.eachAtom !== undefined) { + what.eachAtom(function(atom) { + var pos = atom.pos(); + for (var i = 0; i < 3; ++i) { + intervals[i].update(vec3.dot(pos, axes[i])); + } + }); + for (var i = 0; i < 3; ++i) { + intervals[i].extend(1.5); + } + } + this._fitToIntervals(axes, intervals, slabMode); +}; + +PV.prototype._fitToIntervals = function(axes, intervals) { + if (intervals[0].empty() || intervals[1].empty() || intervals[2].empty()) { + console.error('could not determine interval. No objects shown?'); + return; + } + var cx = intervals[0].center(); + var cy = intervals[1].center(); + var cz = intervals[2].center(); + var center = [ + cx * axes[0][0] + cy * axes[1][0] + cz * axes[2][0], + cx * axes[0][1] + cy * axes[1][1] + cz * axes[2][1], + cx * axes[0][2] + cy * axes[1][2] + cz * axes[2][2] + ]; + var fovY = this._cam.fieldOfViewY(); + var aspect = this._cam.aspectRatio(); + var inPlaneX = intervals[0].length() / aspect; + var inPlaneY = intervals[1].length(); + var inPlane = Math.max(inPlaneX, inPlaneY) * 0.5; + var distanceToFront = inPlane / Math.tan(0.5 * fovY); + var newZoom = + (distanceToFront + 0.5*intervals[2].length()); + var grace = 0.5; + var near = Math.max(distanceToFront - grace, 0.1); + var far = 2 * grace + distanceToFront + intervals[2].length(); + this._cam.setNearFar(near, far); + this.setCamera(this._cam.rotation(), center, newZoom, this._options.animateTime); + this.requestRedraw(); +}; + +// adapt the zoom level to fit the viewport to all visible objects. +PV.prototype.autoZoom = function() { + var axes = this._cam.mainAxes(); + var intervals = [ new Range(), new Range(), new Range() ]; + this.forEach(function(obj) { + if (!obj.visible()) { + return; + } + obj.updateProjectionIntervals(axes[0], axes[1], axes[2], intervals[0], + intervals[1], intervals[2]); + }); + this._fitToIntervals(axes, intervals); +}; + +PV.prototype.slabInterval = function() { +}; + +PV.prototype.autoSlab = function() { + var slab = this._options._slabMode.update(this._objects, this._cam); + if (slab !== null) { + this._cam.setNearFar(slab.near, slab.far); + } + this.requestRedraw(); +}; + +// enable disable rock and rolling of camera +PV.prototype.rockAndRoll = function(enable) { + if (enable === true) { + this._camAnim.rotation = new RockAndRoll(this._cam.rotation(), + [0, 1, 0], 2000); + this.requestRedraw(); + } else if (enable === false) { + this._camAnim.rotation = null; + this.requestRedraw(); + } + return this._camAnim.rotation !== null; +}; + +PV.prototype.slabMode = function(mode, options) { + options = options || {}; + var strategy = slabModeToStrategy(mode, options); + var slab = strategy.update(this._objects, this._cam); + if (slab !== null) { + this._cam.setNearFar(slab.near, slab.far); + } + this._options.slabMode = strategy; + this.requestRedraw(); +}; + +PV.prototype.label = function(name, text, pos) { + var label = new TextLabel(this._gl, this._textureCanvas, + this._2dcontext, pos, text); + this.add(name, label); + return label; +}; + +// INTERNAL: draws scene into offscreen pick buffer with the "select" +// shader. +PV.prototype._drawPickingScene = function() { + this._gl.clearColor(0.0, 0.0, 0.0, 0.0); + this._gl.disable(this._gl.BLEND); + this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT); + this._gl.clearColor(this._options.background[0], this._options.background[1], this._options.background[2], 1.0); + this._gl.cullFace(this._gl.FRONT); + this._gl.enable(this._gl.CULL_FACE); + this._drawWithPass('select'); +}; + +function PickingResult(obj, symIndex, transform) { + this._obj = obj; + this._symIndex = symIndex; + this._transform = transform; +} + +PickingResult.prototype.object = function() { + return this._obj; +}; + +PickingResult.prototype.symIndex = function() { + return this._symIndex; +}; + +PickingResult.prototype.transform = function() { + return this._transform; +}; + +PV.prototype.pick = function(pos) { + this._pickBuffer.bind(); + this._drawPickingScene(); + var pixels = new Uint8Array(4); + this._gl.readPixels(pos.x, this._options.height - pos.y, 1, 1, + this._gl.RGBA, this._gl.UNSIGNED_BYTE, pixels); + this._pickBuffer.release(); + if (pixels.data) { + pixels = pixels.data; + } + var pickedIds = {}; + if (pixels[3] === 0) { + return null; + } + var objId = pixels[0] | pixels[1] << 8; + var symIndex = pixels[2]; + + var obj = this._objectIdManager.objectForId(objId); + if (obj === undefined) { + return null; + } + var transform = null; + if (symIndex !== 255) { + transform = obj.geom.symWithIndex(symIndex); + } + return new PickingResult(obj, symIndex < 255 ? symIndex : null, + transform); +}; + +PV.prototype.add = function(name, obj) { + obj.name(name); + this._objects.push(obj); + this._objects.sort(function(lhs, rhs) { return lhs.order() - rhs.order(); }); + this.requestRedraw(); + return obj; +}; + +PV.prototype._globToRegex = function(glob) { + var r = glob.replace('.', '\\.').replace('*', '.*'); + return new RegExp('^' + r + '$'); +}; + +PV.prototype.forEach = function() { + var callback, pattern = '*'; + if (arguments.length === 2) { + callback = arguments[1]; + pattern = arguments[0]; + } else { + callback = arguments[0]; + } + var regex = this._globToRegex(pattern); + for (var i = 0; i < this._objects.length; ++i) { + var obj = this._objects[i]; + if (regex.test(obj.name())) { + callback(obj, i); + } + } +}; + +PV.prototype.get = function(name) { + for (var i = 0; i < this._objects.length; ++i) { + if (this._objects[i].name() === name) { + return this._objects[i]; + } + } + console.error('could not find object with name', name); + return null; +}; + +PV.prototype.hide = function(glob) { + this.forEach(glob, function(obj) { obj.hide(); }); +}; + +PV.prototype.show = function(glob) { + this.forEach(glob, function(obj) { obj.show(); }); +}; + +// remove all objects whose names match the provided glob pattern from +// the viewer. +PV.prototype.rm = function(glob) { + var newObjects = []; + var regex = this._globToRegex(glob); + for (var i = 0; i < this._objects.length; ++i) { + var obj = this._objects[i]; + if (!regex.test(obj.name())) { + newObjects.push(obj); + } else { + obj.destroy(); + } + } + this._objects = newObjects; +}; + +PV.prototype.all = function() { + return this._objects; +}; + + +return { Viewer : function(elem, options) { return new PV(elem, options); +} +} +; +})(); + +if(typeof(exports) !== 'undefined') { + module.exports = pv; +} diff --git a/src/pdf/pdfjs-lib.js b/src/pdf/pdfjs-lib.js new file mode 100644 index 000000000..8a534cdf7 --- /dev/null +++ b/src/pdf/pdfjs-lib.js @@ -0,0 +1,17 @@ +/* Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals module, __non_webpack_require__ */ + +module.exports = require('pdfjs/pdf'); diff --git a/src/style/examples.css b/src/style/examples.css new file mode 100644 index 000000000..98fbf7af9 --- /dev/null +++ b/src/style/examples.css @@ -0,0 +1,268 @@ +.slick-header-column { + max-height:17px; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +#mfrViewer > div { + font-family: arial; + font-size: 8pt; +} +.slick-header-column.ui-state-default { + height: 25px; + font-size: 12px; +} + +.grid-header { + border: 1px solid gray; + border-bottom: 0; + border-top: 0; + background: url('../img/header-bg.gif') repeat-x center top; + color: black; + height: 24px; + line-height: 24px; + font-family: arial; + font-size: 8pt; +} + +.grid-header label { + display: inline-block; + font-weight: bold; + margin: auto auto auto 6px; +} + +.grid-header .ui-icon { + margin: 4px 4px auto 6px; + background-color: transparent; + border-color: transparent; +} + +.grid-header .ui-icon.ui-state-hover { + background-color: white; +} + +.grid-header #txtSearch { + margin: 0 4px 0 4px; + padding: 2px 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border: 1px solid silver; +} + +.options-panel { + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border: 1px solid silver; + background: #f0f0f0; + padding: 4px; + margin-bottom: 20px; + width: 320px; + position: absolute; + top: 0px; + left: 650px; +} + +/* Individual cell styles */ +.slick-cell.task-name { + font-weight: bold; + text-align: right; +} + +.slick-cell.task-percent { + text-align: right; +} + +.slick-cell.cell-move-handle { + font-weight: bold; + text-align: right; + border-right: solid gray; + + background: #efefef; + cursor: move; +} + +.cell-move-handle:hover { + background: #52bd2b; +} + +.slick-column-name{ + height: 100px; +} +.slick-row.selected .cell-move-handle { + background: #D5DC8D; +} + +.slick-row .cell-actions { + text-align: left; +} + +.slick-row.complete { + background-color: #DFD; + color: #555; +} + +.percent-complete-bar { + display: inline-block; + height: 6px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +/* Slick.Editors.Text, Slick.Editors.Date */ +input.editor-text { + width: 100%; + height: 100%; + border: 0; + margin: 0; + background: transparent; + outline: 0; + padding: 0; + +} + +.ui-datepicker-trigger { + margin-top: 2px; + padding: 0; + vertical-align: top; +} + +/* Slick.Editors.PercentComplete */ +input.editor-percentcomplete { + width: 100%; + height: 100%; + border: 0; + margin: 0; + background: transparent; + outline: 0; + padding: 0; + + float: left; +} + +.editor-percentcomplete-picker { + position: relative; + display: inline-block; + width: 16px; + height: 100%; + background: url('../img/pencil.gif') no-repeat center center; + overflow: visible; + z-index: 1000; + float: right; +} + +.editor-percentcomplete-helper { + border: 0 solid gray; + position: absolute; + top: -2px; + left: -9px; + background: url('../img/editor-helper-bg.gif') no-repeat top left; + padding-left: 9px; + + width: 120px; + height: 140px; + display: none; + overflow: visible; +} + +.editor-percentcomplete-wrapper { + background: white; + padding: 20px 8px; + + width: 100%; + height: 98px; + border: 1px solid gray; + border-left: 0; +} + +.editor-percentcomplete-buttons { + float: right; +} + +.editor-percentcomplete-buttons button { + width: 80px; +} + +.editor-percentcomplete-slider { + float: left; +} + +.editor-percentcomplete-picker:hover .editor-percentcomplete-helper { + display: block; +} + +.editor-percentcomplete-helper:hover { + display: block; +} + +/* Slick.Editors.YesNoSelect */ +select.editor-yesno { + width: 100%; + margin: 0; + vertical-align: middle; +} + +/* Slick.Editors.Checkbox */ +input.editor-checkbox { + margin: 0; + height: 100%; + padding: 0; + border: 0; +} + +.wrapper { + position:relative; + margin:0 auto; + overflow:hidden; + padding:5px; + height:50px; +} + +.list { + left:0px; + top:0px; + margin-left:12px; + margin-top:0px; +} + +.list li{ + display:table-cell; + position:relative; + text-align:center; + cursor:grab; + cursor:-webkit-grab; + color:#efefef; + vertical-align:middle; +} + +.scroller { + position: absolute; + text-align:center; + cursor:pointer; + display:none; + padding:7px; + padding-top:11px; + white-space:no-wrap; + vertical-align:middle; + width: 50px; + height: 45px; + z-index: 9; +} + +.scroller-right{ + right: 0; + background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(100%,rgba(255,255,255,1))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* IE10+ */ +} + +.scroller-left { + left:0; + background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */ +} diff --git a/mfr/server/static/css/default.css b/src/style/md.css similarity index 100% rename from mfr/server/static/css/default.css rename to src/style/md.css diff --git a/src/style/mfr.css b/src/style/mfr.css new file mode 100644 index 000000000..88ff69122 --- /dev/null +++ b/src/style/mfr.css @@ -0,0 +1,77 @@ +.mfr-logo-spin { + -webkit-animation: mfr-spin 3s infinite linear, mfr-opacity 3s infinite linear; + -moz-animation: mfr-spin 3s infinite linear mfr-opacity 3s infinite linear; + animation: mfr-spin 3s infinite linear, mfr-opacity 3s infinite linear; + position: absolute; + top: 0; + left: 50%; + z-index: -1; + background-image: url('../img/cos_logo.png'); + /*background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAEnQAABJ0BfDRroQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjMwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4zMDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjY0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj42NDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgICA8cmRmOlNlcS8+CiAgICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTU6MDQ6MjAgMTM6MDQ6Mzg8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPlBpeGVsbWF0b3IgMy4zLjE8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Ckf0KjsAABM9SURBVHgB3VsLeFRFlq6qe7tvdycYQngTQF7xkQADnXcUg/JQHHYUZZzZ/cZxFtfH7OiHI+7KqCMos+s4uoy6zrAuuzq6ji44OPopCkFEIYS8QCAwoiwgkKBGSQJJv++t/c/t3KaTdN90YmS/b+v7uu/tqlOnzjl16tQ5p6o5+z8oxd7imULIJZKzaxmTWYKxAyDjBaHrb2yvrz91Pkni53OwEq/3Yq6ojzIpFzLONSmlOTznnBEh+HVCGvIZLd2zetu2bZHzQdt5E0BJQckVnBsvCS7G6oaekDdTEBAG2l+9IDPz1s2bN3ckBBzASmUAcSVFVVRUNIUz+QZUfawhjaRw1EBaoSpqXqDDn3WyqfFtW+ABaARN33rhwpCrFM6zjU6V721EXdexQthtRfn5c3qD/abt37oASvPzc0HkdbphP/PdGeECi4WLu7rXD/Tvb10AhhCzFSGcfSWclgIMVEnJ9Olj+tq3L/BqX4ABq5QVFc3SDXYl1nQOPi7JeRM3+M6gjGypT7CFCYNNkf2wNKYAOM/kTudojNsYT6fX6x2lcXWOFLKUSzkaogowxg8pkm/dUbfrQ8CmrG4pC6BoZlGxUOUqw5BXYkYxOdENxPxW2B2adBwvKyz87ejx459ev359zMxja/NEIeNZAMk6djmsdXPz44JJBaSIrpKCEBR8YtqzePFipemzz+6WTCyFjRhnbp4xSoDOMB4sKSh6j0n9gaq6upr4EZO9dx0xCVRJYeH14Hkd1uSlIAhCl6a1jn+CoAzOxfwzLW3jJuVM2XTs2DFzH88ePaZQEXwWwVIxGVdUXZ80sz08fTbXc4qYMXycj/vbI6K9RWVY+WQBO4tPKuLpxsbG5vLycldr89fPwTb8I4SWET+29U7ohRAT0f+GsaOyD51oajxkIUr27FUDiouLQaF8HggyDBtDZhEBAm4Jdvi/Avx95qBcVlvWn+thpo+Y1BG4finTJ850S1VV4f0Qu27W3hLRdv7prPO9F9OYoSvghIR8DPiOEh7gfBSTcIsdDQRH7fAnhjDBni8tKJ2/s3ZnLdUnK7YasDg313lWdbwIIi4iBlMqZLw4LxgzatTWk01NJ0aMGvWFwsSNghlD9KHZAd+SJ4Ux9mIP08MKj4R5dClEBHNoqp5TqDJ3uk89WOlQFJVD8M9W1VRvKSkoKAXSNZCILb3x9IFmt5SRKRmZma80NzfHlmQ8DL3b7gJN6ellgJnVm9TjkZKYIAAHlsNPqR6GsQ2r8zGIhQUW/DQih2a7WAg2q7tAyTsM+ZVg6SJX5NJSXQ8Gjiq69jvCAVx3COBMcQqoi6kJ6HnFoEGDSs2KJF+2AgDjcyHJ2IJMgqNHNWkLOl02x+vNoEatru4PWPO/MyZ701gYzCcrpvYIR7jsxhYR9i/Zvmd7c1lZ2SBYnMtT1sA43ES7MIw5cVU9Xm0FAInn9OiRSgUYwbrPCp08PITAtzEW6bj92Y3MlYad034eOTRBn5TfXPnwx5XUlx89mAk7kUWRUn8Khpti189WADDHml3nZG2YMfR0u9rue3G+BSPDfjfYsX4mf4Jiaeiu8mHR5dmx7MUFzOnyQKTJ+9i0YDna8mArAJDSv9icmHBozMgp/G3ulg7TFhhSP8KMcAQL2oZcNJE/wNmRbbN5ILfC97PI5PynCFdvmpMMKcJrWx5sBQBiTTVMhjxZPVl2Y/iFfum5wAmV/tW0Ta0TPEMz9oOYakHM2BUICPrzct4m31guI6uAw2EMn+A3dwu7fsnbqpI39bIL6FJuMgy9CWpkh6NHGwyWDM2YB4cN27nTPdjANlifz8NcNx6QkUgHV2POXVxfzoQ7ncmAr4K1nXhF8sgP0TeDcERmziWUfbICRDO81sYIl5vjBunxaqsBtbW1n0uDP9kXAfBIiBkTpnVEvnOVxvAO3cXH8NLI+xdkfgDR/EhK44RweZjQ3Iw7XXh6GCefKOh7U2rqzQe+nxuCHqAP+gJHaPpVGuEk3KkWk2bJniAe7PrYCoA6wnd7BtvhH+D/2+Ex20zmM0cG/It/wWG4nNa6BRsxB6ZhXubrit9fYgSDy4xQ4HU4RO8YkcBaTPDCi1vSFx2YnW4SDI872ocmHrgIpwHcqQgB2x9llZ7X0t3/2hvRKel2+YUXuoLDhj3KuXIXNMvM5UU1EsSBQI6gBrNqGBOn+/yLl3NjxIUeFsYkotBMGwHfiob5GSt7Iya+feq7bY9wt+ch9I1WO5xSfH7M517/T1Ic3eeBcySkAhmZy5PCh+gH5AShcU99mXX6l4ffOYx3+5KSACwUxfn5ZRj3NvyeBe3MMrcnsvbDJ/iwTqGqczTm1JwsEja7cDO6k35usMJ98wc1WHhSeeZtaZ8GqVYD1oXFHO2iOhAUBELOfe8F1d0VTHx51MPD4DFkSonijw8xI89V1dbuTGUMgrEVQHl5OQVLKjK0JElMd7Qgx3eBaG8fHvjbX8+N5HhXS/cFTjJWJuNWzg9qKBygPdi+smF+5gqrb1+epha4oAXkPVpCQOjMSBCGLrn/bEg9VLvU/cLyCj0trbm6uvpMHH4O+mnLidhlmHsIoNzrHRpUlBu45Aug1jnQLQc+X8E72QXgDTtrayHlzrJCirwy/99zLlfAssPr65QR1FGGgj5D1584MP+ClejfPy9GSjF185mVTKg/R2LEY9kUmjcZCZ2GnVjRUOl+lq04h7+0oGAWqFiEwLgY8EPxQcQlPkEQv1HT9T9tq68nTYmVLgIoyS/8IWhfBTWfSBDxOw+CEXJvI0yPrHfX19/zHmNfWFguebtlvOpw/ECyyEw4bApTHA0sIjc0XJO+z4L5Js/cd1umK4rzesMI54F3nTN1dyQcfvUv12Z+ZuG9irERfq93NRypxaBVtUJward2MZipI7ARD1bV1bxi9YsJoCy/8B6DsycALOIZtwBNRNiG5OARLFj0vZ3BubfsFoYOg8gPCRnZsG/+4KPxsN/2+9QtrROlVBdBF3IMoQS1iudnatVvlPLWL5lM6GdEBQHeDCHZssq6mtUmT/RVlF+0QBHsz3h1JGMe61uGZ8z1B//qbuRjhnloqzEL9A3q2Cr18OMNVRm/jlfHKMAAf69bp+RlzLufC/U+7tAyLCtGITuY92lvPs0cH21xw07EJjeegk5tCCOveV11XfVGjgSjx8nFdqEoMwlJwoKZD0+/siPwN484sbYc2Lu7gHFsR+TdGUE/DF7Gii6NA/wj952WRxV3+oMGWf/uJ0wKjCNmwvVfDwcd+7amsySaQJMH+7Q7JI3LhYvzK7DmkzMPqy7TBoeD19yJxE5P5ok/CT/ACIcYBPEPl25smTHAPMfQTa/omMFVx7LorpAgyUMTIxRH8JrbVenJCJ8zmjEU5oupLeCZeBcGF3d2be76i4IQfbLXL4dlO6z9vStE5y/MBlTSjQToDxK2D0AlMgWID7RzfkEinPBB5PBxDn2KFwFUV03tDk68wx6wkqTrnnpgWRjDxnngmcbc2e6IrN+wskwKTidB304xZC6N0VshWo2hY+GCJtCSzs7EM/F+LsHfG9b/h+2wknCrOKuy9smEPJJH13zcx2mh91JgSxg35IFewPrfLPgBGqO3QrSKr074YA+Sgpo8g3fBdbYmKRQa6MRGOVzv5s0nw6YLmgwYhMlw0K8b8tVkIN+0Hh7WK0YoGIAxTo4KbrJoPhFSPq13S9oVbArxLoJMfx++wZ7Yvt69AylJR6vD9c4amHpYxERIIWnT74+Ef3NwQeae7igG6vfeuWl7kGF6QiCHkHB2iTa4i66Na3TuayMXPuHQxCvxTLybEAhuFigyJUcoEFx4F7zB4e6YwMgRCgfbpB55HD7AY/32+xOSmqBynVRyM84sF4q6DL5HV0eorbnDdIT2VHh6dYQ4HKFqOELWECm7wpkjWaj4e5WBK2/ei/WuwYZ8DNd7w/45riMWrvPxzH23bTKM/fUw5RdJIYLa1he+o1WRK/xF311hi2Acgv41DOMqBBMTqC5+e4wLhv4bwdC98cFQbsVpnNQ6b8IS8VIswQTfjz349X3zh/QpB2DR0f059e32qUzj1wH/VMq7IOCpk4HQugMLhxy3YMsZGxnyev/FLhhCgHQUXD1YVVPzR6tfTAOsistnXD4s4ggu6hIOS4TD3IDF5Bsqa2q2W7AM4fDUy3x3YhWsFKozC8ijTRQ5hoIdcJyeaKge/Ei/4wOEw7kVZx4WTFnGNSQOsXd3DsCMSOhrEP/w/h2e38fj7wyHoRmihPGu4bAa1jbQaVOMfrz0EEB8Y/m5hEiX8yzzuKqtbbj/J7+aF8kpQkJkUIKECBlGZIv87SsarslcGY831Xeo+UrF5fllF7+/S0LkTEj9tG6p+z+XV/CMjC8rKyvPxuMG/bCWfUyIxCPo/g5jeRli5Vsx0bPQloUDi3SkxSSlxMJIXYenXeViGpKhcSkxLCMflLbw4NWD+uQfTK2A2jNGKTE3ToqipFAmKBQMOvduCam7NyMldsyDdBhHWqwdM/kV9GM7rPu/76qrq4x26P3bVgOs7iRJOp+HZt+NNe4k2xDVRrileIklRSdMQ1IU2duRXZOi0h94eP/Vgx6x8KXyzN2M2dcw+/1IiuIY7enmrNMPpZIU7fWCBMJlR7DDtwZp8R/TTa94w2iuIEhFqqZjIpRj+9M9a+8J+G5d7TNGjE8zNQECMrhOsxkr095tnQkHazFmLA8bN2ymsh+wr+27evDuGJCUubE1T87NF0f9nrU/V0TL5y46KrOsgQUfnRSzVsMWfd/QrzKHZpeX32aXD6S+Ni5VFLWDKXcBocm8NViyJ2ViiEAXUtdQTcTHUQXDd1SHKce3qe1RqTp2cKd2v1Ad3xWq9l3mcC6nOqx5GExp0gRNi/YhHKFgyLX+MWkynyTGj6fJ3CiE8pNgu/9n8fWJ3m0FUFBQMBIJsnu7znoiNOfqSAgK8vbOj94LRhMSJARRTxB5m9uWc83zINapm1QbByPRD94xhltxuR/KK26738Smy49MDQM+ZHhCypG9uFKT6EjNhO7xZdLM2TLioUdjXIWtAHC7c74Qyui+CIBwky+g7qnAXqhLI+Rv1SP6OnJcEHvdH7XoCUJaGDqJpArWw/K8ja2TVJcGv9/fRjgceypMnHF09/pKNON+xBhV8nl2wLYCwLSU2XVO1kYBFA4t3Nx3NiSF8oCZvRV8Ec4C08FQsm50L4DOCdOZIhZ9NNt9DFw/xH1nwoTLvEaXtKdtQ4ldq60AcNg0yq5z0jasWzqxEZ9ULz0wJy16z8cwLk4K37PhIqpqmDvoGfXT+qXm6U+nPekJal+DW3e2PNgKAJtd6sexcXSAfZz0+gMZv7l5k1WNP0fY56csQPLNuIzBpj35o7dxHOYnnP0pWAq2PNgKAEMe6s+gZP3hNn/tzJ58OtZfirqU0llkC7S0XVa/8NicVuxVcMX7JwB0+8TClehpKwBhiApsKd233ER4utTBCNI+vQOXh3FFLlqM5pNvyUDHKWR1raqeT4rnT58Kev7jvkarkc77MPvbCWdfC9GOy9pb7PrZCmB0oJ1cyg9jsb8dps42IhNqFzYM/vt48Mx/XnSj+7XH0nFNJpQws0SCwZ7hevtZXd239bnSGaXjrf7YNtcgksMZX+pCiNIsPzh79qztSXHypBlGP4gblmPGZn+M6fw+BqeT1l5L58BP7aqrWWsBX+b1jpOa+49K0+FM+O8d+thLwiw9U8G+jkAPiRuhRMTpJp/rtcfD6t6tHkVzD9G5rln/GKEbp9mjszOwrZWmsiWbgpKyFS7qj3fv3X3coiPRMyWR4p8bN+C2z1okJAeTl5Wo0KD0gdK9xBTl9qqqKr8FV1pQfDeCuKfMvnTNJT0zGMFZA1LXaQSDBGYHcnge3tHiJOfJNKJMfmkIPg1LwDyENeMRn+/fcPXxZhJCMkHQBKCtBRBLEPe/btGQ7GmrAVanxqamv2SPGY08AJ+IugsxCHiNMmw90XZccn2V5klbvmPHji6WF33vBVyuSTRlasNBVTl12KUc3q0o/7NHUb44isOOCHKe50ITwKdJnW1sPNV4jOig2+eTp0x5Sw+HYFfEJSABt9O70gD8JJr3Qd2SnTU1tmufcFJJSQOioIwtZrivX3R8Fk6E5+A8DBeoOU6XeKPB5c6wrm9O9IcJ6ltaWLQVcLOTaY+FP/5JzEGbbtpVW7suvp7e6Q8TuE09T0hzSYzB/YQAzuUO4bmlsrr6A4AkVtPuiPC7TwJI0D+lqpKCwjehNQv7KgBkgBdU1tW9k9Ig/QSy3QX6ibNHNy7ZuTC3R2vSinZoQf/8kKQoezacFwHAs/szVmcgVXXrNGTvz7n22mM9SR7YmpSM4Dcd8kRT0+fZo0ePUoRaSGbKrtDaR/HByP3dSy+/fMIOdiDazo8GgFJ4ZL+AUduoYBfoZLIH/Z31uIdjLIUxi7nDPQAHsOK8aADRiz8+BTOzst7AX2HIocrDVqVZukCMm8xLth+H8Xfgnl/sEtMA8poQVarLMmHn/laWeEsu5kK/BQK4CV7mUAQ7dJ64xuD8rW53/fo7RMr9/hdwQ5lulTAblwAAAABJRU5ErkJggg==');*/ + width: 100px; + height: 100px; + background-size: cover; +} +@-moz-keyframes mfr-spin { + from { + -moz-transform: rotate(0deg); + } + to { + -moz-transform: rotate(360deg); + } +} +@-webkit-keyframes mfr-spin { + from { + -webkit-transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + } +} +@keyframes mfr-spin { + from { + -webkit-transform: rotate(0deg); + transform:rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform:rotate(360deg); + } +} +@-moz-keyframes mfr-opacity { + 0% { + opacity : 0.1 + } + 50% { + opacity: 1 + } + 100% { + opacity: 0.1 + } +} +@-webkit-keyframes mfr-opacity { + 0% { + opacity : 0.1 + } + 50% { + opacity: 1 + } + 100% { + opacity: 0.1 + } +} +@keyframes mfr-opacity { + 0% { + opacity : 0.1 + } + 50% { + opacity: 1 + } + 100% { + opacity: 0.1 + } +} + +.embed-responsive-pdf { + padding-bottom: 95%; +} diff --git a/src/style/slick-default-theme.css b/src/style/slick-default-theme.css new file mode 100644 index 000000000..723a47126 --- /dev/null +++ b/src/style/slick-default-theme.css @@ -0,0 +1,118 @@ +/* +IMPORTANT: +In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. +No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS +classes should alter those! +*/ + +.slick-header-columns { + background: url('../img/header-columns-bg.gif') repeat-x center bottom; + border-bottom: 1px solid silver; +} + +.slick-header-column { + background: url('../img/header-columns-bg.gif') repeat-x center bottom; + border-right: 1px solid silver; +} + +.slick-header-column:hover, .slick-header-column-active { + background: white url('../img/header-columns-over-bg.gif') repeat-x center bottom; +} + +.slick-headerrow { + background: #fafafa; +} + +.slick-headerrow-column { + background: #fafafa; + border-bottom: 0; + height: 100%; +} + +.slick-row.ui-state-active { + background: #F5F7D7; +} + +.slick-row { + position: absolute; + background: white; + border: 0px; + line-height: 20px; +} + +.slick-row.selected { + z-index: 10; + background: #DFE8F6; +} + +.slick-cell { + padding-left: 4px; + padding-right: 4px; +} + +.slick-group { + border-bottom: 2px solid silver; +} + +.slick-group-toggle { + width: 9px; + height: 9px; + margin-right: 5px; +} + +.slick-group-toggle.expanded { + background: url('../img/collapse.gif') no-repeat center center; +} + +.slick-group-toggle.collapsed { + background: url('../img/expand.gif') no-repeat center center; +} + +.slick-group-totals { + color: gray; + background: white; +} + +.slick-cell.selected { + background-color: beige; +} + +.slick-cell.active { + border-color: gray; + border-style: solid; +} + +.slick-sortable-placeholder { + background: silver !important; +} + +.slick-row.odd { + background: #fafafa; +} + +.slick-row.ui-state-active { + background: #F5F7D7; +} + +.slick-row.loading { + opacity: 0.5; + filter: alpha(opacity = 50); +} + +.slick-cell.invalid { + border-color: red; + -moz-animation-duration: 0.2s; + -webkit-animation-duration: 0.2s; + -moz-animation-name: slickgrid-invalid-hilite; + -webkit-animation-name: slickgrid-invalid-hilite; +} + +@-moz-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } +} + +@-webkit-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } +} diff --git a/src/style/slick.grid.css b/src/style/slick.grid.css new file mode 100644 index 000000000..429f3b9f5 --- /dev/null +++ b/src/style/slick.grid.css @@ -0,0 +1,157 @@ +/* +IMPORTANT: +In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. +No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS +classes should alter those! +*/ + +.slick-header.ui-state-default, .slick-headerrow.ui-state-default { + width: 100%; + overflow: hidden; + border-left: 0px; +} + +.slick-header-columns, .slick-headerrow-columns { + position: relative; + white-space: nowrap; + cursor: default; + overflow: hidden; +} + +.slick-header-column.ui-state-default { + position: relative; + display: inline-block; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + height: 16px; + line-height: 16px; + margin: 0; + padding: 4px; + border-right: 1px solid silver; + border-left: 0px; + border-top: 0px; + border-bottom: 0px; + float: left; +} + +.slick-headerrow-column.ui-state-default { + padding: 4px; +} + +.slick-header-column-sorted { + font-style: italic; +} + +.slick-sort-indicator { + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; + margin-top: 6px; + float: left; +} + +.slick-sort-indicator-desc { + background: url('../img/sort-desc.gif'); +} + +.slick-sort-indicator-asc { + background: url('../img/sort-asc.gif'); +} + +.slick-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + cursor: col-resize; + width: 4px; + right: 0px; + top: 0; + height: 100%; +} + +.slick-sortable-placeholder { + background: silver; +} + +.grid-canvas { + position: relative; + outline: 0; +} + +.slick-row.ui-widget-content, .slick-row.ui-state-active { + position: absolute; + border: 0px; + width: 100%; +} + +.slick-cell, .slick-headerrow-column { + position: absolute; + border: 1px solid transparent; + border-right: 1px dotted silver; + border-bottom-color: silver; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + vertical-align: middle; + z-index: 1; + padding: 1px 2px 2px 1px; + margin: 0; + white-space: nowrap; + cursor: default; +} + +.slick-group { +} + +.slick-group-toggle { + display: inline-block; +} + +.slick-cell.highlighted { + background: lightskyblue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + -o-transition: all 0.5s; + transition: all 0.5s; +} + +.slick-cell.flashing { + border: 1px solid red !important; +} + +.slick-cell.editable { + z-index: 11; + overflow: visible; + background: white; + border-color: black; + border-style: solid; +} + +.slick-cell:focus { + outline: none; +} + +.slick-reorder-proxy { + display: inline-block; + background: blue; + opacity: 0.15; + filter: alpha(opacity = 15); + cursor: move; +} + +.slick-reorder-guide { + display: inline-block; + height: 2px; + background: blue; + opacity: 0.7; + filter: alpha(opacity = 70); +} + +.slick-selection { + z-index: 10; + position: absolute; + border: 2px dashed black; +} diff --git a/src/style/viewer.css b/src/style/viewer.css new file mode 100644 index 000000000..f19705d4a --- /dev/null +++ b/src/style/viewer.css @@ -0,0 +1,1996 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.textLayer { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; + opacity: 0.2; +} + +.textLayer > div { + color: transparent; + position: absolute; + white-space: pre; + cursor: text; + -webkit-transform-origin: 0% 0%; + -moz-transform-origin: 0% 0%; + -o-transform-origin: 0% 0%; + -ms-transform-origin: 0% 0%; + transform-origin: 0% 0%; +} + +.textLayer .highlight { + margin: -1px; + padding: 1px; + + background-color: rgb(180, 0, 170); + border-radius: 4px; +} + +.textLayer .highlight.begin { + border-radius: 4px 0px 0px 4px; +} + +.textLayer .highlight.end { + border-radius: 0px 4px 4px 0px; +} + +.textLayer .highlight.middle { + border-radius: 0px; +} + +.textLayer .highlight.selected { + background-color: rgb(0, 100, 0); +} + +.textLayer ::selection { background: rgb(0,0,255); } +.textLayer ::-moz-selection { background: rgb(0,0,255); } + +.pdfViewer .canvasWrapper { + overflow: hidden; +} + +.pdfViewer .page { + direction: ltr; + width: 816px; + height: 1056px; + margin: 1px auto -8px auto; + position: relative; + overflow: visible; + border: 9px solid transparent; + background-clip: content-box; + border-image: url(images/shadow.png) 9 9 repeat; + background-color: white; +} + +.pdfViewer .page canvas { + margin: 0; + display: block; +} + +.pdfViewer .page .loadingIcon { + position: absolute; + display: block; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: url('images/loading-icon.gif') center no-repeat; +} + +.pdfViewer .page .annotLink > a:hover { + opacity: 0.2; + background: #ff0; + box-shadow: 0px 2px 10px #ff0; +} + +:-webkit-full-screen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +:-moz-full-screen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +:-ms-fullscreen .pdfViewer .page { + margin-bottom: 100% !important; + border: 0; +} + +:fullscreen .pdfViewer .page { + margin-bottom: 100%; + border: 0; +} + +.pdfViewer .page .annotText > img { + position: absolute; + cursor: pointer; +} + +.pdfViewer .page .annotTextContentWrapper { + position: absolute; + width: 20em; +} + +.pdfViewer .page .annotTextContent { + z-index: 200; + float: left; + max-width: 20em; + background-color: #FFFF99; + box-shadow: 0px 2px 5px #333; + border-radius: 2px; + padding: 0.6em; + cursor: pointer; +} + +.pdfViewer .page .annotTextContent > h1 { + font-size: 1em; + border-bottom: 1px solid #000000; + padding-bottom: 0.2em; +} + +.pdfViewer .page .annotTextContent > p { + padding-top: 0.2em; +} + +.pdfViewer .page .annotLink > a { + position: absolute; + font-size: 1em; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.pdfViewer .page .annotLink > a /* -ms-a */ { + background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAA\ + LAAAAAABAAEAAAIBRAA7") 0 0 repeat; +} + +* { + padding: 0; + margin: 0; +} + +html { + height: 100%; + /* Font size is needed to make the activity bar the correct size. */ + font-size: 10px; +} + +body { + height: 100%; + background-color: #404040; + background-image: url(images/texture.png); +} + +body, +input, +button, +select { + font: message-box; + outline: none; +} + +.hidden { + display: none !important; +} +[hidden] { + display: none !important; +} + +#viewerContainer:-webkit-full-screen { + top: 0px; + border-top: 2px solid transparent; + background-color: #000; + width: 100%; + height: 100%; + overflow: hidden; + cursor: none; + -webkit-user-select: none; +} + +#viewerContainer:-moz-full-screen { + top: 0px; + border-top: 2px solid transparent; + background-color: #000; + width: 100%; + height: 100%; + overflow: hidden; + cursor: none; + -moz-user-select: none; +} + +#viewerContainer:-ms-fullscreen { + top: 0px !important; + border-top: 2px solid transparent; + width: 100%; + height: 100%; + overflow: hidden !important; + cursor: none; + -ms-user-select: none; +} + +#viewerContainer:-ms-fullscreen::-ms-backdrop { + background-color: #000; +} + +#viewerContainer:fullscreen { + top: 0px; + border-top: 2px solid transparent; + background-color: #000; + width: 100%; + height: 100%; + overflow: hidden; + cursor: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +:-webkit-full-screen a:not(.internalLink) { + display: none; +} + +:-moz-full-screen a:not(.internalLink) { + display: none; +} + +:-ms-fullscreen a:not(.internalLink) { + display: none !important; +} + +:fullscreen a:not(.internalLink) { + display: none; +} + +:-webkit-full-screen .textLayer > div { + cursor: none; +} + +:-moz-full-screen .textLayer > div { + cursor: none; +} + +:fullscreen .textLayer > div { + cursor: none; +} + +#viewerContainer.presentationControls, +#viewerContainer.presentationControls .textLayer > div { + cursor: default; +} + +/* outer/inner center provides horizontal center */ +.outerCenter { + pointer-events: none; + position: relative; +} +html[dir='ltr'] .outerCenter { + float: right; + right: 50%; +} +html[dir='rtl'] .outerCenter { + float: left; + left: 50%; +} +.innerCenter { + pointer-events: auto; + position: relative; +} +html[dir='ltr'] .innerCenter { + float: right; + right: -50%; +} +html[dir='rtl'] .innerCenter { + float: left; + left: -50%; +} + +#outerContainer { + width: 100%; + height: 100%; + position: relative; +} + +#sidebarContainer { + position: absolute; + top: 0; + bottom: 0; + width: 200px; + visibility: hidden; + -webkit-transition-duration: 200ms; + -webkit-transition-timing-function: ease; + transition-duration: 200ms; + transition-timing-function: ease; + +} +html[dir='ltr'] #sidebarContainer { + -webkit-transition-property: left; + transition-property: left; + left: -200px; +} +html[dir='rtl'] #sidebarContainer { + -webkit-transition-property: right; + transition-property: right; + right: -200px; +} + +#outerContainer.sidebarMoving > #sidebarContainer, +#outerContainer.sidebarOpen > #sidebarContainer { + visibility: visible; +} +html[dir='ltr'] #outerContainer.sidebarOpen > #sidebarContainer { + left: 0px; +} +html[dir='rtl'] #outerContainer.sidebarOpen > #sidebarContainer { + right: 0px; +} + +#mainContainer { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + min-width: 320px; + -webkit-transition-duration: 200ms; + -webkit-transition-timing-function: ease; + transition-duration: 200ms; + transition-timing-function: ease; +} +html[dir='ltr'] #outerContainer.sidebarOpen > #mainContainer { + -webkit-transition-property: left; + transition-property: left; + left: 200px; +} +html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer { + -webkit-transition-property: right; + transition-property: right; + right: 200px; +} + +#sidebarContent { + top: 32px; + bottom: 0; + overflow: auto; + -webkit-overflow-scrolling: touch; + position: absolute; + width: 200px; + background-color: hsla(0,0%,0%,.1); +} +html[dir='ltr'] #sidebarContent { + left: 0; + box-shadow: inset -1px 0 0 hsla(0,0%,0%,.25); +} +html[dir='rtl'] #sidebarContent { + right: 0; + box-shadow: inset 1px 0 0 hsla(0,0%,0%,.25); +} + +#viewerContainer { + overflow: auto; + -webkit-overflow-scrolling: touch; + position: absolute; + top: 32px; + right: 0; + bottom: 0; + left: 0; + outline: none; +} +html[dir='ltr'] #viewerContainer { + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); +} +html[dir='rtl'] #viewerContainer { + box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05); +} + +.toolbar { + position: relative; + left: 0; + right: 0; + z-index: 9999; + cursor: default; +} + +#toolbarContainer { + width: 100%; +} + +#toolbarSidebar { + width: 200px; + height: 32px; + background-color: #424242; /* fallback */ + background-image: url(images/texture.png), + linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95)); +} +html[dir='ltr'] #toolbarSidebar { + box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 0 1px hsla(0,0%,0%,.1); +} +html[dir='rtl'] #toolbarSidebar { + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25), + inset 0 1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 0 1px hsla(0,0%,0%,.1); +} + +#toolbarContainer, .findbar, .secondaryToolbar { + position: relative; + height: 32px; + background-color: #474747; /* fallback */ + background-image: url(images/texture.png), + linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); +} +html[dir='ltr'] #toolbarContainer, .findbar, .secondaryToolbar { + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08), + inset 0 1px 1px hsla(0,0%,0%,.15), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 1px 1px hsla(0,0%,0%,.1); +} +html[dir='rtl'] #toolbarContainer, .findbar, .secondaryToolbar { + box-shadow: inset -1px 0 0 hsla(0,0%,100%,.08), + inset 0 1px 1px hsla(0,0%,0%,.15), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 1px 1px hsla(0,0%,0%,.1); +} + +#toolbarViewer { + height: 32px; +} + +#loadingBar { + position: relative; + width: 100%; + height: 4px; + background-color: #333; + border-bottom: 1px solid #333; +} + +#loadingBar .progress { + position: absolute; + top: 0; + left: 0; + width: 0%; + height: 100%; + background-color: #ddd; + overflow: hidden; + -webkit-transition: width 200ms; + transition: width 200ms; +} + +@-webkit-keyframes progressIndeterminate { + 0% { left: 0%; } + 50% { left: 100%; } + 100% { left: 100%; } +} + +@keyframes progressIndeterminate { + 0% { left: 0%; } + 50% { left: 100%; } + 100% { left: 100%; } +} + +#loadingBar .progress.indeterminate { + background-color: #999; + -webkit-transition: none; + transition: none; +} + +#loadingBar .indeterminate .glimmer { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 50px; + + background-image: linear-gradient(to right, #999 0%, #fff 50%, #999 100%); + background-size: 100% 100%; + background-repeat: no-repeat; + + -webkit-animation: progressIndeterminate 2s linear infinite; + animation: progressIndeterminate 2s linear infinite; +} + +.findbar, .secondaryToolbar { + top: 32px; + position: absolute; + z-index: 10000; + height: 32px; + + min-width: 16px; + padding: 0px 6px 0px 6px; + margin: 4px 2px 4px 2px; + color: hsl(0,0%,85%); + font-size: 12px; + line-height: 14px; + text-align: left; + cursor: default; +} + +html[dir='ltr'] .findbar { + left: 68px; +} + +html[dir='rtl'] .findbar { + right: 68px; +} + +.findbar label { + -webkit-user-select: none; + -moz-user-select: none; +} + +#findInput[data-status="pending"] { + background-image: url(images/loading-small.png); + background-repeat: no-repeat; + background-position: right; +} +html[dir='rtl'] #findInput[data-status="pending"] { + background-position: left; +} + +.secondaryToolbar { + padding: 6px; + height: auto; + z-index: 30000; +} +html[dir='ltr'] .secondaryToolbar { + right: 4px; +} +html[dir='rtl'] .secondaryToolbar { + left: 4px; +} + +#secondaryToolbarButtonContainer { + max-width: 200px; + max-height: 400px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + margin-bottom: -4px; +} + +.doorHanger, +.doorHangerRight { + border: 1px solid hsla(0,0%,0%,.5); + border-radius: 2px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); +} +.doorHanger:after, .doorHanger:before, +.doorHangerRight:after, .doorHangerRight:before { + bottom: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.doorHanger:after, +.doorHangerRight:after { + border-bottom-color: hsla(0,0%,32%,.99); + border-width: 8px; +} +.doorHanger:before, +.doorHangerRight:before { + border-bottom-color: hsla(0,0%,0%,.5); + border-width: 9px; +} + +html[dir='ltr'] .doorHanger:after, +html[dir='rtl'] .doorHangerRight:after { + left: 13px; + margin-left: -8px; +} + +html[dir='ltr'] .doorHanger:before, +html[dir='rtl'] .doorHangerRight:before { + left: 13px; + margin-left: -9px; +} + +html[dir='rtl'] .doorHanger:after, +html[dir='ltr'] .doorHangerRight:after { + right: 13px; + margin-right: -8px; +} + +html[dir='rtl'] .doorHanger:before, +html[dir='ltr'] .doorHangerRight:before { + right: 13px; + margin-right: -9px; +} + +#findMsg { + font-style: italic; + color: #A6B7D0; +} + +#findInput.notFound { + background-color: rgb(255, 102, 102); +} + +html[dir='ltr'] #toolbarViewerLeft { + margin-left: -1px; +} +html[dir='rtl'] #toolbarViewerRight { + margin-right: -1px; +} + +html[dir='ltr'] #toolbarViewerLeft, +html[dir='rtl'] #toolbarViewerRight { + position: absolute; + top: 0; + left: 0; +} +html[dir='ltr'] #toolbarViewerRight, +html[dir='rtl'] #toolbarViewerLeft { + position: absolute; + top: 0; + right: 0; +} +html[dir='ltr'] #toolbarViewerLeft > *, +html[dir='ltr'] #toolbarViewerMiddle > *, +html[dir='ltr'] #toolbarViewerRight > *, +html[dir='ltr'] .findbar > * { + position: relative; + float: left; +} +html[dir='rtl'] #toolbarViewerLeft > *, +html[dir='rtl'] #toolbarViewerMiddle > *, +html[dir='rtl'] #toolbarViewerRight > *, +html[dir='rtl'] .findbar > * { + position: relative; + float: right; +} + +html[dir='ltr'] .splitToolbarButton { + margin: 3px 2px 4px 0; + display: inline-block; +} +html[dir='rtl'] .splitToolbarButton { + margin: 3px 0 4px 2px; + display: inline-block; +} +html[dir='ltr'] .splitToolbarButton > .toolbarButton { + border-radius: 0; + float: left; +} +html[dir='rtl'] .splitToolbarButton > .toolbarButton { + border-radius: 0; + float: right; +} + +.toolbarButton, +.secondaryToolbarButton, +.overlayButton { + border: 0 none; + background: none; + width: 32px; + height: 25px; +} + +.toolbarButton > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; +} + +.toolbarButton[disabled], +.secondaryToolbarButton[disabled], +.overlayButton[disabled] { + opacity: .5; +} + +.toolbarButton.group { + margin-right: 0; +} + +.splitToolbarButton.toggled .toolbarButton { + margin: 0; +} + +.splitToolbarButton:hover > .toolbarButton, +.splitToolbarButton:focus > .toolbarButton, +.splitToolbarButton.toggled > .toolbarButton, +.toolbarButton.textButton { + background-color: hsla(0,0%,0%,.12); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + border: 1px solid hsla(0,0%,0%,.35); + border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 150ms; + -webkit-transition-timing-function: ease; + transition-property: background-color, border-color, box-shadow; + transition-duration: 150ms; + transition-timing-function: ease; + +} +.splitToolbarButton > .toolbarButton:hover, +.splitToolbarButton > .toolbarButton:focus, +.dropdownToolbarButton:hover, +.overlayButton:hover, +.toolbarButton.textButton:hover, +.toolbarButton.textButton:focus { + background-color: hsla(0,0%,0%,.2); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 0 1px hsla(0,0%,0%,.05); + z-index: 199; +} +.splitToolbarButton > .toolbarButton { + position: relative; +} +html[dir='ltr'] .splitToolbarButton > .toolbarButton:first-child, +html[dir='rtl'] .splitToolbarButton > .toolbarButton:last-child { + position: relative; + margin: 0; + margin-right: -1px; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; + border-right-color: transparent; +} +html[dir='ltr'] .splitToolbarButton > .toolbarButton:last-child, +html[dir='rtl'] .splitToolbarButton > .toolbarButton:first-child { + position: relative; + margin: 0; + margin-left: -1px; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-left-color: transparent; +} +.splitToolbarButtonSeparator { + padding: 8px 0; + width: 1px; + background-color: hsla(0,0%,0%,.5); + z-index: 99; + box-shadow: 0 0 0 1px hsla(0,0%,100%,.08); + display: inline-block; + margin: 5px 0; +} +html[dir='ltr'] .splitToolbarButtonSeparator { + float: left; +} +html[dir='rtl'] .splitToolbarButtonSeparator { + float: right; +} +.splitToolbarButton:hover > .splitToolbarButtonSeparator, +.splitToolbarButton.toggled > .splitToolbarButtonSeparator { + padding: 12px 0; + margin: 1px 0; + box-shadow: 0 0 0 1px hsla(0,0%,100%,.03); + -webkit-transition-property: padding; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: ease; + transition-property: padding; + transition-duration: 10ms; + transition-timing-function: ease; +} + +.toolbarButton, +.dropdownToolbarButton, +.secondaryToolbarButton, +.overlayButton { + min-width: 16px; + padding: 2px 6px 0; + border: 1px solid transparent; + border-radius: 2px; + color: hsla(0,0%,100%,.8); + font-size: 12px; + line-height: 14px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + /* Opera does not support user-select, use <... unselectable="on"> instead */ + cursor: default; + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 150ms; + -webkit-transition-timing-function: ease; + transition-property: background-color, border-color, box-shadow; + transition-duration: 150ms; + transition-timing-function: ease; +} + +html[dir='ltr'] .toolbarButton, +html[dir='ltr'] .overlayButton, +html[dir='ltr'] .dropdownToolbarButton { + margin: 3px 2px 4px 0; +} +html[dir='rtl'] .toolbarButton, +html[dir='rtl'] .overlayButton, +html[dir='rtl'] .dropdownToolbarButton { + margin: 3px 0 4px 2px; +} + +.toolbarButton:hover, +.toolbarButton:focus, +.dropdownToolbarButton, +.overlayButton, +.secondaryToolbarButton:hover, +.secondaryToolbarButton:focus { + background-color: hsla(0,0%,0%,.12); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + border: 1px solid hsla(0,0%,0%,.35); + border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 1px 0 hsla(0,0%,100%,.05); +} + +.toolbarButton:hover:active, +.overlayButton:hover:active, +.dropdownToolbarButton:hover:active, +.secondaryToolbarButton:hover:active { + background-color: hsla(0,0%,0%,.2); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.4) hsla(0,0%,0%,.45); + box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: linear; + transition-property: background-color, border-color, box-shadow; + transition-duration: 10ms; + transition-timing-function: linear; +} + +.toolbarButton.toggled, +.splitToolbarButton.toggled > .toolbarButton.toggled, +.secondaryToolbarButton.toggled { + background-color: hsla(0,0%,0%,.3); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.45) hsla(0,0%,0%,.5); + box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: linear; + transition-property: background-color, border-color, box-shadow; + transition-duration: 10ms; + transition-timing-function: linear; +} + +.toolbarButton.toggled:hover:active, +.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active, +.secondaryToolbarButton.toggled:hover:active { + background-color: hsla(0,0%,0%,.4); + border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.5) hsla(0,0%,0%,.55); + box-shadow: 0 1px 1px hsla(0,0%,0%,.2) inset, + 0 0 1px hsla(0,0%,0%,.3) inset, + 0 1px 0 hsla(0,0%,100%,.05); +} + +.dropdownToolbarButton { + width: 120px; + max-width: 120px; + padding: 3px 2px 2px; + overflow: hidden; + background: url(images/toolbarButton-menuArrows.png) no-repeat; +} +html[dir='ltr'] .dropdownToolbarButton { + background-position: 95%; +} +html[dir='rtl'] .dropdownToolbarButton { + background-position: 5%; +} + +.dropdownToolbarButton > select { + min-width: 140px; + font-size: 12px; + color: hsl(0,0%,95%); + margin: 0; + padding: 0; + border: none; + background: rgba(0,0,0,0); /* Opera does not support 'transparent'