Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
2 changes: 2 additions & 0 deletions src/GlobalStates/GlobalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type StoreState = {
textureArrayDepths: number[];
textureData: Uint8Array;
clampExtremes: boolean;
permute: number[] | undefined;

// setters
setDataShape: (dataShape: number[]) => void;
Expand Down Expand Up @@ -116,6 +117,7 @@ export const useGlobalStore = create<StoreState>((set, get) => ({
DPR: 1,
scalingFactor: null,
clampExtremes: false,
permute: undefined, //Dim permutations
// setters

setDataShape: (dataShape) => set({ dataShape }),
Expand Down
2 changes: 2 additions & 0 deletions src/GlobalStates/PlotStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type PlotState ={
maskValue: number;
cameraPosition: THREE.Vector3;
disablePointScale: boolean;
permute: number[]; // Need one in plotStore as well so plot states don't change when permute changes from UI

setQuality: (quality: number) => void;
setTimeScale: (timeScale : number) =>void;
Expand Down Expand Up @@ -185,6 +186,7 @@ export const usePlotStore = create<PlotState>((set, get) => ({
borderWidth: 0.05,
cameraPosition: new THREE.Vector3(0, 0, 5),
disablePointScale: false,
permute: [0,1,2],

setVTransferRange: (vTransferRange) => set({ vTransferRange }),
setVTransferScale: (vTransferScale) => set({ vTransferScale }),
Expand Down
16 changes: 9 additions & 7 deletions src/components/plots/AxisLines.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js
import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
import { LineMaterial } from 'three-stdlib';
import { useFrame } from '@react-three/fiber';
import { parseLoc, coarsenFlatArray } from '@/utils/HelperFuncs';
import { parseLoc, coarsenFlatArray, permuteArr } from '@/utils/HelperFuncs';
import { useCSSVariable } from '../ui';
import * as THREE from 'three'

Expand Down Expand Up @@ -41,12 +41,12 @@ const CubeAxis = ({flipX, flipY, flipDown}: {flipX: boolean, flipY: boolean, fli
is4D: state.is4D
})))

const {xRange, yRange, zRange, plotType, timeScale, animProg, zSlice, ySlice, xSlice, coarsen} = usePlotStore(useShallow(state => ({
const {xRange, yRange, zRange, plotType, timeScale, animProg, zSlice, ySlice, xSlice, coarsen, permute} = usePlotStore(useShallow(state => ({
xRange: state.xRange, yRange: state.yRange,
zRange: state.zRange, plotType: state.plotType,
timeScale: state.timeScale, animProg: state.animProg,
zSlice: state.zSlice, ySlice: state.ySlice,
xSlice: state.xSlice, coarsen: state.coarsen,
xSlice: state.xSlice, coarsen: state.coarsen, permute:state.permute
})))
const {hideAxis, hideAxisControls} = useImageExportStore(useShallow( state => ({
hideAxis: state.hideAxis,
Expand All @@ -73,13 +73,15 @@ const CubeAxis = ({flipX, flipY, flipDown}: {flipX: boolean, flipY: boolean, fli
const [yResolution, setYResolution] = useState<number>(AXIS_CONSTANTS.INITIAL_RESOLUTION)
const [zResolution, setZResolution] = useState<number>(AXIS_CONSTANTS.INITIAL_RESOLUTION)

const permuteShape = useMemo(()=>permuteArr(dataShape,permute),[permute])
Comment thread
TheJeran marked this conversation as resolved.
Outdated

const isPC = useMemo(()=>plotType == 'point-cloud',[plotType])
const globalScale = isPC ? dataShape[2]/AXIS_CONSTANTS.PC_GLOBAL_SCALE_DIVISOR : 1
const globalScale = isPC ? permuteShape[2]/AXIS_CONSTANTS.PC_GLOBAL_SCALE_DIVISOR : 1


const depthRatio = useMemo(()=>dataShape[0]/dataShape[2]*timeScale,[dataShape, timeScale]);
const shapeRatio = useMemo(()=>dataShape[1]/dataShape[2], [dataShape])
const timeRatio = Math.max(dataShape[0]/dataShape[2], 2);
const depthRatio = useMemo(()=>permuteShape[0]/permuteShape[2]*timeScale,[permuteShape, timeScale]);
const shapeRatio = useMemo(()=>permuteShape[1]/permuteShape[2], [permuteShape])
const timeRatio = Math.max(permuteShape[0]/permuteShape[2], 2);

const secondaryColor = useCSSVariable('--text-plot') //replace with needed variable
const colorHex = useMemo(()=>{
Expand Down
7 changes: 4 additions & 3 deletions src/components/plots/Sphere.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const Sphere = ({textures} : {textures: THREE.Data3DTexture[] | THREE.Dat
flipY: state.flipY,
textureArrayDepths: state.textureArrayDepths
})))

const {animate, animProg, cOffset, cScale, valueRange, selectTS, nanColor, nanTransparency, sphereDisplacement, sphereResolution,
zSlice, ySlice, xSlice, fillValue, borderTexture, maskTexture, maskValue,
getColorIdx, incrementColorIdx} = usePlotStore(useShallow(state=> ({
Expand Down Expand Up @@ -179,11 +178,13 @@ export const Sphere = ({textures} : {textures: THREE.Data3DTexture[] | THREE.Dat

return (
<>
{/* Don't really understand why now I have to ALSO flip the sphere in this update. Maybe something about the updated GetCurrentArray */}
<group scale={[1, flipY ? -1 : 1, 1]}>
<SquareMeshes />
<mesh renderOrder={1} geometry={geometry} material={shaderMaterial} onClick={e=>selectTS && HandleTimeSeries(e)}/>
<mesh renderOrder={0} geometry={geometry} material={backMaterial} />
</group>
<mesh renderOrder={1} geometry={geometry} material={shaderMaterial} onClick={e=>selectTS && HandleTimeSeries(e)}/>
<mesh renderOrder={0} geometry={geometry} material={backMaterial} />

</>
)
}
61 changes: 61 additions & 0 deletions src/components/ui/Elements/DimensionOrder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, {useRef, useState, useEffect} from 'react'
import { useGlobalStore } from '@/GlobalStates/GlobalStore'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

export const DimensionOrder = ({dimNames, setDuplicateDims}: {dimNames: string[], setDuplicateDims: React.Dispatch<React.SetStateAction<boolean>>}) => {
const {permute} = useGlobalStore.getState() // Just need the value on mount to set initial <select> values
const slotCount = Math.min(3, dimNames.length);
const permuteRef = useRef<number[]>(
Array.from({ length: slotCount }, (_, i) => i)
)
const [warnDuplicates, setWarnDuplicates] = useState(false);
const handleChange = (slotIdx: number, dimIdx: number) => {
permuteRef.current[slotIdx] = dimIdx;
useGlobalStore.setState({ permute: [...permuteRef.current] });
const permuteArray = permuteRef.current;
const hasDupes = permuteArray.length != new Set(permuteArray).size
setWarnDuplicates(hasDupes)
setDuplicateDims(hasDupes)
};

useEffect(()=>{ //The first render didn't have dimNames and used default value. UseEffect updates after fetch. Also updates when switching to variable with diff Dims
// At the moment it resets each time because of the double click issue with menus that got introduced in 0.3.0.
permuteRef.current = Array.from({ length: slotCount }, (_, i) => i)
useGlobalStore.setState({ permute: [...permuteRef.current] });
},[slotCount])
Comment on lines +27 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The useEffect hook currently only triggers when slotCount changes. If you switch to a different variable that has the same number of dimensions (e.g., switching from one 3D variable to another 3D variable), slotCount remains 3, meaning the effect won't run and the dimension permutation will not reset. Adding dimNames to the dependency array ensures the permutation resets correctly when switching variables.

Suggested change
useEffect(()=>{ //The first render didn't have dimNames and used default value. UseEffect updates after fetch. Also updates when switching to variable with diff Dims
// At the moment it resets each time because of the double click issue with menus that got introduced in 0.3.0.
permuteRef.current = Array.from({ length: slotCount }, (_, i) => i)
useGlobalStore.setState({ permute: [...permuteRef.current] });
},[slotCount])
useEffect(()=>{ //The first render didn't have dimNames and used default value. UseEffect updates after fetch. Also updates when switching to variable with diff Dims
// At the moment it resets each time because of the double click issue with menus that got introduced in 0.3.0.
permuteRef.current = Array.from({ length: slotCount }, (_, i) => i)
useGlobalStore.setState({ permute: [...permuteRef.current] });
},[dimNames, slotCount])

return (
<>
<div className="flex gap-2">
{Array.from({ length: slotCount }).map((_, slotIdx) => (
<Select
key={slotIdx}
value={String(permute?.[slotIdx])}
onValueChange={(v) => handleChange(slotIdx, Number(v))}
>
<SelectTrigger className='flex-1 min-w-0 truncate'>
<SelectValue />
</SelectTrigger>
<SelectContent>
{dimNames.map((dim, dimIdx) => (
<SelectItem key={dimIdx} value={String(dimIdx)}>
{dim}
</SelectItem>
))}
</SelectContent>
</Select>
))}
</div>
{warnDuplicates && <p className='text-red-500 font-bold'>
DUPLICATE DIMENSION USED
</p>}
</>
);
};


20 changes: 12 additions & 8 deletions src/components/ui/MainPanel/MetaDataInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { useShallow } from 'zustand/shallow'
import { SliderThumbs } from "@/components/ui/Widgets/SliderThumbs"
import Metadata, { defaultAttributes, renderAttributes } from "@/components/ui/MetaData"
import { BsFillQuestionCircleFill } from "react-icons/bs";
import { parseLoc, HandleCoarselNums } from "@/utils/HelperFuncs"
import { parseLoc, permuteArr } from "@/utils/HelperFuncs"
import { DimensionOrder } from "../Elements/DimensionOrder";
import {
Tooltip,
TooltipContent,
Expand Down Expand Up @@ -58,9 +59,9 @@ function HandleCustomSteps(e: string, chunkSize: number){


const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSide }: { meta: any, metadata: Record<string, any>, setShowMeta: React.Dispatch<React.SetStateAction<boolean>>, setOpenVariables: (open: boolean) => void, popoverSide: string }) => {
const {is4D, idx4D, variable, initStore, setIs4D, setIdx4D, setVariable, setTextureArrayDepths} = useGlobalStore(useShallow(state => ({
const {is4D, idx4D, variable, initStore, permute, setIs4D, setIdx4D, setVariable, setTextureArrayDepths} = useGlobalStore(useShallow(state => ({
is4D: state.is4D, idx4D: state.idx4D, variable: state.variable,
initStore: state.initStore,
initStore: state.initStore, permute:state.permute,
setIs4D: state.setIs4D, setIdx4D: state.setIdx4D, setVariable: state.setVariable,
setTextureArrayDepths: state.setTextureArrayDepths,
})))
Expand All @@ -78,12 +79,14 @@ const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSi
const {maxTextureSize, max3DTextureSize} = usePlotStore(useShallow(state => ({maxTextureSize: state.maxTextureSize, max3DTextureSize: state.max3DTextureSize})))

const [tooBig, setTooBig] = useState(false)
const [duplicateDims, setDuplicateDims] = useState(false)
const [cached, setCached] = useState(false)
const [cachedChunks, setCachedChunks] = useState<string | null>(null)
const [texCount, setTexCount] = useState(0)
const [displaySpat, setDisplaySpat] = useState(String(kernelSize))
const [displayDepth, setDisplayDepth] = useState(String(kernelDepth))


// ---- Meta Info ---- //
const {dimArrays, dimNames, dimUnits} = meta.dimInfo
const totalSize = meta.totalSize ? meta.totalSize : 0
Expand Down Expand Up @@ -190,7 +193,7 @@ const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSi
}
},[currentSize, meta])

const smallCache = cachedSize > cacheSize
const smallCache = cachedSize > cacheSize // If the current cache is too small

useEffect(()=>{
const this4D = meta.shape.length == 4;
Expand Down Expand Up @@ -224,7 +227,6 @@ const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSi
setCached(false)
}
},[meta, chunkIDs])

return (
<>
<b>{`${meta.long_name} `}</b>
Expand All @@ -246,15 +248,17 @@ const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSi
<div> <Metadata data={metadata} variable ={'Attributes'} /> </div>
}
<br/>
{/* This doesn't work on more than 3D at the moment. But it's a start */}
{shapeLength <= 3 && <DimensionOrder dimNames={dimNames} setDuplicateDims={setDuplicateDims}/> }
<br/>
<div className="grid grid-cols-[40%_40%_20%]">
<div className="flex flex-col">
<b>Data Shape</b>
{`[${formatArray(dataShape)}]`}
{`[${formatArray(permuteArr(dataShape, permute))}]`}
</div>
<div className="flex flex-col">
<b>Chunk Shape</b>
{`[${formatArray(chunkShape)}]`}
{`[${formatArray(permuteArr(chunkShape, permute))}]`}
</div>
<div className="flex flex-col items-center">
<label htmlFor="coarsen"><b>Coarsen</b></label>
Expand Down Expand Up @@ -483,7 +487,7 @@ const MetaDataInfo = ({ meta, metadata, setShowMeta, setOpenVariables, popoverSi
{!tooBig && <Button
variant="pink"
className="cursor-pointer hover:scale-[1.05]"
disabled={((is4D && idx4D == null) || smallCache)}
disabled={((is4D && idx4D == null) || smallCache || duplicateDims)}
onClick={() => {
if (variable == meta.name){
ReFetch();
Expand Down
42 changes: 6 additions & 36 deletions src/components/zarr/GetArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useGlobalStore } from "@/GlobalStates/GlobalStore";
import { useZarrStore } from "@/GlobalStates/ZarrStore";
import { useCacheStore } from "@/GlobalStates/CacheStore";
import { useErrorStore } from "@/GlobalStates/ErrorStore";
import { calculateStrides } from "@/utils/HelperFuncs";
import { ToFloat16, CompressArray, DecompressArray, copyChunkToArray, RescaleArray, copyChunkToArray2D } from "./utils";
import { calculateStrides, GetCurrentArray } from "@/utils/HelperFuncs";
import { ToFloat16, CompressArray, RescaleArray } from "./utils";
import { NCFetcher, zarrFetcher } from "./dataFetchers";
import { Convolve } from "../computation/webGPU";
import { coarsen3DArray } from "@/utils/HelperFuncs";
Expand Down Expand Up @@ -68,37 +68,12 @@ export async function GetArray(varOveride?: string) {
cachedChunk.kernel.kernelSize === (coarsen ? kernelSize : undefined) &&
cachedChunk.kernel.kernelDepth === (coarsen ? kernelDepth : undefined);

if (isCacheValid) {
const chunkData = cachedChunk.compressed ? DecompressArray(cachedChunk.data) : cachedChunk.data.slice();
if (hasZ) {
copyChunkToArray(
chunkData,
cachedChunk.shape,
cachedChunk.stride,
typedArray,
outputShape,
destStride as any, [z, y, x],
[zDim.start, yDim.start, xDim.start]
)
} else {
copyChunkToArray2D(
chunkData,
cachedChunk.shape,
cachedChunk.stride,
typedArray,
outputShape,
destStride as any, [y, x],
[yDim.start, xDim.start])
}
} else {
if (!isCacheValid) {
const raw = await fetcher.fetchChunk({ variable:targetVariable, rank, shape, chunkShape, x, y, z, xDimIndex, yDimIndex, zDimIndex, idx4D });

const rawData = Number.isFinite(fillValue) ? raw.data.map((v: number) => v === fillValue ? NaN : v) : raw.data; // Don't map if no fillvalue

let [chunkF16, newScalingFactor] = ToFloat16(rawData, scalingFactor);
let thisShape = raw.shape;
let chunkStride = raw.stride;

if (coarsen) {
chunkF16 = await Convolve(chunkF16, { shape: chunkShape, strides: chunkStride }, "Mean3D", { kernelSize, kernelDepth }) as Float16Array;
thisShape = thisShape.map((dim, idx) => Math.floor(dim / (idx === 0 ? kernelDepth : kernelSize)));
Expand All @@ -118,12 +93,6 @@ export async function GetArray(varOveride?: string) {
}
}

if (hasZ) {
copyChunkToArray(chunkF16, thisShape.slice(-3), chunkStride.slice(-3) as any, typedArray, outputShape, destStride as any, [z, y, x], [zDim.start, yDim.start, xDim.start]);
} else {
copyChunkToArray2D(chunkF16, thisShape, chunkStride as any, typedArray, outputShape, destStride as any, [y, x], [yDim.start, xDim.start]);
}

cache.set(cacheName, {
data: compress ? CompressArray(chunkF16, 7) : chunkF16,
shape: chunkShape, stride: chunkStride,
Expand All @@ -134,8 +103,9 @@ export async function GetArray(varOveride?: string) {
}
setProgress(Math.round(iter++ / totalChunks * 100));
}
}
}
}
useGlobalStore.setState({dataShape:outputShape}) // Needed for initial GetCurrentArray call which needs dataShape if needs to be permuted
setProgress(0);
return { data: typedArray, shape: outputShape, dtype, scalingFactor };
return { data: GetCurrentArray(), shape: outputShape, dtype, scalingFactor };
}
19 changes: 12 additions & 7 deletions src/hooks/useDataFetcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useGlobalStore } from '@/GlobalStates/GlobalStore';
import { usePlotStore } from '@/GlobalStates/PlotStore';
import { useZarrStore } from '@/GlobalStates/ZarrStore';
import { useShallow } from 'zustand/shallow';
import { ParseExtent, GetDimInfo } from '@/utils/HelperFuncs';
import { ParseExtent, GetDimInfo, permuteArr } from '@/utils/HelperFuncs';
import { GetAttributes } from '@/components/zarr/ZarrLoaderLRU';
import { GetArray } from '@/components/zarr/GetArray';
import { ArrayToTexture } from '@/components/textures';
Expand All @@ -25,10 +25,11 @@ export const useDataFetcher = () => {
setPlotOn: state.setPlotOn,
setStatus: state.setStatus
})))
const {variable, is4D, setIsFlat} = useGlobalStore(
const {variable, is4D, permute, setIsFlat} = useGlobalStore(
useShallow(state=>({
variable: state.variable,
is4D: state.is4D,
permute: state.permute,
setIsFlat: state.setIsFlat,
})))
const {plotType, interpPixels, setPlotType} = usePlotStore(
Expand Down Expand Up @@ -66,20 +67,19 @@ export const useDataFetcher = () => {
}
//----- TS Cleanup ----//
useGlobalStore.setState({timeSeries:{}, dimCoords:{}})
//---- Set Plot Slicez ----//
//---- Set Plot Slices ----//
const { setZSlice, setYSlice, setXSlice } = usePlotStore.getState();
setZSlice(zSlice);
setYSlice(ySlice);
setXSlice(xSlice);

//---- Main Fetch ----//
GetArray().then((result) => {
const shape = result.shape.filter((val) => val != 1);
const shape = permuteArr(result.shape.filter((val) => val != 1),permute);
const [tempTexture, scaling] = ArrayToTexture({
data: result.data,
shape
});

setTextures(tempTexture);
setValueScales(scaling as { maxVal: number; minVal: number });
useGlobalStore.getState().setScalingFactor(result.scalingFactor);
Expand All @@ -104,6 +104,7 @@ export const useDataFetcher = () => {
setShow(true);
setPlotOn(true);
setStatus(null);
usePlotStore.setState({permute})
});
} catch (error) {
console.error(error);
Expand All @@ -125,15 +126,19 @@ export const useDataFetcher = () => {
dimUnits = dimUnits.slice(1);
dimNames = dimNames.slice(1);
}
if (permute?.some((v, i) => i > 0 && v < permute[i - 1])){
Comment thread
TheJeran marked this conversation as resolved.
dimArrays = permuteArr(dimArrays, permute);
dimNames = permuteArr(dimNames, permute);
dimUnits = permuteArr(dimUnits, permute)
}
setDimNames(dimNames);
setDimArrays(dimArrays);

const targetDim = dimArrays.length > 2 ? dimArrays[1] : dimArrays[0];
const shouldFlip = targetDim[1] < targetDim[0];
setFlipY(shouldFlip);

setDimUnits(dimUnits);
ParseExtent(dimUnits, dimArrays);
// ParseExtent(dimUnits, dimArrays);
});

} else {
Expand Down
Loading
Loading