diff --git a/web/src/components/settings/PolygonCanvas.tsx b/web/src/components/settings/PolygonCanvas.tsx index e6851b63c..d2a0a46b5 100644 --- a/web/src/components/settings/PolygonCanvas.tsx +++ b/web/src/components/settings/PolygonCanvas.tsx @@ -17,6 +17,7 @@ type PolygonCanvasProps = { activePolygonIndex: number | undefined; hoveredPolygonIndex: number | null; selectedZoneMask: PolygonType[] | undefined; + activeLine?: number; }; export function PolygonCanvas({ @@ -29,6 +30,7 @@ export function PolygonCanvas({ activePolygonIndex, hoveredPolygonIndex, selectedZoneMask, + activeLine, }: PolygonCanvasProps) { const [isLoaded, setIsLoaded] = useState(false); const [image, setImage] = useState(); @@ -281,12 +283,14 @@ export function PolygonCanvas({ stageRef={stageRef} key={index} points={polygon.points} + distances={polygon.distances} isActive={index === activePolygonIndex} isHovered={index === hoveredPolygonIndex} isFinished={polygon.isFinished} color={polygon.color} handlePointDragMove={handlePointDragMove} handleGroupDragEnd={handleGroupDragEnd} + activeLine={activeLine} /> ), )} @@ -298,12 +302,14 @@ export function PolygonCanvas({ stageRef={stageRef} key={activePolygonIndex} points={polygons[activePolygonIndex].points} + distances={polygons[activePolygonIndex].distances} isActive={true} isHovered={activePolygonIndex === hoveredPolygonIndex} isFinished={polygons[activePolygonIndex].isFinished} color={polygons[activePolygonIndex].color} handlePointDragMove={handlePointDragMove} handleGroupDragEnd={handleGroupDragEnd} + activeLine={activeLine} /> )} diff --git a/web/src/components/settings/PolygonDrawer.tsx b/web/src/components/settings/PolygonDrawer.tsx index 966aad2ca..1ae3d4601 100644 --- a/web/src/components/settings/PolygonDrawer.tsx +++ b/web/src/components/settings/PolygonDrawer.tsx @@ -6,7 +6,7 @@ import { useRef, useState, } from "react"; -import { Line, Circle, Group } from "react-konva"; +import { Line, Circle, Group, Text, Rect } from "react-konva"; import { minMax, toRGBColorString, @@ -20,23 +20,27 @@ import { Vector2d } from "konva/lib/types"; type PolygonDrawerProps = { stageRef: RefObject; points: number[][]; + distances: number[]; isActive: boolean; isHovered: boolean; isFinished: boolean; color: number[]; handlePointDragMove: (e: KonvaEventObject) => void; handleGroupDragEnd: (e: KonvaEventObject) => void; + activeLine?: number; }; export default function PolygonDrawer({ stageRef, points, + distances, isActive, isHovered, isFinished, color, handlePointDragMove, handleGroupDragEnd, + activeLine, }: PolygonDrawerProps) { const vertexRadius = 6; const flattenedPoints = useMemo(() => flattenPoints(points), [points]); @@ -113,6 +117,33 @@ export default function PolygonDrawer({ stageRef.current.container().style.cursor = 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 ( )} + {isActive && activeLinePoints.length > 0 && ( + + )} {points.map((point, index) => { if (!isActive) { return; @@ -195,6 +234,43 @@ export default function PolygonDrawer({ /> ); })} + {isFinished && ( + + {midpoints.map((midpoint, index) => { + const [x, y] = midpoint; + const distance = distances[index]; + if (distance === undefined) return null; + + const squareSize = 22; + + return ( + + + + + ); + })} + + )} ); } diff --git a/web/src/components/settings/ZoneEditPane.tsx b/web/src/components/settings/ZoneEditPane.tsx index a7a568b01..abdc90e93 100644 --- a/web/src/components/settings/ZoneEditPane.tsx +++ b/web/src/components/settings/ZoneEditPane.tsx @@ -40,6 +40,7 @@ type ZoneEditPaneProps = { setIsLoading: React.Dispatch>; onSave?: () => void; onCancel?: () => void; + setActiveLine: React.Dispatch>; }; export default function ZoneEditPane({ @@ -52,6 +53,7 @@ export default function ZoneEditPane({ setIsLoading, onSave, onCancel, + setActiveLine, }: ZoneEditPaneProps) { const { data: config, mutate: updateConfig } = useSWR("config"); @@ -569,9 +571,13 @@ export default function ZoneEditPane({ name="topWidth" render={({ field }) => ( - Top Width + Line A distance - + setActiveLine(1)} + onBlur={() => setActiveLine(undefined)} + /> )} @@ -581,9 +587,13 @@ export default function ZoneEditPane({ name="bottomWidth" render={({ field }) => ( - Bottom Width + Line B distance - + setActiveLine(2)} + onBlur={() => setActiveLine(undefined)} + /> )} @@ -593,9 +603,13 @@ export default function ZoneEditPane({ name="leftDepth" render={({ field }) => ( - Left Depth + Line C distance - + setActiveLine(3)} + onBlur={() => setActiveLine(undefined)} + /> )} @@ -605,9 +619,13 @@ export default function ZoneEditPane({ name="rightDepth" render={({ field }) => ( - Right Depth + Line D distance - + setActiveLine(4)} + onBlur={() => setActiveLine(undefined)} + /> )} diff --git a/web/src/views/settings/MasksAndZonesView.tsx b/web/src/views/settings/MasksAndZonesView.tsx index ab2646b5f..5c74a121b 100644 --- a/web/src/views/settings/MasksAndZonesView.tsx +++ b/web/src/views/settings/MasksAndZonesView.tsx @@ -61,6 +61,7 @@ export default function MasksAndZonesView({ ); const containerRef = useRef(null); const [editPane, setEditPane] = useState(undefined); + const [activeLine, setActiveLine] = useState(); const { addMessage } = useContext(StatusBarMessagesContext)!; @@ -161,6 +162,7 @@ export default function MasksAndZonesView({ ...(allPolygons || []), { points: [], + distances: [], isFinished: false, type, typeIndex: 9999, @@ -238,6 +240,7 @@ export default function MasksAndZonesView({ scaledWidth, scaledHeight, ), + distances: zoneData.distances.map((distance) => parseFloat(distance)), isFinished: true, color: zoneData.color, }), @@ -267,6 +270,7 @@ export default function MasksAndZonesView({ scaledWidth, scaledHeight, ), + distances: [], isFinished: true, color: [0, 0, 255], })); @@ -290,6 +294,7 @@ export default function MasksAndZonesView({ scaledWidth, scaledHeight, ), + distances: [], isFinished: true, color: [128, 128, 128], })); @@ -316,6 +321,7 @@ export default function MasksAndZonesView({ scaledWidth, scaledHeight, ), + distances: [], isFinished: true, color: [128, 128, 128], }; @@ -391,6 +397,7 @@ export default function MasksAndZonesView({ setIsLoading={setIsLoading} onCancel={handleCancel} onSave={handleSave} + setActiveLine={setActiveLine} /> )} {editPane == "motion_mask" && ( @@ -653,6 +660,7 @@ export default function MasksAndZonesView({ activePolygonIndex={activePolygonIndex} hoveredPolygonIndex={hoveredPolygonIndex} selectedZoneMask={selectedZoneMask} + activeLine={activeLine} /> ) : (