From 73df838daff9cfd5e723ca978a81ac09c2b77802 Mon Sep 17 00:00:00 2001 From: Jeran Date: Mon, 20 Jul 2026 16:59:09 +0200 Subject: [PATCH 1/8] Grabbing correct spot in dataArray. Need to update columnmeshes --- src/components/plots/DataCube.tsx | 7 ++- src/components/plots/TransectMeshes.tsx | 5 +- src/components/plots/UVCube.tsx | 50 ++++++++++++++++---- src/components/textures/ProjectionTexture.ts | 3 +- src/components/ui/MainPanel/AdjustPlot.tsx | 33 ++++++------- 5 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/components/plots/DataCube.tsx b/src/components/plots/DataCube.tsx index bae8dbd0..0a787b8b 100644 --- a/src/components/plots/DataCube.tsx +++ b/src/components/plots/DataCube.tsx @@ -86,7 +86,6 @@ export const DataCube = ({ volTexture: propVolTexture }: DataCubeProps ) => { side: useOrtho ? THREE.FrontSide : THREE.BackSide, }),[useFragOpt, useOrtho, volTexture, remapTexture]); - const geometry = useMemo(() => new THREE.BoxGeometry(shape.x, shape.y, shape.z), [shape]); useEffect(() => { if (shaderMaterial) { @@ -120,10 +119,10 @@ export const DataCube = ({ volTexture: propVolTexture }: DataCubeProps ) => { .invert(); }) return ( - + - - + + ) } \ No newline at end of file diff --git a/src/components/plots/TransectMeshes.tsx b/src/components/plots/TransectMeshes.tsx index 5f35d051..3a785683 100644 --- a/src/components/plots/TransectMeshes.tsx +++ b/src/components/plots/TransectMeshes.tsx @@ -134,10 +134,11 @@ export const SquareMeshes = () => { } export const ColumnMeshes = () => { - const {timeSeries, dataShape, shape} = useGlobalStore(useShallow(state=>({ + const {timeSeries, dataShape, remapTexture} = useGlobalStore(useShallow(state=>({ timeSeries:state.timeSeries, dataShape: state.dataShape, - shape: state.shape + shape: state.shape, + remapTexture: state.remapTexture }))) const {plotType} = usePlotStore(useShallow(state=>({ plotType: state.plotType diff --git a/src/components/plots/UVCube.tsx b/src/components/plots/UVCube.tsx index 301caa68..7f2ea9f8 100644 --- a/src/components/plots/UVCube.tsx +++ b/src/components/plots/UVCube.tsx @@ -26,6 +26,23 @@ function updateFace( } } +function sample2D(tex: THREE.DataTexture, u:number, v:number): [THREE.Vector2, boolean] { + const { data, width, height } = tex.image; + if (!data) return [new THREE.Vector2(u, v), true]; + + const x = Math.floor(u * (width - 1)); + const y = Math.floor(v * (height - 1)); + + const idx = (y * width + x) * 4; // RGBA + const newU = THREE.DataUtils.fromHalfFloat(data[idx + 0]) + const newV = THREE.DataUtils.fromHalfFloat(data[idx + 1]) + const valid = THREE.DataUtils.fromHalfFloat(data[idx + 2]) + return [ + new THREE.Vector2(newU,newV), + valid > 0.5 + ]; +} + const UpdateUVs = ( geometry: THREE.BoxGeometry, @@ -78,14 +95,16 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ analysisArray: state.analysisArray }))) - const {shape, dataShape, strides, dimArrays,dimNames,dimUnits} = useGlobalStore( + const {shape, dataShape, strides, axisDimArrays,axisDimNames,axisDimUnits, remapTexture, flipY} = useGlobalStore( useShallow(state=>({ shape:state.shape, dataShape: state.dataShape, strides: state.strides, - dimArrays:state.dimArrays, - dimNames:state.dimNames, - dimUnits:state.dimUnits + axisDimArrays:state.axisDimArrays, + axisDimNames:state.axisDimNames, + axisDimUnits:state.axisDimUnits, + remapTexture: state.remapTexture, + flipY: state.flipY }))) const {selectTS, xRange, yRange, zRange,getColorIdx, incrementColorIdx} = usePlotStore(useShallow(state => ({ @@ -99,14 +118,25 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ function HandleTimeSeries(event: THREE.Intersection){ const uv = event.uv!; + let valid = true; + let newUV: THREE.Vector2 | undefined; const normal = event.normal!; + if (remapTexture && Math.abs(normal.z) > 0.5){ // Get new UV if reprojected and along z Axis + const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + if (flipY) thisUV.y = 1-thisUV.y + if (isValid) newUV = thisUV; + } + const dimAxis = getUnitAxis(normal); if (dimAxis != lastNormal.current){ setTimeSeries({}); //Clear timeseries if new axis setDimCoords({}); } lastNormal.current = dimAxis; - const tempTS = GetTimeSeries({data: analysisMode ? analysisArray : GetCurrentArray(), shape: dataShape, stride: strides},{uv,normal}) + const tempTS = valid ? GetTimeSeries( + { data: analysisMode ? analysisArray : GetCurrentArray(), shape: dataShape, stride: strides }, + { uv: newUV ?? uv, normal } + ) : [] const plotDim = (normal.toArray()).map((val, idx) => { if (Math.abs(val) > 0) { return idx; @@ -114,10 +144,10 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ return null;}).filter(idx => idx !== null); setPlotDim(2-plotDim[0]) //I think this 2 is only if there are 3-dims. Need to rework the logic - const coordUV = parseUVCoords({normal:normal,uv:uv}) - let dimCoords = coordUV.map((val,idx)=>val ? dimArrays[idx][Math.round(val*dimArrays[idx].length)] : null) - const thisDimNames = dimNames.filter((_,idx)=> dimCoords[idx] !== null) - const thisDimUnits = dimUnits.filter((_,idx)=> dimCoords[idx] !== null) + const coordUV = valid ? parseUVCoords({normal:normal,uv}) : [null] + let dimCoords = coordUV.map((val,idx)=>val ? axisDimArrays[idx][Math.round(val*axisDimArrays[idx].length)] : null) + const thisDimNames = axisDimNames.filter((_,idx)=> dimCoords[idx] !== null) + const thisDimUnits = axisDimUnits.filter((_,idx)=> dimCoords[idx] !== null) dimCoords = dimCoords.filter(val => val !== null) const tsID = `${dimCoords[0]}_${dimCoords[1]}` const tsObj = { @@ -140,7 +170,7 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ units:thisDimUnits[1] }, plot:{ - units:dimUnits[2-plotDim[0]] + units:axisDimUnits[2-plotDim[0]] } } updateDimCoords({[tsID] : dimObj}) diff --git a/src/components/textures/ProjectionTexture.ts b/src/components/textures/ProjectionTexture.ts index e5821aac..42936d0b 100644 --- a/src/components/textures/ProjectionTexture.ts +++ b/src/components/textures/ProjectionTexture.ts @@ -237,8 +237,7 @@ export function reproject(resolution: number = 256){ for (let i = 0; i < targetWidth; i++) { const [lon, lat, valid] = safeInverse(proj, [xTicks[i], yTicks[j]]); const u = xRangeDiff > 0 ? (isXDescending ? (xMax - lon) / xRangeDiff : (lon - xMin) / xRangeDiff) : 0; - const v = yRangeDiff > 0 ? (isYDescending ? (yMax - lat) / yRangeDiff : (lat - yMin) / yRangeDiff) : 0; - + const v = (isYDescending ? (yMax - lat) / yRangeDiff : (lat - yMin) / yRangeDiff) // Check boundary bounds to avoid displaying clamped blocks outside the dataset area const inBounds = lon >= xMin && lon <= xMax && lat >= yMin && lat <= yMax; const validVal = (valid === 1 && inBounds) ? 1 : 0; diff --git a/src/components/ui/MainPanel/AdjustPlot.tsx b/src/components/ui/MainPanel/AdjustPlot.tsx index e0842be3..7e1e1a0a 100644 --- a/src/components/ui/MainPanel/AdjustPlot.tsx +++ b/src/components/ui/MainPanel/AdjustPlot.tsx @@ -22,6 +22,7 @@ import {Select, SelectTrigger, SelectContent, SelectItem, SelectValue} from '@/c import { RiCloseLargeLine } from "react-icons/ri"; import { reproject } from '@/components/textures/ProjectionTexture'; import { Reprojection } from '../Elements/Reprojection'; +import { useAxisIndices } from '@/hooks'; function DeNorm(val : number, min : number, max : number){ const range = max-min; @@ -92,17 +93,13 @@ const DimSlicer = () =>{ const defaultScales = {minVal: 0, maxVal: 0} //This is fed into MinMax as it is required but overwritten if an array is present - const {dimArrays, dimNames, dimUnits, is4D} = useGlobalStore(useShallow(state => ({ - dimArrays : state.dimArrays, - dimNames: state.dimNames, - dimUnits: state.dimUnits, + const {axisDimArrays, axisDimNames, axisDimUnits, is4D} = useGlobalStore(useShallow(state => ({ + axisDimArrays : state.axisDimArrays, + axisDimNames: state.axisDimNames, + axisDimUnits: state.axisDimUnits, is4D: state.is4D }))) - - const theseDims = is4D ? dimNames.slice(1) : dimNames - const theseUnits = is4D ? dimUnits.slice(1) : dimUnits - const theseArrays = is4D ? dimArrays.slice(1) : dimArrays - + const {xIdx, yIdx, zIdx} = useAxisIndices() const [isSpatialOpen, setIsSpatialOpen] = useState(false); return ( <> @@ -129,33 +126,33 @@ const DimSlicer = () =>{
-

{theseDims[2]}

+

{axisDimNames[xIdx]}

-

{theseDims[1]}

+

{axisDimNames[yIdx]}

-

{theseDims[0]}

+

{axisDimNames[zIdx]}

From 0345b763f8ef45e088b33f7784fb92ebd4d982f6 Mon Sep 17 00:00:00 2001 From: Jeran Date: Tue, 21 Jul 2026 17:20:31 +0200 Subject: [PATCH 2/8] Something more --- src/components/plots/TransectMeshes.tsx | 25 +++++++++++++------- src/components/plots/UVCube.tsx | 20 +++++++++++++++- src/components/textures/ProjectionTexture.ts | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/components/plots/TransectMeshes.tsx b/src/components/plots/TransectMeshes.tsx index 3a785683..f8d69ecb 100644 --- a/src/components/plots/TransectMeshes.tsx +++ b/src/components/plots/TransectMeshes.tsx @@ -5,6 +5,7 @@ import { useGlobalStore } from '@/GlobalStates/GlobalStore' import { useShallow } from 'zustand/shallow' import { deg2rad, parseUVCoords } from '@/utils/HelperFuncs' import { useCoordBounds } from '@/hooks/useCoordBounds' +import { useAxisIndices } from '@/hooks' function remapToXYZ(uv: THREE.Vector2, latBounds: number[], lonBounds: number[]): THREE.Vector3 { const u = 1 - uv.x; @@ -44,6 +45,7 @@ function normalToPos(uv: THREE.Vector2, normal:THREE.Vector3, ratios:{depthRatio } function normalToScale(normal:THREE.Vector3, ratios:{depthRatio:number, aspectRatio:number}, steps:{xSteps:number, ySteps:number, zSteps:number}){ + //This function scales meshes to match the observed size of the pixels let scaleZ, scaleY, scaleX: number; const {xSteps,ySteps,zSteps} = steps; const {aspectRatio, depthRatio} = ratios; @@ -143,17 +145,22 @@ export const ColumnMeshes = () => { const {plotType} = usePlotStore(useShallow(state=>({ plotType: state.plotType }))) - + const {xIdx, yIdx, zIdx} = useAxisIndices() const meshes: THREE.Mesh[] = useMemo(()=>{ const meshes: THREE.Mesh[] = [] - const dataLen = dataShape.length; - const xSteps = dataShape[dataLen-1]; - const ySteps = dataShape[dataLen-2]; - const zSteps = dataShape[dataLen-3]; - const aspectRatio = dataShape[dataLen-2]/dataShape[dataLen-1] - const depthRatio = dataShape[dataLen-3]/dataShape[dataLen-1] + const xSteps = remapTexture + ? remapTexture.image.width + : dataShape[xIdx]; + const ySteps = remapTexture + ? remapTexture.image.height + : dataShape[yIdx]; + const zSteps = dataShape[zIdx]; + const aspectRatio = ySteps/xSteps; // This is not aspect ratio + const depthRatio = zSteps/xSteps; + for (const [tsID, tsObj] of Object.entries(timeSeries)){ - const {normal, uv, color} = tsObj + const {normal, uv, newUV, color} = tsObj + const thisUV = remapTexture ? uv : newUV?? uv; const position = normalToPos(uv, normal, {aspectRatio,depthRatio}) const meshScale = normalToScale(normal, {aspectRatio, depthRatio}, {xSteps, ySteps, zSteps}) const thisColor = color.map((c: number) => Math.pow((c/255), 2.2)) // Gamma correct the color @@ -166,7 +173,7 @@ export const ColumnMeshes = () => { } return meshes - },[timeSeries, plotType]) + },[timeSeries, plotType, remapTexture]) useEffect(() => { return () => { meshes.forEach(mesh => { diff --git a/src/components/plots/UVCube.tsx b/src/components/plots/UVCube.tsx index 7f2ea9f8..2ef87494 100644 --- a/src/components/plots/UVCube.tsx +++ b/src/components/plots/UVCube.tsx @@ -125,8 +125,10 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. if (flipY) thisUV.y = 1-thisUV.y if (isValid) newUV = thisUV; + else{ + return; + } } - const dimAxis = getUnitAxis(normal); if (dimAxis != lastNormal.current){ setTimeSeries({}); //Clear timeseries if new axis @@ -154,6 +156,7 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ color:evaluate_cmap(getColorIdx()/10,"Paired"), normal, uv, + newUV, data:tempTS } updateTimeSeries({ [tsID] : tsObj}) @@ -176,6 +179,21 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ updateDimCoords({[tsID] : dimObj}) } + // useEffect(()=>{ + // // This effect gets the reprojected UVs for all points after reprojecting + + // if (remapTexture){ + // const {timeSeries} = useGlobalStore.getState() + // for (const [tsID, tsObj] of Object.entries(timeSeries)){ + // const {uv} = tsObj + // const [thisUV, _isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Should always be valid as either was plotted from original CRS, or invalid are not added in new CRS + // if (flipY) thisUV.y = 1-thisUV.y + // const newTSObj = {...tsObj, newUV:thisUV} + // updateTimeSeries({ [tsID] : newTSObj}) + // } + // } + // },[remapTexture]) + const {geometry, position} = useMemo(() => { const xScale = (xRange[1] - xRange[0])/2 const yScale = (yRange[1] - yRange[0])/2 diff --git a/src/components/textures/ProjectionTexture.ts b/src/components/textures/ProjectionTexture.ts index 42936d0b..bd341e9b 100644 --- a/src/components/textures/ProjectionTexture.ts +++ b/src/components/textures/ProjectionTexture.ts @@ -198,7 +198,7 @@ export function reproject(resolution: number = 256){ targetWidth = width; targetHeight = height; xTicks = linspace(minX, maxX, targetWidth); - yTicks = linspace(minY, maxY, targetHeight); + yTicks = flipY ? linspace(maxY, minY, targetHeight) : linspace(minY, maxY, targetHeight); data = new Uint16Array(targetWidth * targetHeight * 4); const xDiff = Math.abs(maxX - minX); From 5b7c75c7c16abea6fc09a604334aff2efea920f4 Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 11:13:38 +0200 Subject: [PATCH 3/8] Working with FlatMap and Analysis window --- src/components/plots/AnalysisInfo.tsx | 37 +++++++------- src/components/plots/FlatMap.tsx | 28 ++++++++--- src/components/plots/TransectMeshes.tsx | 17 ++++--- src/components/plots/UVCube.tsx | 64 ++++++++++--------------- src/utils/HelperFuncs.ts | 18 +++++++ 5 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/components/plots/AnalysisInfo.tsx b/src/components/plots/AnalysisInfo.tsx index feb61eeb..1473ae0c 100644 --- a/src/components/plots/AnalysisInfo.tsx +++ b/src/components/plots/AnalysisInfo.tsx @@ -8,25 +8,28 @@ import { parseLoc } from '@/utils/HelperFuncs' const AnalysisInfo = ({loc, show, info, } : {loc: number[], show: boolean, info: number[]}) => { - const {dimNames, dimUnits} = useGlobalStore(useShallow(state=>({dimNames: state.dimNames, dimUnits: state.dimUnits}))) + const {axisDimNames, axisDimArrays, axisDimUnits} = useGlobalStore(useShallow(state=>({axisDimNames: state.axisDimNames, axisDimArrays:state.axisDimArrays, axisDimUnits: state.axisDimUnits}))) const axis = useAnalysisStore(state=> state.axis) - const plotNames = useMemo(()=>{ - if (dimNames.length < 3){ - return [dimNames[0], dimNames[1]] + // This logic is weak and May not hold up with >3 dimensions + const plotInfo = useMemo(()=>{ + let plotNames, plotUnits, plotArrays; + if (axisDimNames.length < 3){ + plotNames = [axisDimNames[0], axisDimNames[1]] + plotUnits = [axisDimUnits[0], axisDimUnits[1]] + plotArrays = [axisDimArrays[0], axisDimArrays[1]] } else{ - return dimNames.filter((_val,idx)=> idx != axis) + plotNames = axisDimNames.filter((_val,idx)=> idx != axis) + plotUnits = axisDimUnits.filter((_val,idx)=> idx != axis) + plotArrays = axisDimArrays.filter((_val,idx)=> idx != axis) } - },[dimNames, axis]) - - const plotUnits = useMemo(()=>{ - if (dimNames.length < 3){ - return [dimUnits[0], dimUnits[1]] - } - else{ - return dimUnits.filter((_val,idx)=> idx != axis) - } - },[dimUnits, axis]) + return {plotNames, plotUnits, plotArrays} + },[axisDimNames, axisDimUnits, axisDimArrays, axis]) + const {plotNames, plotUnits, plotArrays} = plotInfo; + const yArray = plotArrays[0] + const xArray = plotArrays[1] + const yCoord = yArray[Math.floor(info[0] * yArray.length)] + const xCoord = xArray[Math.floor(info[1] * xArray.length)] return (
- {`${plotNames[0]}: ${show && parseLoc(info[0],plotUnits[0])}`}
- {`${plotNames[1]}: ${show && parseLoc(info[1],plotUnits[1])}`}
+ {`${plotNames[0]}: ${show && parseLoc(yCoord,plotUnits[0])}`}
+ {`${plotNames[1]}: ${show && parseLoc(xCoord,plotUnits[1])}`}
{`Value: ${Math.round(info[2] * 100)/100}`}
) diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx index 105f3c73..474885a9 100644 --- a/src/components/plots/FlatMap.tsx +++ b/src/components/plots/FlatMap.tsx @@ -1,6 +1,6 @@ "use client"; -import React, {useMemo, useEffect, useRef, useState} from 'react' +import React, {useMemo, useEffect, useRef} from 'react' import * as THREE from 'three' import { useAnalysisStore } from '@/GlobalStates/AnalysisStore'; import { useGlobalStore } from '@/GlobalStates/GlobalStore'; @@ -9,7 +9,7 @@ import { useZarrStore } from '@/GlobalStates/ZarrStore'; import { vertShader } from '@/components/computation/shaders' import { useShallow } from 'zustand/shallow' import { ThreeEvent } from '@react-three/fiber'; -import { coarsenFlatArray, GetCurrentArray, GetTimeSeries, parseUVCoords, deg2rad } from '@/utils/HelperFuncs'; +import { coarsenFlatArray, GetCurrentArray, GetTimeSeries, parseUVCoords, deg2rad, sample2D } from '@/utils/HelperFuncs'; import { evaluate_cmap } from 'js-colormaps-es'; import { useCoordBounds } from '@/hooks/useCoordBounds'; import { flatFrag } from '../textures/shaders'; @@ -92,7 +92,6 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT const geometry = useMemo(()=>new THREE.PlaneGeometry(2,2*shapeRatio),[shapeRatio]) const infoRef = useRef(false) - const lastUV = useRef(new THREE.Vector2(0,0)) const rotateMap = analysisMode && axis == 2; const sampleArray = useMemo(()=> analysisMode ? analysisArray : GetCurrentArray(),[analysisMode, analysisArray, textures]) const analysisDims = useMemo(() => { @@ -117,9 +116,22 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT const eventRef = useRef | null>(null); const handleMove = (e: ThreeEvent) => { if (infoRef.current && e.uv) { + let {uv} = e; + if (!uv) return; eventRef.current = e; + if (remapTexture){ + const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + if (flipY) thisUV.y = 1-thisUV.y + if (isValid) uv = thisUV; + else{ + val.current = NaN; + setLoc([e.clientX, e.clientY]); + return; + } + } + setLoc([e.clientX, e.clientY]); - lastUV.current = e.uv; + const { x, y } = e.uv; const zSliceIdx = dimSlices.length > 2 ? 2 : 1; const ySliceIdx = dimSlices.length > 2 ? 1 : 0; @@ -132,11 +144,10 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT dataIdx += isFlat ? 0 : Math.floor((dimSlices[0].length-1) * animProg) * xSize*ySize const dataVal = sampleArray ? sampleArray[dataIdx] : 0; val.current = dataVal; - coords.current = isFlat ? analysisMode ? [analysisDims[0][yIdx], analysisDims[1][xIdx]] : [dimSlices[0][yIdx], dimSlices[1][xIdx]] : [dimSlices[ySliceIdx][yIdx], dimSlices[zSliceIdx][xIdx]] + coords.current = [y,x] } } - // ----- TIMESERIES ----- // function HandleTimeSeries(event: THREE.Intersection){ const uv = event.uv; @@ -223,7 +234,10 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT uniforms.fillValue.value = fillValue?? NaN } },[cScale, cOffset, colormap, animProg, nanColor, nanTransparency, latBounds, lonBounds, fillValue, maskValue, valueRange]) - + useEffect(()=>{ + // This is duplicated. Probably shoud just move it to Plot.tsx + useGlobalStore.setState({timeSeries:{}, dimCoords:{}}) + },[remapTexture]) return ( <> diff --git a/src/components/plots/TransectMeshes.tsx b/src/components/plots/TransectMeshes.tsx index f8d69ecb..1a98cd97 100644 --- a/src/components/plots/TransectMeshes.tsx +++ b/src/components/plots/TransectMeshes.tsx @@ -66,20 +66,20 @@ function normalToScale(normal:THREE.Vector3, ratios:{depthRatio:number, aspectRa } export const SquareMeshes = () => { - const {timeSeries, dataShape, shape, flipY} = useGlobalStore(useShallow(state=>({ + const {timeSeries, dataShape, shape} = useGlobalStore(useShallow(state=>({ timeSeries:state.timeSeries, dataShape: state.dataShape, - shape: state.shape, flipY:state.flipY + shape: state.shape }))) const {plotType} = usePlotStore(useShallow(state=>({ plotType: state.plotType }))) const {lonBounds, latBounds} = useCoordBounds() + const {xIdx, yIdx} = useAxisIndices() const meshes: THREE.Mesh[] = useMemo(() =>{ const meshes = [] - const dataLen = dataShape.length; - const xSteps = dataShape[dataLen-1]; - const ySteps = dataShape[dataLen-2]; + const xSteps = dataShape[xIdx]; + const ySteps = dataShape[yIdx]; const normedXExtent = (lonBounds[1]-lonBounds[0])/360 const normedYExtent = (latBounds[1]-latBounds[0])/180 const isSphere = plotType == "sphere"; @@ -158,9 +158,8 @@ export const ColumnMeshes = () => { const aspectRatio = ySteps/xSteps; // This is not aspect ratio const depthRatio = zSteps/xSteps; - for (const [tsID, tsObj] of Object.entries(timeSeries)){ - const {normal, uv, newUV, color} = tsObj - const thisUV = remapTexture ? uv : newUV?? uv; + for (const [_tsID, tsObj] of Object.entries(timeSeries)){ + const {normal, uv, color} = tsObj const position = normalToPos(uv, normal, {aspectRatio,depthRatio}) const meshScale = normalToScale(normal, {aspectRatio, depthRatio}, {xSteps, ySteps, zSteps}) const thisColor = color.map((c: number) => Math.pow((c/255), 2.2)) // Gamma correct the color @@ -173,7 +172,7 @@ export const ColumnMeshes = () => { } return meshes - },[timeSeries, plotType, remapTexture]) + },[timeSeries, plotType]) useEffect(() => { return () => { meshes.forEach(mesh => { diff --git a/src/components/plots/UVCube.tsx b/src/components/plots/UVCube.tsx index 2ef87494..718130c8 100644 --- a/src/components/plots/UVCube.tsx +++ b/src/components/plots/UVCube.tsx @@ -1,6 +1,6 @@ import * as THREE from 'three' import { useMemo, useState, useEffect, useRef } from 'react'; -import { parseUVCoords, getUnitAxis, GetTimeSeries, GetCurrentArray } from '@/utils/HelperFuncs'; +import { parseUVCoords, getUnitAxis, GetTimeSeries, GetCurrentArray, sample2D } from '@/utils/HelperFuncs'; import { useAnalysisStore } from '@/GlobalStates/AnalysisStore'; import { useGlobalStore } from '@/GlobalStates/GlobalStore'; import { usePlotStore } from '@/GlobalStates/PlotStore'; @@ -26,23 +26,6 @@ function updateFace( } } -function sample2D(tex: THREE.DataTexture, u:number, v:number): [THREE.Vector2, boolean] { - const { data, width, height } = tex.image; - if (!data) return [new THREE.Vector2(u, v), true]; - - const x = Math.floor(u * (width - 1)); - const y = Math.floor(v * (height - 1)); - - const idx = (y * width + x) * 4; // RGBA - const newU = THREE.DataUtils.fromHalfFloat(data[idx + 0]) - const newV = THREE.DataUtils.fromHalfFloat(data[idx + 1]) - const valid = THREE.DataUtils.fromHalfFloat(data[idx + 2]) - return [ - new THREE.Vector2(newU,newV), - valid > 0.5 - ]; -} - const UpdateUVs = ( geometry: THREE.BoxGeometry, @@ -75,7 +58,7 @@ const UpdateUVs = ( //Z- updateFace(20, {pos:xPos, scale:xScale}, {pos:yPos, scale:yScale}, uvs) - } +} export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ @@ -118,9 +101,8 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ function HandleTimeSeries(event: THREE.Intersection){ const uv = event.uv!; - let valid = true; - let newUV: THREE.Vector2 | undefined; const normal = event.normal!; + let newUV: THREE.Vector2 | undefined; if (remapTexture && Math.abs(normal.z) > 0.5){ // Get new UV if reprojected and along z Axis const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. if (flipY) thisUV.y = 1-thisUV.y @@ -135,10 +117,10 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ setDimCoords({}); } lastNormal.current = dimAxis; - const tempTS = valid ? GetTimeSeries( + const tempTS = GetTimeSeries( { data: analysisMode ? analysisArray : GetCurrentArray(), shape: dataShape, stride: strides }, { uv: newUV ?? uv, normal } - ) : [] + ) const plotDim = (normal.toArray()).map((val, idx) => { if (Math.abs(val) > 0) { return idx; @@ -146,7 +128,7 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ return null;}).filter(idx => idx !== null); setPlotDim(2-plotDim[0]) //I think this 2 is only if there are 3-dims. Need to rework the logic - const coordUV = valid ? parseUVCoords({normal:normal,uv}) : [null] + const coordUV = parseUVCoords({normal:normal,uv}) let dimCoords = coordUV.map((val,idx)=>val ? axisDimArrays[idx][Math.round(val*axisDimArrays[idx].length)] : null) const thisDimNames = axisDimNames.filter((_,idx)=> dimCoords[idx] !== null) const thisDimUnits = axisDimUnits.filter((_,idx)=> dimCoords[idx] !== null) @@ -156,7 +138,7 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ color:evaluate_cmap(getColorIdx()/10,"Paired"), normal, uv, - newUV, + newUV, // Delete this if never solve moving between projections. ATM is redundant data:tempTS } updateTimeSeries({ [tsID] : tsObj}) @@ -179,20 +161,24 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ updateDimCoords({[tsID] : dimObj}) } - // useEffect(()=>{ - // // This effect gets the reprojected UVs for all points after reprojecting - - // if (remapTexture){ - // const {timeSeries} = useGlobalStore.getState() - // for (const [tsID, tsObj] of Object.entries(timeSeries)){ - // const {uv} = tsObj - // const [thisUV, _isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Should always be valid as either was plotted from original CRS, or invalid are not added in new CRS - // if (flipY) thisUV.y = 1-thisUV.y - // const newTSObj = {...tsObj, newUV:thisUV} - // updateTimeSeries({ [tsID] : newTSObj}) - // } - // } - // },[remapTexture]) + useEffect(()=>{ + // This effect gets the reprojected UVs for all points after reprojecting to move columns when switching to projection + // I can't quite get it to work so I will just clear the timeSeries for now. + // if (remapTexture){ + // const newTimeSeries: Record> = {} + // const {timeSeries} = useGlobalStore.getState() + // for (const [tsID, tsObj] of Object.entries(timeSeries)){ + // const {uv} = tsObj + // const [thisUV, _isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Should always be valid as either was plotted from original CRS, or invalid are not added in new CRS + + // if (flipY) thisUV.y = 1-thisUV.y + // const newTSObj = {...tsObj, newUV:thisUV} + // newTimeSeries[tsID] = newTSObj + // } + // useGlobalStore.setState({timeSeries:newTimeSeries}) + // } + useGlobalStore.setState({timeSeries:{}, dimCoords:{}}) + },[remapTexture]) const {geometry, position} = useMemo(() => { const xScale = (xRange[1] - xRange[0])/2 diff --git a/src/utils/HelperFuncs.ts b/src/utils/HelperFuncs.ts index 2654fa91..96a2ef64 100644 --- a/src/utils/HelperFuncs.ts +++ b/src/utils/HelperFuncs.ts @@ -437,4 +437,22 @@ export function calculateStrides( return shape.reduce((a: number, b: number, i: number) => a * (i > idx ? b : 1), 1) }) return newStrides +} + +export function sample2D(tex: THREE.DataTexture, u:number, v:number): [THREE.Vector2, boolean] { + // Samples an array given UVs + const { data, width, height } = tex.image; + if (!data) return [new THREE.Vector2(u, v), true]; + + const x = Math.floor(u * (width - 1)); + const y = Math.floor(v * (height - 1)); + + const idx = (y * width + x) * 4; // RGBA + const newU = THREE.DataUtils.fromHalfFloat(data[idx + 0]) + const newV = THREE.DataUtils.fromHalfFloat(data[idx + 1]) + const valid = THREE.DataUtils.fromHalfFloat(data[idx + 2]) + return [ + new THREE.Vector2(newU,newV), + valid > 0.5 + ]; } \ No newline at end of file From 7ad2e62c9e9331fe114914f38f764bd65d4b3503 Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 11:45:55 +0200 Subject: [PATCH 4/8] Flatmap update --- package.json | 1 + src/components/plots/FlatMap.tsx | 97 +++++++++++++++++--------------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index e6a667ba..ba6bce39 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@ffmpeg/util": "^0.12.2", "@hookform/resolvers": "^5.2.2", "@mattnucc/gribberish": "^1.5.0", + "@mattnucc/gribberish-wasm32-wasi": "^1.5.0", "@monaco-editor/react": "^4.7.0", "@react-spring/three": "^10.0.0", "@react-three/drei": "^10.7.7", diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx index e5b4a5cd..3886ed2f 100644 --- a/src/components/plots/FlatMap.tsx +++ b/src/components/plots/FlatMap.tsx @@ -129,19 +129,18 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT return; } } - setLoc([e.clientX, e.clientY]); - const { x, y } = e.uv; + const { x, y } = uv; const zSliceIdx = dimSlices.length > 2 ? 2 : 1; const ySliceIdx = dimSlices.length > 2 ? 1 : 0; const xSize = isFlat ? (analysisMode ? analysisDims[1].length : dimSlices[1].length) : dimSlices[zSliceIdx].length; const ySize = isFlat ? (analysisMode ? analysisDims[0].length : dimSlices[0].length) : dimSlices[ySliceIdx].length; - const xIdx = Math.round(x*xSize-.5) - const yIdx = Math.round(y*ySize-.5) - let dataIdx = xSize * yIdx + xIdx; - dataIdx += isFlat ? 0 : Math.floor((dimSlices[0].length-1) * animProg) * xSize*ySize + const xId = Math.round(x*xSize-.5) + const yId = Math.round(y*ySize-.5) + let dataIdx = xSize * yId + xId; + dataIdx += isFlat ? 0 : Math.floor((dimSlices[zIdx].length-1) * animProg) * xSize*ySize const dataVal = sampleArray ? sampleArray[dataIdx] : 0; val.current = dataVal; coords.current = [y,x] @@ -150,45 +149,55 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT // ----- TIMESERIES ----- // function HandleTimeSeries(event: THREE.Intersection){ - const uv = event.uv; - const normal = new THREE.Vector3(0,0,1) - if(uv){ - const tsUV = flipY ? new THREE.Vector2(uv.x, 1-uv.y) : uv - const tempTS = GetTimeSeries({data:analysisMode ? analysisArray : GetCurrentArray(), shape:dataShape, stride:strides},{uv:tsUV,normal}) - setPlotDim(0) //I think this 2 is only if there are 3-dims. Need to rework the logic - - const coordUV = parseUVCoords({normal:normal,uv:uv}) - let dimCoords = coordUV.map((val,idx)=>val ? dimSlices[idx][Math.round(val*dimSlices[idx].length)] : null) - const thisDimNames = dimNames.filter((_,idx)=> dimCoords[idx] !== null) - const thisDimUnits = dimUnits.filter((_,idx)=> dimCoords[idx] !== null) - dimCoords = dimCoords.filter(val => val !== null) - const tsID = `${dimCoords[0]}_${dimCoords[1]}` - const tsObj = { - color: evaluateColorMap(getColorIdx() / 10, 'Paired'), - data: tempTS, - normal, - uv: tsUV, - } - incrementColorIdx(); - updateTimeSeries({ [tsID] : tsObj}) - const dimObj = { - first:{ - name:thisDimNames[0], - loc:dimCoords[0] ?? 0, - units:thisDimUnits[0] - }, - second:{ - name:thisDimNames[1], - loc:dimCoords[1] ?? 0, - units:thisDimUnits[1] - }, - plot:{ - units:dimUnits[0] - } - } - updateDimCoords({[tsID] : dimObj}) - } + const uv = event.uv; + if (!uv) return; + const tsUV = flipY ? new THREE.Vector2(uv.x, 1-uv.y) : uv + let newUV: THREE.Vector2 | undefined; + const normal = new THREE.Vector3(0,0,1) + if (remapTexture){ + const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + if (flipY) thisUV.y = 1-thisUV.y + if (isValid) newUV = thisUV; + else{ + return; } + } + + const tempTS = GetTimeSeries({data:analysisMode ? analysisArray : GetCurrentArray(), shape:dataShape, stride:strides},{uv:newUV ?? tsUV,normal}) + setPlotDim(0) //I think this 2 is only if there are 3-dims. Need to rework the logic + + const coordUV = parseUVCoords({normal:normal,uv:uv}) + let dimCoords = coordUV.map((val,idx)=>val ? dimSlices[idx][Math.round(val*dimSlices[idx].length)] : null) + const thisDimNames = dimNames.filter((_,idx)=> dimCoords[idx] !== null) + const thisDimUnits = dimUnits.filter((_,idx)=> dimCoords[idx] !== null) + dimCoords = dimCoords.filter(val => val !== null) + const tsID = `${dimCoords[0]}_${dimCoords[1]}` + const tsObj = { + color: evaluateColorMap(getColorIdx() / 10, 'Paired'), + data: tempTS, + normal, + uv: tsUV, + } + incrementColorIdx(); + updateTimeSeries({ [tsID] : tsObj}) + const dimObj = { + first:{ + name:thisDimNames[0], + loc:dimCoords[0] ?? 0, + units:thisDimUnits[0] + }, + second:{ + name:thisDimNames[1], + loc:dimCoords[1] ?? 0, + units:thisDimUnits[1] + }, + plot:{ + units:dimUnits[0] + } + } + updateDimCoords({[tsID] : dimObj}) + + } // ----- SHADER MATERIAL ----- // const shaderMaterial = useMemo(()=>new THREE.ShaderMaterial({ glslVersion: THREE.GLSL3, From 2e7ef7fac665b6dcf21abc8877b3241364edfc7c Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 14:15:23 +0200 Subject: [PATCH 5/8] Missing non-Z timeseries in new CRS --- src/components/plots/FlatMap.tsx | 7 +++-- src/components/plots/UVCube.tsx | 29 +++++++++++++------- src/components/plots/plotarea/FixedTicks.tsx | 12 ++++---- src/components/plots/plotarea/LinePlot.tsx | 12 ++++---- src/components/textures/ProjectionTexture.ts | 18 ++++++++++++ src/utils/HelperFuncs.ts | 18 ------------ 6 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx index 3886ed2f..2007141a 100644 --- a/src/components/plots/FlatMap.tsx +++ b/src/components/plots/FlatMap.tsx @@ -9,7 +9,8 @@ import { useZarrStore } from '@/GlobalStates/ZarrStore'; import { vertShader } from '@/components/computation/shaders' import { useShallow } from 'zustand/shallow' import { ThreeEvent } from '@react-three/fiber'; -import { coarsenFlatArray, GetCurrentArray, GetTimeSeries, parseUVCoords, deg2rad, sample2D } from '@/utils/HelperFuncs'; +import { coarsenFlatArray, GetCurrentArray, GetTimeSeries, parseUVCoords, deg2rad } from '@/utils/HelperFuncs'; +import { sampleCRS } from '../textures/ProjectionTexture'; import { evaluateColorMap } from '@/components/textures'; import { useCoordBounds } from '@/hooks/useCoordBounds'; import { flatFrag } from '../textures/shaders'; @@ -120,7 +121,7 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT if (!uv) return; eventRef.current = e; if (remapTexture){ - const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + const [thisUV, isValid] = sampleCRS(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. if (flipY) thisUV.y = 1-thisUV.y if (isValid) uv = thisUV; else{ @@ -155,7 +156,7 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT let newUV: THREE.Vector2 | undefined; const normal = new THREE.Vector3(0,0,1) if (remapTexture){ - const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + const [thisUV, isValid] = sampleCRS(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. if (flipY) thisUV.y = 1-thisUV.y if (isValid) newUV = thisUV; else{ diff --git a/src/components/plots/UVCube.tsx b/src/components/plots/UVCube.tsx index 2726b179..83fe585d 100644 --- a/src/components/plots/UVCube.tsx +++ b/src/components/plots/UVCube.tsx @@ -1,6 +1,7 @@ import * as THREE from 'three' import { useMemo, useState, useEffect, useRef } from 'react'; -import { parseUVCoords, getUnitAxis, GetTimeSeries, GetCurrentArray, sample2D } from '@/utils/HelperFuncs'; +import { parseUVCoords, getUnitAxis, GetTimeSeries, GetCurrentArray } from '@/utils/HelperFuncs'; +import { sampleCRS } from '../textures/ProjectionTexture'; import { useAnalysisStore } from '@/GlobalStates/AnalysisStore'; import { useGlobalStore } from '@/GlobalStates/GlobalStore'; import { usePlotStore } from '@/GlobalStates/PlotStore'; @@ -103,13 +104,20 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ const uv = event.uv!; const normal = event.normal!; let newUV: THREE.Vector2 | undefined; - if (remapTexture && Math.abs(normal.z) > 0.5){ // Get new UV if reprojected and along z Axis - const [thisUV, isValid] = sample2D(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. - if (flipY) thisUV.y = 1-thisUV.y - if (isValid) newUV = thisUV; - else{ - return; + if (remapTexture){ // Get new UV if reprojected and along z Axis + if (Math.abs(normal.z) > 0.5){ // If its along the Z just grab the full timeseries at new UV + const [thisUV, isValid] = sampleCRS(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. + if (flipY) thisUV.y = 1-thisUV.y + if (isValid) newUV = thisUV; + else{ + return; + } + } else if (Math.abs(normal.x) > 0.5){ // If along Either X or y, we need a new function to resample the timeseries into the new CRS + // For later + } else { + //for later } + } const dimAxis = getUnitAxis(normal); if (dimAxis != lastNormal.current){ @@ -147,17 +155,18 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{ first:{ name:thisDimNames[0], loc:dimCoords[0] ?? 0, - units:thisDimUnits[0] + units:thisDimUnits[0] ?? '' }, second:{ name:thisDimNames[1], loc:dimCoords[1] ?? 0, - units:thisDimUnits[1] + units:thisDimUnits[1] ?? '' }, plot:{ - units:axisDimUnits[2-plotDim[0]] + units:axisDimUnits[2-plotDim[0]] ?? '' } } + console.log(dimObj) updateDimCoords({[tsID] : dimObj}) } diff --git a/src/components/plots/plotarea/FixedTicks.tsx b/src/components/plots/plotarea/FixedTicks.tsx index 5234690a..1fcd3388 100644 --- a/src/components/plots/plotarea/FixedTicks.tsx +++ b/src/components/plots/plotarea/FixedTicks.tsx @@ -41,10 +41,10 @@ export function FixedTicks({ const { camera, size } = useThree() const initSize = useRef(size) const [bounds, setBounds] = useState({ left: 0, right: 0, top: 0, bottom: 0 }) - const {dimCoords, dimArrays, plotDim, valueScales} = useGlobalStore( + const {dimCoords, axisDimArrays, plotDim, valueScales} = useGlobalStore( useShallow(state=>({ dimCoords:state.dimCoords, - dimArrays:state.dimArrays, + axisDimArrays:state.axisDimArrays, plotDim:state.plotDim, valueScales:state.valueScales }))) @@ -57,11 +57,11 @@ export function FixedTicks({ const dimSlices = useMemo(() => { return [ - dimArrays[zIdx]?.slice(zSlice[0], zSlice[1] ? zSlice[1] : undefined) ?? [], - dimArrays[yIdx]?.slice(ySlice[0], ySlice[1] ? ySlice[1] : undefined) ?? [], - dimArrays[xIdx]?.slice(xSlice[0], xSlice[1] ? xSlice[1] : undefined) ?? [], + axisDimArrays[zIdx]?.slice(zSlice[0], zSlice[1] ? zSlice[1] : undefined) ?? [], + axisDimArrays[yIdx]?.slice(ySlice[0], ySlice[1] ? ySlice[1] : undefined) ?? [], + axisDimArrays[xIdx]?.slice(xSlice[0], xSlice[1] ? xSlice[1] : undefined) ?? [], ] - }, [dimArrays, xSlice, ySlice, zSlice, xIdx, yIdx, zIdx]) + }, [axisDimArrays, xSlice, ySlice, zSlice, xIdx, yIdx, zIdx]) const xDimArray = useMemo(() => dimSlices[plotDim], [dimSlices, plotDim]) const xTickCount = 10; const yTickCount = 8; diff --git a/src/components/plots/plotarea/LinePlot.tsx b/src/components/plots/plotarea/LinePlot.tsx index 7a0c356c..cac55f1a 100644 --- a/src/components/plots/plotarea/LinePlot.tsx +++ b/src/components/plots/plotarea/LinePlot.tsx @@ -19,12 +19,12 @@ interface pointInfo{ const MIN_HEIGHT = 10; function PointInfo({pointID,pointLoc,showPointInfo, plotUnits}:pointInfo){ - const {plotDim, dimArrays, dimNames, dimUnits, timeSeries} = useGlobalStore( + const {plotDim, axisDimArrays, axisDimNames, axisDimUnits, timeSeries} = useGlobalStore( useShallow(state=>({ plotDim:state.plotDim, - dimArrays:state.dimArrays, - dimNames:state.dimNames, - dimUnits:state.dimUnits, + axisDimArrays:state.axisDimArrays, + axisDimNames:state.axisDimNames, + axisDimUnits:state.axisDimUnits, timeSeries:state.timeSeries, })) ); @@ -33,7 +33,7 @@ function PointInfo({pointID,pointLoc,showPointInfo, plotUnits}:pointInfo){ if (Object.entries(pointID).length > 0 && Object.entries(timeSeries).length > 0){ const [tsID, idx] = pointID; pointY = timeSeries[tsID]['data'][idx]; - pointX = dimArrays[plotDim][idx]; + pointX = axisDimArrays[plotDim][idx]; } const [divX,divY] = pointLoc; @@ -59,7 +59,7 @@ function PointInfo({pointID,pointLoc,showPointInfo, plotUnits}:pointInfo){ }} > {`${pointY.toFixed(2)}${plotUnits??''}`}
- {`${dimNames[plotDim]}: ${parseLoc(pointX,dimUnits[plotDim])} + {`${axisDimNames[plotDim]}: ${parseLoc(pointX,axisDimUnits[plotDim])} `}
} diff --git a/src/components/textures/ProjectionTexture.ts b/src/components/textures/ProjectionTexture.ts index bd341e9b..259f7ca6 100644 --- a/src/components/textures/ProjectionTexture.ts +++ b/src/components/textures/ProjectionTexture.ts @@ -125,6 +125,24 @@ export function SetReprojectionTexture(dimArrays: Array[]){ useGlobalStore.setState({remapTexture}); } +export function sampleCRS(tex: THREE.DataTexture, u:number, v:number): [THREE.Vector2, boolean] { + // Samples an array given UVs + const { data, width, height } = tex.image; + if (!data) return [new THREE.Vector2(u, v), true]; + + const x = Math.floor(u * (width - 1)); + const y = Math.floor(v * (height - 1)); + + const idx = (y * width + x) * 4; // RGBA + const newU = THREE.DataUtils.fromHalfFloat(data[idx + 0]) + const newV = THREE.DataUtils.fromHalfFloat(data[idx + 1]) + const valid = THREE.DataUtils.fromHalfFloat(data[idx + 2]) + return [ + new THREE.Vector2(newU,newV), + valid > 0.5 + ]; +} + export function reproject(resolution: number = 256){ const {nativeCRS, destCRS, plotType} = usePlotStore.getState() if (!nativeCRS || !destCRS) return; // This shouldn't trigger as the button will be disabled for this same condition diff --git a/src/utils/HelperFuncs.ts b/src/utils/HelperFuncs.ts index 96a2ef64..2654fa91 100644 --- a/src/utils/HelperFuncs.ts +++ b/src/utils/HelperFuncs.ts @@ -437,22 +437,4 @@ export function calculateStrides( return shape.reduce((a: number, b: number, i: number) => a * (i > idx ? b : 1), 1) }) return newStrides -} - -export function sample2D(tex: THREE.DataTexture, u:number, v:number): [THREE.Vector2, boolean] { - // Samples an array given UVs - const { data, width, height } = tex.image; - if (!data) return [new THREE.Vector2(u, v), true]; - - const x = Math.floor(u * (width - 1)); - const y = Math.floor(v * (height - 1)); - - const idx = (y * width + x) * 4; // RGBA - const newU = THREE.DataUtils.fromHalfFloat(data[idx + 0]) - const newV = THREE.DataUtils.fromHalfFloat(data[idx + 1]) - const valid = THREE.DataUtils.fromHalfFloat(data[idx + 2]) - return [ - new THREE.Vector2(newU,newV), - valid > 0.5 - ]; } \ No newline at end of file From 9bee7a4654cce60470cd78f94953f8258097ca3e Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 14:17:11 +0200 Subject: [PATCH 6/8] package --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index ba6bce39..e6a667ba 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "@ffmpeg/util": "^0.12.2", "@hookform/resolvers": "^5.2.2", "@mattnucc/gribberish": "^1.5.0", - "@mattnucc/gribberish-wasm32-wasi": "^1.5.0", "@monaco-editor/react": "^4.7.0", "@react-spring/three": "^10.0.0", "@react-three/drei": "^10.7.7", From 7c0d69778f469939b7fa446199872cacd3d3ae63 Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 15:15:19 +0200 Subject: [PATCH 7/8] update coords even when invalid --- src/components/plots/FlatMap.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx index 2007141a..a60fb98b 100644 --- a/src/components/plots/FlatMap.tsx +++ b/src/components/plots/FlatMap.tsx @@ -127,6 +127,7 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT else{ val.current = NaN; setLoc([e.clientX, e.clientY]); + coords.current = [thisUV.y,thisUV.x] return; } } From ac799fba14a4dcc10fa583832bf0a5093c9cdac8 Mon Sep 17 00:00:00 2001 From: Jeran Date: Wed, 22 Jul 2026 15:15:55 +0200 Subject: [PATCH 8/8] redundancy --- src/components/plots/FlatMap.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx index a60fb98b..de2ff35c 100644 --- a/src/components/plots/FlatMap.tsx +++ b/src/components/plots/FlatMap.tsx @@ -119,6 +119,7 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT if (infoRef.current && e.uv) { let {uv} = e; if (!uv) return; + setLoc([e.clientX, e.clientY]); eventRef.current = e; if (remapTexture){ const [thisUV, isValid] = sampleCRS(remapTexture, uv.x, flipY ? 1-uv.y: uv.y) // Weird double flippiing of UVs with flipY. Has something to do with how projected data is done. @@ -126,13 +127,11 @@ const FlatMap = ({textures: propTextures, infoSetters} : {textures : THREE.DataT if (isValid) uv = thisUV; else{ val.current = NaN; - setLoc([e.clientX, e.clientY]); coords.current = [thisUV.y,thisUV.x] return; } } - setLoc([e.clientX, e.clientY]); - + const { x, y } = uv; const zSliceIdx = dimSlices.length > 2 ? 2 : 1; const ySliceIdx = dimSlices.length > 2 ? 1 : 0;