@@ -18,7 +18,7 @@ type Continent = 'North America' | 'South America' | 'Europe' | 'Africa' | 'Asia
1818const getContinent = ( lng : number ) : Continent => {
1919 if ( lng > - 125 && lng < - 66 ) return 'North America' ;
2020 if ( lng > - 81 && lng < - 34 ) return 'South America' ;
21- if ( lng > - 10 && lng < 44 ) return 'Europe' ; // Europe/Africa
21+ if ( lng > - 10 && lng < 44 ) return 'Europe' ; // Europe/Africa are grouped for simplicity
2222 if ( lng > 100 && lng < 180 ) return 'Oceania' ;
2323 return 'Asia' ; // Asia/Africa
2424} ;
@@ -71,43 +71,43 @@ const Globe3D = () => {
7171 globeGroup . rotation . z = 23.5 * ( Math . PI / 180 ) ;
7272 scene . add ( globeGroup ) ;
7373
74- // Globe Layers
74+ // --- Globe Layers ---
75+
7576 globeGroup . add ( new THREE . Mesh (
7677 new THREE . SphereGeometry ( globeRadius , 64 , 64 ) ,
77- new THREE . MeshBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.0 } )
78+ new THREE . MeshBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.05 } )
7879 ) ) ;
7980
8081 const earthTexture = new THREE . TextureLoader ( ) . load ( '/earth-dark.jpeg' ) ;
8182 earthTexture . colorSpace = THREE . SRGBColorSpace ;
8283 earthTexture . anisotropy = renderer . capabilities . getMaxAnisotropy ( ) ;
83- globeGroup . add ( new THREE . Mesh (
84- new THREE . SphereGeometry ( globeRadius , 64 , 64 ) ,
85- new THREE . MeshBasicMaterial ( { map : earthTexture , transparent : true , opacity : 0.0 } )
86- ) ) ;
8784
88- // --- NEW: Faint Lat/Long Grid Lines ---
85+ const earthMaterial = new THREE . MeshBasicMaterial ( {
86+ map : earthTexture ,
87+ transparent : true ,
88+ blending : THREE . AdditiveBlending ,
89+ color : 0x777777 ,
90+ } ) ;
91+ const earthMesh = new THREE . Mesh ( new THREE . SphereGeometry ( globeRadius , 64 , 64 ) , earthMaterial ) ;
92+ globeGroup . add ( earthMesh ) ;
93+
8994 const gridPoints : THREE . Vector3 [ ] = [ ] ;
90- const gridRadius = globeRadius + 0.005 ; // Slightly above the surface
91- // Latitude lines
95+ const gridRadius = globeRadius + 0.005 ;
9296 for ( let lat = - 90 ; lat <= 90 ; lat += 15 ) {
9397 for ( let lng = - 180 ; lng <= 180 ; lng += 5 ) {
94- gridPoints . push ( latLngToVector3 ( lat , lng , gridRadius ) ) ;
95- gridPoints . push ( latLngToVector3 ( lat , lng + 5 , gridRadius ) ) ;
98+ gridPoints . push ( latLngToVector3 ( lat , lng , gridRadius ) , latLngToVector3 ( lat , lng + 5 , gridRadius ) ) ;
9699 }
97100 }
98- // Longitude lines
99101 for ( let lng = - 180 ; lng <= 180 ; lng += 15 ) {
100102 for ( let lat = - 90 ; lat <= 90 ; lat += 5 ) {
101- gridPoints . push ( latLngToVector3 ( lat , lng , gridRadius ) ) ;
102- gridPoints . push ( latLngToVector3 ( lat + 5 , lng , gridRadius ) ) ;
103+ gridPoints . push ( latLngToVector3 ( lat , lng , gridRadius ) , latLngToVector3 ( lat + 5 , lng , gridRadius ) ) ;
103104 }
104105 }
105106 const gridGeom = new THREE . BufferGeometry ( ) . setFromPoints ( gridPoints ) ;
106- const gridMaterial = new THREE . LineBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.1 } ) ;
107+ const gridMaterial = new THREE . LineBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.05 } ) ;
107108 const gridLines = new THREE . LineSegments ( gridGeom , gridMaterial ) ;
108109 globeGroup . add ( gridLines ) ;
109110
110-
111111 const connections : Connection [ ] = [ ] ;
112112
113113 Promise . all ( [
@@ -116,8 +116,7 @@ const Globe3D = () => {
116116 ] ) . then ( ( [ countriesData , serverJson ] ) => {
117117 const servers : Server [ ] = serverJson . servers ;
118118
119- // **ENHANCEMENT:** Bolder country lines
120- const outlineMaterial = new THREE . LineBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.25 } ) ;
119+ const outlineMaterial = new THREE . LineBasicMaterial ( { color : 0x00ff00 , transparent : true , opacity : 0.4 } ) ;
121120 const outlinePoints : THREE . Vector3 [ ] = [ ] ;
122121 countriesData . features . forEach ( ( feature : any ) => {
123122 if ( feature . geometry ?. type === 'LineString' ) {
@@ -153,7 +152,9 @@ const Globe3D = () => {
153152 establishedConnections . add ( key ) ;
154153
155154 const distance = start . vector . distanceTo ( end . vector ) ;
156- const controlPoint = start . vector . clone ( ) . lerp ( end . vector , 0.5 ) . normalize ( ) . multiplyScalar ( globeRadius + distance * 0.2 ) ;
155+ const arcHeight = distance * distance * 0.05 ;
156+ const controlPoint = start . vector . clone ( ) . lerp ( end . vector , 0.5 ) . normalize ( ) . multiplyScalar ( globeRadius + arcHeight ) ;
157+
157158 const curve = new THREE . QuadraticBezierCurve3 ( start . vector , controlPoint , end . vector ) ;
158159
159160 const tubeGeom = new THREE . TubeGeometry ( curve , 32 , 0.005 , 8 , false ) ;
@@ -170,30 +171,77 @@ const Globe3D = () => {
170171 connections . push ( {
171172 curve,
172173 trailMaterial,
173- packet1 : { sprite : packet1 , position : Math . random ( ) , speed : Math . random ( ) * 0.01 + 0.005 } ,
174- packet2 : { sprite : packet2 , position : Math . random ( ) , speed : Math . random ( ) * 0.01 + 0.005 } ,
174+ packet1 : { sprite : packet1 , position : Math . random ( ) , speed : Math . random ( ) * 0.02 + 0.005 } ,
175+ packet2 : { sprite : packet2 , position : Math . random ( ) , speed : Math . random ( ) * 0.02 + 0.005 } ,
175176 } ) ;
176177 } ;
178+
179+ // --- THE NEW CONNECTION ALGORITHM ---
180+
181+ const serversByContinent = new Map < Continent , ServerPoint [ ] > ( ) ;
182+ serverPoints . forEach ( p => {
183+ if ( ! serversByContinent . has ( p . continent ) ) serversByContinent . set ( p . continent , [ ] ) ;
184+ serversByContinent . get ( p . continent ) ! . push ( p ) ;
185+ } ) ;
177186
178- serverPoints . forEach ( startServer => {
179- const others = serverPoints . filter ( p => p . name !== startServer . name ) ;
180- const nearest = others . sort ( ( a , b ) => startServer . vector . distanceTo ( a . vector ) - startServer . vector . distanceTo ( b . vector ) ) [ 0 ] ;
181- createConnection ( startServer , nearest ) ;
182-
183- const sameContinent = others . filter ( p => p . continent === startServer . continent ) . sort ( ( ) => 0.5 - Math . random ( ) ) . slice ( 0 , 2 ) ;
184- sameContinent . forEach ( peer => createConnection ( startServer , peer ) ) ;
187+ // 1. Identify Gateways
188+ const gateways = new Map < Continent , ServerPoint > ( ) ;
189+ for ( const [ continent , points ] of serversByContinent . entries ( ) ) {
190+ let bestGateway : ServerPoint | null = null ;
191+ let minAvgDist = Infinity ;
185192
186- if ( Math . random ( ) < 0.2 ) {
187- const differentContinent = others . filter ( p => p . continent !== startServer . continent ) ;
188- if ( differentContinent . length > 0 ) createConnection ( startServer , differentContinent [ Math . floor ( Math . random ( ) * differentContinent . length ) ] ) ;
193+ for ( const candidate of points ) {
194+ let totalDist = 0 ;
195+ let otherCount = 0 ;
196+ for ( const other of serverPoints ) {
197+ if ( other . continent !== continent ) {
198+ totalDist += candidate . vector . distanceTo ( other . vector ) ;
199+ otherCount ++ ;
200+ }
201+ }
202+ const avgDist = totalDist / otherCount ;
203+ if ( avgDist < minAvgDist ) {
204+ minAvgDist = avgDist ;
205+ bestGateway = candidate ;
206+ }
189207 }
208+ if ( bestGateway ) gateways . set ( continent , bestGateway ) ;
209+ }
210+ const gatewayList = Array . from ( gateways . values ( ) ) ;
211+
212+ // 2. Connect Gateways (Intercontinental Backbone)
213+ for ( let i = 0 ; i < gatewayList . length ; i ++ ) {
214+ for ( let j = i + 1 ; j < gatewayList . length ; j ++ ) {
215+ createConnection ( gatewayList [ i ] , gatewayList [ j ] ) ;
216+ }
217+ }
218+
219+ // 3. Connect regional servers to their gateway (Spoke-to-Hub)
220+ for ( const [ continent , points ] of serversByContinent . entries ( ) ) {
221+ const gateway = gateways . get ( continent ) ;
222+ if ( gateway ) {
223+ points . forEach ( point => {
224+ if ( point !== gateway ) {
225+ createConnection ( point , gateway ) ;
226+ }
227+ } ) ;
228+ }
229+ }
230+
231+ // 4. Create local peering (nearest neighbor)
232+ serverPoints . forEach ( startServer => {
233+ const nearest = serverPoints
234+ . filter ( p => p . name !== startServer . name )
235+ . sort ( ( a , b ) => startServer . vector . distanceTo ( a . vector ) - startServer . vector . distanceTo ( b . vector ) ) [ 0 ] ;
236+ createConnection ( startServer , nearest ) ;
190237 } ) ;
238+
191239 } ) ;
192240
193241 const animate = ( ) => {
194242 requestAnimationFrame ( animate ) ;
195243 connections . forEach ( conn => {
196- if ( conn . trailMaterial . map ) conn . trailMaterial . map . offset . x -= 0.0005 ;
244+ if ( conn . trailMaterial . map ) conn . trailMaterial . map . offset . x -= 0.002 ;
197245 conn . packet1 . position = ( conn . packet1 . position + conn . packet1 . speed ) % 1 ;
198246 conn . packet2 . position = ( conn . packet2 . position + conn . packet2 . speed ) % 1 ;
199247 conn . curve . getPointAt ( conn . packet1 . position , conn . packet1 . sprite . position ) ;
0 commit comments