From 3d3bb50c958348de306e722e7f44ff7ac9ebc379 Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:04:10 +0100 Subject: [PATCH] feat(visualizer): randomize sinusoid amplitudes (#1169) * feat(visualizer): randomize sinusoidal period times * chore: remove comments * feat(visualizer): randomize sinusoid amplitudes * refactor: rename HALF_PERIOD constants to PERIOD --------- Co-authored-by: Mario --- .../features/visualizer-threejs/Emitter.tsx | 18 +++-- .../visualizer-threejs/VisualizerInstance.tsx | 2 + .../visualizer-threejs/blockPositions.ts | 4 +- .../features/visualizer-threejs/constants.ts | 9 ++- .../features/visualizer-threejs/interfaces.ts | 1 + .../visualizer-threejs/store/config.ts | 14 ++++ .../src/features/visualizer-threejs/utils.ts | 67 ++++++++++++++----- 7 files changed, 87 insertions(+), 28 deletions(-) diff --git a/client/src/features/visualizer-threejs/Emitter.tsx b/client/src/features/visualizer-threejs/Emitter.tsx index 9c251e245..fba26e0c7 100644 --- a/client/src/features/visualizer-threejs/Emitter.tsx +++ b/client/src/features/visualizer-threejs/Emitter.tsx @@ -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"; @@ -30,15 +30,20 @@ const Emitter: React.FC = ({ 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(null); useLayoutEffect(() => { const { periods, sum: periodsSum } = generateRandomPeriods(); - setRandomizedSinusoidPeriods(periods); + const amplitudes = generateRandomAmplitudes(); + setSinusoidRandomPeriods(periods); setSinusoidPeriodsSum(periodsSum); + setRandomSinusoidAmplitudes(amplitudes); }, []); useEffect(() => { @@ -65,8 +70,9 @@ const Emitter: React.FC = ({ setRunListeners, emitterRef }: Emitte const currentAnimationTime = getVisualizerTimeDiff(); const { x, y } = getEmitterPositions({ currentAnimationTime, - periods: randomizedSinusoidPeriods, + periods: sinusoidRandomPeriods, periodsSum: sinusoidPeriodsSum, + sinusoidAmplitudes: randomSinusoidAmplitudes, }); if (isPlaying) { @@ -99,7 +105,7 @@ const Emitter: React.FC = ({ setRunListeners, emitterRef }: Emitte {/* Emitter Mesh */} - + ); diff --git a/client/src/features/visualizer-threejs/VisualizerInstance.tsx b/client/src/features/visualizer-threejs/VisualizerInstance.tsx index fcf5fc4c0..26f62570f 100644 --- a/client/src/features/visualizer-threejs/VisualizerInstance.tsx +++ b/client/src/features/visualizer-threejs/VisualizerInstance.tsx @@ -75,6 +75,7 @@ const VisualizerInstance: React.FC> = 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); @@ -205,6 +206,7 @@ const VisualizerInstance: React.FC> = currentAnimationTime, periods: sinusoidRandomPeriods, periodsSum: sinusoidPeriodsSum, + sinusoidAmplitudes: sinusoidRandomAmplitudes, }); const targetPosition = getBlockTargetPosition(initPosition, bps); diff --git a/client/src/features/visualizer-threejs/blockPositions.ts b/client/src/features/visualizer-threejs/blockPositions.ts index 82caa101c..a01f521a6 100644 --- a/client/src/features/visualizer-threejs/blockPositions.ts +++ b/client/src/features/visualizer-threejs/blockPositions.ts @@ -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 }; diff --git a/client/src/features/visualizer-threejs/constants.ts b/client/src/features/visualizer-threejs/constants.ts index 94f527f66..cc588c7f8 100644 --- a/client/src/features/visualizer-threejs/constants.ts +++ b/client/src/features/visualizer-threejs/constants.ts @@ -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; diff --git a/client/src/features/visualizer-threejs/interfaces.ts b/client/src/features/visualizer-threejs/interfaces.ts index 55df2965f..81bae2dd5 100644 --- a/client/src/features/visualizer-threejs/interfaces.ts +++ b/client/src/features/visualizer-threejs/interfaces.ts @@ -18,4 +18,5 @@ export interface ITimeBasedPositionParams { export interface ISinusoidalPositionParams extends ITimeBasedPositionParams { periods: number[]; periodsSum: number; + sinusoidAmplitudes: number[]; } diff --git a/client/src/features/visualizer-threejs/store/config.ts b/client/src/features/visualizer-threejs/store/config.ts index b3152e8cd..d505f5177 100644 --- a/client/src/features/visualizer-threejs/store/config.ts +++ b/client/src/features/visualizer-threejs/store/config.ts @@ -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((set) => ({ @@ -96,4 +99,15 @@ export const useConfigStore = create((set) => ({ sinusoidRandomPeriods: randomizedPeriods, })); }, + + /** + * Randomized amplitudes for the tangle. + */ + randomSinusoidAmplitudes: [], + setRandomSinusoidAmplitudes: (randomizedAmplitudes) => { + set((state) => ({ + ...state, + randomSinusoidAmplitudes: randomizedAmplitudes, + })); + }, })); diff --git a/client/src/features/visualizer-threejs/utils.ts b/client/src/features/visualizer-threejs/utils.ts index f38a82354..bba6c9ec2 100644 --- a/client/src/features/visualizer-threejs/utils.ts +++ b/client/src/features/visualizer-threejs/utils.ts @@ -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"; @@ -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 */ @@ -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); @@ -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 }; } @@ -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; }