- added Konvas lib
- added feed from proof of concept
This commit is contained in:
@@ -19,8 +19,10 @@
|
|||||||
"@tanstack/react-router": "^1.136.18",
|
"@tanstack/react-router": "^1.136.18",
|
||||||
"@tanstack/react-router-devtools": "^1.136.18",
|
"@tanstack/react-router-devtools": "^1.136.18",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"konva": "^10.0.11",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0"
|
"react-dom": "^19.2.0",
|
||||||
|
"react-konva": "^19.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
|
|||||||
11
src/features/cameras/components/CameraGrid.tsx
Normal file
11
src/features/cameras/components/CameraGrid.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import VideoFeedCard from "./VideoFeedCard";
|
||||||
|
|
||||||
|
const CameraGrid = () => {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2">
|
||||||
|
<VideoFeedCard />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CameraGrid;
|
||||||
13
src/features/cameras/components/VideoFeedCard.tsx
Normal file
13
src/features/cameras/components/VideoFeedCard.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Card from "../../../ui/Card";
|
||||||
|
|
||||||
|
import VideoFeedGridPainter from "./VideoFeedGridPainter";
|
||||||
|
|
||||||
|
const VideoFeedCard = () => {
|
||||||
|
return (
|
||||||
|
<Card className="w-full md:w-[70%]">
|
||||||
|
<VideoFeedGridPainter />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VideoFeedCard;
|
||||||
75
src/features/cameras/components/VideoFeedGridPainter.tsx
Normal file
75
src/features/cameras/components/VideoFeedGridPainter.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { useRef, type RefObject } from "react";
|
||||||
|
import { Stage, Layer, Image, Rect } from "react-konva";
|
||||||
|
import { useCreateVideoSnapshot } from "../hooks/useGetvideoSnapshots";
|
||||||
|
|
||||||
|
const VideoFeedGridPainter = () => {
|
||||||
|
const { latestBitmapRef } = useCreateVideoSnapshot();
|
||||||
|
const isDrawing = useRef(false);
|
||||||
|
|
||||||
|
const rows = 100;
|
||||||
|
const cols = 100;
|
||||||
|
const size = 10;
|
||||||
|
const gap = 0;
|
||||||
|
|
||||||
|
const squares = [];
|
||||||
|
for (let row = 0; row < rows; row++) {
|
||||||
|
for (let col = 0; col < cols; col++) {
|
||||||
|
squares.push(
|
||||||
|
<Rect
|
||||||
|
key={`${row}-${col}`}
|
||||||
|
x={col * (size + gap)}
|
||||||
|
y={row * (size + gap)}
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
fill="#ddd"
|
||||||
|
stroke="black"
|
||||||
|
strokeWidth={0.5}
|
||||||
|
opacity={0.5}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCoords = (e) => {
|
||||||
|
isDrawing.current = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
if (!isDrawing.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pos = e.target.getStage().getPointerPosition();
|
||||||
|
console.log(pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
isDrawing.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const draw = (bmp: RefObject<ImageBitmap | null>) => {
|
||||||
|
if (!bmp || !bmp.current) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const frame = bmp.current;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stage
|
||||||
|
width={640}
|
||||||
|
height={360}
|
||||||
|
onMouseDown={getCoords}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onMouseUp={handleMouseUp}
|
||||||
|
onMouseLeave={handleMouseUp}
|
||||||
|
>
|
||||||
|
<Layer>
|
||||||
|
<Image image={draw(latestBitmapRef)} width={640} height={360} />
|
||||||
|
</Layer>
|
||||||
|
<Layer opacity={0.3}>{squares}</Layer>
|
||||||
|
</Stage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VideoFeedGridPainter;
|
||||||
22
src/features/cameras/hooks/useGetVideoFeed.ts
Normal file
22
src/features/cameras/hooks/useGetVideoFeed.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
const getfeed = async () => {
|
||||||
|
const response = await fetch(`http://100.115.148.59/TargetDetectionColour-preview`, {
|
||||||
|
signal: AbortSignal.timeout(300000),
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Cannot reach endpoint (${response.status})`);
|
||||||
|
}
|
||||||
|
return response.blob();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetVideoFeed = () => {
|
||||||
|
const videoQuery = useQuery({
|
||||||
|
queryKey: ["getfeed"],
|
||||||
|
queryFn: getfeed,
|
||||||
|
refetchInterval: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { videoQuery };
|
||||||
|
};
|
||||||
25
src/features/cameras/hooks/useGetvideoSnapshots.ts
Normal file
25
src/features/cameras/hooks/useGetvideoSnapshots.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { useGetVideoFeed } from "./useGetVideoFeed";
|
||||||
|
|
||||||
|
export const useCreateVideoSnapshot = () => {
|
||||||
|
const latestBitmapRef = useRef<ImageBitmap | null>(null);
|
||||||
|
const { videoQuery } = useGetVideoFeed();
|
||||||
|
|
||||||
|
const snapShot = videoQuery?.data;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function createBitmap() {
|
||||||
|
if (!snapShot) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bitmap = await createImageBitmap(snapShot);
|
||||||
|
latestBitmapRef.current = bitmap;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createBitmap();
|
||||||
|
}, [snapShot]);
|
||||||
|
|
||||||
|
return { latestBitmapRef };
|
||||||
|
};
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import CameraGrid from "../features/cameras/components/CameraGrid";
|
||||||
|
|
||||||
export const Route = createFileRoute('/baywatch')({
|
export const Route = createFileRoute("/baywatch")({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
})
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
return <div>Hello "/baywatch"!</div>
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold">Cameras</h2>
|
||||||
|
<CameraGrid />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const Header = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to="/baywatch" className="[&.active]:font-bold">
|
<Link to="/baywatch" className="[&.active]:font-bold">
|
||||||
Baywatch
|
Cameras
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/output" className="[&.active]:font-bold">
|
<Link to="/output" className="[&.active]:font-bold">
|
||||||
Output
|
Output
|
||||||
|
|||||||
41
yarn.lock
41
yarn.lock
@@ -1011,6 +1011,16 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
|
||||||
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
||||||
|
|
||||||
|
"@types/react-reconciler@^0.28.9":
|
||||||
|
version "0.28.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz#d24b4864c384e770c83275b3fe73fba00269c83b"
|
||||||
|
integrity sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==
|
||||||
|
|
||||||
|
"@types/react-reconciler@^0.32.2":
|
||||||
|
version "0.32.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.32.3.tgz#eb4b346f367f29f07628032934d30a4f3f9eaba7"
|
||||||
|
integrity sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==
|
||||||
|
|
||||||
"@types/react@^19.2.5":
|
"@types/react@^19.2.5":
|
||||||
version "19.2.6"
|
version "19.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.6.tgz#d27db1ff45012d53980f5589fda925278e1249ca"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.6.tgz#d27db1ff45012d53980f5589fda925278e1249ca"
|
||||||
@@ -1726,6 +1736,13 @@ isexe@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||||
|
|
||||||
|
its-fine@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-2.0.0.tgz#a90b18a3ee4c211a1fb6faac2abcc2b682ce1f21"
|
||||||
|
integrity sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==
|
||||||
|
dependencies:
|
||||||
|
"@types/react-reconciler" "^0.28.9"
|
||||||
|
|
||||||
jiti@^2.6.1:
|
jiti@^2.6.1:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92"
|
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92"
|
||||||
@@ -1775,6 +1792,11 @@ keyv@^4.5.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
json-buffer "3.0.1"
|
json-buffer "3.0.1"
|
||||||
|
|
||||||
|
konva@^10.0.11:
|
||||||
|
version "10.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/konva/-/konva-10.0.11.tgz#f63d3422625d13513094b646a2f09359d644542a"
|
||||||
|
integrity sha512-h0O6YNrwdgfb76kzkiMIqkyufUxE6GPcNwJZhYalnZ5qnYBEuxSRk62fQwtJqygGP5hdZKzJs2ea1ivVP+zetw==
|
||||||
|
|
||||||
levn@^0.4.1:
|
levn@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
|
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
|
||||||
@@ -2044,6 +2066,23 @@ react-dom@^19.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
scheduler "^0.27.0"
|
scheduler "^0.27.0"
|
||||||
|
|
||||||
|
react-konva@^19.2.0:
|
||||||
|
version "19.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-19.2.0.tgz#b4cc5d73cd6d642569e4df36a0139996c3dcf8e6"
|
||||||
|
integrity sha512-Ofifq/rdNvff50+Lj8x86WSfoeQDvdysOlsXMMrpD2uWmDxUPrEYSRLt27iCfdovQZL6xinKRpX9VaL9xDwXDQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/react-reconciler" "^0.32.2"
|
||||||
|
its-fine "^2.0.0"
|
||||||
|
react-reconciler "0.33.0"
|
||||||
|
scheduler "0.27.0"
|
||||||
|
|
||||||
|
react-reconciler@0.33.0:
|
||||||
|
version "0.33.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.33.0.tgz#9dd20208d45baa5b0b4701781f858236657f15e1"
|
||||||
|
integrity sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==
|
||||||
|
dependencies:
|
||||||
|
scheduler "^0.27.0"
|
||||||
|
|
||||||
react-refresh@^0.18.0:
|
react-refresh@^0.18.0:
|
||||||
version "0.18.0"
|
version "0.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062"
|
||||||
@@ -2125,7 +2164,7 @@ run-parallel@^1.1.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask "^1.2.2"
|
queue-microtask "^1.2.2"
|
||||||
|
|
||||||
scheduler@^0.27.0:
|
scheduler@0.27.0, scheduler@^0.27.0:
|
||||||
version "0.27.0"
|
version "0.27.0"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
|
||||||
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
|
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
|
||||||
|
|||||||
Reference in New Issue
Block a user