(function (root, initialize){ var Elegans = initialize(); if(typeof define !== "undefined" && define.amd)define(Elegans); root.Elegans = Elegans; }(this, function(){ //modules here /** * @license almond 0.3.3 Copyright jQuery Foundation and other contributors. * Released under MIT license, http://github.com/requirejs/almond/LICENSE */ //Going sloppy to avoid 'use strict' string cost, but strict practices should //be followed. /*global setTimeout: false */ var requirejs, require, define; (function (undef) { var main, req, makeMap, handlers, defined = {}, waiting = {}, config = {}, defining = {}, hasOwn = Object.prototype.hasOwnProperty, aps = [].slice, jsSuffixRegExp = /\.js$/; function hasProp(obj, prop) { return hasOwn.call(obj, prop); } /** * Given a relative module name, like ./something, normalize it to * a real name that can be mapped to a path. * @param {String} name the relative name * @param {String} baseName a real name that the name arg is relative * to. * @returns {String} normalized name */ function normalize(name, baseName) { var nameParts, nameSegment, mapValue, foundMap, lastIndex, foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, baseParts = baseName && baseName.split("/"), map = config.map, starMap = (map && map['*']) || {}; //Adjust any relative paths. if (name) { name = name.split('/'); lastIndex = name.length - 1; // If wanting node ID compatibility, strip .js from end // of IDs. Have to do this here, and not in nameToUrl // because node allows either .js or non .js to map // to same file. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); } // Starts with a '.' so need the baseName if (name[0].charAt(0) === '.' && baseParts) { //Convert baseName to array, and lop off the last part, //so that . matches that 'directory' and not name of the baseName's //module. For instance, baseName of 'one/two/three', maps to //'one/two/three.js', but we want the directory, 'one/two' for //this normalization. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); name = normalizedBaseParts.concat(name); } //start trimDots for (i = 0; i < name.length; i++) { part = name[i]; if (part === '.') { name.splice(i, 1); i -= 1; } else if (part === '..') { // If at the start, or previous value is still .., // keep them so that when converted to a path it may // still work when converted to a path, even though // as an ID it is less than ideal. In larger point // releases, may be better to just kick out an error. if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { continue; } else if (i > 0) { name.splice(i - 1, 2); i -= 2; } } } //end trimDots name = name.join('/'); } //Apply map config if available. if ((baseParts || starMap) && map) { nameParts = name.split('/'); for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join("/"); if (baseParts) { //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { mapValue = map[baseParts.slice(0, j).join('/')]; //baseName segment has config, find if it has one for //this name. if (mapValue) { mapValue = mapValue[nameSegment]; if (mapValue) { //Match, update name to the new value. foundMap = mapValue; foundI = i; break; } } } } if (foundMap) { break; } //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. if (!foundStarMap && starMap && starMap[nameSegment]) { foundStarMap = starMap[nameSegment]; starI = i; } } if (!foundMap && foundStarMap) { foundMap = foundStarMap; foundI = starI; } if (foundMap) { nameParts.splice(0, foundI, foundMap); name = nameParts.join('/'); } } return name; } function makeRequire(relName, forceSync) { return function () { //A version of a require function that passes a moduleName //value for items that may need to //look up paths relative to the moduleName var args = aps.call(arguments, 0); //If first arg is not require('string'), and there is only //one arg, it is the array form without a callback. Insert //a null so that the following concat is correct. if (typeof args[0] !== 'string' && args.length === 1) { args.push(null); } return req.apply(undef, args.concat([relName, forceSync])); }; } function makeNormalize(relName) { return function (name) { return normalize(name, relName); }; } function makeLoad(depName) { return function (value) { defined[depName] = value; }; } function callDep(name) { if (hasProp(waiting, name)) { var args = waiting[name]; delete waiting[name]; defining[name] = true; main.apply(undef, args); } if (!hasProp(defined, name) && !hasProp(defining, name)) { throw new Error('No ' + name); } return defined[name]; } //Turns a plugin!resource to [plugin, resource] //with the plugin being undefined if the name //did not have a plugin prefix. function splitPrefix(name) { var prefix, index = name ? name.indexOf('!') : -1; if (index > -1) { prefix = name.substring(0, index); name = name.substring(index + 1, name.length); } return [prefix, name]; } //Creates a parts array for a relName where first part is plugin ID, //second part is resource ID. Assumes relName has already been normalized. function makeRelParts(relName) { return relName ? splitPrefix(relName) : []; } /** * Makes a name map, normalizing the name, and using a plugin * for normalization if necessary. Grabs a ref to plugin * too, as an optimization. */ makeMap = function (name, relParts) { var plugin, parts = splitPrefix(name), prefix = parts[0], relResourceName = relParts[1]; name = parts[1]; if (prefix) { prefix = normalize(prefix, relResourceName); plugin = callDep(prefix); } //Normalize according if (prefix) { if (plugin && plugin.normalize) { name = plugin.normalize(name, makeNormalize(relResourceName)); } else { name = normalize(name, relResourceName); } } else { name = normalize(name, relResourceName); parts = splitPrefix(name); prefix = parts[0]; name = parts[1]; if (prefix) { plugin = callDep(prefix); } } //Using ridiculous property names for space reasons return { f: prefix ? prefix + '!' + name : name, //fullName n: name, pr: prefix, p: plugin }; }; function makeConfig(name) { return function () { return (config && config.config && config.config[name]) || {}; }; } handlers = { require: function (name) { return makeRequire(name); }, exports: function (name) { var e = defined[name]; if (typeof e !== 'undefined') { return e; } else { return (defined[name] = {}); } }, module: function (name) { return { id: name, uri: '', exports: defined[name], config: makeConfig(name) }; } }; main = function (name, deps, callback, relName) { var cjsModule, depName, ret, map, i, relParts, args = [], callbackType = typeof callback, usingExports; //Use name if no relName relName = relName || name; relParts = makeRelParts(relName); //Call the callback to define the module, if necessary. if (callbackType === 'undefined' || callbackType === 'function') { //Pull out the defined dependencies and pass the ordered //values to the callback. //Default to [require, exports, module] if no deps deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; for (i = 0; i < deps.length; i += 1) { map = makeMap(deps[i], relParts); depName = map.f; //Fast path CommonJS standard dependencies. if (depName === "require") { args[i] = handlers.require(name); } else if (depName === "exports") { //CommonJS module spec 1.1 args[i] = handlers.exports(name); usingExports = true; } else if (depName === "module") { //CommonJS module spec 1.1 cjsModule = args[i] = handlers.module(name); } else if (hasProp(defined, depName) || hasProp(waiting, depName) || hasProp(defining, depName)) { args[i] = callDep(depName); } else if (map.p) { map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); args[i] = defined[depName]; } else { throw new Error(name + ' missing ' + depName); } } ret = callback ? callback.apply(defined[name], args) : undefined; if (name) { //If setting exports via "module" is in play, //favor that over return value and exports. After that, //favor a non-undefined return value over exports use. if (cjsModule && cjsModule.exports !== undef && cjsModule.exports !== defined[name]) { defined[name] = cjsModule.exports; } else if (ret !== undef || !usingExports) { //Use the return value from the function. defined[name] = ret; } } } else if (name) { //May just be an object definition for the module. Only //worry about defining if have a module name. defined[name] = callback; } }; requirejs = require = req = function (deps, callback, relName, forceSync, alt) { if (typeof deps === "string") { if (handlers[deps]) { //callback in this case is really relName return handlers[deps](callback); } //Just return the module wanted. In this scenario, the //deps arg is the module name, and second arg (if passed) //is just the relName. //Normalize module name, if it contains . or .. return callDep(makeMap(deps, makeRelParts(callback)).f); } else if (!deps.splice) { //deps is a config object, not an array. config = deps; if (config.deps) { req(config.deps, config.callback); } if (!callback) { return; } if (callback.splice) { //callback is an array, which means it is a dependency list. //Adjust args if there are dependencies deps = callback; callback = relName; relName = null; } else { deps = undef; } } //Support require(['a']) callback = callback || function () {}; //If relName is a function, it is an errback handler, //so remove it. if (typeof relName === 'function') { relName = forceSync; forceSync = alt; } //Simulate async callback; if (forceSync) { main(undef, deps, callback, relName); } else { //Using a non-zero value because of concern for what old browsers //do, and latest browsers "upgrade" to 4 if lower value is used: //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: //If want a value immediately, use require('id') instead -- something //that works in almond on the global level, but not guaranteed and //unlikely to work in other AMD implementations. setTimeout(function () { main(undef, deps, callback, relName); }, 4); } return req; }; /** * Just drops the config on the floor, but returns req in case * the config return value is used. */ req.config = function (cfg) { return req(cfg); }; /** * Expose module registry for debugging and tooling */ requirejs._defined = defined; define = function (name, deps, callback) { if (typeof name !== 'string') { throw new Error('See almond README: incorrect module build, no module name'); } //This module may not have dependencies if (!deps.splice) { //deps is not an array, so probably means //an object literal or factory function for //the value. Adjust args. callback = deps; deps = []; } if (!hasProp(defined, name) && !hasProp(waiting, name)) { waiting[name] = [name, deps, callback]; } }; define.amd = { jQuery: true }; }()); define("../node_modules/almond/almond", function(){}); define('utils/TrackballControls',[],function(){ /** * @author Eberhard Graether / http://egraether.com/ * @author Mark Lundin / http://mark-lundin.com */ var TrackballControls = function ( object, domElement ) { var _this = this; var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; this.object = object; this.domElement = ( domElement !== undefined ) ? domElement : document; // API this.enabled = true; this.screen = { left: 0, top: 0, width: 0, height: 0 }; this.rotateSpeed = 1.0; this.zoomSpeed = 1.2; this.panSpeed = 0.3; this.noRotate = false; this.noZoom = false; this.noPan = false; this.noRoll = false; this.staticMoving = false; this.dynamicDampingFactor = 0.2; this.minDistance = 0; this.maxDistance = Infinity; this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; // internals this.target = new THREE.Vector3(); var EPS = 0.000001; var lastPosition = new THREE.Vector3(); var _state = STATE.NONE, _prevState = STATE.NONE, _eye = new THREE.Vector3(), _rotateStart = new THREE.Vector3(), _rotateEnd = new THREE.Vector3(), _zoomStart = new THREE.Vector2(), _zoomEnd = new THREE.Vector2(), _touchZoomDistanceStart = 0, _touchZoomDistanceEnd = 0, _panStart = new THREE.Vector2(), _panEnd = new THREE.Vector2(); // for reset this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); this.up0 = this.object.up.clone(); // events var changeEvent = { type: 'change' }; var startEvent = { type: 'start'}; var endEvent = { type: 'end'}; // methods this.handleResize = function () { if ( this.domElement === document ) { this.screen.left = 0; this.screen.top = 0; this.screen.width = window.innerWidth; this.screen.height = window.innerHeight; } else { var box = this.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function var d = this.domElement.ownerDocument.documentElement; this.screen.left = box.left + window.pageXOffset - d.clientLeft; this.screen.top = box.top + window.pageYOffset - d.clientTop; this.screen.width = box.width; this.screen.height = box.height; } }; this.handleEvent = function ( event ) { if ( typeof this[ event.type ] == 'function' ) { this[ event.type ]( event ); } }; var getMouseOnScreen = ( function () { var vector = new THREE.Vector2(); return function ( pageX, pageY ) { vector.set( ( pageX - _this.screen.left ) / _this.screen.width, ( pageY - _this.screen.top ) / _this.screen.height ); return vector; }; }() ); var getMouseProjectionOnBall = ( function () { var vector = new THREE.Vector3(); var objectUp = new THREE.Vector3(); var mouseOnBall = new THREE.Vector3(); return function ( pageX, pageY ) { mouseOnBall.set( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5), ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5), 0.0 ); var length = mouseOnBall.length(); if ( _this.noRoll ) { if ( length < Math.SQRT1_2 ) { mouseOnBall.z = Math.sqrt( 1.0 - length*length ); } else { mouseOnBall.z = .5 / length; } } else if ( length > 1.0 ) { mouseOnBall.normalize(); } else { mouseOnBall.z = Math.sqrt( 1.0 - length * length ); } _eye.copy( _this.object.position ).sub( _this.target ); vector.copy( _this.object.up ).setLength( mouseOnBall.y ); vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); vector.add( _eye.setLength( mouseOnBall.z ) ); return vector; }; }() ); this.rotateCamera = (function(){ var axis = new THREE.Vector3(), quaternion = new THREE.Quaternion(); return function () { var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); if ( angle ) { axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); angle *= _this.rotateSpeed; quaternion.setFromAxisAngle( axis, -angle ); _eye.applyQuaternion( quaternion ); _this.object.up.applyQuaternion( quaternion ); _rotateEnd.applyQuaternion( quaternion ); if ( _this.staticMoving ) { _rotateStart.copy( _rotateEnd ); } else { quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); _rotateStart.applyQuaternion( quaternion ); } } }; }()); this.zoomCamera = function () { if ( _state === STATE.TOUCH_ZOOM_PAN ) { var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; _touchZoomDistanceStart = _touchZoomDistanceEnd; _eye.multiplyScalar( factor ); } else { var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; if ( factor !== 1.0 && factor > 0.0 ) { _eye.multiplyScalar( factor ); if ( _this.staticMoving ) { _zoomStart.copy( _zoomEnd ); } else { _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; } } } }; this.panCamera = (function(){ var mouseChange = new THREE.Vector2(), objectUp = new THREE.Vector3(), pan = new THREE.Vector3(); return function () { mouseChange.copy( _panEnd ).sub( _panStart ); if ( mouseChange.lengthSq() ) { mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); _this.object.position.add( pan ); _this.target.add( pan ); if ( _this.staticMoving ) { _panStart.copy( _panEnd ); } else { _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); } } }; }()); this.checkDistances = function () { if ( !_this.noZoom || !_this.noPan ) { if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); } if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); } } }; this.update = function () { _eye.subVectors( _this.object.position, _this.target ); if ( !_this.noRotate ) { _this.rotateCamera(); } if ( !_this.noZoom ) { _this.zoomCamera(); } if ( !_this.noPan ) { _this.panCamera(); } _this.object.position.addVectors( _this.target, _eye ); _this.checkDistances(); _this.object.lookAt( _this.target ); if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { _this.dispatchEvent( changeEvent ); lastPosition.copy( _this.object.position ); } }; this.reset = function () { _state = STATE.NONE; _prevState = STATE.NONE; _this.target.copy( _this.target0 ); _this.object.position.copy( _this.position0 ); _this.object.up.copy( _this.up0 ); _eye.subVectors( _this.object.position, _this.target ); _this.object.lookAt( _this.target ); _this.dispatchEvent( changeEvent ); lastPosition.copy( _this.object.position ); }; // listeners function keydown( event ) { if ( _this.enabled === false ) return; window.removeEventListener( 'keydown', keydown ); _prevState = _state; if ( _state !== STATE.NONE ) { return; } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { _state = STATE.ROTATE; } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { _state = STATE.ZOOM; } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { _state = STATE.PAN; } } function keyup( event ) { if ( _this.enabled === false ) return; _state = _prevState; window.addEventListener( 'keydown', keydown, false ); } function mousedown( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); if ( _state === STATE.NONE ) { _state = event.button; } if ( _state === STATE.ROTATE && !_this.noRotate ) { _rotateStart.copy(getMouseProjectionOnBall( event.layerX, event.layerY )); _rotateEnd.copy( _rotateStart ); } else if ( _state === STATE.ZOOM && !_this.noZoom ) { _zoomStart.copy( getMouseOnScreen( event.layerX, event.layerY ) ); _zoomEnd.copy(_zoomStart); } else if ( _state === STATE.PAN && !_this.noPan ) { _panStart.copy( getMouseOnScreen( event.layerX, event.layerY ) ); _panEnd.copy(_panStart); } document.addEventListener( 'mousemove', mousemove, false ); document.addEventListener( 'mouseup', mouseup, false ); _this.dispatchEvent( startEvent ); } function mousemove( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); if ( _state === STATE.ROTATE && !_this.noRotate ) { _rotateEnd.copy( getMouseProjectionOnBall( event.layerX, event.layerY ) ); } else if ( _state === STATE.ZOOM && !_this.noZoom ) { _zoomEnd.copy( getMouseOnScreen( event.layerX, event.layerY ) ); } else if ( _state === STATE.PAN && !_this.noPan ) { _panEnd.copy( getMouseOnScreen( event.layerX, event.layerY ) ); } } function mouseup( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); _state = STATE.NONE; document.removeEventListener( 'mousemove', mousemove ); document.removeEventListener( 'mouseup', mouseup ); _this.dispatchEvent( endEvent ); } function mousewheel( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); var delta = 0; if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 delta = event.wheelDelta / 40; } else if ( event.detail ) { // Firefox delta = - event.detail / 3; } _zoomStart.y += delta * 0.01; _this.dispatchEvent( startEvent ); _this.dispatchEvent( endEvent ); } function touchstart( event ) { if ( _this.enabled === false ) return; switch ( event.touches.length ) { case 1: _state = STATE.TOUCH_ROTATE; _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); _rotateEnd.copy( _rotateStart ); break; case 2: _state = STATE.TOUCH_ZOOM_PAN; var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panStart.copy( getMouseOnScreen( x, y ) ); _panEnd.copy( _panStart ); break; default: _state = STATE.NONE; } _this.dispatchEvent( startEvent ); } function touchmove( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); switch ( event.touches.length ) { case 1: _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); break; case 2: var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panEnd.copy( getMouseOnScreen( x, y ) ); break; default: _state = STATE.NONE; } } function touchend( event ) { if ( _this.enabled === false ) return; switch ( event.touches.length ) { case 1: _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); _rotateStart.copy( _rotateEnd ); break; case 2: _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panEnd.copy( getMouseOnScreen( x, y ) ); _panStart.copy( _panEnd ); break; } _state = STATE.NONE; _this.dispatchEvent( endEvent ); } this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); this.domElement.addEventListener( 'mousedown', mousedown, false ); this.domElement.addEventListener( 'mousewheel', mousewheel, false ); this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox this.domElement.addEventListener( 'touchstart', touchstart, false ); this.domElement.addEventListener( 'touchend', touchend, false ); this.domElement.addEventListener( 'touchmove', touchmove, false ); window.addEventListener( 'keydown', keydown, false ); window.addEventListener( 'keyup', keyup, false ); this.handleResize(); // force an update at start this.update(); }; TrackballControls.prototype = Object.create(THREE.EventDispatcher.prototype); TrackballControls.prototype.constructor = TrackballControls; return TrackballControls; }); define('utils/OrthographicTrackballControls',[], function(){ /** * @author Eberhard Graether / http://egraether.com/ * @author Mark Lundin / http://mark-lundin.com * @author Patrick Fuller / http://patrick-fuller.com */ var OrthographicTrackballControls = function ( object, domElement ) { var _this = this; var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; this.object = object; this.domElement = ( domElement !== undefined ) ? domElement : document; // API this.enabled = true; this.screen = { left: 0, top: 0, width: 0, height: 0 }; this.rotateSpeed = 1.0; this.zoomSpeed = 1.2; this.panSpeed = 0.3; this.noRotate = false; this.noZoom = false; this.noPan = false; this.noRoll = false; this.staticMoving = false; this.dynamicDampingFactor = 0.2; this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; // internals this.target = new THREE.Vector3(); var EPS = 0.000001; var lastPosition = new THREE.Vector3(); var _state = STATE.NONE, _prevState = STATE.NONE, _eye = new THREE.Vector3(), _rotateStart = new THREE.Vector3(), _rotateEnd = new THREE.Vector3(), _zoomStart = new THREE.Vector2(), _zoomEnd = new THREE.Vector2(), _zoomFactor = 1, _touchZoomDistanceStart = 0, _touchZoomDistanceEnd = 0, _panStart = new THREE.Vector2(), _panEnd = new THREE.Vector2(); // for reset this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); this.up0 = this.object.up.clone(); this.left0 = this.object.left; this.right0 = this.object.right; this.top0 = this.object.top; this.bottom0 = this.object.bottom; this.center0 = new THREE.Vector2((this.left0 + this.right0) / 2.0, (this.top0 + this.bottom0) / 2.0); // events var changeEvent = { type: 'change' }; var startEvent = { type: 'start'}; var endEvent = { type: 'end'}; // methods this.handleResize = function () { if ( this.domElement === document ) { this.screen.left = 0; this.screen.top = 0; this.screen.width = window.innerWidth; this.screen.height = window.innerHeight; } else { var box = this.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function var d = this.domElement.ownerDocument.documentElement; this.screen.left = box.left + window.pageXOffset - d.clientLeft; this.screen.top = box.top + window.pageYOffset - d.clientTop; this.screen.width = box.width; this.screen.height = box.height; } this.left0 = this.object.left; this.right0 = this.object.right; this.top0 = this.object.top; this.bottom0 = this.object.bottom; this.center0.set((this.left0 + this.right0) / 2.0, (this.top0 + this.bottom0) / 2.0); }; this.handleEvent = function ( event ) { if ( typeof this[ event.type ] == 'function' ) { this[ event.type ]( event ); } }; var getMouseOnScreen = ( function () { var vector = new THREE.Vector2(); return function ( pageX, pageY ) { vector.set( ( pageX - _this.screen.left ) / _this.screen.width, ( pageY - _this.screen.top ) / _this.screen.height ); return vector; }; }() ); var getMouseProjectionOnBall = ( function () { var vector = new THREE.Vector3(); var objectUp = new THREE.Vector3(); var mouseOnBall = new THREE.Vector3(); return function ( pageX, pageY ) { mouseOnBall.set( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5), ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5), 0.0 ); var length = mouseOnBall.length(); if ( _this.noRoll ) { if ( length < Math.SQRT1_2 ) { mouseOnBall.z = Math.sqrt( 1.0 - length*length ); } else { mouseOnBall.z = .5 / length; } } else if ( length > 1.0 ) { mouseOnBall.normalize(); } else { mouseOnBall.z = Math.sqrt( 1.0 - length * length ); } _eye.copy( _this.object.position ).sub( _this.target ); vector.copy( _this.object.up ).setLength( mouseOnBall.y ); vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); vector.add( _eye.setLength( mouseOnBall.z ) ); return vector; }; }() ); this.rotateCamera = (function(){ var axis = new THREE.Vector3(), quaternion = new THREE.Quaternion(); return function () { var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); if ( angle ) { axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); angle *= _this.rotateSpeed; quaternion.setFromAxisAngle( axis, -angle ); _eye.applyQuaternion( quaternion ); _this.object.up.applyQuaternion( quaternion ); _rotateEnd.applyQuaternion( quaternion ); if ( _this.staticMoving ) { _rotateStart.copy( _rotateEnd ); } else { quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); _rotateStart.applyQuaternion( quaternion ); } } }; }()); this.zoomCamera = function () { var factor; if ( _state === STATE.TOUCH_ZOOM_PAN ) { factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; _touchZoomDistanceStart = _touchZoomDistanceEnd; } else { factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; } if ( factor !== 1.0 && factor > 0.0 ) { _zoomFactor *= factor; _this.object.left = _zoomFactor * _this.left0 + ( 1 - _zoomFactor ) * _this.center0.x; _this.object.right = _zoomFactor * _this.right0 + ( 1 - _zoomFactor ) * _this.center0.x; _this.object.top = _zoomFactor * _this.top0 + ( 1 - _zoomFactor ) * _this.center0.y; _this.object.bottom = _zoomFactor * _this.bottom0 + ( 1 - _zoomFactor ) * _this.center0.y; if ( _this.staticMoving ) { _zoomStart.copy( _zoomEnd ); } else { _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; } } }; this.panCamera = (function(){ var mouseChange = new THREE.Vector2(), objectUp = new THREE.Vector3(), pan = new THREE.Vector3(); return function () { mouseChange.copy( _panEnd ).sub( _panStart ); if ( mouseChange.lengthSq() ) { mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); _this.object.position.add( pan ); _this.target.add( pan ); if ( _this.staticMoving ) { _panStart.copy( _panEnd ); } else { _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); } } }; }()); this.update = function () { _eye.subVectors( _this.object.position, _this.target ); if ( !_this.noRotate ) { _this.rotateCamera(); } if ( !_this.noZoom ) { _this.zoomCamera(); _this.object.updateProjectionMatrix(); } if ( !_this.noPan ) { _this.panCamera(); } _this.object.position.addVectors( _this.target, _eye ); _this.object.lookAt( _this.target ); if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { _this.dispatchEvent( changeEvent ); lastPosition.copy( _this.object.position ); } }; this.reset = function () { _state = STATE.NONE; _prevState = STATE.NONE; _this.target.copy( _this.target0 ); _this.object.position.copy( _this.position0 ); _this.object.up.copy( _this.up0 ); _eye.subVectors( _this.object.position, _this.target ); _this.object.left = _this.left0; _this.object.right = _this.right0; _this.object.top = _this.top0; _this.object.bottom = _this.bottom0; _this.object.lookAt( _this.target ); _this.dispatchEvent( changeEvent ); lastPosition.copy( _this.object.position ); }; // listeners function keydown( event ) { if ( _this.enabled === false ) return; window.removeEventListener( 'keydown', keydown ); _prevState = _state; if ( _state !== STATE.NONE ) { return; } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { _state = STATE.ROTATE; } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { _state = STATE.ZOOM; } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { _state = STATE.PAN; } } function keyup( event ) { if ( _this.enabled === false ) return; _state = _prevState; window.addEventListener( 'keydown', keydown, false ); } function mousedown( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); if ( _state === STATE.NONE ) { _state = event.button; } if ( _state === STATE.ROTATE && !_this.noRotate ) { _rotateStart.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); _rotateEnd.copy( _rotateStart ); } else if ( _state === STATE.ZOOM && !_this.noZoom ) { _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); _zoomEnd.copy(_zoomStart); } else if ( _state === STATE.PAN && !_this.noPan ) { _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); _panEnd.copy(_panStart); } document.addEventListener( 'mousemove', mousemove, false ); document.addEventListener( 'mouseup', mouseup, false ); _this.dispatchEvent( startEvent ); } function mousemove( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); if ( _state === STATE.ROTATE && !_this.noRotate ) { _rotateEnd.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); } else if ( _state === STATE.ZOOM && !_this.noZoom ) { _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); } else if ( _state === STATE.PAN && !_this.noPan ) { _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); } } function mouseup( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); _state = STATE.NONE; document.removeEventListener( 'mousemove', mousemove ); document.removeEventListener( 'mouseup', mouseup ); _this.dispatchEvent( endEvent ); } function mousewheel( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); var delta = 0; if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 delta = event.wheelDelta / 40; } else if ( event.detail ) { // Firefox delta = - event.detail / 3; } _zoomStart.y += delta * 0.01; _this.dispatchEvent( startEvent ); _this.dispatchEvent( endEvent ); } function touchstart( event ) { if ( _this.enabled === false ) return; switch ( event.touches.length ) { case 1: _state = STATE.TOUCH_ROTATE; _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); _rotateEnd.copy( _rotateStart ); break; case 2: _state = STATE.TOUCH_ZOOM_PAN; var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panStart.copy( getMouseOnScreen( x, y ) ); _panEnd.copy( _panStart ); break; default: _state = STATE.NONE; } _this.dispatchEvent( startEvent ); } function touchmove( event ) { if ( _this.enabled === false ) return; event.preventDefault(); event.stopPropagation(); switch ( event.touches.length ) { case 1: _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); break; case 2: var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panEnd.copy( getMouseOnScreen( x, y ) ); break; default: _state = STATE.NONE; } } function touchend( event ) { if ( _this.enabled === false ) return; switch ( event.touches.length ) { case 1: _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); _rotateStart.copy( _rotateEnd ); break; case 2: _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; _panEnd.copy( getMouseOnScreen( x, y ) ); _panStart.copy( _panEnd ); break; } _state = STATE.NONE; _this.dispatchEvent( endEvent ); } this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); this.domElement.addEventListener( 'mousedown', mousedown, false ); this.domElement.addEventListener( 'mousewheel', mousewheel, false ); this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox this.domElement.addEventListener( 'touchstart', touchstart, false ); this.domElement.addEventListener( 'touchend', touchend, false ); this.domElement.addEventListener( 'touchmove', touchmove, false ); window.addEventListener( 'keydown', keydown, false ); window.addEventListener( 'keyup', keyup, false ); this.handleResize(); // force an update at start this.update(); }; OrthographicTrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); OrthographicTrackballControls.prototype.constructor = OrthographicTrackballControls; return OrthographicTrackballControls; }); // http://threejs.org/examples/js/controls/OrbitControls.js define('utils/OrbitControls',[],function(){ function OrbitConstraint ( object ) { this.object = object; // "target" sets the location of focus, where the object orbits around // and where it pans with respect to. this.target = new THREE.Vector3(); // Limits to how far you can dolly in and out ( PerspectiveCamera only ) this.minDistance = 0; this.maxDistance = Infinity; // Limits to how far you can zoom in and out ( OrthographicCamera only ) this.minZoom = 0; this.maxZoom = Infinity; // How far you can orbit vertically, upper and lower limits. // Range is 0 to Math.PI radians. this.minPolarAngle = 0; // radians this.maxPolarAngle = Math.PI; // radians // How far you can orbit horizontally, upper and lower limits. // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. this.minAzimuthAngle = - Infinity; // radians this.maxAzimuthAngle = Infinity; // radians // Set to true to enable damping (inertia) // If damping is enabled, you must call controls.update() in your animation loop this.enableDamping = false; this.dampingFactor = 0.25; //////////// // internals var scope = this; var EPS = 0.000001; // Current position in spherical coordinate system. var theta; var phi; // Pending changes var phiDelta = 0; var thetaDelta = 0; var scale = 1; var panOffset = new THREE.Vector3(); var zoomChanged = false; // API this.getPolarAngle = function () { return phi; }; this.getAzimuthalAngle = function () { return theta; }; this.rotateLeft = function ( angle ) { thetaDelta -= angle; }; this.rotateUp = function ( angle ) { phiDelta -= angle; }; // pass in distance in world space to move left this.panLeft = function() { var v = new THREE.Vector3(); return function panLeft ( distance ) { var te = this.object.matrix.elements; // get X column of matrix v.set( te[ 0 ], te[ 1 ], te[ 2 ] ); v.multiplyScalar( - distance ); panOffset.add( v ); }; }(); // pass in distance in world space to move up this.panUp = function() { var v = new THREE.Vector3(); return function panUp ( distance ) { var te = this.object.matrix.elements; // get Y column of matrix v.set( te[ 4 ], te[ 5 ], te[ 6 ] ); v.multiplyScalar( distance ); panOffset.add( v ); }; }(); // pass in x,y of change desired in pixel space, // right and down are positive this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) { if ( scope.object instanceof THREE.PerspectiveCamera ) { // perspective var position = scope.object.position; var offset = position.clone().sub( scope.target ); var targetDistance = offset.length(); // half of the fov is center to top of screen targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); // we actually don't use screenWidth, since perspective camera is fixed to screen height scope.panLeft( 2 * deltaX * targetDistance / screenHeight ); scope.panUp( 2 * deltaY * targetDistance / screenHeight ); } else if ( scope.object instanceof THREE.OrthographicCamera ) { // orthographic scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth ); scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight ); } else { // camera neither orthographic or perspective console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); } }; this.dollyIn = function ( dollyScale ) { if ( scope.object instanceof THREE.PerspectiveCamera ) { scale /= dollyScale; } else if ( scope.object instanceof THREE.OrthographicCamera ) { scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); scope.object.updateProjectionMatrix(); zoomChanged = true; } else { console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); } }; this.dollyOut = function ( dollyScale ) { if ( scope.object instanceof THREE.PerspectiveCamera ) { scale *= dollyScale; } else if ( scope.object instanceof THREE.OrthographicCamera ) { scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); scope.object.updateProjectionMatrix(); zoomChanged = true; } else { console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); } }; this.update = function() { var offset = new THREE.Vector3(); // so camera.up is the orbit axis var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); var quatInverse = quat.clone().inverse(); var lastPosition = new THREE.Vector3(); var lastQuaternion = new THREE.Quaternion(); return function () { var position = this.object.position; offset.copy( position ).sub( this.target ); // rotate offset to "y-axis-is-up" space offset.applyQuaternion( quat ); // angle from z-axis around y-axis theta = Math.atan2( offset.x, offset.z ); // angle from y-axis phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); theta += thetaDelta; phi += phiDelta; // restrict theta to be between desired limits theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); // restrict phi to be between desired limits phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); // restrict phi to be betwee EPS and PI-EPS phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); var radius = offset.length() * scale; // restrict radius to be between desired limits radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); // move target to panned location this.target.add( panOffset ); offset.x = radius * Math.sin( phi ) * Math.sin( theta ); offset.y = radius * Math.cos( phi ); offset.z = radius * Math.sin( phi ) * Math.cos( theta ); // rotate offset back to "camera-up-vector-is-up" space offset.applyQuaternion( quatInverse ); position.copy( this.target ).add( offset ); this.object.lookAt( this.target ); if ( this.enableDamping === true ) { thetaDelta *= ( 1 - this.dampingFactor ); phiDelta *= ( 1 - this.dampingFactor ); } else { thetaDelta = 0; phiDelta = 0; } scale = 1; panOffset.set( 0, 0, 0 ); // update condition is: // min(camera displacement, camera rotation in radians)^2 > EPS // using small-angle approximation cos(x/2) = 1 - x^2 / 8 if ( zoomChanged || lastPosition.distanceToSquared( this.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) { lastPosition.copy( this.object.position ); lastQuaternion.copy( this.object.quaternion ); zoomChanged = false; return true; } return false; }; }(); }; // This set of controls performs orbiting, dollying (zooming), and panning. It maintains // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is // supported. // // Orbit - left mouse / touch: one finger move // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish // Pan - right mouse, or arrow keys / touch: three finter swipe var OrbitControls = function ( object, domElement ) { var constraint = new OrbitConstraint( object ); this.domElement = ( domElement !== undefined ) ? domElement : document; // API Object.defineProperty( this, 'constraint', { get: function() { return constraint; } } ); this.getPolarAngle = function () { return constraint.getPolarAngle(); }; this.getAzimuthalAngle = function () { return constraint.getAzimuthalAngle(); }; // Set to false to disable this control this.enabled = true; // center is old, deprecated; use "target" instead this.center = this.target; // This option actually enables dollying in and out; left as "zoom" for // backwards compatibility. // Set to false to disable zooming this.enableZoom = true; this.zoomSpeed = 1.0; // Set to false to disable rotating this.enableRotate = true; this.rotateSpeed = 1.0; // Set to false to disable panning this.enablePan = true; this.keyPanSpeed = 7.0; // pixels moved per arrow key push // Set to true to automatically rotate around the target // If auto-rotate is enabled, you must call controls.update() in your animation loop this.autoRotate = false; this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 // Set to false to disable use of the keys this.enableKeys = true; // The four arrow keys this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; // Mouse buttons this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; //////////// // internals var scope = this; var rotateStart = new THREE.Vector2(); var rotateEnd = new THREE.Vector2(); var rotateDelta = new THREE.Vector2(); var panStart = new THREE.Vector2(); var panEnd = new THREE.Vector2(); var panDelta = new THREE.Vector2(); var dollyStart = new THREE.Vector2(); var dollyEnd = new THREE.Vector2(); var dollyDelta = new THREE.Vector2(); var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; var state = STATE.NONE; // for reset console.log(this.target); this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); this.zoom0 = this.object.zoom; // events var changeEvent = { type: 'change' }; var startEvent = { type: 'start' }; var endEvent = { type: 'end' }; // pass in x,y of change desired in pixel space, // right and down are positive function pan( deltaX, deltaY ) { var element = scope.domElement === document ? scope.domElement.body : scope.domElement; constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight ); } this.update = function () { if ( this.autoRotate && state === STATE.NONE ) { constraint.rotateLeft( getAutoRotationAngle() ); } if ( constraint.update() === true ) { this.dispatchEvent( changeEvent ); } }; this.reset = function () { state = STATE.NONE; this.target.copy( this.target0 ); this.object.position.copy( this.position0 ); this.object.zoom = this.zoom0; this.object.updateProjectionMatrix(); this.dispatchEvent( changeEvent ); this.update(); }; function getAutoRotationAngle() { return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; } function getZoomScale() { return Math.pow( 0.95, scope.zoomSpeed ); } function onMouseDown( event ) { if ( scope.enabled === false ) return; event.preventDefault(); if ( event.button === scope.mouseButtons.ORBIT ) { if ( scope.enableRotate === false ) return; state = STATE.ROTATE; rotateStart.set( event.clientX, event.clientY ); } else if ( event.button === scope.mouseButtons.ZOOM ) { if ( scope.enableZoom === false ) return; state = STATE.DOLLY; dollyStart.set( event.clientX, event.clientY ); } else if ( event.button === scope.mouseButtons.PAN ) { if ( scope.enablePan === false ) return; state = STATE.PAN; panStart.set( event.clientX, event.clientY ); } if ( state !== STATE.NONE ) { document.addEventListener( 'mousemove', onMouseMove, false ); document.addEventListener( 'mouseup', onMouseUp, false ); scope.dispatchEvent( startEvent ); } } function onMouseMove( event ) { if ( scope.enabled === false ) return; event.preventDefault(); var element = scope.domElement === document ? scope.domElement.body : scope.domElement; if ( state === STATE.ROTATE ) { if ( scope.enableRotate === false ) return; rotateEnd.set( event.clientX, event.clientY ); rotateDelta.subVectors( rotateEnd, rotateStart ); // rotating across whole screen goes 360 degrees around constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); // rotating up and down along whole screen attempts to go 360, but limited to 180 constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); rotateStart.copy( rotateEnd ); } else if ( state === STATE.DOLLY ) { if ( scope.enableZoom === false ) return; dollyEnd.set( event.clientX, event.clientY ); dollyDelta.subVectors( dollyEnd, dollyStart ); if ( dollyDelta.y > 0 ) { constraint.dollyIn( getZoomScale() ); } else if ( dollyDelta.y < 0 ) { constraint.dollyOut( getZoomScale() ); } dollyStart.copy( dollyEnd ); } else if ( state === STATE.PAN ) { if ( scope.enablePan === false ) return; panEnd.set( event.clientX, event.clientY ); panDelta.subVectors( panEnd, panStart ); pan( panDelta.x, panDelta.y ); panStart.copy( panEnd ); } if ( state !== STATE.NONE ) scope.update(); } function onMouseUp( /* event */ ) { if ( scope.enabled === false ) return; document.removeEventListener( 'mousemove', onMouseMove, false ); document.removeEventListener( 'mouseup', onMouseUp, false ); scope.dispatchEvent( endEvent ); state = STATE.NONE; } function onMouseWheel( event ) { if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; event.preventDefault(); event.stopPropagation(); var delta = 0; if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 delta = event.wheelDelta; } else if ( event.detail !== undefined ) { // Firefox delta = - event.detail; } if ( delta > 0 ) { constraint.dollyOut( getZoomScale() ); } else if ( delta < 0 ) { constraint.dollyIn( getZoomScale() ); } scope.update(); scope.dispatchEvent( startEvent ); scope.dispatchEvent( endEvent ); } function onKeyDown( event ) { if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; switch ( event.keyCode ) { case scope.keys.UP: pan( 0, scope.keyPanSpeed ); scope.update(); break; case scope.keys.BOTTOM: pan( 0, - scope.keyPanSpeed ); scope.update(); break; case scope.keys.LEFT: pan( scope.keyPanSpeed, 0 ); scope.update(); break; case scope.keys.RIGHT: pan( - scope.keyPanSpeed, 0 ); scope.update(); break; } } function touchstart( event ) { if ( scope.enabled === false ) return; switch ( event.touches.length ) { case 1: // one-fingered touch: rotate if ( scope.enableRotate === false ) return; state = STATE.TOUCH_ROTATE; rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); break; case 2: // two-fingered touch: dolly if ( scope.enableZoom === false ) return; state = STATE.TOUCH_DOLLY; var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; var distance = Math.sqrt( dx * dx + dy * dy ); dollyStart.set( 0, distance ); break; case 3: // three-fingered touch: pan if ( scope.enablePan === false ) return; state = STATE.TOUCH_PAN; panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); break; default: state = STATE.NONE; } if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); } function touchmove( event ) { if ( scope.enabled === false ) return; event.preventDefault(); event.stopPropagation(); var element = scope.domElement === document ? scope.domElement.body : scope.domElement; switch ( event.touches.length ) { case 1: // one-fingered touch: rotate if ( scope.enableRotate === false ) return; if ( state !== STATE.TOUCH_ROTATE ) return; rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); rotateDelta.subVectors( rotateEnd, rotateStart ); // rotating across whole screen goes 360 degrees around constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); // rotating up and down along whole screen attempts to go 360, but limited to 180 constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); rotateStart.copy( rotateEnd ); scope.update(); break; case 2: // two-fingered touch: dolly if ( scope.enableZoom === false ) return; if ( state !== STATE.TOUCH_DOLLY ) return; var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; var distance = Math.sqrt( dx * dx + dy * dy ); dollyEnd.set( 0, distance ); dollyDelta.subVectors( dollyEnd, dollyStart ); if ( dollyDelta.y > 0 ) { constraint.dollyOut( getZoomScale() ); } else if ( dollyDelta.y < 0 ) { constraint.dollyIn( getZoomScale() ); } dollyStart.copy( dollyEnd ); scope.update(); break; case 3: // three-fingered touch: pan if ( scope.enablePan === false ) return; if ( state !== STATE.TOUCH_PAN ) return; panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); panDelta.subVectors( panEnd, panStart ); pan( panDelta.x, panDelta.y ); panStart.copy( panEnd ); scope.update(); break; default: state = STATE.NONE; } } function touchend( /* event */ ) { if ( scope.enabled === false ) return; scope.dispatchEvent( endEvent ); state = STATE.NONE; } function contextmenu( event ) { event.preventDefault(); } this.dispose = function() { this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); this.domElement.removeEventListener( 'mousedown', onMouseDown, false ); this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false ); this.domElement.removeEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox this.domElement.removeEventListener( 'touchstart', touchstart, false ); this.domElement.removeEventListener( 'touchend', touchend, false ); this.domElement.removeEventListener( 'touchmove', touchmove, false ); document.removeEventListener( 'mousemove', onMouseMove, false ); document.removeEventListener( 'mouseup', onMouseUp, false ); window.removeEventListener( 'keydown', onKeyDown, false ); } this.domElement.addEventListener( 'contextmenu', contextmenu, false ); this.domElement.addEventListener( 'mousedown', onMouseDown, false ); this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox this.domElement.addEventListener( 'touchstart', touchstart, false ); this.domElement.addEventListener( 'touchend', touchend, false ); this.domElement.addEventListener( 'touchmove', touchmove, false ); window.addEventListener( 'keydown', onKeyDown, false ); // force an update at start this.update(); }; OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); OrbitControls.prototype.constructor = OrbitControls; Object.defineProperties( OrbitControls.prototype, { object: { get: function () { return this.constraint.object; } }, target: { get: function () { return this.constraint.target; }, set: function ( value ) { console.warn( 'OrbitControls: target is now immutable. Use target.set() instead.' ); this.constraint.target.copy( value ); } }, minDistance : { get: function () { return this.constraint.minDistance; }, set: function ( value ) { this.constraint.minDistance = value; } }, maxDistance : { get: function () { return this.constraint.maxDistance; }, set: function ( value ) { this.constraint.maxDistance = value; } }, minZoom : { get: function () { return this.constraint.minZoom; }, set: function ( value ) { this.constraint.minZoom = value; } }, maxZoom : { get: function () { return this.constraint.maxZoom; }, set: function ( value ) { this.constraint.maxZoom = value; } }, minPolarAngle : { get: function () { return this.constraint.minPolarAngle; }, set: function ( value ) { this.constraint.minPolarAngle = value; } }, maxPolarAngle : { get: function () { return this.constraint.maxPolarAngle; }, set: function ( value ) { this.constraint.maxPolarAngle = value; } }, minAzimuthAngle : { get: function () { return this.constraint.minAzimuthAngle; }, set: function ( value ) { this.constraint.minAzimuthAngle = value; } }, maxAzimuthAngle : { get: function () { return this.constraint.maxAzimuthAngle; }, set: function ( value ) { this.constraint.maxAzimuthAngle = value; } }, enableDamping : { get: function () { return this.constraint.enableDamping; }, set: function ( value ) { this.constraint.enableDamping = value; } }, dampingFactor : { get: function () { return this.constraint.dampingFactor; }, set: function ( value ) { this.constraint.dampingFactor = value; } }, // backward compatibility noZoom: { get: function () { console.warn( 'OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); return ! this.enableZoom; }, set: function ( value ) { console.warn( 'OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); this.enableZoom = ! value; } }, noRotate: { get: function () { console.warn( 'OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); return ! this.enableRotate; }, set: function ( value ) { console.warn( 'OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); this.enableRotate = ! value; } }, noPan: { get: function () { console.warn( 'OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); return ! this.enablePan; }, set: function ( value ) { console.warn( 'OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); this.enablePan = ! value; } }, noKeys: { get: function () { console.warn( 'OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); return ! this.enableKeys; }, set: function ( value ) { console.warn( 'OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); this.enableKeys = ! value; } }, staticMoving : { get: function () { console.warn( 'OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); return ! this.constraint.enableDamping; }, set: function ( value ) { console.warn( 'OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); this.constraint.enableDamping = ! value; } }, dynamicDampingFactor : { get: function () { console.warn( 'OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); return this.constraint.dampingFactor; }, set: function ( value ) { console.warn( 'OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); this.constraint.dampingFactor = value; } } } ); return OrbitControls; }); define('utils/utils',[],function(){ var mixin = function(sub, sup) { sup.call(sub); }; var merge = function(dest, src){ for(var key in src){ dest[key] = src[key]; } return dest; }; var exports = { mixin:mixin, merge:merge }; return exports; }); define('components/world',[ "utils/TrackballControls", "utils/OrthographicTrackballControls", "utils/OrbitControls", "utils/utils" ],function(TrackballControls, OrthographicTrackballControls, OrbitControls, Utils){ function World(options){ this.options = { width: 0, height: 0, perspective: true, bg_color: 0xffffff, orbit: false, save_image: false }; if(arguments.length > 0){ Utils.merge(this.options, options); }; this.scene = new THREE.Scene(); if(this.options.perspective) this.camera = new THREE.PerspectiveCamera(45, this.options.width/this.options.height, 1, 1000); else this.camera = new THREE.OrthographicCamera(-20,20,-20,20); this.scene.add(this.camera); var positions = [[1,1,1],[-1,-1,1],[-1,1,1],[1,-1,1]]; for(var i=0;i<4;i++){ var light=new THREE.DirectionalLight(0xdddddd); light.position.set(positions[i][0],positions[i][1],1*positions[i][2]); this.scene.add(light); } this.renderer = new THREE.WebGLRenderer({ antialias:true, clearAlpha: 1, preserveDrawingBuffer: !this.options.save_image }); this.renderer.setSize(this.options.width, this.options.height); this.renderer.setClearColor(this.options.bg_color, 1); this.renderer.sortObjects = false; if(this.options.perspective && this.options.orbit) this.controls = new OrbitControls(this.camera, this.renderer.domElement); else if (this.options.perspective) this.controls = new TrackballControls(this.camera, this.renderer.domElement); else this.controls = new OrthographicTrackballControls(this.camera, this.renderer.domElement); this.controls.screen = {left: 0, top: 0, width: this.options.width, height: this.options.height}; this.controls.rotateSpeed = 0.5; this.camera.position.set(-30, 31,42); this.camera.rotation.set(-0.6,-0.5,0.6); return this; } World.prototype.begin = function(selection){ selection[0][0].appendChild(this.renderer.domElement); var world = this; var minInterval = 1000/30; var before = Date.now(); this.animate = function(){ window.requestAnimationFrame(world.animate); var now = Date.now(); if(now - before > minInterval){ before += minInterval; world.renderer.render(world.scene, world.camera); world.controls.update(); } }; this.animate(); }; World.prototype.addMesh = function(mesh){ if(mesh instanceof Array){ for(var i=0; i 1){ Utils.merge(this.options, options); }; var BIGIN=-10, END=10, WIDTH=END-BIGIN; var geometry = new THREE.PlaneGeometry(WIDTH,WIDTH); var material = new THREE.MeshBasicMaterial({color:0xf0f0f0, shading: THREE.FlatShading, overdraw: 0.5, side: THREE.DoubleSide}); var newV = function(x,y,z){return new THREE.Vector3(x,y,z);}; this.meshes = []; if(this.options.mode == "solid"){ var xy_plane = new THREE.Mesh(geometry, material); var xz_plane = new THREE.Mesh(geometry, material); var yz_plane = new THREE.Mesh(geometry, material); xz_plane.rotateOnAxis(newV(1,0,0), Math.PI/2); xz_plane.translateOnAxis(newV(0,0,-1), -10); yz_plane.rotateOnAxis(newV(0,1,0), Math.PI/2); yz_plane.translateOnAxis(newV(0,0,-1), -10); xy_plane.translateOnAxis(newV(0,0,1), -10); this.meshes.push(xy_plane); this.meshes.push(xz_plane); this.meshes.push(yz_plane); }else if(this.options.mode == "wireframe"){ var coordinates = [ [[-10, 10, -10], [-10, -10, -10],[10,-10,-10]], [[-10, 10, 10], [-10, -10, 10], [10,-10,10],[10,10,10], [-10, 10, 10]], [[10, -10, -10], [10, -10, 10]], [[-10, 10, -10], [-10, 10, 10]], [[-10, -10, -10], [-10, -10, 10]], [[10, -10, -10], [10, 10, -10]] ]; var meshes = this.meshes; } this.scales = {}; this.scales.x = d3.scale.linear().domain([ranges.x.max, ranges.x.min]).range([-10, 10]); this.scales.y = d3.scale.linear().domain([ranges.y.max, ranges.y.min]).range([10, -10]); this.scales.z = d3.scale.linear().domain([ranges.z.max, ranges.z.min]).range([10,-10]); // generate axis var x_scale = d3.scale.linear().domain([ranges.x.max, ranges.x.min]).range([20, 0]); var y_scale = d3.scale.linear().domain([ranges.y.max, ranges.y.min]).range([20, 0]); var z_scale = d3.scale.linear().domain([ranges.z.max, ranges.z.min]).range([20,0]); this.meshes = this.meshes.concat(generateAxisAndLabels(this.options, this.options.axis.labels.x, newV( 10,10,-10), newV(-10,10,-10), newV(0,1,0),x_scale)); this.meshes = this.meshes.concat(generateAxisAndLabels(this.options, this.options.axis.labels.y, newV(-10,-10,-10),newV(-10,10,-10),newV(-1,0,0),y_scale)); this.meshes = this.meshes.concat(generateAxisAndLabels(this.options, this.options.axis.labels.z, newV(10,10,-10),newV(10,10,10),newV(0,1,0),z_scale)); // generate grids if(this.options.grid){ this.meshes.push(generateGrid([-10,10],[-10,10],[-10,-10],2));//x-y this.meshes.push(generateGrid([-10,10],[-10,-10],[-10,10],2));//x-z this.meshes.push(generateGrid([10,10],[-10,10],[-10,10],2));//y-z } return this; } var generateLabel = function(text, position, labelOptions){ var canvas = document.createElement('canvas'); var options = labelOptions || {}; canvas.width = options.height || 100; canvas.height = options.width || 100; var spriteScale = options.scale || 1.5; var context = canvas.getContext('2d'); context.fillStyle = options.fill || "rgb(0, 0, 0)"; context.font = options.font || "60px sans-serif"; var text_width = context.measureText(text).width; console.log("TEXT-WIDTH: " + text_width + "," + (canvas.width-text_width)/2); context.fillText(text, (canvas.width-text_width)/2, .8 * canvas.height); //context.fillText(text, 0, 80); var texture = new THREE.Texture(canvas); texture.flipY = true; texture.needsUpdate = true; var material = new THREE.SpriteMaterial({ map: texture, transparent: true, useScreenCoordinates: false }); var sprite = new THREE.Sprite(material); sprite.scale.set(spriteScale, spriteScale); sprite.position.set.apply(sprite.position, position.toArray()); return sprite; }; var generateAxisAndLabels = function(options, axis_label, axis_start, axis_end, nv_tick, scale){ var meshes = []; var geometry = new THREE.Geometry(); var nv_start2end = (new THREE.Vector3).subVectors(axis_end, axis_start).normalize(); geometry.vertices.push(axis_start); geometry.vertices.push(axis_end); var label_position = (new THREE.Vector3).addVectors(axis_end, axis_start).divideScalar(2); label_position.add(nv_tick.clone().multiplyScalar(3)); console.dir(options); meshes.push(generateLabel(axis_label, label_position, options.axis.labelOptions)); console.log("OPTIONS"); console.dir(options); // generate d3.js axis var svg = d3.select("body") .append("svg") .style("width", "" + options.width) .style("height", "" + options.height) .style("display", "none"); var ticks = svg.append("g") .call(d3.svg.axis() .scale(scale) .orient("left") .ticks(options.numTicks)) .selectAll(".tick"); // parse svg axis, and generate ticks and labels mimicing svg's. var tick_values = []; for(var i=0; i [{t: 0, data: [{x:1, y:2, z:3}, ..., {x:10, y:2, z:5}]}, {t: 100, data: []}, ..., {}] // this.lists -> {0:[], 1:[], ..., 100:[]} DataBase.add = function(name, data, data_label, seek_label, init){ var raw = {}, range=[Infinity,-Infinity]; data.forEach(function(row){ var val = row[seek_label]; raw[row[seek_label]] = row[data_label]; range[0] = (val > range[0] ? range[0] : val); range[1] = (val < range[1] ? range[1] : val); }); this.lists[name] = {data:raw, seek:init}; this.range = range; }; DataBase.seek = function(name, seek){ for(var n in this.lists){ this.lists[n].seek = seek; } //this.lists[name].seek = seek; }; DataBase.getRange = function(){ return this.range; }; DataBase.find = function(name){ var seek = this.lists[name].seek; return this.lists[name].data[seek]; }; return DataBase; }); define('components/player',[ "utils/utils", "utils/database" ], function(Utils, DataBase){ function Player(element, stage, options){ this.options = { }; if(arguments.length > 1){ Utils.merge(this.options, options); }; this.model = element; this.stage = stage; }; Player.prototype.render = function(){ var range = DataBase.getRange(); var model = this.model.append("div") .style("height", 27) .style("width", 500) .style("background-color", "#fff"); var thisObj = this; model.append("button") .attr("title", "play") .style("float", "left") .text("\u25ba") .on("click", function(){ console.log("huga"); thisObj.start(); }); var form = model.append("form") .style("height", 30) .style("width", 500); form.append("input") .attr("type", "range") .attr("name", "range") .attr("class", "range") .attr("max", range[1]) .attr("min", range[0]) .attr("step", 1) .attr("value", range[0]) .style("width", 350) .style("float", "left") .on("change", function(){ thisObj.update(this.value); }); form.append("input") .attr("type", "text") .style("width", 30) .style("float", "left") .attr("value", range[0]) .attr("class", "input_label"); form.append("div") .style("color", "#fff") .append("p").style("line-height", 25) .text(range[1]); this.form = form; }; Player.prototype.start = function(){ var target_player = this; var timer = window.setInterval("timer_func()", 400); window["timer_func"] = function(){ var val, step, max; target_player .form .select(".range") .each(function(){ var selector = d3.select(this); val = parseInt(this.value); step = parseInt(selector.attr("step")); max = parseInt(selector.attr("max")); }); if(val+step <= max){ console.log("called!"); target_player.form .select(".range") .each(function(selector){ this.value = val + step; }); target_player.update(val + step); }else{ window.clearInterval(timer); } }; }; Player.prototype.update = function(val){ DataBase.seek("", val); this.model.select(".input_label").attr("value", val); this.stage.clear(); this.stage.update(); }; return Player; }); define('components/menu',[ "utils/utils" ], function(Utils){ function Menu(selection, options){ this.options = { filename: "plot" }; if(arguments.length > 1){ Utils.merge(this.options, options); }; this.selection = selection; } Menu.prototype.begin = function(){ var filename = this.options.filename; function removeMenu(){ d3.selectAll(".download_menu").remove(); } function createMenu(pos, canvas){ removeMenu(); var ul = d3.select("body") .append("ul") .on("click", removeMenu); ul.style({ "list-style-type": "none", position: "absolute", left: pos[0] + "px", top: pos[1] + "px", background: "#f3f3f3", border: "1px solid #fff", padding: 10, margin: 0 }).attr("class", "download_menu"); ul.append("li") .append("a") .text("save as png") .attr({ download: filename + ".png", href: canvas.toDataURL("image/png") }); ul.append("li") .append("a") .text("save as jpeg") .attr({ download: filename + ".jpg", href: canvas.toDataURL("image/jpeg") }); ul.selectAll("a") .style({ display: "block", "text-decoration": "none", "text-align": "left", "line-style": "none", "color": "#333", "font-size" : "13px", "line-height" : "17px" }); ul.selectAll("li") .style({ "margin": 0 }); } this.selection.select("canvas") .on("contextmenu", function(){ var pos = d3.mouse(document.body); createMenu(pos, this, filename); d3.event.preventDefault(); }) .on("click", removeMenu); }; return Menu; }); define('utils/range',[],function(){ function Range(arg0, arg1){ if(arguments.length > 1){ this.max = arg0; this.min = arg1; }else{ this.max = arg0[1]; this.min = arg0[0]; } } Range.prototype.divide = function(num){ var arr = new Array(); var interval = Math.ceil((this.max-this.min)/(num-1)); for(var i=0;i 1) { Utils.merge(this.options, options); } ; var selection = d3.select(element); selection.style("width", String(this.options.width)); this.world_space = selection.append("div") .attr("id", "world") .attr("class", "world") .style({ "float": "left", "width": String(this.options.space.width), "height": String(this.options.space.height), "save_image": this.options.save_image }); this.legend_space = selection.append("div") .attr("id", "legend") .attr("class", "legend") .style({ "float": "left", "width": String(this.options.width - this.options.space.width), "height": String(this.options.height) }); if (this.options.player) { var player_space = selection.append("div") .style("width", String(this.options.width)) .style("height", String(this.options.height - this.options.space.height)); this.player = new Player(player_space, this); } if (this.options.save_image) { this.menu = new Menu(this.world_space); } this.charts = []; this.world = new World({ width: this.options.space.width, height: this.options.space.height, bg_color: this.options.bg_color, perspective: this.options.perspective, orbit: this.options.orbit }); this.data_ranges = { x: new Range(this.options.range.x[0], this.options.range.x[1]), y: new Range(this.options.range.y[0], this.options.range.y[1]), z: new Range(this.options.range.z[0], this.options.range.z[1]) }; return this; } Stage.prototype.add = function (chart) { if (this.options.autorange) { var ranges = chart.getDataRanges(); var thisObj = this; ['x', 'y', 'z'].forEach(function (i) { thisObj.data_ranges[i] = Range.expand(thisObj.data_ranges[i], ranges[i]); }); } this.charts.push(chart); }; Stage.prototype.render = function () { this.space = new Space(this.data_ranges, this.options.space); this.world.addMesh(this.space.getMeshes()); for (var i = 0; i < this.charts.length; i++) { var chart = this.charts[i]; chart.generateMesh(this.space.getScales(), this); this.world.addMesh(chart.getMesh()); if (chart.hasLegend()) { var legend = chart.getLegend(); this.legend_space[0][0].appendChild(legend[0][0]); } } if (this.options.player) { this.player.render(); } this.world.begin(this.world_space); if (this.options.save_image) this.menu.begin(); }; Stage.prototype.dispose = function () { this.clear(); this.world.renderer.clear(); }; Stage.prototype.clear = function () { for (var i = 0; i < this.charts.length; i++) { var chart = this.charts[i]; this.world.removeMesh(chart.getMesh()); } }; Stage.prototype.update = function () { for (var i = 0; i < this.charts.length; i++) { var chart = this.charts[i]; chart.generateMesh(this.space.getScales(), this); this.world.addMesh(chart.getMesh()); } }; return Stage; }); define('components/legends',[],function(){ function generateContinuousLegend(range, color){ var scale = d3.scale.linear().domain([range.max, range.min]).range([0,200]); var div = d3.select(document.createElement("div")) .style("padding", "5px") .style("float", "left") .style("width","100") .style("height","auto"); var svg = div.append("svg") .style("height","100%") // fixed for Mozilla Firefox Bug 736431 .style("width", "100px"); var gradient = svg.append("svg:defs") .append("svg:linearGradient") .attr("id", "gradient") .attr("x1", "0%") .attr("x2", "0%") .attr("y1", "100%") .attr("y2", "0%"); for(var i=0; i 1){ Utils.merge(this.options, options); } this.dataset = new Datasets.Matrix(data); this.ranges = this.dataset.ranges; } Surface.prototype.generateMesh = function(scales){ var data = this.dataset.raw; var geometry = new THREE.Geometry(); var color_scale = d3.scale.linear() .domain(this.ranges.z.divide(this.options.fill_colors.length)) .range(this.options.fill_colors); var colors = []; var offset = function(x,y){return x*width+y;}; var fillFace = function(geometry, p1, p2, p3, colors){ var vec0 = new THREE.Vector3(), vec1 = new THREE.Vector3(); vec0.subVectors(geometry.vertices[p1],geometry.vertices[p2]); vec1.subVectors(geometry.vertices[p1],geometry.vertices[p3]); vec1.cross(vec0).normalize(); var color_arr = [colors[p1], colors[p2], colors[p3]]; geometry.faces.push(new THREE.Face3(p1, p2, p3, vec1, color_arr)); color_arr = [colors[p3], colors[p2], colors[p1]]; geometry.faces.push(new THREE.Face3(p3, p2, p1, vec1.negate(), color_arr)); }; var width = data.x.length, height = data.x[0].length; for(var i=0;i 1){ Utils.merge(this.options, options); } this.dataset = new Datasets.Matrix(data); this.ranges = this.dataset.ranges; } Wireframe.prototype.generateMesh = function(scales){ var data = this.dataset.raw; var width = data.x.length, height = data.x[0].length; var material = new THREE.LineBasicMaterial({ color: this.options.color, linewidth: this.options.thickness, transparent: true }); var meshes = []; for(var i=0;i=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this); //# sourceMappingURL=underscore-min.map; define('charts/particles',[ "underscore", "components/legends", "utils/utils", "utils/datasets", "utils/colorbrewer" ],function(_, Legends, Utils, Datasets, colorbrewer){ function Particles(data, options){ this.options = { name: "Particle", color: colorbrewer.Reds[3], size: 0.3, has_legend: true, fill_by: null, fill_by_range: null }; if(arguments.length > 1){ Utils.merge(this.options, options); } this.data = data; } Particles.prototype.generateMesh = function(scales){ var data = new Datasets.Array(this.data).raw; var meshes = []; for(var i=0;i 1){ Utils.merge(this.options, options); } this.data = data; this.dataset = new Datasets.Array(data); this.ranges = this.dataset.ranges; } Line.prototype.generateMesh = function(scales){ var data = new Datasets.Array(this.data).raw; var geometry = new THREE.Geometry(); var range = new Range(data.x.length, 0); var color_scale = d3.scale.linear() .domain(range.divide(this.options.colors.length)) .range(this.options.colors); for(var i=0;i 1){ Utils.merge(this.options, options); } this.dataset = new Datasets.Array(data); this.ranges = this.dataset.ranges; } Scatter.prototype.generateMesh = function(scales){ var shape_funcs = { circle: function(ctx){ ctx.arc(50, 50, 40, 0, Math.PI*2, false); }, rect: function(ctx){ ctx.beginPath(); ctx.moveTo(20,20); ctx.lineTo(80,20); ctx.lineTo(80,80); ctx.lineTo(20,80); ctx.lineTo(20,20); }, cross: function(ctx){ var vertexes = [[35,5],[65,5],[65,35],[95,35],[95,65],[65,65],[65,95],[35,95],[35,65],[5,65],[5,35],[35,35]]; ctx.moveTo(vertexes[11][0],vertexes[11][1]); for(var i=0;i-EPS) )__terminate__\ __terminate__\ __terminate__\ //---------------------------------------------------------__terminate__\ // CONSTANTS__terminate__\ //---------------------------------------------------------__terminate__\ __terminate__\ // 32 48 64 96 128__terminate__\ #define MAX_STEPS 64__terminate__\ __terminate__\ //#define uTMK 20.0__terminate__\ #define TM_MIN 0.05__terminate__\ __terminate__\ __terminate__\ //---------------------------------------------------------__terminate__\ // SHADER VARS__terminate__\ //---------------------------------------------------------__terminate__\ __terminate__\ varying vec2 vUv;__terminate__\ varying vec3 vPos0; // position in world coords__terminate__\ varying vec3 vPos1; // position in object coords__terminate__\ varying vec3 vPos1n; // normalized 0 to 1, for texture lookup__terminate__\ __terminate__\ uniform vec3 uOffset; // TESTDEBUG__terminate__\ __terminate__\ uniform vec3 uCamPos;__terminate__\ __terminate__\ uniform vec3 uColor; // color of volume__terminate__\ uniform sampler2D uTex; // 3D(2D) volume texture__terminate__\ uniform vec3 uTexDim; // dimensions of texture__terminate__\ __terminate__\ uniform float fPerRow;__terminate__\ uniform float fPerColumn;__terminate__\ __terminate__\ uniform float uTMK;__terminate__\ __terminate__\ float gStepSize;__terminate__\ float gStepFactor;__terminate__\ __terminate__\ //---------------------------------------------------------__terminate__\ // PROGRAM__terminate__\ //---------------------------------------------------------__terminate__\ __terminate__\ // TODO: convert world to local volume space__terminate__\ vec3 toLocal(vec3 p) {__terminate__\ return p + vec3(0.5);__terminate__\ }__terminate__\ __terminate__\ vec4 sampleVolTex(vec3 pos) {__terminate__\ pos = pos;__terminate__\ __terminate__\ // note: z is up in 3D tex coords, pos.z is tex.y, pos.y is zSlice__terminate__\ float zSlice = (1.0-pos.y)*(uTexDim.z-1.0); // float value of slice number, slice 0th to 63rd__terminate__\ __terminate__\ float x0 = mod(floor(zSlice), fPerRow)*uTexDim.x +__terminate__\ pos.x*(uTexDim.x-1.0) +__terminate__\ 0.5;__terminate__\ __terminate__\ float y0 = floor(floor(zSlice)/fPerRow)*uTexDim.y +__terminate__\ pos.z*(uTexDim.y-1.0) +__terminate__\ 0.5;__terminate__\ __terminate__\ float width = uTexDim.x*fPerRow;__terminate__\ float height = uTexDim.y*fPerColumn;__terminate__\ __terminate__\ float uni_x0 = min(x0/width, 1.0);__terminate__\ float uni_y0 = min(y0/height, 1.0);__terminate__\ float uni_x1;__terminate__\ float uni_y1;__terminate__\ __terminate__\ if(mod(floor(zSlice)+1.0, fPerRow) == 0.0){__terminate__\ uni_x1 = min((pos.x*(uTexDim.x-1.0) + 0.5)/width, 1.0);__terminate__\ uni_y1 = min((y0 + uTexDim.y)/height, 1.0);__terminate__\ }else{__terminate__\ uni_x1 = min((x0 + uTexDim.x)/width, 1.0);__terminate__\ uni_y1 = uni_y0;__terminate__\ }__terminate__\ __terminate__\ // get (bi)linear interped texture reads at two slices__terminate__\ vec4 z0 = texture2D(uTex, vec2(uni_x0, uni_y0));__terminate__\ vec4 z1 = texture2D(uTex, vec2(uni_x1, uni_y1));__terminate__\ return mix(z0, z1, fract(zSlice));__terminate__\ }__terminate__\ __terminate__\ vec4 raymarchNoLight(vec3 ro, vec3 rd) {__terminate__\ vec3 step = rd*gStepSize;__terminate__\ vec3 pos = ro;__terminate__\ __terminate__\ vec4 col = vec4(0.0);__terminate__\ __terminate__\ for (int i=0; i 1.0 || pos.x < 0.0 ||__terminate__\ pos.y > 1.0 || pos.y < 0.0 ||__terminate__\ pos.z > 1.0 || pos.z < 0.0)__terminate__\ break;__terminate__\ }__terminate__\ __terminate__\ if(col.r > 1.0)col.r = 1.0;__terminate__\ if(col.g > 1.0)col.g = 1.0;__terminate__\ if(col.b > 1.0)col.b = 1.0;__terminate__\ return vec4(col.rgb, 1.0);__terminate__\ }__terminate__\ __terminate__\ __terminate__\ void main() {__terminate__\ // in world coords, just for now__terminate__\ vec3 ro = vPos1n;__terminate__\ vec3 rd = normalize( ro - toLocal(uCamPos) );__terminate__\ //vec3 rd = normalize(ro-uCamPos);__terminate__\ __terminate__\ // step_size = root_three / max_steps ; to get through diagonal __terminate__\ gStepSize = ROOTTHREE / float(MAX_STEPS);__terminate__\ gStepFactor = 32.0 * gStepSize;__terminate__\ __terminate__\ gl_FragColor = raymarchNoLight(ro, rd);__terminate__\ }__terminate__\ ".replace(/__terminate__/g, "\\n");}); define('shaders/vs',[],function(){return "#ifdef GL_ES__terminate__\ precision highp float;__terminate__\ #endif__terminate__\ __terminate__\ varying vec2 vUv;__terminate__\ varying vec3 vPos0;__terminate__\ varying vec3 vPos1;__terminate__\ varying vec3 vPos1n;__terminate__\ varying mat4 vObjMatInv;__terminate__\ __terminate__\ void main()__terminate__\ {__terminate__\ vUv = uv;__terminate__\ __terminate__\ gl_Position = projectionMatrix *__terminate__\ modelViewMatrix *__terminate__\ vec4(position,1.0);__terminate__\ __terminate__\ vPos0 = ( modelMatrix * vec4(position, 1.0) ).xyz;__terminate__\ vPos1 = position;__terminate__\ vPos1n = position+vec3(0.5);__terminate__\ __terminate__\ //vObjMatInv = inverse(modelMatrix);__terminate__\ }__terminate__\ ".replace(/__terminate__/g, "\\n");}); define('charts/volume',[ "underscore", "components/legends", "utils/utils", "utils/range", "utils/datasets", "utils/colorbrewer", "shaders/fs", "shaders/vs" ],function(_, Legends, Utils, Range, Datasets, colorbrewer, fs, vs){ var THREE = window.THREE; function Volume(data, _options){ this.options = { name: "Volume", has_legend: true, width: 100, height: 100, depth: 100, f_per_row: 1, f_per_column: 1, filter: THREE.NearestFilter }; if(arguments.length>1)_.extend(this.options, _options); this.data = new Datasets.Compressed(data); this.ranges = {x: [0,1], y: [0,1], z: [0,1]}; } Volume.prototype.generateMesh = function(scales, stage){ var uniforms = (_.bind(function(){ var voltexDim = new THREE.Vector3(this.options.width, this.options.height, this.options.depth); var texture = (_.bind(function(){ var image = document.createElement("img"); var voltex = new THREE.Texture(image); image.onload = function(){ console.log("Texture loading finished"); voltex.needsUpdate=true; }; image.src = this.data.raw; voltex.minFilter = voltex.magFilter = this.options.filter; voltex.wrapS = voltex.wrapT = THREE.ClampToEdgeWrapping; voltex.flipX = true; voltex.flipY = false; return voltex; }, this))(); var camera = stage.world.camera; return { uCamPos: {type: "v3", value: camera.position}, uColor: {type: "v3", value: new THREE.Vector3(1.0, 1.0, 1.0)}, uTex: {type: "t", value: texture}, uTexDim: {type: "v3", value: voltexDim}, fPerRow: {type: "f", value: this.options.f_per_row}, fPerColumn: {type: "f", value: this.options.f_per_column}, uOffset: {type: "v3", value: new THREE.Vector3()}, uTMK: {type: "f", value: 16.0} }; }, this))(); var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vs, fragmentShader: fs, depthWrite: false }); this.mesh = new THREE.Mesh( new THREE.CubeGeometry(1, 1, 1), material ); this.mesh.scale.set(20, 20, 20); }; Volume.prototype.getDataRanges = function(){ return this.ranges; }; Volume.prototype.hasLegend = function(){ return this.options.has_legend; }; Volume.prototype.disappear = function(){ this.mesh.material.opacity = 0; this.mesh.material.needsUpdate = true; }; Volume.prototype.appear = function(){ this.mesh.material.opacity = 1; }; Volume.prototype.getLegend = function(){ return Legends.generateDiscreteLegend(this.options.name, "#000", this); }; Volume.prototype.getMesh = function(){ return this.mesh; }; return Volume; }); define('charts/cylinder',[ "components/legends", "utils/utils", "utils/datasets", "utils/colorbrewer" ],function(Legends, Utils, Datasets, colorbrewer){ function Cylinder(data, options){ this.options = { name: "Cylinder", color: "#756bb1", size: 0.3, has_legend: true }; if(arguments.length > 1){ Utils.merge(this.options, options); } this.data = data; this.dataset = new Datasets.Array(data); this.ranges = this.dataset.ranges; } Cylinder.prototype.generateMesh = function(scales){ var data = new Datasets.Array(this.data).raw; var geometry = new THREE.Geometry(); for(var i=0;i 1){ Utils.merge(this.options, options); } this.data = data; this.dataset = new Datasets.Array(data); this.ranges = this.dataset.ranges; } Debug_Object.prototype.generateMesh = function(scales){ var data = new Datasets.Array(this.data).raw; var geometry = new THREE.Geometry(); for(var i=0;i