@@ -15,124 +15,207 @@ const Globe3D = () => {
1515 const centerY = canvas . height / 2 ;
1616 const radius = Math . min ( canvas . width , canvas . height ) * 0.35 ;
1717
18- let rotation = 0 ;
19- const connections : Array < { from : number ; to : number ; opacity : number } > = [ ] ;
20- const points : Array < { x : number ; y : number ; z : number ; lat : number ; lng : number } > = [ ] ;
18+ let rotationX = 0 ;
19+ 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 } > = [ ] ;
2122
22- // Generate random points on sphere
23+ // Generate random points on sphere surface
2324 const generatePoints = ( ) => {
24- for ( let i = 0 ; i < 50 ; i ++ ) {
25+ for ( let i = 0 ; i < 80 ; i ++ ) {
2526 const lat = ( Math . random ( ) - 0.5 ) * Math . PI ;
2627 const lng = Math . random ( ) * 2 * Math . PI ;
27- const x = radius * Math . cos ( lat ) * Math . cos ( lng ) ;
28- const y = radius * Math . cos ( lat ) * Math . sin ( lng ) ;
29- const z = radius * Math . sin ( lat ) ;
30- points . push ( { x, y, z, lat, lng } ) ;
28+ points . push ( {
29+ x : 0 , y : 0 , z : 0 ,
30+ lat, lng,
31+ pulse : Math . random ( ) * Math . PI * 2
32+ } ) ;
3133 }
3234 } ;
3335
34- // Generate connections
36+ // Generate connections between points
3537 const generateConnections = ( ) => {
36- for ( let i = 0 ; i < 30 ; i ++ ) {
38+ for ( let i = 0 ; i < 60 ; i ++ ) {
3739 const from = Math . floor ( Math . random ( ) * points . length ) ;
3840 let to = Math . floor ( Math . random ( ) * points . length ) ;
3941 while ( to === from ) {
4042 to = Math . floor ( Math . random ( ) * points . length ) ;
4143 }
42- connections . push ( { from, to, opacity : Math . random ( ) * 0.5 + 0.1 } ) ;
44+ connections . push ( {
45+ from,
46+ to,
47+ opacity : Math . random ( ) * 0.5 + 0.2 ,
48+ pulse : Math . random ( ) * Math . PI * 2
49+ } ) ;
4350 }
4451 } ;
4552
53+ // Convert spherical coordinates to 3D cartesian
54+ const sphericalTo3D = ( lat : number , lng : number ) => {
55+ const x = radius * Math . cos ( lat ) * Math . cos ( lng ) ;
56+ const y = radius * Math . sin ( lat ) ;
57+ const z = radius * Math . cos ( lat ) * Math . sin ( lng ) ;
58+ return { x, y, z } ;
59+ } ;
60+
61+ // Rotate point in 3D space
62+ const rotate3D = ( x : number , y : number , z : number ) => {
63+ // Rotate around Y axis
64+ const cosY = Math . cos ( rotationY ) ;
65+ const sinY = Math . sin ( rotationY ) ;
66+ const x1 = x * cosY - z * sinY ;
67+ const z1 = x * sinY + z * cosY ;
68+
69+ // Rotate around X axis
70+ const cosX = Math . cos ( rotationX ) ;
71+ const sinX = Math . sin ( rotationX ) ;
72+ const y1 = y * cosX - z1 * sinX ;
73+ const z2 = y * sinX + z1 * cosX ;
74+
75+ return { x : x1 , y : y1 , z : z2 } ;
76+ } ;
77+
78+ // Project 3D point to 2D screen
79+ const project3D = ( x : number , y : number , z : number ) => {
80+ const perspective = 800 ;
81+ const scale = perspective / ( perspective + z ) ;
82+ return {
83+ x : centerX + x * scale ,
84+ y : centerY + y * scale ,
85+ scale : scale
86+ } ;
87+ } ;
88+
4689 generatePoints ( ) ;
4790 generateConnections ( ) ;
4891
4992 const draw = ( ) => {
5093 ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
5194
52- // Draw globe wireframe
53- ctx . strokeStyle = 'rgba(0, 255, 0, 0.3)' ;
95+ // Update point positions
96+ points . forEach ( point => {
97+ const pos3D = sphericalTo3D ( point . lat , point . lng ) ;
98+ const rotated = rotate3D ( pos3D . x , pos3D . y , pos3D . z ) ;
99+ point . x = rotated . x ;
100+ point . y = rotated . y ;
101+ point . z = rotated . z ;
102+ point . pulse += 0.05 ;
103+ } ) ;
104+
105+ // Draw globe wireframe (latitude and longitude lines)
106+ ctx . strokeStyle = 'rgba(0, 255, 0, 0.15)' ;
54107 ctx . lineWidth = 1 ;
55108
56109 // Draw latitude lines
57- for ( let lat = - Math . PI / 2 ; lat <= Math . PI / 2 ; lat += Math . PI / 6 ) {
110+ for ( let lat = - Math . PI / 2 ; lat <= Math . PI / 2 ; lat += Math . PI / 8 ) {
58111 ctx . beginPath ( ) ;
112+ let firstPoint = true ;
59113 for ( let lng = 0 ; lng <= 2 * Math . PI ; lng += 0.1 ) {
60- const x = centerX + radius * Math . cos ( lat ) * Math . cos ( lng + rotation ) ;
61- const y = centerY + radius * Math . cos ( lat ) * Math . sin ( lng + rotation ) ;
62- const z = radius * Math . sin ( lat ) ;
114+ const pos3D = sphericalTo3D ( lat , lng ) ;
115+ const rotated = rotate3D ( pos3D . x , pos3D . y , pos3D . z ) ;
63116
64- if ( z > - radius * 0.5 ) { // Only draw front hemisphere
65- if ( lng === 0 ) {
66- ctx . moveTo ( x , y ) ;
117+ if ( rotated . z > - radius * 0.3 ) { // Only draw visible parts
118+ const projected = project3D ( rotated . x , rotated . y , rotated . z ) ;
119+ if ( firstPoint ) {
120+ ctx . moveTo ( projected . x , projected . y ) ;
121+ firstPoint = false ;
67122 } else {
68- ctx . lineTo ( x , y ) ;
123+ ctx . lineTo ( projected . x , projected . y ) ;
69124 }
125+ } else {
126+ firstPoint = true ;
70127 }
71128 }
72129 ctx . stroke ( ) ;
73130 }
74131
75132 // Draw longitude lines
76- for ( let lng = 0 ; lng < 2 * Math . PI ; lng += Math . PI / 6 ) {
133+ for ( let lng = 0 ; lng < 2 * Math . PI ; lng += Math . PI / 8 ) {
77134 ctx . beginPath ( ) ;
135+ let firstPoint = true ;
78136 for ( let lat = - Math . PI / 2 ; lat <= Math . PI / 2 ; lat += 0.1 ) {
79- const x = centerX + radius * Math . cos ( lat ) * Math . cos ( lng + rotation ) ;
80- const y = centerY + radius * Math . cos ( lat ) * Math . sin ( lng + rotation ) ;
81- const z = radius * Math . sin ( lat ) ;
137+ const pos3D = sphericalTo3D ( lat , lng ) ;
138+ const rotated = rotate3D ( pos3D . x , pos3D . y , pos3D . z ) ;
82139
83- if ( z > - radius * 0.5 ) { // Only draw front hemisphere
84- if ( lat === - Math . PI / 2 ) {
85- ctx . moveTo ( x , y ) ;
140+ if ( rotated . z > - radius * 0.3 ) { // Only draw visible parts
141+ const projected = project3D ( rotated . x , rotated . y , rotated . z ) ;
142+ if ( firstPoint ) {
143+ ctx . moveTo ( projected . x , projected . y ) ;
144+ firstPoint = false ;
86145 } else {
87- ctx . lineTo ( x , y ) ;
146+ ctx . lineTo ( projected . x , projected . y ) ;
88147 }
148+ } else {
149+ firstPoint = true ;
89150 }
90151 }
91152 ctx . stroke ( ) ;
92153 }
93154
94- // Draw connection points
95- points . forEach ( point => {
96- const x = centerX + point . x * Math . cos ( rotation ) - point . y * Math . sin ( rotation ) ;
97- const y = centerY + point . y * Math . cos ( rotation ) + point . x * Math . sin ( rotation ) ;
98- const z = point . z ;
99-
100- if ( z > - radius * 0.5 ) {
101- ctx . fillStyle = 'rgba(0, 255, 0, 0.8)' ;
102- ctx . beginPath ( ) ;
103- ctx . arc ( x , y , 2 , 0 , 2 * Math . PI ) ;
104- ctx . fill ( ) ;
105- }
106- } ) ;
155+ // Sort points by z-depth for proper rendering
156+ const visiblePoints = points
157+ . map ( ( point , index ) => ( { ...point , index } ) )
158+ . filter ( point => point . z > - radius * 0.5 )
159+ . sort ( ( a , b ) => a . z - b . z ) ;
107160
108- // Draw connections
161+ // Draw connections first (behind points)
109162 connections . forEach ( connection => {
110163 const fromPoint = points [ connection . from ] ;
111164 const toPoint = points [ connection . to ] ;
112165
113- const fromX = centerX + fromPoint . x * Math . cos ( rotation ) - fromPoint . y * Math . sin ( rotation ) ;
114- const fromY = centerY + fromPoint . y * Math . cos ( rotation ) + fromPoint . x * Math . sin ( rotation ) ;
115- const fromZ = fromPoint . z ;
166+ 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 ) ;
116169
117- const toX = centerX + toPoint . x * Math . cos ( rotation ) - toPoint . y * Math . sin ( rotation ) ;
118- const toY = centerY + toPoint . y * Math . cos ( rotation ) + toPoint . x * Math . sin ( rotation ) ;
119- const toZ = toPoint . z ;
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 ;
120174
121- if ( fromZ > - radius * 0.5 && toZ > - radius * 0.5 ) {
122- ctx . strokeStyle = `rgba(0, 255, 0, ${ connection . opacity } )` ;
123- ctx . lineWidth = 1 ;
175+ ctx . strokeStyle = `rgba(0, 255, 0, ${ opacity } )` ;
176+ ctx . lineWidth = 1.5 ;
124177 ctx . beginPath ( ) ;
125- ctx . moveTo ( fromX , fromY ) ;
126- ctx . lineTo ( toX , toY ) ;
178+ ctx . moveTo ( fromProjected . x , fromProjected . y ) ;
179+ ctx . lineTo ( toProjected . x , toProjected . y ) ;
127180 ctx . stroke ( ) ;
128181 }
182+ } ) ;
129183
130- // Animate connection opacity
131- connection . opacity += ( Math . random ( ) - 0.5 ) * 0.02 ;
132- connection . opacity = Math . max ( 0.1 , Math . min ( 0.6 , connection . opacity ) ) ;
184+ // Draw connection points
185+ visiblePoints . forEach ( point => {
186+ const projected = project3D ( point . x , point . y , point . z ) ;
187+
188+ // Calculate depth-based brightness and size
189+ const depthFactor = ( point . z + radius ) / ( 2 * radius ) ;
190+ const brightness = 0.3 + depthFactor * 0.7 ;
191+ const size = 1.5 + depthFactor * 2 ;
192+
193+ // Pulsing effect
194+ const pulseSize = Math . sin ( point . pulse ) * 0.5 + 1 ;
195+
196+ // Draw glow effect
197+ const gradient = ctx . createRadialGradient (
198+ projected . x , projected . y , 0 ,
199+ projected . x , projected . y , size * pulseSize * 3
200+ ) ;
201+ gradient . addColorStop ( 0 , `rgba(0, 255, 0, ${ brightness * 0.8 } )` ) ;
202+ gradient . addColorStop ( 1 , 'rgba(0, 255, 0, 0)' ) ;
203+
204+ ctx . fillStyle = gradient ;
205+ ctx . beginPath ( ) ;
206+ ctx . arc ( projected . x , projected . y , size * pulseSize * 3 , 0 , 2 * Math . PI ) ;
207+ ctx . fill ( ) ;
208+
209+ // Draw the point itself
210+ ctx . fillStyle = `rgba(0, 255, 0, ${ brightness } )` ;
211+ ctx . beginPath ( ) ;
212+ ctx . arc ( projected . x , projected . y , size * pulseSize , 0 , 2 * Math . PI ) ;
213+ ctx . fill ( ) ;
133214 } ) ;
134215
135- rotation += 0.005 ;
216+ // Increment rotation for continuous animation
217+ rotationY += 0.005 ;
218+ rotationX += 0.002 ;
136219 } ;
137220
138221 const interval = setInterval ( draw , 50 ) ;
@@ -143,9 +226,9 @@ const Globe3D = () => {
143226 return (
144227 < canvas
145228 ref = { canvasRef }
146- width = { 300 }
147- height = { 300 }
148- className = "opacity-80 "
229+ width = { 350 }
230+ height = { 350 }
231+ className = "opacity-90 "
149232 />
150233 ) ;
151234} ;
0 commit comments