diff --git a/SortVision/src/algorithms/bubbleSort.ts b/SortVision/src/algorithms/bubbleSort.ts index abc98f28..80b78cfc 100644 --- a/SortVision/src/algorithms/bubbleSort.ts +++ b/SortVision/src/algorithms/bubbleSort.ts @@ -8,9 +8,14 @@ export const bubbleSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; let swaps = 0; let comparisons = 0; const arr = [...array]; diff --git a/SortVision/src/algorithms/bucketSort.ts b/SortVision/src/algorithms/bucketSort.ts index 0d685a39..e34b32a4 100644 --- a/SortVision/src/algorithms/bucketSort.ts +++ b/SortVision/src/algorithms/bucketSort.ts @@ -46,9 +46,14 @@ export const bucketSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; const metrics: SortStepMetrics = { swaps: 0, comparisons: 0 }; if (array.length === 0) { diff --git a/SortVision/src/algorithms/heapSort.ts b/SortVision/src/algorithms/heapSort.ts index 71b6d432..dc644ad2 100644 --- a/SortVision/src/algorithms/heapSort.ts +++ b/SortVision/src/algorithms/heapSort.ts @@ -82,9 +82,14 @@ export const heapSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; const metrics: SortStepMetrics = { swaps: 0, comparisons: 0 }; const arr = [...array]; const n = arr.length; diff --git a/SortVision/src/algorithms/insertionSort.ts b/SortVision/src/algorithms/insertionSort.ts index a560c846..031cf4aa 100644 --- a/SortVision/src/algorithms/insertionSort.ts +++ b/SortVision/src/algorithms/insertionSort.ts @@ -8,9 +8,14 @@ export const insertionSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; let swaps = 0; let comparisons = 0; const arr = [...array]; diff --git a/SortVision/src/algorithms/mergeSort.ts b/SortVision/src/algorithms/mergeSort.ts index efa9bce8..ad9fc842 100644 --- a/SortVision/src/algorithms/mergeSort.ts +++ b/SortVision/src/algorithms/mergeSort.ts @@ -141,9 +141,14 @@ export const mergeSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; const metrics: SortStepMetrics = { swaps: 0, comparisons: 0 }; const arr = [...array]; diff --git a/SortVision/src/algorithms/quickSort.ts b/SortVision/src/algorithms/quickSort.ts index dd25c266..53eafd80 100644 --- a/SortVision/src/algorithms/quickSort.ts +++ b/SortVision/src/algorithms/quickSort.ts @@ -114,9 +114,14 @@ export const quickSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; const metrics: SortStepMetrics = { swaps: 0, comparisons: 0 }; const arr = [...array]; diff --git a/SortVision/src/algorithms/radixSort.ts b/SortVision/src/algorithms/radixSort.ts index 6f496d95..e4077aeb 100644 --- a/SortVision/src/algorithms/radixSort.ts +++ b/SortVision/src/algorithms/radixSort.ts @@ -71,9 +71,14 @@ export const radixSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; const metrics: SortStepMetrics = { swaps: 0, comparisons: 0 }; if (array.length === 0) { diff --git a/SortVision/src/algorithms/selectionSort.ts b/SortVision/src/algorithms/selectionSort.ts index 15bf4a2d..05d7bf5e 100644 --- a/SortVision/src/algorithms/selectionSort.ts +++ b/SortVision/src/algorithms/selectionSort.ts @@ -8,9 +8,14 @@ export const selectionSort: SortingAlgorithm = async ( setCurrentBar, shouldStopRef, sortPausedRef, - audio + audio, + delayRef ) => { - const delayRefs: SortStepDelayRefs = { shouldStopRef, sortPausedRef }; + const delayRefs: SortStepDelayRefs = { + shouldStopRef, + sortPausedRef, + delayRef, + }; let swaps = 0; let comparisons = 0; const arr = [...array]; diff --git a/SortVision/src/algorithms/sleep.test.ts b/SortVision/src/algorithms/sleep.test.ts new file mode 100644 index 00000000..4072cfd2 --- /dev/null +++ b/SortVision/src/algorithms/sleep.test.ts @@ -0,0 +1,27 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { delayStep } from '@/algorithms/sleep'; +import type { SortStepDelayRefs } from '@/algorithms/types'; + +const createDelayRefs = (delay: number): SortStepDelayRefs => ({ + shouldStopRef: { current: false }, + sortPausedRef: { current: false }, + delayRef: { current: delay }, +}); + +describe('delayStep', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('uses updated delayRef values while waiting', async () => { + vi.useFakeTimers(); + const refs = createDelayRefs(1000); + const wait = delayStep(1000, refs); + + await vi.advanceTimersByTimeAsync(40); + refs.delayRef!.current = 10; + await vi.advanceTimersByTimeAsync(32); + + await expect(wait).resolves.toBeUndefined(); + }); +}); diff --git a/SortVision/src/algorithms/sleep.ts b/SortVision/src/algorithms/sleep.ts index 292d23e3..5b32e349 100644 --- a/SortVision/src/algorithms/sleep.ts +++ b/SortVision/src/algorithms/sleep.ts @@ -7,7 +7,7 @@ const POLL_MS = 32; * Honors {@link SortStepDelayRefs.shouldStopRef} (abort) and {@link SortStepDelayRefs.sortPausedRef} (pause). */ export function delayStep(ms: number, refs: SortStepDelayRefs): Promise { - const deadline = performance.now() + ms; + const start = performance.now(); return new Promise(resolve => { const step = () => { if (refs.shouldStopRef.current) { @@ -19,11 +19,13 @@ export function delayStep(ms: number, refs: SortStepDelayRefs): Promise { return; } const now = performance.now(); - if (now >= deadline) { + const currentDelay = refs.delayRef?.current ?? ms; + const elapsed = now - start; + if (elapsed >= currentDelay) { resolve(); return; } - setTimeout(step, Math.min(POLL_MS, deadline - now)); + setTimeout(step, Math.min(POLL_MS, currentDelay - elapsed)); }; step(); }); diff --git a/SortVision/src/algorithms/types.ts b/SortVision/src/algorithms/types.ts index 6d701057..14cf674c 100644 --- a/SortVision/src/algorithms/types.ts +++ b/SortVision/src/algorithms/types.ts @@ -34,10 +34,12 @@ export type ShouldStopRef = MutableRefObject; /** When true, {@link delayStep} blocks until cleared (pause without aborting). */ export type SortPausedRef = MutableRefObject; +export type SortDelayRef = MutableRefObject; export type SortStepDelayRefs = { shouldStopRef: ShouldStopRef; sortPausedRef: SortPausedRef; + delayRef?: SortDelayRef; }; /** @@ -50,5 +52,6 @@ export type SortingAlgorithm = ( setCurrentBar: Dispatch>, shouldStopRef: ShouldStopRef, sortPausedRef: SortPausedRef, - audio: SortingAlgorithmAudio + audio: SortingAlgorithmAudio, + delayRef?: SortDelayRef ) => Promise; diff --git a/SortVision/src/components/panels/ConfigPanel/ConfigPanel.tsx b/SortVision/src/components/panels/ConfigPanel/ConfigPanel.tsx index 60a1cde1..850a2b57 100644 --- a/SortVision/src/components/panels/ConfigPanel/ConfigPanel.tsx +++ b/SortVision/src/components/panels/ConfigPanel/ConfigPanel.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { ArrayVisualization } from '../../sortingVisualizer/components/ArrayVisualization'; +import StepExplanationPanel from '../../sortingVisualizer/components/StepExplanationPanel'; import { AlgorithmSelector, ComplexityInfo, @@ -73,16 +74,27 @@ const ConfigPanel = ({ {/* Add the array visualization */} {array && ( - +
+ + +
)} ); diff --git a/SortVision/src/components/panels/config/SpeedControl.tsx b/SortVision/src/components/panels/config/SpeedControl.tsx index f4601d76..5c4840b6 100644 --- a/SortVision/src/components/panels/config/SpeedControl.tsx +++ b/SortVision/src/components/panels/config/SpeedControl.tsx @@ -10,12 +10,7 @@ import { normalizedSpeedRatio, } from './visualizerControlConstants'; -const SpeedControl = ({ - speed, - setSpeed, - isSorting, - audio, -}: SpeedControlProps) => { +const SpeedControl = ({ speed, setSpeed, audio }: SpeedControlProps) => { const { t } = useLanguage(); return ( @@ -102,7 +97,6 @@ const SpeedControl = ({ max={SPEED_MS_MAX} step={SPEED_MS_SLIDER_STEP} onValueChange={value => setSpeed(value[0]!)} - disabled={isSorting} className="relative z-10" name="animation speed" aria-label="Animation Speed Slider" @@ -154,13 +148,13 @@ const SpeedControl = ({