Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add rectangle drawing using two points #605

Open
wants to merge 1 commit into
base: alpha
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/lb-annotation/src/constant/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,12 @@ export enum EOperationMode {
General = 1, // Common
MultiMove = 2, // Experimental
}

/** rectToll name for LocalStorage */
export const RECT_TOOL_MODE_NAME = 'rect_tool_mode';

/** rectToll mode*/
export enum ERectToolModeType {
ThreePoints = 'three_points',
TwoPoints = 'two_points',
}
115 changes: 85 additions & 30 deletions packages/lb-annotation/src/core/toolOperation/polygonOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ import {
TEXT_ATTRIBUTE_OFFSET,
} from '../../constant/annotation';
import EKeyCode from '../../constant/keyCode';
import { edgeAdsorptionScope, ELineTypes, EPolygonPattern, EToolName } from '../../constant/tool';
import {
edgeAdsorptionScope,
ELineTypes,
EPolygonPattern,
EToolName,
ERectToolModeType,
RECT_TOOL_MODE_NAME,
} from '../../constant/tool';
import locale from '../../locales';
import { EMessage } from '../../locales/constants';
import { IPolygonConfig, IPolygonData, IPolygonPoint } from '../../types/tool/polygon';
Expand Down Expand Up @@ -76,6 +83,8 @@ class PolygonOperation extends BasicToolOperation {

public selection: Selection;

private rectToolMode?: ERectToolModeType;

constructor(props: IPolygonOperationProps) {
super(props);
this.config = CommonToolUtils.jsonParser(props.config);
Expand Down Expand Up @@ -159,6 +168,18 @@ class PolygonOperation extends BasicToolOperation {
return this.selectedPolygon?.textAttribute;
}

public get isThreePointsMode() {
return this.pattern === EPolygonPattern.Rect && this.drawingPointList.length === 2;
}

public get isTwoPointsMode() {
return (
this.pattern === EPolygonPattern.Rect &&
this.rectToolMode === ERectToolModeType.TwoPoints &&
this.drawingPointList.length === 1
);
}

// 是否直接执行操作
public isAllowDouble = (e: MouseEvent) => {
const { selectedID } = this;
Expand Down Expand Up @@ -325,6 +346,7 @@ class PolygonOperation extends BasicToolOperation {
return;
}

this.rectToolMode = localStorage.getItem(RECT_TOOL_MODE_NAME) as ERectToolModeType;
this.deleteSelectedID();
const coordinateZoom = this.getCoordinateUnderZoom(e);
const coordinate = AxisUtils.changeDrawOutsideTarget(
Expand Down Expand Up @@ -358,9 +380,13 @@ class PolygonOperation extends BasicToolOperation {
1 / this.zoom,
);

if (this.pattern === EPolygonPattern.Rect && this.drawingPointList.length === 2) {
const rect = MathUtils.getRectangleByRightAngle(coordinateWithOrigin, this.drawingPointList);
this.drawingPointList = rect;
if (this.isThreePointsMode || this.isTwoPointsMode) {
if (this.isThreePointsMode) {
const rect = MathUtils.getRectangleByRightAngle(coordinateWithOrigin, this.drawingPointList);
this.drawingPointList = rect;
} else if (this.isTwoPointsMode) {
this.drawingPointList = this.createRectByTwoPointsMode(this.drawingPointList, coordinateWithOrigin);
}

// 边缘判断 - 仅限支持图片下范围下
if (this.config.drawOutsideTarget === false && this.imgInfo) {
Expand Down Expand Up @@ -1734,37 +1760,42 @@ class PolygonOperation extends BasicToolOperation {
let drawingPointList = [...this.drawingPointList];
let coordinate = AxisUtils.getOriginCoordinateWithOffsetCoordinate(this.coord, this.zoom, this.currentPos);

if (this.pattern === EPolygonPattern.Rect && drawingPointList.length === 2) {
if (this.isThreePointsMode) {
// 矩形模式特殊绘制
drawingPointList = MathUtils.getRectangleByRightAngle(coordinate, drawingPointList);
} else {
if (this.config?.edgeAdsorption && this.isAlt === false) {
const { dropFoot } = PolygonUtils.getClosestPoint(
coordinate,
this.polygonList,
this.config?.lineType,
edgeAdsorptionScope / this.zoom,
);
if (dropFoot) {
coordinate = dropFoot;
}
} else if (this.config?.edgeAdsorption && this.isAlt === false) {
const { dropFoot } = PolygonUtils.getClosestPoint(
coordinate,
this.polygonList,
this.config?.lineType,
edgeAdsorptionScope / this.zoom,
);
if (dropFoot) {
coordinate = dropFoot;
}
drawingPointList.push(coordinate);
} else if (this.isTwoPointsMode) {
drawingPointList = this.createRectByTwoPointsMode(drawingPointList, coordinate);
} else {
drawingPointList.push(coordinate);
}
const polygon = AxisUtils.changePointListByZoom(drawingPointList, this.zoom, this.currentPos);
DrawUtils.drawSelectedPolygonWithFillAndLine(this.canvas, polygon, {
fillColor: toolData.fill,
strokeColor: toolData.stroke,
pointColor: 'white',
thickness: 2,
lineCap: 'round',
isClose: false,
lineType: this.config.lineType,
});
if (this.isTwoPointsMode) {
DrawUtils.drawLine(this.canvas, polygon[0], polygon[1], {
color: 'white',
thickness: 3,
lineDash: [6],
});
}

DrawUtils.drawSelectedPolygonWithFillAndLine(
this.canvas,
AxisUtils.changePointListByZoom(drawingPointList, this.zoom, this.currentPos),
{
fillColor: toolData.fill,
strokeColor: toolData.stroke,
pointColor: 'white',
thickness: 2,
lineCap: 'round',
isClose: false,
lineType: this.config.lineType,
},
);
}

// 5. 编辑中高亮的点
Expand Down Expand Up @@ -1805,6 +1836,30 @@ class PolygonOperation extends BasicToolOperation {
}
}

/**
* used in isTwoPointsMode;
* Effect:Determine the order of the four points, because the orientation is drawn along the line between the starting point and the first point;
* Orientation Logic: to the first point, adjacent to the two sides take one for orientation, such as side 1, Side 2, if side 1 clockwise rotation 90 degrees can get side 2, then take side 1 for orientation;
*/
private createRectByTwoPointsMode(drawingPointList: IPolygonPoint[], coordinate: IPolygonPoint) {
const startPoint = drawingPointList[0];
const result = [...drawingPointList];
const fromLeftTopToRightBottom = coordinate.x > startPoint.x && coordinate.y > startPoint.y;
const fromRightBottomToLeftTop = coordinate.x < startPoint.x && coordinate.y < startPoint.y;
if (fromLeftTopToRightBottom || fromRightBottomToLeftTop) {
result.push({ x: startPoint.x, y: coordinate.y }, coordinate, {
x: coordinate.x,
y: startPoint.y,
});
} else {
result.push({ x: coordinate.x, y: startPoint.y }, coordinate, {
x: startPoint.x,
y: coordinate.y,
});
}
return result;
}

public render() {
if (!this.ctx) {
return;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/lb-components/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,16 @@ $prefix: bee;
margin-left: 16px;
}

&__multiBox {
display: flex;
align-items: center;
margin: 0 5px;
}

&__dropdown .ant-dropdown-menu-item:not(.ant-dropdown-menu-item-selected) {
color: #999999;
}

&__singleTool {
max-width: 20px;
max-height: 20px;
Expand Down
65 changes: 56 additions & 9 deletions packages/lb-components/src/views/MainView/sidebar/ToolIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,33 @@ import pointSvg from '@/assets/annotation/pointTool/icon_point.svg';
import pointASvg from '@/assets/annotation/pointTool/icon_point_a.svg';
import PolygonASvg from '@/assets/annotation/polygonTool/icon_polygon_a.svg';
import PolygonSvg from '@/assets/annotation/polygonTool/icon_polygon.svg';
import rectSvg from '@/assets/annotation/rectTool/icon_rect.svg';
import rectASvg from '@/assets/annotation/rectTool/icon_rect_a.svg';
import { cTool } from '@labelbee/lb-annotation';
import classnames from 'classnames';
import { useTranslation } from 'react-i18next';
import type { MenuProps } from 'antd';
import { Dropdown } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import { ReactComponent as TwoPointsRectSvg } from '@/assets/annotation/rectTool/two_points_rect.svg';
import { ReactComponent as ThreePointsRectSvg } from '@/assets/annotation/rectTool/three_points_rect.svg';
import { useLocalStorageState } from 'ahooks';

const { EPointCloudName, TOOL_NAME, TOOL_NAME_EN } = cTool;
const { EPointCloudName, TOOL_NAME, TOOL_NAME_EN, ERectToolModeType, RECT_TOOL_MODE_NAME } = cTool;

const toolList = [
{
toolName: EToolName.Rect,
commonSvg: rectSvg,
selectedSvg: rectASvg,
dropdownItems: [
{
key: ERectToolModeType.ThreePoints,
icon: <ThreePointsRectSvg />,
label: '三点画矩形',
},
{
key: ERectToolModeType.TwoPoints,
icon: <TwoPointsRectSvg />,
label: '两点画矩形',
},
],
},
{
toolName: EToolName.Polygon,
Expand Down Expand Up @@ -73,6 +87,19 @@ export const ToolIcons = ({

const hasMultiTools = renderTools.length > 1;

const [rectToolMode, setRectToolMode] = useLocalStorageState(RECT_TOOL_MODE_NAME, {
defaultValue: ERectToolModeType.ThreePoints as string,
serializer: (v) => v ?? '',
deserializer: (v) => v,
});

const SelectedRectSvg =
rectToolMode === ERectToolModeType.TwoPoints ? TwoPointsRectSvg : ThreePointsRectSvg;

const toggleTwoOrThreePointRect: MenuProps['onClick'] = (e) => {
setRectToolMode(e.key);
};

return (
<div className={`${sidebarCls}__level`}>
{renderTools.map((tool) => {
Expand All @@ -83,10 +110,30 @@ export const ToolIcons = ({
key={tool.toolName}
onClick={() => onChange?.(tool.toolName)}
>
<img
className={`${sidebarCls}__singleTool`}
src={isSelected ? tool?.selectedSvg : tool?.commonSvg}
/>
{tool.dropdownItems ? (
<Dropdown
overlayClassName={`${sidebarCls}__dropdown`}
menu={{
items: tool.dropdownItems,
selectable: true,
defaultSelectedKeys: rectToolMode ? [rectToolMode] : [],
onClick: toggleTwoOrThreePointRect,
}}
>
<div
className={`${sidebarCls}__multiBox`}
style={{ color: isSelected ? '#666fff' : '#999999' }}
>
<SelectedRectSvg />
<DownOutlined />
</div>
</Dropdown>
) : (
<img
className={`${sidebarCls}__singleTool`}
src={isSelected ? tool?.selectedSvg : tool?.commonSvg}
/>
)}
<span
className={classnames({
[`${sidebarCls}__toolOption__selected`]: isSelected,
Expand Down
Loading