@@ -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