Download src/hooks/useCanvas.ts from TechAvenger/kernel-convolution-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 3.69 kB
-
https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useCanvas.ts
- Command line
-
hf download hf://spaces/TechAvenger/kernel-convolution-visualizer@25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useCanvas.ts
-
curl -L -o useCanvas.ts https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useCanvas.ts
3.69 kB
| import { useCallback, useRef, useEffect } from 'react' | |
| import { useAppStore } from '@/store' | |
| export function useCanvas() { | |
| const canvasRef = useRef<HTMLCanvasElement>(null) | |
| const contextRef = useRef<CanvasRenderingContext2D | null>(null) | |
| useEffect(() => { | |
| if (canvasRef.current) { | |
| contextRef.current = canvasRef.current.getContext('2d', { willReadFrequently: true }) | |
| } | |
| }, []) | |
| const drawImage = useCallback((imageData: ImageData) => { | |
| const ctx = contextRef.current | |
| const canvas = canvasRef.current | |
| if (!ctx || !canvas) return | |
| canvas.width = imageData.width | |
| canvas.height = imageData.height | |
| ctx.putImageData(imageData, 0, 0) | |
| }, []) | |
| const drawImageScaled = useCallback( | |
| (imageData: ImageData, scale: number, offsetX: number, offsetY: number) => { | |
| const ctx = contextRef.current | |
| const canvas = canvasRef.current | |
| if (!ctx || !canvas) return | |
| const displayWidth = Math.round(imageData.width * scale) | |
| const displayHeight = Math.round(imageData.height * scale) | |
| canvas.width = displayWidth | |
| canvas.height = displayHeight | |
| ctx.imageSmoothingEnabled = false | |
| ctx.drawImage( | |
| createImageBitmapFromImageData(imageData), | |
| 0, | |
| 0, | |
| imageData.width, | |
| imageData.height, | |
| offsetX, | |
| offsetY, | |
| displayWidth, | |
| displayHeight | |
| ) | |
| }, | |
| [] | |
| ) | |
| const clear = useCallback(() => { | |
| const ctx = contextRef.current | |
| const canvas = canvasRef.current | |
| if (!ctx || !canvas) return | |
| ctx.clearRect(0, 0, canvas.width, canvas.height) | |
| }, []) | |
| const getCanvas = useCallback(() => canvasRef.current, []) | |
| const getContext = useCallback(() => contextRef.current, []) | |
| return { | |
| canvasRef, | |
| drawImage, | |
| drawImageScaled, | |
| clear, | |
| getCanvas, | |
| getContext, | |
| } | |
| } | |
| function createImageBitmapFromImageData(imageData: ImageData): ImageBitmap { | |
| const canvas = document.createElement('canvas') | |
| canvas.width = imageData.width | |
| canvas.height = imageData.height | |
| const ctx = canvas.getContext('2d') | |
| if (!ctx) throw new Error('Failed to create canvas context') | |
| ctx.putImageData(imageData, 0, 0) | |
| // Note: createImageBitmap is async, this is a synchronous fallback | |
| return canvas as unknown as ImageBitmap | |
| } | |
| export function useZoomPan() { | |
| const { settings, setZoomLevel, setPanOffset } = useAppStore() | |
| const zoomRef = useRef(settings.zoomLevel) | |
| const panRef = useRef(settings.panOffset) | |
| useEffect(() => { | |
| zoomRef.current = settings.zoomLevel | |
| panRef.current = settings.panOffset | |
| }, [settings.zoomLevel, settings.panOffset]) | |
| const zoomIn = useCallback(() => { | |
| const newZoom = Math.min(zoomRef.current * 1.2, 20) | |
| setZoomLevel(newZoom) | |
| }, [setZoomLevel]) | |
| const zoomOut = useCallback(() => { | |
| const newZoom = Math.max(zoomRef.current / 1.2, 0.1) | |
| setZoomLevel(newZoom) | |
| }, [setZoomLevel]) | |
| const resetZoom = useCallback(() => { | |
| setZoomLevel(1) | |
| setPanOffset({ x: 0, y: 0 }) | |
| }, [setZoomLevel, setPanOffset]) | |
| const setZoom = useCallback( | |
| (zoom: number) => { | |
| setZoomLevel(Math.max(0.1, Math.min(20, zoom))) | |
| }, | |
| [setZoomLevel] | |
| ) | |
| const pan = useCallback( | |
| (deltaX: number, deltaY: number) => { | |
| const newPan = { | |
| x: panRef.current.x + deltaX, | |
| y: panRef.current.y + deltaY, | |
| } | |
| setPanOffset(newPan) | |
| }, | |
| [setPanOffset] | |
| ) | |
| const panTo = useCallback( | |
| (x: number, y: number) => { | |
| setPanOffset({ x, y }) | |
| }, | |
| [setPanOffset] | |
| ) | |
| return { | |
| zoom: settings.zoomLevel, | |
| panOffset: settings.panOffset, | |
| zoomIn, | |
| zoomOut, | |
| resetZoom, | |
| setZoom, | |
| pan, | |
| panTo, | |
| } | |
| } |