mihirmithani
Add Kernel Convolution Visualizer - Phase 1 complete
80bcac8
Raw History Blame
5.65 kB
'use client'
import { useAppStore } from '@/store'
import { useConvolution } from '@/hooks/useConvolution'
import { Button, Slider, Card, CardContent } from '@/components/ui'
import { cn } from '@/utils/helpers'
import { Play, Pause, RotateCcw, StepForward, ZoomIn, ZoomOut, Maximize } from 'lucide-react'
export function PlaybackControls() {
const {
convolution,
settings,
startConvolution,
pauseConvolution,
resumeConvolution,
stopConvolution,
updateConvolutionStep,
updateConvolutionProgress,
completeConvolution,
setConvolutionError,
resetConvolution,
setAnimationSpeed,
setZoomLevel,
setPanOffset,
toggleOverlay,
setPadding,
setComparisonMode,
setStepMode,
} = useAppStore()
const { runConvolution, pause, resume, stop, step, reset, isRunning, isPaused, canRun, toggleStepMode } = useConvolution()
const { status, progress, elapsedTime } = convolution
const { animationSpeed, zoomLevel, stepMode } = settings
const isCompleted = status === 'completed'
const isActive = isRunning || isPaused || isCompleted
const formatTime = (ms: number) => {
if (ms < 1000) return `${ms}ms`
return `${(ms / 1000).toFixed(1)}s`
}
return (
<Card className="w-full">
<CardContent className="p-4 space-y-4">
{/* Main Controls */}
<div className="flex items-center gap-2 flex-wrap">
<Button
variant={isRunning ? 'secondary' : 'primary'}
size="lg"
onClick={isRunning ? pause : runConvolution}
disabled={!canRun}
className="min-w-[100px]"
>
{isRunning ? (
<>
<Pause className="h-4 w-4" />
Pause
</>
) : (
<>
<Play className="h-4 w-4" />
{isPaused ? 'Resume' : 'Play'}
</>
)}
</Button>
<Button variant="outline" size="lg" onClick={step} disabled={isRunning || isCompleted}>
<StepForward className="h-4 w-4" />
Step
</Button>
<Button variant="outline" size="lg" onClick={stop} disabled={status === 'idle'}>
<RotateCcw className="h-4 w-4" />
Stop
</Button>
<Button variant="outline" size="lg" onClick={reset} disabled={status === 'idle'}>
<RotateCcw className="h-4 w-4" />
Reset
</Button>
</div>
{/* Speed Control */}
<div>
<label className="flex items-center justify-between text-sm mb-1">
<span>Animation Speed</span>
<span className="font-mono text-blue-600">{animationSpeed.toFixed(1)}x</span>
</label>
<Slider
min={0.1}
max={10}
step={0.1}
value={animationSpeed}
onValueChange={setAnimationSpeed}
showValue
valueFormat={(v: number) => `${v.toFixed(1)}x`}
/>
</div>
{/* Zoom Controls */}
<div>
<label className="flex items-center justify-between text-sm mb-1">
<span>Zoom</span>
<span className="font-mono text-blue-600">{Math.round(zoomLevel * 100)}%</span>
</label>
<Slider
min={0.1}
max={20}
step={0.1}
value={zoomLevel}
onValueChange={setZoomLevel}
showValue
valueFormat={(v: number) => `${Math.round(v * 100)}%`}
/>
<div className="flex gap-2 mt-2">
<Button variant="outline" size="sm" onClick={() => setZoomLevel(Math.min(20, zoomLevel * 1.2))}>
<ZoomIn className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm" onClick={() => setZoomLevel(Math.max(0.1, zoomLevel / 1.2))}>
<ZoomOut className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm" onClick={() => { setZoomLevel(1); setPanOffset({ x: 0, y: 0 }) }}>
<Maximize className="h-4 w-4" />
Fit
</Button>
</div>
</div>
{/* Progress */}
{isActive && (
<div className="space-y-2 pt-2 border-t dark:border-gray-700">
<div className="flex items-center justify-between text-sm">
<span>Progress</span>
<span className="font-mono">{Math.round(progress * 100)}%</span>
</div>
<div className="h-2 bg-gray-200 rounded-full overflow-hidden dark:bg-gray-700">
<div
className="h-full bg-blue-600 transition-all duration-300"
style={{ width: `${progress * 100}%` }}
/>
</div>
<div className="flex justify-between text-xs text-gray-500">
<span>Elapsed: {formatTime(elapsedTime)}</span>
<span>{status === 'running' ? 'Running' : status === 'paused' ? 'Paused' : 'Completed'}</span>
</div>
</div>
)}
{/* Step Mode Toggle */}
<div className="flex items-center justify-between pt-2 border-t dark:border-gray-700">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={stepMode}
onChange={(e) => toggleStepMode(e.target.checked)}
className="rounded border-gray-300"
/>
<span className="text-sm">Step Mode (manual advance)</span>
</label>
</div>
</CardContent>
</Card>
)
}