File size: 3,893 Bytes
d2226ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { useMemo, useState } from "react";
import { Line, Text, Billboard } from "@react-three/drei";
import { CONSTELLATIONS } from "../../data/constellations";

export function Constellations({ data, visible, showLabels, onSelect, tiers }) {
  const [hovered, setHovered] = useState(null);

  const { constellationGroups } = useMemo(() => {
    if (!data) return { constellationGroups: [] };

    const radius = 99.5;
    const starMap = new Map();
    data.ids.forEach((id, index) => starMap.set(id, index));

    const groups = [];

    CONSTELLATIONS.forEach((c) => {
      const tier = c.tier || 3;
      if (tier === 1 && !tiers.tier1) return;
      if (tier === 2 && !tiers.tier2) return;
      if (tier === 3 && !tiers.tier3) return;

      const lines = [];
      if (c.lines) {
        c.lines.forEach(([start, end]) => {
          const idxA = starMap.get(start);
          const idxB = starMap.get(end);

          if (idxA !== undefined && idxB !== undefined) {
            lines.push([
              getPosition(data, idxA, radius),
              getPosition(data, idxB, radius),
            ]);
          }
        });
      }

      let labelPos = null;
      if (c.anchor) {
        const anchorIdx = starMap.get(c.anchor);
        if (anchorIdx !== undefined) {
          labelPos = getPosition(data, anchorIdx, radius);
        }
      }

      groups.push({ data: c, lines, labelPos });
    });

    return { constellationGroups: groups };
  }, [data, tiers]);

  function getPosition(data, index, r) {
    const alt = data.altitude[index];
    const az = data.azimuth[index];
    const phi = (90 - alt) * (Math.PI / 180);
    const theta = az * (Math.PI / 180);
    return [
      r * Math.sin(phi) * Math.sin(theta),
      r * Math.cos(phi),
      -r * Math.sin(phi) * Math.cos(theta),
    ];
  }

  return (
    <group visible={visible}>
      {constellationGroups.map((group, i) => (
        <group key={i}>
          {group.lines.map((points, j) => (
            <Line
              key={j}
              points={points}
              color={
                hovered === group.data.name
                  ? "#ffd700"
                  : "rgba(255, 255, 255, 0.2)"
              }
              lineWidth={hovered === group.data.name ? 2 : 1}
              transparent
              opacity={hovered === group.data.name ? 0.8 : 0.3}
              onPointerOver={(e) => {
                e.stopPropagation();
                setHovered(group.data.name);
                document.body.style.cursor = "pointer";
              }}
              onPointerOut={(e) => {
                setHovered(null);
                document.body.style.cursor = "auto";
              }}
              onClick={(e) => {
                e.stopPropagation();
                onSelect(group.data);
              }}
            />
          ))}

          {showLabels && group.labelPos && (
            <Billboard position={group.labelPos}>
              <Text
                fontSize={hovered === group.data.name ? 2.5 : 1.8}
                color={hovered === group.data.name ? "#ffd700" : "#88ccff"}
                anchorX="center"
                anchorY="middle"
                fillOpacity={hovered === group.data.name ? 1 : 0.6}
                onPointerOver={(e) => {
                  e.stopPropagation();
                  setHovered(group.data.name);
                  document.body.style.cursor = "pointer";
                }}
                onPointerOut={(e) => {
                  setHovered(null);
                  document.body.style.cursor = "auto";
                }}
                onClick={(e) => {
                  e.stopPropagation();
                  onSelect(group.data);
                }}
              >
                {group.data.name.toUpperCase()}
              </Text>
            </Billboard>
          )}
        </group>
      ))}
    </group>
  );
}