CornerstoneTools之Tools
简介
Cornerstone 是一组 JavaScript 库,可用于构建基于 Web 的医学成像应用程序,它提供了构建放射学应用程序(例如OHIF 查看器)的框架
本文叙述的是库 @cornerstonejs/cornerstoneTools
,提供了操作、注释和分割工具以及用于构建交互式成像应用程序的工具框架
最新版本的是@cornerstonejs/cornerstone3D/tree/main/packages/tools
,重构于旧的cornerstoneTools
cornerstoneTools常用工具
Annotation Tools
- AngleTool
- 角度工具:通过放置三个连续点来创建并定位角度
- ArrowAnnotateTool
- 箭头工具:创建并定位箭头和标签
- BidirectionalTool
- 双向工具:创建并定位测量的注释区域的长度和宽度
- CircleRoiTool
- 用于绘制圆形感兴趣区域并测量封闭像素统计数据的工具
- CobbAngleTool
- Cobb角:用于测量两条直线之间角度的工具
- EllipticalRoiTool
- 椭圆工具:用于绘制椭圆感兴趣区域并测量封闭像素统计数据的工具
- FreehandRoiTool
- 自由测量:用于绘制任意多边形感兴趣区域并测量封闭像素统计数据的工具
- LengthTool
- 长度工具:测量距离
- ProbeTool
- 探针工具:在所需位置提供图像数据探测的工具
- RectangleRoiTool
- 矩形工具:用于绘制感兴趣的矩形区域并测量封闭像素的统计数据的工具
- TextMarkerTool
- 使用文本标记注释图像的工具
Segmentation Tools
- BrushTool
- 在图像上绘制分割的工具
- SphericalBrushTool
- 球形图像上绘制分割的工具
- CircleScissorsTool
- 通过绘制圆圈来操作标签图数据的工具
- CorrectionScissorsTool
- 用于纠正标签图上的分段的工具
- FreehandScissorsTool
- 通过徒手绘制多边形来操作标签图数据的工具
- RectangleScissorsTool
- 通过绘制矩形来操作标签图数据的工具
Other Tools
CrosshairsTool
- 十字线工具:用于查找另一个元素中对应于的切片的工具同步图像系列中的图像位置
DragProbeTool
- 提供图像数据探测的工具,拖动时输入位置
EraserTool
- 橡皮擦:删除其他注释工具数据的工具
MagnifyTool
- 放大镜工具:用于以增加的放大倍率检查区域的工具
OrientationMarkersTool
- 用于在图像上显示方向标记的工具
OverlayTool
- 用于在图像上显示比例覆盖的工具,使用 viewport.overlayColor 设置默认颜色
PanMultiTouchTool
- 平移多点触控工具
PanTool
- 平移工具:用于平移图像的工具
ReferenceLinesTool
- 参考线:用于显示其他启用元素的参考线的工具
RotateTool
- 旋转工具:旋转图像
RotateTouchTool
- 使用触摸旋转图像的工具
ScaleOverlayTool
- 用于在图像上显示比例覆盖的工具
StackScrollMouseWheelTool
- 鼠标滚轮工具:是一个允许用户使用鼠标滚轮滚动图像堆栈的工具
StackScrollMultiTouchTool
- 多点触控滚动工具:使用多点触控滚动浏览系列的工具
StackScrollTool
- 拖动滑动工具:允许用户通过按鼠标单击并拖动来滚动图像堆栈
WwwcRegionTool
- 窗体工具:基于矩形区域设置wwwc的工具
WwwcTool
- 窗体工具:通过鼠标/触摸拖动设置wwwc的工具
ZoomMouseWheelTool
- 缩放工具:使用鼠标滚轮更改放大倍率的工具
ZoomTool
- 改变放大倍率的工具
ZoomTouchPinchTool
- 使用多点触控改变放大倍率
工具模板原理
BaseTool
- 基础工具类说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276/**
* @typedef ToolConfiguration
* @param {String} name
* @param {object} strategies - Named strategy functions
* @param {String} defaultStrategy - The name of the strategy to use by default
* @param {Object} configuration
* @param {String[]} mixins - A list of mixin names to apply to the tool
*/
class BaseTool {
/**
* Constructor description
* @param {props} [props={}] Tool properties set on instantiation of a tool
* @param {defaultProps} [defaultProps={}] Tools Default properties
*/
constructor(props, defaultProps) {
/**
* Merge default props with custom props
*/
this.initialConfiguration = deepmerge(defaultProps, props);
const {
name,
strategies,
defaultStrategy,
configuration,
supportedInteractionTypes,
mixins,
svgCursor,
} = this.initialConfiguration;
/**
* A unique, identifying tool name
* @type {String}
*/
this.name = name;
/** @type {String} */
this.mode = 'disabled';
this.element = undefined;
this.supportedInteractionTypes = supportedInteractionTypes || [];
this.strategies = strategies || {};
this.defaultStrategy =
defaultStrategy || Object.keys(this.strategies)[0] || undefined;
this.activeStrategy = this.defaultStrategy;
if (svgCursor) {
this.svgCursor = svgCursor;
}
// Options are set when a tool is added, during a "mode" change,
// or via a tool's option's setter
this._options = {};
// Configuration is set at tool initalization
this._configuration = Object.assign({}, configuration);
// `updateOnMouseMove` causes the frame to render on every mouse move when
// the tool is active. This is useful for tools that render large/dynamic
// items to the canvas which can't easily be respresented with an SVG Cursor.
this.updateOnMouseMove = false;
this.hideDefaultCursor = false;
// Apply mixins if mixinsArray is not empty.
if (mixins && mixins.length) {
this._applyMixins(mixins);
}
this._cursors = Object.assign({}, this.initialConfiguration.cursors);
const defaultCursor =
this.defaultStrategy && this._cursors[this.activeStrategy];
if (defaultCursor) {
this.svgCursor = defaultCursor;
}
}
// CONFIGURATION
/**
* Config...
* @public
* @type {Object}
* @instance
*/
static get configuration() {}
get configuration() {
return this._configuration;
}
set configuration(configuration) {
this._configuration = configuration;
}
// OPTIONS
/**
* Options...
* @readonly
* @instance
*/
get options() {
return this._options;
}
/**
* Merges provided options with existing options.
*
* @public
* @instance
* @param {Object} options - options object to merge with existing options.
* @returns {undefined}
*/
mergeOptions(options) {
this._options = Object.assign({}, this._options, options);
}
/**
* Clears the tools options.
*
* @public
* @instance
* @memberof Tools.Base.BaseTool
* @returns {undefined}
*/
clearOptions() {
this._options = {};
}
/**
* Apply the currently set/active strategy.
*
* @public
* @instance
* @method applyActiveStrategy
* @memberof Tools.Base.BaseTool
*
* @param {Object} evt The event that triggered the strategies application
* @param {Object} operationData - An object containing extra data not present in the `evt`,
* required to apply the strategy.
* @returns {*} strategies vary widely; check each specific strategy to find expected return value
*/
applyActiveStrategy(evt, operationData) {
return this.strategies[this.activeStrategy].call(this, evt, operationData);
}
/**
* Iterates over registered mixins; any matching names in the provided `mixinsArray` will
* be merged with this instance.
*
* @private
* @method _applyMixins
* @param {string[]} mixinsArray An array of mixin identifiers (strings).
* @returns {undefined}
*/
_applyMixins(mixinsArray) {
for (let i = 0; i < mixinsArray.length; i++) {
const mixin = mixins[`${mixinsArray[i]}`];
if (typeof mixin === 'object') {
Object.assign(this, mixin);
if (typeof this.initializeMixin === 'function') {
// Run the mixin's initialisation process.
this.initializeMixin();
}
} else {
logger.warn(`${this.name}: mixin ${mixins[i]} does not exist.`);
}
}
// Don't keep initialiseMixin from last mixin.
if (this.initializeMixin === 'function') {
delete this.initializeMixin;
}
}
/**
* Change the active strategy.
*
* @public
* @method setActiveStrategy
* @param {string} strategy
* @returns {null}
*/
setActiveStrategy(strategy) {
this.activeStrategy = strategy;
if (globalConfigurationModule.configuration.showSVGCursors) {
this.changeCursor(this.element, strategy);
}
}
/**
* Function responsible for changing the Cursor, according to the strategy.
* @param {HTMLElement} element
* @param {string} strategy The strategy to be used on Tool
* @public
* @returns {void}
*/
changeCursor(element, strategy) {
// Necessary to avoid setToolCursor call without elements, which throws an error.
if (!element) {
return;
}
// If there are cursors set per strategy, change the cursor.
const cursor = this._cursors[strategy];
if (cursor) {
this.svgCursor = cursor;
if (this.mode === 'active') {
setToolCursor(element, cursor);
// External.cornerstone.updateImage(element);
}
}
}
// ===================================================================
// Virtual Methods - Have default behavior but may be overridden.
// ===================================================================
//
// MOUSE
//
/**
* Callback that takes priority if the tool is active, before `MOUSE_DOWN`
* events are processed. Does nothing by default.
*
* @callback BaseTool~preMouseDownCallback
* @param {CornerstoneTools.event:cornerstonetoolsmousedown} evt
* @returns {boolean} consumedEvent - True if function consumed the event.
*/
/**
* Callback that takes priority if the tool is active, after `MOUSE_DOWN`
* events are processed. Does nothing by default.
*
* @callback BaseTool~postMouseDownCallback
* @param {CornerstoneTools.event:cornerstonetoolsmousedown} evt
* @returns {boolean} consumedEvent - True if function consumed the event.
*/
/**
* Callback that is called if the tool is active, after `MOUSE_DOWN`
* events are processed. Does nothing by default.
*
* @virtual
* @param {type} evt
* @returns {boolean} consumedEvent - True if function consumed the event.
*/
/**
* Callback that takes priority if the tool is active, before `TOUCH_START`
* events are processed. Does nothing by default.
*
* @virtual
* @param {type} evt
* @returns {boolean} consumedEvent - True if function consumed the event.
*/
/**
* Callback that is called if the tool is active, after `TOUCH_START`
* events are processed. Does nothing by default.
*
* @virtual
* @param {type} evt
* @returns {boolean} consumedEvent - True if function consumed the event.
*/
}
export default BaseTool;
BaseAnnotationTool
- 在基石画布上创建和显示注释的工具的抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159class BaseAnnotationTool extends BaseTool {
// ===================================================================
// Abstract Methods - Must be implemented.
// ===================================================================
/**
* Creates a new annotation.
*
* @method createNewMeasurement
* @memberof Tools.Base.BaseAnnotationTool
*
* @param {type} evt description
* @returns {type} description
*/
// eslint-disable-next-line no-unused-vars
createNewMeasurement(evt) {
throw new Error(
`Method createNewMeasurement not implemented for ${this.name}.`
);
}
/**
*
* Returns true if the given coords are need the tool.
*
* @method pointNearTool
* @memberof Tools.Base.BaseAnnotationTool
*
* @param {*} element
* @param {*} data
* @param {*} coords
* @param {string} [interactionType=mouse]
* @returns {boolean} If the point is near the tool
*/
// eslint-disable-next-line no-unused-vars
pointNearTool(element, data, coords, interactionType = 'mouse') {
throw new Error(`Method pointNearTool not implemented for ${this.name}.`);
}
/**
* Returns the distance in px from the given coords to the closest handle of the annotation.
*
* @method distanceFromPoint
* @memberof Tools.Base.BaseAnnotationTool
*
* @param {*} element
* @param {*} data
* @param {*} coords
* @returns {number} - the distance in px from the provided coordinates to the
* closest rendered portion of the annotation. -1 if the distance cannot be
* calculated.
*/
// eslint-disable-next-line no-unused-vars
distanceFromPoint(element, data, coords) {
throw new Error(
`Method distanceFromPoint not implemented for ${this.name}.`
);
}
/**
* Used to redraw the tool's annotation data per render
*
* @abstract
* @param {*} evt
* @returns {void}
*/
// eslint-disable-next-line no-unused-vars
renderToolData(evt) {
throw new Error(`renderToolData not implemented for ${this.name}.`);
}
// ===================================================================
// Virtual Methods - Have default behavior but may be overriden.
// ===================================================================
/**
* Event handler for MOUSE_MOVE event.
*
* @abstract
* @event
* @param {Object} evt - The event.
* @returns {boolean} - True if the image needs to be updated
*/
mouseMoveCallback(evt) {
const { element, currentPoints } = evt.detail;
const coords = currentPoints.canvas;
const toolState = getToolState(element, this.name);
let imageNeedsUpdate = false;
for (let d = 0; d < toolState.data.length; d++) {
const data = toolState.data[d];
// Hovering a handle?
if (handleActivator(element, data.handles, coords) === true) {
imageNeedsUpdate = true;
}
// Tool data's 'active' does not match coordinates
// TODO: can't we just do an if/else and save on a pointNearTool check?
const nearToolAndNotMarkedActive =
this.pointNearTool(element, data, coords, 'mouse') && !data.active;
const notNearToolAndMarkedActive =
!this.pointNearTool(element, data, coords, 'mouse') && data.active;
if (nearToolAndNotMarkedActive || notNearToolAndMarkedActive) {
data.active = !data.active;
imageNeedsUpdate = true;
}
}
return imageNeedsUpdate;
}
/**
* Custom callback for when a handle is selected.
* @method handleSelectedCallback
* @memberof Tools.Base.BaseAnnotationTool
*
* @param {*} evt -
* @param {*} toolData -
* @param {*} handle - The selected handle.
* @param {String} interactionType -
* @returns {void}
*/
handleSelectedCallback(evt, toolData, handle, interactionType = 'mouse') {
moveHandleNearImagePoint(evt, this, toolData, handle, interactionType);
}
/**
* Custom callback for when a tool is selected.
*
* @method toolSelectedCallback
* @memberof Tools.Base.BaseAnnotationTool
*
* @param {*} evt
* @param {*} annotation
* @param {string} [interactionType=mouse]
* @returns {void}
*/
toolSelectedCallback(evt, annotation, interactionType = 'mouse') {
moveAnnotation(evt, this, annotation, interactionType);
}
/**
* Updates cached statistics for the tool's annotation data on the element
*
* @param {*} image
* @param {*} element
* @param {*} data
* @returns {void}
*/
updateCachedStats(image, element, data) {
// eslint-disable-line
logger.warn(`updateCachedStats not implemented for ${this.name}.`);
}
}
export default BaseAnnotationTool;
BaseBrushTool
- 用于操作要在基石画布上显示的遮罩数据的工具的抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338class BaseBrushTool extends BaseTool {
constructor(props, defaultProps = {}) {
if (!defaultProps.configuration) {
defaultProps.configuration = { alwaysEraseOnClick: false };
}
super(props, defaultProps);
this.updateOnMouseMove = true;
this.hideDefaultCursor = true;
this._drawing = false;
this._drawingMouseUpCallback = this._drawingMouseUpCallback.bind(this);
}
// ===================================================================
// Abstract Methods - Must be implemented.
// ===================================================================
/**
* Helper function for rendering the brush.
*
* @abstract
* @param {Object} evt - The event.
* @returns {void}
*/
// eslint-disable-next-line no-unused-vars
renderBrush(evt) {
throw new Error(`Method renderBrush not implemented for ${this.name}.`);
}
/**
* Paints the data to the labelmap.
*
* @protected
* @abstract
* @param {Object} evt The event.
* @returns {void}
*/
// eslint-disable-next-line no-unused-vars
_paint(evt) {
throw new Error(`Method _paint not implemented for ${this.name}.`);
}
// ===================================================================
// Virtual Methods - Have default behavior but may be overriden.
// ===================================================================
/**
* Event handler for MOUSE_DRAG event.
*
* @override
* @abstract
* @event
* @param {Object} evt - The event.
*/
mouseDragCallback(evt) {
const { currentPoints } = evt.detail;
this._lastImageCoords = currentPoints.image;
// Safety measure incase _startPainting is overridden and doesn't always
// start a paint.
if (this._drawing) {
this._paint(evt);
}
}
/**
* Event handler for MOUSE_DOWN event.
*
* @override
* @abstract
* @event
* @param {Object} evt - The event.
*/
preMouseDownCallback(evt) {
const eventData = evt.detail;
const { element, currentPoints } = eventData;
this._startPainting(evt);
this._lastImageCoords = currentPoints.image;
this._drawing = true;
this._startListeningForMouseUp(element);
this._paint(evt);
return true;
}
/**
* Initialise painting with BaseBrushTool.
*
* @abstract
* @event
* @param {Object} evt - The event.
* @returns {void}
*/
_startPainting(evt) {
const eventData = evt.detail;
const element = eventData.element;
const { configuration, getters } = segmentationModule;
const {
labelmap2D,
labelmap3D,
currentImageIdIndex,
activeLabelmapIndex,
} = getters.labelmap2D(element);
const shouldErase =
this._isCtrlDown(eventData) || this.configuration.alwaysEraseOnClick;
this.paintEventData = {
labelmap2D,
labelmap3D,
currentImageIdIndex,
activeLabelmapIndex,
shouldErase,
};
if (configuration.storeHistory) {
const previousPixelData = labelmap2D.pixelData.slice();
this.paintEventData.previousPixelData = previousPixelData;
}
}
/**
* End painting with BaseBrushTool.
*
* @abstract
* @event
* @param {Object} evt - The event.
* @returns {void}
*/
_endPainting(evt) {
const { configuration, setters } = segmentationModule;
const { labelmap2D, currentImageIdIndex } = this.paintEventData;
// Grab the labels on the slice.
const segmentSet = new Set(labelmap2D.pixelData);
const iterator = segmentSet.values();
const segmentsOnLabelmap = [];
let done = false;
while (!done) {
const next = iterator.next();
done = next.done;
if (!done) {
segmentsOnLabelmap.push(next.value);
}
}
labelmap2D.segmentsOnLabelmap = segmentsOnLabelmap;
if (configuration.storeHistory) {
const { previousPixelData } = this.paintEventData;
const newPixelData = labelmap2D.pixelData;
const operation = {
imageIdIndex: currentImageIdIndex,
diff: getDiffBetweenPixelData(previousPixelData, newPixelData),
};
setters.pushState(this.element, [operation]);
}
triggerLabelmapModifiedEvent(this.element);
}
// ===================================================================
// Implementation interface
// ===================================================================
/**
* Event handler for MOUSE_MOVE event.
*
* @override
* @abstract
* @event
* @param {Object} evt - The event.
*/
mouseMoveCallback(evt) {
const { currentPoints } = evt.detail;
this._lastImageCoords = currentPoints.image;
}
/**
* Used to redraw the tool's cursor per render.
*
* @override
* @param {Object} evt - The event.
*/
renderToolData(evt) {
const eventData = evt.detail;
const element = eventData.element;
// Only brush needs to render.
if (isToolActiveForElement(element, this.name)) {
// Call the hover event for the brush
this.renderBrush(evt);
}
}
/**
* Event handler for switching mode to passive.
*
* @override
* @event
* @param {Object} evt - The event.
*/
// eslint-disable-next-line no-unused-vars
passiveCallback(evt) {
try {
external.cornerstone.updateImage(this.element);
} catch (error) {
// It is possible that the image has not been loaded
// when this is called.
return;
}
}
/**
* Event handler for MOUSE_UP during the drawing event loop.
*
* @protected
* @event
* @param {Object} evt - The event.
* @returns {void}
*/
_drawingMouseUpCallback(evt) {
const eventData = evt.detail;
const element = eventData.element;
this._endPainting(evt);
this._drawing = false;
this._mouseUpRender = true;
this._stopListeningForMouseUp(element);
}
newImageCallback(evt) {
if (this._drawing) {
// End painting on one slice and start on another.
this._endPainting(evt);
this._startPainting(evt);
}
}
/**
* Adds modify loop event listeners.
*
* @protected
* @param {Object} element - The viewport element to add event listeners to.
* @modifies {element}
* @returns {void}
*/
_startListeningForMouseUp(element) {
element.removeEventListener(EVENTS.MOUSE_UP, this._drawingMouseUpCallback);
element.removeEventListener(
EVENTS.MOUSE_CLICK,
this._drawingMouseUpCallback
);
element.addEventListener(EVENTS.MOUSE_UP, this._drawingMouseUpCallback);
element.addEventListener(EVENTS.MOUSE_CLICK, this._drawingMouseUpCallback);
external.cornerstone.updateImage(element);
}
/**
* Adds modify loop event listeners.
*
* @protected
* @param {Object} element - The viewport element to add event listeners to.
* @modifies {element}
* @returns {void}
*/
_stopListeningForMouseUp(element) {
element.removeEventListener(EVENTS.MOUSE_UP, this._drawingMouseUpCallback);
element.removeEventListener(
EVENTS.MOUSE_CLICK,
this._drawingMouseUpCallback
);
external.cornerstone.updateImage(element);
}
// ===================================================================
// Brush API. This is effectively a wrapper around the store.
// ===================================================================
/**
* Increases the brush size
*
* @public
* @api
* @returns {void}
*/
increaseBrushSize() {
const { configuration, setters } = segmentationModule;
const oldRadius = configuration.radius;
let newRadius = Math.floor(oldRadius * 1.2);
// If e.g. only 2 pixels big. Math.floor(2*1.2) = 2.
// Hence, have minimum increment of 1 pixel.
if (newRadius === oldRadius) {
newRadius += 1;
}
setters.radius(newRadius);
}
/**
* Decreases the brush size
*
* @public
* @api
* @returns {void}
*/
decreaseBrushSize() {
const { configuration, setters } = segmentationModule;
const oldRadius = configuration.radius;
const newRadius = Math.floor(oldRadius * 0.8);
setters.radius(newRadius);
}
_isCtrlDown(eventData) {
return (eventData.event && eventData.event.ctrlKey) || eventData.ctrlKey;
}
}
export default BaseBrushTool;
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 随心所欲录!