Skip to content

Commit

Permalink
Feature - WIP boxes (#28)
Browse files Browse the repository at this point in the history
* Adds boxes visual

* Updates

* Updates for wip anims
  • Loading branch information
dcyoung authored Feb 12, 2024
1 parent 184317f commit c40a9b6
Show file tree
Hide file tree
Showing 21 changed files with 327 additions and 116 deletions.
4 changes: 2 additions & 2 deletions app/src/components/canvas/Visual3D.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { APPLICATION_MODE } from "@/lib/applicationModes";
import { OrbitControls } from "@react-three/drei";
import { Canvas, useFrame, useThree } from "@react-three/fiber";

import { MaybePaletteTracker } from "./paletteTracker";
import { PaletteTracker } from "./paletteTracker";

const VisualizerComponent = ({
mode,
Expand Down Expand Up @@ -94,7 +94,7 @@ const Visual3DCanvas = ({
<VisualizerComponent mode={mode} />
{/* <Stats /> */}
<CameraControls />
<MaybePaletteTracker />
<PaletteTracker />
</Canvas>
);
};
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/canvas/common.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useVisualContext } from "@/context/visual";
import { usePalette } from "@/lib/appState";
import { ColorPalette } from "@/lib/palettes";

const useBackgroundColor = () => {
const { palette, colorBackground } = useVisualContext();
const { colorBackground } = useVisualContext();
const palette = usePalette();
return colorBackground
? ColorPalette.getPalette(palette).calcBackgroundColor(0)
: "#010204";
Expand Down
42 changes: 13 additions & 29 deletions app/src/components/canvas/paletteTracker.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
import { useState } from "react";
import { useVisualContext, useVisualContextSetters } from "@/context/visual";
import { useEnergyInfo } from "@/lib/appState";
import { useVisualContext } from "@/context/visual";
import { ScalarMovingAvgEventDetector } from "@/lib/analyzers/eventDetector";
import { useAppStateActions, useEnergyInfo } from "@/lib/appState";
import { type IScalarTracker } from "@/lib/mappers/valueTracker/common";
import { EnergyTracker } from "@/lib/mappers/valueTracker/energyTracker";
import { AVAILABLE_COLOR_PALETTES } from "@/lib/palettes";
import { useFrame } from "@react-three/fiber";

const PaletteUpdater = ({
scalarTracker,
}: {
scalarTracker: IScalarTracker;
}) => {
const threshold = 0.5;
const frameSpan = 10;
const { setPalette } = useVisualContextSetters();
const [movingAvg, setMovingAvg] = useState(0);
const { paletteTrackEnergy: enabled } = useVisualContext();
const detector = new ScalarMovingAvgEventDetector(0.5, 50, 500);
const { nextPalette } = useAppStateActions();

useFrame(() => {
const curr = scalarTracker.getNormalizedValue();
if (movingAvg < threshold && curr > threshold) {
setPalette((prev) => {
const currIdx = AVAILABLE_COLOR_PALETTES.indexOf(prev) ?? 0;
const nextIdx = (currIdx + 1) % AVAILABLE_COLOR_PALETTES.length;
return AVAILABLE_COLOR_PALETTES[nextIdx];
});
setMovingAvg(1);
} else {
setMovingAvg((prev) => {
return (prev * (frameSpan - 1) + curr) / frameSpan;
});
if (!enabled) {
return;
}

if (detector.step(scalarTracker.getNormalizedValue())) {
nextPalette();
}
});

return <></>;
};

const PaletteTracker = () => {
export const PaletteTracker = () => {
const energyInfo = useEnergyInfo();
const scalarTracker = new EnergyTracker(energyInfo);

return <PaletteUpdater scalarTracker={scalarTracker} />;
};

export const MaybePaletteTracker = () => {
const { paletteTrackEnergy } = useVisualContext();
if (!paletteTrackEnergy) {
return null;
}
return <PaletteTracker />;
};
9 changes: 7 additions & 2 deletions app/src/components/controls/visualSettingsSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Switch } from "@/components/ui/switch";
import { useModeContext } from "@/context/mode";
import { useVisualContext, useVisualContextSetters } from "@/context/visual";
import { APPLICATION_MODE } from "@/lib/applicationModes";
import { useAppStateActions, usePalette } from "@/lib/appState";
import {
AVAILABLE_COLOR_PALETTES,
ColorPalette,
Expand Down Expand Up @@ -67,6 +68,7 @@ const VisualSettingsControls = () => {
case "diffusedRing":
return DiffusedRingVisualSettingsControls();
case "dna":
case "boxes":
return null;
default:
return visual satisfies never;
Expand All @@ -76,9 +78,12 @@ const VisualSettingsControls = () => {
export const VisualSettingsSheet = ({ children }: PropsWithChildren) => {
const [open, setOpen] = useState(false);
const { mode } = useModeContext();
const { colorBackground, palette, paletteTrackEnergy } = useVisualContext();
const { setColorBackground, setPalette, setPaletteTrackEnergy } =
const { colorBackground, paletteTrackEnergy } = useVisualContext();
const { setColorBackground, setPaletteTrackEnergy } =
useVisualContextSetters();
const palette = usePalette();
const { setPalette } = useAppStateActions();

return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>{children}</SheetTrigger>
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/controls/visualsDock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type VisualType,
} from "@/components/visualizers/common";
import { useVisualContext, useVisualContextSetters } from "@/context/visual";
import { Box, CircleDashed, Dna, Globe, Grid3x3 } from "lucide-react";
import { Box, Boxes, CircleDashed, Dna, Globe, Grid3x3 } from "lucide-react";

import { Dock, DockItem, DockNav } from "./dock";

Expand All @@ -20,6 +20,8 @@ const VisualIcon = ({ visual }: { visual: VisualType }) => {
return <CircleDashed />;
case "dna":
return <Dna />;
case "boxes":
return <Boxes />;
default:
return visual satisfies never;
}
Expand Down
8 changes: 5 additions & 3 deletions app/src/components/visualizers/audioScope/reactive.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useEffect } from "react";
import { useVisualContext, useVisualContextSetters } from "@/context/visual";
import { useVisualContextSetters } from "@/context/visual";
import { useAppStateActions, usePalette } from "@/lib/appState";
import { ColorPalette } from "@/lib/palettes";

import BaseScopeVisual, { type TextureMapper } from "./base";

const ScopeVisual = ({ textureMapper }: { textureMapper: TextureMapper }) => {
const { palette } = useVisualContext();
const { setColorBackground, setPalette } = useVisualContextSetters();
const palette = usePalette();
const { setPalette } = useAppStateActions();
const { setColorBackground } = useVisualContextSetters();
const color = ColorPalette.getPalette(palette).lerpColor(0.5);
const usePoints = true;

Expand Down
118 changes: 118 additions & 0 deletions app/src/components/visualizers/boxes/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { useEffect, useMemo, useRef } from "react";
import { ScalarMovingAvgEventDetector } from "@/lib/analyzers/eventDetector";
import { usePalette } from "@/lib/appState";
import { type IScalarTracker } from "@/lib/mappers/valueTracker/common";
import { ColorPalette } from "@/lib/palettes";
import { useFrame } from "@react-three/fiber";
import {
BoxGeometry,
Matrix4,
MeshBasicMaterial,
type InstancedMesh,
} from "three";

const BaseBoxes = ({
scalarTracker,
nBoxes = 5,
gridSize = 10,
cellSize = 0.25,
}: {
scalarTracker: IScalarTracker;
nBoxes?: number;
gridSize?: number;
cellSize?: number;
}) => {
const rotateDurationMs = 250;
const nRows = gridSize;
const nCols = gridSize;
const detector = useMemo(
() => new ScalarMovingAvgEventDetector(0.65, 150, 2 * rotateDurationMs),
[rotateDurationMs],
);
const meshRef = useRef<InstancedMesh>(null!);
const tmpMatrix = useMemo(() => new Matrix4(), []);
const palette = usePalette();
const lut = ColorPalette.getPalette(palette).buildLut();

const cellAssignments = useMemo(
() =>
Array.from({ length: nBoxes }, (_) => {
const row = Math.floor(nRows * Math.random());
const col = Math.floor(nCols * Math.random());
return {
fromRow: row,
fromCol: col,
toRow: row,
toCol: col,
};
}),
[nBoxes, nRows, nCols],
);

// Recolor;
useEffect(() => {
for (let instanceIdx = 0; instanceIdx < nBoxes; instanceIdx++) {
meshRef.current.setColorAt(
instanceIdx,
lut.getColor(instanceIdx / (nBoxes - 1)),
);
}
meshRef.current.instanceColor!.needsUpdate = true;
}, [lut, nBoxes]);

useFrame(() => {
if (detector.step(scalarTracker?.getNormalizedValue() ?? 0)) {
// random jitter
const rowJitter = Math.floor(Math.random() * 3) - 1;
const colJitter = Math.floor(Math.random() * 3) - 1;
for (let i = 0; i < nBoxes; i++) {
cellAssignments[i].fromRow = cellAssignments[i].toRow;
cellAssignments[i].fromCol = cellAssignments[i].toCol;
cellAssignments[i].toRow += (Math.random() > 0.5 ? 1 : -1) * colJitter;
cellAssignments[i].toCol += (Math.random() > 0.5 ? 1 : -1) * rowJitter;
}
}

const alpha = Math.min(
1,
Math.max(0, detector.timeSinceLastEventMs / rotateDurationMs),
);

let normCubeX, normCubeY, x, y, z;
cellAssignments.forEach(
({ fromRow, fromCol, toRow, toCol }, instanceIdx) => {
const row = fromRow + alpha * (toRow - fromRow);
const col = fromCol + alpha * (toCol - fromCol);

// Find a random cell
normCubeX = row / (nRows - 1);
normCubeY = col / (nCols - 1);

x = nRows * cellSize * (normCubeX - 0.5);
y = nCols * cellSize * (normCubeY - 0.5);
z = 0;
// Position
tmpMatrix.setPosition(x, y, z);

meshRef.current.setMatrixAt(instanceIdx, tmpMatrix);
},
);

// Update the instance
meshRef.current.instanceMatrix.needsUpdate = true;
});

return (
<instancedMesh
ref={meshRef}
castShadow={true}
receiveShadow={true}
args={[new BoxGeometry(), new MeshBasicMaterial(), nBoxes]}
>
<boxGeometry attach="geometry" args={[cellSize, cellSize, cellSize, 1]} />
<meshBasicMaterial attach="material" color={"white"} toneMapped={false} />
</instancedMesh>
);
};

export default BaseBoxes;
29 changes: 29 additions & 0 deletions app/src/components/visualizers/boxes/reactive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { type VisualProps } from "@/components/visualizers/common";
import Ground from "@/components/visualizers/ground";
import { Vector3 } from "three";

import BaseBoxes from "./base";

const BoxesVisual = ({ scalarTracker }: VisualProps) => {
const nBoxes = 100;
const gridSize = 100;
const cellSize = 0.25;

return (
<>
<BaseBoxes
scalarTracker={
scalarTracker ?? {
getNormalizedValue: () => Math.sin(0.0025 * Date.now()) + 1,
}
}
nBoxes={nBoxes}
gridSize={gridSize}
cellSize={cellSize}
/>
<Ground position={new Vector3(0, 0, -cellSize / 2)} />
</>
);
};

export default BoxesVisual;
1 change: 1 addition & 0 deletions app/src/components/visualizers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const AVAILABLE_VISUALS = [
"cube",
"diffusedRing",
"dna",
"boxes",
// "stencil",
// "swarm",
] as const;
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/visualizers/cube/base.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef } from "react";
import { useVisualContext } from "@/context/visual";
import { usePalette } from "@/lib/appState";
import {
COORDINATE_TYPE,
HALF_DIAGONAL_UNIT_SQUARE,
Expand Down Expand Up @@ -32,7 +32,7 @@ const BaseCube = ({
const inputCoordinateType = volume
? COORDINATE_TYPE.CARTESIAN_3D
: COORDINATE_TYPE.CARTESIAN_CUBE_FACES;
const { palette } = useVisualContext();
const palette = usePalette();
const lut = ColorPalette.getPalette(palette).buildLut();

// Recolor
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/visualizers/dna/base.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef, useEffect, useMemo, useRef } from "react";
import { useVisualContext } from "@/context/visual";
import { usePalette } from "@/lib/appState";
import {
COORDINATE_TYPE,
TWO_PI,
Expand Down Expand Up @@ -91,7 +91,7 @@ const BaseDoubleHelix = forwardRef<
},
ref,
) => {
const { palette } = useVisualContext();
const palette = usePalette();
const lut = ColorPalette.getPalette(palette).buildLut();
const nBasePairs = Math.floor(helixLength / baseSpacing);
const refBaseMesh = useRef<InstancedMesh>(null!);
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/visualizers/grid/base.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef } from "react";
import { useVisualContext } from "@/context/visual";
import { usePalette } from "@/lib/appState";
import {
COORDINATE_TYPE,
type ICoordinateMapper,
Expand Down Expand Up @@ -28,7 +28,7 @@ const BaseGrid = ({
}) => {
const meshRef = useRef<InstancedMesh>(null!);
const tmpMatrix = useMemo(() => new Matrix4(), []);
const { palette } = useVisualContext();
const palette = usePalette();
const lut = ColorPalette.getPalette(palette).buildLut();

// Recolor
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/visualizers/sphere/base.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef } from "react";
import { useVisualContext } from "@/context/visual";
import { usePalette } from "@/lib/appState";
import {
COORDINATE_TYPE,
TWO_PI,
Expand Down Expand Up @@ -29,9 +29,9 @@ const BaseSphere = ({
nPoints?: number;
cubeSideLength?: number;
}) => {
const palette = usePalette();
const meshRef = useRef<InstancedMesh>(null!);
const tmpMatrix = useMemo(() => new Matrix4(), []);
const { palette } = useVisualContext();
const lut = ColorPalette.getPalette(palette).buildLut();
useEffect(() => {
for (let i = 0; i < nPoints; i++) {
Expand Down
1 change: 1 addition & 0 deletions app/src/components/visualizers/visualizerAudio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const AudioVisual = ({ visual }: { visual: VisualType }) => {

const coordinateMapper = new CoordinateMapper_Data(amplitude, freqData);
const energyTracker = new EnergyTracker(energyInfo);

const VisualComponent = useMemo(
() =>
lazy(
Expand Down
Loading

0 comments on commit c40a9b6

Please sign in to comment.