From 8ab564d180b74645de52c329f488e2e40302dc17 Mon Sep 17 00:00:00 2001 From: Original-Recipe-Chicken Date: Fri, 20 Dec 2024 18:02:14 +0800 Subject: [PATCH 1/2] feat: 2DView Performance Optimization --- .../src/core/pointCloud/annotation.ts | 12 +++-- packages/lb-annotation/src/core/scheduler.ts | 4 +- .../core/toolOperation/basicToolOperation.ts | 27 +++++++++-- .../core/toolOperation/polygonOperation.ts | 11 +++-- .../src/core/toolOperation/rectOperation.ts | 48 +++++++++++++++---- .../pointCloud2DRectOperationView/index.tsx | 17 +++++-- .../hooks/usePointCloudViews.ts | 2 +- 7 files changed, 94 insertions(+), 27 deletions(-) diff --git a/packages/lb-annotation/src/core/pointCloud/annotation.ts b/packages/lb-annotation/src/core/pointCloud/annotation.ts index c97aa8c39..138e6436e 100644 --- a/packages/lb-annotation/src/core/pointCloud/annotation.ts +++ b/packages/lb-annotation/src/core/pointCloud/annotation.ts @@ -230,7 +230,11 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { this.updatePointList(sphereList); } - public updatePolygonList = (pointCloudDataList: IPointCloudBox[], extraList?: IPolygonData[]) => { + public updatePolygonList = ( + pointCloudDataList: IPointCloudBox[], + extraList?: IPolygonData[], + isDeleteSelectedID: boolean = true, + ) => { let polygonList = pointCloudDataList.map((v: IPointCloudBox) => { const { polygon2d: pointList } = this.pointCloudInstance.getBoxTopPolygon2DCoordinate(v); return { @@ -256,10 +260,10 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { ); } - this.toolScheduler.updateDataByToolName(EToolName.PointCloudPolygon, polygonList); + this.toolScheduler.updateDataByToolName(EToolName.PointCloudPolygon, polygonList, isDeleteSelectedID); }; - public updatePointList = (sphereList: IPointCloudSphere[]) => { + public updatePointList = (sphereList: IPointCloudSphere[], isDeleteSelectedID: boolean = true) => { const pointList = sphereList?.map((v: IPointCloudSphere) => { const { point2d } = this.pointCloudInstance.getSphereTopPoint2DCoordinate(v); return { @@ -271,7 +275,7 @@ export class PointCloudAnnotation implements IPointCloudAnnotationOperation { textAttribute: '', }; }) as IPointUnit[]; - this.toolScheduler.updateDataByToolName(EToolName.Point, pointList); + this.toolScheduler.updateDataByToolName(EToolName.Point, pointList, isDeleteSelectedID); }; /** diff --git a/packages/lb-annotation/src/core/scheduler.ts b/packages/lb-annotation/src/core/scheduler.ts index c5f43ae09..479e8814f 100644 --- a/packages/lb-annotation/src/core/scheduler.ts +++ b/packages/lb-annotation/src/core/scheduler.ts @@ -350,11 +350,11 @@ export class ToolScheduler implements IToolSchedulerOperation { * there is no more specific instance like pointCloud2dOperation you can reach, * so if you need to update result in specific operation instance, you can try this. */ - public updateDataByToolName(toolName: EToolName, result: any) { + public updateDataByToolName(toolName: EToolName, result: any, isDeleteSelectedID: boolean = true) { const operationIndex = this.toolOperationNameList.indexOf(toolName); if (operationIndex >= 0) { const operationInstance = this.toolOperationList[operationIndex]; - operationInstance.setResult(result); + operationInstance.setResult(result, isDeleteSelectedID); } } diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index 68d2a5e32..2d14c3acc 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -251,7 +251,7 @@ class BasicToolOperation extends EventListener { this.clearImgDrag = this.clearImgDrag.bind(this); // 初始化监听事件 - this.dblClickListener = new DblClickEventListener(this.container, 200); + this.dblClickListener = new DblClickEventListener(this.container, 300); this.coordUtils = new CoordinateUtils(this); this.coordUtils.setBasicImgInfo(this.basicImgInfo); @@ -857,15 +857,36 @@ class BasicToolOperation extends EventListener { this.offscreenCtx?.clearRect(0, 0, this.size.width, this.size.height); } + private moveAnimationFrameId: number | null = null; + + private onWheelAnimationFrameId: number | null = null; + /** 事件绑定 */ public eventBinding() { this.dblClickListener.addEvent(() => {}, this.onLeftDblClick, this.onRightDblClick); this.container.addEventListener('mousedown', this.onMouseDown); - this.container.addEventListener('mousemove', this.onMouseMove); + this.container.addEventListener('mousemove', (event) => { + if (this.moveAnimationFrameId) return; + + this.moveAnimationFrameId = requestAnimationFrame(() => { + this.onMouseMove(event); + this.moveAnimationFrameId = null; + }); + }); + this.container.addEventListener('mouseup', this.onMouseUp); this.container.addEventListener('mouseleave', this.onMouseLeave); this.container.addEventListener('click', this.onClick); - this.container.addEventListener('wheel', this.onWheel); + + this.container.addEventListener('wheel', (event) => { + if (this.onWheelAnimationFrameId) return; + + this.onWheelAnimationFrameId = requestAnimationFrame(() => { + this.onWheel(event); + this.onWheelAnimationFrameId = null; + }); + }); + document.addEventListener('keydown', this.onKeyDown); document.addEventListener('keyup', this.onKeyUp); window.parent.document.addEventListener('contextmenu', this.onContextmenu, false); diff --git a/packages/lb-annotation/src/core/toolOperation/polygonOperation.ts b/packages/lb-annotation/src/core/toolOperation/polygonOperation.ts index 0c1254969..816235af7 100644 --- a/packages/lb-annotation/src/core/toolOperation/polygonOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/polygonOperation.ts @@ -259,8 +259,8 @@ class PolygonOperation extends BasicToolOperation { this.render(); } - public setResult(polygonList: IPolygonData[]) { - this.clearActiveStatus(); + public setResult(polygonList: IPolygonData[], isDeleteSelectedID: boolean = true) { + this.clearActiveStatus(isDeleteSelectedID); this.setPolygonList(polygonList); this.render(); } @@ -350,7 +350,6 @@ class PolygonOperation extends BasicToolOperation { } this.rectToolMode = localStorage.getItem(RECT_TOOL_MODE_NAME) as ERectToolModeType; - this.deleteSelectedID(); const coordinateZoom = this.getCoordinateUnderZoom(e); const coordinate = AxisUtils.changeDrawOutsideTarget( coordinateZoom, @@ -440,9 +439,11 @@ class PolygonOperation extends BasicToolOperation { /** * 清楚所有的中间状态 */ - public clearActiveStatus() { + public clearActiveStatus(isDeleteSelectedID: boolean = true) { this.clearPolygonDrag(); - this.deleteSelectedID(); + if (isDeleteSelectedID) { + this.deleteSelectedID(); + } } public clearDrawingStatus() { diff --git a/packages/lb-annotation/src/core/toolOperation/rectOperation.ts b/packages/lb-annotation/src/core/toolOperation/rectOperation.ts index 8316debbf..f137a3d4e 100644 --- a/packages/lb-annotation/src/core/toolOperation/rectOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/rectOperation.ts @@ -74,7 +74,7 @@ class RectOperation extends BasicToolOperation { public renderPointCloud2DHighlight(): void {} constructor(props: IRectOperationProps) { - super(props); + super({ ...props, isOffscreenCanvas: true }); this._drawOutSideTarget = props.drawOutSideTarget || false; this.rectList = []; this.isFlow = true; @@ -875,8 +875,7 @@ class RectOperation extends BasicToolOperation { return; } this.lastMouseMoveTime = now; - - if (super.onMouseMove(e) || this.forbidMouseOperation || !this.imgInfo) { + if (super.onMouseMove(e, false) || this.forbidMouseOperation || !this.imgInfo) { return; } @@ -893,6 +892,7 @@ class RectOperation extends BasicToolOperation { if (this.selectedIDs.length > 0 && this.dragInfo) { this.onDragMove(coordinate); + this.render(); return; } @@ -984,6 +984,13 @@ class RectOperation extends BasicToolOperation { }; this.render(); } + // When dragging the canvas + if (this.isDrag && !this.dragInfo) { + this.render('drag'); + return; + } + // At present, the hover effect of 2D views has little impact and will not be processed temporarily. The default is mouse movement interaction + this.render('move'); return undefined; }); @@ -1600,6 +1607,14 @@ class RectOperation extends BasicToolOperation { }); } + // Render the selected rectangle + public renderSelectedRects() { + this.selectedRects.forEach((rect) => { + this.renderDrawingRect(rect); + this.renderSelectedRect(rect); + }); + } + public renderSelectedRect(rect?: IRect) { if (!this.ctx || !rect) { return; @@ -1864,21 +1879,38 @@ class RectOperation extends BasicToolOperation { /** * 渲染矩形框体 */ - public renderRect() { - this.renderStaticRect(); + public renderRect(trigger?: string) { + if (trigger !== 'move') { + this.renderStaticRect(); + } this.renderCreatingRect(); } - public render() { + public render(trigger?: string) { if (!this.ctx) { return; } - super.render(); - this.renderRect(); + if (trigger !== 'move') { + super.render(); + } + if (trigger !== 'drag') { + this.renderRect(trigger); + } this.renderCursorLine(this.getLineColor(this.defaultAttribute)); } + // Rendering Crosslines + public renderCursorLine(lineColor: string) { + // Clear Extra Canvas + this.clearOffscreenCanvas(); + const { x, y } = this.coord; + + DrawUtils.drawLine(this.offscreenCanvas, { x: 0, y }, { x: 10000, y }, { color: lineColor }); + DrawUtils.drawLine(this.offscreenCanvas, { x, y: 0 }, { x, y: 10000 }, { color: lineColor }); + DrawUtils.drawCircleWithFill(this.offscreenCanvas, { x, y }, 1, { color: 'white' }); + } + public setDefaultAttribute(defaultAttribute?: string) { const oldDefault = this.defaultAttribute; this.defaultAttribute = defaultAttribute ?? ''; diff --git a/packages/lb-components/src/components/pointCloud2DRectOperationView/index.tsx b/packages/lb-components/src/components/pointCloud2DRectOperationView/index.tsx index 285e53ccd..c09c6daf0 100644 --- a/packages/lb-components/src/components/pointCloud2DRectOperationView/index.tsx +++ b/packages/lb-components/src/components/pointCloud2DRectOperationView/index.tsx @@ -270,8 +270,17 @@ const PointCloud2DRectOperationView = (props: IPointCloud2DRectOperationViewProp const onRightClick = ({ targetId, id }: { targetId: string; id: string }) => { setNeedUpdateCenter(false); - setSelectedIDs(targetId); - setRightClickRectId(id); + requestAnimationFrame(() => { + updateSelectedIDs(targetId); + setRightClickRectId(id); + }); + }; + + const updateSelectedIDs = (newID: string) => { + const currentIDs = Array.isArray(selectedIDs) ? selectedIDs : []; + if (!currentIDs.includes(newID)) { + setSelectedIDs(newID); + } }; useEffect(() => { @@ -416,7 +425,7 @@ const PointCloud2DRectOperationView = (props: IPointCloud2DRectOperationViewProp // Center the view by selectedID if (!selectedID || !needUpdateCenter) { setNeedUpdateCenter(true); - // during disconnection + // during disconnection if (shouldExcludePointCloudBoxListUpdate) { operation.current.setHoverRectID(''); operation.current.render(); @@ -431,7 +440,7 @@ const PointCloud2DRectOperationView = (props: IPointCloud2DRectOperationViewProp setNeedUpdateCenter(true); return; } - // during disconnection + // during disconnection if (shouldExcludePointCloudBoxListUpdate) { operation.current.setHoverRectID(rect.id); } diff --git a/packages/lb-components/src/components/pointCloudView/hooks/usePointCloudViews.ts b/packages/lb-components/src/components/pointCloudView/hooks/usePointCloudViews.ts index 4b501b7ae..c0a495033 100644 --- a/packages/lb-components/src/components/pointCloudView/hooks/usePointCloudViews.ts +++ b/packages/lb-components/src/components/pointCloudView/hooks/usePointCloudViews.ts @@ -834,7 +834,7 @@ export const usePointCloudViews = (params?: IUsePointCloudViewsParams) => { config?.secondaryAttributeConfigurable ? config?.inputList ?? [] : [], ); - topViewInstance?.updatePolygonList(newPointCloudList ?? [], polygonList); + topViewInstance?.updatePolygonList(newPointCloudList ?? [], polygonList, false); /** If new box is hidden will not active target point box */ if (isBoxHidden) { setSelectedIDs([]); From 678ee12ae0d2132b500cf5c7d0bdfd655a311625 Mon Sep 17 00:00:00 2001 From: Original-Recipe-Chicken Date: Mon, 23 Dec 2024 17:14:30 +0800 Subject: [PATCH 2/2] feat: Code review --- .../core/toolOperation/basicToolOperation.ts | 50 +++++++++++-------- .../src/core/toolOperation/rectOperation.ts | 8 --- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index 2d14c3acc..4c2ca7e10 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -239,11 +239,13 @@ class BasicToolOperation extends EventListener { // 阻止右键菜单栏 this.onMouseDown = this.onMouseDown.bind(this); this.onMouseMove = this.onMouseMove.bind(this); + this.optimizeMouseMove = this.optimizeMouseMove.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); this.onMouseUp = this.onMouseUp.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.onWheel = this.onWheel.bind(this); + this.optimizeWheel = this.optimizeWheel.bind(this); this.onLeftDblClick = this.onLeftDblClick.bind(this); this.onRightDblClick = this.onRightDblClick.bind(this); this.onClick = this.onClick.bind(this); @@ -857,35 +859,17 @@ class BasicToolOperation extends EventListener { this.offscreenCtx?.clearRect(0, 0, this.size.width, this.size.height); } - private moveAnimationFrameId: number | null = null; - - private onWheelAnimationFrameId: number | null = null; - /** 事件绑定 */ public eventBinding() { this.dblClickListener.addEvent(() => {}, this.onLeftDblClick, this.onRightDblClick); this.container.addEventListener('mousedown', this.onMouseDown); - this.container.addEventListener('mousemove', (event) => { - if (this.moveAnimationFrameId) return; - - this.moveAnimationFrameId = requestAnimationFrame(() => { - this.onMouseMove(event); - this.moveAnimationFrameId = null; - }); - }); + this.container.addEventListener('mousemove', this.optimizeMouseMove); this.container.addEventListener('mouseup', this.onMouseUp); this.container.addEventListener('mouseleave', this.onMouseLeave); this.container.addEventListener('click', this.onClick); - this.container.addEventListener('wheel', (event) => { - if (this.onWheelAnimationFrameId) return; - - this.onWheelAnimationFrameId = requestAnimationFrame(() => { - this.onWheel(event); - this.onWheelAnimationFrameId = null; - }); - }); + this.container.addEventListener('wheel', this.optimizeWheel); document.addEventListener('keydown', this.onKeyDown); document.addEventListener('keyup', this.onKeyUp); @@ -894,10 +878,10 @@ class BasicToolOperation extends EventListener { public eventUnbinding() { this.container.removeEventListener('mousedown', this.onMouseDown); - this.container.removeEventListener('mousemove', this.onMouseMove); + this.container.removeEventListener('mousemove', this.optimizeMouseMove); this.container.removeEventListener('mouseup', this.onMouseUp); this.container.removeEventListener('mouseleave', this.onMouseLeave); - this.container.removeEventListener('wheel', this.onWheel); + this.container.removeEventListener('wheel', this.optimizeWheel); this.container.removeEventListener('click', this.onClick); document.removeEventListener('keydown', this.onKeyDown); document.removeEventListener('keyup', this.onKeyUp); @@ -922,6 +906,28 @@ class BasicToolOperation extends EventListener { }; } + private moveAnimationFrameId: number | null = null; + + private onWheelAnimationFrameId: number | null = null; + + public optimizeMouseMove(event: MouseEvent): boolean | void { + if (this.moveAnimationFrameId) return; + + this.moveAnimationFrameId = requestAnimationFrame(() => { + this.onMouseMove(event); + this.moveAnimationFrameId = null; + }); + } + + public optimizeWheel(event: MouseEvent): boolean | void { + if (this.onWheelAnimationFrameId) return; + + this.onWheelAnimationFrameId = requestAnimationFrame(() => { + this.onWheel(event); + this.onWheelAnimationFrameId = null; + }); + } + public onMouseDown(e: MouseEvent): void | boolean { // e.stopPropagation(); if (!this.canvas || this.isImgError) { diff --git a/packages/lb-annotation/src/core/toolOperation/rectOperation.ts b/packages/lb-annotation/src/core/toolOperation/rectOperation.ts index f137a3d4e..a555a2237 100644 --- a/packages/lb-annotation/src/core/toolOperation/rectOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/rectOperation.ts @@ -1607,14 +1607,6 @@ class RectOperation extends BasicToolOperation { }); } - // Render the selected rectangle - public renderSelectedRects() { - this.selectedRects.forEach((rect) => { - this.renderDrawingRect(rect); - this.renderSelectedRect(rect); - }); - } - public renderSelectedRect(rect?: IRect) { if (!this.ctx || !rect) { return;