/** maximum number of peaks and envelopes points to be renderedon to graph per each bin(range) */
const circlesPerRange = 200;
const peaksPerRange = 200;
/**
* @function SpectrumGraph
* @description Function draws the graph, binds zoom and drag function to the graph
* @param {String} svgId - SVG id on which the graph needed to be drawn
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
* @param {Array} peakData - contains peakList and envelope list
* @param {Array} ionData - Contains data with mass and ACID name to plot on the graph
*/
SpectrumGraph = function(svgId,spectrumParameters,peakData, ionData){
this.svg = d3.select("body").select(svgId);
this.id = svgId;
this.para = spectrumParameters;
this.data = peakData;
this.ionData = ionData;
let graph = this;
let tempid = svgId.split("#")[1];
this.redraw = function(mono_mz,graphFeatures){
this.para = compSpectrumParameters(this.data.peak_list, this.data.envelope_list, mono_mz);
this.para.graphFeatures = graphFeatures;
spectrumParameters = drawSpectrum(this.id, this.para, this.data,this.ionData);
//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
if (typeof correspondingSpecParams_g !== "undefined") {
correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
}
}
this.zoomed = function () {
let transform = d3.event.transform;
let distance = transform.x - spectrumParameters.specX;
let ratio = transform.k / spectrumParameters.specScale;
spectrumParameters.specX = transform.x;
spectrumParameters.specScale = transform.k;
let mousePos = d3.mouse(this);
if(ratio == 1) {
spectrumParameters.drag(distance);
}
else{
spectrumParameters.zoom(mousePos[0], mousePos[1], ratio);
}
spectrumParameters = drawSpectrum(svgId, spectrumParameters, peakData, ionData);
//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
if (typeof correspondingSpecParams_g !== "undefined") {
correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
}
}
this.zoom = d3.zoom()
.on("zoom", this.zoomed);
this.svg.attr("viewBox", "0 0 "+ spectrumParameters.graphFeatures.svgWidth+" "+ spectrumParameters.graphFeatures.svgHeight)
.attr("width", "100%")
.attr("height", "100%")
.call(this.zoom);
this.svg.call(this.zoom.transform, d3.zoomIdentity);
spectrumParameters = drawSpectrum(svgId, spectrumParameters, peakData, ionData);
//Copying as a new variable than referencing. Referencing will change the properties of parent if child properties are changes
if (typeof correspondingSpecParams_g !== "undefined") {
correspondingSpecParams_g[tempid] = jQuery.extend(true, {}, spectrumParameters);
}
return graph;
}
/**
* @function drawTicks
* @description Function that draws ticks on x-axis and y-axis
* @param{HTMLBaseElement} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
drawTicks = function(svg,spectrumParameters){
// Creating a group under svg node with id 'ticks' under which ticks are drawn
this.addXTicks = svg.append("g").attr("id","ticks");
for(let i=0; i <= spectrumParameters.xTicks ; i++)
{
// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
let tickWidth = spectrumParameters.getTickWidth();
if(tickWidth < 1 && tickWidth != 0)
{
tickWidth = (i*tickWidth + spectrumParameters.minMz) - parseFloat((i*tickWidth + spectrumParameters.minMz)%tickWidth) ;
}
else if(tickWidth != 0)
{
tickWidth = i*tickWidth + spectrumParameters.minMz - (i*tickWidth + spectrumParameters.minMz)%tickWidth ;
}
// get the x position of the tick
x = spectrumParameters.getPeakXPos(tickWidth);
// Below condition helps the ticks to be to the right of the y - axis
if(x >= spectrumParameters.padding.left &&
x <= (spectrumParameters.svgWidth - spectrumParameters.padding.right))
{
this.addXTicks.append("line")
.attr("x1",x)
.attr("y1",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
.attr("x2",x)
.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom + spectrumParameters.ticklength)
.attr("stroke","black")
.attr("stroke-width","1")
}
}
this.addYTicks = svg.append("g").attr("id","ticks")
.attr("class","ticks");
for(let i=0; i <= spectrumParameters.yTicks ; i++)
{
// Get the default tick height and calculate the actual tick height position
let tickHeight = spectrumParameters.getTickHeight();
tickHeight = i*tickHeight * spectrumParameters.dataMaxInte /100;
tickHeight = parseFloat(spectrumParameters.getPeakYPos(tickHeight)) ;
let y = tickHeight;
if(!isNaN(y) && y >= spectrumParameters.padding.head)//y >= spectrumParameters.padding.head helps the ticks to be in the length of Y axis
{
this.addYTicks.append("line")
.attr("x1",spectrumParameters.padding.left)
.attr("y1",y)
.attr("x2",spectrumParameters.padding.left - spectrumParameters.ticklength)
.attr("y2",y)
.attr("stroke","black")
.attr("stroke-width","1")
}
}
}
/**
* @function drawAxis
* @description Function to draw x-axis and y-axis
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
drawAxis = function(svg,spectrumParameters){
//Draw x-axis
this.xAxis = svg.append("g").attr("id", "xaxis").append("line")
.attr("x1",spectrumParameters.padding.left)
.attr("y1",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
.attr("x2",spectrumParameters.specWidth+spectrumParameters.padding.left)
.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
.attr("stroke","black")
.attr("stroke-width","2")
// Draw y-axis
this.yAxis = svg.append("g").attr("id", "yaxis").append("line")
.attr("x1",spectrumParameters.padding.left)
.attr("y1",spectrumParameters.padding.head)
.attr("x2",spectrumParameters.padding.left)
.attr("y2",spectrumParameters.svgHeight -spectrumParameters.padding.bottom)
.attr("stroke","black")
.attr("stroke-width","2")
}
/**
* @function addDatatoAxis
* @description Function to add tick numbers on x and y axis
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
addDatatoAxis = function(svg,spectrumParameters){
let maxMz = spectrumParameters.maxMz;
let minMz = spectrumParameters.minMz ;
// Creating a group wih id 'axisPoints' under which the code to add tick numbers is added
this.xAxisData = svg.append("g")
.attr("id", "xAxisPoints");
for(let i = 0 ; i <=spectrumParameters.xTicks ; i++)
{
// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
let tickWidth = spectrumParameters.getTickWidth();
if(tickWidth < 1 && tickWidth != 0)
{
tickWidth = i*tickWidth + spectrumParameters.minMz - parseFloat((i*tickWidth + spectrumParameters.minMz)%tickWidth) ;
}
else if(tickWidth != 0)
{
tickWidth = i*tickWidth + spectrumParameters.minMz - (i*tickWidth + spectrumParameters.minMz)%tickWidth ;
}
x = spectrumParameters.getPeakXPos(tickWidth);
let l_tickWidth = tickWidth;
if(x >= spectrumParameters.padding.left &&
x <= (spectrumParameters.svgWidth - spectrumParameters.padding.right))
{
this.xAxisData.append("text").attr("id","xtext").attr("x",x)
.attr("y",(spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.padding.bottom + 20))// Dividing with 1.6 to set the position of the numbers under the ticks appropriately
.attr("text-anchor","middle")
.text(function(){
// conditions to show more decimal values as we zoom in further and limit decimals when zoomed back
if(maxMz - minMz <=0.0001) return parseFloat(l_tickWidth).toFixed(6);
else if(maxMz - minMz <=0.001) return parseFloat(l_tickWidth).toFixed(5);
else if(maxMz - minMz <=0.01) return parseFloat(l_tickWidth).toFixed(4);
else if(maxMz - minMz <=0.1) return parseFloat(l_tickWidth).toFixed(3);
else if(maxMz - minMz <=1) return parseFloat(l_tickWidth).toFixed(2);
else if(maxMz - minMz <= 3) return parseFloat(l_tickWidth).toFixed(2)
else if(maxMz - minMz <= 5) return parseFloat(l_tickWidth).toFixed(1)
return parseInt(l_tickWidth)
})
.style("font-size","14px")
}
}
// Creating a group wih id 'axisPoints' under which the code to add tick numbers is added
this.yAxisData = svg.append("g")
.attr("id", "yAxisPoints");
for(let i = 0 ; i <= spectrumParameters.yTicks ; i++)
{
let tickHeight = 0;
// Get the default tick height and calculate the actual tick height position
tickHeight = spectrumParameters.getTickHeight();
let data = i*tickHeight ;
if(data <= 1 && data != 0) data = data.toFixed(1);
tickHeight = i*tickHeight * spectrumParameters.dataMaxInte /100;
tickHeight = parseFloat(spectrumParameters.getPeakYPos(tickHeight)) ;
let y = tickHeight;
if(!isNaN(y) && y >= spectrumParameters.padding.head)
{
this.yAxisData.append("text").attr("class","ytext").attr("x",spectrumParameters.padding.left - spectrumParameters.ticklength)
.attr("y",y)
.attr("text-anchor","end")
.attr("alignment-baseline","middle")
.text(data + "%")
.style("font-size","14px")
}
}
}
/**
* @function addBackGround
* @description Function to add backGround color to the spectrum graph for MS1 spectrum at precursor mz
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
addBackGround = function(svg,spectrumParameters){
let svg_temp = svg.append("g")
.attr("id", "svg_bgColor");
if(!((spectrumParameters.graphFeatures.bgMinMz < spectrumParameters.minMz && spectrumParameters.graphFeatures.bgMaxMz < spectrumParameters.minMz)
|| (spectrumParameters.graphFeatures.bgMinMz > spectrumParameters.maxMz && spectrumParameters.graphFeatures.bgMaxMz > spectrumParameters.maxMz)))
{
let x = spectrumParameters.getPeakXPos(spectrumParameters.graphFeatures.bgMinMz);
let x2 = spectrumParameters.getPeakXPos(spectrumParameters.graphFeatures.bgMaxMz);
if(spectrumParameters.graphFeatures.bgMinMz < spectrumParameters.minMz)
{
x = spectrumParameters.getPeakXPos(spectrumParameters.minMz);
}
if(spectrumParameters.graphFeatures.bgMaxMz > spectrumParameters.maxMz)
{
x2 = spectrumParameters.getPeakXPos(spectrumParameters.maxMz);
}
svg_temp.append("rect")
.attr("x", x)
.attr("y", spectrumParameters.padding.head)
.attr("width", x2-x)
.attr("height", function(){
let y1 = spectrumParameters.svgHeight - spectrumParameters.padding.bottom;
let y2 = spectrumParameters.padding.head;
return y1-y2;
})
.style("fill", spectrumParameters.graphFeatures.bgColor)
.style("fill-opacity", ".4")
.style("stroke-width", "1.5px");
}
}
/**
* @function drawPeaks
* @description Function to draw peak lines on the graph
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
* @param {Array} peakdata - Contians both peak list and envelopelist
*/
drawPeaks = function(svg,spectrumParameters,peakdata){
let peaks = svg.append("g")
.attr("id", "peaks");
var len = peakdata.peak_list.length;
// limits provide current count of number of peaks drawn on graph per bin(range)
// so that we can limit tha peak count to peaksPerRange count
let limits=[0,0,0,0,0,0,0,0];
for(let i =0;i<len;i++)
{
let peak = peakdata.peak_list[i];
let inLimit = false;
for(let j=0;j<(spectrumParameters.ranges.length-1);j++)
{
if(peak.mz > spectrumParameters.ranges[j] && peak.mz < spectrumParameters.ranges[j+1])
{
limits[j] = limits[j]+1;
if(limits[j] <= peaksPerRange) inLimit = true;
break;
}
}
if(peak.mz >= spectrumParameters.minMz && peak.mz <= spectrumParameters.maxMz && inLimit == true)
{
peaks.append("line")
.attr("x1",function(d,i){
return spectrumParameters.getPeakXPos(peak.mz);
})
.attr("y1",function(d,i){
let y = spectrumParameters.getPeakYPos(peak.intensity);
if(y<=spectrumParameters.padding.head) return spectrumParameters.padding.head ;
else return y ;
})
.attr("x2",function(d,i){
return spectrumParameters.getPeakXPos(peak.mz);
})
.attr("y2",spectrumParameters.svgHeight - spectrumParameters.padding.bottom )
.attr("stroke","black")
.attr("stroke-width","2")
.on("mouseover",function(d,i){
onMouseOverPeak(this,peak,spectrumParameters);
})
.on("mouseout",function(d,i){
onPeakMouseOut(this);
});
}
}
}
/**
* @function addCircles
* @description Function to add circles for the envelope data
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
* @param {Array} peakdata - Contians both peak list and envelopelist
*/
addCircles = function(svg,spectrumParameters,peakData){
let circles = svg.append("g").attr("id", "circles");
let minPercentage = 0.0;
let maxIntensity = spectrumParameters.dataMaxInte ;
// limits provide current count of number of peaks drawn on graph per bin(range)
// so that we can limit tha peak count to circlesPerRange count
let limits=[0,0,0,0,0,0,0,0];
peakData.envelope_list.forEach(function(envelope_list){
envelope_list.env_peaks.forEach(function(env_peaks){
//Show only envelopes with minimum of 0.5%
let percentInte = env_peaks.intensity/maxIntensity * 100 ;
let inLimit = false;
for(let i=0;i<(spectrumParameters.ranges.length-1);i++)
{
if(env_peaks.mz > spectrumParameters.ranges[i] && env_peaks.mz < spectrumParameters.ranges[i+1])
{
limits[i] = limits[i]+1;
if(limits[i] <= circlesPerRange) inLimit = true;
break;
}
}
// Condition keeps the circles to the right of the y axis till the end of the x-axis
if(env_peaks.mz > spectrumParameters.minMz && env_peaks.mz <= spectrumParameters.maxMz && percentInte >= minPercentage && inLimit == true)
{
circles.append("circle")
.attr("id","circles")
.attr("cx",function(d,i){
return spectrumParameters.getPeakXPos(env_peaks.mz);
})
.attr("cy",function(d,i){
let cy = spectrumParameters.getPeakYPos(env_peaks.intensity);
if(cy < spectrumParameters.padding.head) return spectrumParameters.padding.head ;
else return cy ;
})
.attr("r",function(d,i){
return spectrumParameters.getCircleSize();
})
.style("fill","white")
.style("opacity", "0.6")
.style("stroke",envelope_list.color)
.style("stroke-width","2")
.on("mouseover",function(d,i){
onMouseOverCircle(this,envelope_list,spectrumParameters);
})
.on("mouseout",function(d,i){
onCircleMouseOut(this);
});
}
})
})
}
/**
* @function drawIons
* @description Function to add IONS at the top of the peaks for each cluster of envelopes
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
* @param {Array} ionData - Contians Ion list to display on the graph
*/
drawIons = function(svg,spectrumParameters,ionData){
let ions = svg.append("g").attr("id", "graph_ions");
// Get the default tick width and calculate the actual tick width position based on the current minMz value on the xaxis
let tickWidth = spectrumParameters.getTickWidth();
ionData.forEach((element)=>{
if(tickWidth <= spectrumParameters.graphFeatures.tickWidthThreshholdval)
{
element.forEach((innerElement)=>{
placeIonOnGraph_innerFunc(innerElement);
})
}else{
let innerElement = element[0];
placeIonOnGraph_innerFunc(innerElement);
}
})
// Inner function to draw ions on the graph at respective position
function placeIonOnGraph_innerFunc(innerElement){
if(innerElement.mz > spectrumParameters.minMz && innerElement.mz <= spectrumParameters.maxMz)
{
ions.append("text")
.attr("id","graph_matched_ions")
.attr("x",(spectrumParameters.getPeakXPos((innerElement.mz))-spectrumParameters.graphFeatures.adjustableIonPosition))
.attr("y",function(d,i){
let y = spectrumParameters.getPeakYPos(innerElement.intensity + (0.1*innerElement.intensity));// Adding 10% to get the Ions on the max Intensity Peak
y = y - spectrumParameters.graphFeatures.fixedHeightOfIonAboveThePeak;
if(y <= spectrumParameters.padding.head) return spectrumParameters.padding.head ;
else return y ;
})
.style("fill","black")
.style("opacity", "0.6")
.style("stroke-width","2")
.text(innerElement.ion);
}
}
}
/**
* @function drawSequence
* @description Draw Sequence on spectrum graph
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
drawSequence = function(svg,spectrumParameters){
let seqSvg = svg.append("g").attr("id", "graph_sequence");
let sequenceData = spectrumParameters.graphFeatures.prefixSequenceData;
let x,y,text;
// Draw | at 0 for prefix mass list
x = spectrumParameters.getPeakXPos((0));
y = spectrumParameters.padding.head-35;
text = "|";
// Add "|" At the start of the prefix sequence
if(0 >= spectrumParameters.minMz && 0 <= spectrumParameters.maxMz)
{
drawAcids_innerFunc(seqSvg,x,y,text);
}
sequenceData.forEach(function(element,index,prefixSequenceData){
if(element.mass >= spectrumParameters.minMz && element.mass <= spectrumParameters.maxMz)
{
let x1 = spectrumParameters.getPeakXPos(0);
if(index != 0) x1 = spectrumParameters.getPeakXPos(prefixSequenceData[index-1].mass);
x2 = spectrumParameters.getPeakXPos((element.mass));
x = (x1+x2)/2;
y = spectrumParameters.padding.head-35;
drawAcids_innerFunc(seqSvg,x,y,element.acid);
drawAcids_innerFunc(seqSvg,x2,y,"|");
}
})
sequenceData = spectrumParameters.graphFeatures.suffixSequeceData;
// Draw | at water mass for suffix mass list
let massOfWater = 18.010564683704;
x = spectrumParameters.getPeakXPos(massOfWater);// Mass of water=18.010564683704
y = spectrumParameters.padding.head-15;
text = "|";
if(massOfWater >= spectrumParameters.minMz && massOfWater <= spectrumParameters.maxMz)
{
drawAcids_innerFunc(seqSvg,x,y,text);
}
sequenceData.forEach(function(element,index,suffixSequeceData){
if(element.mass >= spectrumParameters.minMz && element.mass <= spectrumParameters.maxMz)
{
let x1 = spectrumParameters.getPeakXPos(18.010564683704);// Mass of water=18.010564683704
if(index != 0) x1 = spectrumParameters.getPeakXPos(suffixSequeceData[index-1].mass);
x2 = spectrumParameters.getPeakXPos((element.mass));
x = (x1+x2)/2;
y = spectrumParameters.padding.head-15;
drawAcids_innerFunc(seqSvg,x,y,element.acid);
drawAcids_innerFunc(seqSvg,x2,y,"|");
}
})
function drawAcids_innerFunc(svgId,x,y,text){
svgId.append("text")
.attr("id","")
.attr("x",x)
.attr("y",y)
.style("fill","black")
.style("opacity", "0.6")
.style("stroke-width","2")
.text(text);
}
}
/**
* @function addErrorPlot
* @description Add Error Plot to the MonoMass Spectrum
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
addErrorPlot = function(svg, spectrumParameters){
//Draw x-axis
this.xAxis = svg.append("g").attr("id", "xaxis_errorplot").append("line")
.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot/2 - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot/2 - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("stroke","black")
.style("stroke-dasharray", ("5, 3"))
.attr("stroke-width","1.5")
// Draw y-axis
this.yAxis = svg.append("g").attr("id", "yaxis_errorplot").append("line")
.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("x2",spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("stroke","black")
.attr("stroke-width","1")
}
/**
* @function addErrorBlock
* @description Draw Error plot
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
addErrorBlock = function(svg, spectrumParameters){
let rectBlock = svg.append("g").attr("id", "rect_errorplot");
rectBlock.append("line")
.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("stroke","black")
.attr("stroke-width","1")
rectBlock.append("line")
.attr("x1",spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("x2",spectrumParameters.graphFeatures.specWidth + spectrumParameters.graphFeatures.errorplot_padding.left)
.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("stroke","black")
.attr("stroke-width","1")
rectBlock.append("line")
.attr("x1",spectrumParameters.graphFeatures.svgWidth - spectrumParameters.graphFeatures.errorplot_padding.right)
.attr("y1",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("x2",spectrumParameters.graphFeatures.svgWidth - spectrumParameters.graphFeatures.errorplot_padding.right)
.attr("y2",spectrumParameters.graphFeatures.svgHeight - spectrumParameters.graphFeatures.heightForErrorPlot - spectrumParameters.graphFeatures.errorplot_padding.bottom)
.attr("stroke","black")
.attr("stroke-width","1")
}
/**
* @function drawErrorYticks
* @description Draw Error plot y ticks
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
drawErrorYticks = function(svg, spectrumParameters){
let addYTicks = svg.append("g").attr("id","yErrorTicks")
.attr("class","yErrorTicks");
let tempTick = spectrumParameters.graphFeatures.errorThreshHoldVal/spectrumParameters.errorYticks;
//Draw tick at 0th position
let y = spectrumParameters.getErrorYPos(0);
inner_drawYTicks(y);
inner_addErrorYTickValues(0,y);
// Draw positive ticks above error x axis
for(let i=1;i<=spectrumParameters.errorYticks;i++)
{
y = spectrumParameters.getErrorYPos(i*tempTick);
inner_drawYTicks(y);
inner_addErrorYTickValues(i*tempTick,y);
}
//Draw negative ticks below error x axis
for(let i=1;i<=spectrumParameters.errorYticks;i++)
{
y = spectrumParameters.getErrorYPos(-(i*tempTick));
inner_drawYTicks(y);
inner_addErrorYTickValues(-(i*tempTick),y);
}
function inner_drawYTicks(y){
if(!isNaN(y) && y >= spectrumParameters.padding.head)//y >= spectrumParameters.padding.head helps the ticks to be in the length of Y axis
{
addYTicks.append("line")
.attr("x1",spectrumParameters.padding.left)
.attr("y1",y)
.attr("x2",spectrumParameters.padding.left - spectrumParameters.ticklength)
.attr("y2",y)
.attr("stroke","black")
.attr("stroke-width","1")
}
}
function inner_addErrorYTickValues(data,y){
if(!isNaN(y) && y >= spectrumParameters.padding.head)
{
addYTicks.append("text").attr("class","ytext").attr("x",spectrumParameters.padding.left - spectrumParameters.ticklength)
.attr("y",y)
.attr("text-anchor","end")
.attr("alignment-baseline","middle")
.text(data)
.style("font-size","14px")
}
}
}
/**
* @function drawErrorPoints
* @description Draw Error points on the error graph
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
drawErrorPoints = function(svg, spectrumParameters){
let circles = svg.append("g").attr("id", "error_circles");
spectrumParameters.graphFeatures.errorListData.forEach((element)=>{
if(parseFloat(element.theoretical_mass) > spectrumParameters.minMz && parseFloat(element.theoretical_mass) <= spectrumParameters.maxMz){
circles.append("circle")
.attr("class","error_circles")
.attr("cx",function(d,i){
return spectrumParameters.getPeakXPos(parseFloat(element.theoretical_mass));
})
.attr("cy",function(d,i){
let cy = spectrumParameters.getErrorYPos(parseFloat(element.mass_error));
if(cy < spectrumParameters.padding.head) return spectrumParameters.padding.head ;
else return cy ;
})
.attr("r",function(d,i){
return 3;
})
.style("fill","black")
.style("opacity", "1")
//.style("stroke",envelope_list.color)
.style("stroke-width","2");
}
})
}
/**
* @function addLabels
* @description Function to add labels on x and y axis
* @param {Node} svg - is a html node on which the graph is being ploted
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
addLabels = function(svg, spectrumParameters){
svg.append("text").attr("id","label")
// -5 is added simply as buffer to place m/z on top of error rect plot
.attr("transform","translate(" + (spectrumParameters.svgWidth/2) + "," + (spectrumParameters.svgHeight-spectrumParameters.graphFeatures.padding.bottom +spectrumParameters.graphFeatures.adjustableHeightVal - 5) + ")")
.attr("fill","black")
.attr("font-family","Helvetica Neue,Helvetica,Arial,sans-serif")
.attr("font-size","16px")
.text("m/z");
svg.append("text").attr("id","label")
.attr("transform", "translate("+ spectrumParameters.padding.left/3 +","+(spectrumParameters.svgHeight/2+spectrumParameters.labelAdjustVal)+")rotate(-90)")
.attr("fill","black")
.attr("font-family","Helvetica Neue,Helvetica,Arial,sans-serif")
.attr("font-size","16px")
.text("Intensity");
}
/**
* @function onMouseOverPeak
* @description Function to show the data of Mass and Intensity on mouse over of peaks
* @param {Node} this_element - is a html node. On mouse over generates tooltip based on the current peak
* @param {Object} - Contains mz and intensity value of the current peak
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
onMouseOverPeak = function(this_element,peak,spectrumParameters)
{
let x = spectrumParameters.getPeakXPos(peak.mz);
let y = spectrumParameters.getPeakYPos(peak.intensity);
intensity =" I:"+ parseFloat(peak.intensity).toFixed(3);
mz = "M:"+parseFloat(peak.mz).toFixed(3);
y = y - spectrumParameters.mouseOverPadding.head ;
if(y<=spectrumParameters.mouseOverPadding.head)
{
y = spectrumParameters.mouseOverPadding.head;
}
d3.select(this_element).style("stroke","red")
.style("stroke-width","2");
let tooltipData = mz + "<br>" + intensity ;
/* Rectangle to have flexible on click and on mouse actions */
var div = d3.select("body").append("div")
.attr("id", "MyTextMZIN")
.attr("class", "tooltip")
div.transition().duration(30)
.style("opacity", 2);
div.html(tooltipData).style("left", (d3.event.pageX + 12) + "px")
.style("top", (d3.event.pageY - 28)+ "px")
.style("fill", "black");
}
/**
* @function onMouseOverCircle
* @description Function to show the data of Mass and Intensity on mouse over of circles
* @param {Node} this_element - is a html node. On mouse over generates tooltip based on the current peak
* @param {Array} envelope_list - Contains Envelope List
* @param {object} spectrumParameters - Contains the parameters like height, width etc.,. tht helps to draw the graph
*/
onMouseOverCircle = function(this_element,envelope_list,spectrumParameters)
{
let x = parseFloat(d3.select(this_element).attr("cx"));
let y = parseFloat(d3.select(this_element).attr("cy")) ;
let mass = "Mass:"+envelope_list.mono_mass.toFixed(2);
let charge = "Charge:"+ envelope_list.charge ;
y = y - spectrumParameters.mouseOverPadding.head ;
if(y<=spectrumParameters.mouseOverPadding.head)
{
y = spectrumParameters.mouseOverPadding.head;
}
let tooltipData = mass + "<br>" + charge ;
/* Rectangle to have flexible on click and on mouse actions */
var div = d3.select("body").append("div")
.attr("id", "MyTextMassCharge")
.attr("class", "tooltip")
div.transition().duration(30)
.style("opacity", 2);
div.html(tooltipData).style("left", (d3.event.pageX + 12) + "px")
.style("top", (d3.event.pageY - 28)+ "px")
.style("fill", "black");
}
/**
* @function onPeakMouseOut
* @description Function to reset to the original on mouse out of peaks
* @param {Node} this_element - is a html node. On mouse over generates tooltip based on the current peak
*/
onPeakMouseOut = function(this_element)
{
onMouseOut();
d3.select(this_element).style("stroke","black");
}
/**
* @function onCircleMouseOut
* @description Function to reset to the original on mouse out of peaks
*/
onCircleMouseOut = function(){
onMouseOut();
}
/**
* @function onMouseOut
* @description Function to remove the tooltips on mouseout
*/
onMouseOut = function(){
d3.selectAll("#MyTextMZIN").remove();
d3.selectAll("#MyTextMassCharge").remove();
}
/**
* Function gets invokes whenever zoom or drag is invoked and redraws the graph whenever there is zoom or draw
* This function invokes all the functions that draw the graph
* @param {string} svgId - Contains the Id from html on which the graph should be drawn
* @param {object} spectrumParameters - Contains the parameters to help draw the graph
* @param {list} peakData - Contains the data of Peaks and Envelopes to draws lines and circles on the graph
* @param {list} ionData - Contains Ions to draw upon the peaks
*/
function drawSpectrum(svgId, spectrumParameters, peakData,ionData){
let svg = d3.select("body").select(svgId);
// Removes all the elements under SVG group 'svgGroup' everytime there this function is called
svg.selectAll("#svgGroup").remove();
// Create a group under which all the fucntions of the graph will be appended
svg = svg.append("g").attr("id","svgGroup");
/*call onMouseOut everytime to fix onHover bug adding multiple data when mouseover and zoomed up*/
onMouseOut();
drawTicks(svg, spectrumParameters);
drawAxis(svg,spectrumParameters);
addDatatoAxis(svg,spectrumParameters);
addLabels(svg, spectrumParameters);
// Condition if background color is needed to be added for specific graph
if(spectrumParameters.graphFeatures.isAddbgColor)
{
addBackGround(svg, spectrumParameters);
}
drawPeaks(svg, spectrumParameters, peakData);
if(spectrumParameters.graphFeatures.showCircles && peakData.envelope_list != null)
{
addCircles(svg,spectrumParameters,peakData);
}
if(spectrumParameters.graphFeatures.showIons && ionData != null)
{
drawIons(svg,spectrumParameters,ionData);
}
if(spectrumParameters.graphFeatures.showSequence)
{
drawSequence(svg,spectrumParameters);
}
if(spectrumParameters.graphFeatures.addErrorPlot)
{
addErrorPlot(svg,spectrumParameters);
drawErrorYticks(svg,spectrumParameters);
drawErrorPoints(svg,spectrumParameters);
addErrorBlock(svg,spectrumParameters);
}
return spectrumParameters;
}