mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-11 05:35:25 +03:00
konva tweaks
This commit is contained in:
parent
2ce0c587e8
commit
14854916be
@ -5,7 +5,6 @@ import Konva from "konva";
|
|||||||
import type { KonvaEventObject } from "konva/lib/Node";
|
import type { KonvaEventObject } from "konva/lib/Node";
|
||||||
import { Polygon, PolygonType } from "@/types/canvas";
|
import { Polygon, PolygonType } from "@/types/canvas";
|
||||||
import { useApiHost } from "@/api";
|
import { useApiHost } from "@/api";
|
||||||
import { flattenPoints } from "@/utils/canvasUtil";
|
|
||||||
|
|
||||||
type PolygonCanvasProps = {
|
type PolygonCanvasProps = {
|
||||||
camera: string;
|
camera: string;
|
||||||
@ -60,8 +59,69 @@ export function PolygonCanvas({
|
|||||||
return [stage.getPointerPosition()!.x, stage.getPointerPosition()!.y];
|
return [stage.getPointerPosition()!.x, stage.getPointerPosition()!.y];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addPointToPolygon = (polygon: Polygon, newPoint: number[]) => {
|
||||||
|
const points = polygon.points;
|
||||||
|
const [newPointX, newPointY] = newPoint;
|
||||||
|
const updatedPoints = [...points];
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const [x1, y1] = points[i];
|
||||||
|
const [x2, y2] = i === points.length - 1 ? points[0] : points[i + 1];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(x1 <= newPointX && newPointX <= x2) ||
|
||||||
|
(x2 <= newPointX && newPointX <= x1)
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
(y1 <= newPointY && newPointY <= y2) ||
|
||||||
|
(y2 <= newPointY && newPointY <= y1)
|
||||||
|
) {
|
||||||
|
const insertIndex = i + 1;
|
||||||
|
updatedPoints.splice(insertIndex, 0, [newPointX, newPointY]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedPoints;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPointNearLineSegment = (
|
||||||
|
polygon: Polygon,
|
||||||
|
mousePos: number[],
|
||||||
|
radius = 10,
|
||||||
|
) => {
|
||||||
|
const points = polygon.points;
|
||||||
|
const [x, y] = mousePos;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const [x1, y1] = points[i];
|
||||||
|
const [x2, y2] = i === points.length - 1 ? points[0] : points[i + 1];
|
||||||
|
|
||||||
|
const crossProduct = (x - x1) * (x2 - x1) + (y - y1) * (y2 - y1);
|
||||||
|
if (crossProduct > 0) {
|
||||||
|
const lengthSquared = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
|
||||||
|
const dot = (x - x1) * (x2 - x1) + (y - y1) * (y2 - y1);
|
||||||
|
if (dot < 0 || dot > lengthSquared) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const lineSegmentDistance = Math.abs(
|
||||||
|
((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) /
|
||||||
|
Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)),
|
||||||
|
);
|
||||||
|
if (lineSegmentDistance <= radius) {
|
||||||
|
const midPointX = (x1 + x2) / 2;
|
||||||
|
const midPointY = (y1 + y2) / 2;
|
||||||
|
return [midPointX, midPointY];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const isMouseOverFirstPoint = (polygon: Polygon, mousePos: number[]) => {
|
const isMouseOverFirstPoint = (polygon: Polygon, mousePos: number[]) => {
|
||||||
if (!polygon || !polygon.points) {
|
if (!polygon || !polygon.points || polygon.points.length < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const [firstPoint] = polygon.points;
|
const [firstPoint] = polygon.points;
|
||||||
@ -69,7 +129,7 @@ export function PolygonCanvas({
|
|||||||
mousePos[0] - firstPoint[0],
|
mousePos[0] - firstPoint[0],
|
||||||
mousePos[1] - firstPoint[1],
|
mousePos[1] - firstPoint[1],
|
||||||
);
|
);
|
||||||
return distance < 15;
|
return distance < 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isMouseOverAnyPoint = (polygon: Polygon, mousePos: number[]) => {
|
const isMouseOverAnyPoint = (polygon: Polygon, mousePos: number[]) => {
|
||||||
@ -83,7 +143,7 @@ export function PolygonCanvas({
|
|||||||
mousePos[0] - point[0],
|
mousePos[0] - point[0],
|
||||||
mousePos[1] - point[1],
|
mousePos[1] - point[1],
|
||||||
);
|
);
|
||||||
if (distance < 15) {
|
if (distance < 10) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,11 +176,23 @@ export function PolygonCanvas({
|
|||||||
!activePolygon.isFinished &&
|
!activePolygon.isFinished &&
|
||||||
!isMouseOverAnyPoint(activePolygon, mousePos)
|
!isMouseOverAnyPoint(activePolygon, mousePos)
|
||||||
) {
|
) {
|
||||||
|
let updatedPoints;
|
||||||
|
|
||||||
|
// we've clicked near a line segment, so add a new point in the right position
|
||||||
|
if (isPointNearLineSegment(activePolygon, mousePos)) {
|
||||||
|
updatedPoints = addPointToPolygon(activePolygon, mousePos);
|
||||||
|
updatedPolygons[activePolygonIndex] = {
|
||||||
|
...activePolygon,
|
||||||
|
points: updatedPoints,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
updatedPoints = [...activePolygon.points, mousePos];
|
||||||
// Add a new point to the active polygon
|
// Add a new point to the active polygon
|
||||||
updatedPolygons[activePolygonIndex] = {
|
updatedPolygons[activePolygonIndex] = {
|
||||||
...activePolygon,
|
...activePolygon,
|
||||||
points: [...activePolygon.points, mousePos],
|
points: updatedPoints,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
setPolygons(updatedPolygons);
|
setPolygons(updatedPolygons);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +208,7 @@ export function PolygonCanvas({
|
|||||||
|
|
||||||
const activePolygon = polygons[activePolygonIndex];
|
const activePolygon = polygons[activePolygonIndex];
|
||||||
if (!activePolygon.isFinished && activePolygon.points.length >= 3) {
|
if (!activePolygon.isFinished && activePolygon.points.length >= 3) {
|
||||||
|
e.target.getStage()!.container().style.cursor = "default";
|
||||||
e.currentTarget.scale({ x: 2, y: 2 });
|
e.currentTarget.scale({ x: 2, y: 2 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -161,7 +234,7 @@ export function PolygonCanvas({
|
|||||||
const handleMouseOverAnyPoint = (
|
const handleMouseOverAnyPoint = (
|
||||||
e: KonvaEventObject<MouseEvent | TouchEvent>,
|
e: KonvaEventObject<MouseEvent | TouchEvent>,
|
||||||
) => {
|
) => {
|
||||||
if (activePolygonIndex === undefined || !polygons) {
|
if (!polygons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.target.getStage()!.container().style.cursor = "move";
|
e.target.getStage()!.container().style.cursor = "move";
|
||||||
@ -173,7 +246,12 @@ export function PolygonCanvas({
|
|||||||
if (activePolygonIndex === undefined || !polygons) {
|
if (activePolygonIndex === undefined || !polygons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const activePolygon = polygons[activePolygonIndex];
|
||||||
|
if (activePolygon.isFinished) {
|
||||||
e.target.getStage()!.container().style.cursor = "default";
|
e.target.getStage()!.container().style.cursor = "default";
|
||||||
|
} else {
|
||||||
|
e.target.getStage()!.container().style.cursor = "crosshair";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePointDragMove = (
|
const handlePointDragMove = (
|
||||||
@ -231,18 +309,19 @@ export function PolygonCanvas({
|
|||||||
|
|
||||||
const updatedPolygons = [...polygons];
|
const updatedPolygons = [...polygons];
|
||||||
const activePolygon = updatedPolygons[activePolygonIndex];
|
const activePolygon = updatedPolygons[activePolygonIndex];
|
||||||
|
const stage = e.target.getStage()!;
|
||||||
|
const mousePos = getMousePos(stage);
|
||||||
|
|
||||||
|
if (
|
||||||
|
activePolygon.isFinished ||
|
||||||
|
isMouseOverAnyPoint(activePolygon, mousePos) ||
|
||||||
|
isMouseOverFirstPoint(activePolygon, mousePos)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
if (activePolygon.isFinished) return;
|
|
||||||
e.target.getStage()!.container().style.cursor = "crosshair";
|
e.target.getStage()!.container().style.cursor = "crosshair";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStageMouseOut = (
|
|
||||||
e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
|
|
||||||
) => {
|
|
||||||
if (!e.target) return;
|
|
||||||
e.target.getStage()!.container().style.cursor = "default";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stage
|
<Stage
|
||||||
ref={stageRef}
|
ref={stageRef}
|
||||||
@ -251,7 +330,6 @@ export function PolygonCanvas({
|
|||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onTouchStart={handleMouseDown}
|
onTouchStart={handleMouseDown}
|
||||||
onMouseOver={handleStageMouseOver}
|
onMouseOver={handleStageMouseOver}
|
||||||
onMouseOut={handleStageMouseOut}
|
|
||||||
>
|
>
|
||||||
<Layer>
|
<Layer>
|
||||||
<Image
|
<Image
|
||||||
@ -265,11 +343,11 @@ export function PolygonCanvas({
|
|||||||
{polygons?.map(
|
{polygons?.map(
|
||||||
(polygon, index) =>
|
(polygon, index) =>
|
||||||
(selectedZoneMask === undefined ||
|
(selectedZoneMask === undefined ||
|
||||||
selectedZoneMask.includes(polygon.type)) && (
|
selectedZoneMask.includes(polygon.type)) &&
|
||||||
|
index !== activePolygonIndex && (
|
||||||
<PolygonDrawer
|
<PolygonDrawer
|
||||||
key={index}
|
key={index}
|
||||||
points={polygon.points}
|
points={polygon.points}
|
||||||
flattenedPoints={flattenPoints(polygon.points)}
|
|
||||||
isActive={index === activePolygonIndex}
|
isActive={index === activePolygonIndex}
|
||||||
isHovered={index === hoveredPolygonIndex}
|
isHovered={index === hoveredPolygonIndex}
|
||||||
isFinished={polygon.isFinished}
|
isFinished={polygon.isFinished}
|
||||||
@ -283,6 +361,25 @@ export function PolygonCanvas({
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
{activePolygonIndex !== undefined &&
|
||||||
|
polygons?.[activePolygonIndex] &&
|
||||||
|
(selectedZoneMask === undefined ||
|
||||||
|
selectedZoneMask.includes(polygons[activePolygonIndex].type)) && (
|
||||||
|
<PolygonDrawer
|
||||||
|
key={activePolygonIndex}
|
||||||
|
points={polygons[activePolygonIndex].points}
|
||||||
|
isActive={true}
|
||||||
|
isHovered={activePolygonIndex === hoveredPolygonIndex}
|
||||||
|
isFinished={polygons[activePolygonIndex].isFinished}
|
||||||
|
color={polygons[activePolygonIndex].color}
|
||||||
|
handlePointDragMove={handlePointDragMove}
|
||||||
|
handleGroupDragEnd={handleGroupDragEnd}
|
||||||
|
handleMouseOverStartPoint={handleMouseOverStartPoint}
|
||||||
|
handleMouseOutStartPoint={handleMouseOutStartPoint}
|
||||||
|
handleMouseOverAnyPoint={handleMouseOverAnyPoint}
|
||||||
|
handleMouseOutAnyPoint={handleMouseOutAnyPoint}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Layer>
|
</Layer>
|
||||||
</Stage>
|
</Stage>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import { useCallback, useRef, useState } from "react";
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
import { Line, Circle, Group } from "react-konva";
|
import { Line, Circle, Group } from "react-konva";
|
||||||
import { minMax, toRGBColorString, dragBoundFunc } from "@/utils/canvasUtil";
|
import {
|
||||||
|
minMax,
|
||||||
|
toRGBColorString,
|
||||||
|
dragBoundFunc,
|
||||||
|
flattenPoints,
|
||||||
|
} from "@/utils/canvasUtil";
|
||||||
import type { KonvaEventObject } from "konva/lib/Node";
|
import type { KonvaEventObject } from "konva/lib/Node";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import { Vector2d } from "konva/lib/types";
|
import { Vector2d } from "konva/lib/types";
|
||||||
|
|
||||||
type PolygonDrawerProps = {
|
type PolygonDrawerProps = {
|
||||||
points: number[][];
|
points: number[][];
|
||||||
flattenedPoints: number[];
|
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isHovered: boolean;
|
isHovered: boolean;
|
||||||
isFinished: boolean;
|
isFinished: boolean;
|
||||||
@ -30,7 +34,6 @@ type PolygonDrawerProps = {
|
|||||||
|
|
||||||
export default function PolygonDrawer({
|
export default function PolygonDrawer({
|
||||||
points,
|
points,
|
||||||
flattenedPoints,
|
|
||||||
isActive,
|
isActive,
|
||||||
isHovered,
|
isHovered,
|
||||||
isFinished,
|
isFinished,
|
||||||
@ -43,6 +46,7 @@ export default function PolygonDrawer({
|
|||||||
handleMouseOutAnyPoint,
|
handleMouseOutAnyPoint,
|
||||||
}: PolygonDrawerProps) {
|
}: PolygonDrawerProps) {
|
||||||
const vertexRadius = 6;
|
const vertexRadius = 6;
|
||||||
|
const flattenedPoints = useMemo(() => flattenPoints(points), [points]);
|
||||||
const [stage, setStage] = useState<Konva.Stage>();
|
const [stage, setStage] = useState<Konva.Stage>();
|
||||||
const [minMaxX, setMinMaxX] = useState([0, 0]);
|
const [minMaxX, setMinMaxX] = useState([0, 0]);
|
||||||
const [minMaxY, setMinMaxY] = useState([0, 0]);
|
const [minMaxY, setMinMaxY] = useState([0, 0]);
|
||||||
@ -59,7 +63,7 @@ export default function PolygonDrawer({
|
|||||||
const handleGroupMouseOut = (
|
const handleGroupMouseOut = (
|
||||||
e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
|
e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
|
||||||
) => {
|
) => {
|
||||||
if (!e.target) return;
|
if (!e.target || !isFinished) return;
|
||||||
e.target.getStage()!.container().style.cursor = "default";
|
e.target.getStage()!.container().style.cursor = "default";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,8 +109,6 @@ export default function PolygonDrawer({
|
|||||||
onMouseOver={isActive ? handleGroupMouseOver : undefined}
|
onMouseOver={isActive ? handleGroupMouseOver : undefined}
|
||||||
onTouchStart={isActive ? handleGroupMouseOver : undefined}
|
onTouchStart={isActive ? handleGroupMouseOver : undefined}
|
||||||
onMouseOut={isActive ? handleGroupMouseOut : undefined}
|
onMouseOut={isActive ? handleGroupMouseOut : undefined}
|
||||||
// TODO: don't use zindex
|
|
||||||
zIndex={isActive ? 999 : 100}
|
|
||||||
>
|
>
|
||||||
<Line
|
<Line
|
||||||
points={flattenedPoints}
|
points={flattenedPoints}
|
||||||
@ -119,8 +121,8 @@ export default function PolygonDrawer({
|
|||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const x = point[0] - vertexRadius / 2;
|
const x = point[0];
|
||||||
const y = point[1] - vertexRadius / 2;
|
const y = point[1];
|
||||||
const startPointAttr =
|
const startPointAttr =
|
||||||
index === 0
|
index === 0
|
||||||
? {
|
? {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user