Skip to content

Commit

Permalink
Merge pull request #146 from antgonza/screenshot
Browse files Browse the repository at this point in the history
Screenshot
  • Loading branch information
ElDeveloper committed Jul 19, 2013
2 parents f0c11a0 + 524253d commit 41b6d68
Show file tree
Hide file tree
Showing 9 changed files with 936 additions and 41 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ 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 `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

*Bug Fixes*

Expand Down
55 changes: 54 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,57 @@ 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.
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.

--------------------------------------------------------------------------------

### FileSaver.js [283e78fd3c](https://github.com/eligrey/FileSaver.js/)

Copyright (c) 2011 [Eli Grey](http://eligrey.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.
24 changes: 19 additions & 5 deletions emperor/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class EmperorLogicError(ValueError):
pass

def format_pcoa_to_js(header, coords, eigvals, pct_var, custom_axes=[],
coords_low=None, coords_high=None, number_of_axes=10):
coords_low=None, coords_high=None, number_of_axes=10,
number_of_segments=8):
"""Write the javascript necessary to represent a pcoa file in emperor
Inputs:
Expand All @@ -41,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
Expand Down Expand Up @@ -98,8 +101,9 @@ def format_pcoa_to_js(header, coords, eigvals, pct_var, custom_axes=[],
"'color': 0, 'width': %f, 'height': %f, 'length': %f , 'x': %f,"
" 'y': %f, 'z': %f, %s }\n" % (s_header, s_header,delta[0], delta[1],
delta[2], s_coord[0], s_coord[1], s_coord[2], all_coords))

js_pcoa_string += 'var g_segments = 16, g_rings = 16, g_radius = %f;\n' % (radius)

js_pcoa_string += 'var g_segments = %d, g_rings = %d, g_radius = %f;\n' % (number_of_segments,
number_of_segments, radius)
js_pcoa_string += 'var g_xAxisLength = %f;\n' % (abs(max_x)+abs(min_x))
js_pcoa_string += 'var g_yAxisLength = %f;\n' % (abs(max_y)+abs(min_y))
js_pcoa_string += 'var g_zAxisLength = %f;\n' % (abs(max_z)+abs(min_z))
Expand Down Expand Up @@ -409,7 +413,11 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,
<script src="emperor_required_resources/js/js/Detector.js"></script>
<script src="emperor_required_resources/js/js/RequestAnimationFrame.js"></script>
<script src="emperor_required_resources/emperor/js/emperor.js"></script>
<script type="text/javascript" src="emperor_required_resources/js/THREEx.screenshot.js"></script>
<script type="text/javascript" src="emperor_required_resources/js/FileSaver.min.js"></script>
<script type="text/javascript">
"""

_ELLIPSE_OPACITY_SLIDER = """
Expand Down Expand Up @@ -558,7 +566,7 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,
<table>
<tr><td><div id="axeslabelscolor" class="colorbox" name="axeslabelscolor"></div></td><td title="Axes Labels Color">Axes Labels Color</td></tr>
<tr><td><div id="axescolor" class="colorbox" name="axescolor"></div></td><td title="Axes Color Title">Axes Color</td></tr>
<tr><td><div id="rendererbackgroundcolor"class="colorbox" name="rendererbackgroundcolor"></div></td><td title="Background Color Title">Background Color</td></tr>
<tr><td><div id="rendererbackgroundcolor" class="colorbox" name="rendererbackgroundcolor"></div></td><td title="Background Color Title">Background Color</td></tr>
</table>
<br>
<form name="settingsoptionscolor">
Expand All @@ -582,9 +590,15 @@ def format_emperor_html_footer_string(has_biplots=False, has_ellipses=False,
</div>
<div id="settings">
<br>
<input id="saveas" class="button" type="submit" value="Save as SVG" style="" onClick="saveSVG()">
Filename <small>(only letters, numbers, ., - and _)</small>:
<br>
<input name="saveas_name" id="saveas_name" value="screenshot" type="text"/>
<input id="saveas" class="button" type="submit" value="Save as SVG" style="" onClick="saveSVG()"/>
<br>
Create labels? <input id="saveas_legends" class="checkbox" type="checkbox" style="">
<br><br>
For a PNG, simply press 'ctrl+p'.
<br><br>
<div id="pcoaoptions" class="">
<form name="settingsoptions">
<input type="checkbox" onchange="toggleScaleCoordinates(this)" id="scale_checkbox" name="scale_checkbox">Scale coords by percent explained</input>
Expand Down
125 changes: 106 additions & 19 deletions emperor/support_files/emperor/js/emperor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -356,7 +367,7 @@ function colorByMenuChanged() {
colors = getColorList(vals);

// build the colorby table in HTML
var lines = "<table>";
var lines = "<table id='colorbylist_table'>";
for(var i in vals){
// each field is identified by the value it has in the deduplicated
// list of values and by the number of the column in the mapping file
Expand Down Expand Up @@ -1457,20 +1468,77 @@ function drawEdges(){
}
}

function saveSVG(){
open("data:image/svg+xml," + encodeURIComponent(document.getElementById('main_plot').innerHTML));
}

function SVGSaved(response){
var fileName = eval('('+response+')')
var dlwin = open('','Download SVG', 'width=400,height=50')

dlwin.document.open();
dlwin.document.write('<HTML><HEAD><meta name="content-disposition" content="inline; filename=\''+fileName+'\'">');
dlwin.document.write('</HEAD><BODY>');
dlwin.document.write('<a href=\''+fileName+'\'>'+fileName+'</a>')
dlwin.document.write('</BODY></HTML>');
dlwin.document.close();
/*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){
// 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');

var width = document.getElementById('pcoaPlotWrapper').offsetWidth;
var height = document.getElementById('pcoaPlotWrapper').offsetHeight;

var color = $("#rendererbackgroundcolor").spectrum("get").toHexString(true);
var rendererBackgroundColor = new THREE.Color();
rendererBackgroundColor.setHex(color.replace('#','0x'));

var svgRenderer = new THREE.SVGRenderer({ antialias: true, preserveDrawingBuffer: true });
// this is the proper way to set the color of the background but it doesn't work
svgRenderer.setClearColor(rendererBackgroundColor, 1);
svgRenderer.setSize(width, height);
svgRenderer.render(g_mainScene, g_sceneCamera);
svgRenderer.sortObjects = true;

// converting svgRenderer to string: http://stackoverflow.com/questions/17398134/three-svgrenderer-save-text-of-image
var XMLS = new XMLSerializer();
var svgfile = XMLS.serializeToString(svgRenderer.domElement);

// hacking the color to the svg
var index = svgfile.indexOf('viewBox="')+9;
var viewBox = svgfile.substring(index, svgfile.indexOf('"',index))
viewBox = viewBox.split(" ");
var background = '<rect id="background" height="' + viewBox[3] + '" width="' + viewBox[2] + '" y="' +
viewBox[1] + '" x="' + viewBox[0] + '" stroke-width="0" stroke="#000000" fill="' + color + '"/>'
index = svgfile.indexOf('>',index)+1;
svgfile = svgfile.substr(0, index) + background + svgfile.substr(index);

// 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"}),
$('#saveas_name').val() + ".svg");

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

// adding rectangle
labels_text += '<rect height="27" width="27" y="' + pos_y +
'" x="1" stroke-width="1" ' + 'stroke="#FFFFFF" fill="' +
$("#" + $(this).attr('id')).spectrum("get").toHexString(true) + '"/>';
// adding text
labels_text += '<text xml:space="preserve" y="' + (pos_y+20) + '" x="35" ' +
'font-family="Monospace" font-size="' + font_size + '" stroke-width="0" ' +
'stroke="#000000" fill="#000000">' + $(this).attr('name') + '</text>';
pos_y += increment;
});
labels_text = '<svg width="' + ((font_size*max_len) + 10) + '" height="' +
(pos_y-10) + '" xmlns="http://www.w3.org/2000/svg"><g>' + labels_text +
'</g></svg>';

saveAs(new Blob([labels_text], {type: "text/plain;charset=utf-8"}),
$('#saveas_name').val() + "_labels.svg");
}

$('body').css('cursor','default');
}

/*Utility function to draw two vertices lines at a time
Expand Down Expand Up @@ -1626,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{
Expand All @@ -1641,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();
}
}
Expand Down Expand Up @@ -1677,6 +1753,7 @@ function setParallelPlots() {
$(document).ready(function() {
setJqueryUi()


// Detecting that webgl is activated
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

Expand All @@ -1693,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();
Expand Down Expand Up @@ -1785,10 +1869,13 @@ $(document).ready(function() {
rendererBackgroundColor.setHex("0x000000");

// renderer, the default background color is black
g_mainRenderer = new THREE.WebGLRenderer({ antialias: true });
g_mainRenderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });

// 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 );
// g_mainRenderer.setSize( document.getElementById('vizualizations').offsetWidth , document.getElementById('vizualizations').offsetHeight );
g_mainRenderer.sortObjects = true;
main_plot.append(g_mainRenderer.domElement);

Expand Down
2 changes: 2 additions & 0 deletions emperor/support_files/js/FileSaver.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 41b6d68

Please sign in to comment.