diff --git a/website/utils/test/url_utils.test.ts b/website/utils/test/url_utils.test.ts index adc8f823..2b588000 100644 --- a/website/utils/test/url_utils.test.ts +++ b/website/utils/test/url_utils.test.ts @@ -489,16 +489,15 @@ describe("Viewer state", () => { expect(serializeViewerState(CUSTOM_VIEWER_STATE, false)).toEqual(SERIALIZED_CUSTOM_VIEWER_STATE); }); - it("deserializes partial camera settings", () => { - const state: Partial = { - cameraState: { - position: [1.0, -1.4, 45], - up: [0, 1, 0], - fov: 43.5, - }, + it("shortens long numbers in the slice and region parameters", () => { + // Floats should be rounded to 5 significant digits or less + let state: Partial = { + region: { x: [0.4566666666, 0.8667332], y: [0.49999999, 0.8999999], z: [0.3000000001, 0.16467883] }, + slice: { x: 0.41111186, y: 0.49999999, z: 0.677402 }, }; - const serializedState = "pos:1:-1.4:45,up:0:1:0,fov:43.5"; - expect(deserializeViewerState({ cam: serializedState })).toEqual(state); + let serializedState = serializeViewerState(state, true); + expect(serializedState.reg).toEqual("0.45667:0.86673,0.5:0.9,0.3:0.16468"); + expect(serializedState.slice).toEqual("0.41111,0.5,0.6774"); }); }); @@ -532,6 +531,18 @@ describe("Viewer state", () => { expect(deserializeViewerState(serializeViewerState(state, false)).renderMode).toEqual(renderMode); } }); + + it("deserializes partial camera settings", () => { + const state: Partial = { + cameraState: { + position: [1.0, -1.4, 45], + up: [0, 1, 0], + fov: 43.5, + }, + }; + const serializedState = "pos:1:-1.4:45,up:0:1:0,fov:43.5"; + expect(deserializeViewerState({ cam: serializedState })).toEqual(state); + }); }); }); diff --git a/website/utils/url_utils.ts b/website/utils/url_utils.ts index ff908bbc..c2d55226 100644 --- a/website/utils/url_utils.ts +++ b/website/utils/url_utils.ts @@ -554,6 +554,24 @@ function formatFloat(value: number, maxPrecision: number = 5): string { return Number(value.toPrecision(maxPrecision)).toString(); } +function perAxisToArray(perAxis: PerAxis): T[] { + return [perAxis.x, perAxis.y, perAxis.z]; +} + +/** Serializes a region into a `x1:x2,y1:y2,z1:z2` string format. */ +function serializeRegion(region: PerAxis<[number, number]>): string { + return perAxisToArray(region) + .map((axis) => axis.map((val) => formatFloat(val)).join(":")) + .join(","); +} + +/** Serializes a slice parameter into a `x,y,z` string format. */ +function serializeSlice(slice: PerAxis): string { + return perAxisToArray(slice) + .map((val) => formatFloat(val)) + .join(","); +} + function serializeBoolean(value: boolean | undefined): "1" | "0" | undefined { if (value === undefined) { return undefined; @@ -787,9 +805,8 @@ export function serializeViewerState(state: Partial, removeDefaults [ViewerStateKeys.Brightness]: state.brightness?.toString(), [ViewerStateKeys.Density]: state.density?.toString(), [ViewerStateKeys.Interpolation]: serializeBoolean(state.interpolationEnabled), - [ViewerStateKeys.Region]: - state.region && `${state.region.x.join(":")},${state.region.y.join(":")},${state.region.z.join(":")}`, - [ViewerStateKeys.Slice]: state.slice && `${state.slice.x},${state.slice.y},${state.slice.z}`, + [ViewerStateKeys.Region]: state.region && serializeRegion(state.region), + [ViewerStateKeys.Slice]: state.slice && serializeSlice(state.slice), [ViewerStateKeys.Levels]: state.levels?.join(","), [ViewerStateKeys.Time]: state.time?.toString(), [ViewerStateKeys.CameraState]: