Skip to content

Commit 897b36b

Browse files
committed
Added Axis lines. Culls with trimming volume. Will implement this on the point-cloud tomorrow.
1 parent bf30aee commit 897b36b

5 files changed

Lines changed: 204 additions & 31 deletions

File tree

src/components/plots/AxisLines.tsx

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"use cleint";
2+
3+
import { useGlobalStore, usePlotStore } from '@/utils/GlobalStates'
4+
import React, {useState, useMemo} from 'react'
5+
import { useShallow } from 'zustand/shallow'
6+
import { Text } from '@react-three/drei'
7+
import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js';
8+
import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
9+
import { LineMaterial } from 'three-stdlib';
10+
import { useFrame } from '@react-three/fiber';
11+
import { parseLoc } from '@/utils/HelperFuncs';
12+
13+
const HorizontalAxis = ({flipX, flipY}: {flipX: boolean, flipY: boolean}) =>{
14+
const {dimArrays, dimNames, dimUnits} = useGlobalStore(useShallow(state => ({
15+
dimArrays: state.dimArrays,
16+
dimNames: state.dimNames,
17+
dimUnits: state.dimUnits
18+
})))
19+
20+
const {xRange, yRange, zRange} = usePlotStore(useShallow(state => ({
21+
xRange: state.xRange,
22+
yRange: state.yRange,
23+
zRange: state.zRange
24+
})))
25+
26+
const dimLengths = [dimArrays[0].length, dimArrays[1].length, dimArrays[2].length]
27+
28+
const {shape} = useGlobalStore(useShallow(state => ({
29+
shape: state.shape
30+
})))
31+
const shapeRatio = shape.y/shape.x
32+
//@ts-expect-error The THREE people messed up their types in this component. It does take a string
33+
const lineMat = useMemo(()=>new LineMaterial({color: 'orange', linewidth: 5}),[])
34+
const dimResolution = 7;
35+
36+
const xLine = useMemo(()=> {
37+
const geom = new LineSegmentsGeometry().setPositions([xRange[0], 0, 0, xRange[1], 0, 0]);
38+
return new LineSegments2(geom, lineMat)},[xRange])
39+
40+
const yLine = useMemo(() =>{
41+
const geom = new LineSegmentsGeometry().setPositions([0, yRange[0]*shapeRatio, 0, 0, yRange[1]*shapeRatio, 0]);
42+
return new LineSegments2(geom, lineMat)},[yRange])
43+
44+
const zLine = useMemo(()=> {
45+
const geom = new LineSegmentsGeometry().setPositions([0, 0, zRange[0], 0, 0, zRange[1]]);
46+
return new LineSegments2(geom, lineMat)},[zRange])
47+
48+
const tickLine = useMemo(()=> {
49+
const geom = new LineSegmentsGeometry().setPositions([0, 0, 0, 0, 0, .05]);
50+
return new LineSegments2(geom, lineMat)},[])
51+
52+
53+
const dimScale = dimResolution/(dimResolution-1)
54+
const valDelta = 1/(dimResolution-1)
55+
56+
57+
return (
58+
<>
59+
{/* Horizontal Group */}
60+
<group position={[0, shapeRatio*yRange[0], 0]}>
61+
{/* X Group */}
62+
<group position={[0, 0, flipX ? zRange[0] : zRange[1]]}>
63+
<primitive key={'xLine'} object={xLine} />
64+
{Array(dimResolution).fill(null).map((_val,idx)=>(
65+
(((xRange[0] + 1)/2) <= (idx*dimScale)/dimResolution &&
66+
((xRange[1] + 1)/2) >= (idx*dimScale)/dimResolution)
67+
&&
68+
<group position={[-1 + idx*dimScale/(dimResolution/2), 0, 0]}>
69+
<primitive key={idx} object={tickLine.clone()} rotation={[0, flipX ? Math.PI : 0, 0]} />
70+
<Text
71+
key={`textX_${idx}`}
72+
anchorX={idx == 0 ? (flipX ? 'right' : 'left') : idx == dimResolution-1 ? (flipX ? 'left' : 'right') : 'center'}
73+
anchorY={'top'}
74+
fontSize={0.05}
75+
color={'black'}
76+
material-depthTest={false}
77+
rotation={[-Math.PI/2, 0, flipX ? Math.PI : 0]}
78+
position={[0, 0, flipX ? -0.05 :.05]}
79+
>{parseLoc(dimArrays[2][Math.floor((dimLengths[2]-1)*idx*valDelta)],dimUnits[2])}</Text>
80+
</group>
81+
))}
82+
<Text
83+
key={'xTitle'}
84+
anchorX={'center'}
85+
anchorY={'top'}
86+
fontSize={0.1}
87+
color={'black'}
88+
material-depthTest={false}
89+
rotation={[-Math.PI/2, 0, flipX ? Math.PI : 0]}
90+
position={[(xRange[0]+xRange[1])/2, 0, flipX ? -0.2 :.2]}
91+
>{dimNames[2]}</Text>
92+
</group>
93+
{/* Z Group */}
94+
<group position={[flipY ? xRange[1]: xRange[0], 0, 0]}>
95+
<primitive key={'zLine'} object={zLine} />
96+
{Array(dimResolution).fill(null).map((_val,idx)=>(
97+
(((zRange[0] + 1)/2) <= (idx*dimScale)/dimResolution &&
98+
((zRange[1] + 1)/2) >= (idx*dimScale)/dimResolution )
99+
&&
100+
<group position={[0, 0, -1 + idx*dimScale/(dimResolution/2)]}>
101+
<primitive key={idx} object={tickLine.clone()} rotation={[0, flipY ? Math.PI/2 : -Math.PI/2 , 0]} />
102+
<Text
103+
key={`textY_${idx}`}
104+
anchorX={idx == 0 ? (flipY ? 'right' : 'left') : idx == dimResolution-1 ? (flipY ? 'left' : 'right') : 'center'}
105+
anchorY={'top'}
106+
fontSize={0.04}
107+
color={'black'}
108+
material-depthTest={false}
109+
rotation={[-Math.PI/2, 0, flipY ? Math.PI/2 : -Math.PI/2]}
110+
position={[flipY ? 0.05 :-0.05, 0, 0]}
111+
>{parseLoc(dimArrays[0][Math.floor((dimLengths[0]-1)*idx*valDelta)],dimUnits[0])}</Text>
112+
</group>
113+
))}
114+
<Text
115+
key={'xTitle'}
116+
anchorX={'center'}
117+
anchorY={'top'}
118+
fontSize={0.1}
119+
color={'black'}
120+
material-depthTest={false}
121+
rotation={[-Math.PI/2, 0, flipY ? Math.PI/2 : -Math.PI/2]}
122+
position={[flipY ? 0.2 : -0.2, 0, (zRange[0]+zRange[1])/2]}
123+
>{dimNames[0]}</Text>
124+
</group>
125+
</group>
126+
127+
{/* Vertical Group */}
128+
<group position={[flipY ? xRange[0] : xRange[1], 0, flipX ? zRange[0] : zRange[1]]}>
129+
<primitive key={'yLine'} object={yLine} />
130+
{Array(dimResolution).fill(null).map((_val,idx)=>(
131+
(((yRange[0] + 1)/2) <= (idx*dimScale)/dimResolution &&
132+
((yRange[1] + 1)/2) >= (idx*dimScale)/dimResolution)
133+
&&
134+
<group position={[0, -shape.y/2 + idx*dimScale/(dimResolution/2)*shapeRatio, 0]}>
135+
<primitive key={idx} object={tickLine.clone()} rotation={[0, flipY ? -Math.PI/2 :Math.PI/2 , 0]} />
136+
<Text
137+
key={`text_${idx}`}
138+
anchorX={flipY ? flipX ? 'left' : 'right' : flipX ? 'right' : 'left'}
139+
anchorY={'middle'}
140+
fontSize={0.05}
141+
color={'black'}
142+
material-depthTest={false}
143+
rotation={[0, flipX ? Math.PI : 0, 0]}
144+
position={[flipY ? -0.07 : 0.07, 0, 0]}
145+
>{parseLoc(dimArrays[1][Math.floor((dimLengths[1]-1)*idx*valDelta)],dimUnits[1])}</Text>
146+
</group>
147+
))}
148+
<Text
149+
key={'xTitle'}
150+
anchorX={flipY ? flipX ? 'left' : 'right' : flipX ? 'right' : 'left'}
151+
anchorY={'middle'}
152+
fontSize={0.1}
153+
color={'black'}
154+
material-depthTest={false}
155+
rotation={[0, flipX ? Math.PI : 0, 0]}
156+
position={[flipY ? -0.25 : 0.25, (yRange[0]+yRange[1])/2, 0]}
157+
>{dimNames[1]}</Text>
158+
</group>
159+
</>
160+
)
161+
}
162+
163+
164+
export const AxisLines = () => {
165+
const [flipX, setFlipX] = useState<boolean>(false)
166+
const [flipY, setFlipY] = useState<boolean>(false)
167+
168+
useFrame(({camera})=>{
169+
const shouldFlipX = Math.abs(camera.rotation.z) > Math.PI / 2;
170+
if (flipX !== shouldFlipX) {
171+
setFlipX(shouldFlipX);
172+
}
173+
174+
const shouldFlipY =
175+
(camera.rotation.z > 0 && camera.rotation.x < 0) ||
176+
(camera.rotation.z <= 0 && camera.rotation.x > 0);
177+
if (flipY !== shouldFlipY){
178+
setFlipY(shouldFlipY);
179+
}
180+
})
181+
182+
return (
183+
<>
184+
<HorizontalAxis flipX={flipX} flipY={flipY} />
185+
</>
186+
)
187+
}
188+
189+

src/components/plots/Plot.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { OrbitControls } from '@react-three/drei';
22
import React, { useMemo, useRef, useState, useEffect } from 'react';
33
import * as THREE from 'three';
4-
import { PointCloud, UVCube, DataCube, FlatMap, Sphere, CountryBorders } from '@/components/plots';
4+
import { PointCloud, UVCube, DataCube, FlatMap, Sphere, CountryBorders, AxisLines } from '@/components/plots';
55
import { Canvas, invalidate } from '@react-three/fiber';
66
import { ArrayToTexture } from '@/components/textures';
77
import { ZarrDataset } from '../zarr/ZarrLoaderLRU';
@@ -219,6 +219,7 @@ const Plot = ({ZarrDS}:{ZarrDS: ZarrDataset}) => {
219219
{plotType == "volume" && show && <>
220220
<DataCube volTexture={texture}/>
221221
<UVCube ZarrDS={ZarrDS} />
222+
<AxisLines />
222223
</>}
223224
{plotType == "point-cloud" && show &&<>
224225
<PointCloud textures={{texture,colormap}} ZarrDS={ZarrDS}/>

src/components/plots/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export {ThickLine} from './plotarea/ThickLine'
88
export {PlotPoints} from './plotarea/PlotPoints'
99
export {FlatMap} from './FlatMap'
1010
export {Sphere} from './Sphere'
11-
export {CountryBorders} from './CountryBorders'
11+
export {CountryBorders} from './CountryBorders'
12+
export {AxisLines} from './AxisLines'

src/components/plots/plotarea/PlaneAxis.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/utils/HelperFuncs.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,24 @@ export function parseTimeUnit(units: string | undefined): number {
4444
return unitToMilliseconds[effectiveUnit];
4545
}
4646

47-
export function parseLoc(input:number, units: string | undefined) {
47+
export function parseLoc(input:number, units: string | undefined, verbose: boolean = false) {
4848
if (!units){
4949
return input
5050
}
5151
if (typeof(input) == 'bigint'){
5252
try{
5353
const scale = parseTimeUnit(units)
5454
const timeStamp = Number(input) * scale;
55+
const date = new Date(timeStamp);
56+
if (verbose) {
57+
return date.toDateString(); // e.g., "Mon Aug 18 2025"
58+
} else {
59+
const day = date.getDate();
60+
const month = date.getMonth() + 1; // Months are 0-indexed
61+
const year = date.getFullYear();
62+
return `${day}-${month}-${year}`; // e.g., "18-8-2025"
63+
}
64+
5565
return new Date(timeStamp).toDateString()
5666
}
5767
catch{

0 commit comments

Comments
 (0)