From 345fddca2b9b7227b6257eb2280585b72b9cdb31 Mon Sep 17 00:00:00 2001 From: YuHong Date: Wed, 20 Mar 2024 20:24:15 +0800 Subject: [PATCH] fix: rectangle subtract func (#1647) * fix: rectangle subtract func * fix(render): migrate the subtract * fix((render): type error * feat: cr revise --------- Co-authored-by: DR-Univer --- .../src/shared/__test__/rectangle.spec.ts | 80 +++++++++++++++++++ packages/core/src/shared/rectangle.ts | 28 ++++--- .../src/basics/viewport-subtract.ts | 77 ++++++++++++++++++ packages/engine-render/src/viewport.ts | 5 +- 4 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 packages/core/src/shared/__test__/rectangle.spec.ts create mode 100644 packages/engine-render/src/basics/viewport-subtract.ts diff --git a/packages/core/src/shared/__test__/rectangle.spec.ts b/packages/core/src/shared/__test__/rectangle.spec.ts new file mode 100644 index 0000000000..fd7fb70085 --- /dev/null +++ b/packages/core/src/shared/__test__/rectangle.spec.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, expect, it } from 'vitest'; + +import { Rectangle } from '../rectangle'; + +describe('test "Rectangle"', () => { + it('test "subtract"', () => { + // completely covered + const rect1 = { + startRow: 1, + startColumn: 1, + endRow: 1, + endColumn: 1, + }; + + const rect2 = { + startRow: 1, + startColumn: 1, + endRow: 1, + endColumn: 1, + }; + expect(Rectangle.subtract(rect1, rect2)).toEqual([]); + + // partly covered + const rect3 = { + startRow: 1, + startColumn: 1, + endRow: 3, + endColumn: 3, + }; + + const rect4 = { + startRow: 1, + startColumn: 1, + endRow: 1, + endColumn: 1, + }; + expect(Rectangle.subtract(rect3, rect4)).toStrictEqual([ + { startRow: 2, startColumn: 1, endRow: 3, endColumn: 3 }, + { startRow: 1, startColumn: 2, endRow: 1, endColumn: 3 }, + ]); + + // covered at center point + const rect5 = { + startRow: 1, + startColumn: 1, + endRow: 3, + endColumn: 3, + }; + + const rect6 = { + startRow: 2, + startColumn: 2, + endRow: 2, + endColumn: 2, + }; + + expect(Rectangle.subtract(rect5, rect6)).toStrictEqual([ + { startRow: 1, startColumn: 1, endRow: 1, endColumn: 3 }, + { startRow: 3, startColumn: 1, endRow: 3, endColumn: 3 }, + { startRow: 2, startColumn: 1, endRow: 2, endColumn: 1 }, + { startRow: 2, startColumn: 3, endRow: 2, endColumn: 3 }, + ]); + }); +}); diff --git a/packages/core/src/shared/rectangle.ts b/packages/core/src/shared/rectangle.ts index b99665966f..c1a8bd1220 100644 --- a/packages/core/src/shared/rectangle.ts +++ b/packages/core/src/shared/rectangle.ts @@ -224,10 +224,10 @@ export class Rectangle { static subtract(range1: IRange, range2: IRange): IRange[] { // 如果没有交集,则返回 range1 if ( - range2.startRow >= range1.endRow || - range2.endRow <= range1.startRow || - range2.startColumn >= range1.endColumn || - range2.endColumn <= range1.startColumn + range2.startRow > range1.endRow || + range2.endRow < range1.startRow || + range2.startColumn > range1.endColumn || + range2.endColumn < range1.startColumn ) { return [range1]; } @@ -235,19 +235,19 @@ export class Rectangle { const ranges: IRange[] = []; // 上部分 - if (range2.startRow > range1.startRow) { + if (range2.startRow >= range1.startRow) { ranges.push({ startRow: range1.startRow, startColumn: range1.startColumn, - endRow: range2.startRow, + endRow: range2.startRow - 1, endColumn: range1.endColumn, }); } // 下部分 - if (range2.endRow < range1.endRow) { + if (range2.endRow <= range1.endRow) { ranges.push({ - startRow: range2.endRow, + startRow: range2.endRow + 1, startColumn: range1.startColumn, endRow: range1.endRow, endColumn: range1.endColumn, @@ -258,25 +258,27 @@ export class Rectangle { const topBoundary = Math.max(range1.startRow, range2.startRow); const bottomBoundary = Math.min(range1.endRow, range2.endRow); - if (range2.startColumn > range1.startColumn) { + if (range2.startColumn >= range1.startColumn) { ranges.push({ startRow: topBoundary, startColumn: range1.startColumn, endRow: bottomBoundary, - endColumn: range2.startColumn, + endColumn: range2.startColumn - 1, }); } // 右部分 - if (range2.endColumn < range1.endColumn) { + if (range2.endColumn <= range1.endColumn) { ranges.push({ startRow: topBoundary, - startColumn: range2.endColumn, + startColumn: range2.endColumn + 1, endRow: bottomBoundary, endColumn: range1.endColumn, }); } - return ranges; + const result = ranges.filter((range) => range.startRow <= range.endRow && range.startColumn <= range.endColumn); + + return result; } } diff --git a/packages/engine-render/src/basics/viewport-subtract.ts b/packages/engine-render/src/basics/viewport-subtract.ts new file mode 100644 index 0000000000..9e3b29aaf1 --- /dev/null +++ b/packages/engine-render/src/basics/viewport-subtract.ts @@ -0,0 +1,77 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { IRange } from '@univerjs/core'; + +export function subtractViewportRange(range1: IRange, range2: IRange): IRange[] { + // If there is no intersection, return range1. + + if ( + range2.startRow >= range1.endRow || + range2.endRow <= range1.startRow || + range2.startColumn >= range1.endColumn || + range2.endColumn <= range1.startColumn + ) { + return [range1]; + } + + const ranges: IRange[] = []; + + // top + if (range2.startRow > range1.startRow) { + ranges.push({ + startRow: range1.startRow, + startColumn: range1.startColumn, + endRow: range2.startRow, + endColumn: range1.endColumn, + }); + } + + // bottom + if (range2.endRow < range1.endRow) { + ranges.push({ + startRow: range2.endRow, + startColumn: range1.startColumn, + endRow: range1.endRow, + endColumn: range1.endColumn, + }); + } + + // left + const topBoundary = Math.max(range1.startRow, range2.startRow); + const bottomBoundary = Math.min(range1.endRow, range2.endRow); + + if (range2.startColumn > range1.startColumn) { + ranges.push({ + startRow: topBoundary, + startColumn: range1.startColumn, + endRow: bottomBoundary, + endColumn: range2.startColumn, + }); + } + + // right + if (range2.endColumn < range1.endColumn) { + ranges.push({ + startRow: topBoundary, + startColumn: range2.endColumn, + endRow: bottomBoundary, + endColumn: range1.endColumn, + }); + } + + return ranges; +} diff --git a/packages/engine-render/src/viewport.ts b/packages/engine-render/src/viewport.ts index fb3906f6ee..05637c1882 100644 --- a/packages/engine-render/src/viewport.ts +++ b/packages/engine-render/src/viewport.ts @@ -15,7 +15,7 @@ */ import type { EventState, IPosition, IRange, Nullable } from '@univerjs/core'; -import { Observable, Rectangle, Tools } from '@univerjs/core'; +import { Observable, Tools } from '@univerjs/core'; import type { BaseObject } from './base-object'; import { RENDER_CLASS_TYPE } from './basics/const'; @@ -28,6 +28,7 @@ import { Vector2 } from './basics/vector2'; import type { UniverRenderingContext } from './context'; import type { BaseScrollBar } from './shape/base-scroll-bar'; import type { ThinScene } from './thin-scene'; +import { subtractViewportRange } from './basics/viewport-subtract'; interface IViewPosition { top?: number; @@ -1089,7 +1090,7 @@ export class Viewport { endColumn: subBound.right, }; - const ranges = Rectangle.subtract(range1, range2); + const ranges = subtractViewportRange(range1, range2); return ranges.map((range) => { const { startRow, endRow, startColumn, endColumn } = range;