// Dicom saved settings
var dicom_settings_db = new Dexie("dicom_settings");
dicom_settings_db.version(1).stores({
mouse_bindings: "button,mode,tool",
});
cornerstoneBase64ImageLoader.external.cornerstone = cornerstone;
cornerstoneWebImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneTools.init();
let tool_state = {};
export function loadCornerstone(main_element, db, images, annotations_to_load, load_as_stack = false) {
// canvas-panel holds the enabled elements and all tools / menus
main_element.append("
");
main_element[0].dataset.collapsed = "none";
$(".canvas-panel").append($(""));
//$(".canvas-panel").append($("").load("dicomSummary.html"));
let single_dicom_viewer = main_element.find(".single-dicom-viewer").get(0);
console.log(single_dicom_viewer);
$(".canvas-panel").append($(``));
// Add generic settings menus to canvas-panel
$(".canvas-panel").append(
$(``)
)
.append($("↻"))
.append($("⛶"))
.append($("⚙"))
.append($("⇄"));
$(single_dicom_viewer).append(
$(
"Image of
wc: ww:
"
)
)
// Add buttons
.append(
$(`
`)
);
let window_presets = {
"window-btn-abdomen": [400, 50],
"window-btn-liver": [150, 30],
"window-btn-mediastinum": [350, 50],
"window-btn-lung": [1500, -600],
"window-btn-brain": [80, 40],
"window-btn-stroke": [40, 40],
"window-btn-bone": [1800, 400]
}
$("#dicom-window-panel button").each((n, el) => {
$(el).click((e) => {
console.log(e);
console.log(e.target);
let button_id = e.target.id;
let [ww, wc] = window_presets[button_id];
let viewport = cornerstone.getViewport(single_dicom_viewer);
viewport.voi.windowWidth = parseFloat(ww);
viewport.voi.windowCenter = parseFloat(wc);
cornerstone.setViewport(single_dicom_viewer, viewport);
e.preventDefault();
})
})
$("#dicom-settings-close").click(e => {
$("#dicom-settings-panel").hide();
e.preventDefault();
});
$("#dicom-log-close").click(e => {
$("#dicom-log-panel").hide();
e.preventDefault();
});
$(".view-log-button").click(e => {
$("#dicom-settings-panel").hide();
$("#dicom-log-panel").show();
e.preventDefault();
});
$("#dicom-settings-button").click(e => {
$("#dicom-settings-panel").toggle();
});
$("#dicom-fullscreen-button").click(e => {
if (!document.fullscreenElement) {
$(".canvas-panel").get(0).requestFullscreen().catch(err => {
alert(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
});
} else {
document.exitFullscreen();
}
});
$("#dicom-toggle-mode-button").click(e => {
//$("#dicom-settings-panel").toggle();
let stack = !load_as_stack;
$(main_element).empty();
loadCornerstone(main_element, db, images, annotations_to_load, stack)
});
$("#dicom-collapse-button").click(e => {
//$("#dicom-settings-panel").toggle();
//let stack = !load_as_stack;
if (main_element[0].dataset.collapsed == "none") {
main_element[0].dataset.collapsed = main_element.width();
main_element.width("50px");
} else {
main_element.width(main_element[0].dataset.collapsed);
main_element[0].dataset.collapsed = "none"
}
resizeHandler();
//loadCornerstone(main_element, db, images, annotations_to_load, stack)
});
//let images = data["images"];
// Make sure we have an array
if (!Array.isArray(images)) {
images = [images];
}
console.log("Images", images)
//let annotations = [];
//if (annotations_to_load) {
//annotations = annotations_to_load[figure_to_load.split("-")[1]];
//if (!Array.isArray(annotations)) {
//annotations = [annotations];
//}
//}
let annotations = annotations_to_load;
if (!Array.isArray(annotations)) {
annotations = [annotations];
}
console.log("annon", annotations);
function loadAnnotation(imageId, annotation) {
//console.log("loadAnnotations", imageId, annotation);
const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
if (annotation == undefined || annotation.length < 1 || annotation == [undefined]) { return }
let tool_state_no_id = JSON.parse(annotation);
tool_state[imageId] = tool_state_no_id;
toolStateManager.restoreToolState(tool_state);
}
async function load(images, annotations) {
console.log("LOAD", images);
let imageIds = [];
for (let i = 0; i < images.length; i++) {
let data_url = images[i];
const annotation = annotations[i];
// check stack type
if (data_url.startsWith("data:image")) {
let imageId = "base64://" + data_url.split(",")[1];
loadAnnotation(imageId, annotation);
imageIds.push(imageId);
// Treat application/octet-stream as if they are dicoms
} else if (data_url.startsWith("data:application/dicom") || data_url.startsWith("data:application/octet-stream")) {
//stack = stack.split(";")[1];
let 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;
// This doesn't seem to have any benefit
//if (data_url.startsWith("http")) {
// url = data_url;
//} else {
// url = window.location.href.replace(/\/\#\/?$/, '') + "/" + data_url
//}
url = 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;
}
loadAnnotation(url, annotation);
imageIds.push(url);
}
}
const stack = {
currentImageIdIndex: 0,
imageIds
};
//cornerstone.loadAndCacheImage(imageIds[0]).then(function(image) {
//console.log("load and cache", imageIds[0])
cornerstone.loadAndCacheImage(imageIds[0]).then(function (image) {
//console.log("LOAD and cache, then", image);
console.log(oeu)
loadCornerstoneMainImage(single_dicom_viewer, image, stack, db, load_as_stack);
})
.catch((err, err2) => {
console.log(err);
$("#dicom-log-panel").append("
").append(err.stack);
});
}
if (images.length > 1 && load_as_stack == false) {
$(".canvas-panel").append("");
for (let id = 0; id < images.length; id++) {
let n = id + 1;
let thumb = $(
"" +
n +
"
"
);
$("#image-thumbs").append(thumb);
$("#thumb-" + id).click(selectThumbClick).mousedown(stopEvent);
let image_url = images[id];
console.log(image_url)
let img;
if (image_url.startsWith("data")) {
// based (image) data url, just load the image directly
if (image_url.startsWith("data:image/")) {
img = $("
", {
src: image_url,
id: "thumb-image-" + id,
class: "thumbnail",
title: "Click on the thumbnail to view and manipulate the image.",
draggable: "false",
style: "height: 100px;"
});
$("#thumb-" + id).append(img);
// otherwise try to load it as a dicom
} else {
// convert the data url to a file
urltoFile(image_url, "dicom", "application/dicom").then(function (
dfile
) {
// load the file using cornerstoneWADO file loader
const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
dfile
);
cornerstone.loadAndCacheImage(imageId).then(function (image) {
img = $("").get(0);
img.id = "thumb-image-" + id;
img.class = "thumbnail";
img.title =
"Click on the thumbnail to view and manipulate the image.";
img.draggable = "false";
img.style = "height: 100px; width: 100px";
$("#thumb-" + id).append(img);
const element = document.getElementById("thumb-image-" + id);
cornerstone.enable(element);
cornerstone.displayImage(element, image);
cornerstone.resize(element);
}); //.catch( function(error) {
});
}
} else {
if (!image_url.endsWith("dicom") && !image_url.endsWith("dcm")) {
img = $("
", {
src: image_url,
id: "thumb-image-" + id,
class: "thumbnail",
title: "Click on the thumbnail to view and manipulate the image.",
draggable: "false",
style: "height: 100px;"
});
$("#thumb-" + id).append(img);
// otherwise try to load it as a dicom
} else {
let url = "wadouri:" + image_url;
console.log(url)
cornerstone.loadAndCacheImage(url).then(function (image) {
img = $("").get(0);
img.id = "thumb-image-" + id;
img.class = "thumbnail";
img.title =
"Click on the thumbnail to view and manipulate the image.";
img.draggable = "false";
img.style = "height: 100px; width: 100px";
$("#thumb-" + id).append(img);
const element = document.getElementById("thumb-image-" + id);
cornerstone.enable(element);
cornerstone.displayImage(element, image);
cornerstone.resize(element);
});
}
}
}
}
load(images, annotations);
}
function loadCornerstoneMainImage(element, image, stack, db, load_as_stack) {
dumpDataSet(image.data)
console.log("loadCornerstoneMainImage")
console.log("load as stack:", load_as_stack)
// It is probably silly to do this each time we load a question
const PanTool = cornerstoneTools.PanTool;
const ZoomTool = cornerstoneTools.ZoomTool;
const ZoomMouseWheelTool = cornerstoneTools.ZoomMouseWheelTool;
const StackScrollMouseWheelTool = cornerstoneTools.StackScrollMouseWheelTool;
const WwwcTool = cornerstoneTools.WwwcTool;
const WwwcRegionTool = cornerstoneTools.WwwcRegionTool;
const RotateTool = cornerstoneTools.RotateTool;
const StackScrollTool = cornerstoneTools.StackScrollTool;
const MagnifyTool = cornerstoneTools.MagnifyTool;
const ArrowAnnotateTool = cornerstoneTools.ArrowAnnotateTool;
const RectangleRoiTool = cornerstoneTools.RectangleRoiTool;
const LengthTool = cornerstoneTools.LengthTool;
console.log("enable element", element);
cornerstone.enable(element);
cornerstone.displayImage(element, image);
cornerstoneTools.addStackStateManager(element, ["stack"]);
cornerstoneTools.addToolState(element, "stack", stack);
cornerstoneTools.addToolForElement(element, PanTool);
cornerstoneTools.addToolForElement(element, ZoomTool);
cornerstoneTools.addToolForElement(element, ZoomMouseWheelTool);
cornerstoneTools.addToolForElement(element, StackScrollMouseWheelTool);
cornerstoneTools.addToolForElement(element, WwwcTool);
cornerstoneTools.addToolForElement(element, WwwcRegionTool);
cornerstoneTools.addToolForElement(element, RotateTool);
cornerstoneTools.addToolForElement(element, StackScrollTool);
cornerstoneTools.addToolForElement(element, MagnifyTool);
cornerstoneTools.addToolForElement(element, LengthTool);
//cornerstoneTools.addTool(ArrowAnnotateTool);
//cornerstoneTools.addTool(RectangleRoiTool);
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, {
configuration: {
getTextCallback: () => { },
changeTextCallback: () => { },
allowEmptyLabel: true,
renderDashed: false,
drawHandles: false,
drawHandlesOnHover: true,
},
});
cornerstoneTools.addToolForElement(element, RectangleRoiTool, {
configuration: {
showHounsfieldUnits: false,
showMinMax: false,
allowEmptyLabel: true,
renderDashed: false,
drawHandles: false,
drawHandlesOnHover: true,
},
});
cornerstoneTools.setToolEnabledForElement(element, "ArrowAnnotate");
let available_tools = [
"Pan",
"Zoom",
"Wwwc",
"WwwcRegion",
"Rotate",
"StackScroll",
"Magnify",
"Length",
"ArrowAnnotate",
"RectangleRoi",
];
$(".mouse-binding-select option").remove();
available_tools.forEach(function (tool) {
let option = "";
$(".mouse-binding-select").append(option);
//$("#left-mouse-dicom").append(option);
//$("#middle-mouse-dicom").append(option);
//$("#right-mouse-dicom").append(option);
});
$(".mouse-binding-select").on("change", function (e) {
changeMouseBinding(e, db);
});
// Set default tools
$("#primary-mouse-binding .mouse-binding-select[data-button=1]").val("Pan");
$("#primary-mouse-binding .mouse-binding-select[data-button=2]").val("Wwwc");
$("#primary-mouse-binding .mouse-binding-select[data-button=4]").val("Zoom");
//cornerstoneTools.setToolActive("Pan", { mouseButtonMask: 1 });
console.log(load_as_stack);
if (load_as_stack) {
cornerstoneTools.setToolActiveForElement(element, "StackScrollMouseWheel", { mouseButtonMask: 3 });
} else {
cornerstoneTools.setToolActiveForElement(element, "ZoomMouseWheel", { mouseButtonMask: 3 });
}
//cornerstoneTools.setToolActive("Zoom", { mouseButtonMask: 4 });
//cornerstoneTools.setToolActive("Wwwc", { mouseButtonMask: 2 });
$("#secondary-mouse-binding .mouse-binding-select[data-button=1]").val(
"Magnify"
);
$("#secondary-mouse-binding .mouse-binding-select[data-button=2]").val(
"Rotate"
);
$("#secondary-mouse-binding .mouse-binding-select[data-button=4]").val(
"WwwcRegion"
);
loadPrimaryDicomInterface(db);
loadAltDicomInterface(db);
element.addEventListener("cornerstoneimagerendered", onImageRendered);
//setDicomCanvasNonFullscreen(element);
cornerstone.reset(element);
//element.scrollIntoView(false);
//element.scrollTo(0);
$(element).dblclick(function () {
cornerstone.reset(this);
});
element.removeEventListener("wheel", element.wheelEventHandler);
// Add tool selector
//$(".canvas-panel").append(
// ''
//);
//$(".control-overlay")
// .get(0)
// .addEventListener("change", changeControlSelection);
resizeHandler();
window.addEventListener("resize", resizeHandler);
element.addEventListener(
"contextmenu",
function (e) {
// do something here...
e.preventDefault();
},
false
);
$(element).on("mouseup", function (event) {
$(".magnifyTool").hide();
});
}
function resizeHandler() {
let element = document.getElementsByClassName("single-dicom-viewer")[0];
let h = window.innerHeight - $("#header").height() - 16;
$(element).height(h);
cornerstone.resize(element, true);
}
async function loadPrimaryDicomInterface(db) {
const bindings = await dicom_settings_db.mouse_bindings.where({ mode: "0" }).toArray().catch((err) => { console.log(err); });
bindings.forEach(function (b) {
let sel = $("#primary-mouse-binding select[data-button=" + b.button + "]").get(
0
);
sel.value = b.tool;
//sel.dispatchEvent(new Event("change"));
});
registerPrimaryDicomInterface();
}
async function loadAltDicomInterface(db) {
const bindings = await dicom_settings_db.mouse_bindings.where({ mode: "1" }).toArray().catch((err) => { console.log(err); });
bindings.forEach(function (b) {
let sel = $(
"#secondary-mouse-binding select[data-button=" + b.button + "]"
).get(0);
sel.value = b.tool;
});
}
// Called when the dicom image is loaded / rendered
function onImageRendered(e) {
const eventData = e.detail;
//console.log(e);
// Update ww/wl
$("#wc").text(Math.round(eventData.viewport.voi.windowCenter));
$("#ww").text(Math.round(eventData.viewport.voi.windowWidth));
// update stack data
let stack = eventData.enabledElement.toolStateManager.toolState.stack.data[0];
$("#total_image_number").text(stack.imageIds.length);
$("#current_image_number").text(parseInt(stack.currentImageIdIndex) + 1);
if (stack.imageIds.length > 1) {
$(".thumb").removeClass("thumb-active");
$("#thumb-" + stack.currentImageIdIndex).addClass("thumb-active");
}
//if (stack.imageIds.length > 1) {
// $("option[value=scroll").prop("disabled", false);
// $("option[value=scroll").prop("hidden", false);
// $("option[value=scroll").text(
// "scroll (" +
// (stack.currentImageIdIndex + 1) +
// "/" +
// stack.imageIds.length +
// ")"
// );
}
function stopEvent(evt) {
console.log("stop", evt)
evt.preventDefault();
evt.stopPropagation();
}
function selectThumbClick(evt) {
let new_index = evt.currentTarget.dataset.id;
selectThumb(new_index);
evt.preventDefault();
}
export function registerPrimaryDicomInterface() {
// Set mousetools based upon the selected options
let element = document.getElementsByClassName("single-dicom-viewer")[0];
let selections = $("#primary-mouse-binding select");
selections.each((i, option) => {
cornerstoneTools.setToolActiveForElement(element, option.value, {
mouseButtonMask: parseInt(option.dataset.button)
});
});
}
export function registerAltDicomInterface(e) {
// Called when control is pressed
// Set mousetools based upon the selected options
let element = document.getElementsByClassName("single-dicom-viewer")[0];
let selections = $("#secondary-mouse-binding select");
selections.each((i, option) => {
cornerstoneTools.setToolActiveForElement(element, option.value, {
mouseButtonMask: parseInt(option.dataset.button)
});
});
}
function changeMouseBinding(e, db) {
let select = e.currentTarget;
let button = select.dataset.button;
let mode = select.dataset.mode;
let tool = select.value;
// Directly activate primary tools (secondary will be activated when modifier
// key is pressed
if (mode == "0") {
let element = document.getElementsByClassName("single-dicom-viewer")[0];
cornerstoneTools.setToolActiveForElement(element, tool, { mouseButtonMask: parseInt(button) });
}
dicom_settings_db.mouse_bindings.put({ button: button, mode: mode, tool: tool });
//db.mouse_bindings.put({button: button, mode: mode, tool: tool}).then(loadPrimaryDicomInterface());
}
export function selectThumb(new_index) {
console.log("select thumb new index", new_index);
// There must be a better way to do this...
let dicom_element = document.getElementsByClassName("single-dicom-viewer")[0];
if (dicom_element == null) {
return;
}
let c = cornerstone.getEnabledElement(dicom_element);
// let current_index =
// c.toolStateManager.toolState.stack.data[0].currentImageIdIndex;
c.toolStateManager.toolState.stack.data[0].currentImageIdIndex = new_index;
let id = c.toolStateManager.toolState.stack.data[0].imageIds[new_index];
console.log("select thumb id", id);
console.log("select thumb el", dicom_element);
cornerstone.loadImage(id).then(b => {
console.log("b", b);
cornerstone.displayImage(dicom_element, b);
});
//c = cornerstone.getEnabledElement(dicom_element)
}
function urltoFile(url, filename, mimeType) {
return fetch(url)
.then(function (res) {
return res.arrayBuffer();
})
.then(function (buf) {
return new File([buf], filename, { type: mimeType });
});
}
function dumpDataSet(dataSet)
{
$('span[data-dicom]').each(function(index, value)
{
var attr = $(value).attr('data-dicom');
var element = dataSet.elements[attr];
var text = "";
if(element !== undefined)
{
var str = dataSet.string(attr);
if(str !== undefined) {
text = str;
}
}
$(value).text(text);
});
$('span[data-dicomUint]').each(function(index, value)
{
var attr = $(value).attr('data-dicomUint');
var element = dataSet.elements[attr];
var text = "";
if(element !== undefined)
{
if(element.length === 2)
{
text += dataSet.uint16(attr);
}
else if(element.length === 4)
{
text += dataSet.uint32(attr);
}
}
$(value).text(text);
});
}