visualiser2 / src /components /edges /EdgeGeometry.tsx
Vishalpainjane's picture
added files
8a01471
import { useMemo, useRef } from 'react';
import * as THREE from 'three';
import { useFrame } from '@react-three/fiber';
import { Line, QuadraticBezierLine } from '@react-three/drei';
import type { ComputedEdge } from '@/core/store';
import type { Position3D } from '@/schema/types';
/**
* Edge style type
*/
export type EdgeStyle = 'line' | 'tube' | 'arrow' | 'bezier';
/**
* Props for edge components
*/
export interface EdgeProps {
edge: ComputedEdge;
style?: EdgeStyle;
animated?: boolean;
flowSpeed?: number;
}
/**
* Calculate control points for bezier curves
*/
function getBezierControlPoints(
start: Position3D,
end: Position3D
): { mid1: Position3D; mid2: Position3D } {
const midY = (start.y + end.y) / 2;
const offsetZ = Math.abs(end.y - start.y) * 0.3;
return {
mid1: { x: start.x, y: midY, z: start.z + offsetZ },
mid2: { x: end.x, y: midY, z: end.z + offsetZ },
};
}
/**
* Simple line edge
*/
export function LineEdge({ edge }: EdgeProps) {
const points = useMemo(() => [
new THREE.Vector3(edge.sourcePosition.x, edge.sourcePosition.y, edge.sourcePosition.z),
new THREE.Vector3(edge.targetPosition.x, edge.targetPosition.y, edge.targetPosition.z),
], [edge.sourcePosition, edge.targetPosition]);
const color = edge.highlighted ? '#ffffff' : edge.color;
const lineWidth = edge.highlighted ? 3 : 1.5;
return (
<Line
points={points}
color={color}
lineWidth={lineWidth}
opacity={edge.visible ? 1 : 0.2}
transparent
/>
);
}
/**
* Bezier curve edge for smoother connections
*/
export function BezierEdge({ edge }: EdgeProps) {
const start = useMemo(() =>
new THREE.Vector3(edge.sourcePosition.x, edge.sourcePosition.y, edge.sourcePosition.z),
[edge.sourcePosition]
);
const end = useMemo(() =>
new THREE.Vector3(edge.targetPosition.x, edge.targetPosition.y, edge.targetPosition.z),
[edge.targetPosition]
);
const control = useMemo(() => {
const { mid1 } = getBezierControlPoints(edge.sourcePosition, edge.targetPosition);
return new THREE.Vector3(mid1.x, mid1.y, mid1.z);
}, [edge.sourcePosition, edge.targetPosition]);
const color = edge.highlighted ? '#ffffff' : edge.color;
const lineWidth = edge.highlighted ? 3 : 1.5;
return (
<QuadraticBezierLine
start={start}
end={end}
mid={control}
color={color}
lineWidth={lineWidth}
opacity={edge.visible ? 1 : 0.2}
transparent
/>
);
}
/**
* Tube edge for 3D pipe-like connections
*/
export function TubeEdge({ edge, animated = false, flowSpeed = 1 }: EdgeProps) {
const tubeRef = useRef<THREE.Mesh>(null);
// Create curve for tube
const curve = useMemo(() => {
const start = new THREE.Vector3(
edge.sourcePosition.x,
edge.sourcePosition.y,
edge.sourcePosition.z
);
const end = new THREE.Vector3(
edge.targetPosition.x,
edge.targetPosition.y,
edge.targetPosition.z
);
const { mid1, mid2 } = getBezierControlPoints(edge.sourcePosition, edge.targetPosition);
const control1 = new THREE.Vector3(mid1.x, mid1.y, mid1.z);
const control2 = new THREE.Vector3(mid2.x, mid2.y, mid2.z);
return new THREE.CubicBezierCurve3(start, control1, control2, end);
}, [edge.sourcePosition, edge.targetPosition]);
// Tube geometry
const geometry = useMemo(() => {
const radius = edge.highlighted ? 0.06 : 0.03;
return new THREE.TubeGeometry(curve, 32, radius, 8, false);
}, [curve, edge.highlighted]);
// Animate flow effect
useFrame(({ clock }) => {
if (animated && tubeRef.current) {
const material = tubeRef.current.material as THREE.MeshStandardMaterial;
if (material.map) {
material.map.offset.y = clock.getElapsedTime() * flowSpeed;
}
}
});
const color = edge.highlighted ? '#ffffff' : edge.color;
return (
<mesh ref={tubeRef} geometry={geometry}>
<meshStandardMaterial
color={color}
opacity={edge.visible ? 0.8 : 0.2}
transparent
metalness={0.3}
roughness={0.6}
/>
</mesh>
);
}
/**
* Arrow edge with direction indicator
*/
export function ArrowEdge({ edge }: EdgeProps) {
const start = new THREE.Vector3(
edge.sourcePosition.x,
edge.sourcePosition.y,
edge.sourcePosition.z
);
const end = new THREE.Vector3(
edge.targetPosition.x,
edge.targetPosition.y,
edge.targetPosition.z
);
const direction = end.clone().sub(start).normalize();
const arrowPosition = end.clone().sub(direction.clone().multiplyScalar(0.3));
const color = edge.highlighted ? '#ffffff' : edge.color;
// Calculate arrow rotation
const quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction);
return (
<group>
<LineEdge edge={edge} />
{/* Arrow head */}
<mesh position={arrowPosition} quaternion={quaternion}>
<coneGeometry args={[0.1, 0.2, 8]} />
<meshStandardMaterial color={color} />
</mesh>
</group>
);
}
/**
* Factory function to get edge component by style
*/
export function getEdgeComponent(style: EdgeStyle): React.ComponentType<EdgeProps> {
switch (style) {
case 'line':
return LineEdge;
case 'bezier':
return BezierEdge;
case 'tube':
return TubeEdge;
case 'arrow':
return ArrowEdge;
default:
return TubeEdge;
}
}