(function ($, GLOBAL) { var CONTENT_TEMPLATE = '
FieldProbe Slice 0 Time 0
Slice0
Time0
', OPTION_TEMPLATE = ''; // ======================================================================== // Helper // ======================================================================== function getFileName(filePattern, args) { var fileName = filePattern; for(key in args) { fileName = fileName.replace('{'+key+'}', args[key]); } return fileName; } // ------------------------------------------------------------------------ function getOptions(values) { var buffer = []; for(var idx in values) { buffer.push(OPTION_TEMPLATE.replace('VALUE', values[idx])); } return buffer.join('\n'); } // ------------------------------------------------------------------------ function update(container) { var field = $('select[name="field"]', container).val(), slice = $('input[name="slice"]', container).val(), time = $('input[name="time"]', container).val(), probeAxis = $('select[name="probe"]', container).val(); downloadJSON(container, slice, probeAxis, field, time); downloadImage(container, slice, field, time); } // ======================================================================== // Download manager // ======================================================================== function downloadJSON(container, slice, probeAxis, field, time) { var sliceList = [], cache = container.data('json-cache'), info = container.data('info'), basepath = container.data('base-path'), urls = []; if(probeAxis === "2") { sliceList = info['arguments']['slice']['values']; } else { sliceList.push(slice); } function updateChart(){ if(0 === urls.length) { container.trigger('invalidate-chart'); } } function download(fileName) { $.getJSON( basepath + '/' + fileName, function(data){ cache[fileName] = data; if(urls.length > 0) { download(urls.pop()); } else { updateChart(); } }).fail(function(e){ console.log('Fail to download ' + fileName); console.log(e); }); } // Fill download queue for(var idx in sliceList) { var fileName = getFileName(info['name_pattern'], {'field': field, 'slice': sliceList[idx], 'time': time, 'format': 'json'}); if(!cache.hasOwnProperty(fileName)) { urls.push(fileName); } } // Trigger download or update chart if(urls.length > 0) { download(urls.pop()); } else { updateChart(); } } // ------------------------------------------------------------------------ function downloadImage(container, slice, field, time) { var info = container.data('info'), basepath = container.data('base-path'), fileName = getFileName(info['name_pattern'], {'field': field, 'slice': slice.toString(), 'time': time, 'format': 'jpg'}), img = $('img', container); img.attr('src', basepath + '/' + fileName); } // ======================================================================== // Chart management // ======================================================================== function updateChart(container) { var cache = container.data('json-cache'), info = container.data('info'), probe = container.data('probe-coord'), field = $('select[name="field"]', container).val(), slice = Number($('input[name="slice"]', container).val()), time = $('input[name="time"]', container).val(), probeAxis = $('select[name="probe"]', container).val(), fileName = getFileName(info['name_pattern'], {'field': field, 'slice': slice, 'time': time, 'format': 'json'}), size = [0,0,0], offset = 0, step = 0, nbSteps = 0, data = [], chartContainer = $(".chart-sample", container); if(cache[fileName] === undefined) { return; } size = cache[fileName]["dimensions"]; function clamp(value) { return (value == null || value < -1e10) ? null : value; } if(probe === undefined) { return; } // Figure out way to traverse if(probeAxis === '0') { // Along X nbSteps = size[0]; step = 1; offset = (size[1] - probe[1]) * size[0]; } else if(probeAxis === '1') { // Along Y nbSteps = size[1]; step = size[0]; offset = probe[0]; } else if(probeAxis === '2') { // Along Z nbSteps = size[2]; step = -1; offset = probe[0] + (size[1] - probe[1]) * size[0]; } // Extract data if(probeAxis === '2') { sliceList = info['arguments']['slice']['values']; // Need multi-files for(var idx in sliceList) { f = getFileName(info['name_pattern'], {'field': field, 'slice': sliceList[idx], 'time': time, 'format': 'json'}); sliceDataField = cache[f][field]; data.push({x: Number(idx), y: clamp(sliceDataField[offset])}) } } else if(probeAxis === '1') { // Same slice sliceDataField = cache[fileName][field]; for(var i = 0; i < nbSteps; ++i) { data.push({x: i, y: clamp(sliceDataField[offset + (i*step)])}) } } else if(probeAxis === '0') { // Same slice sliceDataField = cache[fileName][field]; for(var i = 0; i < nbSteps; ++i) { data.push({x: i, y: clamp(sliceDataField[offset + (i*step)])}) } } // Update UI with chart if(chartContainer.hasClass('vtk-chart')) { // Update chartContainer.vtkChartUpdateData([ { data: data, color: 'steelblue', name: field } ], true); chartContainer.vtkChartConfigure({'chart-padding': [0, 0, 0, 0]}); } else { // Create chart chartContainer.vtkChart({ 'legend': {basic: false, toggle: false, highlight: false}, 'renderer': 'line', 'series': [ { data: data, color: 'steelblue', name: field } ], 'axes': [ "bottom", "left", "top"], 'chart-padding': [0, 0, 0, 0] }); } } // ======================================================================== // Listeners // ======================================================================== function initializeListeners(container) { // Attach redraw callback var canvas = $('canvas', container), chartContainer = $('.chart-sample', container), image = $('img', container), enableProbing = false, startSliding = false, sliders = $('input', container), timeSlider = $('input[name="time"]', container), sliceSlider = $('input[name="slice"]', container), timeTxt = $('span.time-value', container), sliceTxt = $('span.slice-value', container), activeSlider = null; dropDowns = $('select', container), currentSlideValue = 0, maxSlideValue = 1, keepAnimation = false, play = $('.play', container), stop = $('.stop', container); function animate() { if(activeSlider) { currentSlideValue = (currentSlideValue + 1) % maxSlideValue; activeSlider.val(currentSlideValue); if(keepAnimation) { setTimeout(animate, 150); } updateAll(); } } // Generic data update function updateAll() { timeTxt.html(timeSlider.val()); sliceTxt.html(sliceSlider.val()); update(container); paint(); } // Callback methods to probe data function probe(event) { if(enableProbing) { var offset = canvas.offset(), z = $('input[name="slice"]', container).val(), scale = image[0].naturalWidth / canvas.width(); container.data('probe-coord', [Math.floor(scale*(event.pageX - offset.left)), Math.floor(scale*(event.pageY - offset.top)), Number(z)]); updateAll(); } } // Callback methods to paint image function paint() { if(image[0].naturalWidth == 0) { setTimeout(paint, 100); return; } var ctx = canvas[0].getContext("2d"), w = canvas.parent().width(), img = image[0], ih = img.naturalHeight, iw = img.naturalWidth, ratio = ih / iw, scale = iw / w, headHeight = canvas.offset().top - container.offset().top + 22, probePoint = container.data('probe-coord'); canvas.css('left', '20px').attr('width', w + 'px').attr('height', Math.ceil(w*ratio) + 'px'); chartContainer.css('width', '100%').css('height', (container.height() - Math.ceil(w*ratio) - headHeight) + 'px'); ctx.drawImage(image[0], 0, // source image upper left x 0, // source image upper left y iw, // source image width ih, // source image height 0, // destination canvas upper left x 0, // destination canvas upper left y canvas.width(), // destination canvas width canvas.height()); // destination canvas height // Draw line if(probePoint) { var probeAxis = $('select[name="probe"]', container).val(); ctx.strokeStyle = "#000000"; ctx.fillStyle="#FFFFFF"; if(probeAxis === '0') { // Along X var y = probePoint[1] / scale; ctx.rect(0,y,canvas.width(), Math.ceil(scale)); ctx.stroke(); ctx.fill(); } else if(probeAxis === '1') { // Along Y var x = probePoint[0] / scale; ctx.rect(x,scale,Math.ceil(scale),canvas.height()); ctx.stroke(); ctx.fill(); } else if(probeAxis === '2') { // Along Z var x = probePoint[0] / scale, y = probePoint[1] / scale; ctx.beginPath(); ctx.arc(x,y,5,0,2*Math.PI); ctx.fill(); ctx.stroke(); } } } // Attach probing listeners canvas.bind('mousemove', probe); canvas.bind('mouseup', function(){ enableProbing = false; }); canvas.bind('mousedown', function(e){ enableProbing = true; probe(e); }); // Attach auto-paint listener image.bind('load onload', paint); // Attach graph update container.bind('invalidate-chart', function(){ updateChart(container); }); // Attach dropDowns listeners dropDowns.bind('change', updateAll); // Attach slider listener sliders.bind('change keyup', updateAll); sliders.bind('mousedown', function(){ startSliding = true; }); sliders.bind('mouseup', function(){ startSliding = false; }); sliders.bind('mousemove', function(){ if(startSliding) { updateAll(); } }); $('div.label', container).click(function(){ var me = $(this), all = $('.label', container); activeSlider = $('input[name="' + me.attr('data-name') + '"]', container); if(activeSlider) { currentSlideValue = Number(activeSlider.val()); maxSlideValue = Number(activeSlider.attr('max')); // Handle flag visibility all.removeClass('active'); me.addClass('active'); } }); $('.toggle', container).click(function(){ container.toggleClass('small'); }); play.click(function(){ play.hide(); stop.show(); keepAnimation = true; animate(); }); stop.click(function(){ stop.hide(); play.show(); keepAnimation = false; }); } // ======================================================================== // JQuery // ======================================================================== /** * jQuery catalyst view constructor. * * @member jQuery.vtkCatalystViewer * @param basePath * Root directory for data to visualize */ $.fn.vtkCatalystResamplerViewer = function(dataBasePath) { return this.each(function() { var me = $(this).empty().addClass('vtk-catalyst-resample-viewer small').unbind(); // Get meta-data $.ajax({ url: dataBasePath + '/info.json', dataType: 'json', success: function( data ) { // Store metadata me.data('info', data); me.data('active-args', {}); me.data('json-cache', {}); me.data('base-path', dataBasePath); // Create UI me.html(CONTENT_TEMPLATE .replace('FIELDS', getOptions(data['arguments']['field']['values'])) .replace('NB_SLICES', data['arguments']['slice']['values'].length-1) .replace('NB_TIMES', data['arguments']['time']['values'].length-1) ); initializeListeners(me); // Load data update(me); }, error: function(error) { console.log("error when trying to download " + dataBasePath + '/info.json'); console.log(error); } }); }); } }(jQuery, window));