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 period times #1168

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions client/src/features/visualizer-threejs/Emitter.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable react/no-unknown-property */
import { useFrame, useThree } from "@react-three/fiber";
import React, { RefObject, Dispatch, SetStateAction, useEffect, useRef } from "react";
import React, { RefObject, Dispatch, SetStateAction, useEffect, useRef, useLayoutEffect } from "react";
import * as THREE from "three";
import { useConfigStore, useTangleStore } from "./store";
import { useRenderTangle } from "./useRenderTangle";
import { getTangleDistances, getEmitterPositions } from "./utils";
import { getTangleDistances, getEmitterPositions, generateRandomPeriods } from "./utils";
import { CanvasElement } from "./enums";
import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer";
import { EMITTER_DEPTH, EMITTER_HEIGHT, EMITTER_WIDTH } from "./constants";
Expand All @@ -28,8 +28,19 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
const setIsPlaying = useConfigStore((state) => state.setIsPlaying);
const setInitialTime = useConfigStore((state) => state.setInitialTime);

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 tangleWrapperRef = useRef<THREE.Mesh | null>(null);

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

useEffect(() => {
setZoom(currentZoom);
}, [currentZoom]);
Expand All @@ -52,7 +63,11 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
*/
useFrame(() => {
const currentAnimationTime = getVisualizerTimeDiff();
const { x, y } = getEmitterPositions(currentAnimationTime);
const { x, y } = getEmitterPositions({
currentAnimationTime,
periods: randomizedSinusoidPeriods,
periodsSum: sinusoidPeriodsSum,
});

if (isPlaying) {
if (emitterRef.current) {
Expand Down
12 changes: 9 additions & 3 deletions client/src/features/visualizer-threejs/VisualizerInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
}) => {
const [networkConfig] = useNetworkConfig(network);
const themeMode = useGetThemeMode();
const getCurrentAnimationTime = useVisualizerTimer();

const [runListeners, setRunListeners] = React.useState<boolean>(false);

Expand Down Expand Up @@ -72,14 +73,15 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
const addToConfirmedBlocksSlot = useTangleStore((s) => s.addToConfirmedBlocksBySlot);
const removeConfirmedBlocksSlot = useTangleStore((s) => s.removeConfirmedBlocksSlot);

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

const selectedFeedItem: TSelectFeedItemNova = clickedInstanceId ? blockMetadata.get(clickedInstanceId) ?? null : null;
const resetConfigState = useTangleStore((s) => s.resetConfigState);

const emitterRef = useRef<THREE.Mesh>(null);
const [feedService, setFeedService] = React.useState<NovaFeedClient | null>(ServiceFactory.get<NovaFeedClient>(`feed-${network}`));

const getCurrentAnimationTime = useVisualizerTimer();

/**
* Pause on tab or window change
*/
Expand Down Expand Up @@ -199,7 +201,11 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
if (blockData) {
const currentAnimationTime = getCurrentAnimationTime();
const bps = bpsCounter.getBPS();
const initPosition = getBlockInitPosition(currentAnimationTime);
const initPosition = getBlockInitPosition({
currentAnimationTime,
periods: sinusoidRandomPeriods,
periodsSum: sinusoidPeriodsSum,
});
const targetPosition = getBlockTargetPosition(initPosition, bps);

bpsCounter.addBlock();
Expand Down
5 changes: 3 additions & 2 deletions client/src/features/visualizer-threejs/blockPositions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EMITTER_WIDTH, EMITTER_X_POSITION_MULTIPLIER } from "./constants";
import { ISinusoidalPositionParams } from "./interfaces";
import { getEmitterPositions, getGenerateDynamicYZPosition, getTangleDistances, randomIntFromInterval } from "./utils";

const generateYZPositions = getGenerateDynamicYZPosition();
Expand All @@ -22,9 +23,9 @@ export function getBlockTargetPosition(initPosition: IPos, bps: number): IPos {
return { x, y, z };
}

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

return { x, y, z };
Expand Down
10 changes: 7 additions & 3 deletions client/src/features/visualizer-threejs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const PENDING_BLOCK_COLOR = new Color("#A6C3FC");
export const ACCEPTED_BLOCK_COLOR = new Color("#0101AB");
export const CONFIRMED_BLOCK_COLOR = new Color("#0000DB");
export const FINALIZED_BLOCK_COLOR = new Color("#0101FF");
// TODO Remove accepted state once is added to the SDK (missing)
export const BLOCK_STATE_TO_COLOR = new Map<BlockState | "accepted", Color>([

export const BLOCK_STATE_TO_COLOR = new Map<BlockState, Color>([
["pending", PENDING_BLOCK_COLOR],
["accepted", ACCEPTED_BLOCK_COLOR],
["confirmed", CONFIRMED_BLOCK_COLOR],
Expand Down Expand Up @@ -72,7 +72,6 @@ export const EMITTER_HEIGHT = 250;
export const EMITTER_DEPTH = 250;

// conic emitter

export const MIN_TANGLE_RADIUS = 100;
export const MAX_TANGLE_RADIUS = 300;

Expand All @@ -90,3 +89,8 @@ 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;
9 changes: 9 additions & 0 deletions client/src/features/visualizer-threejs/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ export interface IThreeDimensionalPosition {
y: number;
z: number;
}

export interface ITimeBasedPositionParams {
currentAnimationTime: number;
}

export interface ISinusoidalPositionParams extends ITimeBasedPositionParams {
periods: number[];
periodsSum: number;
}
23 changes: 23 additions & 0 deletions client/src/features/visualizer-threejs/store/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ interface ConfigState {

initialTime: number | null;
setInitialTime: (initialTime: number) => void;

sinusoidPeriodsSum: number;
setSinusoidPeriodsSum: (totalPeriodsSum: number) => void;
sinusoidRandomPeriods: number[];
setSinusoidRandomPeriods: (randomizedPeriods: number[]) => void;
}

export const useConfigStore = create<ConfigState>((set) => ({
Expand Down Expand Up @@ -73,4 +78,22 @@ export const useConfigStore = create<ConfigState>((set) => ({
initialTime,
}));
},

/**
* Randomized periods for the tangle.
*/
sinusoidPeriodsSum: 0,
setSinusoidPeriodsSum: (totalPeriodsSum) => {
set((state) => ({
...state,
sinusoidPeriodsSum: totalPeriodsSum,
}));
},
sinusoidRandomPeriods: [],
setSinusoidRandomPeriods: (randomizedPeriods) => {
set((state) => ({
...state,
sinusoidRandomPeriods: randomizedPeriods,
}));
},
}));
55 changes: 46 additions & 9 deletions client/src/features/visualizer-threejs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
MIN_BLOCK_NEAR_RADIUS,
MAX_PREV_POINTS,
MAX_POINT_RETRIES,
HALF_WAVE_PERIOD_SECONDS,
MAX_BLOCK_INSTANCES,
EMITTER_SPEED_MULTIPLIER,
MAX_SINUSOIDAL_AMPLITUDE,
Expand All @@ -18,8 +17,11 @@ import {
CAMERA_Y_OFFSET,
SINUSOIDAL_AMPLITUDE_ACCUMULATOR,
INITIAL_SINUSOIDAL_AMPLITUDE,
NUMBER_OF_RANDOM_PERIODS,
MIN_SINUSOID_PERIOD,
MAX_SINUSOID_PERIOD,
} from "./constants";
import { ICameraAngles, IThreeDimensionalPosition } from "./interfaces";
import type { ICameraAngles, ISinusoidalPositionParams, IThreeDimensionalPosition } from "./interfaces";

/**
* Generates a random number within a specified range.
Expand Down Expand Up @@ -231,16 +233,22 @@ export function getCameraAngles(): ICameraAngles {
}

/**
* Calculates the sinusoidal position for the emitter based on the current animation time.
* Calculates the sinusoidal position for the emitter based on the current animation time,
* considering random periods.
* @returns the sinusoidal position
*/
export function calculateSinusoidalAmplitude(currentAnimationTime: number): number {
const wavePeriod = HALF_WAVE_PERIOD_SECONDS * 2;
const currentWaveCount = Math.floor(currentAnimationTime / wavePeriod);
export function calculateSinusoidalAmplitude({ currentAnimationTime, periods, periodsSum }: ISinusoidalPositionParams): number {
const elapsedTime = currentAnimationTime % periodsSum;
const { 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 yPosition = currentAmplitude * Math.sin((2 * Math.PI * currentAnimationTime) / wavePeriod);
const yPosition = currentAmplitude * Math.sin((2 * Math.PI * timeInCurrentPeriod) / period);

return yPosition;
}
Expand All @@ -257,9 +265,9 @@ 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: number): IThreeDimensionalPosition {
export function getEmitterPositions({ currentAnimationTime, periods, periodsSum }: ISinusoidalPositionParams): IThreeDimensionalPosition {
const x = calculateEmitterPositionX(currentAnimationTime);
const y = calculateSinusoidalAmplitude(currentAnimationTime);
const y = calculateSinusoidalAmplitude({ currentAnimationTime, periods, periodsSum });
return { x, y, z: 0 };
}

Expand All @@ -271,3 +279,32 @@ export function getEmitterPositions(currentAnimationTime: number): IThreeDimensi
export function positionToVector(position: IThreeDimensionalPosition) {
return new Vector3(position.x, position.y, position.z);
}

export function generateRandomPeriods(): { periods: number[]; sum: number } {
let sum = 0;
const periods = Array.from({ length: NUMBER_OF_RANDOM_PERIODS }, () => {
const period = Number(randomNumberFromInterval(MIN_SINUSOID_PERIOD, MAX_SINUSOID_PERIOD).toFixed(4));
sum += period;
return period;
});
return { periods, sum };
}

type PeriodResult = {
period: number;
accumulatedTime: 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];
if (effectiveTime < accumulatedTime) {
return { period: periods[i], accumulatedTime };
}
}

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