Skip to content

Commit 2718930

Browse files
committed
Add buttons to scroll and rotate the images
1 parent d9d9bd2 commit 2718930

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

src/pages/PathwaysNiivueCanvas.jsx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,49 @@ export const PathwaysNiivueCanvas = () => (
402402
setIsCrosshair(crosshairState);
403403
};
404404

405+
// Animate, looping continuously through the coronal slices
406+
const [isPlaying, setIsPlaying] = useState(false);
407+
const playRafRef = React.useRef(null);
408+
const playLastTimeRef = React.useRef(0);
409+
const SLICES_PER_SECOND = 12;
410+
411+
const animateCoronalSlice = (now) => {
412+
const nv = niivue_slice.current;
413+
if (!nv || !nv.volumes.length) {
414+
playRafRef.current = requestAnimationFrame(animateCoronalSlice);
415+
return;
416+
}
417+
if (!playLastTimeRef.current) playLastTimeRef.current = now;
418+
const elapsed = (now - playLastTimeRef.current) / 1000; // seconds
419+
playLastTimeRef.current = now;
420+
421+
const numSlices = nv.volumes[0].dims[2];
422+
// Advance the crosshair fraction by the elapsed time, looping at the volume edge
423+
let y = nv.scene.crosshairPos[1] + (SLICES_PER_SECOND / numSlices) * elapsed;
424+
y -= Math.floor(y); // wrap into [0, 1) to loop continuously
425+
nv.scene.crosshairPos[1] = y;
426+
nv.drawScene();
427+
428+
playRafRef.current = requestAnimationFrame(animateCoronalSlice);
429+
};
430+
431+
const handlePlayToggle = () => {
432+
if (isPlaying) {
433+
cancelAnimationFrame(playRafRef.current);
434+
playRafRef.current = null;
435+
setIsPlaying(false);
436+
} else {
437+
playLastTimeRef.current = 0;
438+
playRafRef.current = requestAnimationFrame(animateCoronalSlice);
439+
setIsPlaying(true);
440+
}
441+
};
442+
443+
// Stop the animation when the component unmounts
444+
React.useEffect(() => () => {
445+
if (playRafRef.current) cancelAnimationFrame(playRafRef.current);
446+
}, []);
447+
405448
const [isOrientCube, setIsOrientCube] = useState(true);
406449

407450
const handleOrientCubeChange = (event) => {
@@ -411,6 +454,34 @@ export const PathwaysNiivueCanvas = () => (
411454
setIsOrientCube(orientCubeState);
412455
};
413456

457+
// Continuously rotate the 3D render view
458+
const [isRotating, setIsRotating] = useState(false);
459+
const rotateIntervalRef = React.useRef(null);
460+
461+
const stepRotation = () => {
462+
const nv = niivue_render.current;
463+
if (!nv) return;
464+
// Increment the azimuth to spin the camera around the volume, wrapping at 360
465+
const azimuth = (nv.scene.renderAzimuth + 2) % 360;
466+
nv.setRenderAzimuthElevation(azimuth, nv.scene.renderElevation);
467+
};
468+
469+
const handleRotateToggle = () => {
470+
if (isRotating) {
471+
clearInterval(rotateIntervalRef.current);
472+
rotateIntervalRef.current = null;
473+
setIsRotating(false);
474+
} else {
475+
rotateIntervalRef.current = setInterval(stepRotation, 50);
476+
setIsRotating(true);
477+
}
478+
};
479+
480+
// Stop the rotation when the component unmounts
481+
React.useEffect(() => () => {
482+
if (rotateIntervalRef.current) clearInterval(rotateIntervalRef.current);
483+
}, []);
484+
414485
const [tractVisibility, setTractVisibility] = useState(
415486
trackList.map(() => true)
416487
);
@@ -495,6 +566,15 @@ export const PathwaysNiivueCanvas = () => (
495566
Crosshair
496567
</label>
497568
</div>
569+
<div style={{ marginTop: "8px" }}>
570+
<button
571+
type="button"
572+
className="button button--sm button--primary"
573+
onClick={handlePlayToggle}
574+
>
575+
{isPlaying ? "⏸ Pause slices" : "▶ Play slices"}
576+
</button>
577+
</div>
498578

499579
<hr />
500580
<h4>3D View</h4>
@@ -509,6 +589,15 @@ export const PathwaysNiivueCanvas = () => (
509589
Orientation cube
510590
</label>
511591
</div>
592+
<div style={{ marginTop: "8px" }}>
593+
<button
594+
type="button"
595+
className="button button--sm button--primary"
596+
onClick={handleRotateToggle}
597+
>
598+
{isRotating ? "⏸ Pause rotation" : "▶ Rotate view"}
599+
</button>
600+
</div>
512601

513602
<hr />
514603
<h4>Nuclei</h4>

0 commit comments

Comments
 (0)