* feat: initial RT support * make the segmentation service work with representation data * feat: make segmentation service work with representations * fix rtss vis * fix: rt hydration * fix the rendering of rt names * fix imports * refactor: Modify status and click handling for hydration of RTStructures Modify status and click handling for hydration of RTStructures by renaming `onPillClick` to `onStatusClick` in `OHIFCornerstoneRTViewport.tsx` and `_getStatusComponent.tsx` files. Also, update initial segmentation configurations in `PanelSegmentation.tsx` and simplify configuration changes and values for segmentation service in `SegmentationService.ts`. Finally, remove console debug in `CornerstoneViewportService.ts`. * wip for highlighting contours * refactor rt displayset code * review code update * update cornerstone dependencies * refactor: Update license year, version number, and minor code cleanup This commit updates the license year in several files, updates the version number in package.json, and contains minor code cleanup in two files. * add bulkdataURI retrieve for RT * fix package version * apply review comments * apply review comments * apply review comments * feat(panels): refactor and streamline segmentation configuration and inputs Rewrote state hooks and streamlined the configuration input for `PanelSegmentation` to be more verbose and reusable. Included several new input types, including the `InputRange` component which now shows a fixed floating value based on the step provided. The `SegmentationConfig` component now works with dynamic values controlled by `initialConfig`. These changes should improve function usability and make the code more maintainable going forward. * fix various bugs * fix contour delete by upgrade cs3d version * feat(viewport, inputNumber, segmentationConfig, orthanc): Implement minimum and maximum values for input number components, and useBulkDataURI for Orthanc configuration. Compare measurement view planes with absolute viewport view planes in Cornerstone viewport. * update yarn lock
142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
import guid from '../utils/guid.js';
|
|
import { Vector3 } from 'cornerstone-math';
|
|
|
|
type Attributes = Record<string, unknown>;
|
|
type Image = {
|
|
StudyInstanceUID?: string;
|
|
getData(): {
|
|
metadata: {
|
|
ImagePositionPatient: number[];
|
|
ImageOrientationPatient: number[];
|
|
};
|
|
};
|
|
};
|
|
|
|
/**
|
|
* This class defines an ImageSet object which will be used across the viewer. This object represents
|
|
* a list of images that are associated by any arbitrary criteria being thus content agnostic. Besides the
|
|
* main attributes (images and uid) it allows additional attributes to be appended to it (currently
|
|
* indiscriminately, but this should be changed).
|
|
*/
|
|
class ImageSet {
|
|
images: Image[];
|
|
uid: string;
|
|
instances: Image[];
|
|
instance?: Image;
|
|
StudyInstanceUID?: string;
|
|
|
|
constructor(images: Image[]) {
|
|
if (!Array.isArray(images)) {
|
|
throw new Error('ImageSet expects an array of images');
|
|
}
|
|
|
|
// @property "images"
|
|
Object.defineProperty(this, 'images', {
|
|
enumerable: false,
|
|
configurable: false,
|
|
writable: false,
|
|
value: images,
|
|
});
|
|
|
|
// @property "uid"
|
|
Object.defineProperty(this, 'uid', {
|
|
enumerable: false,
|
|
configurable: false,
|
|
writable: false,
|
|
value: guid(), // Unique ID of the instance
|
|
});
|
|
|
|
this.instances = images;
|
|
this.instance = images[0];
|
|
this.StudyInstanceUID = this.instance?.StudyInstanceUID;
|
|
}
|
|
|
|
load: () => Promise<void>;
|
|
|
|
getUID(): string {
|
|
return this.uid;
|
|
}
|
|
|
|
setAttribute(attribute: string, value: unknown): void {
|
|
this[attribute] = value;
|
|
}
|
|
|
|
getAttribute(attribute: string): unknown {
|
|
return this[attribute];
|
|
}
|
|
|
|
setAttributes(attributes: Attributes): void {
|
|
if (typeof attributes === 'object' && attributes !== null) {
|
|
for (const [attribute, value] of Object.entries(attributes)) {
|
|
this[attribute] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
getNumImages = (): number => this.images.length;
|
|
|
|
getImage(index: number): Image {
|
|
return this.images[index];
|
|
}
|
|
|
|
sortBy(sortingCallback: (a: Image, b: Image) => number): Image[] {
|
|
return this.images.sort(sortingCallback);
|
|
}
|
|
|
|
sortByImagePositionPatient(): void {
|
|
const images = this.images;
|
|
const referenceImagePositionPatient = _getImagePositionPatient(images[0]);
|
|
|
|
const refIppVec = new Vector3(
|
|
referenceImagePositionPatient[0],
|
|
referenceImagePositionPatient[1],
|
|
referenceImagePositionPatient[2]
|
|
);
|
|
|
|
const ImageOrientationPatient = _getImageOrientationPatient(images[0]);
|
|
|
|
const scanAxisNormal = new Vector3(
|
|
ImageOrientationPatient[0],
|
|
ImageOrientationPatient[1],
|
|
ImageOrientationPatient[2]
|
|
).cross(
|
|
new Vector3(
|
|
ImageOrientationPatient[3],
|
|
ImageOrientationPatient[4],
|
|
ImageOrientationPatient[5]
|
|
)
|
|
);
|
|
|
|
const distanceImagePairs = images.map(function(image: Image) {
|
|
const ippVec = new Vector3(..._getImagePositionPatient(image));
|
|
const positionVector = refIppVec.clone().sub(ippVec);
|
|
const distance = positionVector.dot(scanAxisNormal);
|
|
|
|
return {
|
|
distance,
|
|
image,
|
|
};
|
|
});
|
|
|
|
distanceImagePairs.sort(function(a, b) {
|
|
return b.distance - a.distance;
|
|
});
|
|
|
|
const sortedImages = distanceImagePairs.map(a => a.image);
|
|
|
|
images.sort(function(a, b) {
|
|
return sortedImages.indexOf(a) - sortedImages.indexOf(b);
|
|
});
|
|
}
|
|
}
|
|
|
|
function _getImagePositionPatient(image) {
|
|
return image.getData().metadata.ImagePositionPatient;
|
|
}
|
|
|
|
function _getImageOrientationPatient(image) {
|
|
return image.getData().metadata.ImageOrientationPatient;
|
|
}
|
|
|
|
export default ImageSet;
|