diff --git a/Modules/QmitkExt/resources/Histogram.html b/Modules/QmitkExt/resources/Histogram.html index 530c718625..09eaf6a574 100644 --- a/Modules/QmitkExt/resources/Histogram.html +++ b/Modules/QmitkExt/resources/Histogram.html @@ -1,19 +1,15 @@ Histogram - - -

- diff --git a/Modules/QmitkExt/resources/Histogram.js b/Modules/QmitkExt/resources/Histogram.js index 9342d84126..2e73055140 100644 --- a/Modules/QmitkExt/resources/Histogram.js +++ b/Modules/QmitkExt/resources/Histogram.js @@ -1,531 +1,539 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ var margin = { top : 10, bottom : 50, left : 45, right : 20, }; var height = histogramData.height - margin.top - margin.bottom; var width = histogramData.width - margin.left - margin.right; var tension = 0.8; var connected = false; var dur = 1000; var binSize = 10; +var min; +var max; /* * Connecting signals from qt side with JavaScript methods. */ if (!connected) { connected = true; histogramData.SignalDataChanged.connect(updateHistogram); histogramData.SignalGraphChanged.connect(updateHistogram); } /* * Predefinition of scales. */ var xScale = d3.scale.linear() .domain([d3.min(histogramData.measurement)-binSize/2,d3.max(histogramData.measurement)+binSize/2]) .range([0,width]); var yScale = d3.scale.linear() .domain([d3.min(histogramData.frequency),d3.max(histogramData.frequency)]) .range([height,margin.top]); /* * Predefinition of axis elements. */ var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom") .tickFormat(d3.format("s")); var yAxis = d3.svg.axis() .scale(yScale) .orient("left") .tickFormat(d3.format("s")); /* * Predefinition of the zoom. */ var zoombie = d3.behavior.zoom().x(xScale).scaleExtent([1, 50]).on("zoom", zoom); /* * Creation of the svg element, which holds the complete histogram. */ var svg = d3.select("body") .append("svg") .attr("class", "svg") .attr("width", width + margin.right + margin.left) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate (" + margin.left + "," + margin.top + ")") .call(zoombie) .on("mousemove", myMouseMove); /* * Appending a rectangle to the svg, to guarantee the possibility * of zooming on the whole histogram. */ svg.append("rect") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("opacity", 0); /* * Appending a second svg to main svg, which holds only the graph. */ var vis = svg.append("svg") .attr("width", width) .attr("height", height); /* * Predefinition of the lines. */ var line = d3.svg.line() .interpolate("linear") .x(function(d,i) { return xScale(histogramData.measurement[i]-binSize/2); }) .y(function(d) { return yScale(d); }); var linenull = d3.svg.line() .interpolate("linear") .x(function(d,i) { return xScale(histogramData.measurement[i]-binSize/2); }) .y(function(d) { return yScale(0); }); updateHistogram(); /* * Method to update the histogram data * and to change the displayed graph. */ function updateHistogram() { calcBinSize(); if (!histogramData.useLineGraph) { barChart(); } else if (histogramData.useLineGraph) { linePlot() } } /* * Calculation of the bin size. */ function calcBinSize() { - var min = d3.min(histogramData.measurement); - var max = d3.max(histogramData.measurement); + min = d3.min(histogramData.measurement); + max = d3.max(histogramData.measurement); binSize = ((max - min) / (histogramData.measurement.length)); } /* * Method to display histogram as a barchart. */ function barChart() { definition(); /* * Change zoom to a fixed y-axis. */ zoombie = d3.behavior.zoom().x(xScale).scaleExtent([1, 50]).on("zoom", zoom); svg.call(zoombie); /* * Element to animate transition from linegraph to barchart. */ vis.selectAll("path.line").remove(); vis.selectAll("circle").remove(); /* * Definition of the bar elements. */ var bar = vis.selectAll("rect.bar").data(histogramData.frequency); /* * Definition how to handle new bar elements. */ bar.enter().append("rect") .attr("class", "bar") .on("mouseover", myMouseOver) .on("mouseout", myMouseOut) .attr("x", function(d,i) { return xScale(histogramData.measurement[i]-binSize/2); }) .attr("y", height) .attr("height", 0) .attr("width", barWidth) /* * Definition how to handle changed bar elements. */ bar.transition() .duration(dur) .attr("x", function(d,i) { return xScale(histogramData.measurement[i]-binSize/2); }) .attr("y", myYPostion) .attr("height", barHeight) .attr("width", barWidth); /* * Definition how to handle bar elements which doesn't exist anymore.' */ bar.exit() .transition() .duration(dur) .attr("y", height) .attr("height", 0) .remove(); /* * Update of axis elements. * First delete old ones, then generate new. */ svg.selectAll("g") .remove(); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); } /* * Method to display histogram as a linegraph. */ function linePlot() { definition(); /* * Change zoom to a zoomable y-axis. */ zoombie = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 50]).on("zoom", zoom); svg.call(zoombie); /* * Elements to animate transitions from barchart to linegraph. * Different transition when an intesity profile is generated. */ if(!histogramData.intensityProfile) { vis.selectAll("rect.bar") .transition() .duration(dur) .attr("height", 0) .remove(); } else { vis.selectAll("rect.bar") .transition() .duration(dur) .attr("y", height) // <-- .attr("height", 0) .remove(); } /* * Creating circle elements, when an intensity profile is generated to show tooltips. * Due performance losses tooltips are not supported for line histograms. */ if(histogramData.intensityProfile) { var circles = vis.selectAll("circle").data(histogramData.frequency); /* * Definition how to handle new circle elements. */ circles.enter() .append("circle") .on("mouseover", myMouseOverLine) .on("mouseout", myMouseOutLine) .attr("cx", function(d,i) { return xScale(histogramData.measurement[i]-binSize/2); }) .attr("cy", function (d) { return yScale(d) }) .attr("r", 5) .attr("opacity", 0) .style("stroke", "red") .style("stroke-width", 1) .style("fill-opacity", 0); /* * Definition how to handle bar elements which doesn't exist anymore. */ circles.exit().remove(); } else { /* * Removing of all circle elements if a line histogram is generated. */ vis.selectAll("circle").remove(); } /* * Creating a new path element. */ var graph = vis.selectAll("path.line") .data([histogramData.frequency]); /* * Definition how to handle a new path element, using predefined lines. */ graph.enter() .append("path") .attr("class", "line") .transition() .duration(dur) .attr("d", line); /* * Definition how to handle change points in an existing path element. */ graph.transition() .duration(dur) .attr("d", line); /* * Update of axis elements. * First delete old ones, then generate new. */ svg.selectAll("g") .remove(); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); } function definition() { /* * Match scale to current data. */ xScale = d3.scale.linear() .domain([d3.min(histogramData.measurement)-binSize/2,d3.max(histogramData.measurement)+binSize/2]) .range([0,width]); yScale = d3.scale.linear() .domain([d3.min(histogramData.frequency),d3.max(histogramData.frequency)]) .range([height,margin.top]); /* * Match axes to current scale */ xAxis = d3.svg.axis() .scale(xScale) .orient("bottom") .tickFormat(d3.format("s")); yAxis = d3.svg.axis() .scale(yScale) .orient("left") .tickFormat(d3.format("s")); } /* * Method to calculate barwidth in px. */ function barWidth(d, i) { var bw; if (i != (histogramData.measurement.length-1)) { bw =(xScale(histogramData.measurement[i + 1]) - xScale(histogramData.measurement[i])) * (histogramData.frequency.length / (histogramData.frequency.length + 1)) - 1; } else { bw =(xScale(histogramData.measurement[i]) - xScale(histogramData.measurement[i - 1])) * (histogramData.frequency.length / (histogramData.frequency.length + 1)) - 1; } /* * Ensure barwidth is not smaller than 1px. */ bw = bw > 1 ? bw : 1; return bw; } /* * Method to calculate barheight in px. * Ensure barheight is not smaller than 1px. */ function barHeight(d) { var bh; bh = height - yScale(d); bh = bh >=2 ? bh : 2; return bh; } /* * Method to calculate dynamical y positions. */ function myYPostion(d) { var myy = yScale(d); myy = (height-myy) > 2 ? myy : (height-2); if (d == 0) { return height; } return myy; } /* * Method to fit parameters of bars/line when zoomed. * Update axes elements. * Resets the view if scale is 1. */ function zoom() { if (zoombie.scale() == 1) { zoombie.translate([0,0]); xScale.domain([d3.min(histogramData.measurement)-binSize/2,d3.max(histogramData.measurement)+binSize/2]); yScale.domain([d3.min(histogramData.frequency),d3.max(histogramData.frequency)]); } if (!histogramData.useLineGraph) { svg.select(".x.axis").call(xAxis); vis.selectAll(".bar") .attr("width", barWidth) .attr("x", function(d, i) { return xScale(histogramData.measurement[i]-binSize/2); }); } else { svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); vis.selectAll("path.line") .attr("transform", "translate(" + zoombie.translate() + ")scale(" + zoombie.scale() + ")") .style("stroke-width", 1 / zoombie.scale()); vis.selectAll("circle") .attr("cx", function(d, i) { return xScale(histogramData.measurement[i]-binSize/2); }) .attr("cy", function(d) { return yScale(d); }); } } /* * Method to show infobox, while mouse is over a bin. */ function myMouseOver() { var myBar = d3.select(this); var reScale = d3.scale.linear() .domain(xScale.range()) .range(xScale.domain()); var y = myBar.data(); var x = reScale(myBar.attr("x")); - myBar.style("fill", "red"); d3.select(".infobox").style("display", "block"); - d3.select(".measurement").text("Greyvalue: " + (Math.round(x)) + " ... " + (Math.round(x+binSize))); + if ((min >= 0) && (max <= 2)) //tooltip for float images + { + d3.select(".measurement").text("Greayvalue: " + (Math.round(x*1000)/1000)); + } + else + { + d3.select(".measurement").text("Greyvalue: " + (Math.round(x*10)/10) + " ... " + (Math.round((x+binSize)*10)/10)); + } d3.select(".frequency").text("Frequency: " + y); } /* * Hide infobox, when mouse not over a bin. */ function myMouseOut() { var myBar = d3.select(this); myBar.style("fill", d3.rgb(0,71,185)); d3.select(".infobox").style("display", "none"); } /* * Show tooltip, while mouse is over a circle in an intesity profile. */ function myMouseOverLine() { var myCircle = d3.select(this) var reScale = d3.scale.linear() .domain(xScale.range()) .range(xScale.domain()); var y = myCircle.data(); var x = reScale(myCircle.attr("cx")); x = x >= 0 ? x : 0; myCircle.attr("opacity", 1); d3.select(".infobox").style("display", "block"); d3.select(".measurement").text("Distance: " + (Math.round(x*100)/100) + " mm"); d3.select(".frequency").text("Intesity: " + y); } /* * Hide infobox, when mouse not over a circle. */ function myMouseOutLine() { var myCircle = d3.select(this); myCircle.attr("opacity", 0); d3.select(".infobox").style("display", "none"); } /* * Update mousecoordinates when mouse is moved. * Tooltip is shown on the right side of the mouse until it reaches the right boundary, * the it switches to the left side. */ function myMouseMove() { var infobox = d3.select(".infobox"); var coords = d3.mouse(this); if ((coords[0]+120)<(width-margin.right)) { infobox.style("left", coords[0] + 75 + "px"); infobox.style("top", coords[1] + "px"); } else { infobox.style("left", coords[0] - 90 + "px"); infobox.style("top",coords[1] + "px"); } }