diff --git a/package.json b/package.json index c6e75e0e..e0afaba8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "seamless-immutable": "^7.1.4", "shallow-equal": "^1.2.1", "storybook": "^5.3.14", + "styled-components": "^5.2.1", "transformation-matrix-js": "^2.7.6", "use-event-callback": "^0.1.0", "use-key-hook": "^1.3.0" diff --git a/src/Annotator/index.js b/src/Annotator/index.js index a00bf98e..ea964393 100644 --- a/src/Annotator/index.js +++ b/src/Annotator/index.js @@ -66,6 +66,7 @@ export const Annotator = ({ "create-point", "create-box", "create-polygon", + "create-line", "create-expanding-line", "show-mask", ], diff --git a/src/Annotator/reducers/general-reducer.js b/src/Annotator/reducers/general-reducer.js index e4e5b42e..9390fadf 100644 --- a/src/Annotator/reducers/general-reducer.js +++ b/src/Annotator/reducers/general-reducer.js @@ -377,6 +377,16 @@ export default (state: MainLayoutState, action: Action) => { [x, y] ) } + case "DRAW_LINE": { + const { regionId } = state.mode + const [region, regionIndex] = getRegion(regionId) + if (!region) return setIn(state, ["mode"], null) + return setIn(state, [...pathToActiveImage, "regions", regionIndex], { + ...region, + x2: x, + y2: y, + }) + } case "DRAW_EXPANDING_LINE": { const { regionId } = state.mode const [expandingLine, regionIndex] = getRegion(regionId) @@ -449,6 +459,16 @@ export default (state: MainLayoutState, action: Action) => { { ...polygon, points: polygon.points.concat([[x, y]]) } ) } + case "DRAW_LINE": { + const [line, regionIndex] = getRegion(state.mode.regionId) + if (!line) break + setIn(state, [...pathToActiveImage, "regions", regionIndex], { + ...line, + x2: x, + y2: y, + }) + return setIn(state, ["mode"], null) + } case "DRAW_EXPANDING_LINE": { const [expandingLine, regionIndex] = getRegion(state.mode.regionId) if (!expandingLine) break @@ -588,6 +608,27 @@ export default (state: MainLayoutState, action: Action) => { }) break } + case "create-line": { + if (state.mode && state.mode.mode === "DRAW_LINE") break + state = saveToHistory(state, "Create Line") + newRegion = { + type: "line", + x1: x, + y1: y, + x2: x, + y2: y, + highlighted: true, + editingLabels: false, + color: defaultRegionColor, + cls: defaultRegionCls, + id: getRandomId(), + } + state = setIn(state, ["mode"], { + mode: "DRAW_LINE", + regionId: newRegion.id, + }) + break + } case "create-keypoints": { state = saveToHistory(state, "Create Keypoints") const [ diff --git a/src/DemoSite/Editor.js b/src/DemoSite/Editor.js index 281b5308..ab1a2702 100644 --- a/src/DemoSite/Editor.js +++ b/src/DemoSite/Editor.js @@ -179,7 +179,7 @@ const Editor = ({ onOpenAnnotator, lastOutput }: any) => { imageTagList?: Array, imageClsList?: Array, // all tools are enabled by default - enabledTools?: Array< "select" | "create-point" | "create-box" | "create-polygon">, + enabledTools?: Array< "select" | "create-point" | "create-box" | "create-polygon" | "create-line">, selectedImage?: string, // initial selected image images: Array<{ src: string, diff --git a/src/ImageCanvas/region-tools.js b/src/ImageCanvas/region-tools.js index 8970685e..77f9c86d 100644 --- a/src/ImageCanvas/region-tools.js +++ b/src/ImageCanvas/region-tools.js @@ -48,6 +48,16 @@ export type Polygon = {| open?: boolean, points: Array<[number, number]>, |} + +export type Line = {| + ...$Exact, + type: "line", + x1: number, + y1: number, + x2: number, + y2: number, +|} + export type ExpandingLine = {| ...$Exact, type: "expanding-line", @@ -132,6 +142,9 @@ export const getEnclosingBox = (region: Region) => { box.h = Math.max(...region.points.map(({ x, y }) => y)) - box.y return box } + case "line": { + return { x: region.x1, y: region.y1, w: 0, h: 0 } + } case "box": { return { x: region.x, y: region.y, w: region.w, h: region.h } } diff --git a/src/MainLayout/icon-dictionary.js b/src/MainLayout/icon-dictionary.js index 3b8e8de2..cec9c3a4 100644 --- a/src/MainLayout/icon-dictionary.js +++ b/src/MainLayout/icon-dictionary.js @@ -16,6 +16,7 @@ import { faSearch, faMask, faEdit, + faChartLine, } from "@fortawesome/free-solid-svg-icons" import FullscreenIcon from "@material-ui/icons/Fullscreen" import AccessibilityNewIcon from "@material-ui/icons/AccessibilityNew" @@ -62,6 +63,9 @@ export const iconDictionary = { "create-expanding-line": () => ( ), + "create-line": () => ( + + ), "show-mask": () => ( ), diff --git a/src/MainLayout/index.js b/src/MainLayout/index.js index 70e422ba..bf63bb47 100644 --- a/src/MainLayout/index.js +++ b/src/MainLayout/index.js @@ -304,6 +304,10 @@ export const MainLayout = ({ name: "create-polygon", helperText: "Add Polygon" + getHotkeyHelpText("create_polygon"), }, + { + name: "create-line", + helperText: "Add Line", + }, { name: "create-expanding-line", helperText: "Add Expanding Line", diff --git a/src/RegionShapes/index.js b/src/RegionShapes/index.js index 5265bfff..8b46c68c 100644 --- a/src/RegionShapes/index.js +++ b/src/RegionShapes/index.js @@ -18,6 +18,19 @@ const RegionComponents = { /> )), + line: memo(({ region, iw, ih }) => ( + + + + )), box: memo(({ region, iw, ih }) => (