Skip to content

Commit

Permalink
feat: add reference lines tool (#292)
Browse files Browse the repository at this point in the history
* initial commit for setting up

* feat: add annotation display Tool

* fix: reference lines render for parallel viewports

* remove window assignment

* apply review

* remove stream for lerna

* fix: reference lines for edge cases

* fix types

* fix naming

* try to fix the api extractor
  • Loading branch information
sedghi authored Nov 11, 2022
1 parent 18aa4d8 commit c56df91
Show file tree
Hide file tree
Showing 11 changed files with 806 additions and 4 deletions.
6 changes: 5 additions & 1 deletion common/reviews/api/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@ function getTargetVolumeAndSpacingInNormalDir(viewport: IVolumeViewport, camera:
spacingInNormalDirection: number;
};

// @public (undocumented)
function getViewportImageCornersInWorld(viewport: IStackViewport | IVolumeViewport): Point3[];

// @public (undocumented)
function getViewportsWithImageURI(imageURI: string, renderingEngineId?: string): Array<Viewport_2>;

Expand Down Expand Up @@ -1961,7 +1964,8 @@ declare namespace utilities {
isImageActor,
getViewportsWithImageURI,
calculateViewportsSpatialRegistration,
spatialRegistrationMetadataProvider
spatialRegistrationMetadataProvider,
getViewportImageCornersInWorld
}
}
export { utilities }
Expand Down
48 changes: 47 additions & 1 deletion common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3590,6 +3590,51 @@ export class RectangleScissorsTool extends BaseTool {
static toolName: any;
}

// @public (undocumented)
interface ReferenceLineAnnotation extends Annotation {
// (undocumented)
data: {
handles: {
points: Types_2.Point3[];
};
};
}

// @public (undocumented)
export class ReferenceLinesTool extends AnnotationDisplayTool {
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
// (undocumented)
editData: {
renderingEngine: any;
sourceViewport: any;
annotation: ReferenceLineAnnotation;
} | null;
// (undocumented)
_init: () => void;
// (undocumented)
isDrawing: boolean;
// (undocumented)
isHandleOutsideImage: boolean;
// (undocumented)
isParallel(vec1: Types_2.Point3, vec2: Types_2.Point3): boolean;
// (undocumented)
isPerpendicular: (vec1: Types_2.Point3, vec2: Types_2.Point3) => boolean;
// (undocumented)
mouseDragCallback: any;
// (undocumented)
onCameraModified: (evt: Types_2.EventTypes.CameraModifiedEvent) => void;
// (undocumented)
onSetToolEnabled: () => void;
// (undocumented)
renderAnnotation: (enabledElement: Types_2.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
// (undocumented)
_throttledCalculateCachedStats: any;
// (undocumented)
static toolName: any;
// (undocumented)
touchDragCallback: any;
}

// @public (undocumented)
function registerCursor(toolName: string, iconContent: string, viewBox: {
x: number;
Expand Down Expand Up @@ -4287,7 +4332,8 @@ declare namespace ToolSpecificAnnotationTypes {
RectangleROIStartEndThresholdAnnotation,
PlanarFreehandROIAnnotation,
ArrowAnnotation,
AngleAnnotation
AngleAnnotation,
ReferenceLineAnnotation
}
}

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
"yarn": ">=1.19.1"
},
"scripts": {
"api-check": "lerna run api-check --stream",
"api-check": "lerna run --concurrency 1 api-check ",
"build": "lerna run build --stream",
"build:umd": "lerna run build:umd --stream",
"build:update-api": "lerna run build:update-api --stream",
"build:update-api": "lerna run --concurrency 1 build:update-api",
"example": "node ./utils/ExampleRunner/example-runner-cli.js",
"all-examples": "node ./utils/ExampleRunner/build-all-examples-cli.js --fromRoot",
"build-all-examples": "node ./utils/ExampleRunner/build-all-examples-cli.js --build --fromRoot",
"serve-static-examples": "npx serve .static-examples",
"build:all": "lerna run build:all",
"dev": "yarn run all-examples",
"docs": "lerna run docs",
"docs:watch": "lerna run docs:watch",
Expand Down
102 changes: 102 additions & 0 deletions packages/core/src/utilities/getViewportImageCornersInWorld.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
IImageData,
IStackViewport,
IVolumeViewport,
Point2,
Point3,
} from '../types';

/**
* Given a viewport, return the corners of the image in the viewport in world coordinates.
* Note that this is different than the corners of the canvas in world coordinates since
* an image can be zoomed out and the canvas can be larger than the image; hence, the
* corners of the canvas in world coordinates will be outside the image.
*
* @param viewport - The viewport to get the corners of.
* @returns The corners of the image in the viewport in world coordinates.
*/
export default function getViewportImageCornersInWorld(
viewport: IStackViewport | IVolumeViewport
): Point3[] {
const { imageData, dimensions } = viewport.getImageData() as IImageData;
const { canvas } = viewport;

const topLeftCanvas: Point2 = [0, 0];
const topRightCanvas: Point2 = [canvas.width, 0];
const bottomRightCanvas: Point2 = [canvas.width, canvas.height];
const bottomLeftCanvas: Point2 = [0, canvas.height];

const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
const topRightWorld = viewport.canvasToWorld(topRightCanvas);
const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
const bottomLeftWorld = viewport.canvasToWorld(bottomLeftCanvas);

const topLeftImage = imageData.worldToIndex(topLeftWorld);
const topRightImage = imageData.worldToIndex(topRightWorld);
const bottomRightImage = imageData.worldToIndex(bottomRightWorld);
const bottomLeftImage = imageData.worldToIndex(bottomLeftWorld);

return _getStackViewportImageCorners({
dimensions,
imageData,
topLeftImage,
topRightImage,
bottomRightImage,
bottomLeftImage,
topLeftWorld,
topRightWorld,
bottomRightWorld,
bottomLeftWorld,
});
}

function _getStackViewportImageCorners({
dimensions,
imageData,
topLeftImage,
topRightImage,
bottomRightImage,
bottomLeftImage,
topLeftWorld,
topRightWorld,
bottomRightWorld,
bottomLeftWorld,
}) {
const topLeftImageWorld = _isInBounds(topLeftImage, dimensions)
? topLeftWorld
: (imageData.indexToWorld([0, 0, 0]) as Point3);

const topRightImageWorld = _isInBounds(topRightImage, dimensions)
? topRightWorld
: (imageData.indexToWorld([dimensions[0] - 1, 0, 0]) as Point3);

const bottomRightImageWorld = _isInBounds(bottomRightImage, dimensions)
? bottomRightWorld
: (imageData.indexToWorld([
dimensions[0] - 1,
dimensions[1] - 1,
0,
]) as Point3);

const bottomLeftImageWorld = _isInBounds(bottomLeftImage, dimensions)
? bottomLeftWorld
: (imageData.indexToWorld([0, dimensions[1] - 1, 0]) as Point3);

return [
topLeftImageWorld,
topRightImageWorld,
bottomLeftImageWorld,
bottomRightImageWorld,
];
}

function _isInBounds(imageCoord, dimensions) {
return (
imageCoord[0] > 0 ||
imageCoord[0] < dimensions[0] - 1 ||
imageCoord[1] > 0 ||
imageCoord[1] < dimensions[1] - 1 ||
imageCoord[2] > 0 ||
imageCoord[2] < dimensions[2] - 1
);
}
2 changes: 2 additions & 0 deletions packages/core/src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import isImageActor from './isImageActor';
import getViewportsWithImageURI from './getViewportsWithImageURI';
import calculateViewportsSpatialRegistration from './calculateViewportsSpatialRegistration';
import spatialRegistrationMetadataProvider from './spatialRegistrationMetadataProvider';
import getViewportImageCornersInWorld from './getViewportImageCornersInWorld';

// name spaces
import * as planar from './planar';
Expand Down Expand Up @@ -68,4 +69,5 @@ export {
getViewportsWithImageURI,
calculateViewportsSpatialRegistration,
spatialRegistrationMetadataProvider,
getViewportImageCornersInWorld,
};
Loading

0 comments on commit c56df91

Please sign in to comment.