diff --git a/js/viewer.js b/js/viewer.js
index 8ca0fac..389817e 100644
--- a/js/viewer.js
+++ b/js/viewer.js
@@ -69,7 +69,7 @@ export function loadMainImage(image, stack) {
//element.scrollIntoView(false);
//element.scrollTo(0);
- $(element).dblclick(function () {
+ $(element).dblclick(function() {
if ($(".canvas-panel").length == 0) {
// already fullscreen (disable it)
disableFullscreen(this);
@@ -82,7 +82,7 @@ export function loadMainImage(image, stack) {
$(".dicom-select-control")
.get(0)
- .addEventListener("change", function () {
+ .addEventListener("change", function() {
changeControlSelection();
});
@@ -114,10 +114,10 @@ function onImageRendered(e) {
$("option[value=scroll").prop("hidden", false);
$("option[value=scroll").text(
"scroll (" +
- (stack.currentImageIdIndex + 1) +
- "/" +
- stack.imageIds.length +
- ")"
+ (stack.currentImageIdIndex + 1) +
+ "/" +
+ stack.imageIds.length +
+ ")"
);
// Temp way to enable CT window presets
@@ -566,6 +566,62 @@ export function keydown_handler(event) {
event.preventDefault();
}
+function loadAnnotation(imageId, annotation) {
+ const toolStateManager =
+ cornerstoneTools.globalImageIdSpecificToolStateManager;
+
+ if (annotation == undefined || annotation.length < 1) {
+ return;
+ }
+
+ let tool_state_no_id = JSON.parse(annotation);
+
+ let tool_state = {};
+ tool_state[imageId] = tool_state_no_id;
+
+ toolStateManager.restoreToolState(tool_state);
+}
+async function load(images, annotations) {
+ // Parallelize image preparation and annotation loading
+ const imageTasks = images.map(async (data_url, i) => {
+ const annotation = annotations[i];
+ let imageId;
+ if (data_url.startsWith("data:image")) {
+ imageId = "base64://" + data_url.split(",")[1];
+ } else if (data_url.startsWith("data:")) {
+ const dfile = await urltoFile(data_url, "dicom", "application/dicom");
+ imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(dfile);
+ } else {
+ let url = data_url;
+ if (url.endsWith("dcm")) {
+ url = "wadouri:" + url;
+ }
+ if (/(?:\/|^)[^.\/]+$/.test(url)) {
+ url = "wadouri:" + url;
+ }
+ imageId = url;
+ }
+ return {imageId, annotation};
+ });
+
+ // Wait for all image tasks to complete
+ const imageResults = await Promise.all(imageTasks);
+ const imageIds = imageResults.map(r => r.imageId);
+
+ // Display the first image as soon as possible
+ cornerstone.loadAndCacheImage(imageIds[0]).then(function(image) {
+ const stack = {
+ currentImageIdIndex: 0,
+ imageIds,
+ };
+ loadMainImage(image, stack);
+ // Restore annotations for all images after display
+ imageResults.forEach(({imageId, annotation}) => {
+ loadAnnotation(imageId, annotation);
+ });
+ });
+}
+
/**
* View cornstone image
* @param {*} t
@@ -576,82 +632,7 @@ export function openMainImage(current_question, t, source) {
* Load image
* @param {*} images - list of images
*/
- function loadAnnotation(imageId, annotation) {
- const toolStateManager =
- cornerstoneTools.globalImageIdSpecificToolStateManager;
- if (annotation == undefined || annotation.length < 1) {
- return;
- }
-
- let tool_state_no_id = JSON.parse(annotation);
-
- let tool_state = {};
- tool_state[imageId] = tool_state_no_id;
-
- toolStateManager.restoreToolState(tool_state);
- }
- async function load(images, annotations) {
- //console.debug("Load function", images)
- const imageIds = [];
- for (let i = 0; i < images.length; i++) {
- const data_url = images[i];
- const annotation = annotations[i];
-
- // check stack type
- if (data_url.startsWith("data:image")) {
- const imageId = "base64://" + data_url.split(",")[1];
-
- loadAnnotation(imageId, annotation);
-
- imageIds.push(imageId);
- //} else if (data_url.startsWith("data:application/dicom")) {
- } else if (data_url.startsWith("data:")) {
- // stack = stack.split(";")[1];
-
- const dfile = await urltoFile(data_url, "dicom", "application/dicom");
-
- const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
- dfile
- );
-
- loadAnnotation(imageId, annotation);
-
- imageIds.push(imageId);
- // cornerstone.loadImage(imageId).then(function(image) {
- // tempFunction(image);
- // });
- } else {
- let url = data_url;
- //if (data_url.startsWith("http")) {
- // url = data_url;
- //} else {
- // url = window.location.href.replace(/\/\#\/?$/, "") + "/" + data_url;
- //}
-
- if (url.endsWith("dcm")) {
- url = "wadouri:" + url;
- }
-
- // if there is no extension treat it as a dicom
- if (/(?:\/|^)[^.\/]+$/.test(url)) {
- url = "wadouri:" + url;
- }
- //console.debug(url)
-
- loadAnnotation(url, annotation);
-
- imageIds.push(url);
- }
- }
- const stack = {
- currentImageIdIndex: 0,
- imageIds,
- };
- cornerstone.loadAndCacheImage(imageIds[0]).then(function (image) {
- loadMainImage(image, stack);
- });
- }
if (current_question) {
// Check if the figure is already loaded (or if another one is)
@@ -680,44 +661,91 @@ export function openMainImage(current_question, t, source) {
source.className = "figure-open";
- console.log(current_question, figure_to_load, source);
let image_title = source.getElementsByClassName("figcaption")[0].textContent;
- $(".question").prepend(
- `
-
-
-
-
-
-
-
-
-
'
- );
+ // Native DOM creation for performance
+ const questionElem = document.querySelector('.question');
+ const canvasPanel = document.createElement('div');
+ canvasPanel.className = 'canvas-panel';
+
+ const viewerTitleBar = document.createElement('div');
+ viewerTitleBar.id = 'viewer-title-bar';
+
+ const flexDiv = document.createElement('div');
+ flexDiv.style.flex = '1 1 auto';
+
+ const viewerTitle = document.createElement('div');
+ viewerTitle.id = 'viewer-title';
+ viewerTitle.style.color = 'white';
+ viewerTitle.textContent = image_title;
+ flexDiv.appendChild(viewerTitle);
+ viewerTitleBar.appendChild(flexDiv);
+
+ // Select control
+ const select = document.createElement('select');
+ select.title = 'SELECT CLICK ACTION';
+ select.className = 'dicom-select-control title-bar-button';
+ select.style.marginRight = '8px';
+ const options = [
+ { value: 'pan', text: 'pan [p]' },
+ { value: 'zoom', text: 'zoom [z]' },
+ { value: 'rotate', text: 'rotate [r]' },
+ { value: 'window', text: 'window (128 ± 127.5) [w]' },
+ { value: 'measure', text: 'measure [m]' },
+ { value: 'ellipse', text: 'ellipse [l]' },
+ { value: 'rectangle', text: 'rectangle [c]' },
+ { value: 'reset', text: 'reset [e]' },
+ { value: 'notes', text: '[modality = SC]', disabled: true }
+ ];
+ options.forEach(opt => {
+ const o = document.createElement('option');
+ o.value = opt.value;
+ o.textContent = opt.text;
+ if (opt.disabled) o.disabled = true;
+ select.appendChild(o);
+ });
+ viewerTitleBar.appendChild(select);
+
+ // Buttons
+ function createButton(id, title, svg, style = '') {
+ const btn = document.createElement('button');
+ btn.id = id;
+ btn.className = 'title-bar-button';
+ btn.title = title;
+ btn.style.marginRight = '8px';
+ if (style) btn.style.display = style;
+ const span = document.createElement('span');
+ span.className = 'viewer-icon';
+ span.innerHTML = svg;
+ btn.appendChild(span);
+ return btn;
+ }
+ // SVGs
+ const svgReset = ``;
+ const svgFullscreen = ``;
+ const svgFullscreenExit = ``;
+ const svgClose = ``;
+
+ const btnReset = createButton('reset-viewer-button', 'RESET VIEW', svgReset);
+ const btnFullscreen = createButton('fullscreen-viewer-button', 'TOGGLE FULLSCREEN', svgFullscreen);
+ const btnFullscreenExit = createButton('disable-fullscreen-viewer-button', 'TOGGLE FULLSCREEN', svgFullscreenExit, 'none');
+ const btnClose = createButton('close-viewer-button', 'CLOSE VIEWER', svgClose);
+
+ viewerTitleBar.appendChild(btnReset);
+ viewerTitleBar.appendChild(btnFullscreen);
+ viewerTitleBar.appendChild(btnFullscreenExit);
+ viewerTitleBar.appendChild(btnClose);
+
+ canvasPanel.appendChild(viewerTitleBar);
+
+ // DICOM image container
+ const dicomImageDiv = document.createElement('div');
+ dicomImageDiv.id = 'dicom-image';
+ dicomImageDiv.setAttribute('data-figure', figure_to_load);
+ canvasPanel.appendChild(dicomImageDiv);
+
+ // Prepend to question
+ questionElem.insertBefore(canvasPanel, questionElem.firstChild);
let images = current_question.images[figure_to_load.split("-")[1]];
// images = current_question.images
@@ -737,16 +765,16 @@ export function openMainImage(current_question, t, source) {
const dicom_element = document.getElementById("dicom-image");
// Bind button actions
- $("#close-viewer-button").click(function () {
+ $("#close-viewer-button").click(function() {
closeViewer(dicom_element);
});
- $("#reset-viewer-button").click(function () {
+ $("#reset-viewer-button").click(function() {
resetViewer(dicom_element);
});
- $("#fullscreen-viewer-button").click(function () {
+ $("#fullscreen-viewer-button").click(function() {
enableFullscreen(dicom_element);
});
- $("#disable-fullscreen-viewer-button").click(function () {
+ $("#disable-fullscreen-viewer-button").click(function() {
disableFullscreen(dicom_element);
});
@@ -755,39 +783,39 @@ export function openMainImage(current_question, t, source) {
}
function closeViewer(dicom_element) {
- // disable fullscreen if required
- if ($(".canvas-panel").length == 0) {
- disableFullscreen(dicom_element);
- }
- if (dicom_element != undefined) {
- // See https://github.com/cornerstonejs/cornerstoneTools/issues/1337
- cornerstone.removeElementData(dicom_element);
- cornerstone.disable(dicom_element);
- $(".canvas-panel").remove();
- $(".figure-open").removeClass("figure-open").addClass("figure");
- $(dicom_element).remove();
- }
+ // disable fullscreen if required
+ if ($(".canvas-panel").length == 0) {
+ disableFullscreen(dicom_element);
+ }
+ if (dicom_element != undefined) {
+ // See https://github.com/cornerstonejs/cornerstoneTools/issues/1337
+ cornerstone.removeElementData(dicom_element);
+ cornerstone.disable(dicom_element);
+ $(".canvas-panel").remove();
+ $(".figure-open").removeClass("figure-open").addClass("figure");
+ $(dicom_element).remove();
+ }
}
export function urltoFile(url, filename, mimeType) {
return fetch(url)
- .then(function (res) {
+ .then(function(res) {
return res.arrayBuffer();
})
- .then(function (buf) {
- return new File([buf], filename, { type: mimeType });
+ .then(function(buf) {
+ return new File([buf], filename, {type: mimeType});
});
}
function enableFullscreen(dicom_element) {
- $(".content-panel").prepend($(".canvas-panel"));
- $(".canvas-panel").toggleClass("canvas-panel canvas-panel-fullscreen");
- $("#dicom-image").attr("height", "100%");
- $("#dicom-image").height("100%");
- $("#disable-fullscreen-viewer-button").show();
- $("#fullscreen-viewer-button").hide();
- // $(".cornerstone-canvas").attr("height", "100%");
- // $(".cornerstone-canvas").height("100%");
- cornerstone.resize(dicom_element, true);
+ $(".content-panel").prepend($(".canvas-panel"));
+ $(".canvas-panel").toggleClass("canvas-panel canvas-panel-fullscreen");
+ $("#dicom-image").attr("height", "100%");
+ $("#dicom-image").height("100%");
+ $("#disable-fullscreen-viewer-button").show();
+ $("#fullscreen-viewer-button").hide();
+ // $(".cornerstone-canvas").attr("height", "100%");
+ // $(".cornerstone-canvas").height("100%");
+ cornerstone.resize(dicom_element, true);
}
\ No newline at end of file