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): add tangle tilting factor #1172

Merged
merged 15 commits into from
Feb 26, 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
44 changes: 22 additions & 22 deletions client/src/features/visualizer-threejs/CameraControls.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,33 @@
import { CameraControls as DreiCameraControls } from "@react-three/drei";
import React, { useEffect } from "react";
import { getCameraAngles } from "./utils";
import React, { useEffect, useState } from "react";
import { useThree } from "@react-three/fiber";
import { CanvasElement } from "./enums";
import { useConfigStore } from "./store";
import { useTangleStore, useConfigStore } from "./store";
import { VISUALIZER_PADDINGS } from "./constants";
import { getCameraAngles } from "./utils";

const CAMERA_ANGLES = getCameraAngles();

const CameraControls = () => {
const controls = React.useRef<DreiCameraControls>(null);
const [shouldLockZoom, setShouldLockZoom] = useState<boolean>(false);

const scene = useThree((state) => state.scene);
const zoom = useTangleStore((state) => state.zoom);
const mesh = scene.getObjectByName(CanvasElement.TangleWrapperMesh) as THREE.Mesh | undefined;
const canvasDimensions = useConfigStore((state) => state.dimensions);

/**
* Locks the camera zoom to the current zoom value.
*/
function lockCameraZoom(controls: DreiCameraControls) {
const zoom = controls.camera.zoom;
controls.maxZoom = zoom;
controls.minZoom = zoom;
}

/**
* Unlocks the camera zoom for free movement.
*/
function unlockCameraZoom(controls: DreiCameraControls) {
controls.maxZoom = Infinity;
controls.minZoom = 0.01;
}

/**
* Fits the camera to the TangleMesh.
*/
function fitCameraToTangle(controls: DreiCameraControls | null, mesh?: THREE.Mesh) {
if (controls && mesh) {
unlockCameraZoom(controls);
const previousZoom = controls.camera.zoom;
controls.minZoom = 0.01;
controls.maxZoom = Infinity;
controls.fitToBox(mesh, false, { ...VISUALIZER_PADDINGS });
lockCameraZoom(controls);
controls.minZoom = previousZoom;
controls.maxZoom = previousZoom;
}
}

Expand All @@ -53,7 +41,9 @@ const CameraControls = () => {
const camera = controls.current?.camera;
const renderVerticalScene = canvasDimensions.width < canvasDimensions.height;
const cameraUp: [number, number, number] = renderVerticalScene ? [1, 0, 0] : [0, 1, 0];
setShouldLockZoom(false);
camera.up.set(...cameraUp);
setShouldLockZoom(true);
}
}, [canvasDimensions, controls, mesh]);

Expand All @@ -70,6 +60,16 @@ const CameraControls = () => {
};
}, [controls, mesh]);

/**
* Locks the camera zoom to the current zoom value.
*/
useEffect(() => {
if (controls.current) {
controls.current.maxZoom = shouldLockZoom ? zoom : Infinity;
controls.current.minZoom = shouldLockZoom ? zoom : 0.01;
}
}, [controls.current, shouldLockZoom, zoom]);

return <DreiCameraControls ref={controls} makeDefault {...CAMERA_ANGLES} />;
};

Expand Down
16 changes: 15 additions & 1 deletion client/src/features/visualizer-threejs/Emitter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ 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,
getCurrentTiltValue,
} from "./utils";
import { CanvasElement } from "./enums";
import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer";
import { EMITTER_DEPTH, EMITTER_HEIGHT, EMITTER_WIDTH } from "./constants";
Expand Down Expand Up @@ -36,14 +43,19 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
const randomSinusoidAmplitudes = useConfigStore((state) => state.randomSinusoidAmplitudes);
const setRandomSinusoidAmplitudes = useConfigStore((state) => state.setRandomSinusoidAmplitudes);

const randomTilts = useConfigStore((state) => state.randomTilts);
const setRandomTilts = useConfigStore((state) => state.setRandomTilts);

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

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

useEffect(() => {
Expand All @@ -68,6 +80,7 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
*/
useFrame(() => {
const currentAnimationTime = getVisualizerTimeDiff();
const currentTilt = getCurrentTiltValue(currentAnimationTime, randomTilts);
const { x, y } = getEmitterPositions({
currentAnimationTime,
periods: sinusoidRandomPeriods,
Expand All @@ -79,6 +92,7 @@ const Emitter: React.FC<EmitterProps> = ({ setRunListeners, emitterRef }: Emitte
if (emitterRef.current) {
emitterRef.current.position.x = x;
emitterRef.current.position.y = y;
emitterRef.current.rotation.z = THREE.MathUtils.degToRad(currentTilt);
}

if (tangleWrapperRef.current) {
Expand Down
13 changes: 7 additions & 6 deletions client/src/features/visualizer-threejs/VisualizerInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import { Wrapper } from "./wrapper/Wrapper";
import { CanvasElement } from "./enums";
import { useGetThemeMode } from "~/helpers/hooks/useGetThemeMode";
import { TSelectFeedItemNova } from "~/app/types/visualizer.types";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { BasicBlockBody, Utils, type IBlockMetadata, type BlockState, type SlotIndex, type BlockId } from "@iota/sdk-wasm-nova/web";
import { BasicBlockBody, Utils, type IBlockMetadata, type BlockState, type SlotIndex } from "@iota/sdk-wasm-nova/web";
import { IFeedBlockData } from "~/models/api/nova/feed/IFeedBlockData";
import CameraControls from "./CameraControls";
import "./Visualizer.scss";
import useVisualizerTimer from "~/helpers/nova/hooks/useVisualizerTimer";
import { getBlockInitPosition, getBlockTargetPosition } from "./blockPositions";
import { getCurrentTiltValue } from "./utils";
import "./Visualizer.scss";

const features = {
statsEnabled: false,
Expand Down Expand Up @@ -76,6 +76,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 randomTilts = useConfigStore((state) => state.randomTilts);

const selectedFeedItem: TSelectFeedItemNova = clickedInstanceId ? blockMetadata.get(clickedInstanceId) ?? null : null;
const resetConfigState = useTangleStore((s) => s.resetConfigState);
Expand Down Expand Up @@ -208,7 +209,8 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
periodsSum: sinusoidPeriodsSum,
sinusoidAmplitudes: sinusoidRandomAmplitudes,
});
const targetPosition = getBlockTargetPosition(initPosition, bps);
const blockTiltFactor = getCurrentTiltValue(currentAnimationTime, randomTilts);
const targetPosition = getBlockTargetPosition(initPosition, bps, blockTiltFactor);

bpsCounter.addBlock();

Expand All @@ -227,11 +229,10 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
if (blockWeakParents.length > 0) {
addToEdgeQueue(blockData.blockId, blockWeakParents);
}

addBlock({
id: blockData.blockId,
color: PENDING_BLOCK_COLOR,
blockAddedTimestamp: getCurrentAnimationTime(),
blockAddedTimestamp: currentAnimationTime,
targetPosition,
initPosition,
});
Expand Down
37 changes: 15 additions & 22 deletions client/src/features/visualizer-threejs/blockPositions.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { EMITTER_WIDTH, EMITTER_X_POSITION_MULTIPLIER } from "./constants";
import { ISinusoidalPositionParams } from "./interfaces";
import { getEmitterPositions, getGenerateDynamicYZPosition, getTangleDistances, randomIntFromInterval } from "./utils";
import { ISinusoidalPositionParams, IThreeDimensionalPosition } from "./interfaces";
import { getEmitterPositions, getTangleDistances, getBlockPositionGenerator } from "./utils";

const generateYZPositions = getGenerateDynamicYZPosition();
const generateBlockTargetPosition = getBlockPositionGenerator();

interface IPos {
x: number;
y: number;
z: number;
export function getBlockTargetPosition(
initPosition: IThreeDimensionalPosition,
bps: number,
tiltDegress: number,
): IThreeDimensionalPosition {
return generateBlockTargetPosition(bps, initPosition, tiltDegress);
}
export function getBlockTargetPosition(initPosition: IPos, bps: number): IPos {
const { y, z } = generateYZPositions(bps, initPosition);

const emitterMinX = initPosition.x - EMITTER_WIDTH / 2;
const emitterMaxX = initPosition.x + EMITTER_WIDTH / 2;

const minX = emitterMinX - (emitterMaxX - emitterMinX) * EMITTER_X_POSITION_MULTIPLIER;
const maxX = emitterMaxX + (emitterMaxX - emitterMinX) * EMITTER_X_POSITION_MULTIPLIER;

const x = randomIntFromInterval(minX, maxX);

return { x, y, z };
}

export function getBlockInitPosition({ currentAnimationTime, periods, periodsSum, sinusoidAmplitudes }: ISinusoidalPositionParams): IPos {
export function getBlockInitPosition({
currentAnimationTime,
periods,
periodsSum,
sinusoidAmplitudes,
}: ISinusoidalPositionParams): IThreeDimensionalPosition {
const { xTangleDistance } = getTangleDistances();
const { x: xEmitterPos, y, z } = getEmitterPositions({ currentAnimationTime, periods, periodsSum, sinusoidAmplitudes });
const x = xEmitterPos + xTangleDistance / 2;
Expand Down
17 changes: 11 additions & 6 deletions client/src/features/visualizer-threejs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const ZOOM_DEFAULT = 2;
export const TIME_DIFF_COUNTER = 250;
export const SECOND = 1000;
export const DATA_SENDER_TIME_INTERVAL = 500;
export const ANIMATION_TIME_SECONDS = 3;

// colors
export const PENDING_BLOCK_COLOR = new Color("#A6C3FC");
Expand All @@ -35,7 +34,7 @@ export const BLOCK_STATE_TO_COLOR = new Map<BlockState, Color>([
]);

// emitter
export const EMITTER_SPEED_MULTIPLIER = 80;
export const EMITTER_SPEED_MULTIPLIER = 150;
export const EMITTER_PADDING_RIGHT = 150;
export const VISUALIZER_SAFE_ZONE = 150;

Expand Down Expand Up @@ -72,18 +71,19 @@ export const EMITTER_HEIGHT = 250;
export const EMITTER_DEPTH = 250;

// conic emitter
export const MIN_TANGLE_RADIUS = 100;
export const MAX_TANGLE_RADIUS = 300;
export const MIN_TANGLE_RADIUS = 200;
export const MAX_TANGLE_RADIUS = 600;

export const MIN_BLOCKS_PER_SECOND = 100;
export const MIN_BLOCKS_PER_SECOND = 150;
export const MAX_BLOCKS_PER_SECOND = 250;

export const MIN_BLOCK_NEAR_RADIUS = 20;

export const MAX_POINT_RETRIES = 10;
export const MAX_PREV_POINTS = 20;

export const EMITTER_X_POSITION_MULTIPLIER = 3;
export const SPRAY_DISTANCE = 400;
export const SPRAY_ANIMATION_DURATION = SPRAY_DISTANCE / EMITTER_SPEED_MULTIPLIER;

/* Values for randomizing the tangle */
export const NUMBER_OF_RANDOM_PERIODS = 100;
Expand All @@ -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;
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 @@ -5,12 +5,21 @@ export interface ICameraAngles {
maxAzimuthAngle: number;
}

export interface ITwoDimensionalPosition {
x: number;
y: number;
}

export interface IThreeDimensionalPosition {
x: number;
y: number;
z: number;
}

export interface IThreeDimensionalPositionWithTilt extends IThreeDimensionalPosition {
tiltFactor: number;
}

export interface ITimeBasedPositionParams {
currentAnimationTime: number;
}
Expand Down
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 @@ -23,6 +23,9 @@ interface ConfigState {

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

randomTilts: number[];
setRandomTilts: (randomTilts: number[]) => void;
}

export const useConfigStore = create<ConfigState>((set) => ({
Expand Down Expand Up @@ -110,4 +113,15 @@ export const useConfigStore = create<ConfigState>((set) => ({
randomSinusoidAmplitudes: randomizedAmplitudes,
}));
},

/**
* Randomized tilts for the tangle.
*/
randomTilts: [],
setRandomTilts: (randomTilts) => {
set((state) => ({
...state,
randomTilts,
}));
},
}));
5 changes: 3 additions & 2 deletions client/src/features/visualizer-threejs/store/tangle.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Color } from "three";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { ZOOM_DEFAULT, ANIMATION_TIME_SECONDS } from "../constants";
import { ZOOM_DEFAULT, EMITTER_SPEED_MULTIPLIER, SPRAY_DISTANCE } from "../constants";
import { IFeedBlockData } from "~models/api/nova/feed/IFeedBlockData";
import { IThreeDimensionalPosition } from "../interfaces";
import { BlockId, SlotIndex } from "@iota/sdk-wasm-nova/web";
Expand Down Expand Up @@ -104,7 +104,8 @@ export const useTangleStore = create<TangleState>()(
});

for (const [key, value] of state.blockIdToAnimationPosition) {
if (value.elapsedTime > ANIMATION_TIME_SECONDS) {
const animationTime = SPRAY_DISTANCE / EMITTER_SPEED_MULTIPLIER;
if (value.elapsedTime > animationTime) {
state.blockIdToAnimationPosition.delete(key);
}
}
Expand Down
Loading
Loading