Third Party Functionality

This documentation is for an unreleased (still in testing) version of Cornerstone Tools. Until it is released, its API may experience frequent breaking changes. The current stable release can be found here.

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 modules 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 mixins if you re-use some functionality between various custom Tools. Mixins are simply Objects consisting of functions 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

BaseAnnotationTools use manipulators to interact with the annotation's handles 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, toolType, 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/brushModule.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 importing the required base tool Type and extending it.

For instance, if we wanted to package the HelloWorldMouseTool we made in the Custom Tools section into a third-party tool:

const BaseTool = cornerstoneTools.import('base/BaseTool');

export default class HelloWorldMouseTool extends BaseTool {
  constructor (name = 'HelloWorldMouse') {
    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);

results matching ""

    No results matching ""