Third Party Functionality
Since 3.0, cornerstoneTools
comes with 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
Module
are objects that store global data, which may be shared between multiple tools, effect multiple cornerstone elements, etc. Most modules will have getters and setters, unless they only contain primitives (e.g. the module's state is only comprised of boolean
toggles). You can learn more about modules here. Here is a simple toy example of a module with state, setters and getters:
const configuration = {
// Some app-level configuration for this module.
};
// The modules internal state.
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,
};
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 an 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
Helpful internal library methods and utilities can be retrieved by the top-level importInternal
function, e.g.:
const evenMoreHelloWorldMixin = cornerstoneTools.importInternal(
'mixins/evenMoreHelloWorld'
);
You can add to the list of methods and utilities that can be retrieved by registering them.
Store modules may also be retrieved with the top-level getModule
function:
const { helloWorldModule } = cornerstoneTools.getModule('helloWorldModule');
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
that we created in the Custom Tools section into a third-party tool:
const BaseTool = cornerstoneTools.importInternal('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.getModule('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);