diff --git a/src/components/plots/DataCube.tsx b/src/components/plots/DataCube.tsx
index a5cf9c05..bc97a87a 100644
--- a/src/components/plots/DataCube.tsx
+++ b/src/components/plots/DataCube.tsx
@@ -8,6 +8,7 @@ import { invalidate, useFrame } from '@react-three/fiber';
import { deg2rad } from '@/utils/HelperFuncs';
import { useCoordBounds } from '@/hooks/useCoordBounds';
import { UVCube } from '@/components/plots'
+import { ColumnMeshes } from './TransectMeshes';
interface DataCubeProps {
volTexture: THREE.Data3DTexture[] | THREE.DataTexture[] | null,
@@ -110,6 +111,7 @@ export const DataCube = ({ volTexture }: DataCubeProps ) => {
})
return (
+
diff --git a/src/components/plots/FlatMap.tsx b/src/components/plots/FlatMap.tsx
index a5909fe1..9d86eca7 100644
--- a/src/components/plots/FlatMap.tsx
+++ b/src/components/plots/FlatMap.tsx
@@ -13,7 +13,7 @@ import { coarsenFlatArray, GetCurrentArray, GetTimeSeries, parseUVCoords, deg2ra
import { evaluate_cmap } from 'js-colormaps-es';
import { useCoordBounds } from '@/hooks/useCoordBounds';
import { GetFrag } from '../textures';
-
+import { SquareMeshes } from './TransectMeshes';
interface InfoSettersProps{
setLoc: React.Dispatch>;
setShowInfo: React.Dispatch>;
@@ -138,7 +138,9 @@ const FlatMap = ({textures, infoSetters} : {textures : THREE.DataTexture[] | THR
const tsID = `${dimCoords[0]}_${dimCoords[1]}`
const tsObj = {
color:evaluate_cmap(getColorIdx()/10,"Paired"),
- data:tempTS
+ data:tempTS,
+ normal,
+ uv: tsUV,
}
incrementColorIdx();
updateTimeSeries({ [tsID] : tsObj})
@@ -203,6 +205,7 @@ const FlatMap = ({textures, infoSetters} : {textures : THREE.DataTexture[] | THR
return (
<>
+
{
uniforms.maskValue.value = maskValue
}
}, [pointSize, colormap, cOffset, cScale, valueRange, scalePoints, scaleIntensity, animProg, timeScale, xRange, yRange, fillValue, zRange, maskValue, lonBounds, latBounds]);
- return (
-
-
-
+ const tsScale = dataShape[2]/500
+ return (
+
+
+
- );
+
+
+
+
+ );
}
\ No newline at end of file
diff --git a/src/components/plots/Sphere.tsx b/src/components/plots/Sphere.tsx
index 52464621..a9eb589e 100644
--- a/src/components/plots/Sphere.tsx
+++ b/src/components/plots/Sphere.tsx
@@ -8,7 +8,7 @@ import { parseUVCoords, GetTimeSeries, GetCurrentArray, deg2rad } from '@/utils/
import { evaluate_cmap } from 'js-colormaps-es';
import { useCoordBounds } from '@/hooks/useCoordBounds'
import { GetFrag, GetVert } from '../textures';
-
+import { SquareMeshes } from './TransectMeshes';
function XYZtoRemap(xyz : THREE.Vector3, latBounds: number[], lonBounds : number[]){
const lon = Math.atan2(xyz.z,xyz.x)
const lat = Math.asin(xyz.y);
@@ -153,7 +153,9 @@ export const Sphere = ({textures} : {textures: THREE.Data3DTexture[] | THREE.Dat
const tsID = `${dimCoords[0]}_${dimCoords[1]}`
const tsObj = {
color:evaluate_cmap(getColorIdx()/10,"Paired"),
- data:tempTS
+ data:tempTS,
+ normal,
+ uv: tsUV,
}
incrementColorIdx();
updateTimeSeries({ [tsID] : tsObj})
@@ -177,6 +179,9 @@ export const Sphere = ({textures} : {textures: THREE.Data3DTexture[] | THREE.Dat
return (
<>
+
+
+
selectTS && HandleTimeSeries(e)}/>
>
diff --git a/src/components/plots/TransectMeshes.tsx b/src/components/plots/TransectMeshes.tsx
new file mode 100644
index 00000000..5f35d051
--- /dev/null
+++ b/src/components/plots/TransectMeshes.tsx
@@ -0,0 +1,183 @@
+import React, {useEffect, useMemo} from 'react'
+import * as THREE from 'three'
+import { usePlotStore } from '@/GlobalStates/PlotStore'
+import { useGlobalStore } from '@/GlobalStates/GlobalStore'
+import { useShallow } from 'zustand/shallow'
+import { deg2rad, parseUVCoords } from '@/utils/HelperFuncs'
+import { useCoordBounds } from '@/hooks/useCoordBounds'
+
+function remapToXYZ(uv: THREE.Vector2, latBounds: number[], lonBounds: number[]): THREE.Vector3 {
+ const u = 1 - uv.x;
+ const v = uv.y;
+ const lon = u * (deg2rad(lonBounds[1]) - deg2rad(lonBounds[0])) + deg2rad(lonBounds[0]);
+ const lat = v * (deg2rad(latBounds[1]) - deg2rad(latBounds[0])) + deg2rad(latBounds[0]);
+ return new THREE.Vector3(
+ Math.cos(lat) * Math.cos(lon),
+ Math.sin(lat),
+ Math.cos(lat) * Math.sin(lon)
+ );
+}
+
+function normalToPos(uv: THREE.Vector2, normal:THREE.Vector3, ratios:{depthRatio:number, aspectRatio:number}): THREE.Vector3{
+ let posZ, posY, posX: number;
+ const {aspectRatio, depthRatio} = ratios;
+ if (Math.abs(normal.z) == 1){
+ const flip = normal.z < 0;
+ const x = flip ? (1-uv.x)-0.5: (uv.x-0.5)
+ posX = x*2;
+ posY = (uv.y-0.5)*2*aspectRatio;
+ posZ = 0;
+ } else if (Math.abs(normal.y) == 1){
+ const flip = normal.y > 0;
+ const y = flip ? (1-uv.y)-0.5: (uv.y-0.5)
+ posX = (uv.x-0.5)*2;
+ posY = 0;
+ posZ = y*Math.max(depthRatio,2);
+ } else {
+ const flip = normal.x > 0;
+ const x = flip ? (1-uv.x)-0.5: (uv.x-0.5)
+ posX = 0;
+ posY = (uv.y-0.5)*2*aspectRatio;
+ posZ = x*Math.max(depthRatio,2);
+ }
+ return new THREE.Vector3(posX, posY, posZ)
+}
+
+function normalToScale(normal:THREE.Vector3, ratios:{depthRatio:number, aspectRatio:number}, steps:{xSteps:number, ySteps:number, zSteps:number}){
+ let scaleZ, scaleY, scaleX: number;
+ const {xSteps,ySteps,zSteps} = steps;
+ const {aspectRatio, depthRatio} = ratios;
+ if (Math.abs(normal.z) == 1){
+ scaleX = 2/xSteps;
+ scaleY = 2*aspectRatio/ySteps;
+ scaleZ = Math.max(depthRatio,2);
+ } else if (Math.abs(normal.y) == 1){
+ scaleX = 2/xSteps;
+ scaleY = 2*aspectRatio;
+ scaleZ = 2*Math.max(depthRatio,2)/zSteps;
+ } else{
+ scaleX = 2;
+ scaleY = 2*aspectRatio/ySteps;
+ scaleZ = 2*Math.max(depthRatio,2)/zSteps;
+ }
+ return new THREE.Vector3(scaleX, scaleY, scaleZ);
+}
+
+export const SquareMeshes = () => {
+ const {timeSeries, dataShape, shape, flipY} = useGlobalStore(useShallow(state=>({
+ timeSeries:state.timeSeries,
+ dataShape: state.dataShape,
+ shape: state.shape, flipY:state.flipY
+ })))
+ const {plotType} = usePlotStore(useShallow(state=>({
+ plotType: state.plotType
+ })))
+ const {lonBounds, latBounds} = useCoordBounds()
+ const meshes: THREE.Mesh[] = useMemo(() =>{
+ const meshes = []
+ const dataLen = dataShape.length;
+ const xSteps = dataShape[dataLen-1];
+ const ySteps = dataShape[dataLen-2];
+ const normedXExtent = (lonBounds[1]-lonBounds[0])/360
+ const normedYExtent = (latBounds[1]-latBounds[0])/180
+ const isSphere = plotType == "sphere";
+ const aspect = shape.y/shape.x;
+ for (const [_tsID, tsObj] of Object.entries(timeSeries)){
+ const {normal, uv, color} = tsObj
+ if (normal.z != 1) break; // It should never be, but just in case, flat versions only do time. Skip all of these.
+ let geometry = new THREE.PlaneGeometry(1, 1)
+ // Color from 0-255 to 0-1 range
+ const thisColor = color.map((c: number) => Math.pow((c/255), 2.2)) // Gamma correct the color
+ const material = new THREE.MeshBasicMaterial({color: new THREE.Color(...thisColor)});
+ material.side = THREE.DoubleSide; // For flipY or to see it on otherside of sphere after clipping values
+ material.needsUpdate = true;
+ const mesh = new THREE.Mesh(geometry, material)
+ let position: THREE.Vector3;
+ const uvX = (Math.floor(uv.x * xSteps)+0.5)/xSteps;
+ const uvY = (Math.floor(uv.y * ySteps)+0.5)/ySteps;
+ if (isSphere){
+ const circum = 2*Math.PI;
+ const xScale = circum/xSteps * normedXExtent;
+ const yScale = circum/2/ySteps * normedYExtent;
+ const xScaler = Math.cos((uvY - 0.5) * Math.PI);
+ position = remapToXYZ(new THREE.Vector2(uvX, uvY), latBounds, lonBounds)
+ // Rotate the plane where position is also normal vector
+ mesh.lookAt(position.x, position.y, position.z)
+ geometry.scale(xScale*xScaler, yScale, 1)
+ }
+ else{
+ const sqScale = 2/xSteps
+ const posX = (uvX-0.5)*2;
+ const posY = (uvY-0.5)*2*aspect;
+ position = new THREE.Vector3(posX, posY, 0.001)
+ geometry.scale(sqScale,sqScale,1)
+ }
+ mesh.position.set(position.x, position.y, position.z)
+ meshes.push(mesh)
+ }
+ return meshes
+ }, [timeSeries, plotType, latBounds, lonBounds])
+ useEffect(() => {
+ return () => {
+ meshes.forEach(mesh => {
+ mesh.geometry.dispose()
+ //@ts-ignore TS thiunks this is a different material type
+ mesh.material.dispose()
+ });
+ };}, [meshes]
+ );
+ return (
+ <>
+ {meshes.map((mesh, idx) => )}
+ >
+ )
+}
+
+export const ColumnMeshes = () => {
+ const {timeSeries, dataShape, shape} = useGlobalStore(useShallow(state=>({
+ timeSeries:state.timeSeries,
+ dataShape: state.dataShape,
+ shape: state.shape
+ })))
+ const {plotType} = usePlotStore(useShallow(state=>({
+ plotType: state.plotType
+ })))
+
+ 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]
+ 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
+ const material = new THREE.MeshBasicMaterial({color: new THREE.Color(...thisColor)})
+ let geometry = new THREE.BoxGeometry(1,1,1)
+ geometry.scale(...meshScale.toArray())
+ const mesh = new THREE.Mesh(geometry, material)
+ mesh.position.copy(position)
+ meshes.push(mesh)
+ }
+ return meshes
+
+ },[timeSeries, plotType])
+ useEffect(() => {
+ return () => {
+ meshes.forEach(mesh => {
+ mesh.geometry.dispose()
+ //@ts-ignore TS thiunks this is a different material type
+ mesh.material.dispose()
+ });
+ };}, [meshes]
+ );
+ return (
+ <>
+ {meshes.map((mesh, idx) => )}
+ >
+ )
+}
diff --git a/src/components/plots/UVCube.tsx b/src/components/plots/UVCube.tsx
index c31db838..301caa68 100644
--- a/src/components/plots/UVCube.tsx
+++ b/src/components/plots/UVCube.tsx
@@ -122,6 +122,8 @@ export const UVCube = ( {scale} : {scale?:THREE.Vector3} )=>{
const tsID = `${dimCoords[0]}_${dimCoords[1]}`
const tsObj = {
color:evaluate_cmap(getColorIdx()/10,"Paired"),
+ normal,
+ uv,
data:tempTS
}
updateTimeSeries({ [tsID] : tsObj})
diff --git a/src/components/textures/shaders/pointVertex.glsl b/src/components/textures/shaders/pointVertex.glsl
index 738c2842..b5108cdf 100644
--- a/src/components/textures/shaders/pointVertex.glsl
+++ b/src/components/textures/shaders/pointVertex.glsl
@@ -33,7 +33,7 @@ vec3 computePosition(int vertexID) {
float px = (float(x) - (float(width)/2.)) / 500.;
float py = (float(y) - (float(height)/2.)) / 500.;
- float pz = (float(z) - (float(depth )/2.)) /500.;
+ float pz = (float(z) - (float(depth)/2.)) /500.;
return vec3(px * 2.0, py * 2.0, pz * 2.0);
}
@@ -62,12 +62,11 @@ vec2 realCoords(vec2 uv){
}
void main() {
- vec2 testV = giveUV(gl_VertexID);
- if (maskValue != 0 ){
+ if (maskValue != 0 ){ // If using a mask, quick check if vertex is masked out before doing additional rendering
vec2 newV = realCoords(giveUV(gl_VertexID));
float mask = texture(maskTexture, newV).r;
bool cond = maskValue == 1 ? mask< 0.5 : mask>=0.5;
- if (cond){
+ if (cond){ // Masked out. Move off screen
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
return;
}
@@ -115,8 +114,5 @@ void main() {
if (xCheck || zCheck || yCheck || fillCheck){ //Hide points that are clipped
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
}
-
-
-
}
diff --git a/src/utils/ExportCanvas.tsx b/src/utils/ExportCanvas.tsx
index c9bb1603..ca963885 100644
--- a/src/utils/ExportCanvas.tsx
+++ b/src/utils/ExportCanvas.tsx
@@ -12,8 +12,6 @@ import { lerp } from 'three/src/math/MathUtils.js';
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { deg2rad } from './HelperFuncs';
-
-
const DrawText = (
//Context and cbarlocs
ctx: CanvasRenderingContext2D,
diff --git a/src/utils/HelperFuncs.ts b/src/utils/HelperFuncs.ts
index f18a7ea4..e3df047a 100644
--- a/src/utils/HelperFuncs.ts
+++ b/src/utils/HelperFuncs.ts
@@ -130,6 +130,8 @@ export function parseUVCoords({normal,uv}:{normal:THREE.Vector3,uv:THREE.Vector2
return [uv.x, flipY ? 1-uv.y : uv.y, null]
case normal.y === 1:
return [1-uv.y, null, uv.x]
+ case normal.y === -1:
+ return [uv.y, null, uv.x]
default:
return [0,0,0]
}