From 0e49d68c34c3dddf620def8021d9901cb4a03191 Mon Sep 17 00:00:00 2001 From: Andy Sweet Date: Wed, 13 Mar 2024 14:05:10 -0700 Subject: [PATCH] Use defaultValue for Zarr URL to allow more edits (#50) This uses `defaultValue` instead of `value` in the JSX component definition, to allow more flexible editing on the Zarr URL. This also changes the `dataUrl` state from a `URL` to a `string` to simplify passing it around and validating it. Closes #46 --------- Co-authored-by: Ashley Anderson --- src/ViewerState.ts | 9 ++++----- src/scene.tsx | 29 +++++++++++++++++------------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/ViewerState.ts b/src/ViewerState.ts index 00dac67c..b309ecf9 100644 --- a/src/ViewerState.ts +++ b/src/ViewerState.ts @@ -1,9 +1,8 @@ import { Vector3 } from "three"; -export const DEFAULT_ZARR_URL = new URL( +export const DEFAULT_ZARR_URL = "https://sci-imaging-vis-public-demo-data.s3.us-west-2.amazonaws.com" + - "/points-web-viewer/sparse-zarr-v2/ZSNS001_tracks_bundle.zarr", -); + "/points-web-viewer/sparse-zarr-v2/ZSNS001_tracks_bundle.zarr"; const HASH_KEY = "viewerState"; @@ -17,13 +16,13 @@ export function clearUrlHash() { // Encapsulates all the persistent state in the viewer (e.g. that can be serialized and shared). export class ViewerState { - dataUrl: URL; + dataUrl: string; curTime: number; cameraPosition: Vector3; cameraTarget: Vector3; constructor( - dataUrl: URL = DEFAULT_ZARR_URL, + dataUrl: string = DEFAULT_ZARR_URL, curTime: number = 0, // Default position and target from interacting with ZSNS001. cameraPosition: Vector3 = new Vector3(500, 500, -1250), diff --git a/src/scene.tsx b/src/scene.tsx index d3aca112..85806d57 100644 --- a/src/scene.tsx +++ b/src/scene.tsx @@ -34,7 +34,7 @@ export default function Scene(props: SceneProps) { const [playing, setPlaying] = useState(false); // Other state that is not or does not need to be persisted. - const [trackManager, setTrackManager] = useState(); + const [trackManager, setTrackManager] = useState(null); const [numTimes, setNumTimes] = useState(0); const [trackHighlightLength, setTrackHighlightLength] = useState(11); const [loading, setLoading] = useState(false); @@ -120,15 +120,14 @@ export default function Scene(props: SceneProps) { // update the array when the dataUrl changes useEffect(() => { console.log("load data from %s", dataUrl); - const trackManager = loadTrackManager(dataUrl.toString()); + const trackManager = loadTrackManager(dataUrl); // TODO: add clean-up by returning another closure trackManager.then((tm: TrackManager | null) => { - if (!tm) return; setTrackManager(tm); - setNumTimes(tm.points.shape[0]); + setNumTimes(tm?.points.shape[0] || numTimes); // Defend against the case when a curTime valid for previous data // is no longer valid. - setCurTime(Math.min(curTime, tm.points.shape[0] - 1)); + setCurTime(Math.min(curTime, tm?.points.shape[0] - 1 || numTimes - 1)); }); }, [dataUrl]); @@ -187,6 +186,12 @@ export default function Scene(props: SceneProps) { setLoading(false); console.debug("IGNORE FETCH points at time %d", curTime); } + + // stop playback if there is no data + if (!trackManager) { + setPlaying(false); + } + return () => { clearTimeout(loadingTimer); ignore = true; @@ -219,16 +224,16 @@ export default function Scene(props: SceneProps) { setDataUrl(new URL(e.target.value))} + placeholder={initialViewerState.dataUrl} + defaultValue={initialViewerState.dataUrl} + onChange={(e) => setDataUrl(e.target.value)} fullWidth={true} intent={trackManager ? "default" : "error"} /> { setAutoRotate((e.target as HTMLInputElement).checked); }} @@ -261,13 +266,13 @@ export default function Scene(props: SceneProps) { onLabel="Play" offLabel="Play" checked={playing} - disabled={trackManager === undefined} + disabled={!trackManager} onChange={(e) => { setPlaying((e.target as HTMLInputElement).checked); }} />