Skip to content
Merged
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
31 changes: 24 additions & 7 deletions src/components/CanvasGeometry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import { lightTheme } from '@/utils/levaTheme'
import { ArrayToTexture, DefaultCube } from './TextureMakers';
import { DataCube, PointCloud, UVCube } from './PlotObjects';
import { TimeSeries } from './TimeSeries';
import { GetColorMapTexture } from '@/utils/colormap';

console.log(DataCube)
const colormaps = ['viridis', 'plasma', 'inferno', 'magma', 'Accent', 'Blues',
'CMRmap', 'twilight', 'tab10', 'gist_earth', 'cividis',
'Spectral', 'gist_stern', 'gnuplot', 'gnuplot2', 'ocean', 'turbo',
'GnBu', 'afmhot', 'cubehelix', 'hot', 'spring','terrain', 'winter', 'Wistia',
]

const storeURL = "https://s3.bgc-jena.mpg.de:9000/esdl-esdc-v3.0.2/esdc-16d-2.5deg-46x72x1440-3.0.2.zarr"

Expand All @@ -23,7 +28,7 @@ interface TimeSeriesLocs{


export function CanvasGeometry() {
const { variable, plotter } = useControls({
const { variable, plotter, cmap } = useControls({
variable: {
value: "Default",
options: variables,
Expand All @@ -33,13 +38,25 @@ export function CanvasGeometry() {
value:"volume",
options:["volume","point-cloud"],
label:"Select Plot Style"
}
},
cmap: {
value: "Spectral",
options:colormaps,
label: 'ColorMap'
},
})

const [texture, setTexture] = useState<THREE.DataTexture | THREE.Data3DTexture | null>(null)
const [shape, setShape] = useState<THREE.Vector3 | THREE.Vector3>(new THREE.Vector3(2, 2, 2))
const [timeSeriesLocs,setTimeSeriesLocs] = useState<TimeSeriesLocs>({uv:new THREE.Vector2(.5,.5), normal:new THREE.Vector3(0,0,1)})
const [valueScales,setValueScales] = useState({maxVal:1,minVal:-1})
const [showTimeSeries,setShowTimeSeries] = useState<boolean>(false)
const [colormap,setColormap] = useState<THREE.DataTexture>(GetColorMapTexture())

useEffect(()=>{
setColormap(GetColorMapTexture(colormap,cmap));
},[cmap, colormap])


useEffect(() => {
if (variable != "Default") {
Expand All @@ -50,7 +67,7 @@ export function CanvasGeometry() {
data: result.data,
shape: result.shape
})
console.log(shape)
console.log(`logging the shape since we will want to use it in the future for 2D vs 3D actions ${shape}`)
if (texture instanceof THREE.DataTexture || texture instanceof THREE.Data3DTexture) {
setTexture(texture)
} else {
Expand Down Expand Up @@ -89,19 +106,19 @@ export function CanvasGeometry() {

<Center top position={[-1, 0, 1]}/>
{plotter == "volume" && <>
<DataCube volTexture={texture} shape={shape}/>
<DataCube volTexture={texture} shape={shape} colormap={colormap}/>
<mesh onClick={() => setShowTimeSeries(true)}>
<UVCube shape={shape} setTimeSeriesLocs={setTimeSeriesLocs}/>
</mesh>

</>}
{plotter == "point-cloud" && <PointCloud texture={texture} />}
{plotter == "point-cloud" && <PointCloud textures={{texture,colormap}} />}

<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} enablePan={false}/>
<Environment preset="city" />
{showTimeSeries && <>

<TimeSeries timeSeriesLocs={timeSeriesLocs} DSInfo={{variable:variable, storePath:storeURL}} scaling={valueScales}/>
<TimeSeries timeSeriesLocs={timeSeriesLocs} DSInfo={{variable:variable, storePath:storeURL}} scaling={{...valueScales,colormap}}/>
<Html
fullscreen
style={{
Expand Down
38 changes: 13 additions & 25 deletions src/components/PlotObjects.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useEffect, useMemo } from 'react'
import { useState, useRef } from 'react'
import { useMemo } from 'react'
import { useRef } from 'react'
import * as THREE from 'three'
import vertexShader from '@/utils/shaders/vertex.glsl';
import fragmentShader from '@/utils/shaders/fragment.glsl';
import { useControls } from 'leva';
import { GetColorMapTexture } from '@/utils/colormap';
import pointVert from '@/utils/shaders/pointVertex.glsl';
import pointFrag from '@/utils/shaders/pointFrag.glsl';

Expand All @@ -16,19 +15,21 @@ const colormaps = ['viridis', 'plasma', 'inferno', 'magma', 'Accent', 'Blues',

interface DataCubeProps {
volTexture: THREE.Data3DTexture | THREE.DataTexture | null,
shape : THREE.Vector3
shape : THREE.Vector3,
colormap: THREE.DataTexture

}

interface PCProps {
texture: THREE.Data3DTexture | THREE.DataTexture | null
texture: THREE.Data3DTexture | THREE.DataTexture | null,
colormap: THREE.DataTexture
}

export const DataCube = ({ volTexture, shape }: DataCubeProps ) => {
export const DataCube = ({ volTexture, shape, colormap }: DataCubeProps ) => {
const meshRef = useRef<THREE.Mesh>(null);
// const materialRef = useRef<THREE.ShaderMaterial | null>(null);
// const { invalidate } = useThree();
const [colormap,setColormap] = useState<THREE.DataTexture>(GetColorMapTexture())
const { threshold, steps, flip, cmap, xMax,xMin,yMax,yMin,zMax,zMin } = useControls({
const { threshold, steps, flip, xMax,xMin,yMax,yMin,zMax,zMin } = useControls({
threshold: {
value: 0, // Default value
min: 0, // Minimum value
Expand Down Expand Up @@ -116,10 +117,6 @@ export const DataCube = ({ volTexture, shape }: DataCubeProps ) => {
// Use geometry once, avoid recreating -- Using a sphere to avoid the weird angles you get with cube
const geometry = useMemo(() => new THREE.IcosahedronGeometry(4, 8), []);

useEffect(()=>{
setColormap(GetColorMapTexture(colormap,cmap));
},[cmap, colormap])

return (
<>
<mesh ref={meshRef} geometry={geometry}>
Expand Down Expand Up @@ -156,19 +153,13 @@ export const UVCube = ({shape,setTimeSeriesLocs} : {shape:THREE.Vector3, setTime

}

export const PointCloud = ({texture} : PCProps )=>{
const [colormap,setColormap] = useState<THREE.DataTexture>(GetColorMapTexture())
const {cmap,pointScale,scalePoints} = useControls(
{
cmap: {
value: "Spectral",
options:colormaps,
label: 'ColorMap'
},
export const PointCloud = ({textures} : {textures:PCProps} )=>{
const {texture, colormap } = textures;
const {pointScale,scalePoints} = useControls({
pointScale:{
value:1,
min:1,
max:20,
max:40,
step:1
},
scalePoints:{
Expand Down Expand Up @@ -242,9 +233,6 @@ export const PointCloud = ({texture} : PCProps )=>{
depthWrite: true,
});

useEffect(()=>{
setColormap(GetColorMapTexture(colormap,cmap));
},[cmap, colormap])
return (
<points geometry={geometry} material={shaderMaterial} />
);
Expand Down
1 change: 0 additions & 1 deletion src/components/TextureMakers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ function ArrayTo3D(array: Array){
export function ArrayToTexture(array: Array){
const shape = array.shape;
const [texture,scales] = shape.length == 3 ? ArrayTo3D(array) : ArrayTo2D(array);
console.log(scales)
return [texture, shape, scales];
}

Expand Down
119 changes: 110 additions & 9 deletions src/components/TimeSeries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import { Hud, OrthographicCamera, Text } from '@react-three/drei'
import { useMemo, useState, useEffect } from 'react';
import { GetTimeSeries } from './ZarrLoaderLRU';
import { useControls } from 'leva';
import vertexShader from '@/utils/shaders/LineVert.glsl'

interface timeSeriesLocs{
uv:THREE.Vector2;
Expand All @@ -18,7 +20,8 @@

interface scaling{
maxVal:number,
minVal:number
minVal:number,
colormap:THREE.DataTexture
}

interface AxisLabels{
Expand All @@ -30,20 +33,54 @@
//This function will take in some coords, get the timeseries from zarr loader and create a new THREE scene with a static camera. Need to create a graph basically
const {uv,normal} = timeSeriesLocs;
const {variable, storePath} = DSInfo;
const {maxVal,minVal} = scaling;
const {maxVal,minVal,colormap} = scaling;
const [timeSeries, setTimeSeries] = useState<number[]>([0]);
// const [xLabls,setXLabels] = useState();
const [yLabels, setYLabels] = useState<AxisLabels>();
const verticleScale = 2;
const verticalScale = 2;
const horizontalScale = 5;
const {width} = useControls({
width:{value:5,
min:1,
max:15,
step:1,
label:"Line Width"
}
})
const splineResolution = 3;

useEffect(() => {
if (uv && normal ) {
GetTimeSeries({ TimeSeriesObject: { uv, normal, variable, storePath } })
.then((data) => setTimeSeries(data.data as number[]));
}
}, [timeSeriesLocs, variable]);

Check warning on line 57 in src/components/TimeSeries.tsx

View workflow job for this annotation

GitHub Actions / core

React Hook useEffect has missing dependencies: 'normal', 'storePath', and 'uv'. Either include them or remove the dependency array

const material = new THREE.ShaderMaterial({
glslVersion: THREE.GLSL3,
uniforms: {
cmap:{value: colormap},
width: { value: width},
aspect: {value : window.innerWidth / window.innerHeight},
thickness:{value:width/100},
miter:{value:1},

},
vertexShader,
fragmentShader:`
out vec4 Color;
uniform sampler2D cmap;
varying float vNormed;

void main() {
vec4 texColor = texture(cmap, vec2(vNormed, 0.1));
texColor.a = 1.;
Color = texColor;
}
`,
depthWrite: false,
});

const lineObj = useMemo(() => {
//Need to convert whatever timeseries is into vectors. Depends on the camera and scene zoom.
//Currently this creates a new one each time coords changes. Will need to fix later
Expand All @@ -53,16 +90,78 @@
const vecs = [];
for (let i=0 ; i<size ; i++){
const x = i/size*horizontalScale-horizontalScale/2;
const y = (normed[i]-.5)*verticleScale;
const y = (normed[i]-.5)*verticalScale;
vecs.push(new THREE.Vector2(x,y))
}
const curve = new THREE.SplineCurve(vecs)
const points = curve.getPoints(vecs.length*3)
const points = curve.getPoints(vecs.length*splineResolution)
const geometry = new THREE.BufferGeometry().setFromPoints( points );
const material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth:5 });
const obj = new THREE.Line(geometry,material);
return obj;
},[timeSeries])

Check warning on line 102 in src/components/TimeSeries.tsx

View workflow job for this annotation

GitHub Actions / core

React Hook useMemo has missing dependencies: 'maxVal' and 'minVal'. Either include them or remove the dependency array

const geometry = useMemo(() => {
//Need to convert whatever timeseries is into vectors. Depends on the camera and scene zoom.
//Currently this creates a new one each time coords changes. Will need to fix later

const normed = timeSeries.map((i) => (i - minVal) / (maxVal - minVal));
const size = timeSeries.length;
const vecs = []
for (let i = 0; i < size; i++) {
const x = (i / (size - 1)) * horizontalScale - (horizontalScale /2);
const y = (normed[i] - 0.5) * verticalScale;
vecs.push(new THREE.Vector2(x,y)) // 3D points (z = 0 for 2D)
}

const curve = new THREE.SplineCurve(vecs)
const points = curve.getPoints(vecs.length*splineResolution)
const path = points.map((i)=>[i.x,i.y,0])

if (path.length < 2) return new THREE.BufferGeometry(); // Need at least 2 points

// Step 2: Duplicate vertices and compute attributes
const numPoints = path.length;
const positions = [];
const directions = [];
const previous = [];
const next = [];
const normValues = []
const indices = [];

for (let i = 0; i < numPoints; i++) {
const point = path[i];
const prevPoint = path[Math.max(0, i - 1)];
const nextPoint = path[Math.min(numPoints - 1, i + 1)];
// Duplicate vertices
positions.push(...point, ...point); // [x, y, z, x, y, z]
directions.push(1.0, -1.0);
previous.push(...prevPoint, ...prevPoint);
next.push(...nextPoint, ...nextPoint);
normValues.push(...Array(splineResolution*2).fill(normed[i]));

}

// Step 3: Create triangle indices
for (let i = 0; i < numPoints - 1; i++) {
const i0 = i * 2; // First vertex of current point (+1)
const i1 = i0 + 1; // Second vertex of current point (-1)
const i2 = i0 + 2; // First vertex of next point (+1)
const i3 = i0 + 3; // Second vertex of next point (-1)
indices.push(i0, i1, i2); // First triangle
indices.push(i1, i3, i2); // Second triangle
}

// Step 4: Create geometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('direction', new THREE.Float32BufferAttribute(directions, 1));
geometry.setAttribute('previous', new THREE.Float32BufferAttribute(previous, 3));
geometry.setAttribute('next', new THREE.Float32BufferAttribute(next, 3));
geometry.setAttribute('normed', new THREE.Float32BufferAttribute(normValues, 1));
geometry.setIndex(new THREE.Uint16BufferAttribute(indices, 1));
return geometry
},[timeSeries])

Check warning on line 164 in src/components/TimeSeries.tsx

View workflow job for this annotation

GitHub Actions / core

React Hook useMemo has missing dependencies: 'maxVal' and 'minVal'. Either include them or remove the dependency array
const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 5;

Expand All @@ -71,13 +170,13 @@
const xMax = timeSeries.length;
const xPos = [-horizontalScale/2,-horizontalScale/4,0,horizontalScale/4,horizontalScale/2];
let yMin = (Math.ceil(minVal)-minVal)/(maxVal-minVal);
yMin = (yMin-0.5)*verticleScale
yMin = (yMin-0.5)*verticalScale
let yMax = (Math.floor(maxVal)-minVal)/(maxVal-minVal);
yMax = (yMax-0.5)*verticleScale
yMax = (yMax-0.5)*verticalScale
const step = (maxVal-minVal)/3;
let yLabels = Array.from({ length: 3 }, (_, i) => Math.round(minVal + step * i))
let yPos = yLabels.map(val => {
return (((val-minVal)/(maxVal-minVal))-0.5)*verticleScale
return (((val-minVal)/(maxVal-minVal))-0.5)*verticalScale
})
yPos = [yMin, ...yPos.slice(1), yMax]
yLabels = [Math.ceil(minVal),...yLabels.slice(1), Math.floor(maxVal)]
Expand All @@ -88,7 +187,7 @@
positions:yPos,
labels:yLabels
})
},[maxVal,minVal,verticleScale])
},[maxVal,minVal,verticalScale])

Check warning on line 190 in src/components/TimeSeries.tsx

View workflow job for this annotation

GitHub Actions / core

React Hook useEffect has a missing dependency: 'timeSeries.length'. Either include it or remove the dependency array

return (
<>
Expand All @@ -105,6 +204,7 @@
zoom={1}
/>
<group position={[0,-1,0]}>
<mesh geometry={geometry} material={material} />
<primitive object={lineObj} />
<mesh position={[-2.5,0,0]}>
<boxGeometry args={[.02,2,1]} />
Expand All @@ -116,6 +216,7 @@
</mesh>
{yLabels && yLabels.labels.map((value,index)=>(
<Text
key={`y-${index}`}
position={[-2.6,yLabels.positions[index],0]}
color="black"
fontSize={.1}
Expand Down
Loading
Loading