Skip to content

Commit 75953e7

Browse files
committed
Add accomidations for setting/displaying "kinetic" reference lines.
1 parent 0ccc04f commit 75953e7

File tree

1 file changed

+138
-69
lines changed

1 file changed

+138
-69
lines changed

d3_resources/SpectrumChartD3.js

Lines changed: 138 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,6 @@ SpectrumChartD3 = function(elem, options) {
377377
.on("*.zoom", null)
378378
.on(".zoom", null )
379379
;
380-
381-
382380
/// @TODO triggering the cancel events on document.body and window is probably a bit agressive; could probably do this for just this.vis + on leave events
383381
d3.select(document.body)
384382
.on("mouseup", self.handleCancelAllMouseEvents() )
@@ -538,6 +536,13 @@ SpectrumChartD3 = function(elem, options) {
538536

539537
if( this.options.gridy )
540538
this.setGridY(this.options.gridy,true);
539+
540+
this.vis.on("mouseout", function(){
541+
if( self.currentKineticRefLine ){
542+
self.currentKineticRefLine = null;
543+
self.drawRefGammaLines();
544+
}
545+
});
541546
}
542547

543548
registerKeyboardHandler = function(callback) {
@@ -1283,8 +1288,7 @@ SpectrumChartD3.prototype.setTitle = function(title,dontRedraw) {
12831288
.node().getBBox().height;
12841289
this.options.txt.title = title;
12851290
}
1286-
this.handleResize( dontRedraw );
1287-
this.refreshRefGammaLines();
1291+
this.handleResize( dontRedraw );
12881292
}
12891293

12901294
SpectrumChartD3.prototype.hasCompactXAxis = function() {
@@ -1730,14 +1734,15 @@ SpectrumChartD3.prototype.handleChartMouseMove = function() {
17301734

17311735
/* Right Click Dragging: pans the chart left and right */
17321736
self.handlePanChart();
1733-
} else if( self.options.allowDragRoiExtent
1734-
&& self.rawData.spectra && self.rawData.spectra.length > 0
1737+
} else if( self.rawData.spectra && self.rawData.spectra.length > 0
17351738
&& (x >= 0 && y >= 0 && y <= self.size.height && x <= self.size.width
17361739
&& !d3.event.altKey && !d3.event.ctrlKey && !d3.event.metaKey
17371740
&& !d3.event.shiftKey && !self.fittingPeak && !self.escapeKeyPressed ) ) {
17381741
// If we are here, the mouse button is not down, and the user isnt holding control, shift, etc
1739-
1740-
if( self.roiDragBoxes && self.showDragLineWhileInRoi && !self.roiIsBeingDragged ){
1742+
if( self.kineticRefLines )
1743+
self.handleUpdateKineticRefLineUpdate();
1744+
1745+
if( self.options.allowDragRoiExtent && self.roiDragBoxes && self.showDragLineWhileInRoi && !self.roiIsBeingDragged ){
17411746
// If we're here, the user clicked on a peak to show the ROIS drag box/line, but the user
17421747
// hasnt moved mouse out of ROI, or clicked down, or hit esc or anything
17431748
const dx = (self.showDragLineWhileInRoi ? 1 : 0.5) * self.options.roiDragWidth;
@@ -1751,7 +1756,7 @@ SpectrumChartD3.prototype.handleChartMouseMove = function() {
17511756

17521757
if( !within_x )
17531758
self.handleCancelRoiDrag();
1754-
}else if( !self.roiIsBeingDragged ) {
1759+
}else if( self.options.allowDragRoiExtent && !self.roiIsBeingDragged ) {
17551760

17561761
//Also check if we are between ymin and ymax of ROI....
17571762
const drawn_roi = self.getDrawnRoiForCoordinate( m, true );
@@ -1812,15 +1817,17 @@ SpectrumChartD3.prototype.getMousePos = function(){
18121817
}
18131818

18141819
if( this.lastMouseMovePos ){
1815-
console.log( 'getMousePos returning lastMouseMovePos (', lastMouseMovePos, ")" )
1820+
console.log( 'getMousePos returning lastMouseMovePos (', this.lastMouseMovePos, ")" )
18161821
return this.lastMouseMovePos;
18171822
}
18181823

1819-
console.assert( this.lastTapEvent, "Failed to find mouse position!" );
1820-
18211824
if( this.lastTapEvent )
18221825
return this.lastTapEvent.visCoordinates;
18231826

1827+
//We can fail to get mouse position if a mouse/finger hasnt been over the chart yet
1828+
//console.assert( this.lastTapEvent, "Failed to find mouse position!" );
1829+
console.warn( "Failed to find mouse position!" );
1830+
18241831
// I dont think we ever get here..., but I guess we'll just return _something_
18251832
return [0, 0, pad_left, pad_top];
18261833
}//getMousePos(...)
@@ -2226,6 +2233,11 @@ SpectrumChartD3.prototype.handleChartMouseLeave = function() {
22262233
}
22272234

22282235
self.updateFeatureMarkers(-1);
2236+
2237+
if( self.currentKineticRefLine ){
2238+
self.currentKineticRefLine = null;
2239+
self.drawRefGammaLines();
2240+
}
22292241

22302242
self.mousedOverRefLine = null;
22312243
self.refLineInfo.style("display", "none");
@@ -3994,7 +4006,8 @@ SpectrumChartD3.prototype.drawRefGammaLines = function() {
39944006
/*Drawing of the reference lines is super duper un-optimized!!! */
39954007
const self = this;
39964008

3997-
if( !self.refLines || !self.refLines.length || !self.refLines[0].lines || !self.refLines[0].lines.length ) {
4009+
if( (!self.refLines || !self.refLines.length || !self.refLines[0].lines || !self.refLines[0].lines.length)
4010+
&& (!self.currentKineticRefLine || !self.currentKineticRefLine.lines || !self.currentKineticRefLine.lines.length ) ) {
39984011
self.vis.selectAll("g.ref").remove();
39994012
return;
40004013
}
@@ -4006,14 +4019,20 @@ SpectrumChartD3.prototype.drawRefGammaLines = function() {
40064019
return lines.slice(lindex,rindex).filter(function(d){return d.h > 1E-16;});
40074020
}
40084021

4009-
var lowerx = this.xScale.domain()[0], upperx = this.xScale.domain()[1];
4022+
let reflines = [];
4023+
if( self.refLines ){
4024+
self.refLines.forEach( function(input) {
4025+
const lines = getLinesInRange(self.xScale.domain(),input.lines);
4026+
input.maxVisibleAmp = d3.max(lines, function(d){return d.h;}); /*same as lines[0].parent.maxVisibleAmp = ... */
4027+
reflines = reflines.concat( lines );
4028+
});
4029+
}
40104030

4011-
var reflines = [];
4012-
self.refLines.forEach( function(input) {
4013-
var lines = getLinesInRange(self.xScale.domain(),input.lines);
4014-
input.maxVisibleAmp = d3.max(lines, function(d){return d.h;}); /*same as lines[0].parent.maxVisibleAmp = ... */
4031+
if( self.currentKineticRefLine ){
4032+
const lines = getLinesInRange(self.xScale.domain(),self.currentKineticRefLine.lines);
4033+
self.currentKineticRefLine.maxVisibleAmp = d3.max(lines, function(d){return d.h;});
40154034
reflines = reflines.concat( lines );
4016-
});
4035+
}
40174036

40184037
reflines.sort( function(l,r){ return ((l.e < r.e) ? -1 : (l.e===r.e ? 0 : 1)); } );
40194038

@@ -4023,10 +4042,6 @@ SpectrumChartD3.prototype.drawRefGammaLines = function() {
40234042
.attr("transform", tx)
40244043
.attr("stroke-width", self.options.refLineWidth );
40254044

4026-
var gye = gy.enter().insert("g", "a")
4027-
.attr("class", "ref")
4028-
.attr("transform", tx);
4029-
40304045
function stroke(d){ return d.color ? d.color : d.parent.color; };
40314046

40324047
function dashfunc(d){
@@ -4037,21 +4052,23 @@ SpectrumChartD3.prototype.drawRefGammaLines = function() {
40374052
return (index > -1) ? dash[index] : null;
40384053
};
40394054

4040-
var h = self.size.height;
4041-
var m = Math.min(h,self.options.refLineTopPad); // leave 20px margin at top of chart
4055+
const h = self.size.height;
4056+
const m = Math.min(h,self.options.refLineTopPad); // leave 20px margin at top of chart
40424057

4043-
gye.append("line")
4058+
gy.enter().insert("g", "a")
4059+
.attr("class", "ref")
4060+
.attr("transform", tx)
4061+
.append("line")
40444062
.style("stroke-dasharray", dashfunc )
40454063
.attr("stroke", stroke )
40464064
.attr("y1", h )
4047-
.attr("dx", "-0.5" )
4048-
;
4065+
.attr("dx", "-0.5" );
40494066

40504067
gy.exit().remove(); // Remove old elements as needed.
40514068

4052-
/* Now update the height of all the lines. If we did this in the gye.append("line") */
4053-
/* line above then the values for existing lines wouldnt be updated (only */
4054-
/* the new lines would have correct height) */
4069+
/* Now update the height of all the lines. If we did this in the gy.enter().append("line")
4070+
line above then the values for existing lines wouldnt be updated (only
4071+
the new lines would have correct height) */
40554072
const y2Lin = function(d){ return Math.min(h - (h-m)*d.h/d.parent.maxVisibleAmp,h-2); };
40564073

40574074
/*
@@ -4071,19 +4088,6 @@ SpectrumChartD3.prototype.drawRefGammaLines = function() {
40714088
.attr("y1", h ); //needed for initial load sometimes
40724089
}
40734090

4074-
SpectrumChartD3.prototype.refreshRefGammaLines = function() {
4075-
var self = this;
4076-
4077-
// No need to clear, we don't have any data
4078-
if (!self.refLines)
4079-
return;
4080-
4081-
// Erase all the reference lines
4082-
self.vis.selectAll("g.ref").remove();
4083-
4084-
// Redraw the reference gamma lines
4085-
self.drawRefGammaLines();
4086-
}
40874091

40884092
SpectrumChartD3.prototype.clearReferenceLines = function() {
40894093
var self = this;
@@ -4102,38 +4106,38 @@ SpectrumChartD3.prototype.setReferenceLines = function( data ) {
41024106

41034107
var default_colors = ["#0000FF","#006600", "#006666", "#0099FF","#9933FF", "#FF66FF", "#CC3333", "#FF6633","#FFFF99", "#CCFFCC", "#0000CC", "#666666", "#003333"];
41044108

4105-
var index = 0;
41064109
if( !data ){
41074110
this.refLines = null;
41084111
} else {
41094112
try {
41104113
if( !Array.isArray(data) )
41114114
throw "Input is not an array of reference lines";
41124115

4113-
data.forEach( function(a,i){
4114-
if( !a.color )
4115-
a.color = default_colors[i%default_colors.length];
4116-
if( !a.lines || !Array.isArray(a.lines) )
4117-
throw "Reference lines does not contain an array of lines";
4118-
a.lines.forEach( function(d){
4119-
d.id = ++index; //We need to assign an ID to use as D3 data, that is unique (energy may not be unique)
4120-
4121-
/*{e:30.27,h:6.22e-05,particle:'xray',decay:'xray',el:'barium'} */
4122-
/*particle in ["gamma", "xray", "beta", "alpha", "positron", "electronCapture"]; */
4123-
if( (typeof d.e !== "number") || (typeof d.h !== "number") || (typeof d.particle !== "string") )
4124-
throw "Reference line is invalid (" + JSON.stringify(d) + ")";
4125-
});
4126-
});
4127-
4128-
/*this.refLines = JSON.parse(JSON.stringify(data)); /*creates deep copy, but then also have to go through and */
4129-
this.refLines = data;
4130-
this.refLines.forEach( function(a,i){ a.lines.forEach( function(d){ d.parent = a; } ) } );
4131-
}catch(e){
4132-
this.refLines = null;
4133-
console.log( "invalid input to setReferenceLines" );
4134-
}
4135-
this.refLines = data;
4136-
}
4116+
/*this.refLines = JSON.parse(JSON.stringify(data)); //creates deep copy, but then also have to go through and */
4117+
//
4118+
this.refLines = data;
4119+
let index = 0;
4120+
this.refLines.forEach( function(a,i){
4121+
if( !a.color )
4122+
a.color = default_colors[i%default_colors.length];
4123+
if( !a.lines || !Array.isArray(a.lines) )
4124+
throw "Reference lines does not contain an array of lines";
4125+
4126+
a.lines.forEach( function(d){
4127+
d.parent = a;
4128+
d.id = ++index; //We need to assign an ID to use as D3 data, that is unique (energy may not be unique)
4129+
4130+
/*{e:30.27,h:6.22e-05,particle:'xray',decay:'xray',el:'barium'} */
4131+
/*particle in ["gamma", "xray", "beta", "alpha", "positron", "electronCapture"]; */
4132+
if( (typeof d.e !== "number") || (typeof d.h !== "number") || (typeof d.particle !== "string") )
4133+
throw "Reference line is invalid (" + JSON.stringify(d) + ")";
4134+
} );
4135+
} );
4136+
}catch(e){
4137+
this.refLines = null;
4138+
console.log( "invalid input to setReferenceLines" );
4139+
}
4140+
}//if( !data ) / else
41374141

41384142
this.redraw()();
41394143
}
@@ -4145,6 +4149,71 @@ SpectrumChartD3.prototype.setShowRefLineInfoForMouseOver = function( show ) {
41454149
self.redraw()();
41464150
}
41474151

4152+
SpectrumChartD3.prototype.setKineticReferenceLines = function( data ) {
4153+
// data looks like: { fwhm_fcn: function(e){...}, ref_lines: [{weight: 1, src_lines:{color: "red", parent: "Eu152", age: "2.5 HL", lines: [{e: 39.1, h: 0.000135, particle: "xray", desc_ind: 0, parent: Object},....], desc_strs: ["Eu152 to Sm152 via Electron Capture",...]},{...}] }
4154+
// TODO: Need to validate format of `data`
4155+
4156+
if( data ){
4157+
let index = 1000000;
4158+
data.ref_lines.forEach( function(a){
4159+
a.src_lines.lines.forEach( function(d){
4160+
d.id = ++index;
4161+
d.parent = a.src_lines;
4162+
} );
4163+
} );
4164+
}//if( data )
4165+
//console.log( "setKineticReferenceLines:", data );
4166+
4167+
this.kineticRefLines = data;
4168+
4169+
this.handleUpdateKineticRefLineUpdate();
4170+
}//SpectrumChartD3.prototype.setKineticReferenceLines
4171+
4172+
4173+
SpectrumChartD3.prototype.handleUpdateKineticRefLineUpdate = function(){
4174+
const m = this.getMousePos(); // Get current mouse position for energy calculation
4175+
if( !this.kineticRefLines || !this.kineticRefLines.ref_lines || !this.kineticRefLines.ref_lines.length || ((m[0] == 0) && (m[1] == 0)) ){
4176+
if( this.currentKineticRefLine ){
4177+
this.currentKineticRefLine = null;
4178+
this.drawRefGammaLines();
4179+
}
4180+
return;
4181+
}//if( we dont need to draw the lines )
4182+
4183+
const energy = this.xScale.invert(m[0]);
4184+
const peak_sigma = (this.kineticRefLines.fwhm_fcn ? this.kineticRefLines.fwhm_fcn(energy) : 2.35482) / 2.35482;
4185+
4186+
// Find the line with the lowest weight across all reference line groups
4187+
let minWeight = Number.MAX_VALUE;
4188+
let bestRefLine = null;
4189+
4190+
for( const refLineGroup of this.kineticRefLines.ref_lines ) {
4191+
if( !refLineGroup.src_lines || !refLineGroup.src_lines.lines || !refLineGroup.src_lines.lines.length ) continue;
4192+
4193+
const src_weight = refLineGroup.weight || 1.0;
4194+
for( const line of refLineGroup.src_lines.lines ) {
4195+
if( line.h <= 0.0 ) continue;
4196+
const delta_energy = Math.abs(energy - line.e);
4197+
const weight = ((0.25 * peak_sigma + delta_energy) / line.h) / src_weight;
4198+
if( (weight < minWeight) && (delta_energy < 5*peak_sigma) ) {
4199+
minWeight = weight;
4200+
bestRefLine = refLineGroup.src_lines;
4201+
}
4202+
}
4203+
}
4204+
4205+
if( bestRefLine && this.refLines && this.refLines.some( input => bestRefLine.parent == input.parent ) ){
4206+
bestRefLine = null;
4207+
}
4208+
4209+
// If the this.kineticRefLines.ref_lines[i].src_lines that contains the lowest weight is
4210+
// not equal to this.currentKineticRefLine, then set this.currentKineticRefLine, and call this.drawRefGammaLines()
4211+
if( bestRefLine !== this.currentKineticRefLine ) {
4212+
this.currentKineticRefLine = bestRefLine;
4213+
this.drawRefGammaLines();
4214+
}
4215+
4216+
}//SpectrumChartD3.prototype.handleUpdateKineticRefLineUpdate
41484217

41494218
/**
41504219
* -------------- Grid Lines Functions --------------

0 commit comments

Comments
 (0)