Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 37 additions & 23 deletions footsteps-web/app/interpolation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import { useState } from 'react';
export default function InterpolationDemo() {
const [enableInterpolation, setEnableInterpolation] = useState(true);
const [isDragging, setIsDragging] = useState(false);
const {
year,

const {
year,
targetYear,
sliderValue,
sliderValue,
updateYear,
updateSlider,
updateSliderContinuous,
formattedYear,
Expand Down Expand Up @@ -53,28 +54,35 @@ export default function InterpolationDemo() {
<main className="relative w-full h-screen overflow-hidden bg-black">
<ErrorBoundary>
{/* Globe visualization with interpolation */}
<FootstepsVizWithInterpolation
year={year}
<FootstepsVizWithInterpolation
year={year}
enableInterpolation={enableInterpolation}
interpolationThreshold={50}
manualInterpolation={
shouldInterpolate
? {
fromYear: interpolationFromYear,
toYear: interpolationToYear,
t: interpolationProgress,
}
: undefined
}
/>
</ErrorBoundary>

{/* Enhanced time control slider */}
<div className="relative">
<TimeSlider
value={sliderValue}
<TimeSlider
value={sliderValue}
onChange={handleSliderChange}
onBeforeChange={handleBeforeChange}
onAfterChange={handleAfterChange}
/>

{/* Current year display - show the animated year */}
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-4 text-center pointer-events-none">
<div className="bg-black bg-opacity-60 px-4 py-2 rounded-lg">
<div className="text-white text-2xl font-bold">
{formattedYear}
</div>
<div className="text-white text-2xl font-bold">{formattedYear}</div>
{isAnimating && (
<div className="text-white text-xs opacity-75 mt-1">
Animating to {targetYear}...
Expand All @@ -83,11 +91,13 @@ export default function InterpolationDemo() {
</div>
</div>
</div>

{/* Controls panel */}
<div className="absolute top-4 left-4 z-10 bg-black bg-opacity-50 text-white p-3 rounded-lg">
<h3 className="text-lg font-bold mb-2">Population Interpolation Demo</h3>

<h3 className="text-lg font-bold mb-2">
Population Interpolation Demo
</h3>

<label className="flex items-center mb-2 cursor-pointer">
<input
type="checkbox"
Expand All @@ -97,34 +107,38 @@ export default function InterpolationDemo() {
/>
Enable Interpolation
</label>

<div className="text-xs text-gray-300 space-y-1">
<div>Current Year: {year}</div>
<div>Target Year: {targetYear}</div>
<div>Animating: {isAnimating ? 'Yes' : 'No'}</div>
{shouldInterpolate && (
<>
<div>Interpolation: {interpolationFromYear} → {interpolationToYear}</div>
<div>
Interpolation: {interpolationFromYear} → {interpolationToYear}
</div>
<div>Progress: {Math.round(interpolationProgress * 100)}%</div>
</>
)}
<div>Dragging: {isDragging ? 'Yes' : 'No'}</div>
</div>

<div className="mt-3 pt-2 border-t border-gray-600 text-xs text-gray-400">
<p><strong>Instructions:</strong></p>
<p>
<strong>Instructions:</strong>
</p>
<p>• Click on distant years to see smooth interpolation</p>
<p>• Drag slider for immediate scrubbing</p>
<p>• Toggle interpolation on/off to compare</p>
</div>
</div>

{/* Quick jump buttons for testing */}
<div className="absolute bottom-20 right-4 z-10 flex flex-col gap-2">
{[-10000, -5000, -1000, 0, 500, 1000].map((testYear) => (
<button
key={testYear}
onClick={() => updateSlider(testYear)}
onClick={() => updateYear(testYear)}
className="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-sm"
disabled={isAnimating}
>
Expand All @@ -134,4 +148,4 @@ export default function InterpolationDemo() {
</div>
</main>
);
}
}
79 changes: 60 additions & 19 deletions footsteps-web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,74 @@
'use client';

import { useYear } from '../lib/useYear';
import FootstepsViz from '../components/footsteps/FootstepsViz';
import { useState } from 'react';
import { useYearWithInterpolation } from '../lib/useYearWithInterpolation';
import FootstepsVizWithInterpolation from '../components/footsteps/FootstepsVizWithInterpolation';
import TimeSlider from '../components/ui/TimeSlider';
import { ErrorBoundary } from '../components/common/ErrorBoundary';

export default function Home() {
const { year, sliderValue, updateSlider } = useYear(-1000); // Start at 1000 BC (we know this data exists)

const [isDragging, setIsDragging] = useState(false);

const {
year,
sliderValue,
updateSlider,
updateSliderContinuous,
stopAnimation,
shouldInterpolate,
interpolationFromYear,
interpolationToYear,
interpolationProgress,
} = useYearWithInterpolation(-1000, {
enableInterpolation: true,
interpolationThreshold: 50,
autoAnimateThreshold: 200,
animationDurationMs: 2000,
});

const handleSliderChange = (value: number) => {
if (isDragging) {
updateSliderContinuous(value);
} else {
updateSlider(value);
}
};

const handleBeforeChange = () => {
setIsDragging(true);
stopAnimation();
};

const handleAfterChange = () => {
setIsDragging(false);
};

return (
<main className="relative w-full h-screen overflow-hidden bg-black">
<ErrorBoundary>
{/* Globe visualization - now with React 18 compatibility */}
<FootstepsViz year={year} />
{/* Globe visualization */}
<FootstepsVizWithInterpolation
year={year}
enableInterpolation={true}
manualInterpolation={
shouldInterpolate
? {
fromYear: interpolationFromYear,
toYear: interpolationToYear,
t: interpolationProgress,
}
: undefined
}
/>
</ErrorBoundary>

{/* Time control slider */}
<TimeSlider
value={sliderValue}
onChange={updateSlider}
<TimeSlider
value={sliderValue}
onChange={handleSliderChange}
onBeforeChange={handleBeforeChange}
onAfterChange={handleAfterChange}
/>

{/* Branding indicator - only show when not loading */}
{/* <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 pointer-events-none">
<div className="text-white/50 text-center">
<div className="text-2xl font-bold mb-2">Globe of Humans</div>
<div className="text-sm">React 18 Compatible</div>
</div>
</div> */}
</main>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,35 @@ import { type ColorScheme } from '@/components/footsteps/layers/color';
import { radiusStrategies } from '@/components/footsteps/layers/radiusStrategies';

const DEBUG = (process.env.NEXT_PUBLIC_DEBUG_LOGS || 'false') === 'true';
const ENABLE_PLAIN_BASEMAP = (process.env.NEXT_PUBLIC_ENABLE_PLAIN_BASEMAP || 'true') === 'true';
const ENABLE_PLAIN_BASEMAP =
(process.env.NEXT_PUBLIC_ENABLE_PLAIN_BASEMAP || 'true') === 'true';

interface FootstepsVizWithInterpolationProps {
year: number;
enableInterpolation?: boolean;
interpolationThreshold?: number;
manualInterpolation?: {
fromYear: number;
toYear: number;
t: number;
};
}

const DEFAULT_COLOURSCHEME = 'cyan';

function FootstepsVizWithInterpolation({
year,
function FootstepsVizWithInterpolation({
year,
enableInterpolation = true,
interpolationThreshold = 50 // Only interpolate for year changes > 50 years
interpolationThreshold = 50, // Only interpolate for year changes > 50 years
manualInterpolation,
}: FootstepsVizWithInterpolationProps) {
const [is3DMode, setIs3DMode] = useState(() => getViewMode());
const [showTerrain, setShowTerrain] = useState(() => !ENABLE_PLAIN_BASEMAP);
const [colorScheme, setColorScheme] = useState<ColorScheme>(DEFAULT_COLOURSCHEME);
const [colorScheme, setColorScheme] =
useState<ColorScheme>(DEFAULT_COLOURSCHEME);

const { viewState, onViewStateChange, isZooming, isPanning } = useGlobeViewState();
const { viewState, onViewStateChange, isZooming, isPanning } =
useGlobeViewState();

const [tooltipData, setTooltipData] = useState<{
population: number;
Expand All @@ -52,18 +61,30 @@ function FootstepsVizWithInterpolation({
} | null>(null);

// Interpolation state management
const {
const interpolationState = useInterpolation(year, {
animationDurationMs: 2000,
interpolationThreshold,
});

let {
isInterpolating,
fromYear,
toYear,
interpolationT,
currentDisplayYear,
startInterpolation,
stopInterpolation,
} = useInterpolation(year, {
animationDurationMs: 2000,
interpolationThreshold,
});
} = interpolationState;

if (manualInterpolation) {
isInterpolating = true;
fromYear = manualInterpolation.fromYear;
toYear = manualInterpolation.toYear;
interpolationT = manualInterpolation.t;
currentDisplayYear = Math.round(
manualInterpolation.fromYear +
(manualInterpolation.toYear - manualInterpolation.fromYear) *
manualInterpolation.t,
);
}

// Use year crossfade hook for non-interpolated transitions
const { previousYear, currentOpacity, previousOpacity, newLayerHasTileRef } =
Expand Down Expand Up @@ -148,7 +169,9 @@ function FootstepsVizWithInterpolation({
toYear,
t: interpolationT,
viewState: layerViewState,
radiusStrategy: is3DMode ? radiusStrategies.globe3D : radiusStrategies.zoomAdaptive,
radiusStrategy: is3DMode
? radiusStrategies.globe3D
: radiusStrategies.zoomAdaptive,
colorScheme,
opacity: 1.0,
onTileLoad: () => {
Expand Down Expand Up @@ -196,19 +219,20 @@ function FootstepsVizWithInterpolation({
true,
);

const previousYearLayer = previousYear !== null
? createHumanLayerForYear(
previousYear as number,
stableLODLevel,
previousOpacity,
`human-layer-previous-${colorScheme}`,
false,
)
: null;
const previousYearLayer =
previousYear !== null
? createHumanLayerForYear(
previousYear as number,
stableLODLevel,
previousOpacity,
`human-layer-previous-${colorScheme}`,
false,
)
: null;

return previousYearLayer
? [...baseLayers, previousYearLayer, currentYearLayer] as LayersList
: [...baseLayers, currentYearLayer] as LayersList;
? ([...baseLayers, previousYearLayer, currentYearLayer] as LayersList)
: ([...baseLayers, currentYearLayer] as LayersList);
}
}, [
backgroundLayers,
Expand Down Expand Up @@ -292,14 +316,16 @@ function FootstepsVizWithInterpolation({
colorScheme={colorScheme}
onColorSchemeChange={setColorScheme}
/>

{/* Interpolation debug info */}
{DEBUG && enableInterpolation && (
<div className="bg-black bg-opacity-50 text-white text-xs p-2 rounded">
<div>Interpolating: {isInterpolating ? 'Yes' : 'No'}</div>
{isInterpolating && (
<>
<div>From: {fromYear} → To: {toYear}</div>
<div>
From: {fromYear} → To: {toYear}
</div>
<div>Progress: {Math.round(interpolationT * 100)}%</div>
<div>Display Year: {currentDisplayYear}</div>
</>
Expand All @@ -317,4 +343,4 @@ function FootstepsVizWithInterpolation({
);
}

export default memo(FootstepsVizWithInterpolation);
export default memo(FootstepsVizWithInterpolation);
Loading
Loading