Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(visualizer): randomize sinusoid amplitudes #1169

Merged
merged 8 commits into from
Feb 22, 2024
18 changes: 12 additions & 6 deletions client/src/features/visualizer-threejs/Emitter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { RefObject, Dispatch, SetStateAction, useEffect, useRef, useLayou
import * as THREE from "three";
import { useConfigStore, useTangleStore } from "./store";
import { useRenderTangle } from "./useRenderTangle";
import { getTangleDistances, getEmitterPositions, generateRandomPeriods } from "./utils";
import { getTangleDistances, getEmitterPositions, generateRandomPeriods, generateRandomAmplitudes } from "./utils";
import { CanvasElement } from "./enums";
import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer";
import { EMITTER_DEPTH, EMITTER_HEIGHT, EMITTER_WIDTH } from "./constants";
Expand All @@ -30,15 +30,20 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte

const sinusoidPeriodsSum = useConfigStore((state) => state.sinusoidPeriodsSum);
const setSinusoidPeriodsSum = useConfigStore((state) => state.setSinusoidPeriodsSum);
const randomizedSinusoidPeriods = useConfigStore((state) => state.sinusoidRandomPeriods);
const setRandomizedSinusoidPeriods = useConfigStore((state) => state.setSinusoidRandomPeriods);
const sinusoidRandomPeriods = useConfigStore((state) => state.sinusoidRandomPeriods);
const setSinusoidRandomPeriods = useConfigStore((state) => state.setSinusoidRandomPeriods);

const randomSinusoidAmplitudes = useConfigStore((state) => state.randomSinusoidAmplitudes);
const setRandomSinusoidAmplitudes = useConfigStore((state) => state.setRandomSinusoidAmplitudes);

const tangleWrapperRef = useRef<THREE.Mesh | null>(null);

useLayoutEffect(() => {
const { periods, sum: periodsSum } = generateRandomPeriods();
setRandomizedSinusoidPeriods(periods);
const amplitudes = generateRandomAmplitudes();
setSinusoidRandomPeriods(periods);
setSinusoidPeriodsSum(periodsSum);
setRandomSinusoidAmplitudes(amplitudes);
}, []);

useEffect(() => {
Expand All @@ -65,8 +70,9 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
const currentAnimationTime = getVisualizerTimeDiff();
const { x, y } = getEmitterPositions({
currentAnimationTime,
periods: randomizedSinusoidPeriods,
periods: sinusoidRandomPeriods,
periodsSum: sinusoidPeriodsSum,
sinusoidAmplitudes: randomSinusoidAmplitudes,
});

if (isPlaying) {
Expand Down Expand Up @@ -99,7 +105,7 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
{/* Emitter Mesh */}
<mesh ref={emitterRef} name={CanvasElement.EmitterMesh} position={[0, 0, 0]}>
<boxGeometry args={[EMITTER_WIDTH, EMITTER_HEIGHT, EMITTER_DEPTH]} />
<meshPhongMaterial transparent opacity={0} />
<meshPhongMaterial transparent opacity={1} />
</mesh>
</>
);
Expand Down
2 changes: 2 additions & 0 deletions client/src/features/visualizer-threejs/VisualizerInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =

const sinusoidPeriodsSum = useConfigStore((s) => s.sinusoidPeriodsSum);
const sinusoidRandomPeriods = useConfigStore((s) => s.sinusoidRandomPeriods);
const sinusoidRandomAmplitudes = useConfigStore((s) => s.randomSinusoidAmplitudes);

const selectedFeedItem: TSelectFeedItemNova = clickedInstanceId ? blockMetadata.get(clickedInstanceId) ?? null : null;
const resetConfigState = useTangleStore((s) => s.resetConfigState);
Expand Down Expand Up @@ -205,6 +206,7 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
currentAnimationTime,
periods: sinusoidRandomPeriods,
periodsSum: sinusoidPeriodsSum,
sinusoidAmplitudes: sinusoidRandomAmplitudes,
});
const targetPosition = getBlockTargetPosition(initPosition, bps);

Expand Down
4 changes: 2 additions & 2 deletions client/src/features/visualizer-threejs/blockPositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export function getBlockTargetPosition(initPosition: IPos, bps: number): IPos {
return { x, y, z };
}

export function getBlockInitPosition({ currentAnimationTime, periods, periodsSum }: ISinusoidalPositionParams): IPos {
export function getBlockInitPosition({ currentAnimationTime, periods, periodsSum, sinusoidAmplitudes }: ISinusoidalPositionParams): IPos {
const { xTangleDistance } = getTangleDistances();
const { x: xEmitterPos, y, z } = getEmitterPositions({ currentAnimationTime, periods, periodsSum });
const { x: xEmitterPos, y, z } = getEmitterPositions({ currentAnimationTime, periods, periodsSum, sinusoidAmplitudes });
const x = xEmitterPos + xTangleDistance / 2;

return { x, y, z };
Expand Down
9 changes: 4 additions & 5 deletions client/src/features/visualizer-threejs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ export const MAX_PREV_POINTS = 20;

export const EMITTER_X_POSITION_MULTIPLIER = 3;

export const MAX_SINUSOIDAL_AMPLITUDE = 200;
export const SINUSOIDAL_AMPLITUDE_ACCUMULATOR = 30;
export const INITIAL_SINUSOIDAL_AMPLITUDE = 80;
export const HALF_WAVE_PERIOD_SECONDS = 5;

/* Values for randomizing the tangle */
export const NUMBER_OF_RANDOM_PERIODS = 100;
export const MIN_SINUSOID_PERIOD = 5;
export const MAX_SINUSOID_PERIOD = 8;

export const NUMBER_OF_RANDOM_AMPLITUDES = 100;
export const MIN_SINUSOID_AMPLITUDE = 100;
export const MAX_SINUSOID_AMPLITUDE = 200;
1 change: 1 addition & 0 deletions client/src/features/visualizer-threejs/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export interface ITimeBasedPositionParams {
export interface ISinusoidalPositionParams extends ITimeBasedPositionParams {
periods: number[];
periodsSum: number;
sinusoidAmplitudes: number[];
}
14 changes: 14 additions & 0 deletions client/src/features/visualizer-threejs/store/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ interface ConfigState {
setSinusoidPeriodsSum: (totalPeriodsSum: number) => void;
sinusoidRandomPeriods: number[];
setSinusoidRandomPeriods: (randomizedPeriods: number[]) => void;

randomSinusoidAmplitudes: number[];
setRandomSinusoidAmplitudes: (randomizedAmplitudes: number[]) => void;
}

export const useConfigStore = create<ConfigState>((set) => ({
Expand Down Expand Up @@ -96,4 +99,15 @@ export const useConfigStore = create<ConfigState>((set) => ({
sinusoidRandomPeriods: randomizedPeriods,
}));
},

/**
* Randomized amplitudes for the tangle.
*/
randomSinusoidAmplitudes: [],
setRandomSinusoidAmplitudes: (randomizedAmplitudes) => {
set((state) => ({
...state,
randomSinusoidAmplitudes: randomizedAmplitudes,
}));
},
}));
67 changes: 52 additions & 15 deletions client/src/features/visualizer-threejs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import {
MAX_POINT_RETRIES,
MAX_BLOCK_INSTANCES,
EMITTER_SPEED_MULTIPLIER,
MAX_SINUSOIDAL_AMPLITUDE,
CAMERA_X_AXIS_MOVEMENT,
CAMERA_Y_AXIS_MOVEMENT,
CAMERA_X_OFFSET,
CAMERA_Y_OFFSET,
SINUSOIDAL_AMPLITUDE_ACCUMULATOR,
INITIAL_SINUSOIDAL_AMPLITUDE,
NUMBER_OF_RANDOM_PERIODS,
MIN_SINUSOID_PERIOD,
MAX_SINUSOID_PERIOD,
NUMBER_OF_RANDOM_AMPLITUDES,
MIN_SINUSOID_AMPLITUDE,
MAX_SINUSOID_AMPLITUDE,
} from "./constants";
import type { ICameraAngles, ISinusoidalPositionParams, IThreeDimensionalPosition } from "./interfaces";

Expand Down Expand Up @@ -193,7 +193,7 @@ export function getTangleDistances(): {
const maxXDistance = MAX_BLOCK_DISTANCE;

/* Max Y Distance will be multiplied by 2 to position blocks in the negative and positive Y axis */
const maxYDistance = MAX_TANGLE_RADIUS * 2 + MAX_SINUSOIDAL_AMPLITUDE * 2;
const maxYDistance = MAX_TANGLE_RADIUS * 2 + MAX_SINUSOID_AMPLITUDE * 2;

/* TODO: add sinusoidal distances */

Expand Down Expand Up @@ -237,16 +237,18 @@ export function getCameraAngles(): ICameraAngles {
* considering random periods.
* @returns the sinusoidal position
*/
export function calculateSinusoidalAmplitude({ currentAnimationTime, periods, periodsSum }: ISinusoidalPositionParams): number {
export function calculateSinusoidalAmplitude({
currentAnimationTime,
periods,
periodsSum,
sinusoidAmplitudes,
}: ISinusoidalPositionParams): number {
const elapsedTime = currentAnimationTime % periodsSum;
const { period, accumulatedTime } = getCurrentPeriodValues(currentAnimationTime, periods, periodsSum);
const { index, period, accumulatedTime } = getCurrentPeriodValues(currentAnimationTime, periods, periodsSum);

const startTimeOfCurrentPeriod = accumulatedTime - period;
const timeInCurrentPeriod = elapsedTime - startTimeOfCurrentPeriod;

const currentWaveCount = Math.floor(elapsedTime / period);
const accumulatedAmplitude = currentWaveCount * SINUSOIDAL_AMPLITUDE_ACCUMULATOR;
const currentAmplitude = Math.min(INITIAL_SINUSOIDAL_AMPLITUDE + accumulatedAmplitude, MAX_SINUSOIDAL_AMPLITUDE);
const currentAmplitude = sinusoidAmplitudes[index];

const yPosition = currentAmplitude * Math.sin((2 * Math.PI * timeInCurrentPeriod) / period);

Expand All @@ -265,9 +267,14 @@ export function calculateEmitterPositionX(currentAnimationTime: number): number
* Calculates the emitter position based on the current animation time.
* @returns the emitter X,Y,Z positions
*/
export function getEmitterPositions({ currentAnimationTime, periods, periodsSum }: ISinusoidalPositionParams): IThreeDimensionalPosition {
export function getEmitterPositions({
currentAnimationTime,
periods,
periodsSum,
sinusoidAmplitudes,
}: ISinusoidalPositionParams): IThreeDimensionalPosition {
const x = calculateEmitterPositionX(currentAnimationTime);
const y = calculateSinusoidalAmplitude({ currentAnimationTime, periods, periodsSum });
const y = calculateSinusoidalAmplitude({ currentAnimationTime, periods, periodsSum, sinusoidAmplitudes });
return { x, y, z: 0 };
}

Expand All @@ -293,18 +300,48 @@ export function generateRandomPeriods(): { periods: number[]; sum: number } {
type PeriodResult = {
period: number;
accumulatedTime: number;
index: number;
};

function getCurrentPeriodValues(animationTime: number, periods: number[], totalSum: number): PeriodResult {
const effectiveTime = animationTime % totalSum;

let accumulatedTime = 0;

for (let i = 0; i < periods.length; i++) {
accumulatedTime += periods[i];
const period = periods[i];
accumulatedTime += period;
if (effectiveTime < accumulatedTime) {
return { period: periods[i], accumulatedTime };
return { index: i, period, accumulatedTime };
}
}

return { period: periods[0], accumulatedTime: 0 };
return { index: 0, period: periods[0], accumulatedTime: 0 };
}

function getNextAmplitudeWithVariation(currentAmplitude: number = 0): number {
const variation = (2 * MIN_SINUSOID_AMPLITUDE) / 3;
const randomAmplitudeVariation = randomNumberFromInterval(-variation, variation);

let newAmplitude = currentAmplitude + randomAmplitudeVariation;

if (newAmplitude > MAX_SINUSOID_AMPLITUDE) {
newAmplitude = currentAmplitude - Math.abs(randomAmplitudeVariation);
} else if (newAmplitude < MIN_SINUSOID_AMPLITUDE) {
newAmplitude = currentAmplitude + Math.abs(randomAmplitudeVariation);
}

newAmplitude = Math.max(MIN_SINUSOID_AMPLITUDE, Math.min(newAmplitude, MAX_SINUSOID_AMPLITUDE));

return newAmplitude;
}

export function generateRandomAmplitudes(): number[] {
const amplitudes: number[] = [];
let currentAmplitude: number = 0;
for (let i = 0; i < NUMBER_OF_RANDOM_AMPLITUDES; i++) {
currentAmplitude = getNextAmplitudeWithVariation(currentAmplitude);
amplitudes.push(currentAmplitude);
}
return amplitudes;
}
Loading