1+ mod support;
2+
13use colored:: Color ;
24use crossterm:: {
35 cursor,
@@ -12,92 +14,12 @@ use std::collections::VecDeque;
1214use std:: io:: { self , Write } ;
1315use std:: thread;
1416use std:: time:: { Duration , Instant } ;
17+ use support:: solar:: { line_z, plot_z, PickingZBuffer as ZBuffer } ;
18+ use support:: three_d:: {
19+ make_sphere_points, project_with_projection, rotate_x, rotate_y, Projection , Vec3 ,
20+ } ;
1521use txtplot:: ChartContext ;
1622
17- // ============================================================================
18- // 3D MATH
19- // ============================================================================
20- #[ derive( Clone , Copy , Debug ) ]
21- struct Vec3 {
22- x : f64 ,
23- y : f64 ,
24- z : f64 ,
25- }
26- impl Vec3 {
27- fn new ( x : f64 , y : f64 , z : f64 ) -> Self {
28- Self { x, y, z }
29- }
30- fn add ( self , o : Vec3 ) -> Self {
31- Self :: new ( self . x + o. x , self . y + o. y , self . z + o. z )
32- }
33- fn sub ( self , o : Vec3 ) -> Self {
34- Self :: new ( self . x - o. x , self . y - o. y , self . z - o. z )
35- }
36- fn mul ( self , s : f64 ) -> Self {
37- Self :: new ( self . x * s, self . y * s, self . z * s)
38- }
39- fn dot ( self , o : Vec3 ) -> f64 {
40- self . x * o. x + self . y * o. y + self . z * o. z
41- }
42- fn norm ( self ) -> f64 {
43- self . dot ( self ) . sqrt ( )
44- }
45- fn normalize ( self ) -> Self {
46- let l = self . norm ( ) ;
47- if l > 0.0 {
48- Self :: new ( self . x / l, self . y / l, self . z / l)
49- } else {
50- self
51- }
52- }
53- }
54-
55- fn rotate_x ( v : Vec3 , a : f64 ) -> Vec3 {
56- let ( s, c) = a. sin_cos ( ) ;
57- Vec3 :: new ( v. x , v. y * c - v. z * s, v. y * s + v. z * c)
58- }
59- fn rotate_y ( v : Vec3 , a : f64 ) -> Vec3 {
60- let ( s, c) = a. sin_cos ( ) ;
61- Vec3 :: new ( v. x * c - v. z * s, v. y , v. x * s + v. z * c)
62- }
63- // ============================================================================
64- // GRAPHICS ENGINE (Z-BUFFER)
65- // ============================================================================
66- struct ZBuffer {
67- w : usize ,
68- h : usize ,
69- z : Vec < f64 > ,
70- id : Vec < Option < usize > > ,
71- }
72- impl ZBuffer {
73- fn new ( w : usize , h : usize ) -> Self {
74- Self {
75- w,
76- h,
77- z : vec ! [ f64 :: INFINITY ; w * h] ,
78- id : vec ! [ None ; w * h] ,
79- }
80- }
81- fn clear ( & mut self ) {
82- self . z . fill ( f64:: INFINITY ) ;
83- self . id . fill ( None ) ;
84- }
85- #[ inline]
86- fn idx ( & self , x : usize , y : usize ) -> usize {
87- y * self . w + x
88- }
89- fn test_and_set ( & mut self , x : usize , y : usize , depth : f64 , body_id : Option < usize > ) -> bool {
90- let i = self . idx ( x, y) ;
91- if depth < self . z [ i] {
92- self . z [ i] = depth;
93- self . id [ i] = body_id;
94- true
95- } else {
96- false
97- }
98- }
99- }
100-
10123// ============================================================================
10224// GRAVITY: NEWTONIAN N-BODY PHYSICS
10325// ============================================================================
@@ -169,95 +91,6 @@ fn create_body(
16991 }
17092}
17193
172- // ============================================================================
173- // DRAWING FUNCTIONS
174- // ============================================================================
175- fn make_sphere_points ( lat_steps : usize , lon_steps : usize ) -> Vec < Vec3 > {
176- let mut pts = Vec :: with_capacity ( lat_steps * lon_steps) ;
177- for i in 0 ..lat_steps {
178- let v = i as f64 / ( lat_steps - 1 ) . max ( 1 ) as f64 ;
179- let theta = v * std:: f64:: consts:: PI ;
180- let ( st, ct) = theta. sin_cos ( ) ;
181- for j in 0 ..lon_steps {
182- let u = j as f64 / lon_steps as f64 ;
183- let phi = u * std:: f64:: consts:: TAU ;
184- pts. push ( Vec3 :: new ( st * phi. cos ( ) , ct, st * phi. sin ( ) ) ) ;
185- }
186- }
187- pts
188- }
189-
190- fn project_to_screen ( v_cam : Vec3 , w : f64 , h : f64 , scale : f64 ) -> Option < ( isize , isize , f64 ) > {
191- if v_cam. z <= 1.5 {
192- return None ;
193- } // Camera culling
194- let px = ( v_cam. x / v_cam. z ) * 2.0 ;
195- let py = v_cam. y / v_cam. z ;
196- Some ( (
197- ( w / 2.0 + px * scale) . round ( ) as isize ,
198- ( h / 2.0 + py * scale) . round ( ) as isize ,
199- v_cam. z ,
200- ) )
201- }
202-
203- fn plot_z (
204- chart : & mut ChartContext ,
205- zb : & mut ZBuffer ,
206- x : isize ,
207- y : isize ,
208- z : f64 ,
209- col : Color ,
210- id : Option < usize > ,
211- ) {
212- if x < 0 || y < 0 {
213- return ;
214- }
215- let ( ux, uy) = ( x as usize , y as usize ) ;
216- if ux < zb. w && uy < zb. h && zb. test_and_set ( ux, uy, z, id) {
217- chart. canvas . set_pixel_screen ( ux, uy, Some ( col) ) ;
218- }
219- }
220-
221- fn line_z (
222- chart : & mut ChartContext ,
223- zb : & mut ZBuffer ,
224- p1 : ( isize , isize , f64 ) ,
225- p2 : ( isize , isize , f64 ) ,
226- col : Color ,
227- ) {
228- let min_x = p1. 0 . min ( p2. 0 ) ;
229- let max_x = p1. 0 . max ( p2. 0 ) ;
230- let min_y = p1. 1 . min ( p2. 1 ) ;
231- let max_y = p1. 1 . max ( p2. 1 ) ;
232-
233- if max_x < 0 || min_x >= zb. w as isize || max_y < 0 || min_y >= zb. h as isize {
234- return ;
235- }
236-
237- let dx = ( p2. 0 - p1. 0 ) . abs ( ) ;
238- let dy = ( p2. 1 - p1. 1 ) . abs ( ) ;
239- let steps = dx. max ( dy) . max ( 1 ) as i32 ;
240- if steps > 1500 {
241- return ;
242- }
243-
244- for s in 0 ..=steps {
245- let t = s as f64 / steps as f64 ;
246- let xf = p1. 0 as f64 + ( p2. 0 as f64 - p1. 0 as f64 ) * t;
247- let yf = p1. 1 as f64 + ( p2. 1 as f64 - p1. 1 as f64 ) * t;
248- let zf = p1. 2 + ( p2. 2 - p1. 2 ) * t;
249- plot_z (
250- chart,
251- zb,
252- xf. round ( ) as isize ,
253- yf. round ( ) as isize ,
254- zf,
255- col,
256- None ,
257- ) ;
258- }
259- }
260-
26194// ============================================================================
26295// MAIN LOOP
26396// ============================================================================
@@ -392,8 +225,8 @@ fn main() -> io::Result<()> {
392225
393226 // --- INPUT ---
394227 while event:: poll ( Duration :: from_millis ( 0 ) ) ? {
395- if let Event :: Key ( KeyEvent { code , .. } ) = event:: read ( ) ? {
396- match code {
228+ match event:: read ( ) ? {
229+ Event :: Key ( KeyEvent { code , .. } ) => match code {
397230 KeyCode :: Char ( 'q' ) | KeyCode :: Esc => {
398231 execute ! (
399232 stdout,
@@ -476,47 +309,15 @@ fn main() -> io::Result<()> {
476309 }
477310 }
478311 _ => { }
479- }
480- } else if let Event :: Mouse ( me) = event:: read ( ) ? {
481- let get_clicked_id = |c : u16 , r : u16 , zb : & ZBuffer | -> Option < usize > {
482- let mx = ( c. saturating_sub ( 2 ) as isize ) * 2 ;
483- let my = ( r. saturating_sub ( 2 ) as isize ) * 4 ;
484- let mut cl_id = None ;
485- let mut min_dist = 40.0 ;
486- let mut close_z = f64:: INFINITY ;
487- for py in ( my - 40 ) . max ( 0 ) as usize ..=( my + 40 ) . min ( zb. h as isize - 1 ) as usize
488- {
489- for px in
490- ( mx - 40 ) . max ( 0 ) as usize ..=( mx + 40 ) . min ( zb. w as isize - 1 ) as usize
491- {
492- if let Some ( id) = zb. id [ zb. idx ( px, py) ] {
493- let dist = ( ( px as f64 - mx as f64 ) . powi ( 2 )
494- + ( py as f64 - my as f64 ) . powi ( 2 ) )
495- . sqrt ( ) ;
496- if dist < min_dist {
497- min_dist = dist;
498- close_z = zb. z [ zb. idx ( px, py) ] ;
499- cl_id = Some ( id) ;
500- } else if ( dist - min_dist) . abs ( ) < 1.0
501- && zb. z [ zb. idx ( px, py) ] < close_z
502- {
503- close_z = zb. z [ zb. idx ( px, py) ] ;
504- cl_id = Some ( id) ;
505- }
506- }
507- }
508- }
509- cl_id
510- } ;
511-
512- match me. kind {
312+ } ,
313+ Event :: Mouse ( me) => match me. kind {
513314 MouseEventKind :: Down ( MouseButton :: Left ) => {
514315 is_dragging = true ;
515316 last_mouse_pos = Some ( ( me. column , me. row ) ) ;
516- selected_body = get_clicked_id ( me. column , me. row , zb ) ;
317+ selected_body = zb . pick_body ( me. column , me. row ) ;
517318 }
518319 MouseEventKind :: Down ( MouseButton :: Right ) => {
519- follow_body = get_clicked_id ( me. column , me. row , zb ) ;
320+ follow_body = zb . pick_body ( me. column , me. row ) ;
520321 if follow_body. is_some ( ) {
521322 selected_body = follow_body;
522323 }
@@ -576,7 +377,8 @@ fn main() -> io::Result<()> {
576377 }
577378 }
578379 _ => { }
579- }
380+ } ,
381+ _ => { }
580382 }
581383 }
582384
@@ -657,7 +459,12 @@ fn main() -> io::Result<()> {
657459 let mut v_cam = v_world. sub ( camera_target_offset) . sub ( cam_pos) ;
658460 v_cam = rotate_y ( v_cam, -cam_yaw) ;
659461 v_cam = rotate_x ( v_cam, -cam_pitch) ;
660- project_to_screen ( v_cam, cw, ch, scale)
462+ project_with_projection (
463+ v_cam,
464+ cw,
465+ ch,
466+ Projection :: new ( 1.5 , 0.5 , 0.5 , scale * 2.0 , scale) ,
467+ )
661468 } ;
662469
663470 for ( i, body) in bodies. iter ( ) . enumerate ( ) {
0 commit comments