Tool Mixins
Tool Mixins provide a way to construct tools via composition using reusable components. A mixin may include a set of methods which are applied to the class upon instantiation. You can register a mixin by its name in your tool without needing to import it (as this is dealt with inside BaseTool
). For example:
export default class FreehandRoiSculptorTool extends BaseTool {
constructor(props = {}) {
const defaultProps = {
// ...
mixins: ['activeOrDisabledBinaryTool'],
// ...
};
//...
}
//...
}
By adding the mixin to the list of mixins
in the default props, it becomes a core feature of the tool. It is possible, however, to pass a mixin at runtime upon class instantiation to alter the behaviour of the resulting tool.
Mixins have one optional special method initilizeMixin
. If the intializeMixin
method is present in a mixin, it will be called once when the mixin is attached to the tool.
Binary Tools
In general, Tools can be in four modes: Active
, Passive
, Enabled
or Disabled
. However, for some Tools only a subset of these are required/useful. For example, an overlay that can only be toggled on/off only makes sense to be Enabled
or Disabled
(e.g. ScaleOverlayTool
). Using a binary mixin will re-direct the unused modes so that the caller of api doesn't have to worry about this.
enabledOrDisabledBinaryTool
A Tool with the enabledOrDisabledBinaryTool
mixin can only be Enabled
or Disabled
(e.g. ScaleOverlayTool
).
If the Tool is set Active
, the mode will be redirected to Enabled
.
If the Tool is set Passive
, the mode will be redirected to Disabled
.
activeOrDisabledBinaryTool
A Tool with the activeOrDisabledBinaryTool
mixin can only be Active
or Disabled
(e.g. FreehandSculpterMouseTool
).
If the Tool is set Enabled
, the mode will be redirected to Active
.
If the Tool is set Passive
, the mode will be redirected to Disabled
.
Segmentation Mixins
"Segmentation Tools" are BaseTool
s that implement a segmentation mixin.
The role of a segmentation mixin is to provide a delineation mechanism, which generates an operationData
object which is passed to the active strategy alongside the cornerstone event.
Examples of segmentation mixins could be:
- User draws a shape as input.
- User clicks one or more seed points as input on one frame.
- User clicks at the top and bottom of a region of interest (on different frames) in an axial series.
Although segmentation tools may have very specific uses (e.g. the last example above could be used as seeds for an AI-powered spine segmentation, by providing the spine's extent), segmentation mixins themselves are intended to be reusable in many contexts. Other than their intended use case, segmentation mixins are functionally just mixins in the context of cornerstoneTools
, so can be injected by plugins like any other mixin.
As the delineation mechanisms can vary from simple to complex, the only requirement of a segmentation mixin is that it must call BaseTool
's this.applyActiveStrategy(evt, operationData)
with the operationData
it produces at the end of the delination cycle.
Taking the circleSegmentationMixin
as an example:
/**
* @mixin circleSegmentationMixin - Segmentation operations for circles.
* @memberof Mixins
*/
export default {
postTouchStartCallback: _startOutliningRegion,
postMouseDownCallback: _startOutliningRegion,
mouseClickCallback: _startOutliningRegion,
touchDragCallback: _setHandlesAndUpdate,
mouseDragCallback: _setHandlesAndUpdate,
mouseMoveCallback: _setHandlesAndUpdate,
touchEndCallback: _applyStrategy,
mouseUpCallback: _applyStrategy,
initializeMixin: _resetHandles,
renderToolData,
};
This mixin implements a "drag-to-define-a-circle" delineation mechanism, and as such plugs its methods into BaseTool
API for dealing with appropriate user input such as down/touch, drag and release for both mouse and touch interactions.
The renderToolData
function renders the circle to the canvas whilst dilineation is in process.
The _applyStrategy
function here calls BaseTool
's applyActiveStrategy
method:
//src/mixins/segmentation/circleSegmentationMixin.js
// ...
const operationData = {
points,
pixelData,
segmentIndex: labelmap3D.activeSegmentIndex,
segmentationMixinType: `circleSegmentationMixin`,
};
this.applyActiveStrategy(evt, operationData);
// ...
Here the operationData
contains information about the delineation. The segmentationMixinType
property just defines a key that tells the strategy what sort of operationData
it is recieving, so that it knows what to process, or can throw if it can't process operations from that mixin. E.g.:
//src/util/segmentation/operations/fillInsideCircle.js
// ...
if (operationData.segmentationMixinType !== `circleSegmentationMixin`) {
logger.error(
`fillInsideCircle operation requires circleSegmentationMixin operationData, recieved ${
operationData.segmentationMixinType
}`
);
return;
}
// ...
Core Segmentation Mixins
Here are the segmentation mixins present in the core cornerstoneTools
library. They power the various scissor and correction tools.
circleSegmentationMixin
The circleSegmentationMixin
allows the user to draw a circle on a single frame of a series via a touch/mouse drag. On mouse up/touch end, the mixin calls the Tool's activeStrategy
with the following operationData
:
const operationData = {
points,
pixelData,
segmentIndex: labelmap3D.activeSegmentIndex,
segmentationMixinType: `circleSegmentationMixin`,
};
Where:
points
- An object with two pointsstart
andend
containing the (x
,y
) coordinates of the centre of the circle and a point on its edge.pixelData
- ThepixelData
of theLabelmap2D
that corresponds with the image.- The
segmentIndex
to apply the operation to. segmentationMixinType
- The mixin identifier.
freehandSegmentationMixin / polylineSegmentationMixin
The freehandSegmentationMixin
allows the user to draw a freehand polygon on a single frame of a series via a touch/mouse drag. On mouse up/touch end, the mixin calls the Tool's activeStrategy
with the following operationData
:
const operationData = {
points,
pixelData,
segmentIndex: labelmap3D.activeSegmentIndex,
segmentationMixinType: `circleSegmentationMixin`,
};
Where:
points
- An array of points containing the (x
,y
) coordinates of the nodes along its points.pixelData
- ThepixelData
of theLabelmap2D
that corresponds with the image.- The
segmentIndex
to apply the operation to. segmentationMixinType
- The mixin identifier.
The freehandSegmentationMixin
's renderTooldata
method connects the first and last point whilst drawing, signifying a closed polygon to the user. If you wish to display just the open polyline for a Tool whilst deliniating, use the polylineSegmentationMixin
instead, which implements the freehandSegmentationMixin
but overrides its renderToolData
method to display just the line.
rectangleSegmentationMixin
The rectangleSegmentationMixin
allows the user to draw a rectangle on a single frame of a series via a touch/mouse drag. On mouse up/touch end, the mixin calls the Tool's activeStrategy
with the following operationData
:
const operationData = {
points,
pixelData,
segmentIndex: labelmap3D.activeSegmentIndex,
segmentationMixinType: `circleSegmentationMixin`,
};
Where:
points
- An array with two points containing the (x
,y
) coordinates of the top left and the bottom right of the rectangle.pixelData
- ThepixelData
of theLabelmap2D
that corresponds with the image.- The
segmentIndex
to apply the operation to. segmentationMixinType
- The mixin identifier.