Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature add cornerstone adapters #225

Merged
96 changes: 96 additions & 0 deletions src/adapters/Cornerstone/Angle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import MeasurementReport from "./MeasurementReport.js";
import TID300CobbAngle from "../../utilities/TID300/CobbAngle.js";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";

const ANGLE = "Angle";

class Angle {
constructor() {}

/**
* Generate TID300 measurement data for a plane angle measurement - use a CobbAngle, but label it as Angle
* @param MeasurementGroup
* @returns
*/
static getMeasurementData(MeasurementGroup) {
const {
defaultState,
NUMGroup,
SCOORDGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);

const state = {
...defaultState,
rAngle: NUMGroup.MeasuredValueSequence.NumericValue,
toolType: Angle.toolType,
handles: {
start: {},
middle: {},
end: {},
textBox: {
hasMoved: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
}
}
};

[
state.handles.start.x,
state.handles.start.y,
state.handles.middle.x,
state.handles.middle.y,
state.handles.middle.x,
state.handles.middle.y,
state.handles.end.x,
state.handles.end.y
] = SCOORDGroup.GraphicData;

return state;
}

static getTID300RepresentationArguments(tool) {
const { handles, finding, findingSites } = tool;
const point1 = handles.start;
const point2 = handles.middle;
const point3 = handles.middle;
const point4 = handles.end;
const rAngle = tool.rAngle;

const trackingIdentifierTextValue = "cornerstoneTools@^4.0.0:Angle";

return {
point1,
point2,
point3,
point4,
rAngle,
trackingIdentifierTextValue,
finding,
findingSites: findingSites || []
};
}
}

Angle.toolType = ANGLE;
Angle.utilityToolType = ANGLE;
Angle.TID300Representation = TID300CobbAngle;
Angle.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
if (!TrackingIdentifier.includes(":")) {
return false;
}

const [cornerstone4Tag, toolType] = TrackingIdentifier.split(":");

if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
return false;
}

return toolType === ANGLE;
};

MeasurementReport.registerTool(Angle);

export default Angle;
58 changes: 18 additions & 40 deletions src/adapters/Cornerstone/ArrowAnnotate.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,26 @@
import MeasurementReport from "./MeasurementReport.js";
import TID300Point from "../../utilities/TID300/Point.js";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";
import { toArray } from "../helpers.js";

const ARROW_ANNOTATE = "ArrowAnnotate";
const FINDING = "121071";
const FINDING_SITE = "G-C0E3";
const CORNERSTONEFREETEXT = "CORNERSTONEFREETEXT";

class ArrowAnnotate {
constructor() {}

// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(MeasurementGroup) {
const { ContentSequence } = MeasurementGroup;

const NUMGroup = toArray(ContentSequence).find(
group => group.ValueType === "NUM"
);

const SCOORDGroup = toArray(NUMGroup.ContentSequence).find(
group => group.ValueType === "SCOORD"
);

const findingGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === FINDING
);

const findingSiteGroups = toArray(ContentSequence).filter(
group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE
);
const {
defaultState,
SCOORDGroup,
findingGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);

const text = findingGroup.ConceptCodeSequence.CodeMeaning;

const { GraphicData } = SCOORDGroup;

const { ReferencedSOPSequence } = SCOORDGroup.ContentSequence;
const {
ReferencedSOPInstanceUID,
ReferencedFrameNumber
} = ReferencedSOPSequence;
const state = {
sopInstanceUid: ReferencedSOPInstanceUID,
frameIndex: ReferencedFrameNumber || 0,
...defaultState,
toolType: ArrowAnnotate.toolType,
active: false,
handles: {
Expand All @@ -52,11 +30,17 @@ class ArrowAnnotate {
highlight: true,
active: false
},
// TODO: How do we choose where the end goes?
// Just put it pointing from the bottom right for now?
// Use a generic offset if the stored data doesn't have the endpoint, otherwise
// use the actual endpoint.
end: {
x: GraphicData[0] + 20,
y: GraphicData[1] + 20,
x:
GraphicData.length == 4
? GraphicData[2]
: GraphicData[0] + 20,
y:
GraphicData.length == 4
? GraphicData[3]
: GraphicData[1] + 20,
highlight: true,
active: false
},
Expand All @@ -70,20 +54,14 @@ class ArrowAnnotate {
},
invalidated: true,
text,
visible: true,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
})
visible: true
};

return state;
}

static getTID300RepresentationArguments(tool) {
const points = [tool.handles.start];
const points = [tool.handles.start, tool.handles.end];

let { finding, findingSites } = tool;

Expand Down
99 changes: 99 additions & 0 deletions src/adapters/Cornerstone/CobbAngle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import MeasurementReport from "./MeasurementReport.js";
import TID300CobbAngle from "../../utilities/TID300/CobbAngle.js";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";

const COBB_ANGLE = "CobbAngle";

class CobbAngle {
constructor() {}

// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(MeasurementGroup) {
const {
defaultState,
NUMGroup,
SCOORDGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);

const state = {
...defaultState,
rAngle: NUMGroup.MeasuredValueSequence.NumericValue,
toolType: CobbAngle.toolType,
handles: {
start: {},
end: {},
start2: {
highlight: true,
drawnIndependently: true
},
end2: {
highlight: true,
drawnIndependently: true
},
textBox: {
hasMoved: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
}
}
};

[
state.handles.start.x,
state.handles.start.y,
state.handles.end.x,
state.handles.end.y,
state.handles.start2.x,
state.handles.start2.y,
state.handles.end2.x,
state.handles.end2.y
] = SCOORDGroup.GraphicData;

return state;
}

static getTID300RepresentationArguments(tool) {
const { handles, finding, findingSites } = tool;
const point1 = handles.start;
const point2 = handles.end;
const point3 = handles.start2;
const point4 = handles.end2;
const rAngle = tool.rAngle;

const trackingIdentifierTextValue = "cornerstoneTools@^4.0.0:CobbAngle";

return {
point1,
point2,
point3,
point4,
rAngle,
trackingIdentifierTextValue,
finding,
findingSites: findingSites || []
};
}
}

CobbAngle.toolType = COBB_ANGLE;
CobbAngle.utilityToolType = COBB_ANGLE;
CobbAngle.TID300Representation = TID300CobbAngle;
CobbAngle.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
if (!TrackingIdentifier.includes(":")) {
return false;
}

const [cornerstone4Tag, toolType] = TrackingIdentifier.split(":");

if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
return false;
}

return toolType === COBB_ANGLE;
};

MeasurementReport.registerTool(CobbAngle);

export default CobbAngle;
38 changes: 7 additions & 31 deletions src/adapters/Cornerstone/EllipticalRoi.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,11 @@ class EllipticalRoi {

// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(MeasurementGroup) {
const { ContentSequence } = MeasurementGroup;

const findingGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === FINDING
);

const findingSiteGroups = toArray(ContentSequence).filter(
group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE
);

const NUMGroup = toArray(ContentSequence).find(
group => group.ValueType === "NUM"
);

const SCOORDGroup = toArray(NUMGroup.ContentSequence).find(
group => group.ValueType === "SCOORD"
);
const {
defaultState,
NUMGroup,
SCOORDGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);

const { GraphicData } = SCOORDGroup;

Expand Down Expand Up @@ -66,14 +54,8 @@ class EllipticalRoi {
x: majorAxis[1].x - minorAxisDirection.x * halfMinorAxisLength,
y: majorAxis[1].y - minorAxisDirection.y * halfMinorAxisLength
};
const { ReferencedSOPSequence } = SCOORDGroup.ContentSequence;
const {
ReferencedSOPInstanceUID,
ReferencedFrameNumber
} = ReferencedSOPSequence;
const state = {
sopInstanceUid: ReferencedSOPInstanceUID,
frameIndex: ReferencedFrameNumber || 0,
...defaultState,
toolType: EllipticalRoi.toolType,
active: false,
cachedStats: {
Expand Down Expand Up @@ -102,13 +84,7 @@ class EllipticalRoi {
}
},
invalidated: true,
visible: true,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
})
visible: true
};

return state;
Expand Down
Loading