@cornerstonejs/core
Version:
Cornerstone3D Core
122 lines (121 loc) • 4.57 kB
JavaScript
import { computeECGChannelLayouts, getVisibleECGChannels, } from '../../../utilities/ECGUtilities.js';
import ResolvedViewportView from '../ResolvedViewportView.js';
import { getAnchorWorldForCanvasPoint, getAnchorWorldForPan, resolveECGCanvasMapping, getPanForECGCanvasMapping, } from './ecgViewportCamera.js';
class ECGResolvedView extends ResolvedViewportView {
get zoom() {
return Math.max(this.state.viewState.scale ?? 1, 0.001);
}
get pan() {
return getPanForECGCanvasMapping(this.getCanvasMapping());
}
canvasToWorld(canvasPos) {
const mapping = this.getCanvasMapping();
const channelLayouts = this.getChannelLayouts();
const subCanvasPos = [
(canvasPos[0] - mapping.xOffset) / mapping.effectiveRatio,
(canvasPos[1] - mapping.yOffset) / mapping.effectiveRatio,
];
let z = 0;
for (let index = 0; index < channelLayouts.length; index++) {
const channelLayout = channelLayouts[index];
if (subCanvasPos[1] <= channelLayout.yOffset ||
index === channelLayouts.length - 1) {
z = index;
break;
}
}
const channelLayout = channelLayouts[z];
return [
Math.max(0, Math.min(this.state.waveform.numberOfSamples - 1, (subCanvasPos[0] * this.state.waveform.numberOfSamples) /
this.state.metrics.ecgWidth)),
(channelLayout.baseline - subCanvasPos[1]) /
this.state.metrics.channelScale,
z,
];
}
worldToCanvas(worldPos) {
const mapping = this.getCanvasMapping();
const channelLayouts = this.getChannelLayouts();
const z = Math.round(worldPos[2]);
if (z < 0 || z >= channelLayouts.length) {
return [0, 0];
}
return [
(worldPos[0] / this.state.waveform.numberOfSamples) *
this.state.metrics.ecgWidth *
mapping.effectiveRatio +
mapping.xOffset,
(channelLayouts[z].baseline -
worldPos[1] * this.state.metrics.channelScale) *
mapping.effectiveRatio +
mapping.yOffset,
];
}
getFrameOfReferenceUID() {
return this.state.frameOfReferenceUID;
}
withZoom(zoom, canvasPoint) {
const nextZoom = Math.max(zoom, 0.001);
if (!canvasPoint) {
return this.cloneWithViewState({
...this.state.viewState,
scale: nextZoom,
scaleMode: 'fit',
});
}
return this.cloneWithViewState({
...this.state.viewState,
anchorWorld: getAnchorWorldForCanvasPoint(canvasPoint, this.getCanvasMapping()),
anchorCanvas: [
canvasPoint[0] / Math.max(this.state.canvas.clientWidth, 1),
canvasPoint[1] / Math.max(this.state.canvas.clientHeight, 1),
],
scale: nextZoom,
scaleMode: 'fit',
});
}
withPan(pan) {
return this.cloneWithViewState({
...this.state.viewState,
anchorWorld: getAnchorWorldForPan([pan[0], pan[1]], this.getCanvasMapping()),
});
}
buildICamera() {
const mapping = this.getCanvasMapping();
const canvasCenter = [
this.state.canvas.clientWidth / 2,
this.state.canvas.clientHeight / 2,
];
return {
parallelProjection: true,
focalPoint: this.canvasToWorld(canvasCenter),
position: [0, 0, 0],
viewUp: [0, -1, 0],
parallelScale: this.state.canvas.clientHeight /
2 /
Math.max(mapping.effectiveRatio, 0.001),
viewPlaneNormal: [0, 0, 1],
};
}
getCanvasMapping() {
this.cachedCanvasMapping ||= resolveECGCanvasMapping({
canvas: this.state.canvas,
camera: this.state.viewState,
metrics: this.state.metrics,
});
return this.cachedCanvasMapping;
}
getChannelLayouts() {
return computeECGChannelLayouts({
visibleChannels: getVisibleECGChannels(this.state.waveform.channels, this.state.dataPresentation?.visibleChannels),
channelScale: this.state.metrics.channelScale,
});
}
cloneWithViewState(viewState) {
return new ECGResolvedView({
...this.state,
viewState,
});
}
}
export default ECGResolvedView;