diff --git a/package.json b/package.json index cb4c39e..31317c7 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,10 @@ "@tanstack/react-router": "^1.136.18", "@tanstack/react-router-devtools": "^1.136.18", "clsx": "^2.1.1", + "konva": "^10.0.11", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-konva": "^19.2.0" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/src/features/cameras/components/CameraGrid.tsx b/src/features/cameras/components/CameraGrid.tsx new file mode 100644 index 0000000..eb5c2b3 --- /dev/null +++ b/src/features/cameras/components/CameraGrid.tsx @@ -0,0 +1,11 @@ +import VideoFeedCard from "./VideoFeedCard"; + +const CameraGrid = () => { + return ( +
+ +
+ ); +}; + +export default CameraGrid; diff --git a/src/features/cameras/components/VideoFeedCard.tsx b/src/features/cameras/components/VideoFeedCard.tsx new file mode 100644 index 0000000..2d5dc8c --- /dev/null +++ b/src/features/cameras/components/VideoFeedCard.tsx @@ -0,0 +1,13 @@ +import Card from "../../../ui/Card"; + +import VideoFeedGridPainter from "./VideoFeedGridPainter"; + +const VideoFeedCard = () => { + return ( + + + + ); +}; + +export default VideoFeedCard; diff --git a/src/features/cameras/components/VideoFeedGridPainter.tsx b/src/features/cameras/components/VideoFeedGridPainter.tsx new file mode 100644 index 0000000..3ecdba0 --- /dev/null +++ b/src/features/cameras/components/VideoFeedGridPainter.tsx @@ -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( + , + ); + } + } + + 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) => { + if (!bmp || !bmp.current) { + return; + } else { + const frame = bmp.current; + return frame; + } + }; + + return ( + + + + + {squares} + + ); +}; + +export default VideoFeedGridPainter; diff --git a/src/features/cameras/hooks/useGetVideoFeed.ts b/src/features/cameras/hooks/useGetVideoFeed.ts new file mode 100644 index 0000000..bedbaeb --- /dev/null +++ b/src/features/cameras/hooks/useGetVideoFeed.ts @@ -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 }; +}; diff --git a/src/features/cameras/hooks/useGetvideoSnapshots.ts b/src/features/cameras/hooks/useGetvideoSnapshots.ts new file mode 100644 index 0000000..68a6946 --- /dev/null +++ b/src/features/cameras/hooks/useGetvideoSnapshots.ts @@ -0,0 +1,25 @@ +import { useEffect, useRef } from "react"; +import { useGetVideoFeed } from "./useGetVideoFeed"; + +export const useCreateVideoSnapshot = () => { + const latestBitmapRef = useRef(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 }; +}; diff --git a/src/routes/baywatch.tsx b/src/routes/baywatch.tsx index 8ec01dd..9271b97 100644 --- a/src/routes/baywatch.tsx +++ b/src/routes/baywatch.tsx @@ -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, -}) +}); function RouteComponent() { - return
Hello "/baywatch"!
+ return ( +
+

Cameras

+ +
+ ); } diff --git a/src/ui/Header.tsx b/src/ui/Header.tsx index c1934dd..31d2d2a 100644 --- a/src/ui/Header.tsx +++ b/src/ui/Header.tsx @@ -18,7 +18,7 @@ const Header = () => { - Baywatch + Cameras Output diff --git a/yarn.lock b/yarn.lock index f9e9fcd..2c31fb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1011,6 +1011,16 @@ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" 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": version "19.2.6" 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" 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: version "2.6.1" resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" @@ -1775,6 +1792,11 @@ keyv@^4.5.4: dependencies: 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: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -2044,6 +2066,23 @@ react-dom@^19.2.0: dependencies: 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: version "0.18.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062" @@ -2125,7 +2164,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -scheduler@^0.27.0: +scheduler@0.27.0, scheduler@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==