From f9bedf6fb3417375e6066327400a9d92f49212e7 Mon Sep 17 00:00:00 2001 From: Antonio Gonzalez Date: Tue, 25 Jun 2013 10:25:58 -0600 Subject: [PATCH 1/6] Working version but want to do some more test to change the resolution of the image and/or add the option for pdfs --- ChangeLog.md | 1 + LICENSE.md | 28 ++++++++++++++++++++- emperor/format.py | 1 + emperor/support_files/emperor/js/emperor.js | 3 ++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 7fec36d3..1452ab4a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -9,6 +9,7 @@ Emperor 0.9.1 (changes since Emperor 0.9.0 go here) * The user can select the number of axes to be considered in the GUI and re-plot using lower axes; this is, for example: PC3 vs PC4 vs PC10. * In missing_custom_axes_values you can reference other column within the mapping file to place the samples without numeric values at different points in the gradient. * Parallel plots functionality. +* To take a screenshot of your current visualization you can press `p`. *Bug Fixes* diff --git a/LICENSE.md b/LICENSE.md index 9ac3990a..17d27f6d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -167,4 +167,30 @@ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------------------- + +### THREEx.screenshot [v1](http://learningthreejs.com/data/THREEx/docs/THREEx.screenshot.html) +[repository](https://github.com/jeromeetienne/threex) + +Copyright (c) 2011 Jerome Etienne, http://jetienne.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/emperor/format.py b/emperor/format.py index 2235ab2e..1ca57f39 100755 --- a/emperor/format.py +++ b/emperor/format.py @@ -409,6 +409,7 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False, + + + + - @@ -589,7 +590,7 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,

- Create labels? + Create labels?

For a PNG, simply press 'p'. diff --git a/emperor/support_files/emperor/js/emperor.js b/emperor/support_files/emperor/js/emperor.js index 2199e325..90a20287 100644 --- a/emperor/support_files/emperor/js/emperor.js +++ b/emperor/support_files/emperor/js/emperor.js @@ -1457,6 +1457,11 @@ function drawEdges(){ } } +/*Save the current view to SVG + + This will take the current webGL renderer, convert it to SVG and then generate + a file to download. Additionally it will create the labels if this option is selected. +*/ function saveSVG(button){ if (g_visiblePoints>=3500) { var res = confirm("With more than 3500 samples the current implementation will " + @@ -1499,7 +1504,7 @@ function saveSVG(button){ saveAs(new Blob([svgfile], {type: "text/plain;charset=utf-8"}), "emperor_screenshot.svg"); - if ($('#saveas_labels').is(':checked')) { + if ($('#saveas_legends').is(':checked')) { var labels_text = '', pos_y = 1, increment = 40, max_len = 0, font_size = 12; $('#colorbylist_table tr div').each(function() { if ($(this).attr('name').length > max_len) max_len = $(this).attr('name').length @@ -1509,10 +1514,9 @@ function saveSVG(button){ '" x="1" stroke-width="1" ' + 'stroke="#FFFFFF" fill="' + $("#" + $(this).attr('id')).spectrum("get").toHexString(true) + '"/>'; // adding text - labels_text += '' + - $(this).attr('name') + ''; + labels_text += '' + $(this).attr('name') + ''; pos_y += increment; }); labels_text = ' 0 ) { - - _svg.removeChild( _svg.childNodes[ 0 ] ); - - } - - }; - - this.render = function ( scene, camera ) { - - if ( camera instanceof THREE.Camera === false ) { - - console.error( 'THREE.SVGRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - if ( this.autoClear === true ) this.clear(); - - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - - calculateLights( _lights ); - - for ( var e = 0, el = _elements.length; e < el; e ++ ) { - - var element = _elements[ e ]; - var material = element.material; - - if ( material === undefined || material.visible === false ) continue; - - _elemBox.makeEmpty(); - - if ( element instanceof THREE.RenderableParticle ) { - - _v1 = element; - _v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf; - - renderParticle( _v1, element, material ); - - } else if ( element instanceof THREE.RenderableLine ) { - - _v1 = element.v1; _v2 = element.v2; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - - _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); - - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { - - renderLine( _v1, _v2, element, material ); - - } - - } else if ( element instanceof THREE.RenderableFace3 ) { - - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; - - if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; - - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); - - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { - - renderFace3( _v1, _v2, _v3, element, material ); - - } - - } else if ( element instanceof THREE.RenderableFace4 ) { - - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4; - - if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; - if ( _v4.positionScreen.z < -1 || _v4.positionScreen.z > 1 ) continue; - - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf; - _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= -_svgHeightHalf; - _v4.positionScreen.x *= _svgWidthHalf; _v4.positionScreen.y *= -_svgHeightHalf; - - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen, - _v4.positionScreen - ] ); - - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { - - renderFace4( _v1, _v2, _v3, _v4, element, material ); - - } - - } - - } - - }; - - function calculateLights( lights ) { - - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); - - for ( var l = 0, ll = lights.length; l < ll; l++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light instanceof THREE.AmbientLight ) { - - _ambientLight.r += lightColor.r; - _ambientLight.g += lightColor.g; - _ambientLight.b += lightColor.b; - - } else if ( light instanceof THREE.DirectionalLight ) { - - _directionalLights.r += lightColor.r; - _directionalLights.g += lightColor.g; - _directionalLights.b += lightColor.b; - - } else if ( light instanceof THREE.PointLight ) { - - _pointLights.r += lightColor.r; - _pointLights.g += lightColor.g; - _pointLights.b += lightColor.b; - - } - - } - - } - - function calculateLight( lights, position, normal, color ) { - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - var lightColor = light.color; - - if ( light instanceof THREE.DirectionalLight ) { - - var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize(); - - var amount = normal.dot( lightPosition ); - - if ( amount <= 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } else if ( light instanceof THREE.PointLight ) { - - var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ); - - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); - - if ( amount <= 0 ) continue; - - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); - - if ( amount == 0 ) continue; - - amount *= light.intensity; - - color.r += lightColor.r * amount; - color.g += lightColor.g * amount; - color.b += lightColor.b * amount; - - } - - } - - } - - function renderParticle( v1, element, material ) { - - /* - _svgNode = getCircleNode( _circleCount++ ); - _svgNode.setAttribute( 'cx', v1.x ); - _svgNode.setAttribute( 'cy', v1.y ); - _svgNode.setAttribute( 'r', element.scale.x * _svgWidthHalf ); - - if ( material instanceof THREE.ParticleCircleMaterial ) { - - _color.r = _ambientLight.r + _directionalLights.r + _pointLights.r; - _color.g = _ambientLight.g + _directionalLights.g + _pointLights.g; - _color.b = _ambientLight.b + _directionalLights.b + _pointLights.b; - - _color.r = material.color.r * _color.r; - _color.g = material.color.g * _color.g; - _color.b = material.color.b * _color.b; - - _color.updateStyleString(); - - _svgNode.setAttribute( 'style', 'fill: ' + _color.__styleString ); - - } - - _svg.appendChild( _svgNode ); - */ - - } - - function renderLine ( v1, v2, element, material ) { - - _svgNode = getLineNode( _lineCount ++ ); - - _svgNode.setAttribute( 'x1', v1.positionScreen.x ); - _svgNode.setAttribute( 'y1', v1.positionScreen.y ); - _svgNode.setAttribute( 'x2', v2.positionScreen.x ); - _svgNode.setAttribute( 'y2', v2.positionScreen.y ); - - if ( material instanceof THREE.LineBasicMaterial ) { - - _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin ); - - _svg.appendChild( _svgNode ); - - } - - } - - function renderFace3( v1, v2, v3, element, material ) { - - _this.info.render.vertices += 3; - _this.info.render.faces ++; - - _svgNode = getPathNode( _pathCount ++ ); - _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' ); - - if ( material instanceof THREE.MeshBasicMaterial ) { - - _color.copy( material.color ); - - if ( material.vertexColors === THREE.FaceColors ) { - - _color.multiply( element.color ); - - } - - } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { - - _diffuseColor.copy( material.color ); - - if ( material.vertexColors === THREE.FaceColors ) { - - _diffuseColor.multiply( element.color ); - - } - - _color.copy( _ambientLight ); - - calculateLight( _lights, element.centroidModel, element.normalModel, _color ); - - _color.multiply( _diffuseColor ).add( material.emissive ); - - } else if ( material instanceof THREE.MeshDepthMaterial ) { - - _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); - _color.setRGB( _w, _w, _w ); - - } else if ( material instanceof THREE.MeshNormalMaterial ) { - - var normal = element.normalModelView; - - _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); - - } - - if ( material.wireframe ) { - - _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); - - } else { - - _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); - - } - - _svg.appendChild( _svgNode ); - - } - - function renderFace4( v1, v2, v3, v4, element, material ) { - - _this.info.render.vertices += 4; - _this.info.render.faces ++; - - _svgNode = getPathNode( _pathCount ++ ); - _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + ' L ' + v4.positionScreen.x + ',' + v4.positionScreen.y + 'z' ); - - if ( material instanceof THREE.MeshBasicMaterial ) { - - _color.copy( material.color ); - - if ( material.vertexColors === THREE.FaceColors ) { - - _color.multiply( element.color ); - - } - - } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { - - _diffuseColor.copy( material.color ); - - if ( material.vertexColors === THREE.FaceColors ) { - - _diffuseColor.multiply( element.color ); - - } - - _color.copy( _ambientLight ); - - calculateLight( _lights, element.centroidModel, element.normalModel, _color ); - - _color.multiply( _diffuseColor ).add( material.emissive ); - - } else if ( material instanceof THREE.MeshDepthMaterial ) { - - _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); - _color.setRGB( _w, _w, _w ); - - } else if ( material instanceof THREE.MeshNormalMaterial ) { - - var normal = element.normalModelView; - - _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); - - } - - if ( material.wireframe ) { - - _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); - - } else { - - _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); - - } - - _svg.appendChild( _svgNode ); - - } - - function getLineNode( id ) { - - if ( _svgLinePool[ id ] == null ) { - - _svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' ); - - if ( _quality == 0 ) { - - _svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed - - } - - return _svgLinePool[ id ]; - - } - - return _svgLinePool[ id ]; - - } - - function getPathNode( id ) { - - if ( _svgPathPool[ id ] == null ) { - - _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); - - if ( _quality == 0 ) { - - _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed - - } - - return _svgPathPool[ id ]; - - } - - return _svgPathPool[ id ]; - - } - - function getCircleNode( id ) { - - if ( _svgCirclePool[id] == null ) { - - _svgCirclePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' ); - - if ( _quality == 0 ) { - - _svgCirclePool[id].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed - - } - - return _svgCirclePool[ id ]; - - } - - return _svgCirclePool[ id ]; - - } - - function pad( str ) { - - while ( str.length < 6 ) str = '0' + str; - return str; - - } - -}; diff --git a/emperor/support_files/js/Three.js b/emperor/support_files/js/Three.js index 19e90289..3d249dd0 100644 --- a/emperor/support_files/js/Three.js +++ b/emperor/support_files/js/Three.js @@ -37110,3 +37110,528 @@ THREE.ColorConverter = { }; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SVGRenderer = function () { + + console.log( 'THREE.SVGRenderer', THREE.REVISION ); + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + _svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), + _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, + + _v1, _v2, _v3, _v4, + + _clipBox = new THREE.Box2(), + _elemBox = new THREE.Box2(), + + _color = new THREE.Color(), + _diffuseColor = new THREE.Color(), + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _w, // z-buffer to w-buffer + _vector3 = new THREE.Vector3(), // Needed for PointLight + + _svgPathPool = [], _svgCirclePool = [], _svgLinePool = [], + _svgNode, _pathCount, _circleCount, _lineCount, + _quality = 1; + + this.domElement = _svg; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + } + + this.setQuality = function( quality ) { + + switch(quality) { + + case "high": _quality = 1; break; + case "low": _quality = 0; break; + + } + + }; + + // WebGLRenderer compatibility + + this.supportsVertexTextures = function () {}; + this.setFaceCulling = function () {}; + + this.setClearColor = function ( color, alpha ) { + + // TODO + + }; + + this.setSize = function( width, height ) { + + _svgWidth = width; _svgHeight = height; + _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; + + _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); + _svg.setAttribute( 'width', _svgWidth ); + _svg.setAttribute( 'height', _svgHeight ); + + _clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); + _clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); + + }; + + this.clear = function () { + + _pathCount = 0; + _circleCount = 0; + _lineCount = 0; + + while ( _svg.childNodes.length > 0 ) { + + _svg.removeChild( _svg.childNodes[ 0 ] ); + + } + + }; + + this.render = function ( scene, camera ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.SVGRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + if ( this.autoClear === true ) this.clear(); + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + + calculateLights( _lights ); + + for ( var e = 0, el = _elements.length; e < el; e ++ ) { + + var element = _elements[ e ]; + var material = element.material; + + if ( material === undefined || material.visible === false ) continue; + + _elemBox.makeEmpty(); + + if ( element instanceof THREE.RenderableParticle ) { + + _v1 = element; + _v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf; + + renderParticle( _v1, element, material ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + + _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace3 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace4 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4; + + if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; + if ( _v4.positionScreen.z < -1 || _v4.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= -_svgHeightHalf; + _v4.positionScreen.x *= _svgWidthHalf; _v4.positionScreen.y *= -_svgHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen, + _v4.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace4( _v1, _v2, _v3, _v4, element, material ); + + } + + } + + } + + }; + + function calculateLights( lights ) { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = lights.length; l < ll; l++ ) { + + var light = lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.r += lightColor.r; + _ambientLight.g += lightColor.g; + _ambientLight.b += lightColor.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + _directionalLights.r += lightColor.r; + _directionalLights.g += lightColor.g; + _directionalLights.b += lightColor.b; + + } else if ( light instanceof THREE.PointLight ) { + + _pointLights.r += lightColor.r; + _pointLights.g += lightColor.g; + _pointLights.b += lightColor.b; + + } + + } + + } + + function calculateLight( lights, position, normal, color ) { + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.DirectionalLight ) { + + var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } else if ( light instanceof THREE.PointLight ) { + + var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ); + + var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } + + } + + } + + function renderParticle( v1, element, material ) { + + /* + _svgNode = getCircleNode( _circleCount++ ); + _svgNode.setAttribute( 'cx', v1.x ); + _svgNode.setAttribute( 'cy', v1.y ); + _svgNode.setAttribute( 'r', element.scale.x * _svgWidthHalf ); + + if ( material instanceof THREE.ParticleCircleMaterial ) { + + _color.r = _ambientLight.r + _directionalLights.r + _pointLights.r; + _color.g = _ambientLight.g + _directionalLights.g + _pointLights.g; + _color.b = _ambientLight.b + _directionalLights.b + _pointLights.b; + + _color.r = material.color.r * _color.r; + _color.g = material.color.g * _color.g; + _color.b = material.color.b * _color.b; + + _color.updateStyleString(); + + _svgNode.setAttribute( 'style', 'fill: ' + _color.__styleString ); + + } + + _svg.appendChild( _svgNode ); + */ + + } + + function renderLine ( v1, v2, element, material ) { + + _svgNode = getLineNode( _lineCount ++ ); + + _svgNode.setAttribute( 'x1', v1.positionScreen.x ); + _svgNode.setAttribute( 'y1', v1.positionScreen.y ); + _svgNode.setAttribute( 'x2', v2.positionScreen.x ); + _svgNode.setAttribute( 'y2', v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin ); + + _svg.appendChild( _svgNode ); + + } + + } + + function renderFace3( v1, v2, v3, element, material ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + _svgNode = getPathNode( _pathCount ++ ); + _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + + _diffuseColor.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + calculateLight( _lights, element.centroidModel, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( material.emissive ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); + _color.setRGB( _w, _w, _w ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + var normal = element.normalModelView; + + _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + } + + if ( material.wireframe ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); + + } else { + + _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); + + } + + _svg.appendChild( _svgNode ); + + } + + function renderFace4( v1, v2, v3, v4, element, material ) { + + _this.info.render.vertices += 4; + _this.info.render.faces ++; + + _svgNode = getPathNode( _pathCount ++ ); + _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + ' L ' + v4.positionScreen.x + ',' + v4.positionScreen.y + 'z' ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + + _diffuseColor.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + calculateLight( _lights, element.centroidModel, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( material.emissive ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); + _color.setRGB( _w, _w, _w ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + var normal = element.normalModelView; + + _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + } + + if ( material.wireframe ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); + + } else { + + _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); + + } + + _svg.appendChild( _svgNode ); + + } + + function getLineNode( id ) { + + if ( _svgLinePool[ id ] == null ) { + + _svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' ); + + if ( _quality == 0 ) { + + _svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgLinePool[ id ]; + + } + + return _svgLinePool[ id ]; + + } + + function getPathNode( id ) { + + if ( _svgPathPool[ id ] == null ) { + + _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); + + if ( _quality == 0 ) { + + _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgPathPool[ id ]; + + } + + return _svgPathPool[ id ]; + + } + + function getCircleNode( id ) { + + if ( _svgCirclePool[id] == null ) { + + _svgCirclePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' ); + + if ( _quality == 0 ) { + + _svgCirclePool[id].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgCirclePool[ id ]; + + } + + return _svgCirclePool[ id ]; + + } + + function pad( str ) { + + while ( str.length < 6 ) str = '0' + str; + return str; + + } + +}; diff --git a/scripts/make_emperor.py b/scripts/make_emperor.py index 155a755e..9a01014e 100755 --- a/scripts/make_emperor.py +++ b/scripts/make_emperor.py @@ -199,7 +199,11 @@ default=None), make_option('-o','--output_dir',type="new_dirpath", help='path to the ' 'output directory that will contain the PCoA plot. [default: %default]', - default='emperor') + default='emperor'), + make_option('--number_of_segments', type=int, help='the number of segments to ' + + 'generate the spheres. The higher the value the more quality and the bigger/slower ' + + 'the plots will be. The value should be between 4 and 14. [default: %default]', + default=8), ] script_info['version'] = __version__ @@ -223,11 +227,16 @@ def main(): verbose_output = opts.verbose number_of_axes = opts.number_of_axes compare_plots = opts.compare_plots + number_of_segments = opts.number_of_segments # verifying that the number of axes requested is greater than 3 if number_of_axes<3: option_parser.error(('You need to plot at least 3 axes.')) - + + # verifying that the number of segments is between the desired range + if number_of_segments<4 or number_of_segments>14: + option_parser.error(('number_of_segments should be between 4 and 14.')) + # append headernames that the script didn't find in the mapping file # according to different criteria to the following variables offending_fields = [] @@ -543,7 +552,8 @@ def main(): try: fp_out.write(format_pcoa_to_js(coords_headers, coords_data, coords_eigenvalues, coords_pct, custom_axes, coords_low, - coords_high, number_of_axes=number_of_axes)) + coords_high, number_of_axes=number_of_axes, + number_of_segments=number_of_segments)) except EmperorLogicError, e: option_parser.error(e.message) diff --git a/tests/test_format.py b/tests/test_format.py index 92d7d262..5625cc4c 100755 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -109,6 +109,14 @@ def test_format_pcoa_to_js(self): self.assertRaises(EmperorLogicError, format_pcoa_to_js, self.pcoa_headers, self.pcoa_coords, self.pcoa_eigen_values, self.pcoa_pct_var_really_low) + + # test segments + out_js_pcoa_string = format_pcoa_to_js(self.pcoa_jk_headers, + self.pcoa_jk_coords, self.pcoa_jk_eigen_values, + self.pcoa_jk_pct_var, coords_low=self.pcoa_jk_coords_low, + coords_high=self.pcoa_jk_coords_high, number_of_segments=14) + self.assertEquals(out_js_pcoa_string, PCOA_JS_SEGMENTS) + def test_format_mapping_file_to_js(self): """Tests correct formatting of the metadata mapping file""" @@ -223,7 +231,7 @@ def test_format_emperor_html_footer_string(self): g_spherePositions['PC.356'] = { 'name': 'PC.356', 'color': 0, 'x': -0.183191, 'y': 34912.621000, 'z': 0.008695, 'P1': -0.183191, 'P2': 34912.621000, 'P3': 0.008695, 'P4': -0.027388, 'P5': -0.052865, 'P6': -0.025058, 'P7': -0.052142, 'P8': 0.038200 }; var g_ellipsesDimensions = new Array(); -var g_segments = 16, g_rings = 16, g_radius = 0.006899; +var g_segments = 8, g_rings = 8, g_radius = 0.006899; var g_xAxisLength = 0.574888; var g_yAxisLength = 34912.787234; var g_zAxisLength = 0.276989; @@ -255,7 +263,7 @@ def test_format_emperor_html_footer_string(self): g_spherePositions['PC.356'] = { 'name': 'PC.356', 'color': 0, 'x': -0.183191, 'y': 34912.621000, 'z': 0.008695, 'P1': -0.183191, 'P2': 34912.621000, 'P3': 0.008695, 'P4': -0.027388, 'P5': -0.052865, 'P6': -0.025058, 'P7': -0.052142, 'P8': 0.038200 }; var g_ellipsesDimensions = new Array(); -var g_segments = 16, g_rings = 16, g_radius = 0.006899; +var g_segments = 8, g_rings = 8, g_radius = 0.006899; var g_xAxisLength = 0.574888; var g_yAxisLength = 34912.787234; var g_zAxisLength = 0.276989; @@ -286,7 +294,38 @@ def test_format_emperor_html_footer_string(self): g_ellipsesDimensions[\'PC.607\'] = { \'name\': \'PC.607\', \'color\': 0, \'width\': 0.100000, \'height\': 2.000000, \'length\': 0.000000 , \'x\': 1.100000, \'y\': 1.100000, \'z\': 1.000000, \'P1\': 1.100000, \'P2\': 1.100000, \'P3\': 1.000000, \'P4\': 0.800000 } g_ellipsesDimensions[\'PC.634\'] = { \'name\': \'PC.634\', \'color\': 0, \'width\': 0.300000, \'height\': 0.600000, \'length\': 4.000000 , \'x\': 0.100000, \'y\': 3.300000, \'z\': 5.500000, \'P1\': 0.100000, \'P2\': 3.300000, \'P3\': 5.500000, \'P4\': 0.100000 } g_ellipsesDimensions[\'PC.635\'] = { \'name\': \'PC.635\', \'color\': 0, \'width\': 0.010780, \'height\': 1.000000, \'length\': 0.023000 , \'x\': 1.000000, \'y\': 2.000000, \'z\': 1.000000, \'P1\': 1.000000, \'P2\': 2.000000, \'P3\': 1.000000, \'P4\': 1.000000 } -var g_segments = 16, g_rings = 16, g_radius = 0.012000; +var g_segments = 8, g_rings = 8, g_radius = 0.012000; +var g_xAxisLength = 1.200000; +var g_yAxisLength = 3.800000; +var g_zAxisLength = 5.600000; +var g_xMaximumValue = 1.100000; +var g_yMaximumValue = 3.300000; +var g_zMaximumValue = 5.500000; +var g_xMinimumValue = 0.100000; +var g_yMinimumValue = 0.500000; +var g_zMinimumValue = 0.100000; +var g_maximum = 5.500000; +var g_pc1Label = "PC1 (44 %)"; +var g_pc2Label = "PC2 (40 %)"; +var g_pc3Label = "PC3 (15 %)"; +var g_number_of_custom_axes = 0; +var g_fractionExplained = [0.440000, 0.400000, 0.150000, 0.010000]; +var g_fractionExplainedRounded = [44, 40, 15, 1]; +""" + +PCOA_JS_SEGMENTS = """ +var g_spherePositions = new Array(); +g_spherePositions[\'PC.355\'] = { \'name\': \'PC.355\', \'color\': 0, \'x\': 0.300000, \'y\': 0.500000, \'z\': 0.100000, \'P1\': 0.300000, \'P2\': 0.500000, \'P3\': 0.100000, \'P4\': 0.300000 }; +g_spherePositions[\'PC.607\'] = { \'name\': \'PC.607\', \'color\': 0, \'x\': 1.100000, \'y\': 1.100000, \'z\': 1.000000, \'P1\': 1.100000, \'P2\': 1.100000, \'P3\': 1.000000, \'P4\': 0.800000 }; +g_spherePositions[\'PC.634\'] = { \'name\': \'PC.634\', \'color\': 0, \'x\': 0.100000, \'y\': 3.300000, \'z\': 5.500000, \'P1\': 0.100000, \'P2\': 3.300000, \'P3\': 5.500000, \'P4\': 0.100000 }; +g_spherePositions[\'PC.635\'] = { \'name\': \'PC.635\', \'color\': 0, \'x\': 1.000000, \'y\': 2.000000, \'z\': 1.000000, \'P1\': 1.000000, \'P2\': 2.000000, \'P3\': 1.000000, \'P4\': 1.000000 }; + +var g_ellipsesDimensions = new Array(); +g_ellipsesDimensions[\'PC.355\'] = { \'name\': \'PC.355\', \'color\': 0, \'width\': 0.400000, \'height\': 0.500000, \'length\': 0.800000 , \'x\': 0.300000, \'y\': 0.500000, \'z\': 0.100000, \'P1\': 0.300000, \'P2\': 0.500000, \'P3\': 0.100000, \'P4\': 0.300000 } +g_ellipsesDimensions[\'PC.607\'] = { \'name\': \'PC.607\', \'color\': 0, \'width\': 0.100000, \'height\': 2.000000, \'length\': 0.000000 , \'x\': 1.100000, \'y\': 1.100000, \'z\': 1.000000, \'P1\': 1.100000, \'P2\': 1.100000, \'P3\': 1.000000, \'P4\': 0.800000 } +g_ellipsesDimensions[\'PC.634\'] = { \'name\': \'PC.634\', \'color\': 0, \'width\': 0.300000, \'height\': 0.600000, \'length\': 4.000000 , \'x\': 0.100000, \'y\': 3.300000, \'z\': 5.500000, \'P1\': 0.100000, \'P2\': 3.300000, \'P3\': 5.500000, \'P4\': 0.100000 } +g_ellipsesDimensions[\'PC.635\'] = { \'name\': \'PC.635\', \'color\': 0, \'width\': 0.010780, \'height\': 1.000000, \'length\': 0.023000 , \'x\': 1.000000, \'y\': 2.000000, \'z\': 1.000000, \'P1\': 1.000000, \'P2\': 2.000000, \'P3\': 1.000000, \'P4\': 1.000000 } +var g_segments = 14, g_rings = 14, g_radius = 0.012000; var g_xAxisLength = 1.200000; var g_yAxisLength = 3.800000; var g_zAxisLength = 5.600000; @@ -508,7 +547,7 @@ def test_format_emperor_html_footer_string(self):

- Create labels? + Create labels?

For a PNG, simply press 'p'. @@ -678,7 +717,7 @@ def test_format_emperor_html_footer_string(self):

- Create labels? + Create labels?

For a PNG, simply press 'p'. @@ -835,7 +874,7 @@ def test_format_emperor_html_footer_string(self):

- Create labels? + Create labels?

For a PNG, simply press 'p'. @@ -995,7 +1034,7 @@ def test_format_emperor_html_footer_string(self):

- Create labels? + Create labels?

For a PNG, simply press 'p'. @@ -1156,7 +1195,7 @@ def test_format_emperor_html_footer_string(self):

- Create labels? + Create labels?

For a PNG, simply press 'p'. From 8ec42c575a525271153783f84a536795d10220cd Mon Sep 17 00:00:00 2001 From: Antonio Gonzalez Date: Wed, 17 Jul 2013 12:54:26 -0600 Subject: [PATCH 5/6] This should solve all the issues reported in this pull request and the ones covered during code review. Additionally this closes #156, closes #154, closes #35, and closes #22. --- ChangeLog.md | 1 + emperor/format.py | 13 +++-- emperor/support_files/emperor/js/emperor.js | 54 +++++++++++++++----- scripts/make_emperor.py | 8 +-- tests/test_format.py | 55 +++++++++++---------- 5 files changed, 85 insertions(+), 46 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 280f123d..5d2b45ed 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,7 @@ Emperor 0.9.1 (changes since Emperor 0.9.0 go here) * To take a screenshot (PNG) of your current visualization you can press `p`. * Export to SVG your visualization. * Emperor now relies on QIIME 1.7.0. +* Added option `--number_of_segments` to control the quality of all spheres *Bug Fixes* diff --git a/emperor/format.py b/emperor/format.py index 3af8ed09..9f6ae260 100755 --- a/emperor/format.py +++ b/emperor/format.py @@ -42,6 +42,8 @@ def format_pcoa_to_js(header, coords, eigvals, pct_var, custom_axes=[], custom_axes: list of category names for the custom axes coords_low: coordinates representing the lower edges of an ellipse coords_high: coordinates representing the highere edges of an ellipse + number_of_axes: number of axes to be returned + number_of_segments: number of segments and rings for each sphere Output: string: javascript representation of the PCoA data inputed, contains a list @@ -588,14 +590,15 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained diff --git a/emperor/support_files/emperor/js/emperor.js b/emperor/support_files/emperor/js/emperor.js index 90a20287..4c84ecb4 100644 --- a/emperor/support_files/emperor/js/emperor.js +++ b/emperor/support_files/emperor/js/emperor.js @@ -44,6 +44,18 @@ var g_visiblePoints = 0; var g_sphereScaler = 1.0; var g_keyBuilt = false; var g_useDiscreteColors = true; +var g_screenshotBind; + +// valid ascii codes for filename +var g_validAsciiCodes = new Array(); +// adding: .-_ +g_validAsciiCodes = g_validAsciiCodes.concat([45,46,95]); +// adding: 0->9 +g_validAsciiCodes = g_validAsciiCodes.concat([48,49,50,51,52,53,54,55,56,57]); +// adding: A->Z +g_validAsciiCodes = g_validAsciiCodes.concat([65,66,67,68,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90]); +// adding: a->z +g_validAsciiCodes = g_validAsciiCodes.concat([97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122]); // taken from the qiime/colors.py module; a total of 29 colors k_QIIME_COLORS = [ @@ -72,7 +84,6 @@ k_QIIME_COLORS = [ "0x808000", // green3 "0x008080"] // teal3 - // Taken from http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); @@ -1458,16 +1469,16 @@ function drawEdges(){ } /*Save the current view to SVG - This will take the current webGL renderer, convert it to SVG and then generate a file to download. Additionally it will create the labels if this option is selected. */ function saveSVG(button){ - if (g_visiblePoints>=3500) { - var res = confirm("With more than 3500 samples the current implementation will " + - "take a long time (~30secs) and with more than 5000 an extremely long time " + - "(~15min) and in some computers the browser will crash. The suggestion is to " + - "use the png implementation. Do you want to continue?"); + // add a name subfix for the filenames + if ((g_segments<=8 && g_visiblePoints>=10000) || (g_segments>8 && g_visiblePoints>=5000)) { + var res = confirm("The number of segments (" + g_segments + ") combined with the number " + + "of samples could take a long time and in some computers the browser will crash. " + + "If this happens we suggest to lower the number of segments or use the png " + + "implementation. Do you want to continue?"); if (res==false) return; } $('body').css('cursor','progress'); @@ -1502,7 +1513,7 @@ function saveSVG(button){ // adding xmlns header to open in the browser svgfile = svgfile.replace('viewBox=', 'xmlns="http://www.w3.org/2000/svg" viewBox=') saveAs(new Blob([svgfile], {type: "text/plain;charset=utf-8"}), - "emperor_screenshot.svg"); + $('#saveas_name').val() + ".svg"); if ($('#saveas_legends').is(':checked')) { var labels_text = '', pos_y = 1, increment = 40, max_len = 0, font_size = 12; @@ -1519,11 +1530,12 @@ function saveSVG(button){ 'stroke="#000000" fill="#000000">' + $(this).attr('name') + ''; pos_y += increment; }); - labels_text = '' + labels_text + ''; + saveAs(new Blob([labels_text], {type: "text/plain;charset=utf-8"}), - "emperor_screenshot_labels.svg"); + $('#saveas_name').val() + "_labels.svg"); } $('body').css('cursor','default'); @@ -1682,6 +1694,9 @@ function togglePlots() { // make all tabs usable $("#menutabs").tabs({disabled: []}); + + // adding ctrl-p + g_screenshotBind = THREEx.Screenshot.bindKey(g_mainRenderer, {charCode: 16}); } // changes for parallel plots else{ @@ -1697,7 +1712,12 @@ function togglePlots() { // make the visibility, scaling, labels and axes tabs un-usable // they have no contextualized meaning in when lookin at parallel plots - $("#menutabs").tabs({disabled: [2,3,4,5]}); + // 0 = Key, 1 = Colors, 2 = Visibility, 3 = Scaling, 4 = Labels, 5 = Axes, 6 = View, 7 = Options + $("#menutabs").tabs({disabled: [2,3,4,5,7]}); + + // removing the ctrl-p + g_screenshotBind.unbind(); + colorByMenuChanged(); } } @@ -1733,6 +1753,7 @@ function setParallelPlots() { $(document).ready(function() { setJqueryUi() + // Detecting that webgl is activated if ( ! Detector.webgl ) Detector.addGetWebGLMessage(); @@ -1749,6 +1770,13 @@ $(document).ready(function() { g_sceneCamera.aspect = winAspect; g_sceneCamera.updateProjectionMatrix(); }); + + // Validating the string for the saveas filename = taken from http://stackoverflow.com/questions/6741175/trim-input-field-value-to-only-alphanumeric-characters-separate-spaces-with-wi + $('#saveas_name').keypress(function(event) { + var code = (event.keyCode ? event.keyCode : event.which); + if (g_validAsciiCodes.indexOf(code)==-1) + event.preventDefault(); + }); init(); animate(); @@ -1843,8 +1871,8 @@ $(document).ready(function() { // renderer, the default background color is black g_mainRenderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true }); - // adding 'p' to print screenshot - THREEx.Screenshot.bindKey(g_mainRenderer); + // adding 'ctrl+p' to print screenshot + g_screenshotBind = THREEx.Screenshot.bindKey(g_mainRenderer, {charCode: 16}); g_mainRenderer.setClearColor(rendererBackgroundColor, 1); g_mainRenderer.setSize( document.getElementById('pcoaPlotWrapper').offsetWidth, document.getElementById('pcoaPlotWrapper').offsetHeight ); diff --git a/scripts/make_emperor.py b/scripts/make_emperor.py index 9a01014e..4ed93edf 100755 --- a/scripts/make_emperor.py +++ b/scripts/make_emperor.py @@ -200,9 +200,11 @@ make_option('-o','--output_dir',type="new_dirpath", help='path to the ' 'output directory that will contain the PCoA plot. [default: %default]', default='emperor'), - make_option('--number_of_segments', type=int, help='the number of segments to ' + - 'generate the spheres. The higher the value the more quality and the bigger/slower ' + - 'the plots will be. The value should be between 4 and 14. [default: %default]', + make_option('--number_of_segments', type="int", help='the number of segments to ' + 'generate any spheres, this includes the samples, the taxa (biplots), and the ' + 'confidence intervals (jackknifing). Higher values will result in better quality but ' + 'can make the plots less responsive, also it will make the resulting SVG images ' + 'bigger. The value should be between 4 and 14. [default: %default]', default=8), ] script_info['version'] = __version__ diff --git a/tests/test_format.py b/tests/test_format.py index 5625cc4c..36db6038 100755 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -545,14 +545,15 @@ def test_format_emperor_html_footer_string(self):

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained @@ -715,14 +716,15 @@ def test_format_emperor_html_footer_string(self):

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained @@ -872,14 +874,15 @@ def test_format_emperor_html_footer_string(self):

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained @@ -1032,14 +1035,15 @@ def test_format_emperor_html_footer_string(self):

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained @@ -1193,14 +1197,15 @@ def test_format_emperor_html_footer_string(self):

- -
- Create labels? + Filename (only letters, numbers, ., - and _):
+ +
+ Create labels? +

For a PNG, simply press 'p'. -
-
+

Scale coords by percent explained From 524253dc2131d77fd5010434d182d19ece5b8106 Mon Sep 17 00:00:00 2001 From: Antonio Gonzalez Date: Thu, 18 Jul 2013 21:31:59 -0600 Subject: [PATCH 6/6] Fixing p -> ctrl+p --- ChangeLog.md | 2 +- emperor/format.py | 2 +- tests/test_format.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 5d2b45ed..ecd816b7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,7 +14,7 @@ Emperor 0.9.1 (changes since Emperor 0.9.0 go here) * The "Colors" tab now has a selector, which allows to use the arrows to move between categories. * Default coloring scheme is discrete. * Add color pickers for the axes and axes labels. -* To take a screenshot (PNG) of your current visualization you can press `p`. +* To take a screenshot (PNG) of your current visualization you can press `ctrl+p`. * Export to SVG your visualization. * Emperor now relies on QIIME 1.7.0. * Added option `--number_of_segments` to control the quality of all spheres diff --git a/emperor/format.py b/emperor/format.py index 9f6ae260..f1205716 100755 --- a/emperor/format.py +++ b/emperor/format.py @@ -597,7 +597,7 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.

diff --git a/tests/test_format.py b/tests/test_format.py index 36db6038..68fe1e12 100755 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -552,7 +552,7 @@ def test_format_emperor_html_footer_string(self):
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.

@@ -723,7 +723,7 @@ def test_format_emperor_html_footer_string(self):
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.

@@ -881,7 +881,7 @@ def test_format_emperor_html_footer_string(self):
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.

@@ -1042,7 +1042,7 @@ def test_format_emperor_html_footer_string(self):
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.

@@ -1204,7 +1204,7 @@ def test_format_emperor_html_footer_string(self):
Create labels?

- For a PNG, simply press 'p'. + For a PNG, simply press 'ctrl+p'.