Вопрос или проблема
Я пытаюсь создать маленькую диаграмму узлов с использованием react-three-fiber. В ней будет всего несколько узлов, и я хотел бы соединить каждый узел цилиндром (цилиндры представляют собой связь между узлами). Пример можно увидеть здесь:
import "./styles.css";
import { Canvas } from "@react-three/fiber";
export default function App() {
const color = "rgb(255,0,0)";
return (
<div className="App" style={{ height: "600px" }}>
<Canvas camera={{ position: [0, 0, 6.5] }}>
<ambientLight intensity={0.75} />
<directionalLight intensity={2.25} position={[1, 1, 5]} />
<group>
<mesh position={[1, 0, 2]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh position={[-1, -1, 1]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh position={[1, -1, 0]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<group position={[1, -1, 2]}>
<mesh rotation={[Math.PI / 4 / 3, 0, (-Math.PI / 4 / 3) * 2]}>
<cylinderGeometry args={[0.1, 0.1, 3, 32]} />
<meshStandardMaterial color={color} />
</mesh>
</group>
</group>
</Canvas>
</div>
);
}
Демо: https://codesandbox.io/p/sandbox/p7qkq2
Тем не менее, мне очень трудно позиционировать цилиндр между двумя узлами. Учитывая позицию двух мешей, как мне определить, как разместить цилиндр между ними?
Вот пример того, как соединить два меша с помощью цилиндра, который модифицирует оригинальный пример, который я привел выше:
import React, { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import * as THREE from "three";
function positionCylinder(sphere1Ref, sphere2Ref, cylinderRef) {
if (sphere1Ref.current && sphere2Ref.current && cylinderRef.current) {
// Обновите позицию и вращение цилиндра, чтобы соединить сферы
const start = sphere1Ref.current.position;
const end = sphere2Ref.current.position;
const direction = end.clone().sub(start);
const center = start.clone().add(direction.clone().multiplyScalar(0.5));
cylinderRef.current.position.copy(center);
cylinderRef.current.scale.set(0.5, direction.length() / 2, 0.5);
cylinderRef.current.quaternion.setFromUnitVectors(
new THREE.Vector3(0, 1, 0),
direction.clone().normalize()
);
}
}
const SpheresAndCylinder = () => {
const sphere1Ref = useRef();
const sphere2Ref = useRef();
const sphere3Ref = useRef();
const cylinder1Ref = useRef();
const cylinder2Ref = useRef();
const sphere1Position = useMemo(() => new THREE.Vector3(1, 0, 2), []);
const sphere2Position = useMemo(() => new THREE.Vector3(-1, -1, 1), []);
const sphere3Position = useMemo(() => new THREE.Vector3(1, -1, 0), []);
useFrame(() => {
positionCylinder(sphere1Ref, sphere2Ref, cylinder1Ref);
positionCylinder(sphere2Ref, sphere3Ref, cylinder2Ref);
});
const color = "rgb(255,0,0)";
return (
<>
<mesh ref={sphere1Ref} position={[1, 0, 2]}>
<sphereGeometry args={[0.5, 32, 32]} position={sphere1Position} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={sphere2Ref} position={sphere2Position}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={sphere3Ref} position={sphere3Position}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={cylinder1Ref}>
<cylinderGeometry args={[0.1, 0.1, 1, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={cylinder2Ref}>
<cylinderGeometry args={[0.1, 0.1, 1, 32]} />
<meshStandardMaterial color={color} />
</mesh>
</>
);
};
const Scene = () => {
return (
<div style={{ height: "400px" }}>
<Canvas camera={{ position: [0, 0, 6.5] }}>
<ambientLight intensity={0.75} />
<directionalLight intensity={2.25} position={[1, 1, 5]} />
<SpheresAndCylinder />
</Canvas>
</div>
);
};
export default Scene;
Особая благодарность @AndyRay и его комментарию в моем вопросе, который привел меня к ответу! Надеюсь, это поможет кому-то еще, кто столкнется с этой проблемой.
Ответ или решение
Чтобы разместить цилиндр между двумя мешами в react-three-fiber, необходимо правильно вычислить его позицию и ориентацию на основе позиций двух кубов (или сфер). Ваша задача заключается в том, чтобы создать цилиндр, который будет соединять эти две точки. Вот как вы можете это сделать:
-
Определите позиции двух мешей, которые будут соединены цилиндром. Для этого вы можете использовать ссылки (refs) на ваши объекты.
-
Вычислите направление и положение цилиндра. Для этого надо вычесть позицию одного меша из другой, чтобы получить вектор направления. Затем определите среднюю точку между этими двумя позициями, где будет располагаться цилиндр.
-
Установите масштаб и вращение цилиндра. Вам нужно будет задать высоту цилиндра в соответствии с длиной расстояния между двумя мешами и правильно его повернуть, чтобы он был ориентирован по направлению.
Вот полный пример кода, который продемонстрирует эти шаги:
import React, { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import * as THREE from "three";
function positionCylinder(sphere1Ref, sphere2Ref, cylinderRef) {
if (sphere1Ref.current && sphere2Ref.current && cylinderRef.current) {
const start = sphere1Ref.current.position.clone();
const end = sphere2Ref.current.position.clone();
// Вычисляем направление
const direction = end.clone().sub(start);
const center = start.clone().add(direction.clone().multiplyScalar(0.5));
// Устанавливаем позицию цилиндра
cylinderRef.current.position.copy(center);
cylinderRef.current.scale.set(0.1, direction.length() / 2, 0.1);
// Устанавливаем вращение цилиндра
const quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.clone().normalize());
cylinderRef.current.quaternion.copy(quaternion);
}
}
const SpheresAndCylinder = () => {
const sphere1Ref = useRef();
const sphere2Ref = useRef();
const cylinderRef = useRef();
const sphere1Position = useMemo(() => new THREE.Vector3(1, 0, 2), []);
const sphere2Position = useMemo(() => new THREE.Vector3(-1, -1, 1), []);
useFrame(() => {
positionCylinder(sphere1Ref, sphere2Ref, cylinderRef);
});
const color = "rgb(255,0,0)";
return (
<>
<mesh ref={sphere1Ref} position={[1, 0, 2]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={sphere2Ref} position={sphere2Position}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color={color} />
</mesh>
<mesh ref={cylinderRef}>
<cylinderGeometry args={[0.1, 0.1, 1, 32]} />
<meshStandardMaterial color={color} />
</mesh>
</>
);
};
const Scene = () => {
return (
<div style={{ height: "400px" }}>
<Canvas camera={{ position: [0, 0, 6.5] }}>
<ambientLight intensity={0.75} />
<directionalLight intensity={2.25} position={[1, 1, 5]} />
<SpheresAndCylinder />
</Canvas>
</div>
);
};
export default Scene;
Пояснения:
- Позиции мешей — они определены с использованием
ref
, что позволяет нам легко обращаться к их позициям. - Функция
positionCylinder
— она предназначена для вычисления позиции и ориентации цилиндра на основании позиций двух сфер. - Использование
useFrame
— это позволяет обновлять позицию и ориентацию цилиндра в каждом кадре, что делает их связь динамичной.
Запустите этот код, и вы увидите, как цилиндры корректно соединяют две сферы, обеспечивая визуальную нить между ними.