/
vshmidt
/
label-studio
Обзор
Документация
Войти
/
vshmidt
/
label-studio
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
web/libs/editor/src/tools/Bitmask.jsx
243 строки
6 KB
Michael Malyuk
feat: BROS-114: Global Custom Hotkeys (#7784)
05 июл 2025, 19:04
Не верифицирован
05 июл 2025, 19:04
f0fb90a
Код
Авторство
О чём код?
import { observer } from "mobx-react"; import { types } from "mobx-state-tree"; import BaseTool from "./Base"; import ToolMixin from "../mixins/Tool"; import { clamp, findClosestParent } from "../utils/utilities"; import { DrawingTool } from "../mixins/DrawingTool"; import { Tool } from "../components/Toolbar/Tool"; import { Range } from "../common/Range/Range"; import { NodeViews } from "../components/Node/Node"; const MIN_SIZE = 1; const MAX_SIZE = 50; const IconDot = ({ size }) => { return ( <span style={{ display: "block", width: size, height: size, background: "rgba(0, 0, 0, 0.25)", borderRadius: "100%", }} /> ); }; const ToolView = observer(({ item }) => { return ( <Tool label="Bitmask" ariaLabel="bitmask-tool" active={item.selected} shortcut={item.shortcut} extraShortcuts={item.extraShortcuts} icon={item.iconClass} tool={item} onClick={() => { if (item.selected) return; item.manager.selectTool(item, true); }} controls={item.controls} /> ); }); const _Tool = types .model("BitmaskTool", { strokeWidth: types.optional(types.number, 15), group: "segmentation", shortcut: "tool:brush", smart: true, unselectRegionOnToolChange: false, }) .volatile(() => ({ canInteractWithRegions: false, })) .views((self) => ({ get viewClass() { return () => <ToolView item={self} />; }, get iconComponent() { return self.dynamic ? NodeViews.BitmaskRegionModel.altIcon : NodeViews.BitmaskRegionModel.icon; }, get tagTypes() { return { stateTypes: "bitmasklabels", controlTagTypes: ["bitmasklabels", "bitmask"], }; }, get controls() { return [ <Range key="brush-size" value={self.strokeWidth} min={MIN_SIZE} max={MAX_SIZE} reverse align="vertical" minIcon={<IconDot size={8} />} maxIcon={<IconDot size={16} />} onChange={(value) => { self.setStroke(value); }} />, ]; }, get extraShortcuts() { return { "tool:decrease-tool": [ "Decrease size", () => { self.setStroke(clamp(self.strokeWidth - 5, MIN_SIZE, MAX_SIZE)); }, ], "tool:increase-tool": [ "Increase size", () => { self.setStroke(clamp(self.strokeWidth + 5, MIN_SIZE, MAX_SIZE)); }, ], }; }, })) .actions((self) => { let brush; let isFirstBrushStroke; return { commitDrawingRegion() { const { currentArea, control, obj } = self; const value = { imageDataURL: currentArea.getImageDataURL(), }; const newArea = self.annotation.createResult(value, currentArea.results[0].value.toJSON(), control, obj); currentArea.setDrawing(false); self.applyActiveStates(newArea); try { self.deleteRegion(); } catch (e) { /* do nothing*/ } newArea.notifyDrawingFinished(); return newArea; }, setStroke(val) { self.strokeWidth = val; self.updateCursor(); }, afterUpdateSelected() { self.updateCursor(); }, addPoint(x, y) { brush.addPoint(x, y, self.strokeWidth || self.control.strokeWidth); }, mouseupEv(_ev, _, [x, y]) { if (self.mode !== "drawing") return; self.addPoint(x, y); self.mode = "viewing"; brush.setDrawing(false); brush.endPath(); if (isFirstBrushStroke) { const newBrush = self.commitDrawingRegion(); self.obj.annotation.selectArea(newBrush); self.annotation.history.unfreeze(); self.obj.annotation.setIsDrawing(false); } else { self.annotation.history.unfreeze(); self.obj.annotation.setIsDrawing(false); } }, mousemoveEv(ev, _, [x, y]) { if (!self.isAllowedInteraction(ev)) return; if (self.mode !== "drawing") return; if ( !findClosestParent( ev.target, (el) => el === self.obj.stageRef.content, (el) => el.parentElement, ) ) return; self.addPoint(x, y); }, mousedownEv(ev, _, [x, y]) { if (!self.isAllowedInteraction(ev)) return; if ( !findClosestParent( ev.target, (el) => el === self.obj.stageRef.content, (el) => el.parentElement, ) ) return; const c = self.control; const o = self.obj; const hasHighlighted = o.regs.some((r) => r.highlighted); if (hasHighlighted) return; brush = self.getSelectedShape; // prevent drawing when current image is // different from image where the brush was started if (o && brush && o.multiImage && o.currentImage !== brush.item_index) return; // Reset the timer if a user started drawing again if (brush && brush.type === "bitmaskregion") { self.annotation.history.freeze(); self.mode = "drawing"; isFirstBrushStroke = false; brush.setDrawing(true); self.obj.annotation.setIsDrawing(true); } else { if (!self.canStartDrawing()) return; if (self.tagTypes.stateTypes === self.control.type && !self.control.isSelected) return; self.annotation.history.freeze(); self.mode = "drawing"; isFirstBrushStroke = true; self.obj.annotation.setIsDrawing(true); brush = self.createDrawingRegion({ imageDataURL: null, }); } brush.beginPath({ type: "add", strokeWidth: self.strokeWidth || c.strokeWidth, x, y, }); }, }; }); const BitmaskCursorMixin = types .model("BitmaskCursorMixin") .views((self) => ({ get cursorStyleRule() { const val = self.strokeWidth; return "crosshair"; }, })) .actions((self) => ({ updateCursor() { if (!self.selected || !self.obj?.stageRef) return; const stage = self.obj.stageRef; stage.container().style.cursor = self.cursorStyleRule; }, })); const Bitmask = types.compose(_Tool.name, ToolMixin, BaseTool, DrawingTool, BitmaskCursorMixin, _Tool); export { Bitmask, BitmaskCursorMixin };