Source: pages/[space]/[direction]/[walk].js

import { useRouter } from 'next/router'
import React, { useEffect, useRef, useState } from "react";
import AppBar from "@mui/material/AppBar";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import PersonSearchIcon from "@mui/icons-material/PersonSearch";
import Copyright from '@component/components/copyright';

import { Card, CardContent, CardMedia, Slider } from "@mui/material";
import { RadarChartDisplay } from "@component/components/radarChartDisplay";
import VideoCard from '@component/components/video';
import useWalk from '@component/stores/walk';

/**
 * Generate an array of equally spaced numbers.
 * @param {number} start - The start value.
 * @param {number} end - The end value.
 * @param {number} n - The number of values.
 * @returns {number[]} The array of equally spaced numbers.
 */
function linspace(start, end, n) {
    const diff = end - start;
    const step = diff / (n - 1);
    return Array.from({ length: n }, (_, i) => start + i * step);
}

export async function getStaticPaths() {
    // Here you would fetch all walk IDs and their directions you want to pre-render.
    // For the sake of example, let's assume that you can get them from an API.


    // const paths = walks.map((walk) => ({
    //     params: { walkId: walk.id.toString(), direction: walk.direction },
    // }))

    return { paths: [], fallback: true }
}

export async function getStaticProps({ params }) {
    // Fetch necessary data for the walk using params.walkId and params.direction
    const space = params.space
    const direction = params.direction
    const walk = params.walk

    return { props: { direction: direction, walk: walk, space: space } }
}

const ImageStripe = ({ videos, path }) => {
    return (
        <Box sx={{ display: 'flex', flexDirection: 'row', width: '100%', overflow: 'hidden', paddingTop: '26px', paddingLeft: '26px', paddingRight: '26px' }}>
            {
                videos.map((video) =>
                    <Box key={video.i} sx={{ flexBasis: '10%', flexGrow: 1, maxWidth: '10%' }}>
                        <CardMedia
                            component="video"
                            src={path}
                            ref={video.ref}
                            title="green iguana"
                            sx={{ width: '100%' }}
                        />
                    </Box>
                )
            }
        </Box>
    );
}

/**
 * Walk component for detailed exploration of a single walk.
 * Displays the walk visualization.
 * @param {Object} props - The component props.
 * @param {string} props.space - The space value (either z (latent) or w (style) space).
 * @param {string} props.direction - The direction value.
 * @param {string} props.walk - The walk index.
 * @returns {JSX.Element} Walk component.
 */
const Walk = ({ space, direction, walk }) => {
    const router = useRouter();
    const setSpaceDirectionWalk = useWalk(state => state.setSpaceDirectionWalk);

    const start = useWalk(state => state.start);
    const current = useWalk(state => state.current);
    const end = useWalk(state => state.end);
    const setStart = useWalk(state => state.setStart);
    const setCurrent = useWalk(state=>state.setCurrent);
    const setEnd = useWalk(state => state.setEnd);

    const videos = linspace(0, 1, 10).map((i) => {
        return {
            ref: useRef(null),
            i: i
        }
    });

    /**
     * Update the video players' current time based on the slider values.
     */
    useEffect(() => {
        const startSec = start / 20;
        const endSec = end / 20;
        const delta = endSec - startSec;
        videos.forEach(videoData => {
            let video = videoData.ref.current;
            if (video) {
                const newTime = startSec + delta * videoData.i;
                video.currentTime = newTime;
            }
        });
    }, [start, end, ...videos.map(video => video.ref)]);

    const minDistance = 1;

    /**
     * Handle the slider change event to select start and end of walk.
     * @param {Object} event - The event object.
     * @param {number[]} newValue - The new slider values.
     * @param {number} activeThumb - The active thumb index.
     */
    const handleSlider = (event, newValue, activeThumb) => {
        if (!Array.isArray(newValue)) {
            return;
        }
        if (activeThumb === 0) {
            setStart(Math.min(newValue[0], end - minDistance));
        } else if (activeThumb == 1) {
            const val = Math.max(
                Math.min(newValue[1], end - minDistance),
                start + minDistance
            );
            setCurrent(val);
        } else {
            setEnd(Math.max(newValue[2], start + minDistance));
        }
    };

    useEffect(() => {
        if (space && direction && walk) {
            setSpaceDirectionWalk(space, direction, walk);
        }
    }, [space, direction, walk]);

    if (router.isFallback) {
        // This will be displayed while waiting for getStaticProps to finish
        return <div>Loading...</div>
    }


    const path = `/videos/${space}/${direction}/${walk}.mp4`
    return (
        <>
            <AppBar position="relative">
                <Toolbar>
                    <PersonSearchIcon sx={{ mr: 2 }} />
                    <Typography variant="h6" color="inherit" noWrap>
                        VISUALIZATION - WALK
                    </Typography>
                </Toolbar>
            </AppBar>
            <main>
                <Container sx={{ py: 4 }} maxWidth="lg">
                    <Grid container spacing={2} alignItems={"stretch"}>
                        <Grid item xs={12} sm={12} md={12}>
                            <Card
                                sx={{
                                    height: "100%",
                                    display: "flex",
                                    flexDirection: "column",
                                }}
                            >
                                <ImageStripe videos={videos} path={path} />
                                <CardContent>
                                    <Slider
                                        value={[start, current, end]}
                                        onChange={handleSlider}
                                        valueLabelDisplay="auto"
                                        step={1}
                                        marks
                                        min={0}
                                        max={99}
                                    />
                                </CardContent>
                            </Card>
                        </Grid>
                        <Grid item xs={12} sm={12} md={6}>
                            <VideoCard path={`/videos/${space}/${direction}/${walk}.mp4`} />
                        </Grid>
                        <Grid item xs={12} sm={12} md={6}>
                            <RadarChartDisplay direction={{ value: direction }} walk={walk} />
                        </Grid>
                    </Grid>
                </Container>
            </main>
            {/* Footer */}
            <Box sx={{ bgcolor: "background.paper", p: 6 }} component="footer">
                <Typography variant="h6" align="center" gutterBottom>
                    Footer
                </Typography>
                <Typography
                    variant="subtitle1"
                    align="center"
                    color="text.secondary"
                    component="p"
                >
                    Visualization 2 - Implementation of "Interactively Assessing Disentanglement in GANs"
                </Typography>
                <Copyright />
            </Box>
        </>
    );
}

export default Walk