diglettk
Version:
A medical imaging toolkit, built on top of vtk.js
323 lines (294 loc) • 10 kB
HTML
<html class="h-100 overflow-hidden">
<head>
<meta charset="UTF-8" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/vs2015.min.css"
/>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js"></script>
<script>
hljs.highlightAll();
</script>
<title>DigletTK - MPR example</title>
</head>
<body class="h-100" style="background-color: #000000; color: #ffffff">
<div class="row h-100">
<!-- quadview -->
<div class="col-8 h-100">
<div class="row h-100">
<div
id="viewer-1"
class="col-4 h-100"
style="background-color: rgb(0, 0, 0)"
></div>
<div
id="viewer-2"
class="col-4 h-100"
style="background-color: rgb(0, 0, 0)"
></div>
<div
id="viewer-3"
class="col-4 h-100"
style="background-color: rgb(0, 0, 0)"
></div>
</div>
</div>
<!-- code preview -->
<div class="col-4 h-100">
<pre class="h-100">
Press QWERTY to rotate 10 deg on x/y axis on each view
Press ASDFGH to increase MIP thickness 10 px on x/y axis on each view
+ shift to decrease rotation / thickness
Press any other key to reset
<div class="btn-group" role="group" aria-label="Basic radio toggle button group">
<input type="radio" class="btn-check" name="btnradio" id="btn-level" autocomplete="off" checked onclick="updateTool('level')">
<label class="btn btn-outline-danger" for="btn-level">Level</label>
<input type="radio" class="btn-check" name="btnradio" id="btn-cross" autocomplete="off" onclick="updateTool('crosshair')">
<label class="btn btn-outline-danger" for="btn-cross">Crosshair</label>
<input type="radio" class="btn-check" name="btnradio" id="btn-pan" autocomplete="off" onclick="updateTool('pan')">
<label class="btn btn-outline-danger" for="btn-pan">Pan</label>
<input type="radio" class="btn-check" name="btnradio" id="btn-zoom" autocomplete="off" onclick="updateTool('zoom')">
<label class="btn btn-outline-danger" for="btn-zoom">Zoom</label>
</div>
<code class="javascript">
// =====================
// Define viewports ====
// =====================
const targetElements = {
top: {
element: document.getElementById("viewer-1"),
key: "top"
},
left: {
element: document.getElementById("viewer-2"),
key: "left"
},
front: {
element: document.getElementById("viewer-3"),
key: "front"
}
};
// =============================
// Build volume and load mpr ===
// =============================
/**
* Header must be in the form:
* {
* volume: {
* rows: number,
* cols: number.
* imageIds: [],
* imagePosition: [number, number, number],
* sliceThickness: number
* }
* }
*
* Data is a TypedArray
*/
// build vtk volume with larvitar
const image = diglettk.buildVtkVolume(header, data);
// run mpr
mpr = new diglettk.MPRManager(targetElements);
// get initial state obj
state = mpr.getInitialState();
console.log("state", state);
// set image
mpr.setImage(state, image);
// set active tool ("level" or "crosshair")
mpr.setTool("level", state);
// add keyoboard events to interact with mpr
addEvents(mpr, state);
</code>
</pre>
</div>
</div>
<script src="./diglettk.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script src="./larvitar.js"></script>
<script>
let mpr, state;
// =====================
// Define viewports ====
// =====================
const targetElements = {
top: {
element: document.getElementById("viewer-1"),
key: "top"
},
left: {
element: document.getElementById("viewer-2"),
key: "left"
},
front: {
element: document.getElementById("viewer-3"),
key: "front"
}
};
// =====================
// Fetch data ==========
// =====================
let header, data;
fetch("./demo/header.json")
.then(data => data.json())
.then(_header => {
header = _header;
console.log("header", header);
fetch("./demo/data.json")
.then(data => data.arrayBuffer())
.then(buffer => {
console.log(buffer);
data = new Int16Array(buffer);
console.log("data", data);
loadScene();
});
});
// =============================
// Build volume and load mpr ===
// =============================
function loadScene() {
// build vtk volume with larvitar
const image = diglettk.buildVtkVolume(header, data);
// run mpr
mpr = new diglettk.MPRManager(targetElements);
// get initial state obj
state = mpr.getInitialState();
console.log("state", state);
// set image
mpr.setImage(state, image);
// set active tool ("level" or "crosshair")
mpr.setTool("level", state);
// add keyoboard events to interact with mpr
addEvents(mpr, state);
}
// =======================================
// TESTING EVENTS ========================
// Q,W,E,R,T,Y rotate 10 deg clockwise ===
// + shift rotate 10 deg ccw =============
// any other key reset views =============
// =======================================
function addEvents(mpr, global_data) {
let stateUI = {
top: { angle: { x: 0, y: 0 }, dist: 0 },
left: { angle: { x: 0, y: 0 }, dist: 0 },
front: { angle: { x: 0, y: 0 }, dist: 0 }
};
document.addEventListener("keypress", e => {
let key, axis, action;
switch (e.code) {
case "KeyQ":
action = "rotate";
key = "top";
axis = "x";
break;
case "KeyW":
action = "rotate";
key = "top";
axis = "y";
break;
case "KeyE":
action = "rotate";
key = "left";
axis = "x";
break;
case "KeyR":
action = "rotate";
key = "left";
axis = "y";
break;
case "KeyT":
action = "rotate";
key = "front";
axis = "x";
break;
case "KeyY":
action = "rotate";
key = "front";
axis = "y";
break;
case "KeyA":
action = "thickness";
key = "top";
axis = "x";
break;
case "KeyS":
action = "thickness";
key = "top";
axis = "y";
break;
case "KeyD":
action = "thickness";
key = "left";
axis = "x";
break;
case "KeyF":
action = "thickness";
key = "left";
axis = "y";
break;
case "KeyG":
action = "thickness";
key = "front";
axis = "x";
break;
case "KeyH":
action = "thickness";
key = "front";
axis = "y";
break;
}
if (key && axis && action == "rotate") {
// MOVE BY +/- 10 deg
let oldAngle = stateUI[key].angle[axis];
let angle = e.shiftKey ? oldAngle - 10 : oldAngle + 10;
console.log(key, axis, oldAngle, angle);
mpr.onRotate(key, axis, angle, global_data);
stateUI[key].angle[axis] = angle;
} else if (key && axis && action == "thickness") {
// MOVE BY +/- 10 px
let oldDist = stateUI[key].dist;
let dist = e.shiftKey ? oldDist - 10 : oldDist + 10;
console.log(key, axis, oldDist, dist);
mpr.onThickness(key, axis, dist, global_data);
stateUI[key].dist = dist;
} else {
// RESET
mpr.onRotate("top", "x", 0, global_data);
mpr.onThickness("top", "x", 0, global_data);
mpr.onRotate("top", "y", 0, global_data);
mpr.onThickness("top", "y", 0, global_data);
mpr.onRotate("left", "x", 0, global_data);
mpr.onThickness("left", "x", 0, global_data);
mpr.onRotate("left", "y", 0, global_data);
mpr.onThickness("left", "y", 0, global_data);
mpr.onRotate("front", "x", 0, global_data);
mpr.onThickness("front", "x", 0, global_data);
mpr.onRotate("front", "y", 0, global_data);
mpr.onThickness("front", "y", 0, global_data);
stateUI.top.angle.x = 0;
stateUI.top.angle.y = 0;
stateUI.top.dist = 0;
stateUI.left.angle.x = 0;
stateUI.left.angle.y = 0;
stateUI.left.dist = 0;
stateUI.front.angle.x = 0;
stateUI.front.angle.y = 0;
stateUI.front.dist = 0;
}
});
}
// =======================
// switch tool ===========
// =======================
function updateTool(tool) {
mpr.setTool(tool, state);
}
</script>
</body>
</html>