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

Enabled writing of location and description in DICOM SR file #228

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
98 changes: 98 additions & 0 deletions src/adapters/Cornerstone/Angle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import MeasurementReport from "./MeasurementReport.js";
import TID300Angle from "../../utilities/TID300/Angle.js";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";
import GenericTool from "./GenericTool.js";

const ANGLE = "Angle";

class Angle extends GenericTool {
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(MeasurementGroup) {
const toolState = super.getMeasurementData(MeasurementGroup);

const { ContentSequence } = MeasurementGroup;

const NUMGroup = this.getNumericContent(ContentSequence);

const SCOORDGroup = this.getScoordContent(ContentSequence);

let angleState = {
handles: {
start: {},
middle: {},
end: {},
textBox: {
hasMoved: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
}
},
rAngle: NUMGroup.MeasuredValueSequence.NumericValue,
toolName: ANGLE,
toolType: Angle.toolType
};

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

angleState = Object.assign(toolState, angleState);

return angleState;
}

static getTID300RepresentationArguments(tool) {
const TID300Rep = super.getTID300RepresentationArguments(tool);
const { handles } = tool;
const point1 = handles.start;
const point2 = handles.middle;
const point3 = handles.end;
const rAngle = tool.rAngle;

const trackingIdentifierTextValue = CORNERSTONE_4_TAG + ":" + ANGLE;

return Object.assign(TID300Rep, {
point1,
point2,
point3,
rAngle,
trackingIdentifierTextValue
});
}

static checkMeasurementIntegrity(tool) {
if (tool.hasOwnProperty("rAngle")) {
return true;
} else {
return false;
}
}
}

Angle.toolType = ANGLE;
Angle.utilityToolType = ANGLE;
Angle.TID300Representation = TID300Angle;
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;
71 changes: 33 additions & 38 deletions src/adapters/Cornerstone/ArrowAnnotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,30 @@ import MeasurementReport from "./MeasurementReport.js";
import TID300Point from "../../utilities/TID300/Point.js";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";
import { toArray } from "../helpers.js";
import GenericTool from "./GenericTool.js";

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

class ArrowAnnotate {
constructor() {}

class ArrowAnnotate extends GenericTool {
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(MeasurementGroup) {
const toolState = super.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 SCOORDGroup = this.getScoordContent(ContentSequence);

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

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

const text = findingGroup.ConceptCodeSequence.CodeMeaning;

const { GraphicData } = SCOORDGroup;

const { ReferencedSOPSequence } = SCOORDGroup.ContentSequence;
const {
ReferencedSOPInstanceUID,
ReferencedFrameNumber
} = ReferencedSOPSequence;
const state = {
sopInstanceUid: ReferencedSOPInstanceUID,
frameIndex: ReferencedFrameNumber || 0,
toolType: ArrowAnnotate.toolType,
active: false,
let arrowState = {
handles: {
start: {
x: GraphicData[0],
Expand All @@ -55,8 +36,8 @@ class ArrowAnnotate {
// TODO: How do we choose where the end goes?
// Just put it pointing from the bottom right for now?
end: {
x: GraphicData[0] + 20,
y: GraphicData[1] + 20,
x: GraphicData[2],
y: GraphicData[3],
highlight: true,
active: false
},
Expand All @@ -68,28 +49,34 @@ class ArrowAnnotate {
hasBoundingBox: true
}
},
invalidated: true,
text,
visible: true,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
})
toolName: ARROW_ANNOTATE,
toolType: ArrowAnnotate.toolType
};

return state;
if (GraphicData.length === 6) {
arrowState.handles.start.x = GraphicData[0];
arrowState.handles.start.y = GraphicData[1];
arrowState.handles.start.z = GraphicData[2];
arrowState.handles.end.x = GraphicData[3];
arrowState.handles.end.y = GraphicData[4];
arrowState.handles.end.z = GraphicData[5];
}

arrowState = Object.assign(toolState, arrowState);

return arrowState;
}

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

let { finding, findingSites } = tool;

const TID300RepresentationArguments = {
points,
trackingIdentifierTextValue: `cornerstoneTools@^4.0.0:ArrowAnnotate`,
trackingIdentifierTextValue:
CORNERSTONE_4_TAG + ":" + ARROW_ANNOTATE,
findingSites: findingSites || []
};

Expand All @@ -106,6 +93,14 @@ class ArrowAnnotate {

return TID300RepresentationArguments;
}

static checkMeasurementIntegrity(tool) {
if (tool.hasOwnProperty("text")) {
return true;
} else {
return false;
}
}
}

ArrowAnnotate.toolType = ARROW_ANNOTATE;
Expand Down
72 changes: 24 additions & 48 deletions src/adapters/Cornerstone/Bidirectional.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,18 @@ import MeasurementReport from "./MeasurementReport";
import TID300Bidirectional from "../../utilities/TID300/Bidirectional";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";
import { toArray } from "../helpers.js";
import GenericTool from "./GenericTool";

const BIDIRECTIONAL = "Bidirectional";
const LONG_AXIS = "Long Axis";
const SHORT_AXIS = "Short Axis";
const FINDING = "121071";
const FINDING_SITE = "G-C0E3";

class Bidirectional {
constructor() {}

class Bidirectional extends GenericTool {
// 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 toolState = super.getMeasurementData(MeasurementGroup);

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

const longAxisNUMGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeMeaning === LONG_AXIS
Expand All @@ -40,12 +31,6 @@ class Bidirectional {
shortAxisNUMGroup.ContentSequence
).find(group => group.ValueType === "SCOORD");

const { ReferencedSOPSequence } = longAxisSCOORDGroup.ContentSequence;
const {
ReferencedSOPInstanceUID,
ReferencedFrameNumber
} = ReferencedSOPSequence;

// Long axis

const longestDiameter = String(
Expand All @@ -71,11 +56,7 @@ class Bidirectional {
)
};

const state = {
sopInstanceUid: ReferencedSOPInstanceUID,
frameIndex: ReferencedFrameNumber || 1,
toolType: Bidirectional.toolType,
active: false,
let bidirState = {
handles: {
start: {
x: longAxisSCOORDGroup.GraphicData[0],
Expand Down Expand Up @@ -125,42 +106,31 @@ class Bidirectional {
y: bottomRight.y + 10
}
},
invalidated: false,
isCreating: false,
longestDiameter,
shortestDiameter,
toolType: "Bidirectional",
toolName: "Bidirectional",
visible: true,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
})
toolName: BIDIRECTIONAL,
toolType: Bidirectional.toolType
};

return state;
bidirState = Object.assign(toolState, bidirState);

return bidirState;
}

static getTID300RepresentationArguments(tool) {
const TID300Rep = super.getTID300RepresentationArguments(tool);
const {
start,
end,
perpendicularStart,
perpendicularEnd
} = tool.handles;
const {
shortestDiameter,
longestDiameter,
finding,
findingSites
} = tool;
const { shortestDiameter, longestDiameter } = tool;

const trackingIdentifierTextValue =
"cornerstoneTools@^4.0.0:Bidirectional";
CORNERSTONE_4_TAG + ":" + BIDIRECTIONAL;

return {
return Object.assign(TID300Rep, {
longAxis: {
point1: start,
point2: end
Expand All @@ -171,10 +141,16 @@ class Bidirectional {
},
longAxisLength: longestDiameter,
shortAxisLength: shortestDiameter,
trackingIdentifierTextValue,
finding: finding,
findingSites: findingSites || []
};
trackingIdentifierTextValue
});
}

static checkMeasurementIntegrity(tool) {
if (tool.longestDiameter > 0 && tool.shortestDiameter > 0) {
return true;
} else {
return false;
}
}
}

Expand Down
Loading