Third Party Functionality
Cornerstone Tools v3.0 comes with new third-party support so that one can easily drop new common functionality into cornerstoneTools
, then construct new custom Tools which expand upon core and third-party functionality.
The third-party architecture comes with the advantage of not having to maintain a fork of the entire codebase, should you wish to include some custom/bespoke functionality. However, if you think something you have developed would be valued by the wider Cornerstone community, please consider submitting a pull request and contributing it back.
Item Types
The types typically registered by third-parties are:
base
mixins
manipulators
utils
Cornerstone Tools has these additional types (third-parties could expand these, but are unlikely to):
drawing
(for drawing api functions)stateManagement
Additionally, custom module
s can be added to the store
.
Base
A user can define a new abstract
base Tool type, from which third-party Tools can inherit from. The new Tool type must inherit from either BaseTool
, BaseAnnotationTool
or BaseBrushTool
. To create a new base Tool type simply import
the base type you wish to extend and extend it as:
const BaseTool = cornerstoneTools.import('core/base/BaseTool');
export default class BaseNewTypeTool extends BaseTool {
// implementation ...
}
Mixins
You may want to make custom mixin
s if you re-use some functionality between various custom Tools. Mixins are simply Object
s consisting of function
s that may assigned to any Tool as member functions, e.g.:
function evenMoreHelloWorld() {
// Note this would need to be called from somewhere
// Within the Tool's implementation.
console.log('Hello World from the even more hello world mixin!');
}
export default {
evenMoreHelloWorld,
};
Manipulators
BaseAnnotationTool
s use manipulators
to interact with the annotation's handle
s data in a particular way. If you need to build a custom interaction mechanism you envision using more than once, you may want to make a custom manipulator. A manipulator is just a function
. They have rather freeform structure, but principally take eventData
and toolData
and perform an operation, e.g.:
export default function(eventData, toolName, data, handle, someCallback) {
// Implementation, Do something with the handle.
// ...
someCallback();
}
Utils
Utils
are just functions that perform some generic common process, e.g.:
export default function() {
console.log('Super important hello world util.');
}
Modules
A module is a namespaced storage object in the store
that contains the following properties:
Property | Requirement | Description |
---|---|---|
state | Mandatory | An object that stores the module's current state. There is no vigorous structure your state must follow, and you may structure it however you wish. We recommend as many top level primitives as possible, however getters can be used for more complicated queries. |
getters | Optional | An object comprised of functions that query state. Getters can be used for more complex queries of the state (e.g. to yield a value that references a specific cornerstone enabled element). Top level primitives that require no calculation should instead by accessed by const property = state.property , as this reduces boilerplate in implementation code. |
setters | Optional | An object comprised of functions that modify state. Setters can be used for more complex input (e.g. push object x to array y ). Top level primitives should be set by state.property = value , as this reduces boilerplate in implementation code. |
onRegisterCallback (name) | Optional | This function is called when the module is registered to the cornerstoneTools store . It is used to perform any global initialization the modules requires. The name the module was given upon registration is passed to the callback. |
enabledElementCallback (enabledElement) | Optional | This function is called once for each Enabled element upon registering the module, and again any time a new Enabled element is added to the cornerstoneTools instance. The Enabled Element is passed to the callback. |
removeEnabledElementCallback (enabledElement) | Optional | This function is called whenever an Enabled element is removed from the cornerstoneTools instance, allowing cleanup of unneeded data. The Enabled Element is passed to the callback. |
Most modules will have getters and setters, unless they only contain primitives (e.g. the module's state is only comprised of boolean
toggles). Here is a simple toy example of a module with state, setters and getters:
const state = {
isPolite: true,
responses: {
polite: 'Hello World!',
rude: 'Go away, World.',
},
};
const setters = {
politeResponse: response => {
state.responses.polite = response;
},
rudeResponse: response => {
state.responses.rude = response;
},
moodSwitch: () => {
state.isPolite = !state.isPolite;
},
};
const getters = {
response: () => {
if (state.isPolite) {
return state.responses.polite;
} else {
return state.responses.rude;
}
},
};
function onRegisterCallback() {
console.log('Hello onRegisterCallback.');
}
function enabledElementCallback(enabledElement) {
console.log(`hello element ${enabledElement.uuid}.`);
}
export default {
state,
getters,
setters,
onRegisterCallback,
enabledElementCallback,
};
A more complete and realistic example of a module including both optional callbacks can be found in src/store/modules/segmentationModule.js
.
Registration
Registration is abstracted to a simple top-level register
function, taking type
, name
and item
as arguments e.g.:
cornerstoneTools.register('module', 'helloWorldModule', myModule);
cornerstoneTools.register('mixin', 'evenMoreHelloWorld', myMixin);
By default, trying to register an item that would occupy and already registered 'type/name' uri
will log a warning and cancel registration. If you wish to overwrite a uri, you may do so by adding true
to the end of the register call:
cornerstoneTools.register('mixin', 'evenMoreHelloWorld', mySuperiorMixin, true);
If a library has lots of items it would like to register at once, it can pack its items into an array such as:
const lotsOfItems = [
{
type, // String
name, // String
item // Object/Function
},
// Etc...
];
These can then all be registered at once by calling:
cornerstoneTools.registerSome(lotsOfItems);
Again, you can toggle an overwrite by adding true
as an additional argument:
cornerstoneTools.registerSome(lotsOfItems, true);
Imports
Both core and registered functionality can be retrieved by the top-level import
function, e.g.:
const evenMoreHelloWorldMixin = cornerstoneTools.import(
'mixins/evenMoreHelloWorld'
);
And store modules may be retrieved from the modules object of cornerstoneTools:
const { helloWorldModule } = cornerstoneTools.store.modules;
Tools
Third-party tools may be easily created by import
ing the required base tool Type and extending it.
For instance, if we wanted to package the HelloWorldTool
we made in the Custom Tools section into a third-party tool:
const BaseTool = cornerstoneTools.import('base/BaseTool');
export default class HelloWorldTool extends BaseTool {
constructor(name = 'HelloWorld') {
super({
name,
supportedInteractionTypes: ['mouse'],
mixins: [
'activeOrDisabledBinaryTool', // Mixin from cornerstoneTools source.
'evenMoreHelloWorld', // Mixin from the plugin.
],
});
// Use a module from the plugin. It should be first accessed in constructor.
this._helloWorldModule = cornerstoneTools.store.modules.helloWorld;
}
// implementation ...
}
The array of mixins to add to the Tool may be from either the core library or registered functionality. You needn't import any mixin used, the heavy lifting is performed in the constructor
of BaseTool
.
Any other functionality imported must be done so in the constructor, to ensure it is already registered upon import. This includes accessing modules which might not be there when the file is loaded (e.g. the example above).
The tool can then be added to a cornerstoneTools
instance that has the required functionality registered, e.g.:
// Register all custom functionality used by cornerstoneTools in this app.
cornerstoneTools.register('module', 'helloWorldModule', myModule);
cornerstoneTools.register('mixin', 'evenMoreHelloWorld', myMixin);
// Initialise cornerstoneTools.
cornerstoneTools.init();
// Add the custom tool!
cornerstoneTools.addTool(HelloWorldMouseTool);