Skip to content

Commit 3a8f4c4

Browse files
Add globe countries and connections
Enhanced the 3D globe to display country outlines and simulate curved internet connections between points, representing client-server and server-server interactions for a more complex and realistic internet visualization.
1 parent e0b6352 commit 3a8f4c4

File tree

1 file changed

+218
-62
lines changed

1 file changed

+218
-62
lines changed

src/components/Globe3D.tsx

Lines changed: 218 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,127 @@ const Globe3D = () => {
1717

1818
let rotationX = 0;
1919
let rotationY = 0;
20-
const connections: Array<{ from: number; to: number; opacity: number; pulse: number }> = [];
21-
const points: Array<{ x: number; y: number; z: number; lat: number; lng: number; pulse: number }> = [];
22-
23-
// Generate random points on sphere surface
24-
const generatePoints = () => {
25-
for (let i = 0; i < 80; i++) {
26-
const lat = (Math.random() - 0.5) * Math.PI;
27-
const lng = Math.random() * 2 * Math.PI;
28-
points.push({
29-
x: 0, y: 0, z: 0,
30-
lat, lng,
31-
pulse: Math.random() * Math.PI * 2
32-
});
33-
}
34-
};
20+
21+
// Major cities and their approximate coordinates
22+
const cities = [
23+
{ name: 'New York', lat: 40.7128, lng: -74.0060, type: 'server' },
24+
{ name: 'London', lat: 51.5074, lng: -0.1278, type: 'server' },
25+
{ name: 'Tokyo', lat: 35.6762, lng: 139.6503, type: 'server' },
26+
{ name: 'Sydney', lat: -33.8688, lng: 151.2093, type: 'client' },
27+
{ name: 'Mumbai', lat: 19.0760, lng: 72.8777, type: 'client' },
28+
{ name: 'São Paulo', lat: -23.5505, lng: -46.6333, type: 'client' },
29+
{ name: 'Dubai', lat: 25.2048, lng: 55.2708, type: 'server' },
30+
{ name: 'Singapore', lat: 1.3521, lng: 103.8198, type: 'server' },
31+
{ name: 'Frankfurt', lat: 50.1109, lng: 8.6821, type: 'server' },
32+
{ name: 'Los Angeles', lat: 34.0522, lng: -118.2437, type: 'client' },
33+
{ name: 'Hong Kong', lat: 22.3193, lng: 114.1694, type: 'server' },
34+
{ name: 'Stockholm', lat: 59.3293, lng: 18.0686, type: 'client' },
35+
{ name: 'Cape Town', lat: -33.9249, lng: 18.4241, type: 'client' },
36+
{ name: 'Moscow', lat: 55.7558, lng: 37.6176, type: 'client' },
37+
{ name: 'Seoul', lat: 37.5665, lng: 126.9780, type: 'server' },
38+
{ name: 'Mexico City', lat: 19.4326, lng: -99.1332, type: 'client' },
39+
{ name: 'Toronto', lat: 43.6532, lng: -79.3832, type: 'client' },
40+
{ name: 'Berlin', lat: 52.5200, lng: 13.4050, type: 'server' },
41+
{ name: 'Paris', lat: 48.8566, lng: 2.3522, type: 'client' },
42+
{ name: 'Amsterdam', lat: 52.3676, lng: 4.9041, type: 'server' }
43+
];
44+
45+
const connections: Array<{
46+
from: number;
47+
to: number;
48+
opacity: number;
49+
pulse: number;
50+
speed: number;
51+
particlePosition: number;
52+
type: 'server-server' | 'client-server' | 'client-client';
53+
}> = [];
54+
55+
const points: Array<{
56+
x: number; y: number; z: number;
57+
lat: number; lng: number;
58+
pulse: number;
59+
name: string;
60+
type: 'server' | 'client';
61+
activity: number;
62+
}> = [];
63+
64+
// Convert cities to 3D points
65+
cities.forEach(city => {
66+
const lat = city.lat * Math.PI / 180;
67+
const lng = city.lng * Math.PI / 180;
68+
points.push({
69+
x: 0, y: 0, z: 0,
70+
lat, lng,
71+
pulse: Math.random() * Math.PI * 2,
72+
name: city.name,
73+
type: city.type,
74+
activity: Math.random()
75+
});
76+
});
3577

36-
// Generate connections between points
78+
// Generate intelligent connections based on real internet infrastructure patterns
3779
const generateConnections = () => {
38-
for (let i = 0; i < 60; i++) {
39-
const from = Math.floor(Math.random() * points.length);
40-
let to = Math.floor(Math.random() * points.length);
41-
while (to === from) {
42-
to = Math.floor(Math.random() * points.length);
80+
const servers = points.filter(p => p.type === 'server');
81+
const clients = points.filter(p => p.type === 'client');
82+
83+
// Server-to-server backbone connections (major internet hubs)
84+
for (let i = 0; i < servers.length; i++) {
85+
for (let j = i + 1; j < servers.length; j++) {
86+
if (Math.random() < 0.4) { // 40% chance for server-server connection
87+
const serverIndex1 = points.indexOf(servers[i]);
88+
const serverIndex2 = points.indexOf(servers[j]);
89+
connections.push({
90+
from: serverIndex1,
91+
to: serverIndex2,
92+
opacity: Math.random() * 0.3 + 0.4,
93+
pulse: Math.random() * Math.PI * 2,
94+
speed: Math.random() * 0.02 + 0.01,
95+
particlePosition: 0,
96+
type: 'server-server'
97+
});
98+
}
99+
}
100+
}
101+
102+
// Client-to-server connections (users connecting to services)
103+
clients.forEach(client => {
104+
const clientIndex = points.indexOf(client);
105+
// Each client connects to 1-3 servers
106+
const numConnections = Math.floor(Math.random() * 3) + 1;
107+
const shuffledServers = [...servers].sort(() => Math.random() - 0.5);
108+
109+
for (let i = 0; i < Math.min(numConnections, shuffledServers.length); i++) {
110+
const serverIndex = points.indexOf(shuffledServers[i]);
111+
connections.push({
112+
from: clientIndex,
113+
to: serverIndex,
114+
opacity: Math.random() * 0.2 + 0.2,
115+
pulse: Math.random() * Math.PI * 2,
116+
speed: Math.random() * 0.03 + 0.015,
117+
particlePosition: Math.random(),
118+
type: 'client-server'
119+
});
120+
}
121+
});
122+
123+
// Some client-to-client connections (P2P traffic)
124+
for (let i = 0; i < clients.length; i++) {
125+
if (Math.random() < 0.15) { // 15% chance for P2P connection
126+
const otherClient = clients[Math.floor(Math.random() * clients.length)];
127+
if (otherClient !== clients[i]) {
128+
const clientIndex1 = points.indexOf(clients[i]);
129+
const clientIndex2 = points.indexOf(otherClient);
130+
connections.push({
131+
from: clientIndex1,
132+
to: clientIndex2,
133+
opacity: Math.random() * 0.15 + 0.1,
134+
pulse: Math.random() * Math.PI * 2,
135+
speed: Math.random() * 0.025 + 0.02,
136+
particlePosition: Math.random(),
137+
type: 'client-client'
138+
});
139+
}
43140
}
44-
connections.push({
45-
from,
46-
to,
47-
opacity: Math.random() * 0.5 + 0.2,
48-
pulse: Math.random() * Math.PI * 2
49-
});
50141
}
51142
};
52143

@@ -86,7 +177,25 @@ const Globe3D = () => {
86177
};
87178
};
88179

89-
generatePoints();
180+
// Calculate point on sphere surface between two points for curved connections
181+
const getArcPoint = (point1: any, point2: any, t: number) => {
182+
// Spherical interpolation (slerp) for smooth curves on sphere surface
183+
const dot = point1.x * point2.x + point1.y * point2.y + point1.z * point2.z;
184+
const theta = Math.acos(Math.max(-1, Math.min(1, dot / (radius * radius))));
185+
186+
if (theta < 0.001) return point1; // Points too close
187+
188+
const sinTheta = Math.sin(theta);
189+
const a = Math.sin((1 - t) * theta) / sinTheta;
190+
const b = Math.sin(t * theta) / sinTheta;
191+
192+
return {
193+
x: a * point1.x + b * point2.x,
194+
y: a * point1.y + b * point2.y,
195+
z: a * point1.z + b * point2.z
196+
};
197+
};
198+
90199
generateConnections();
91200

92201
const draw = () => {
@@ -100,21 +209,22 @@ const Globe3D = () => {
100209
point.y = rotated.y;
101210
point.z = rotated.z;
102211
point.pulse += 0.05;
212+
point.activity = Math.sin(point.pulse * 0.7) * 0.3 + 0.7;
103213
});
104214

105-
// Draw globe wireframe (latitude and longitude lines)
106-
ctx.strokeStyle = 'rgba(0, 255, 0, 0.15)';
215+
// Draw globe wireframe (simplified for performance)
216+
ctx.strokeStyle = 'rgba(0, 255, 0, 0.1)';
107217
ctx.lineWidth = 1;
108218

109219
// Draw latitude lines
110-
for (let lat = -Math.PI/2; lat <= Math.PI/2; lat += Math.PI/8) {
220+
for (let lat = -Math.PI/2; lat <= Math.PI/2; lat += Math.PI/4) {
111221
ctx.beginPath();
112222
let firstPoint = true;
113-
for (let lng = 0; lng <= 2 * Math.PI; lng += 0.1) {
223+
for (let lng = 0; lng <= 2 * Math.PI; lng += 0.2) {
114224
const pos3D = sphericalTo3D(lat, lng);
115225
const rotated = rotate3D(pos3D.x, pos3D.y, pos3D.z);
116226

117-
if (rotated.z > -radius * 0.3) { // Only draw visible parts
227+
if (rotated.z > -radius * 0.3) {
118228
const projected = project3D(rotated.x, rotated.y, rotated.z);
119229
if (firstPoint) {
120230
ctx.moveTo(projected.x, projected.y);
@@ -130,14 +240,14 @@ const Globe3D = () => {
130240
}
131241

132242
// Draw longitude lines
133-
for (let lng = 0; lng < 2 * Math.PI; lng += Math.PI/8) {
243+
for (let lng = 0; lng < 2 * Math.PI; lng += Math.PI/4) {
134244
ctx.beginPath();
135245
let firstPoint = true;
136-
for (let lat = -Math.PI/2; lat <= Math.PI/2; lat += 0.1) {
246+
for (let lat = -Math.PI/2; lat <= Math.PI/2; lat += 0.2) {
137247
const pos3D = sphericalTo3D(lat, lng);
138248
const rotated = rotate3D(pos3D.x, pos3D.y, pos3D.z);
139249

140-
if (rotated.z > -radius * 0.3) { // Only draw visible parts
250+
if (rotated.z > -radius * 0.3) {
141251
const projected = project3D(rotated.x, rotated.y, rotated.z);
142252
if (firstPoint) {
143253
ctx.moveTo(projected.x, projected.y);
@@ -158,67 +268,113 @@ const Globe3D = () => {
158268
.filter(point => point.z > -radius * 0.5)
159269
.sort((a, b) => a.z - b.z);
160270

161-
// Draw connections first (behind points)
271+
// Draw curved connections with animated particles
162272
connections.forEach(connection => {
163273
const fromPoint = points[connection.from];
164274
const toPoint = points[connection.to];
165275

166276
if (fromPoint.z > -radius * 0.5 && toPoint.z > -radius * 0.5) {
167-
const fromProjected = project3D(fromPoint.x, fromPoint.y, fromPoint.z);
168-
const toProjected = project3D(toPoint.x, toPoint.y, toPoint.z);
277+
// Draw curved connection line
278+
ctx.strokeStyle = connection.type === 'server-server'
279+
? `rgba(0, 255, 0, ${connection.opacity * 0.8})`
280+
: connection.type === 'client-server'
281+
? `rgba(0, 200, 255, ${connection.opacity * 0.6})`
282+
: `rgba(255, 100, 0, ${connection.opacity * 0.4})`;
283+
ctx.lineWidth = connection.type === 'server-server' ? 2 : 1;
284+
285+
ctx.beginPath();
286+
let firstPoint = true;
287+
288+
// Draw arc between points
289+
for (let t = 0; t <= 1; t += 0.05) {
290+
const arcPoint = getArcPoint(fromPoint, toPoint, t);
291+
const projected = project3D(arcPoint.x, arcPoint.y, arcPoint.z);
292+
293+
if (firstPoint) {
294+
ctx.moveTo(projected.x, projected.y);
295+
firstPoint = false;
296+
} else {
297+
ctx.lineTo(projected.x, projected.y);
298+
}
299+
}
300+
ctx.stroke();
169301

170-
// Animate connection opacity with pulse effect
171-
connection.pulse += 0.03;
172-
const pulseOpacity = (Math.sin(connection.pulse) + 1) * 0.5;
173-
const opacity = connection.opacity * pulseOpacity * 0.6;
302+
// Animate particle along connection
303+
connection.particlePosition += connection.speed;
304+
if (connection.particlePosition > 1) {
305+
connection.particlePosition = 0;
306+
}
174307

175-
ctx.strokeStyle = `rgba(0, 255, 0, ${opacity})`;
176-
ctx.lineWidth = 1.5;
308+
// Draw moving particle
309+
const particlePoint = getArcPoint(fromPoint, toPoint, connection.particlePosition);
310+
const particleProjected = project3D(particlePoint.x, particlePoint.y, particlePoint.z);
311+
312+
const gradient = ctx.createRadialGradient(
313+
particleProjected.x, particleProjected.y, 0,
314+
particleProjected.x, particleProjected.y, 4
315+
);
316+
gradient.addColorStop(0, connection.type === 'server-server'
317+
? 'rgba(0, 255, 0, 0.8)'
318+
: 'rgba(0, 200, 255, 0.8)');
319+
gradient.addColorStop(1, 'rgba(0, 255, 0, 0)');
320+
321+
ctx.fillStyle = gradient;
177322
ctx.beginPath();
178-
ctx.moveTo(fromProjected.x, fromProjected.y);
179-
ctx.lineTo(toProjected.x, toProjected.y);
180-
ctx.stroke();
323+
ctx.arc(particleProjected.x, particleProjected.y, 3, 0, 2 * Math.PI);
324+
ctx.fill();
181325
}
182326
});
183327

184-
// Draw connection points
328+
// Draw cities/nodes
185329
visiblePoints.forEach(point => {
186330
const projected = project3D(point.x, point.y, point.z);
187331

188332
// Calculate depth-based brightness and size
189333
const depthFactor = (point.z + radius) / (2 * radius);
190-
const brightness = 0.3 + depthFactor * 0.7;
191-
const size = 1.5 + depthFactor * 2;
334+
const brightness = 0.4 + depthFactor * 0.6;
335+
const baseSize = point.type === 'server' ? 3 : 2;
336+
const size = baseSize + depthFactor * 2;
337+
338+
// Activity-based pulsing
339+
const pulseSize = point.activity;
192340

193-
// Pulsing effect
194-
const pulseSize = Math.sin(point.pulse) * 0.5 + 1;
341+
// Different colors for servers vs clients
342+
const color = point.type === 'server' ? 'rgba(0, 255, 0,' : 'rgba(0, 200, 255,';
195343

196344
// Draw glow effect
197345
const gradient = ctx.createRadialGradient(
198346
projected.x, projected.y, 0,
199-
projected.x, projected.y, size * pulseSize * 3
347+
projected.x, projected.y, size * pulseSize * 4
200348
);
201-
gradient.addColorStop(0, `rgba(0, 255, 0, ${brightness * 0.8})`);
202-
gradient.addColorStop(1, 'rgba(0, 255, 0, 0)');
349+
gradient.addColorStop(0, `${color} ${brightness * 0.8})`);
350+
gradient.addColorStop(1, `${color} 0)`);
203351

204352
ctx.fillStyle = gradient;
205353
ctx.beginPath();
206-
ctx.arc(projected.x, projected.y, size * pulseSize * 3, 0, 2 * Math.PI);
354+
ctx.arc(projected.x, projected.y, size * pulseSize * 4, 0, 2 * Math.PI);
207355
ctx.fill();
208356

209-
// Draw the point itself
210-
ctx.fillStyle = `rgba(0, 255, 0, ${brightness})`;
357+
// Draw the node itself
358+
ctx.fillStyle = `${color} ${brightness})`;
211359
ctx.beginPath();
212360
ctx.arc(projected.x, projected.y, size * pulseSize, 0, 2 * Math.PI);
213361
ctx.fill();
362+
363+
// Draw city labels for major nodes (when they're in front and large enough)
364+
if (point.type === 'server' && depthFactor > 0.6 && size > 4) {
365+
ctx.fillStyle = `rgba(0, 255, 0, ${brightness * 0.7})`;
366+
ctx.font = '10px monospace';
367+
ctx.textAlign = 'center';
368+
ctx.fillText(point.name, projected.x, projected.y - size * 2);
369+
}
214370
});
215371

216372
// Increment rotation for continuous animation
217-
rotationY += 0.005;
218-
rotationX += 0.002;
373+
rotationY += 0.004;
374+
rotationX += 0.001;
219375
};
220376

221-
const interval = setInterval(draw, 50);
377+
const interval = setInterval(draw, 40); // Slightly slower for better performance
222378

223379
return () => clearInterval(interval);
224380
}, []);

0 commit comments

Comments
 (0)