Skip to content

Commit

Permalink
enhancement: Update shape of the tangle: star trail (#924)
Browse files Browse the repository at this point in the history
* change first element

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* feat: update shape position smooth.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* feature: update shape of tangle. Init position using ref.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* feature: update shape of tangle.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* chore: broken changing of position.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* chore: Spray animation in another way.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

* chore: Clear changing of formatting.

Signed-off-by: Eugene Panteleymonchuk <[email protected]>

---------

Signed-off-by: Eugene Panteleymonchuk <[email protected]>
Co-authored-by: Begoña Álvarez de la Cruz <[email protected]>
  • Loading branch information
panteleymonchuk and begonaalvarezd authored Dec 20, 2023
1 parent 98e6b13 commit 72a6a6b
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 51 deletions.
36 changes: 23 additions & 13 deletions client/src/features/visualizer-threejs/VisualizerInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =
const addToEdgeQueue = useTangleStore(s => s.addToEdgeQueue);
const addToColorQueue = useTangleStore(s => s.addToColorQueue);
const addYPosition = useTangleStore(s => s.addYPosition);
const blockIdToPosition = useTangleStore(s => s.blockIdToPosition);
const blockMetadata = useTangleStore(s => s.blockMetadata);
const indexToBlockId = useTangleStore(s => s.indexToBlockId);

Expand Down Expand Up @@ -137,28 +136,39 @@ const VisualizerInstance: React.FC<RouteComponentProps<VisualizerRouteProps>> =

const Y = generateY(secondsFromStart, bpsCounter.getBPS());

const position: [number, number, number] = [
randomIntFromInterval(emitterBox.min.x, emitterBox.max.x),
Y,
randomIntFromInterval(emitterBox.min.z, emitterBox.max.z),
];
const targetPosition = {
x: randomIntFromInterval(emitterBox.min.x, emitterBox.max.x),
y: Y,
z: randomIntFromInterval(emitterBox.min.z, emitterBox.max.z),
};

bpsCounter.addBlock();
if (!bpsCounter.getBPS()) {
bpsCounter.start();
}

addBlock({
id: blockData.blockId,
position,
color: PENDING_BLOCK_COLOR
});

blockIdToPosition.set(blockData.blockId, position);
blockMetadata.set(blockData.blockId, blockData);

addToEdgeQueue(blockData.blockId, blockData.parents ?? []);
addYPosition(Y);

const emitterCenter = new THREE.Vector3();
emitterBox.getCenter(emitterCenter);

addBlock({
id: blockData.blockId,
color: PENDING_BLOCK_COLOR,
targetPosition: {
x: targetPosition.x,
y: targetPosition.y,
z: targetPosition.z
},
initPosition: {
x: emitterCenter.x,
y: emitterCenter.y,
z: emitterCenter.z
}
});
}
};

Expand Down
1 change: 1 addition & 0 deletions client/src/features/visualizer-threejs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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 Down
61 changes: 50 additions & 11 deletions client/src/features/visualizer-threejs/store/tangle.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { Color } from "three";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { ZOOM_DEFAULT } from "../constants";
import {IFeedBlockData} from "../../../models/api/stardust/feed/IFeedBlockData";
import { ZOOM_DEFAULT, ANIMATION_TIME_SECONDS } from "../constants";
import { IFeedBlockData } from "~models/api/stardust/feed/IFeedBlockData";

interface BlockState {
interface IPosition {
x: number;
y: number;
z: number;
}

export interface IBlockInitPosition extends IPosition {
duration: number;
}

export interface BlockState {
id: string;
position: [x: number, y: number, z: number];
color: Color;
}

Expand All @@ -23,7 +32,7 @@ interface EdgeEntry {
interface TangleState {
// Queue for "add block" operation to the canvas
blockQueue: BlockState[];
addToBlockQueue: (newBlock: BlockState) => void;
addToBlockQueue: (newBlock: BlockState & { initPosition: IPosition; targetPosition: IPosition; }) => void;
removeFromBlockQueue: (blockIds: string[]) => void;

edgeQueue: Edge[];
Expand All @@ -39,6 +48,7 @@ interface TangleState {
blockIdToEdges: Map<string, EdgeEntry>;
blockIdToPosition: Map<string, [x: number, y: number, z: number]>;
blockMetadata: Map<string, IFeedBlockData>;
blockIdToAnimationPosition: Map<string, IBlockInitPosition>;

indexToBlockId: string[];
updateBlockIdToIndex: (blockId: string, index: number) => void;
Expand All @@ -55,6 +65,8 @@ interface TangleState {

clickedInstanceId: string | null;
setClickedInstanceId: (instanceId: string | null) => void;

updateBlockIdToAnimationPosition: (updatedPositions: Map<string, IBlockInitPosition>) => void;
}

export const useTangleStore = create<TangleState>()(devtools(set => ({
Expand All @@ -65,19 +77,46 @@ export const useTangleStore = create<TangleState>()(devtools(set => ({
blockIdToIndex: new Map(),
blockIdToPosition: new Map(),
blockMetadata: new Map(),
blockIdToAnimationPosition: new Map(),
indexToBlockId: [],
yPositions: {},
zoom: ZOOM_DEFAULT,
bps: 0,
clickedInstanceId: null,
addToBlockQueue: newBlockData => {
set(state => ({
blockQueue: [...state.blockQueue, newBlockData]
}));
updateBlockIdToAnimationPosition: (updatedPositions) => {
set(state => {
updatedPositions.forEach((value, key) => {
state.blockIdToAnimationPosition.set(key, value);
});

for (const [key, value] of state.blockIdToAnimationPosition) {
if (value.duration > ANIMATION_TIME_SECONDS) {
state.blockIdToAnimationPosition.delete(key);
}
}
return {
blockIdToAnimationPosition: state.blockIdToAnimationPosition
};
});
},
addToBlockQueue: block => {
set(state => {
const { initPosition, targetPosition, ...blockRest } = block;

state.blockIdToPosition.set(block.id, [targetPosition.x, targetPosition.y, targetPosition.z]);
state.blockIdToAnimationPosition.set(block.id, {
...initPosition,
duration: 0,
});
return {
...state,
blockQueue: [...state.blockQueue, blockRest]
};
});
},
removeFromBlockQueue: (blockIds: string[]) => {
set(state => ({
...state,
if (!blockIds.length) return;
set((state) => ({
blockQueue: state.blockQueue.filter(b => !blockIds.includes(b.id))
}));
},
Expand Down
110 changes: 83 additions & 27 deletions client/src/features/visualizer-threejs/useRenderTangle.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useThree } from "@react-three/fiber";
import { useEffect, useRef } from "react";
import * as THREE from "three";
import { MAX_BLOCK_INSTANCES, NODE_SIZE_DEFAULT } from "./constants";
import { MAX_BLOCK_INSTANCES, NODE_SIZE_DEFAULT, ANIMATION_TIME_SECONDS } from "./constants";
import { useMouseMove } from "./hooks/useMouseMove";
import { useTangleStore } from "./store";
import { BlockState, IBlockInitPosition, useConfigStore, useTangleStore } from "./store";
import { useRenderEdges } from "./useRenderEdges";

const SPHERE_GEOMETRY = new THREE.SphereGeometry(NODE_SIZE_DEFAULT, 32, 16);
Expand All @@ -25,6 +25,8 @@ export const useRenderTangle = () => {

const blockIdToIndex = useTangleStore(s => s.blockIdToIndex);
const updateBlockIdToIndex = useTangleStore(s => s.updateBlockIdToIndex);
const blockIdToPosition = useTangleStore(s => s.blockIdToPosition);
const blockIdToAnimationPosition = useTangleStore(s => s.blockIdToAnimationPosition);

const updateBlockColor = (blockId: string, color: THREE.Color): void => {
const indexToUpdate = blockIdToIndex.get(blockId);
Expand All @@ -38,15 +40,83 @@ export const useRenderTangle = () => {
}
};

function updateInstancedMeshPosition(instancedMesh: THREE.InstancedMesh<THREE.SphereGeometry, THREE.MeshPhongMaterial>, index: number, nextPosition: THREE.Vector3) {
const matrix = new THREE.Matrix4();
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3();
instancedMesh.getMatrixAt(index, matrix);
matrix.decompose(position, quaternion, scale);
matrix.compose(nextPosition, quaternion, scale);
instancedMesh.setMatrixAt(index, matrix);
instancedMesh.instanceMatrix.needsUpdate = true;
}

const assignBlockToMesh = (block: BlockState) => {
const initPosition = blockIdToAnimationPosition.get(block.id);

if (!initPosition) return;

SPHERE_TEMP_OBJECT.position.set(
initPosition.x,
initPosition.y,
initPosition.z,
);
SPHERE_TEMP_OBJECT.scale.setScalar(INITIAL_SPHERE_SCALE);
SPHERE_TEMP_OBJECT.updateMatrix();

updateBlockIdToIndex(block.id, objectIndexRef.current);

tangleMeshRef.current.setMatrixAt(objectIndexRef.current, SPHERE_TEMP_OBJECT.matrix);
tangleMeshRef.current.setColorAt(objectIndexRef.current, block.color);

// Reuses old indexes when MAX_INSTANCES is reached
// This also makes it so that old nodes are removed
if (objectIndexRef.current < MAX_BLOCK_INSTANCES - 1) {
objectIndexRef.current += 1;
} else {
objectIndexRef.current = 0;
}

return block.id;
}

useRenderEdges();
useMouseMove({ tangleMeshRef });

const st = useThree(state => state);

/** Spray animation */
useEffect(() => {
// @ts-expect-error: It's fine
window.st = st;
}, [st]);
const PERIOD = 24; // ms

const int = setInterval(() => {
const isPlaying = useConfigStore.getState().isPlaying;
if (!isPlaying) {
return;
}
const blockIdToAnimationPosition = useTangleStore.getState().blockIdToAnimationPosition;
const updateBlockIdToAnimationPosition = useTangleStore.getState().updateBlockIdToAnimationPosition;
const delta = PERIOD / 1000;

const updatedAnimationPositions: Map<string, IBlockInitPosition> = new Map();
blockIdToAnimationPosition.forEach(({ x, y, z, duration: currentTime }, blockId) => {
const nextTime = currentTime + delta;
const startPositionVector = new THREE.Vector3(x, y, z);
const endPositionVector = new THREE.Vector3(...blockIdToPosition.get(blockId) as [number, number, number]);
const interpolationFactor = Math.min(nextTime / ANIMATION_TIME_SECONDS, 1); // set 1 as max value

const targetPositionVector = new THREE.Vector3();
targetPositionVector.lerpVectors(startPositionVector, endPositionVector, interpolationFactor)
updatedAnimationPositions.set(blockId, { x, y, z, duration: nextTime });
const index = blockIdToIndex.get(blockId);
if (index) {
updateInstancedMeshPosition(tangleMeshRef.current, index, targetPositionVector);
}
});
updateBlockIdToAnimationPosition(updatedAnimationPositions);
}, PERIOD);

return () => clearInterval(int);
}, []);

useEffect(() => {
const intervalCallback = () => {
Expand Down Expand Up @@ -75,6 +145,8 @@ export const useRenderTangle = () => {
}
}, [tangleMeshRef]);



useEffect(() => {
if (blockQueue.length === 0) {
return;
Expand All @@ -83,27 +155,11 @@ export const useRenderTangle = () => {
const addedIds = [];

for (const block of blockQueue) {
const [x, y, z] = block.position;
const color = block.color;

SPHERE_TEMP_OBJECT.position.set(x, y, z);
SPHERE_TEMP_OBJECT.scale.setScalar(INITIAL_SPHERE_SCALE);
SPHERE_TEMP_OBJECT.updateMatrix();
const assignedBlockId = assignBlockToMesh(block);

updateBlockIdToIndex(block.id, objectIndexRef.current);

tangleMeshRef.current.setMatrixAt(objectIndexRef.current, SPHERE_TEMP_OBJECT.matrix);
tangleMeshRef.current.setColorAt(objectIndexRef.current, color);

// Reuses old indexes when MAX_INSTANCES is reached
// This also makes it so that old nodes are removed
if (objectIndexRef.current < MAX_BLOCK_INSTANCES - 1) {
objectIndexRef.current += 1;
} else {
objectIndexRef.current = 0;
if (assignedBlockId) {
addedIds.push(assignedBlockId);
}

addedIds.push(block.id);
}

if (tangleMeshRef.current.instanceColor) {
Expand All @@ -114,7 +170,7 @@ export const useRenderTangle = () => {
tangleMeshRef.current.computeBoundingSphere();

removeFromBlockQueue(addedIds);
}, [blockQueue]);
}, [blockQueue, blockIdToAnimationPosition]);

useEffect(() => {
if (colorQueue.length === 0) {
Expand Down

0 comments on commit 72a6a6b

Please sign in to comment.