mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-17 16:44:29 +03:00
highlight line on canvas when entering value in zone edit pane
This commit is contained in:
parent
9be1454f10
commit
bb2a1e12cc
@ -17,6 +17,7 @@ type PolygonCanvasProps = {
|
|||||||
activePolygonIndex: number | undefined;
|
activePolygonIndex: number | undefined;
|
||||||
hoveredPolygonIndex: number | null;
|
hoveredPolygonIndex: number | null;
|
||||||
selectedZoneMask: PolygonType[] | undefined;
|
selectedZoneMask: PolygonType[] | undefined;
|
||||||
|
activeLine?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PolygonCanvas({
|
export function PolygonCanvas({
|
||||||
@ -29,6 +30,7 @@ export function PolygonCanvas({
|
|||||||
activePolygonIndex,
|
activePolygonIndex,
|
||||||
hoveredPolygonIndex,
|
hoveredPolygonIndex,
|
||||||
selectedZoneMask,
|
selectedZoneMask,
|
||||||
|
activeLine,
|
||||||
}: PolygonCanvasProps) {
|
}: PolygonCanvasProps) {
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
const [image, setImage] = useState<HTMLImageElement | undefined>();
|
const [image, setImage] = useState<HTMLImageElement | undefined>();
|
||||||
@ -281,12 +283,14 @@ export function PolygonCanvas({
|
|||||||
stageRef={stageRef}
|
stageRef={stageRef}
|
||||||
key={index}
|
key={index}
|
||||||
points={polygon.points}
|
points={polygon.points}
|
||||||
|
distances={polygon.distances}
|
||||||
isActive={index === activePolygonIndex}
|
isActive={index === activePolygonIndex}
|
||||||
isHovered={index === hoveredPolygonIndex}
|
isHovered={index === hoveredPolygonIndex}
|
||||||
isFinished={polygon.isFinished}
|
isFinished={polygon.isFinished}
|
||||||
color={polygon.color}
|
color={polygon.color}
|
||||||
handlePointDragMove={handlePointDragMove}
|
handlePointDragMove={handlePointDragMove}
|
||||||
handleGroupDragEnd={handleGroupDragEnd}
|
handleGroupDragEnd={handleGroupDragEnd}
|
||||||
|
activeLine={activeLine}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
@ -298,12 +302,14 @@ export function PolygonCanvas({
|
|||||||
stageRef={stageRef}
|
stageRef={stageRef}
|
||||||
key={activePolygonIndex}
|
key={activePolygonIndex}
|
||||||
points={polygons[activePolygonIndex].points}
|
points={polygons[activePolygonIndex].points}
|
||||||
|
distances={polygons[activePolygonIndex].distances}
|
||||||
isActive={true}
|
isActive={true}
|
||||||
isHovered={activePolygonIndex === hoveredPolygonIndex}
|
isHovered={activePolygonIndex === hoveredPolygonIndex}
|
||||||
isFinished={polygons[activePolygonIndex].isFinished}
|
isFinished={polygons[activePolygonIndex].isFinished}
|
||||||
color={polygons[activePolygonIndex].color}
|
color={polygons[activePolygonIndex].color}
|
||||||
handlePointDragMove={handlePointDragMove}
|
handlePointDragMove={handlePointDragMove}
|
||||||
handleGroupDragEnd={handleGroupDragEnd}
|
handleGroupDragEnd={handleGroupDragEnd}
|
||||||
|
activeLine={activeLine}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Layer>
|
</Layer>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Line, Circle, Group } from "react-konva";
|
import { Line, Circle, Group, Text, Rect } from "react-konva";
|
||||||
import {
|
import {
|
||||||
minMax,
|
minMax,
|
||||||
toRGBColorString,
|
toRGBColorString,
|
||||||
@ -20,23 +20,27 @@ import { Vector2d } from "konva/lib/types";
|
|||||||
type PolygonDrawerProps = {
|
type PolygonDrawerProps = {
|
||||||
stageRef: RefObject<Konva.Stage>;
|
stageRef: RefObject<Konva.Stage>;
|
||||||
points: number[][];
|
points: number[][];
|
||||||
|
distances: number[];
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isHovered: boolean;
|
isHovered: boolean;
|
||||||
isFinished: boolean;
|
isFinished: boolean;
|
||||||
color: number[];
|
color: number[];
|
||||||
handlePointDragMove: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
|
handlePointDragMove: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
|
||||||
handleGroupDragEnd: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
|
handleGroupDragEnd: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
|
||||||
|
activeLine?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PolygonDrawer({
|
export default function PolygonDrawer({
|
||||||
stageRef,
|
stageRef,
|
||||||
points,
|
points,
|
||||||
|
distances,
|
||||||
isActive,
|
isActive,
|
||||||
isHovered,
|
isHovered,
|
||||||
isFinished,
|
isFinished,
|
||||||
color,
|
color,
|
||||||
handlePointDragMove,
|
handlePointDragMove,
|
||||||
handleGroupDragEnd,
|
handleGroupDragEnd,
|
||||||
|
activeLine,
|
||||||
}: PolygonDrawerProps) {
|
}: PolygonDrawerProps) {
|
||||||
const vertexRadius = 6;
|
const vertexRadius = 6;
|
||||||
const flattenedPoints = useMemo(() => flattenPoints(points), [points]);
|
const flattenedPoints = useMemo(() => flattenPoints(points), [points]);
|
||||||
@ -113,6 +117,33 @@ export default function PolygonDrawer({
|
|||||||
stageRef.current.container().style.cursor = cursor;
|
stageRef.current.container().style.cursor = cursor;
|
||||||
}, [stageRef, cursor]);
|
}, [stageRef, cursor]);
|
||||||
|
|
||||||
|
// Calculate midpoints for distance labels based on sorted points
|
||||||
|
const midpoints = useMemo(() => {
|
||||||
|
const midpointsArray = [];
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const p1 = points[i];
|
||||||
|
const p2 = points[(i + 1) % points.length];
|
||||||
|
const midpointX = (p1[0] + p2[0]) / 2;
|
||||||
|
const midpointY = (p1[1] + p2[1]) / 2;
|
||||||
|
midpointsArray.push([midpointX, midpointY]);
|
||||||
|
}
|
||||||
|
return midpointsArray;
|
||||||
|
}, [points]);
|
||||||
|
|
||||||
|
// Determine the points for the active line
|
||||||
|
const activeLinePoints = useMemo(() => {
|
||||||
|
if (
|
||||||
|
activeLine === undefined ||
|
||||||
|
activeLine < 1 ||
|
||||||
|
activeLine > points.length
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const p1 = points[activeLine - 1];
|
||||||
|
const p2 = points[activeLine % points.length];
|
||||||
|
return [p1[0], p1[1], p2[0], p2[1]];
|
||||||
|
}, [activeLine, points]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
name="polygon"
|
name="polygon"
|
||||||
@ -158,6 +189,14 @@ export default function PolygonDrawer({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isActive && activeLinePoints.length > 0 && (
|
||||||
|
<Line
|
||||||
|
points={activeLinePoints}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={6}
|
||||||
|
hitStrokeWidth={12}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{points.map((point, index) => {
|
{points.map((point, index) => {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
return;
|
return;
|
||||||
@ -195,6 +234,43 @@ export default function PolygonDrawer({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{isFinished && (
|
||||||
|
<Group>
|
||||||
|
{midpoints.map((midpoint, index) => {
|
||||||
|
const [x, y] = midpoint;
|
||||||
|
const distance = distances[index];
|
||||||
|
if (distance === undefined) return null;
|
||||||
|
|
||||||
|
const squareSize = 22;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
key={`distance-group-${index}`}
|
||||||
|
x={x - squareSize / 2}
|
||||||
|
y={y - squareSize / 2}
|
||||||
|
>
|
||||||
|
<Rect
|
||||||
|
width={squareSize}
|
||||||
|
height={squareSize}
|
||||||
|
fill={colorString(true)}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={1}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
text={`${distance}`}
|
||||||
|
width={squareSize}
|
||||||
|
y={4}
|
||||||
|
fontSize={16}
|
||||||
|
fontFamily="Arial"
|
||||||
|
fill="white"
|
||||||
|
align="center"
|
||||||
|
verticalAlign="middle"
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ type ZoneEditPaneProps = {
|
|||||||
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
|
setActiveLine: React.Dispatch<React.SetStateAction<number | undefined>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ZoneEditPane({
|
export default function ZoneEditPane({
|
||||||
@ -52,6 +53,7 @@ export default function ZoneEditPane({
|
|||||||
setIsLoading,
|
setIsLoading,
|
||||||
onSave,
|
onSave,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
setActiveLine,
|
||||||
}: ZoneEditPaneProps) {
|
}: ZoneEditPaneProps) {
|
||||||
const { data: config, mutate: updateConfig } =
|
const { data: config, mutate: updateConfig } =
|
||||||
useSWR<FrigateConfig>("config");
|
useSWR<FrigateConfig>("config");
|
||||||
@ -569,9 +571,13 @@ export default function ZoneEditPane({
|
|||||||
name="topWidth"
|
name="topWidth"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Top Width</FormLabel>
|
<FormLabel>Line A distance</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="Enter top width..." />
|
<Input
|
||||||
|
{...field}
|
||||||
|
onFocus={() => setActiveLine(1)}
|
||||||
|
onBlur={() => setActiveLine(undefined)}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@ -581,9 +587,13 @@ export default function ZoneEditPane({
|
|||||||
name="bottomWidth"
|
name="bottomWidth"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Bottom Width</FormLabel>
|
<FormLabel>Line B distance</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="Enter bottom width..." />
|
<Input
|
||||||
|
{...field}
|
||||||
|
onFocus={() => setActiveLine(2)}
|
||||||
|
onBlur={() => setActiveLine(undefined)}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@ -593,9 +603,13 @@ export default function ZoneEditPane({
|
|||||||
name="leftDepth"
|
name="leftDepth"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Left Depth</FormLabel>
|
<FormLabel>Line C distance</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="Enter left depth..." />
|
<Input
|
||||||
|
{...field}
|
||||||
|
onFocus={() => setActiveLine(3)}
|
||||||
|
onBlur={() => setActiveLine(undefined)}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@ -605,9 +619,13 @@ export default function ZoneEditPane({
|
|||||||
name="rightDepth"
|
name="rightDepth"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Right Depth</FormLabel>
|
<FormLabel>Line D distance</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} placeholder="Enter right depth..." />
|
<Input
|
||||||
|
{...field}
|
||||||
|
onFocus={() => setActiveLine(4)}
|
||||||
|
onBlur={() => setActiveLine(undefined)}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -61,6 +61,7 @@ export default function MasksAndZonesView({
|
|||||||
);
|
);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [editPane, setEditPane] = useState<PolygonType | undefined>(undefined);
|
const [editPane, setEditPane] = useState<PolygonType | undefined>(undefined);
|
||||||
|
const [activeLine, setActiveLine] = useState<number | undefined>();
|
||||||
|
|
||||||
const { addMessage } = useContext(StatusBarMessagesContext)!;
|
const { addMessage } = useContext(StatusBarMessagesContext)!;
|
||||||
|
|
||||||
@ -161,6 +162,7 @@ export default function MasksAndZonesView({
|
|||||||
...(allPolygons || []),
|
...(allPolygons || []),
|
||||||
{
|
{
|
||||||
points: [],
|
points: [],
|
||||||
|
distances: [],
|
||||||
isFinished: false,
|
isFinished: false,
|
||||||
type,
|
type,
|
||||||
typeIndex: 9999,
|
typeIndex: 9999,
|
||||||
@ -238,6 +240,7 @@ export default function MasksAndZonesView({
|
|||||||
scaledWidth,
|
scaledWidth,
|
||||||
scaledHeight,
|
scaledHeight,
|
||||||
),
|
),
|
||||||
|
distances: zoneData.distances.map((distance) => parseFloat(distance)),
|
||||||
isFinished: true,
|
isFinished: true,
|
||||||
color: zoneData.color,
|
color: zoneData.color,
|
||||||
}),
|
}),
|
||||||
@ -267,6 +270,7 @@ export default function MasksAndZonesView({
|
|||||||
scaledWidth,
|
scaledWidth,
|
||||||
scaledHeight,
|
scaledHeight,
|
||||||
),
|
),
|
||||||
|
distances: [],
|
||||||
isFinished: true,
|
isFinished: true,
|
||||||
color: [0, 0, 255],
|
color: [0, 0, 255],
|
||||||
}));
|
}));
|
||||||
@ -290,6 +294,7 @@ export default function MasksAndZonesView({
|
|||||||
scaledWidth,
|
scaledWidth,
|
||||||
scaledHeight,
|
scaledHeight,
|
||||||
),
|
),
|
||||||
|
distances: [],
|
||||||
isFinished: true,
|
isFinished: true,
|
||||||
color: [128, 128, 128],
|
color: [128, 128, 128],
|
||||||
}));
|
}));
|
||||||
@ -316,6 +321,7 @@ export default function MasksAndZonesView({
|
|||||||
scaledWidth,
|
scaledWidth,
|
||||||
scaledHeight,
|
scaledHeight,
|
||||||
),
|
),
|
||||||
|
distances: [],
|
||||||
isFinished: true,
|
isFinished: true,
|
||||||
color: [128, 128, 128],
|
color: [128, 128, 128],
|
||||||
};
|
};
|
||||||
@ -391,6 +397,7 @@ export default function MasksAndZonesView({
|
|||||||
setIsLoading={setIsLoading}
|
setIsLoading={setIsLoading}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
|
setActiveLine={setActiveLine}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{editPane == "motion_mask" && (
|
{editPane == "motion_mask" && (
|
||||||
@ -653,6 +660,7 @@ export default function MasksAndZonesView({
|
|||||||
activePolygonIndex={activePolygonIndex}
|
activePolygonIndex={activePolygonIndex}
|
||||||
hoveredPolygonIndex={hoveredPolygonIndex}
|
hoveredPolygonIndex={hoveredPolygonIndex}
|
||||||
selectedZoneMask={selectedZoneMask}
|
selectedZoneMask={selectedZoneMask}
|
||||||
|
activeLine={activeLine}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Skeleton className="size-full" />
|
<Skeleton className="size-full" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user