File size: 2,329 Bytes
d9bb6b1 5a3b695 d9bb6b1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import { Billboard, Text } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useRef } from "react";
import type { Group } from "three";
import { FLY_EXTENT_MM, type FlyInstanceData } from "./useFlyLayout";
import type { LiveFlyPositions } from "./useLiveFlyState";
// A fixed, room-scale size (NOT derived from the fly's own tiny body
// extent) -- at the room's scale (thousands of mm across), a label sized
// proportionally to a ~4mm fly is sub-millimeter and unreadable from any
// normal viewing distance. Just above the fly's own bounding box, so the
// label clears the body/wings instead of overlapping them.
const LABEL_HEIGHT_MM = FLY_EXTENT_MM[1] + 15;
const FONT_SIZE_MM = 50;
interface FlyLabelsProps {
flies: FlyInstanceData[];
livePositionsRef: React.RefObject<LiveFlyPositions>;
}
// Own small component (not folded into FlyInstances.tsx) so its per-frame
// position sync stays simple: only the selected fly ever moves, but every
// label still needs to track its own fly's live x/y/z each frame the same
// way, driven off the same livePositionsRef the rest of the scene reads.
export default function FlyLabels({ flies, livePositionsRef }: FlyLabelsProps) {
const groupRefs = useRef(new Map<string, Group>());
useFrame(() => {
const livePositions = livePositionsRef.current;
for (const fly of flies) {
const group = groupRefs.current.get(fly.id);
const live = livePositions.get(fly.id);
if (!group || !live) continue;
group.position.set(live.x, live.y + LABEL_HEIGHT_MM, live.z);
}
});
return (
<>
{flies.map((fly) => (
<group
key={fly.id}
ref={(group: Group | null) => {
if (group) groupRefs.current.set(fly.id, group);
else groupRefs.current.delete(fly.id);
}}
position={[fly.position[0], fly.position[1] + LABEL_HEIGHT_MM, fly.position[2]]}
>
<Billboard>
<Text
fontSize={FONT_SIZE_MM}
color="#e8e9ee"
outlineWidth={FONT_SIZE_MM * 0.08}
outlineColor="#0b0c10"
anchorX="center"
anchorY="bottom"
>
Fly {fly.index + 1}
</Text>
</Billboard>
</group>
))}
</>
);
}
|