changed the orbital camera to allow for an offset without messing with the rotation, added a moisture heatmap, updated the bin3dvisualizer to look cleaner, re-implemented a new version of the dialog box that pops up when clicking a node

This commit is contained in:
csawatzky 2026-05-13 14:32:52 -06:00
parent 619f9b7bf4
commit f54d4b5375
15 changed files with 1349 additions and 214 deletions

View file

@ -33,6 +33,11 @@ interface Props {
* larger when initialRadius is large.
*/
maxRadius?: number;
/**
* used to control the offset of the target from the center that the camera rotates around
*/
offset?: { x: number; y: number; z: number };
viewOffset?: number;
/**
* Called when the camera is reset to its initial position.
* OrbitCameraControls manages the reset internally use this to
@ -48,6 +53,10 @@ export default function OrbitCameraControls(props: Props) {
clampVerticalRotation,
minPhi = 0.01,
maxPhi = Math.PI - 0.01,
offset,
viewOffset,
initialRadius,
maxRadius,
onReset,
} = props;
@ -55,9 +64,9 @@ export default function OrbitCameraControls(props: Props) {
const mouse = useRef(new Vector2());
const targetRef = useRef({
x: target?.x ?? 0,
y: target?.y ?? 0,
z: target?.z ?? 0,
x: (target?.x ?? 0) + (offset?.x ?? 0),
y: (target?.y ?? 0) + (offset?.y ?? 0),
z: (target?.z ?? 0) + (offset?.z ?? 0),
});
const isDragging = useRef(false);
@ -67,7 +76,7 @@ export default function OrbitCameraControls(props: Props) {
// Spherical coords
const spherical = useRef({
radius: props.initialRadius ?? 10,
radius: initialRadius ?? 10,
theta: 0, // horizontal angle
phi: Math.PI / 2, // vertical angle
});
@ -78,11 +87,11 @@ export default function OrbitCameraControls(props: Props) {
const updateCamera = () => {
const { radius, theta, phi } = spherical.current;
const x = radius * Math.sin(phi) * Math.sin(theta);
const y = radius * Math.cos(phi);
const z = radius * Math.sin(phi) * Math.cos(theta);
camera.position.set(
targetRef.current.x + x,
targetRef.current.y + y,
@ -93,19 +102,23 @@ export default function OrbitCameraControls(props: Props) {
targetRef.current.y,
targetRef.current.z
);
// Shift the projected image without affecting orbit
const { width, height } = gl.domElement.getBoundingClientRect();
(camera as any).setViewOffset(width, height, viewOffset ?? 0, 0, width, height);
};
// Hand the reset function to the caller so they can trigger it
// (e.g. from a button in CameraOverlay) without needing an external ref.
if (onReset) {
onReset(() => {
spherical.current.radius = props.initialRadius ?? 10;
spherical.current.radius = initialRadius ?? 10;
spherical.current.theta = 0;
spherical.current.phi = Math.PI / 2;
targetRef.current = {
x: target?.x ?? 0,
y: target?.y ?? 0,
z: target?.z ?? 0,
x: (target?.x ?? 0) + (offset?.x ?? 0),
y: (target?.y ?? 0) + (offset?.y ?? 0),
z: (target?.z ?? 0) + (offset?.z ?? 0),
};
updateCamera();
});
@ -200,7 +213,7 @@ export default function OrbitCameraControls(props: Props) {
spherical.current.radius += e.deltaY * 0.01;
spherical.current.radius = Math.max(
3,
Math.min(props.maxRadius ?? 30, spherical.current.radius)
Math.min(maxRadius ?? 30, spherical.current.radius)
);
updateCamera();

View file

@ -79,7 +79,7 @@ export default function BaseMesh(props: BaseMeshProps) {
};
return (
<group position={position} rotation={rotation}>
<group position={position} rotation={rotation} renderOrder={renderOrder}>
{/* Main surface */}
<mesh geometry={geometry} onClick={onClick} renderOrder={renderOrder}>
{buildMaterial()}