UNPKG

ami.js

Version:

<p align="center"> <img src="https://cloud.githubusercontent.com/assets/214063/23213764/78ade038-f90c-11e6-8208-4fcade5f3832.png" width="60%"> </p>

285 lines (253 loc) 7.21 kB
const URL = require('url'); import Validators from './core.validators'; import {Matrix4, Vector3} from 'three'; /** * General purpose functions. * * @module core/utils */ export default class CoreUtils { /** * Generate a bouding box object. * @param {Vector3} center - Center of the box. * @param {Vector3} halfDimensions - Half Dimensions of the box. * @return {Object} The bounding box object. {Object.min} is a {Vector3} * containing the min bounds. {Object.max} is a {Vector3} containing the * max bounds. * @return {boolean} False input NOT valid. * @example * // Returns * //{ min: { x : 0, y : 0, z : 0 }, * // max: { x : 2, y : 4, z : 6 } * //} * VJS.Core.Utils.bbox( * new Vector3(1, 2, 3), new Vector3(1, 2, 3)); * * //Returns false * VJS.Core.Utils.bbox(new Vector3(), new Matrix4()); * */ static bbox(center, halfDimensions) { // make sure we have valid inputs if (!(Validators.vector3(center) && Validators.vector3(halfDimensions))) { window.console.log('Invalid center or plane halfDimensions.'); return false; } // make sure half dimensions are >= 0 if (!(halfDimensions.x >= 0 && halfDimensions.y >= 0 && halfDimensions.z >= 0)) { window.console.log('halfDimensions must be >= 0.'); window.console.log(halfDimensions); return false; } // min/max bound let min = center.clone().sub(halfDimensions); let max = center.clone().add(halfDimensions); return { min, max, }; } /** * Find min/max values in an array * @param {Array} data * @return {Array} */ static minMax(data = []) { let minMax = [65535, -32768]; let numPixels = data.length; for (let index = 0; index < numPixels; index++) { let spv = data[index]; minMax[0] = Math.min(minMax[0], spv); minMax[1] = Math.max(minMax[1], spv); } return minMax; } /** * Check HTMLElement * @param {HTMLElement} obj * @return {boolean} */ static isElement(obj) { try { // Using W3 DOM2 (works for FF, Opera and Chrom) return obj instanceof HTMLElement; } catch (e) { // Browsers not supporting W3 DOM2 don't have HTMLElement and // an exception is thrown and we end up here. Testing some // properties that all elements have. (works on IE7) return (typeof obj === 'object') && (obj.nodeType === 1) && (typeof obj.style === 'object') && (typeof obj.ownerDocument === 'object'); } } /** * Check string * @param {String} str * @return {Boolean} */ static isString(str) { return typeof str === 'string' || str instanceof String; } /** * Parse url and find out the extension of the exam file. * * @param {*} url - The url to be parsed. * The query string can contain some "special" parameters that can be used to ease the parsing process * when the url doesn't match the exam file name on the filesystem: * - filename: the name of the exam file * - contentType: the mime type of the exam file. Currently only "application/dicom" is recognized, nifti files don't have a standard mime type. * For example: * http://<hostname>/getExam?id=100&filename=myexam%2Enii%2Egz * http://<hostname>/getExam?id=100&contentType=application%2Fdicom * * @return {Object} */ static parseUrl(url) { const data = {}; data.filename = ''; data.extension = ''; data.pathname = ''; data.query = ''; let parsedUrl = URL.parse(url); data.pathname = parsedUrl.pathname; data.query = parsedUrl.query; if (data.query) { // Find "filename" parameter value, if present data.filename = data.query.split('&').reduce((acc, fieldval) => { let fvPair = fieldval.split('='); if (fvPair.length > 0 && fvPair[0] == 'filename') { acc = fvPair[1]; } return acc; }); } // get file name if (!data.filename) { data.filename = data.pathname.split('/').pop(); } // find extension let splittedName = data.filename.split('.'); if (splittedName.length <= 1) { data.extension = 'dicom'; } else { data.extension = data.filename.split('.').pop(); } if (!isNaN(data.extension)) { data.extension = 'dicom'; } if (data.query && data.query.includes('contentType=application%2Fdicom')) { data.extension = 'dicom'; } return data; } /** * Compute IJK to LPS tranform. * http://nipy.org/nibabel/dicom/dicom_orientation.html * * @param {*} xCos * @param {*} yCos * @param {*} zCos * @param {*} spacing * @param {*} origin * @param {*} registrationMatrix * * @return {*} */ static ijk2LPS( xCos, yCos, zCos, spacing, origin, registrationMatrix = new Matrix4()) { const ijk2LPS = new Matrix4(); ijk2LPS.set( xCos.x * spacing.y, yCos.x * spacing.x, zCos.x * spacing.z, origin.x, xCos.y * spacing.y, yCos.y * spacing.x, zCos.y * spacing.z, origin.y, xCos.z * spacing.y, yCos.z * spacing.x, zCos.z * spacing.z, origin.z, 0, 0, 0, 1); ijk2LPS.premultiply(registrationMatrix); return ijk2LPS; } /** * Compute AABB to LPS transform. * AABB: Axe Aligned Bounding Box. * * @param {*} xCos * @param {*} yCos * @param {*} zCos * @param {*} origin * * @return {*} */ static aabb2LPS( xCos, yCos, zCos, origin) { const aabb2LPS = new Matrix4(); aabb2LPS.set( xCos.x, yCos.x, zCos.x, origin.x, xCos.y, yCos.y, zCos.y, origin.y, xCos.z, yCos.z, zCos.z, origin.z, 0, 0, 0, 1); return aabb2LPS; } /** * Transform coordinates from world coordinate to data * * @param {*} lps2IJK * @param {*} worldCoordinates * * @return {*} */ static worldToData(lps2IJK, worldCoordinates) { let dataCoordinate = new Vector3() .copy(worldCoordinates) .applyMatrix4(lps2IJK); // same rounding in the shaders dataCoordinate.addScalar(0.5).floor(); return dataCoordinate; } /** * Get and set voxel value * * @param {*} stack * @param {*} coordinate * @param {*} value * @return {*} */ static value(stack, coordinate) { console.warn('value is deprecated, please use getPixelData instead'); this.getPixelData(stack, coordinate); } static getPixelData(stack, coordinate) { if (coordinate.z >= 0 && coordinate.z < stack._frame.length) { return stack._frame[coordinate.z]. getPixelData(coordinate.x, coordinate.y); } else { return null; } } static setPixelData(stack, coordinate, value) { if (coordinate.z >= 0 && coordinate.z < stack._frame.length) { stack._frame[coordinate.z]. setPixelData(coordinate.x, coordinate.y, value); } else { return null; } } /** * Apply slope/intercept to a value * * @param {*} value * @param {*} slope * @param {*} intercept * * @return {*} */ static rescaleSlopeIntercept(value, slope, intercept) { return value * slope + intercept; } }