diff --git a/packages/lb-annotation/src/constant/tool.ts b/packages/lb-annotation/src/constant/tool.ts index 33f6a8f3d..3dcf97126 100644 --- a/packages/lb-annotation/src/constant/tool.ts +++ b/packages/lb-annotation/src/constant/tool.ts @@ -58,6 +58,8 @@ export enum EToolName { OCRRelation = 'OCRRelationTool', /** 算法分割辅助工具 */ SegmentByRect = 'segmentByRectTool', + /** 点云多边形工具 */ + PointCloudPolygon = 'pointCloudPolygon', } export enum ECheckModel { @@ -71,6 +73,7 @@ export enum ERectPattern { } export type ToolName = EToolName | EVideoToolName | EPointCloudName; +export type THybridToolName = EToolName | Array; export const TOOL_NAME: { [a: string]: string } = { [EToolName.Rect]: '拉框', diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index f31bc254c..e7b219657 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -4,9 +4,9 @@ import { ELang } from '@/constant/annotation'; import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; -import { EToolName } from '@/constant/tool'; +import { EToolName, THybridToolName } from '@/constant/tool'; import { IPolygonData } from '@/types/tool/polygon'; -import { HybridToolUtils, THybridToolName, ToolScheduler } from './scheduler'; +import { HybridToolUtils, ToolScheduler } from './scheduler'; interface IProps { container: HTMLElement; diff --git a/packages/lb-annotation/src/core/pointCloud/annotation.ts b/packages/lb-annotation/src/core/pointCloud/annotation.ts index d1c0f7d73..620ecd46d 100644 --- a/packages/lb-annotation/src/core/pointCloud/annotation.ts +++ b/packages/lb-annotation/src/core/pointCloud/annotation.ts @@ -4,12 +4,13 @@ * @author Ron */ -import { IPointCloudBox, PointCloudUtils, IPointCloudConfig } from '@labelbee/lb-utils'; -import { EPolygonPattern } from '@/constant/tool'; +import { IPointCloudBox, IPointCloudConfig, PointCloudUtils } from '@labelbee/lb-utils'; +import { EPolygonPattern, EToolName, THybridToolName } from '@/constant/tool'; import { CanvasScheduler } from '@/newCore'; -import { IPolygonData } from '@/types/tool/polygon'; +import { IPolygonData, IPolygonPoint } from '@/types/tool/polygon'; import { PointCloud } from '.'; import PointCloud2dOperation, { IPointCloud2dOperationProps } from '../toolOperation/pointCloud2dOperation'; +import { HybridToolUtils, ToolScheduler } from '../scheduler'; interface IPointCloudAnnotationOperation { updateData: (pcdPath: string, result: string) => void; @@ -25,6 +26,7 @@ interface IPointCloudAnnotationProps { config: IPointCloudConfig; checkMode?: boolean; + toolName: THybridToolName; } const createEmptyImage = (size: { width: number; height: number }) => { @@ -43,19 +45,33 @@ const createEmptyImage = (size: { width: number; height: number }) => { export class PointCloudAnnotation implements IPointCloudAnnotationOperation { public pointCloudInstance: PointCloud; - public pointCloud2dOperation: PointCloud2dOperation; + public pointCloud2dOperation!: PointCloud2dOperation; public canvasScheduler: CanvasScheduler; + public toolScheduler: ToolScheduler; + + public toolInstance: any; // 用于存储当前工具实例 + public config: IPointCloudConfig; - constructor({ size, container, pcdPath, polygonOperationProps, config, checkMode }: IPointCloudAnnotationProps) { + constructor({ + size, + container, + pcdPath, + polygonOperationProps, + config, + checkMode, + toolName, + }: IPointCloudAnnotationProps) { const defaultOrthographic = this.getDefaultOrthographic(size); const imgSrc = createEmptyImage(size); const image = new Image(); image.src = imgSrc; + + const toolScheduler = new ToolScheduler({ container, size, toolName }); const canvasScheduler = new CanvasScheduler({ container }); // 1. PointCloud initialization @@ -73,11 +89,9 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { // 2. PointCloud2dOperation initialization const defaultPolygonProps = { - container, size, config: JSON.stringify(config), imgNode: image, - isAppend: false, checkMode, // forbidOperation: true, // forbidOperation: !!checkMode, @@ -86,17 +100,39 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { Object.assign(defaultPolygonProps, polygonOperationProps); } - const polygonOperation = new PointCloud2dOperation(defaultPolygonProps); + // init operations + let toolList: EToolName[] = []; - polygonOperation.eventBinding(); - polygonOperation.setPattern(EPolygonPattern.Rect); - - canvasScheduler.createCanvas(polygonOperation.canvas, { size }); + if (HybridToolUtils.isSingleTool(toolName)) { + toolList = [toolName] as EToolName[]; + } else { + toolList = toolName as EToolName[]; + } + toolList.forEach((tool, i) => { + let toolInstance; + if (tool === EToolName.PointCloudPolygon) { + const pointCloudPolygonOperation = toolScheduler.createOperation(tool, image, defaultPolygonProps); + pointCloudPolygonOperation.eventBinding(); + pointCloudPolygonOperation.setPattern(EPolygonPattern.Rect); + this.toolInstance = pointCloudPolygonOperation; + this.toolInstance.eventBinding(); + this.pointCloud2dOperation = pointCloudPolygonOperation; + } else { + toolInstance = toolScheduler.createOperation(tool, image, config); + } + if (i === toolList.length - 1) { + if (!this.toolInstance) { + this.toolInstance = toolInstance; + this.toolInstance.eventBinding(); + } + // The last one by default is the topmost operation. + } + }); // 3. Data record - this.pointCloud2dOperation = polygonOperation; this.pointCloudInstance = pointCloud; this.canvasScheduler = canvasScheduler; + this.toolScheduler = toolScheduler; this.config = config; } @@ -150,8 +186,11 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { * Notice. It needs to update polygon if it shows polygon. * (Like `ptCtx.topViewInstance.updatePolygonList(ptCtx.pointCloudBoxList);`) */ - this.pointCloud2dOperation.setImgNode(image); - this.pointCloud2dOperation.initImgPos(); + this.toolInstance.setImgNode(image); + this.toolInstance.initImgPos(); + + // this.pointCloud2dOperation.setImgNode(image); + // this.pointCloud2dOperation.initImgPos(); }; // It need to update directly @@ -166,7 +205,7 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { } public updatePolygonList = (pointCloudDataList: IPointCloudBox[], extraList?: IPolygonData[]) => { - let polygonList = pointCloudDataList.map((v) => { + let polygonList = pointCloudDataList.map((v: IPointCloudBox) => { const { polygon2d: pointList } = this.pointCloudInstance.getBoxTopPolygon2DCoordinate(v); return { id: v.id, @@ -181,9 +220,9 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { if (extraList) { // Convert extraList(polygonList) from PointCloud coordinate to Canvas Coordinate polygonList = polygonList.concat( - extraList.map((v) => ({ + extraList.map((v: IPolygonData) => ({ ...v, - pointList: v?.pointList?.map((point) => + pointList: v?.pointList?.map((point: IPolygonPoint) => PointCloudUtils.transferWorld2Canvas(point, this.pointCloud2dOperation.size), ), })), @@ -208,6 +247,20 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { this.addPolygonListOnTopView(result); } + /** + * switch to chosen canvas。 + * + */ + public switchToCanvas(toolName: EToolName) { + const newInstance = this.toolScheduler.switchToCanvas(toolName); + if (newInstance) { + newInstance.eventBinding(); + this.toolInstance = newInstance; + return newInstance; + } + return this.toolInstance; + } + /** * Init All Position * 1. PointCloud camera change to topView diff --git a/packages/lb-annotation/src/core/scheduler.ts b/packages/lb-annotation/src/core/scheduler.ts index 5cafead1d..1881ac54e 100644 --- a/packages/lb-annotation/src/core/scheduler.ts +++ b/packages/lb-annotation/src/core/scheduler.ts @@ -5,15 +5,13 @@ */ import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; -import { EToolName } from '@/constant/tool'; +import { EToolName, THybridToolName } from '@/constant/tool'; import { getCurrentOperation } from '@/utils/tool/EnhanceCommonToolUtils'; import { RectOperation } from './toolOperation/rectOperation'; import PolygonOperation from './toolOperation/polygonOperation'; import { BasicToolOperation } from './toolOperation/basicToolOperation'; import SegmentByRect from './toolOperation/segmentByRect'; -export type THybridToolName = EToolName | Array; - interface IToolSchedulerOperation {} interface IToolSchedulerProps { @@ -71,6 +69,8 @@ export class ToolScheduler implements IToolSchedulerOperation { private toolOperationDom: Array = []; + private toolOperationNameList: Array = []; + private size: ISize; private config: string; // 定义 TODO!! @@ -143,7 +143,6 @@ export class ToolScheduler implements IToolSchedulerOperation { dom.style.height = `${height}px`; const zIndex = this.toolOperationList.length + 1; dom.style.zIndex = `${zIndex}`; - return dom; } @@ -221,6 +220,7 @@ export class ToolScheduler implements IToolSchedulerOperation { this.container.appendChild(dom); this.toolOperationList.push(toolInstance); + this.toolOperationNameList.push(tool); this.toolOperationDom.push(dom); return toolInstance; @@ -278,6 +278,40 @@ export class ToolScheduler implements IToolSchedulerOperation { return this.toolOperationList[0]; } + /** + * switch to canvas by given toolName + * TODO: change operationList to operationMap + */ + public switchToCanvas(toolName: EToolName) { + const chosenIndex = this.toolOperationNameList.indexOf(toolName); + if (chosenIndex < 0 || this.toolOperationList.length < 1 || chosenIndex > this.toolOperationDom.length - 1) { + return; + } + const lastOneIndex = this.toolOperationDom.length - 1; + const chosenDom = this.toolOperationDom[chosenIndex]; + const lastOneDom = this.toolOperationDom[lastOneIndex]; + + if (!chosenDom || !lastOneDom) { + return; + } + + const temp = lastOneDom.style.zIndex; + lastOneDom.style.zIndex = `${chosenDom.style.zIndex}`; + chosenDom.style.zIndex = `${temp}`; + + // The original top-level operation clears the data + this.toolOperationList[lastOneIndex].clearActiveStatus?.(); + this.toolOperationList[lastOneIndex].clearCursorLine?.(); + this.toolOperationList[lastOneIndex].render(); + + // swap + this.toolOperationList = arraySwap(this.toolOperationList, lastOneIndex, chosenIndex); + this.toolOperationDom = arraySwap(this.toolOperationDom, lastOneIndex, chosenIndex); + this.toolOperationNameList = arraySwap(this.toolOperationNameList, lastOneIndex, chosenIndex); + + return this.toolOperationList[lastOneIndex]; + } + public destroyAllLayer() { this.toolOperationList.forEach((toolInstance) => { toolInstance.destroyCanvas(); diff --git a/packages/lb-annotation/src/utils/tool/EnhanceCommonToolUtils.ts b/packages/lb-annotation/src/utils/tool/EnhanceCommonToolUtils.ts index 6b25353a7..be0cb3bdc 100644 --- a/packages/lb-annotation/src/utils/tool/EnhanceCommonToolUtils.ts +++ b/packages/lb-annotation/src/utils/tool/EnhanceCommonToolUtils.ts @@ -6,6 +6,7 @@ import { ECheckModel, EToolName } from '@/constant/tool'; import ScribbleTool from '@/core/toolOperation/ScribbleTool'; +import PointCloud2dOperation from '@/core/toolOperation/pointCloud2dOperation'; import CheckOperation from '../../core/toolOperation/checkOperation'; import PolygonOperation from '../../core/toolOperation/polygonOperation'; import RectOperationAsNewName from '../../core/toolOperation/rectOperation'; @@ -37,6 +38,8 @@ const getCurrentOperation = (toolName: EToolName | ECheckModel) => { return TextToolOperation; case EToolName.ScribbleTool: return ScribbleTool; + case EToolName.PointCloudPolygon: + return PointCloud2dOperation; default: throw new Error('not match tool'); } diff --git a/packages/lb-components/src/components/pointCloudView/PointCloudBackView.tsx b/packages/lb-components/src/components/pointCloudView/PointCloudBackView.tsx index a37960613..39b1e4e50 100644 --- a/packages/lb-components/src/components/pointCloudView/PointCloudBackView.tsx +++ b/packages/lb-components/src/components/pointCloudView/PointCloudBackView.tsx @@ -9,6 +9,7 @@ import { MathUtils, PointCloud, PointCloudAnnotation, + THybridToolName, } from '@labelbee/lb-annotation'; import { getClassName } from '@/utils/dom'; import { PointCloudContainer } from './PointCloudLayout'; @@ -34,6 +35,7 @@ import useSize from '@/hooks/useSize'; import EmptyPage from './components/EmptyPage'; import { useTranslation } from 'react-i18next'; import { LabelBeeContext } from '@/store/ctx'; +import ToolUtils from '@/utils/ToolUtils'; /** * 统一一下,将其拓展为 二维转换为 三维坐标的转换 @@ -185,6 +187,7 @@ const PointCloudSideView = ({ currentData, config, checkMode }: IA2MapStateProps polygonOperationProps: { showDirectionLine: false, forbidAddNew: true }, config, checkMode, + toolName: ToolUtils.getPointCloudToolList() as THybridToolName, }); ptCtx.setBackViewInstance(pointCloudAnnotation); } diff --git a/packages/lb-components/src/components/pointCloudView/PointCloudContext.tsx b/packages/lb-components/src/components/pointCloudView/PointCloudContext.tsx index 922325c38..238bec22e 100644 --- a/packages/lb-components/src/components/pointCloudView/PointCloudContext.tsx +++ b/packages/lb-components/src/components/pointCloudView/PointCloudContext.tsx @@ -53,8 +53,8 @@ export interface IPointCloudContext extends IPointCloudContextInstances { defaultAttribute: string; setDefaultAttribute: (defaultAttribute: string) => void; - pointCloudPattern: EToolName.Rect | EToolName.Polygon; - setPointCloudPattern: (toolName: EToolName.Rect | EToolName.Polygon) => void; + pointCloudPattern: EToolName.Rect | EToolName.Polygon | EToolName.Point | EToolName.Line; + setPointCloudPattern: (toolName: EToolName.Rect | EToolName.Polygon | EToolName.Point | EToolName.Line) => void; } export const PointCloudContext = React.createContext({ @@ -107,7 +107,7 @@ export const PointCloudProvider: React.FC<{}> = ({ children }) => { const [backViewInstance, setBackViewInstance] = useState(); const [mainViewInstance, setMainViewInstance] = useState(); const [defaultAttribute, setDefaultAttribute] = useState(''); - const [pointCloudPattern, setPointCloudPattern] = useState( + const [pointCloudPattern, setPointCloudPattern] = useState( EToolName.Rect, ); const history = useRef(new ActionsHistory()).current; diff --git a/packages/lb-components/src/components/pointCloudView/PointCloudSideView.tsx b/packages/lb-components/src/components/pointCloudView/PointCloudSideView.tsx index 9a5ec9d49..91f26f47a 100644 --- a/packages/lb-components/src/components/pointCloudView/PointCloudSideView.tsx +++ b/packages/lb-components/src/components/pointCloudView/PointCloudSideView.tsx @@ -3,7 +3,7 @@ * @createdate 2022-07-11 * @author Ron */ -import { PointCloud, PointCloudAnnotation } from '@labelbee/lb-annotation'; +import { PointCloud, PointCloudAnnotation, THybridToolName } from '@labelbee/lb-annotation'; import { getClassName } from '@/utils/dom'; import { PointCloudContainer } from './PointCloudLayout'; import React, { useEffect, useRef } from 'react'; @@ -18,6 +18,8 @@ import EmptyPage from './components/EmptyPage'; import useSize from '@/hooks/useSize'; import { useTranslation } from 'react-i18next'; import { LabelBeeContext } from '@/store/ctx'; +import ToolUtils from '@/utils/ToolUtils'; + /** * Get the offset from canvas2d-coordinate to world coordinate * @param currentPos @@ -93,7 +95,8 @@ const PointCloudSideView: React.FC = ({ config, check size, polygonOperationProps: { showDirectionLine: false, forbidAddNew: true }, config, - checkMode + checkMode, + toolName: ToolUtils.getPointCloudToolList() as THybridToolName, }); ptCtx.setSideViewInstance(pointCloudAnnotation); // }; diff --git a/packages/lb-components/src/components/pointCloudView/PointCloudTopView.tsx b/packages/lb-components/src/components/pointCloudView/PointCloudTopView.tsx index bb07fa86a..e3447a268 100644 --- a/packages/lb-components/src/components/pointCloudView/PointCloudTopView.tsx +++ b/packages/lb-components/src/components/pointCloudView/PointCloudTopView.tsx @@ -7,7 +7,7 @@ import { getClassName } from '@/utils/dom'; import { FooterDivider } from '@/views/MainView/toolFooter'; import { ZoomController } from '@/views/MainView/toolFooter/ZoomController'; import { DownSquareOutlined, UpSquareOutlined } from '@ant-design/icons'; -import { cTool, PointCloudAnnotation } from '@labelbee/lb-annotation'; +import { cTool, PointCloudAnnotation, THybridToolName } from '@labelbee/lb-annotation'; import { IPolygonData, PointCloudUtils, UpdatePolygonByDragList } from '@labelbee/lb-utils'; import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { PointCloudContext } from './PointCloudContext'; @@ -26,6 +26,7 @@ import { useTranslation } from 'react-i18next'; import { LabelBeeContext } from '@/store/ctx'; import { jsonParser } from '@/utils'; import { TDrawLayerSlot } from '@/types/main'; +import ToolUtils from '@/utils/ToolUtils'; const { EPolygonPattern } = cTool; @@ -184,6 +185,7 @@ const PointCloudTopView: React.FC = ({ pcdPath: currentData.url, config, checkMode, + toolName: ToolUtils.getPointCloudToolList() as THybridToolName, }); ptCtx.setTopViewInstance(pointCloudAnnotation); @@ -195,7 +197,7 @@ const PointCloudTopView: React.FC = ({ return; } - const { pointCloud2dOperation: TopView2dOperation } = ptCtx.topViewInstance; + const { toolInstance: TopView2dOperation } = ptCtx.topViewInstance; TopView2dOperation.singleOn('polygonCreated', (polygon: IPolygonData, zoom: number) => { if (TopView2dOperation.pattern === EPolygonPattern.Normal || !currentData?.url) { @@ -221,7 +223,7 @@ const PointCloudTopView: React.FC = ({ }); }); - TopView2dOperation.singleOn('deletedObject', ({ id }) => { + TopView2dOperation.singleOn('deletedObject', ({ id }: { id: any }) => { deletePointCloudBox(id); deletePolygon(id); }); diff --git a/packages/lb-components/src/components/pointCloudView/hooks/useStatus.ts b/packages/lb-components/src/components/pointCloudView/hooks/useStatus.ts index 01ba34c56..e1f8b13ad 100644 --- a/packages/lb-components/src/components/pointCloudView/hooks/useStatus.ts +++ b/packages/lb-components/src/components/pointCloudView/hooks/useStatus.ts @@ -36,7 +36,7 @@ export const useStatus = () => { topViewInstance?.pointCloud2dOperation.clearActiveStatus(); topViewInstance?.pointCloud2dOperation.clearResult(); - + syncAllViewPointCloudColor([]); // Add History @@ -44,26 +44,32 @@ export const useStatus = () => { }; const updatePointCloudPattern = (toolName: any) => { - const polygon2dOperation = topViewInstance?.pointCloud2dOperation; - if (!polygon2dOperation) { - return; - } - - polygon2dOperation.clearActiveStatus(); - if (toolName === pointCloudPattern) { return; } switch (toolName) { case EToolName.Rect: - polygon2dOperation.setPattern(EPolygonPattern.Rect); + topViewInstance?.switchToCanvas(EToolName.PointCloudPolygon) + topViewInstance?.toolInstance.setPattern(EPolygonPattern.Rect) + // polygon2dOperation.setPattern(EPolygonPattern.Rect); setPointCloudPattern(EToolName.Rect); break; case EToolName.Polygon: - polygon2dOperation.setPattern(EPolygonPattern.Normal); + topViewInstance?.switchToCanvas(EToolName.PointCloudPolygon) + topViewInstance?.toolInstance.setPattern(EPolygonPattern.Normal) + // polygon2dOperation.setPattern(EPolygonPattern.Normal); setPointCloudPattern(EToolName.Polygon); break; + case EToolName.Point: + // polygon2dOperation.setPattern(EPolygonPattern.Point); + topViewInstance?.switchToCanvas(EToolName.Point) + setPointCloudPattern(EToolName.Point); + break; + case EToolName.Line: + topViewInstance?.switchToCanvas(EToolName.Line) + setPointCloudPattern(EToolName.Line); + break; } }; diff --git a/packages/lb-components/src/data/enums/ToolType.ts b/packages/lb-components/src/data/enums/ToolType.ts index eafbd2e42..7a05aa875 100644 --- a/packages/lb-components/src/data/enums/ToolType.ts +++ b/packages/lb-components/src/data/enums/ToolType.ts @@ -41,6 +41,8 @@ export enum EToolName { OCRRelation = 'OCRRelationTool', /** 算法分割辅助工具 */ SegmentByRect = 'segmentByRectTool', + /** 点云多边形工具 */ + PointCloudPolygon = 'pointCloudPolygon', } diff --git a/packages/lb-components/src/index.tsx b/packages/lb-components/src/index.tsx index 3b9abfc99..5bf1f5545 100644 --- a/packages/lb-components/src/index.tsx +++ b/packages/lb-components/src/index.tsx @@ -54,4 +54,4 @@ export default React.forwardRef(OutputApp); export { AnnotationView, PointCloudAnnotationView, i18n, VideoTagTool }; -export * from './constant'; \ No newline at end of file +export * from './constant'; diff --git a/packages/lb-components/src/utils/ToolUtils.ts b/packages/lb-components/src/utils/ToolUtils.ts index 6af9e692c..aa3d2162d 100644 --- a/packages/lb-components/src/utils/ToolUtils.ts +++ b/packages/lb-components/src/utils/ToolUtils.ts @@ -5,7 +5,7 @@ */ import { cTool } from '@labelbee/lb-annotation'; -const { EVideoToolName, EPointCloudName } = cTool; +const { EVideoToolName, EPointCloudName, EToolName } = cTool; class ToolUtils { public static isVideoTool(tool?: string) { @@ -15,6 +15,10 @@ class ToolUtils { public static isPointCloudTool(tool?: string) { return tool ? (Object.values(EPointCloudName) as string[]).includes(tool) : false; } + + public static getPointCloudToolList() { + return [EToolName.Point, EToolName.Line, EToolName.PointCloudPolygon] + } } export default ToolUtils; diff --git a/packages/lb-components/src/views/MainView/sidebar/ToolIcons.tsx b/packages/lb-components/src/views/MainView/sidebar/ToolIcons.tsx index e381dbc87..c82fdcaa1 100644 --- a/packages/lb-components/src/views/MainView/sidebar/ToolIcons.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/ToolIcons.tsx @@ -63,7 +63,7 @@ export const ToolIcons = ({ const { i18n } = useTranslation(); const renderTools = toolList?.filter((item) => { if (toolName === (EPointCloudName.PointCloud as unknown as EToolName)) { - return [EToolName.Polygon, EToolName.Rect].includes(item?.toolName); + return [EToolName.Polygon, EToolName.Rect, EToolName.Point, EToolName.Line].includes(item?.toolName); } return item?.toolName === toolName;