var dwv = dwv || {};
dwv.test = dwv.test || {};
// Image decoders (for web workers)
dwv.image.decoderScripts = {
jpeg2000: '../../decoders/pdfjs/decode-jpeg2000.js',
'jpeg-lossless': '../../decoders/rii-mango/decode-jpegloss.js',
'jpeg-baseline': '../../decoders/pdfjs/decode-jpegbaseline.js',
rle: '../../decoders/dwv/decode-rle.js'
};
// logger level (optional)
dwv.logger.level = dwv.utils.logger.levels.DEBUG;
var _app = null;
var _mode = 0;
/**
* Setup simple dwv app.
*/
dwv.test.viewerSetup = function () {
// stage options
var dataViewConfigs;
var viewOnFirstLoadItem = true;
// use for concurrent load
var numberOfDataToLoad = 1;
if (_mode === 0) {
// simplest: one layer group
dataViewConfigs = prepareAndGetSimpleDataViewConfig();
} else if (_mode === 1) {
// MPR
viewOnFirstLoadItem = false;
dataViewConfigs = prepareAndGetMPRDataViewConfig();
} else if (_mode === 2) {
// multiple data, multiple layer group
addLayerGroup('layerGroup0');
addLayerGroup('layerGroup1');
dataViewConfigs = {
0: [
{
divId: 'layerGroup0'
},
{
divId: 'layerGroup1'
}
],
1: [
{
divId: 'layerGroup0'
}
],
2: [
{
divId: 'layerGroup1'
}
],
3: [
{
divId: 'layerGroup1'
}
]
};
} else if (_mode === 3) {
// timepoint mode
dataViewConfigs = prepareAndGetSimpleDataViewConfig();
}
// app config
var config = {
viewOnFirstLoadItem: viewOnFirstLoadItem,
dataViewConfigs: dataViewConfigs,
tools: {
Scroll: {},
WindowLevel: {},
ZoomAndPan: {},
Draw: {options: ['Rectangle'], type: 'factory'}
}
};
// app
_app = new dwv.App();
_app.init(config);
// bind events
_app.addEventListener('error', function (event) {
console.error('load error', event);
});
_app.addEventListener('loadstart', function (event) {
console.time('load-data-' + event.loadid);
});
var dataLoadProgress = new Array(numberOfDataToLoad);
var sumReducer = function (sum, value) {
return sum + value;
};
_app.addEventListener('loadprogress', function (event) {
if (typeof event.lengthComputable !== 'undefined' &&
event.lengthComputable) {
dataLoadProgress[event.loadid] =
Math.ceil((event.loaded / event.total) * 100);
document.getElementById('loadprogress').value =
dataLoadProgress.reduce(sumReducer) / numberOfDataToLoad;
}
});
var dataLoad = 0;
_app.addEventListener('load', function (event) {
console.log(_app.getMetaData(event.loadid));
if (!viewOnFirstLoadItem) {
_app.render(event.loadid);
}
// add data control row
if (_mode !== 3) {
addDataRow(dataLoad, dataViewConfigs);
}
++dataLoad;
// init gui
if (dataLoad === numberOfDataToLoad) {
// select tool
_app.setTool('Scroll');
var changeLayoutSelect = document.getElementById('changelayout');
changeLayoutSelect.disabled = false;
var resetLayoutButton = document.getElementById('resetlayout');
resetLayoutButton.disabled = false;
}
});
_app.addEventListener('loadend', function (event) {
console.timeEnd('load-data-' + event.loadid);
});
_app.addEventListener('positionchange', function (event) {
var input = document.getElementById('position');
var toFixed2 = function (val) {
var str = val.toString();
var value = null;
var dotIndex = str.indexOf('.');
if (dotIndex === -1) {
value = str;
} else {
value = str.slice(0, Math.min(dotIndex + 2, str.length));
}
return value;
};
var values = event.value[1];
input.value = values.map(toFixed2);
// index as small text
var span = document.getElementById('positionspan');
span.innerHTML = '(index: ' + event.value[0] + ')';
});
console.log(
'%c Available tools: (s)croll, (w)indowlevel, (z)oomandpan, (d)raw.',
'color: teal;');
_app.addEventListener('keydown', function (event) {
_app.defaultOnKeydown(event);
if (event.keyCode === 83) { // s
console.log('%c tool: scroll', 'color: teal;');
_app.setTool('Scroll');
} else if (event.keyCode === 87) { // w
console.log('%c tool: windowlevel', 'color: teal;');
_app.setTool('WindowLevel');
} else if (event.keyCode === 90) { // z
console.log('%c tool: zoomandpan', 'color: teal;');
_app.setTool('ZoomAndPan');
} else if (event.keyCode === 68) { // d
console.log('%c tool: draw', 'color: teal;');
_app.setTool('Draw');
_app.setDrawShape('Rectangle');
}
});
// load from location
dwv.utils.loadFromUri(window.location.href, _app);
};
/**
* Last minute.
*/
dwv.test.onDOMContentLoadedViewer = function () {
// setup
dwv.test.viewerSetup();
var positionInput = document.getElementById('position');
positionInput.addEventListener('change', function () {
var vls = _app.getViewLayersByDataIndex(0);
var vc = vls[0].getViewController();
var values = this.value.split(',');
vc.setCurrentPosition(new dwv.math.Point3D(
parseFloat(values[0]), parseFloat(values[1]), parseFloat(values[2]))
);
});
var resetLayoutButton = document.getElementById('resetlayout');
resetLayoutButton.addEventListener('click', function () {
_app.resetLayout();
});
var changeLayoutSelect = document.getElementById('changelayout');
changeLayoutSelect.addEventListener('change', function (event) {
var configs;
var value = event.target.value;
if (value === 'mpr') {
configs = prepareAndGetMPRDataViewConfig();
} else {
configs = prepareAndGetSimpleDataViewConfig();
}
_app.setDataViewConfig(configs);
clearDataTable();
for (var i = 0; i < _app.getNumberOfLoadedData(); ++i) {
_app.render(i);
addDataRow(i, configs);
}
_app.setTool('Scroll');
});
setupBindersCheckboxes();
// bind app to input files
var timeId = -1;
var fileinput = document.getElementById('fileinput');
fileinput.addEventListener('change', function (event) {
++timeId;
console.log('%c ----------------', 'color: teal;');
console.log(event.target.files);
var options = {};
if (_mode === 3) {
options = {timepoint: {id: timeId, dataId: 0}};
}
_app.loadFiles(event.target.files, options);
});
};
/**
* Append a layer div in the root 'dwv' one.
*
* @param {string} id The id of the layer.
*/
function addLayerGroup(id) {
var layerDiv = document.createElement('div');
layerDiv.id = id;
layerDiv.className = 'layerGroup';
var root = document.getElementById('dwv');
root.appendChild(layerDiv);
}
/**
* Create simple view config(s).
*
* @returns {object} The view config.
*/
function prepareAndGetSimpleDataViewConfig() {
// clean up
var dwvDiv = document.getElementById('dwv');
dwvDiv.innerHTML = '';
// add div
addLayerGroup('layerGroup0');
return {'*': [{divId: 'layerGroup0'}]};
}
/**
* Create MPR view config(s).
*
* @returns {object} The view config.
*/
function prepareAndGetMPRDataViewConfig() {
// clean up
var dwvDiv = document.getElementById('dwv');
dwvDiv.innerHTML = '';
// add divs
addLayerGroup('layerGroup0');
addLayerGroup('layerGroup1');
addLayerGroup('layerGroup2');
return {
'*': [
{
divId: 'layerGroup0',
orientation: 'axial'
},
{
divId: 'layerGroup1',
orientation: 'coronal'
},
{
divId: 'layerGroup2',
orientation: 'sagittal'
}
]
};
}
/**
* Get the layer groups ids from the data view configs.
*
* @param {object} dataViewConfigs The configs.
* @returns {Array} The list of ids.
*/
function getLayerGroupIds(dataViewConfigs) {
var divIds = [];
var keys = Object.keys(dataViewConfigs);
for (var i = 0; i < keys.length; ++i) {
var dataViewConfig = dataViewConfigs[keys[i]];
for (var j = 0; j < dataViewConfig.length; ++j) {
var divId = dataViewConfig[j].divId;
if (!divIds.includes(divId)) {
divIds.push(divId);
}
}
}
return divIds;
}
/**
* Get the layer group ids associated to a data.
*
* @param {Array} dataViewConfig The data view config.
* @returns {Array} The list of ids.
*/
function getDataLayerGroupIds(dataViewConfig) {
var divIds = [];
for (var j = 0; j < dataViewConfig.length; ++j) {
divIds.push(dataViewConfig[j].divId);
}
return divIds;
}
/**
* Setup the binders checkboxes
*/
function setupBindersCheckboxes() {
var bindersDiv = document.getElementById('binders');
var propList = [
'WindowLevel',
'Position',
'Zoom',
'Offset',
'Opacity'
];
var binders = [];
/**
* Add a binder.
*
* @param {string} propName The name of the property to bind.
*/
function addBinder(propName) {
binders.push(new dwv.gui[propName + 'Binder']);
_app.setLayerGroupsBinders(binders);
}
/**
* Remove a binder.
*
* @param {string} propName The name of the property to bind.
*/
function removeBinder(propName) {
for (var i = 0; i < binders.length; ++i) {
if (binders[i] instanceof dwv.gui[propName + 'Binder']) {
binders.splice(i, 1);
}
}
_app.setLayerGroupsBinders(binders);
}
/**
* Get the input change handler for a binder.
*
* @param {string} propName The name of the property to bind.
* @returns {object} The handler.
*/
function getOnInputChange(propName) {
return function (event) {
if (event.target.checked) {
addBinder(propName);
} else {
removeBinder(propName);
}
};
}
for (var i = 0; i < propList.length; ++i) {
var propName = propList[i];
var input = document.createElement('input');
input.id = i;
input.type = 'checkbox';
input.onchange = getOnInputChange(propName);
var label = document.createElement('label');
label.for = i;
label.appendChild(input);
label.appendChild(document.createTextNode(propName));
bindersDiv.appendChild(label);
}
}
/**
* Clear the data table.
*/
function clearDataTable() {
var detailsDiv = document.getElementById('layersdetails');
detailsDiv.innerHTML = '';
}
/**
* Add a data row.
*
* @param {number} id The data index.
* @param {object} dataViewConfigs The view configurations.
*/
function addDataRow(id, dataViewConfigs) {
var layerGroupIds = getLayerGroupIds(dataViewConfigs);
// use first view layer
var vls = _app.getViewLayersByDataIndex(id);
var vl = vls[0];
var vc = vl.getViewController();
var wl = vc.getWindowLevel();
var table = document.getElementById('layerstable');
var body;
// create table if not present
if (!table) {
table = document.createElement('table');
table.id = 'layerstable';
var header = table.createTHead();
var trow = header.insertRow(0);
var insertTCell = function (text) {
var th = document.createElement('th');
th.innerHTML = text;
trow.appendChild(th);
};
insertTCell('Id');
for (var j = 0; j < layerGroupIds.length; ++j) {
insertTCell('LG' + j);
}
insertTCell('Alpha Range');
insertTCell('Width');
insertTCell('Center');
insertTCell('Alpha');
body = table.createTBody();
var div = document.getElementById('layersdetails');
div.appendChild(table);
} else {
body = table.getElementsByTagName('tbody')[0];
}
// add new layer row
var row = body.insertRow();
var cell;
// cell: id
cell = row.insertCell();
cell.appendChild(document.createTextNode(id));
// cell: radio
var viewConfig = dataViewConfigs[id];
if (typeof viewConfig === 'undefined') {
viewConfig = dataViewConfigs['*'];
}
var dataLayerGroupsIds = getDataLayerGroupIds(viewConfig);
for (var l = 0; l < layerGroupIds.length; ++l) {
var layerGroupId = layerGroupIds[l];
cell = row.insertCell();
if (!dataLayerGroupsIds.includes(layerGroupId)) {
continue;
}
var radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'layerselect-' + l;
radio.id = l + '-' + id;
radio.checked = true;
radio.onchange = function (event) {
var fullId = event.srcElement.id;
var groupId = fullId.substring(0, fullId.indexOf('-'));
var dataId = fullId.substring(fullId.indexOf('-') + 1);
var lg = _app.getLayerGroupById(groupId);
lg.setActiveViewLayerByDataIndex(parseInt(dataId, 10));
};
cell.appendChild(radio);
}
var dataRange = _app.getImage(vl.getDataIndex()).getRescaledDataRange();
// cell: data range
cell = row.insertCell();
var widthmin = document.createElement('input');
widthmin.type = 'range';
widthmin.max = dataRange.max;
widthmin.min = dataRange.min;
widthmin.step = (dataRange.max - dataRange.min) * 0.001;
widthmin.value = dataRange.min;
var widthmax = document.createElement('input');
widthmax.type = 'range';
widthmax.max = widthmin.max;
widthmax.min = widthmin.min;
widthmax.step = widthmin.step;
widthmax.value = dataRange.max;
cell.appendChild(widthmin);
cell.appendChild(widthmax);
var changeAlphaFunc = function (min, max) {
var func = function (value) {
if (value > min && value < max) {
return 255;
}
return 0;
};
vc.setViewAlphaFunction(func);
};
widthmin.oninput = function () {
changeAlphaFunc(this.value, widthmax.value);
};
widthmax.oninput = function () {
changeAlphaFunc(widthmin.value, this.value);
};
// cell: window width
cell = row.insertCell();
var widthrange = document.createElement('input');
widthrange.type = 'range';
widthrange.max = dataRange.max - dataRange.min;
widthrange.min = 0;
widthrange.step = (dataRange.max - dataRange.min) * 0.001;
widthrange.value = wl.width;
var widthnumber = document.createElement('input');
widthnumber.type = 'number';
widthnumber.max = widthrange.max;
widthnumber.min = widthrange.min;
widthnumber.step = widthrange.step;
widthnumber.value = widthrange.value;
cell.appendChild(widthrange);
cell.appendChild(widthnumber);
// cell: window center
cell = row.insertCell();
var centerrange = document.createElement('input');
centerrange.type = 'range';
centerrange.max = dataRange.max;
centerrange.min = dataRange.min;
centerrange.step = (dataRange.max - dataRange.min) * 0.001;
centerrange.value = wl.center;
var centernumber = document.createElement('input');
centernumber.type = 'number';
centernumber.max = centerrange.max;
centernumber.min = centerrange.min;
centernumber.step = centerrange.step;
centernumber.value = centerrange.value;
cell.appendChild(centerrange);
cell.appendChild(centernumber);
var changeWidth = function (value) {
vc.setWindowLevel(centernumber.value, value);
};
widthnumber.oninput = function () {
changeWidth(this.value);
widthrange.value = this.value;
};
widthrange.oninput = function () {
changeWidth(this.value);
widthnumber.value = this.value;
};
var changeCenter = function (value) {
vc.setWindowLevel(value, widthnumber.value);
vl.draw();
};
centernumber.oninput = function () {
changeCenter(this.value);
centerrange.value = this.value;
};
centerrange.oninput = function () {
changeCenter(this.value);
centernumber.value = this.value;
};
// cell: opactiy
cell = row.insertCell();
var changeOpacity = function (value) {
vl.setOpacity(value);
vl.draw();
};
var opacityrange = document.createElement('input');
opacityrange.type = 'range';
opacityrange.max = 1;
opacityrange.min = 0;
opacityrange.step = 0.1;
opacityrange.value = vl.getOpacity();
var opacitynumber = document.createElement('input');
opacitynumber.type = 'number';
opacitynumber.max = opacityrange.max;
opacitynumber.min = opacityrange.min;
opacitynumber.step = opacityrange.step;
opacitynumber.value = opacityrange.value;
opacitynumber.oninput = function () {
changeOpacity(this.value);
opacityrange.value = this.value;
};
opacityrange.oninput = function () {
changeOpacity(this.value);
opacitynumber.value = this.value;
};
cell.appendChild(opacityrange);
cell.appendChild(opacitynumber);
}