mihirmithani
Add Kernel Convolution Visualizer - Phase 1 complete
80bcac8
Raw History Blame
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,
}
}