From df6d034f174d0e86b9434f191aff05c486ca2ca8 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Thu, 22 Feb 2024 12:07:26 +0100 Subject: [PATCH] feat(visualizer): add tangle tilting factor --- .../features/visualizer-threejs/Emitter.tsx | 6 ++- .../visualizer-threejs/VisualizerInstance.tsx | 5 ++- .../visualizer-threejs/blockPositions.ts | 4 +- .../features/visualizer-threejs/constants.ts | 5 +++ .../features/visualizer-threejs/interfaces.ts | 4 ++ .../visualizer-threejs/store/config.ts | 14 +++++++ .../src/features/visualizer-threejs/utils.ts | 41 +++++++++++++++++-- 7 files changed, 71 insertions(+), 8 deletions(-) diff --git a/client/src/features/visualizer-threejs/Emitter.tsx b/client/src/features/visualizer-threejs/Emitter.tsx index fba26e0c7..1a2b7c08c 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, generateRandomAmplitudes } from "./utils"; +import { getTangleDistances, getEmitterPositions, generateRandomPeriods, generateRandomAmplitudes, generateRandomTiltings } from "./utils"; import { CanvasElement } from "./enums"; import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer"; import { EMITTER_DEPTH, EMITTER_HEIGHT, EMITTER_WIDTH } from "./constants"; @@ -36,14 +36,18 @@ const Emitter: React.FC = ({ setRunListeners, emitterRef }: Emitte const randomSinusoidAmplitudes = useConfigStore((state) => state.randomSinusoidAmplitudes); const setRandomSinusoidAmplitudes = useConfigStore((state) => state.setRandomSinusoidAmplitudes); + const setRandomTilts = useConfigStore((state) => state.setRandomTilts); + const tangleWrapperRef = useRef(null); useLayoutEffect(() => { const { periods, sum: periodsSum } = generateRandomPeriods(); const amplitudes = generateRandomAmplitudes(); + const tiltings = generateRandomTiltings(); setSinusoidRandomPeriods(periods); setSinusoidPeriodsSum(periodsSum); setRandomSinusoidAmplitudes(amplitudes); + setRandomTilts(tiltings); }, []); useEffect(() => { diff --git a/client/src/features/visualizer-threejs/VisualizerInstance.tsx b/client/src/features/visualizer-threejs/VisualizerInstance.tsx index 26f62570f..f7a26d2e3 100644 --- a/client/src/features/visualizer-threejs/VisualizerInstance.tsx +++ b/client/src/features/visualizer-threejs/VisualizerInstance.tsx @@ -31,6 +31,7 @@ import CameraControls from "./CameraControls"; import "./Visualizer.scss"; import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer"; import { getBlockInitPosition, getBlockTargetPosition } from "./blockPositions"; +import { getCurrentTiltValue } from "./utils"; const features = { statsEnabled: false, @@ -76,6 +77,7 @@ const VisualizerInstance: React.FC> = const sinusoidPeriodsSum = useConfigStore((s) => s.sinusoidPeriodsSum); const sinusoidRandomPeriods = useConfigStore((s) => s.sinusoidRandomPeriods); const sinusoidRandomAmplitudes = useConfigStore((s) => s.randomSinusoidAmplitudes); + const randomTilts = useConfigStore((state) => state.randomTilts); const selectedFeedItem: TSelectFeedItemNova = clickedInstanceId ? blockMetadata.get(clickedInstanceId) ?? null : null; const resetConfigState = useTangleStore((s) => s.resetConfigState); @@ -208,7 +210,8 @@ const VisualizerInstance: React.FC> = periodsSum: sinusoidPeriodsSum, sinusoidAmplitudes: sinusoidRandomAmplitudes, }); - const targetPosition = getBlockTargetPosition(initPosition, bps); + const blockTiltFactor = getCurrentTiltValue(currentAnimationTime, randomTilts); + const targetPosition = getBlockTargetPosition(initPosition, bps, blockTiltFactor); bpsCounter.addBlock(); diff --git a/client/src/features/visualizer-threejs/blockPositions.ts b/client/src/features/visualizer-threejs/blockPositions.ts index a01f521a6..8d556972b 100644 --- a/client/src/features/visualizer-threejs/blockPositions.ts +++ b/client/src/features/visualizer-threejs/blockPositions.ts @@ -9,8 +9,8 @@ interface IPos { y: number; z: number; } -export function getBlockTargetPosition(initPosition: IPos, bps: number): IPos { - const { y, z } = generateYZPositions(bps, initPosition); +export function getBlockTargetPosition(initPosition: IPos, bps: number, tiltDegress: number): IPos { + const { y, z } = generateYZPositions(bps, initPosition, tiltDegress); const emitterMinX = initPosition.x - EMITTER_WIDTH / 2; const emitterMaxX = initPosition.x + EMITTER_WIDTH / 2; diff --git a/client/src/features/visualizer-threejs/constants.ts b/client/src/features/visualizer-threejs/constants.ts index cc588c7f8..12adbda36 100644 --- a/client/src/features/visualizer-threejs/constants.ts +++ b/client/src/features/visualizer-threejs/constants.ts @@ -93,3 +93,8 @@ 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; + +export const NUMBER_OF_RANDOM_TILTINGS = 100; +export const TILT_DURATION_SECONDS = 4; +export const MAX_TILT_FACTOR_DEGREES = 16; +export const MIN_TILT_FACTOR_DEGREES = 1; diff --git a/client/src/features/visualizer-threejs/interfaces.ts b/client/src/features/visualizer-threejs/interfaces.ts index 81bae2dd5..afe4af6d0 100644 --- a/client/src/features/visualizer-threejs/interfaces.ts +++ b/client/src/features/visualizer-threejs/interfaces.ts @@ -11,6 +11,10 @@ export interface IThreeDimensionalPosition { z: number; } +export interface IThreeDimensionalPositionWithTilt extends IThreeDimensionalPosition { + tiltFactor: number; +} + export interface ITimeBasedPositionParams { currentAnimationTime: number; } diff --git a/client/src/features/visualizer-threejs/store/config.ts b/client/src/features/visualizer-threejs/store/config.ts index d505f5177..8c5d9ce9d 100644 --- a/client/src/features/visualizer-threejs/store/config.ts +++ b/client/src/features/visualizer-threejs/store/config.ts @@ -23,6 +23,9 @@ interface ConfigState { randomSinusoidAmplitudes: number[]; setRandomSinusoidAmplitudes: (randomizedAmplitudes: number[]) => void; + + randomTilts: number[]; + setRandomTilts: (randomTilts: number[]) => void; } export const useConfigStore = create((set) => ({ @@ -110,4 +113,15 @@ export const useConfigStore = create((set) => ({ randomSinusoidAmplitudes: randomizedAmplitudes, })); }, + + /** + * Randomized tilts for the tangle. + */ + randomTilts: [], + setRandomTilts: (randomTilts) => { + set((state) => ({ + ...state, + randomTilts, + })); + }, })); diff --git a/client/src/features/visualizer-threejs/utils.ts b/client/src/features/visualizer-threejs/utils.ts index bba6c9ec2..d66c97542 100644 --- a/client/src/features/visualizer-threejs/utils.ts +++ b/client/src/features/visualizer-threejs/utils.ts @@ -20,6 +20,10 @@ import { NUMBER_OF_RANDOM_AMPLITUDES, MIN_SINUSOID_AMPLITUDE, MAX_SINUSOID_AMPLITUDE, + NUMBER_OF_RANDOM_TILTINGS, + MIN_TILT_FACTOR_DEGREES, + MAX_TILT_FACTOR_DEGREES, + TILT_DURATION_SECONDS, } from "./constants"; import type { ICameraAngles, ISinusoidalPositionParams, IThreeDimensionalPosition } from "./interfaces"; @@ -105,6 +109,7 @@ function getDynamicRandomYZPoints( y: 0, z: 0, }, + tiltDegrees: number, ): IBlockTanglePosition { const theta = Math.random() * (2 * Math.PI); @@ -112,9 +117,12 @@ function getDynamicRandomYZPoints( const randomFactor = Math.random(); const radius = randomFactor * maxRadius; - const y = radius * Math.cos(theta) + initialPosition.y; + let y = radius * Math.cos(theta) + initialPosition.y; const z = radius * Math.sin(theta) + initialPosition.z; + const tiltRadians = tiltDegrees * (Math.PI / 180); + y += Math.tan(tiltRadians) * radius; + return { y, z }; } @@ -138,13 +146,14 @@ function generateAValidRandomPoint( bps: number, initialPosition: IThreeDimensionalPosition, prevPoints: IBlockTanglePosition[], + tiltDegress: number, ): IBlockTanglePosition { let trialPoint: IBlockTanglePosition; let passAllChecks = false; let retries = 0; do { - trialPoint = getDynamicRandomYZPoints(bps, initialPosition); + trialPoint = getDynamicRandomYZPoints(bps, initialPosition, tiltDegress); passAllChecks = pointPassesAllChecks(trialPoint, prevPoints); retries++; } while (!passAllChecks && retries < MAX_POINT_RETRIES); @@ -164,8 +173,8 @@ function generateAValidRandomPoint( export function getGenerateDynamicYZPosition(): typeof getDynamicRandomYZPoints { const prevPoints: IBlockTanglePosition[] = []; - return (bps: number, initialPosition: IThreeDimensionalPosition = { x: 0, y: 0, z: 0 }): IBlockTanglePosition => { - const validPoint = generateAValidRandomPoint(bps, initialPosition, prevPoints); + return (bps: number, initialPosition: IThreeDimensionalPosition = { x: 0, y: 0, z: 0 }, tiltDegress): IBlockTanglePosition => { + const validPoint = generateAValidRandomPoint(bps, initialPosition, prevPoints, tiltDegress); const randomYNumber = randomNumberFromInterval(0, BLOCK_STEP_PX / 20); const randomZNumber = randomNumberFromInterval(0, BLOCK_STEP_PX / 20); @@ -345,3 +354,27 @@ export function generateRandomAmplitudes(): number[] { } return amplitudes; } + +export function generateRandomTiltings(): number[] { + let previousValue: number; + + const tilts: number[] = Array.from({ length: NUMBER_OF_RANDOM_TILTINGS }, () => { + let randomTilt = randomIntFromInterval(MIN_TILT_FACTOR_DEGREES, MAX_TILT_FACTOR_DEGREES); + + if ((previousValue < 0 && randomTilt < 0) || (previousValue > 0 && randomTilt > 0)) { + randomTilt *= -1; + } + + previousValue = randomTilt; + + return randomTilt; + }); + return tilts; +} + +export function getCurrentTiltValue(animationTime: number, tilts: number[]): number { + const tiltAnimationDuration = TILT_DURATION_SECONDS * 2; + const currentAnimationTime = animationTime % tiltAnimationDuration; + currentAnimationTime; + return 0; +}