Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 114 additions & 16 deletions dist/boostlet.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/boostlet.min.js.map

Large diffs are not rendered by default.

68 changes: 39 additions & 29 deletions examples/PixelExplorer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
CATEGORY = "Visualization"

if (typeof window._PixelExplorerLoaded === 'undefined') {
window._PixelExplorerLoaded = true;

(function() {

const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://boostlet.org/dist/boostlet.min.js";

script.onload = run;
script.onload = function() {
/// wait for nv and volumes before calling init
// calling init too early means niivue won't be detected yet
var poll = setInterval(function() {
if (window.nv && nv.volumes && nv.volumes.length > 0) {
clearInterval(poll);
run();
}
}, 300);
};
document.head.appendChild(script);

const HALF = 4;
Expand All @@ -18,35 +31,32 @@ function run() {
}

const nv = Boostlet.framework.instance;
setup(nv);
}

if (!nv.volumes.length) {
alert('No volume loaded yet!');
return;
}

// Create the panel once
function setup(nv) {
// create the panel once
plot();

// Save the existing onLocationChange so we don't clobber it
// save the existing onLocationChange so we don't clobber it
const existingOnLocationChange = nv.onLocationChange;

// Hook into location change — fires on every crosshair move / slice change
// hook into location change — fires on every crosshair move / slice change
nv.onLocationChange = function(loc) {
// call the original if it existed
if (existingOnLocationChange) existingOnLocationChange(loc);

if (!nv.volumes.length) return;
if (!nv.volumes || !nv.volumes.length) return;

const v = [Math.round(loc.vox[0]), Math.round(loc.vox[1]), Math.round(loc.vox[2])]; // Cursor position in voxel coordinates.
const v = [Math.round(loc.vox[0]), Math.round(loc.vox[1]), Math.round(loc.vox[2])];

const start = [v[0] - HALF, v[1] - HALF, v[2] - HALF];
const end = [v[0] + HALF, v[1] + HALF, v[2] + HALF];

// collapses the slice plane axis to a single voxel
// axial(0) Z=2, coronal(1) Y=1, sagittal(2) X=0 so that you don't get a 3D chunk of data
const fixedAxis = nv.opts.sliceType === nv.sliceTypeSagittal ? 0
: nv.opts.sliceType === nv.sliceTypeCoronal ? 1 : 2;
start[fixedAxis] = end[fixedAxis] = Math.round((start[fixedAxis] + end[fixedAxis]) / 2); // Flattens axis, finds midpoint
// collapse the axis perpendicular to the current slice to a single voxel
// 2 = sagittal (x axis), 1 = coronal (y axis), default axial (z axis)
const fixedAxis = nv.opts.sliceType === 2 ? 0
: nv.opts.sliceType === 1 ? 1 : 2;
start[fixedAxis] = end[fixedAxis] = Math.round((start[fixedAxis] + end[fixedAxis]) / 2);

const { data, dims } = Boostlet.get_subvolume(start, end);
if (data && dims) {
Expand All @@ -60,7 +70,7 @@ function run() {
function drawMatrix(data, dims, mc) {
const nv = Boostlet.framework.instance;
const { cal_min, cal_max } = nv.volumes[0];
const range = cal_max - cal_min || 1; // range between display values.
const range = cal_max - cal_min || 1;
const ctx = mc.getContext('2d');

// apply nifti slope/intercept to convert raw values to display values.
Expand All @@ -70,14 +80,13 @@ function drawMatrix(data, dims, mc) {
const inter = nv.volumes[0].hdr.scl_inter || 0;

// getVolumeData returns dims as X Y Z and the collapsed axis has size 1
// the first non 1 dim is cols second is rows
// the first non-1 dim is cols, second is rows
const [cols, rows] = dims.filter(d => d > 1);
if (!cols || !rows) return;

const cell = Math.floor(Math.min(mc.width / cols, mc.height / rows)); // picks between max cell width and max cell height. picks the smallest
const cell = Math.floor(Math.min(mc.width / cols, mc.height / rows));
if (cell < 1) return;

// Figures out whether to show a label or not. needs to be changed IMO
const showLabels = cell >= 8;
if (showLabels) {
ctx.font = `${Math.max(Math.floor(cell * 0.28), 6)}px monospace`;
Expand All @@ -87,13 +96,11 @@ function drawMatrix(data, dims, mc) {

ctx.clearRect(0, 0, mc.width, mc.height);

// Loops through every voxel in the region, draws a grey square for each one based on how bright it is. If the cells are
// big enough, it prints out raw number on top.
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const raw = data[c + (rows - 1 - r) * cols];
const val = raw * slope + inter; // convert raw to display value
const norm = (val - cal_min) / range; // Gives a range from 0 - 1, gets normalized to 0-255 in the line below
const val = raw * slope + inter;
const norm = (val - cal_min) / range;
const g = Math.round(norm * 255);

ctx.fillStyle = `rgb(${g},${g},${g})`;
Expand All @@ -108,7 +115,7 @@ function drawMatrix(data, dims, mc) {
}

function plot() {
// Remove existing panel if re ran
// remove existing panel if re-run
const existing = document.getElementById('PixelExplorerDiv');
if (existing) existing.remove();

Expand All @@ -124,7 +131,7 @@ function plot() {
mc.style.cssText = 'display:block; width:400px; height:400px;';
container.appendChild(mc);

// Drag logic
// drag logic
let startX, startY, startLeft, startTop;
container.addEventListener('mousedown', function(e) {
startX = e.clientX;
Expand All @@ -143,4 +150,7 @@ function plot() {
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
}
}

})();
}
Loading
Loading