Skip to content
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
1 change: 1 addition & 0 deletions web_ui/packages/smart-tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { buildIntelligentScissorsInstance, IntelligentScissors } from './src/int

export { Anchor } from './src/edit-bounding-box/anchor.component';
export { ANCHOR_SIZE, ResizeAnchor } from './src/edit-bounding-box/resize-anchor.component';
export { EditBoundingBox } from './src/edit-bounding-box/edit-bounding-box/edit-bounding-box.component';
7 changes: 7 additions & 0 deletions web_ui/packages/smart-tools/src/css-modules.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (C) 2022-2025 Intel Corporation
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE

declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,7 @@ import { CSSProperties, PointerEvent, ReactNode, useState } from 'react';
import { isFunction } from 'lodash-es';

import { Point } from '../shared/interfaces';

interface MouseButton {
button: number;
buttons: number;
}

const BUTTON_LEFT = {
button: 0,
buttons: 1,
};
const isLeftButton = (button: MouseButton): boolean => {
return button.button === BUTTON_LEFT.button || button.buttons === BUTTON_LEFT.buttons;
};
import { isLeftButton } from '../utils/mouse-utils';

interface AnchorProps {
children: ReactNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@

import { useEffect, useState } from 'react';

import { ANCHOR_SIZE, ResizeAnchor } from '@geti/smart-tools';
import { RegionOfInterest } from '@geti/smart-tools/types';

import { Annotation } from '../../../../../core/annotations/annotation.interface';
import { Point } from '../../../../../core/annotations/shapes.interface';
import { ShapeType } from '../../../../../core/annotations/shapetype.enum';
import { Labels } from '../../../annotation/labels/labels.component';
import { Annotation, Point, RegionOfInterest } from '../../shared/interfaces';
import { getBoundingBoxInRoi, getBoundingBoxResizePoints, getClampedBoundingBox } from '../../utils/tool-utils';
import { ANCHOR_SIZE, ResizeAnchor } from '../resize-anchor.component';
import { TranslateShape } from '../translate-shape.component';
import { getBoundingBoxInRoi, getBoundingBoxResizePoints, getClampedBoundingBox } from '../utils';

import classes from './../../../annotator-canvas.module.scss';
import classes from './edit-bounding-box.module.scss';

interface EditBoundingBoxProps {
annotation: Annotation & { shape: { shapeType: ShapeType.Rect } };
annotation: Annotation & { shape: { shapeType: 'rect' } };
disableTranslation?: boolean;
disablePoints?: boolean;
roi: RegionOfInterest;
Expand All @@ -36,6 +31,8 @@ export const EditBoundingBox = ({
}: EditBoundingBoxProps): JSX.Element => {
const [shape, setShape] = useState(annotation.shape);

const ariaLabel = `${annotation.isSelected ? 'Selected' : 'Not selected'} shape ${annotation.id}`;

useEffect(() => setShape(annotation.shape), [annotation.shape]);

const onComplete = () => {
Expand Down Expand Up @@ -70,11 +67,29 @@ export const EditBoundingBox = ({
annotation={{ ...annotation, shape }}
translateShape={translate}
onComplete={onComplete}
/>
>
<g
id={`canvas-annotation-${annotation.id}`}
fill={annotation.color || '#0095ca'}
fillOpacity='var(--annotation-fill-opacity, 0.2)'
stroke={annotation.color || '#0095ca'}
strokeWidth={2 / zoom}
strokeOpacity='var(--annotation-border-opacity, 0.8)'
strokeLinecap='round'
strokeDasharray='0'
strokeDashoffset='0'
>
<rect
x={shape.x}
y={shape.y}
width={shape.width}
height={shape.height}
aria-label={ariaLabel}
/>
</g>
</TranslateShape>
</svg>

<Labels annotation={{ ...annotation, shape }} />

{disablePoints === false ? (
<svg
width={image.width}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.disabledLayer {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
pointer-events: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@

import { PointerEvent, ReactNode, useState } from 'react';

import { Annotation as AnnotationInterface } from '../../../../core/annotations/annotation.interface';
import { Point } from '../../../../core/annotations/shapes.interface';
import { isLeftButton } from '../../../buttons-utils';
import { Annotation } from '../../annotation/annotation.component';
import { PointerType } from '../tools.interface';
import { allowPanning } from '../utils';
import { Annotation as AnnotationInterface, Point } from '../shared/interfaces';
import { allowPanning, isLeftButton } from '../utils/mouse-utils';

const STROKE_WIDTH = 2;

Expand All @@ -27,7 +23,7 @@ export const TranslateShape = ({
annotation,
onComplete,
translateShape,
children = <Annotation annotation={annotation} />,
children,
}: TranslateShapeProps): JSX.Element => {
const [dragFromPoint, setDragFromPoint] = useState<null | Point>(null);

Expand All @@ -36,7 +32,7 @@ export const TranslateShape = ({
return;
}

if (event.pointerType === PointerType.Touch || !isLeftButton(event)) {
if (event.pointerType === 'touch' || !isLeftButton(event)) {
return;
}

Expand Down Expand Up @@ -67,7 +63,9 @@ export const TranslateShape = ({
if (dragFromPoint === null) {
return;
}

event.preventDefault();

setDragFromPoint(null);
event.currentTarget.releasePointerCapture(event.pointerId);
onComplete();
Expand Down
10 changes: 10 additions & 0 deletions web_ui/packages/smart-tools/src/shared/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ export interface Vector {
x: number;
y: number;
}

export interface Annotation {
readonly id: string;
readonly shape: Shape;
readonly zIndex: number;
readonly isSelected: boolean;
readonly isHidden: boolean;
readonly isLocked: boolean;
readonly color?: string;
}
4 changes: 4 additions & 0 deletions web_ui/packages/smart-tools/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export {
isPolygonValid,
getPointsFromMat,
getMatFromPoints,
getBoundingBoxInRoi,
getBoundingBoxResizePoints,
getClampedBoundingBox,
} from './tool-utils';
export { isLeftButton, isWheelButton, allowPanning } from './mouse-utils';
export * as Vec2 from './vec2';
export {
degreesToRadians,
Expand Down
46 changes: 46 additions & 0 deletions web_ui/packages/smart-tools/src/utils/mouse-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (C) 2022-2025 Intel Corporation
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE

import { PointerEvent, SVGProps } from 'react';

interface MouseButton {
button: number;
buttons: number;
}

const BUTTON_LEFT = {
button: 0,
buttons: 1,
};
const BUTTON_WHEEL = {
button: 1,
buttons: 4,
};

const isButton = (button: MouseButton, buttonToCompare: MouseButton): boolean =>
button.button === buttonToCompare.button || button.buttons === buttonToCompare.buttons;

export const isLeftButton = (button: MouseButton): boolean => {
return button.button === BUTTON_LEFT.button || button.buttons === BUTTON_LEFT.buttons;
};

export const isWheelButton = (button: MouseButton): boolean => {
return isButton(button, BUTTON_WHEEL);
};

type OnPointerDown = SVGProps<SVGElement>['onPointerDown'];
export const allowPanning = (onPointerDown?: OnPointerDown): OnPointerDown | undefined => {
if (onPointerDown === undefined) {
return;
}

return (event: PointerEvent<SVGElement>) => {
const isPressingPanningHotKeys = (isLeftButton(event) && event.ctrlKey) || isWheelButton(event);

if (isPressingPanningHotKeys) {
return;
}

return onPointerDown(event);
};
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (C) 2022-2025 Intel Corporation
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE

import { getBoundingBoxInRoi, getBoundingBoxResizePoints, getClampedBoundingBox } from './utils';
import { getBoundingBoxInRoi, getBoundingBoxResizePoints, getClampedBoundingBox } from './tool-utils';

const mockedRoi = {
x: 0,
Expand Down
Loading
Loading