Custom Tools

This guide will give you an overview of creating a new Tool. This is applicable for Tools being built within the library as well as Tools for 3rd party plugins.

See the Third-Party functionality guide for more details on the integration and extension options available.

Choosing A Base Class

There are 3 base classes to choose from when building a tool:

BaseTool

The BaseTool is the fundamental base class, with just the functionality required to function within the Cornerstone Tools framework. This is the base class to choose if the Tool you wish to create won't have its own annotation data (.e.g MagnifyTool), or only interacts with a different Tool's data (e.g. FreehandRoiSculptorTool). The other two base classes BaseAnnotationTool and BaseBrushTool both inherit from BaseTool.

A BaseTool is also the Tool type you should derive from when making a tool that interacts with the labelmap data, and isn't a brush. These are dubbed "Segmentation Tools".

BaseAnnotationTool

The BaseAnnotationTool inherits from BaseTool, and is intended for any Tool that will create/modify and display its own annotation data on the canvas (e.g. LengthTool).

BaseBrushTool

The BaseBrushTool inherits from BaseTool and is intended specifically for Tools that want to create/modify/delete segmentation data (e.g. BrushTool). Potential subclasses could include adaptive brush Tools, or region growing Tools that require a seed area to be drawn.

Creating Your Tool

Once you have an appropriate base class chosen (we will use the BaseTool in this example), you can extend it to start building your Tool. In this example we shall make a Tool that logs 'Hello cornerstoneTools!' to the console on every mouse click.

Class Definition

By convention the class name should be in PascalCase, and suffixed with Tool. For example we shall call our wonderful tool HelloWorldTool:

import csTools from 'cornerstone-tools';
const BaseTool = csTools.importInternal('base/BaseTool');
// NOTE: if you're creating a tool inside the CornerstoneTools repository
// you can import BaseTool directly from `src/tools/base`.

export default class HelloWorldTool extends BaseTool {
  constructor(name = 'HelloWorld') {
    super({
      name,
      supportedInteractionTypes: ['Mouse'],
    });
  }
}

The constructor must call super(), which passes an object to the constructor of the superclass (BaseTool, in this case, but the same object is passed to BaseAnnotationTool or BaseBrushTool). The object passed may have the following properties:

Property Requirement description
name Mandatory The name of the Tool.
supportedInteractionTypes Mandatory An array of strings listing the interaction types, mouse and/or touch.
strategies Optional If your Tool has multiple strategies of operation, you may pass an array of functions for each strategy here (see the RotateTool for a good example).
defaultStrategy Optional If you have multiple strategies, this one should be used by default (pass a string identical to the strategy function name).
configuration Optional An object with configurable properties used by your Tool. It may include your Tool's sensitivity, how an annotation displays when rendered, etc.
mixins Optional An array of mixins (commonly used behaviours/functionality) to add to the Tool.

For our simple Tool we pass only the two mandatory fields to super. For the Tool's own constructor, it must at minimum take name as a parameter, and it must have a default value. By convention the default name is the same as the classname, minus the Tool suffix.

Adding Mixins

Next you can add any mixins you wish to add to the Tool. These are passed to super, and initialized in BaseTool. For our example, our Tool only makes sense in Active or Disabled modes, as it has none of its own data, and logs 'Hello cornerstoneTools!' on click, as such we shall include the activeOrDisabledBinaryTool mixin:

import csTools from 'cornerstone-tools';
const BaseTool = csTools.importInternal('base/BaseTool');

export default class HelloWorldMouseTool extends BaseTool {
  constructor(name = 'HelloWorldMouse') {
    super({
      name,
      supportedInteractionTypes: ['Mouse'],
      mixins: ['activeOrDisabledBinaryTool'],
    });
  }
}

You need not import any mixins to your class file; this is dealt with in BaseTool.

Mode Change Callbacks

In the Cornerstone Tools framework, if a Tool changes mode, an appropriate callback is called if the Tool has one. These are, quite simply:

  • Active - activeCallback (element)
  • Passive - passiveCallback (element)
  • Enabled - enabledCallback (element)
  • Disabled - enabledCallback (element)

Note that unlike a lot of the callbacks, the element on which the Tool resides is passed to the mode change callbacks, not evt.

For our example Tool, this gives us more chances to log hello to the console:

import csTools from 'cornerstone-tools';
const BaseTool = csTools.importInternal('base/BaseTool');

export default class HelloWorldTool extends BaseTool {
  constructor(name = 'HelloWorld') {
    super({
      name,
      supportedInteractionTypes: ['Mouse'],
      mixins: ['activeOrDisabledBinaryTool'],
    });
  }

  activeCallback(element) {
    console.log(`Hello element ${element.uuid}!`);
  }

  disabledCallback(element) {
    console.log(`Goodbye element ${element.uuid}!`);
  }
}

Event Dispatcher Callbacks

Here we can add the meat of our Tool. Event dispatchers check for methods on Tools and fire them when appropriate.

TODO: List them all?? Or just link to api? Not sure yet. TODO: An actual useful description.

For our Tool, we want to log to the console on mouse click. BaseTool has two appropriate methods: preMouseDownCallback and postMouseDownCallback. These fire before or after other annotation data on the canvas has a chance to claim the mouse click. for our Tool we shall choose preMouseDownCallback, as it's always nice to say hello before doing anything else. The method can simply be defined and it shall be called when appropriate via the mouseToolEventDispatcher:

import csTools from 'cornerstone-tools';
const BaseTool = csTools.importInternal('base/BaseTool');

export default class HelloWorldTool extends BaseTool {
  constructor(name = 'HelloWorld') {
    super({
      name,
      supportedInteractionTypes: ['Mouse'],
      mixins: ['activeOrDisabledBinaryTool'],
    });
  }

  preMouseDownCallback(evt) {
    console.log('Hello cornerstoneTools!');
  }

  activeCallback(element) {
    console.log(`Hello element ${element.uuid}!`);
  }

  disabledCallback(element) {
    console.log(`Goodbye element ${element.uuid}!`);
  }
}

results matching ""

    No results matching ""