@@ -92,6 +92,7 @@ class ServicesCanvas extends Base {
9292 superHexes = [ ]
9393 scale = 1
9494 time = 0
95+ rotation = { x : - 0.4 , y : 0 } // Base tilt (radians) - Floor Perspective
9596
9697 clearGraph ( ) {
9798 let me = this ;
@@ -119,20 +120,30 @@ class ServicesCanvas extends Base {
119120 this . theme = value
120121 }
121122
122- drawHex ( ctx , x , y , size ) {
123+ drawHex ( ctx , x , y , z , size , projection ) {
123124 ctx . beginPath ( ) ;
125+ let first = true ;
126+
124127 for ( let i = 0 ; i < 6 ; i ++ ) {
125128 const angle_deg = 60 * i + 30 ;
126129 const angle_rad = Math . PI / 180 * angle_deg ;
130+
127131 const px = x + size * Math . cos ( angle_rad ) ;
128132 const py = y + size * Math . sin ( angle_rad ) ;
129- if ( i === 0 ) ctx . moveTo ( px , py ) ;
130- else ctx . lineTo ( px , py ) ;
133+
134+ let p = projection . project ( px , py , z ) ;
135+
136+ if ( first ) {
137+ ctx . moveTo ( p . x , p . y ) ;
138+ first = false ;
139+ } else {
140+ ctx . lineTo ( p . x , p . y ) ;
141+ }
131142 }
132143 ctx . closePath ( ) ;
133144 }
134145
135- drawKernel ( ctx , width , height ) {
146+ drawKernel ( ctx , width , height , projection ) {
136147 let me = this ;
137148 if ( ! me . kernelBuffer ) return ;
138149
@@ -154,12 +165,12 @@ class ServicesCanvas extends Base {
154165 for ( let i = 0 ; i < count ; i ++ ) {
155166 let x = buffer [ i * 2 ] + panX ,
156167 y = buffer [ i * 2 + 1 ] + panY ;
157- me . drawHex ( ctx , x , y , size ) ;
168+ me . drawHex ( ctx , x , y , 400 , size , projection ) ;
158169 }
159170 ctx . stroke ( ) ;
160171 }
161172
162- drawGraph ( ctx , width , height ) {
173+ drawGraph ( ctx , width , height , projection ) {
163174 let me = this ;
164175
165176 if ( ! me . cellBuffer ) return ;
@@ -187,7 +198,7 @@ class ServicesCanvas extends Base {
187198
188199 if ( energy <= 0.01 && scale > 0.1 ) {
189200 let size = baseSize * 0.95 * scale ;
190- me . drawHex ( ctx , x , y , size ) ;
201+ me . drawHex ( ctx , x , y , 0 , size , projection ) ;
191202 }
192203 }
193204 ctx . stroke ( ) ;
@@ -206,7 +217,7 @@ class ServicesCanvas extends Base {
206217
207218 if ( progress > 0 ) {
208219 ctx . beginPath ( ) ;
209- me . drawHex ( ctx , x , y , baseSize * 2.5 * progress ) ;
220+ me . drawHex ( ctx , x , y , 0 , baseSize * 2.5 * progress , projection ) ;
210221
211222 ctx . strokeStyle = HIGHLIGHT ;
212223 ctx . globalAlpha = 0.3 * progress ;
@@ -215,8 +226,10 @@ class ServicesCanvas extends Base {
215226 ctx . fillStyle = themeColors . superHex ;
216227 ctx . fill ( ) ;
217228
229+ let p = projection . project ( x , y , 0 ) ;
230+
218231 ctx . beginPath ( ) ;
219- ctx . arc ( x , y , 4 * s * progress , 0 , Math . PI * 2 ) ;
232+ ctx . arc ( p . x , p . y , 4 * s * progress * p . scale , 0 , Math . PI * 2 ) ;
220233 ctx . fillStyle = HIGHLIGHT ;
221234 ctx . globalAlpha = 0.8 * progress ;
222235 ctx . fill ( ) ;
@@ -237,7 +250,7 @@ class ServicesCanvas extends Base {
237250 let currentSize = baseSize * ( 0.95 + ( energy * 0.1 ) ) ;
238251
239252 ctx . beginPath ( ) ;
240- me . drawHex ( ctx , x , y , currentSize ) ;
253+ me . drawHex ( ctx , x , y , 0 , currentSize , projection ) ;
241254
242255 ctx . fillStyle = themeColors . hexActive ;
243256 ctx . globalAlpha = energy * 0.4 ;
@@ -254,7 +267,7 @@ class ServicesCanvas extends Base {
254267 }
255268 }
256269
257- drawRunners ( ctx ) {
270+ drawRunners ( ctx , projection ) {
258271 let me = this ;
259272 if ( ! me . runnerBuffer ) return ;
260273
@@ -286,16 +299,21 @@ class ServicesCanvas extends Base {
286299 let tailX = x - dirX * tailLen ,
287300 tailY = y - dirY * tailLen ;
288301
289- let g = ctx . createLinearGradient ( tailX , tailY , x , y ) ;
302+ let p1 = projection . project ( tailX , tailY , 0 ) ;
303+ let p2 = projection . project ( x , y , 0 ) ;
304+
305+ if ( ! p1 . visible || ! p2 . visible ) continue ;
306+
307+ let g = ctx . createLinearGradient ( p1 . x , p1 . y , p2 . x , p2 . y ) ;
290308 g . addColorStop ( 0 , 'rgba(0,0,0,0)' ) ;
291309 g . addColorStop ( 0.5 , PRIMARY ) ;
292310 g . addColorStop ( 1 , '#FFFFFF' ) ;
293311
294312 ctx . beginPath ( ) ;
295313 ctx . strokeStyle = g ;
296- ctx . lineWidth = 3 * s ;
297- ctx . moveTo ( tailX , tailY ) ;
298- ctx . lineTo ( x , y ) ;
314+ ctx . lineWidth = 3 * s * p2 . scale ; // Scale thickness
315+ ctx . moveTo ( p1 . x , p1 . y ) ;
316+ ctx . lineTo ( p2 . x , p2 . y ) ;
299317 ctx . stroke ( ) ;
300318 }
301319 }
@@ -304,7 +322,7 @@ class ServicesCanvas extends Base {
304322 /**
305323 * Draws particle debris (squares).
306324 */
307- drawDebris ( ctx ) {
325+ drawDebris ( ctx , projection ) {
308326 let me = this ;
309327 if ( ! me . debrisBuffer ) return ;
310328
@@ -325,9 +343,12 @@ class ServicesCanvas extends Base {
325343 y = buffer [ idx + 1 ] ,
326344 size = 3 * s * life ; // Shrink as it dies
327345
328- ctx . globalAlpha = life ;
329- // Draw Square (Data Bit)
330- ctx . fillRect ( x - size / 2 , y - size / 2 , size , size ) ;
346+ let p = projection . project ( x , y , 0 ) ;
347+ if ( p . visible ) {
348+ let scaledSize = size * p . scale ;
349+ ctx . globalAlpha = life ;
350+ ctx . fillRect ( p . x - scaledSize / 2 , p . y - scaledSize / 2 , scaledSize , scaledSize ) ;
351+ }
331352 }
332353 }
333354 ctx . globalAlpha = 1 ;
@@ -570,6 +591,7 @@ class ServicesCanvas extends Base {
570591 if ( ! me . debrisBuffer ) me . initDebris ( ) ;
571592
572593 me . updatePhysics ( width , height ) ;
594+ me . updateRotation ( width , height ) ;
573595 me . updateSuperHexes ( width , height ) ;
574596 me . updateRunners ( width , height ) ;
575597 me . updateDebris ( ) ;
@@ -581,10 +603,12 @@ class ServicesCanvas extends Base {
581603 ctx . fillRect ( 0 , 0 , width , height )
582604 }
583605
584- me . drawKernel ( ctx , width , height ) ;
585- me . drawGraph ( ctx , width , height ) ;
586- me . drawRunners ( ctx ) ;
587- me . drawDebris ( ctx ) ; // Render particles on top
606+ let projection = me . getProjection ( width , height ) ;
607+
608+ me . drawKernel ( ctx , width , height , projection ) ;
609+ me . drawGraph ( ctx , width , height , projection ) ;
610+ me . drawRunners ( ctx , projection ) ;
611+ me . drawDebris ( ctx , projection ) ; // Render particles on top
588612
589613 if ( hasRaf ) {
590614 requestAnimationFrame ( me . renderLoop )
@@ -832,6 +856,62 @@ class ServicesCanvas extends Base {
832856 }
833857 }
834858
859+ updateRotation ( width , height ) {
860+ let me = this ,
861+ mx = me . mouse . x ,
862+ my = me . mouse . y ,
863+ tx = - 0.4 , // Base tilt X (pitch)
864+ ty = 0 ; // Base yaw
865+
866+ if ( mx !== - 1000 ) {
867+ // Map mouse X to Yaw (+/- 0.2 rad)
868+ ty = ( ( mx / width ) - 0.5 ) * 0.4 ;
869+ // Map mouse Y to Pitch (-0.6 to -0.2 rad)
870+ tx = - 0.4 + ( ( my / height ) - 0.5 ) * 0.4 ;
871+ }
872+
873+ // Smooth interpolate
874+ me . rotation . x += ( tx - me . rotation . x ) * 0.05 ;
875+ me . rotation . y += ( ty - me . rotation . y ) * 0.05 ;
876+ }
877+
878+ getProjection ( width , height ) {
879+ let me = this ,
880+ fov = 1000 ,
881+ cx = width / 2 ,
882+ cy = height / 2 ,
883+ cosX = Math . cos ( me . rotation . x ) ,
884+ sinX = Math . sin ( me . rotation . x ) ,
885+ cosY = Math . cos ( me . rotation . y ) ,
886+ sinY = Math . sin ( me . rotation . y ) ;
887+
888+ return {
889+ project ( x , y , z ) {
890+ let dx = x - cx ,
891+ dy = y - cy ,
892+ dz = z ;
893+
894+ // Rotate Y
895+ let x1 = dx * cosY - dz * sinY ;
896+ let z1 = dz * cosY + dx * sinY ;
897+
898+ // Rotate X
899+ let y2 = dy * cosX - z1 * sinX ;
900+ let z2 = z1 * cosX + dy * sinX ;
901+
902+ // Project
903+ let scale = fov / ( fov + z2 ) ;
904+
905+ return {
906+ x : x1 * scale + cx ,
907+ y : y2 * scale + cy ,
908+ scale : scale ,
909+ visible : z2 > - fov // Clip if behind camera
910+ } ;
911+ }
912+ } ;
913+ }
914+
835915 updateResources ( width , height ) {
836916 let me = this ,
837917 ctx = me . context ;
0 commit comments