avila_math/geometry/
geometry4d.rs

1// use std::f64::consts::PI;
2
3/// Ponto no espaço 4D: (x, y, z, w) ∈ ℝ⁴
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct Point4D {
6    pub x: f64,
7    pub y: f64,
8    pub z: f64,
9    pub w: f64,
10}
11
12impl Point4D {
13    pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
14        Self { x, y, z, w }
15    }
16
17    pub fn origin() -> Self {
18        Self::new(0.0, 0.0, 0.0, 0.0)
19    }
20
21    /// Distância euclidiana 4D
22    pub fn distance(&self, other: &Self) -> f64 {
23        let dx = self.x - other.x;
24        let dy = self.y - other.y;
25        let dz = self.z - other.z;
26        let dw = self.w - other.w;
27        (dx * dx + dy * dy + dz * dz + dw * dw).sqrt()
28    }
29
30    /// Norma (distância da origem)
31    pub fn norm(&self) -> f64 {
32        (self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()
33    }
34
35    /// Adiciona outro ponto (álgebra vetorial)
36    pub fn add(&self, other: &Self) -> Self {
37        Self::new(
38            self.x + other.x,
39            self.y + other.y,
40            self.z + other.z,
41            self.w + other.w,
42        )
43    }
44
45    /// Multiplica por escalar
46    pub fn scale(&self, scalar: f64) -> Self {
47        Self::new(
48            self.x * scalar,
49            self.y * scalar,
50            self.z * scalar,
51            self.w * scalar,
52        )
53    }
54
55    /// Produto escalar 4D
56    pub fn dot(&self, other: &Self) -> f64 {
57        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
58    }
59}
60
61/// Vetor 4D (equivalente a Point4D mas com semântica diferente)
62pub type Vector4D = Point4D;
63
64/// Matriz de transformação 4×4
65#[derive(Debug, Clone, Copy)]
66pub struct Matrix4x4 {
67    pub data: [[f64; 4]; 4],
68}
69
70impl Matrix4x4 {
71    pub fn identity() -> Self {
72        Self {
73            data: [
74                [1.0, 0.0, 0.0, 0.0],
75                [0.0, 1.0, 0.0, 0.0],
76                [0.0, 0.0, 1.0, 0.0],
77                [0.0, 0.0, 0.0, 1.0],
78            ],
79        }
80    }
81
82    pub fn zeros() -> Self {
83        Self {
84            data: [[0.0; 4]; 4],
85        }
86    }
87
88    /// Multiplica matriz por ponto 4D
89    pub fn transform(&self, point: &Point4D) -> Point4D {
90        Point4D::new(
91            self.data[0][0] * point.x
92                + self.data[0][1] * point.y
93                + self.data[0][2] * point.z
94                + self.data[0][3] * point.w,
95            self.data[1][0] * point.x
96                + self.data[1][1] * point.y
97                + self.data[1][2] * point.z
98                + self.data[1][3] * point.w,
99            self.data[2][0] * point.x
100                + self.data[2][1] * point.y
101                + self.data[2][2] * point.z
102                + self.data[2][3] * point.w,
103            self.data[3][0] * point.x
104                + self.data[3][1] * point.y
105                + self.data[3][2] * point.z
106                + self.data[3][3] * point.w,
107        )
108    }
109
110    /// Multiplica duas matrizes 4×4
111    pub fn multiply(&self, other: &Self) -> Self {
112        let mut result = Self::zeros();
113        for i in 0..4 {
114            for j in 0..4 {
115                for k in 0..4 {
116                    result.data[i][j] += self.data[i][k] * other.data[k][j];
117                }
118            }
119        }
120        result
121    }
122
123    /// Rotação no plano XY (mantém Z e W fixos)
124    pub fn rotation_xy(angle: f64) -> Self {
125        let cos_a = angle.cos();
126        let sin_a = angle.sin();
127        Self {
128            data: [
129                [cos_a, -sin_a, 0.0, 0.0],
130                [sin_a, cos_a, 0.0, 0.0],
131                [0.0, 0.0, 1.0, 0.0],
132                [0.0, 0.0, 0.0, 1.0],
133            ],
134        }
135    }
136
137    /// Rotação no plano ZW (4D puro!)
138    pub fn rotation_zw(angle: f64) -> Self {
139        let cos_a = angle.cos();
140        let sin_a = angle.sin();
141        Self {
142            data: [
143                [1.0, 0.0, 0.0, 0.0],
144                [0.0, 1.0, 0.0, 0.0],
145                [0.0, 0.0, cos_a, -sin_a],
146                [0.0, 0.0, sin_a, cos_a],
147            ],
148        }
149    }
150
151    /// Rotação no plano XW
152    pub fn rotation_xw(angle: f64) -> Self {
153        let cos_a = angle.cos();
154        let sin_a = angle.sin();
155        Self {
156            data: [
157                [cos_a, 0.0, 0.0, -sin_a],
158                [0.0, 1.0, 0.0, 0.0],
159                [0.0, 0.0, 1.0, 0.0],
160                [sin_a, 0.0, 0.0, cos_a],
161            ],
162        }
163    }
164
165    /// Rotação no plano YW
166    pub fn rotation_yw(angle: f64) -> Self {
167        let cos_a = angle.cos();
168        let sin_a = angle.sin();
169        Self {
170            data: [
171                [1.0, 0.0, 0.0, 0.0],
172                [0.0, cos_a, 0.0, -sin_a],
173                [0.0, 0.0, 1.0, 0.0],
174                [0.0, sin_a, 0.0, cos_a],
175            ],
176        }
177    }
178
179    /// Escala uniforme
180    pub fn scale(factor: f64) -> Self {
181        Self {
182            data: [
183                [factor, 0.0, 0.0, 0.0],
184                [0.0, factor, 0.0, 0.0],
185                [0.0, 0.0, factor, 0.0],
186                [0.0, 0.0, 0.0, factor],
187            ],
188        }
189    }
190
191    /// Translação 4D
192    pub fn translation(dx: f64, dy: f64, dz: f64, dw: f64) -> Self {
193        Self {
194            data: [
195                [1.0, 0.0, 0.0, dx],
196                [0.0, 1.0, 0.0, dy],
197                [0.0, 0.0, 1.0, dz],
198                [0.0, 0.0, 0.0, 1.0 + dw],
199            ],
200        }
201    }
202}
203
204/// Projeção de 4D para 3D (projeção perspectiva)
205pub struct Projection4Dto3D {
206    /// Distância do observador na dimensão W
207    pub viewer_distance: f64,
208}
209
210impl Projection4Dto3D {
211    pub fn new(viewer_distance: f64) -> Self {
212        Self { viewer_distance }
213    }
214
215    /// Projeção perspectiva de 4D para 3D
216    /// Similar à projeção de 3D para 2D, mas uma dimensão acima
217    pub fn project(&self, point: &Point4D) -> (f64, f64, f64) {
218        let w_factor = 1.0 / (self.viewer_distance - point.w);
219        (point.x * w_factor, point.y * w_factor, point.z * w_factor)
220    }
221
222    /// Projeção ortográfica (descarta a coordenada W)
223    pub fn project_orthographic(&self, point: &Point4D) -> (f64, f64, f64) {
224        (point.x, point.y, point.z)
225    }
226
227    /// Projeção estereográfica (da hiperesfera)
228    pub fn project_stereographic(&self, point: &Point4D) -> (f64, f64, f64) {
229        let denom = 1.0 - point.w;
230        if denom.abs() < 1e-10 {
231            // Polo norte mapeia para infinito
232            return (point.x * 1000.0, point.y * 1000.0, point.z * 1000.0);
233        }
234        (point.x / denom, point.y / denom, point.z / denom)
235    }
236}
237
238/// Hipercubo (Tesserato) - Análogo 4D do cubo
239pub struct Tesseract {
240    pub vertices: Vec<Point4D>,
241    pub edges: Vec<(usize, usize)>,
242    pub faces: Vec<Vec<usize>>, // Faces quadradas
243    pub cells: Vec<Vec<usize>>, // Células cúbicas (8 cubos)
244}
245
246impl Default for Tesseract {
247    fn default() -> Self {
248        Self::new()
249    }
250}
251
252impl Tesseract {
253    /// Cria um tesserato centrado na origem com lado de comprimento 2
254    pub fn new() -> Self {
255        let mut vertices = Vec::new();
256
257        // Gera 16 vértices: todas as combinações de ±1
258        for i in 0..16 {
259            let x = if i & 1 == 0 { -1.0 } else { 1.0 };
260            let y = if i & 2 == 0 { -1.0 } else { 1.0 };
261            let z = if i & 4 == 0 { -1.0 } else { 1.0 };
262            let w = if i & 8 == 0 { -1.0 } else { 1.0 };
263            vertices.push(Point4D::new(x, y, z, w));
264        }
265
266        // Gera 32 arestas
267        let mut edges = Vec::new();
268        for i in 0..16_usize {
269            for j in (i + 1)..16_usize {
270                // Conecta vértices que diferem em apenas uma coordenada
271                let diff: u32 = (i as u32) ^ (j as u32);
272                if diff.count_ones() == 1 {
273                    edges.push((i, j));
274                }
275            }
276        }
277
278        // Gera faces quadradas (24 faces)
279        let mut faces = Vec::new();
280        // Cada face é determinada por fixar 2 coordenadas
281        for fix_mask in 0..16_u32 {
282            if fix_mask.count_ones() == 2 {
283                let mut face = Vec::new();
284                for i in 0..16_usize {
285                    if (i as u32 & fix_mask) == fix_mask || (i as u32 & fix_mask) == 0 {
286                        face.push(i);
287                    }
288                }
289                if face.len() == 4 {
290                    faces.push(face);
291                }
292            }
293        }
294
295        // Gera células cúbicas (8 cubos)
296        // Cada célula é formada fixando uma das 4 coordenadas
297        let mut cells = Vec::new();
298
299        // Para cada coordenada e cada sinal
300        for coord in 0..4_u32 {
301            for sign in [0_u32, 1_u32] {
302                let mut cell = Vec::new();
303                for i in 0..16_usize {
304                    let bit = (i as u32 >> coord) & 1;
305                    if bit == sign {
306                        cell.push(i);
307                    }
308                }
309                if cell.len() == 8 {
310                    cells.push(cell);
311                }
312            }
313        }
314
315        Self {
316            vertices,
317            edges,
318            faces,
319            cells,
320        }
321    }
322
323    /// Aplica transformação a todos os vértices
324    pub fn transform(&mut self, matrix: &Matrix4x4) {
325        for vertex in &mut self.vertices {
326            *vertex = matrix.transform(vertex);
327        }
328    }
329
330    /// Retorna estatísticas do tesserato
331    pub fn stats(&self) -> TesseractStats {
332        TesseractStats {
333            vertices: self.vertices.len(),
334            edges: self.edges.len(),
335            faces: self.faces.len(),
336            cells: self.cells.len(),
337        }
338    }
339}
340
341#[derive(Debug)]
342pub struct TesseractStats {
343    pub vertices: usize,
344    pub edges: usize,
345    pub faces: usize,
346    pub cells: usize,
347}
348
349/// 24-cell - Politopo regular 4D autodual
350pub struct Cell24 {
351    pub vertices: Vec<Point4D>,
352    pub edges: Vec<(usize, usize)>,
353}
354
355impl Default for Cell24 {
356    fn default() -> Self {
357        Self::new()
358    }
359}
360
361impl Cell24 {
362    /// Cria um 24-cell com raio 1
363    pub fn new() -> Self {
364        let mut vertices = Vec::new();
365
366        // 24 vértices: permutações de (±1, ±1, 0, 0) e suas rotações
367        let coords = vec![
368            (1.0, 1.0, 0.0, 0.0),
369            (1.0, -1.0, 0.0, 0.0),
370            (-1.0, 1.0, 0.0, 0.0),
371            (-1.0, -1.0, 0.0, 0.0),
372            (1.0, 0.0, 1.0, 0.0),
373            (1.0, 0.0, -1.0, 0.0),
374            (-1.0, 0.0, 1.0, 0.0),
375            (-1.0, 0.0, -1.0, 0.0),
376            (1.0, 0.0, 0.0, 1.0),
377            (1.0, 0.0, 0.0, -1.0),
378            (-1.0, 0.0, 0.0, 1.0),
379            (-1.0, 0.0, 0.0, -1.0),
380            (0.0, 1.0, 1.0, 0.0),
381            (0.0, 1.0, -1.0, 0.0),
382            (0.0, -1.0, 1.0, 0.0),
383            (0.0, -1.0, -1.0, 0.0),
384            (0.0, 1.0, 0.0, 1.0),
385            (0.0, 1.0, 0.0, -1.0),
386            (0.0, -1.0, 0.0, 1.0),
387            (0.0, -1.0, 0.0, -1.0),
388            (0.0, 0.0, 1.0, 1.0),
389            (0.0, 0.0, 1.0, -1.0),
390            (0.0, 0.0, -1.0, 1.0),
391            (0.0, 0.0, -1.0, -1.0),
392        ];
393
394        for (x, y, z, w) in coords {
395            vertices.push(Point4D::new(x, y, z, w));
396        }
397
398        // Conecta vértices que estão a distância √2
399        let mut edges = Vec::new();
400        for i in 0..24 {
401            for j in (i + 1)..24 {
402                let dist = vertices[i].distance(&vertices[j]);
403                if (dist - 2.0_f64.sqrt()).abs() < 0.01 {
404                    edges.push((i, j));
405                }
406            }
407        }
408
409        Self { vertices, edges }
410    }
411
412    pub fn transform(&mut self, matrix: &Matrix4x4) {
413        for vertex in &mut self.vertices {
414            *vertex = matrix.transform(vertex);
415        }
416    }
417}
418
419/// Simplex 4D (5-cell) - Análogo 4D do tetraedro
420pub struct Simplex4D {
421    pub vertices: Vec<Point4D>,
422    pub edges: Vec<(usize, usize)>,
423}
424
425impl Default for Simplex4D {
426    fn default() -> Self {
427        Self::new()
428    }
429}
430
431impl Simplex4D {
432    /// Cria um simplex 4D regular
433    pub fn new() -> Self {
434        // 5 vértices em posições simétricas
435        let sqrt5 = 5.0_f64.sqrt();
436        let vertices = vec![
437            Point4D::new(1.0, 1.0, 1.0, -1.0 / sqrt5),
438            Point4D::new(1.0, -1.0, -1.0, -1.0 / sqrt5),
439            Point4D::new(-1.0, 1.0, -1.0, -1.0 / sqrt5),
440            Point4D::new(-1.0, -1.0, 1.0, -1.0 / sqrt5),
441            Point4D::new(0.0, 0.0, 0.0, 4.0 / sqrt5),
442        ];
443
444        // Conecta todos os pares (grafo completo)
445        let mut edges = Vec::new();
446        for i in 0..5 {
447            for j in (i + 1)..5 {
448                edges.push((i, j));
449            }
450        }
451
452        Self { vertices, edges }
453    }
454}
455
456/// Corpo rígido em 4D
457pub struct RigidBody4D {
458    pub position: Point4D,
459    pub velocity: Vector4D,
460    pub acceleration: Vector4D,
461    pub rotation: Matrix4x4,
462    pub angular_velocity: f64, // Simplificado
463}
464
465impl RigidBody4D {
466    pub fn new(position: Point4D) -> Self {
467        Self {
468            position,
469            velocity: Vector4D::origin(),
470            acceleration: Vector4D::origin(),
471            rotation: Matrix4x4::identity(),
472            angular_velocity: 0.0,
473        }
474    }
475
476    /// Atualiza física (Euler simples)
477    pub fn update(&mut self, dt: f64) {
478        // Atualiza velocidade
479        self.velocity = self.velocity.add(&self.acceleration.scale(dt));
480
481        // Atualiza posição
482        self.position = self.position.add(&self.velocity.scale(dt));
483
484        // Atualiza rotação (simplificado: rotação no plano XY)
485        let rot = Matrix4x4::rotation_xy(self.angular_velocity * dt);
486        self.rotation = self.rotation.multiply(&rot);
487    }
488}
489
490/// Renderizador ASCII 3D para visualização de projeções
491pub struct AsciiRenderer3D {
492    pub width: usize,
493    pub height: usize,
494    pub scale: f64,
495}
496
497impl AsciiRenderer3D {
498    pub fn new(width: usize, height: usize, scale: f64) -> Self {
499        Self {
500            width,
501            height,
502            scale,
503        }
504    }
505
506    /// Converte coordenadas 3D para coordenadas de tela 2D
507    fn to_screen(&self, x: f64, y: f64, _z: f64) -> (usize, usize) {
508        let sx = ((x * self.scale) + (self.width as f64 / 2.0)) as i32;
509        let sy = ((y * self.scale) + (self.height as f64 / 2.0)) as i32;
510        (
511            sx.max(0).min(self.width as i32 - 1) as usize,
512            sy.max(0).min(self.height as i32 - 1) as usize,
513        )
514    }
515
516    /// Renderiza arestas projetadas
517    pub fn render_edges(
518        &self,
519        vertices_3d: &[(f64, f64, f64)],
520        edges: &[(usize, usize)],
521    ) -> Vec<String> {
522        let mut buffer = vec![vec![' '; self.width]; self.height];
523
524        // Desenha vértices
525        for (x, y, z) in vertices_3d {
526            let (sx, sy) = self.to_screen(*x, *y, *z);
527            if sy < self.height && sx < self.width {
528                buffer[sy][sx] = '●';
529            }
530        }
531
532        // Desenha arestas (simplificado: apenas marcadores)
533        for (i, j) in edges {
534            if *i < vertices_3d.len() && *j < vertices_3d.len() {
535                let (x1, y1, z1) = vertices_3d[*i];
536                let (x2, y2, z2) = vertices_3d[*j];
537
538                // Interpola alguns pontos ao longo da aresta
539                for t in 0..5 {
540                    let t_norm = t as f64 / 4.0;
541                    let x = x1 + (x2 - x1) * t_norm;
542                    let y = y1 + (y2 - y1) * t_norm;
543                    let z = z1 + (z2 - z1) * t_norm;
544
545                    let (sx, sy) = self.to_screen(x, y, z);
546                    if sy < self.height && sx < self.width && buffer[sy][sx] == ' ' {
547                        buffer[sy][sx] = '·';
548                    }
549                }
550            }
551        }
552
553        buffer.iter().map(|row| row.iter().collect()).collect()
554    }
555}
556
557#[cfg(test)]
558mod tests {
559    use super::*;
560    use std::f64::consts::PI;
561
562    #[test]
563    fn test_point4d_distance() {
564        let p1 = Point4D::new(1.0, 0.0, 0.0, 0.0);
565        let p2 = Point4D::new(0.0, 1.0, 0.0, 0.0);
566        let dist = p1.distance(&p2);
567        assert!((dist - 2.0_f64.sqrt()).abs() < 1e-10);
568    }
569
570    #[test]
571    fn test_tesseract_structure() {
572        let tesseract = Tesseract::new();
573        let stats = tesseract.stats();
574
575        assert_eq!(stats.vertices, 16);
576        assert_eq!(stats.edges, 32);
577        assert_eq!(stats.cells, 8);
578    }
579
580    #[test]
581    fn test_rotation_4d() {
582        let p = Point4D::new(1.0, 0.0, 0.0, 0.0);
583        let rot = Matrix4x4::rotation_xy(PI / 2.0);
584        let rotated = rot.transform(&p);
585
586        assert!(rotated.x.abs() < 1e-10);
587        assert!((rotated.y - 1.0).abs() < 1e-10);
588    }
589
590    #[test]
591    fn test_24cell_vertices() {
592        let cell = Cell24::new();
593        assert_eq!(cell.vertices.len(), 24);
594
595        // Verifica que todos os vértices estão a distância √2 da origem
596        for v in &cell.vertices {
597            let dist = v.norm();
598            assert!((dist - 2.0_f64.sqrt()).abs() < 0.01);
599        }
600    }
601
602    #[test]
603    fn test_projection() {
604        let proj = Projection4Dto3D::new(4.0);
605        let p4d = Point4D::new(1.0, 1.0, 1.0, 1.0);
606        let (x, _y, _z) = proj.project(&p4d);
607
608        // Verifica que a projeção escalou corretamente
609        assert!((x - 1.0 / 3.0).abs() < 1e-10);
610    }
611}