This commit is contained in:
Josh Hawkins 2026-01-15 15:08:01 -06:00
parent 7badcbdbeb
commit 4f358c376f
8 changed files with 47 additions and 23 deletions

View File

@ -514,7 +514,7 @@
}
},
"motionMaskLabel": "Motion Mask {{number}}",
"objectMaskLabel": "Object Mask {{number}} ({{label}})",
"objectMaskLabel": "Object Mask {{number}}",
"form": {
"zoneName": {
"error": {
@ -636,6 +636,7 @@
},
"add": "New Motion Mask",
"edit": "Edit Motion Mask",
"defaultName": "Motion Mask {{number}}",
"context": {
"title": "Motion masks are used to prevent unwanted types of motion from triggering detection (example: tree branches, camera timestamps). Motion masks should be used <em>very sparingly</em>, over-masking will make it more difficult for objects to be tracked."
},

View File

@ -85,7 +85,7 @@ export default function MotionMaskEditPane({
const count = polygons.filter((poly) => poly.type == "motion_mask").length;
return t("masksAndZones.motionMasks.defaultName", {
number: count + 1,
number: count,
});
}, [polygons, t]);
@ -96,7 +96,7 @@ export default function MotionMaskEditPane({
const count = polygons.filter((poly) => poly.type == "motion_mask").length;
return `motion_mask_${count + 1}`;
return `motion_mask_${count}`;
}, [polygons]);
const polygonArea = useMemo(() => {

View File

@ -90,19 +90,10 @@ export default function ObjectMaskEditPane({
const count = polygons.filter((poly) => poly.type == "object_mask").length;
let objectType = "";
const objects = polygon?.objects[0];
if (objects === undefined) {
objectType = t("masksAndZones.zones.allObjects");
} else {
objectType = getTranslatedLabel(objects);
}
return t("masksAndZones.objectMaskLabel", {
number: count + 1,
label: objectType,
number: count,
});
}, [polygons, polygon, t]);
}, [polygons, t]);
const defaultId = useMemo(() => {
if (!polygons) {
@ -111,7 +102,7 @@ export default function ObjectMaskEditPane({
const count = polygons.filter((poly) => poly.type == "object_mask").length;
return `object_mask_${count + 1}`;
return `object_mask_${count}`;
}, [polygons]);
const formSchema = z.object({

View File

@ -321,6 +321,7 @@ export function PolygonCanvas({
isActive={index === activePolygonIndex}
isHovered={index === hoveredPolygonIndex}
isFinished={polygon.isFinished}
enabled={polygon.enabled}
color={polygon.color}
handlePointDragMove={handlePointDragMove}
handleGroupDragEnd={handleGroupDragEnd}
@ -350,6 +351,7 @@ export function PolygonCanvas({
isActive={true}
isHovered={activePolygonIndex === hoveredPolygonIndex}
isFinished={polygons[activePolygonIndex].isFinished}
enabled={polygons[activePolygonIndex].enabled}
color={polygons[activePolygonIndex].color}
handlePointDragMove={handlePointDragMove}
handleGroupDragEnd={handleGroupDragEnd}

View File

@ -24,6 +24,7 @@ type PolygonDrawerProps = {
isActive: boolean;
isHovered: boolean;
isFinished: boolean;
enabled?: boolean;
color: number[];
handlePointDragMove: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
handleGroupDragEnd: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
@ -39,6 +40,7 @@ export default function PolygonDrawer({
isActive,
isHovered,
isFinished,
enabled = true,
color,
handlePointDragMove,
handleGroupDragEnd,
@ -53,6 +55,26 @@ export default function PolygonDrawer({
const groupRef = useRef<Konva.Group>(null);
const [cursor, setCursor] = useState("default");
const patternCanvas = useMemo(() => {
if (enabled) {
return undefined;
}
const canvas = document.createElement("canvas");
canvas.width = 10;
canvas.height = 10;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.strokeStyle = "rgba(0, 0, 0, 0.3)";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(10, 0);
ctx.lineTo(0, 10);
ctx.stroke();
}
return canvas as unknown as HTMLImageElement;
}, [enabled]);
const handleMouseOverPoint = (
e: KonvaEventObject<MouseEvent | TouchEvent>,
) => {
@ -180,6 +202,15 @@ export default function PolygonDrawer({
: setCursor("default")
}
/>
{!enabled && isFinished && (
<Line
points={flattenedPoints}
closed={isFinished}
fillPriority="pattern"
fillPatternImage={patternCanvas}
listening={false}
/>
)}
{isFinished && isActive && (
<Line
name="unfilled-line"

View File

@ -248,9 +248,7 @@ export default function PolygonItem({
<div
key={index}
className={`transition-background my-1.5 flex flex-row items-center justify-between rounded-lg p-1 duration-100 ${
polygon.enabled === false ? "opacity-50" : ""
}`}
className="transition-background my-1.5 flex flex-row items-center justify-between rounded-lg p-1 duration-100"
data-index={index}
onMouseEnter={() => setHoveredPolygonIndex(index)}
onMouseLeave={() => setHoveredPolygonIndex(null)}

View File

@ -454,7 +454,7 @@ export default function ZoneEditPane({
friendlyNameQuery = `&cameras.${polygon?.camera}.zones.${zoneName}.friendly_name=${encodeURIComponent(friendly_name)}`;
}
const enabledQuery = `&cameras.${polygon?.camera}.zones.${zoneName}.enabled=${enabled}`;
const enabledQuery = `&cameras.${polygon?.camera}.zones.${zoneName}.enabled=${enabled ? "True" : "False"}`;
axios
.put(

View File

@ -169,6 +169,7 @@ export default function MasksAndZonesView({
objects: [],
camera: selectedCamera,
color: polygonColor,
enabled: true,
},
]);
};
@ -242,7 +243,7 @@ export default function MasksAndZonesView({
distances:
zoneData.distances?.map((distance) => parseFloat(distance)) ?? [],
isFinished: true,
color: zoneData.enabled ? zoneData.color : [100, 100, 100],
color: zoneData.color,
}),
);
@ -269,7 +270,7 @@ export default function MasksAndZonesView({
),
distances: [],
isFinished: true,
color: maskData.enabled ? [0, 0, 255] : [100, 100, 100],
color: [0, 0, 255],
}),
);
@ -292,7 +293,7 @@ export default function MasksAndZonesView({
),
distances: [],
isFinished: true,
color: maskData.enabled ? [128, 128, 128] : [80, 80, 80],
color: [128, 128, 128],
}),
);
@ -329,7 +330,7 @@ export default function MasksAndZonesView({
),
distances: [],
isFinished: true,
color: maskData.enabled ? [128, 128, 128] : [80, 80, 80],
color: [128, 128, 128],
};
objectMaskIndex++;
return [newMask];