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: Refactor Visualizer components and improve Block removal #798

Merged
4 changes: 2 additions & 2 deletions client/src/app/routes/stardust/VisualizerContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import { RouteComponentProps } from "react-router-dom";
import VisualizerThree from "../../../features/visualizer-threejs/VisualizerThree";
import VisualizerThree from "../../../features/visualizer-threejs/VisualizerMain";
import { VisualizerRouteProps } from "../VisualizerRouteProps";
import { VisualizerDefault } from "./VisualizerDefault";

Expand All @@ -11,7 +11,7 @@ enum Views {

export const VisualizerContainer: React.FC<
RouteComponentProps<VisualizerRouteProps>
> = (props) => {
> = props => {
const [currentView] = useState(Views.three);

if (currentView === Views.default) {
Expand Down
86 changes: 32 additions & 54 deletions client/src/features/visualizer-threejs/Emitter.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,54 @@
import { useFrame, useThree } from "@react-three/fiber";
import React, { MutableRefObject, useEffect, useRef } from "react";
import React, { RefObject, Dispatch, SetStateAction, useEffect } from "react";
import * as THREE from "three";
import { Box3 } from "three";
import { IFeedBlockData } from "../../models/api/stardust/feed/IFeedBlockData";
import { colors } from "../../shared/visualizer/common/constants";
import { getGenerateY, randomIntFromInterval, timer } from "../../shared/visualizer/common/utils";
import { UpdateListenerReturn } from "../../shared/visualizer/startdust/hooks";
import { TFeedBlockAdd } from "../../shared/visualizer/startdust/types";
import { useBlockStore } from "./store";
import { useBorderPositions } from "./hooks/useBorderPositions";

interface EmitterProps {
refOnNewBlock: MutableRefObject<TFeedBlockAdd | null>;
setOnNewExists: UpdateListenerReturn["setOnNewExists"];
setRunListeners: Dispatch<SetStateAction<boolean>>;
emitterRef: RefObject<THREE.Mesh>;
}

const timerDiff = timer(250);

const Emitter: React.FC<EmitterProps> = ({ refOnNewBlock, setOnNewExists }) => {
const ref = useRef<THREE.Mesh>(null);
const { addBlock, addParents, addYPosition, checkZoom } = useBlockStore();
const viewport = useThree(state => state.viewport);
const canvasWidth = viewport.width;
const generateY = getGenerateY({ withRandom: true });

const onNewBlock = (blockData: IFeedBlockData) => {
const emitterObj = ref.current;
if (emitterObj) {
const emitterBox = new Box3().setFromObject(emitterObj);
const secondsFromStart = timerDiff();

const Y = generateY(secondsFromStart);

const position: [number, number, number] = [
randomIntFromInterval(emitterBox.min.x, emitterBox.max.x),
Y,
randomIntFromInterval(emitterBox.min.z, emitterBox.max.z)
];
addBlock({
id: blockData.blockId,
position
}, {
color: colors[randomIntFromInterval(0, colors.length - 1)],
scale: 1
});
addParents(blockData.blockId, blockData.parents ?? []);
addYPosition(Y);
checkZoom();
const Emitter = ({ setRunListeners, emitterRef }: EmitterProps) => {
useEffect(() => {
if (emitterRef?.current) {
setRunListeners(true);
}
};
}, [emitterRef]);


/**
* Camera shift
*/
const { halfScreenWidth } = useBorderPositions();
const get = useThree(state => state.get);
useFrame(() => {
const camera = get().camera;
const emitterObj = get().scene.getObjectByName("emitter");
if (camera && emitterObj) {
const EMITTER_PADDING_RIGHT = 150;
camera.position.x = emitterObj.position.x - halfScreenWidth + EMITTER_PADDING_RIGHT;
}
});

/**
* Emitter shift
*/
useFrame((_, delta) => {
if (ref.current) {
ref.current.position.x += delta * 80;
if (emitterRef?.current) {
const DELTA_MULTIPLIER = 80; // depends on this param we can manage speed of emitter
emitterRef.current.position.x += delta * DELTA_MULTIPLIER;
}
});

useEffect(() => {
// Set handler for new block
refOnNewBlock.current = onNewBlock;
setOnNewExists(true);
}, []);

return (
<mesh
ref={ref}
ref={emitterRef}
name="emitter"
position={[(canvasWidth / 2) - 100, 0, 0]}
position={[0, 0, 0]}
>
<boxGeometry args={[30, 150, 150]} />
<meshPhongMaterial transparent={true} opacity={0.6} />
</mesh>
);
};

export default Emitter;
67 changes: 0 additions & 67 deletions client/src/features/visualizer-threejs/EmitterContext.tsx

This file was deleted.

15 changes: 10 additions & 5 deletions client/src/features/visualizer-threejs/Sphere.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Instance } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import React, { useRef, useState } from "react";
import * as THREE from "three";
import { useBorderPositions } from "./hooks/useBorderPositions";
import { useBlockStore } from "./store";

interface SphereProps {
Expand All @@ -11,24 +13,27 @@ interface SphereProps {
}

const Sphere: React.FC<SphereProps> = ({ id, position, color, scale }) => {
const { removeBlock, removeYPosition } = useBlockStore();
const zoom = useBlockStore(s => s.zoom);
const removeBlock = useBlockStore(s => s.removeBlock);
const removeYPosition = useBlockStore(s => s.removeYPosition);
const { halfScreenWidth } = useBorderPositions();
const ref = useRef<THREE.Mesh>(null);
const get = useThree(state => state.get);
const viewport = useThree(state => state.viewport);
const [hovered, hover] = useState(false);
const [clicked, click] = useState(false);

const canvasWidth = viewport.width;

useFrame(() => {
const camera = get().camera;
const PADDING_AFTER_OUT_OF_SCREEN = 50 / zoom;
const LEFT_BORDER = camera.position.x - halfScreenWidth - PADDING_AFTER_OUT_OF_SCREEN;
if (
ref.current &&
camera &&
ref.current.position?.x < camera.position.x - canvasWidth
ref.current.position?.x < LEFT_BORDER
) {
removeBlock(id);
removeYPosition(position[1]);
ref.current.remove();
}
});

Expand Down
36 changes: 36 additions & 0 deletions client/src/features/visualizer-threejs/Spheres.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Instances } from "@react-three/drei";
import React from "react";
import { NODE_SIZE_DEFAULT } from "./constants";
import { useZoomDynamic } from "./hooks/useZoomDynamic";
import Sphere from "./Sphere";
import { useBlockStore } from "./store";

const Spheres = () => {
const blocks = useBlockStore(s => s.blocks);
const blockOptions = useBlockStore(s => s.blockOptions);
useZoomDynamic();

return (
<Instances
limit={2500}
range={2500}
frustumCulled={false}
>
<sphereGeometry args={[NODE_SIZE_DEFAULT]} />
<meshPhongMaterial />
{
blocks.map(block => (
<Sphere
key={block.id}
id={block.id}
position={block.position}
color={blockOptions[block.id].color}
scale={blockOptions[block.id].scale}
/>
))
}
</Instances>
);
};

export default Spheres;
Loading