pub fn compose_2d(a: &Mat3, b: &Mat3) -> Mat3Expand description
Compose two 2D transforms: result = a * b (b applied first)
Examples found in repository?
examples/render_test.rs (lines 97-103)
14fn main() {
15 let mut fb = Framebuffer::new(WIDTH, HEIGHT);
16
17 // Background: NexVigilant Navy
18 fb.clear(Color::NAVY);
19
20 // === Section 1: Basic shapes (top-left) ===
21
22 // Colored rectangles
23 let r1 = shapes::rect(20.0, 20.0, 120.0, 80.0, Color::ACCENT_CYAN);
24 rasterize_mesh(&mut fb, &r1);
25
26 let r2 = shapes::rect(160.0, 20.0, 120.0, 80.0, Color::ACCENT_GREEN);
27 rasterize_mesh(&mut fb, &r2);
28
29 let r3 = shapes::rect(300.0, 20.0, 120.0, 80.0, Color::ACCENT_GOLD);
30 rasterize_mesh(&mut fb, &r3);
31
32 let r4 = shapes::rect(440.0, 20.0, 120.0, 80.0, Color::ACCENT_RED);
33 rasterize_mesh(&mut fb, &r4);
34
35 // === Section 2: Gradient rectangle (top-right) ===
36 let grad = shapes::rect_gradient(
37 600.0,
38 20.0,
39 180.0,
40 80.0,
41 Color::ACCENT_CYAN,
42 Color::ACCENT_GREEN,
43 Color::ACCENT_GOLD,
44 Color::ACCENT_RED,
45 );
46 rasterize_mesh(&mut fb, &grad);
47
48 // === Section 3: Circles (middle row) ===
49 let c1 = shapes::circle(80.0, 180.0, 50.0, 64, Color::ACCENT_CYAN);
50 rasterize_mesh(&mut fb, &c1);
51
52 let c2 = shapes::circle(220.0, 180.0, 50.0, 6, Color::ACCENT_GREEN); // hexagon
53 rasterize_mesh(&mut fb, &c2);
54
55 let c3 = shapes::circle(360.0, 180.0, 50.0, 3, Color::ACCENT_GOLD); // triangle
56 rasterize_mesh(&mut fb, &c3);
57
58 let c4 = shapes::circle(500.0, 180.0, 50.0, 32, Color::ACCENT_RED);
59 rasterize_mesh(&mut fb, &c4);
60
61 // === Section 4: Lines (middle) ===
62 for i in 0..8 {
63 let angle = (i as f64) * core::f64::consts::PI / 8.0;
64 let cx = 680.0;
65 let cy = 180.0;
66 let r = 50.0;
67 let x1 = cx + r * angle.cos();
68 let y1 = cy + r * angle.sin();
69 let color = Color::rgba(i as f64 / 8.0, 1.0 - (i as f64 / 8.0), 0.5, 1.0);
70 let l = shapes::line(cx, cy, x1, y1, 2.0, color);
71 rasterize_mesh(&mut fb, &l);
72 }
73
74 // === Section 5: Rounded rectangles (lower-middle) ===
75 let rr1 = shapes::rounded_rect(20.0, 260.0, 160.0, 80.0, 15.0, Color::ACCENT_CYAN, 8);
76 rasterize_mesh(&mut fb, &rr1);
77
78 let rr2 = shapes::rounded_rect(200.0, 260.0, 160.0, 80.0, 30.0, Color::ACCENT_GREEN, 8);
79 rasterize_mesh(&mut fb, &rr2);
80
81 let rr3 = shapes::rounded_rect(380.0, 260.0, 160.0, 80.0, 40.0, Color::ACCENT_GOLD, 16);
82 rasterize_mesh(&mut fb, &rr3);
83
84 // === Section 6: Barycentric color interpolation (RGB triangle) ===
85 let mut tri_mesh = Mesh::new();
86 tri_mesh.push(Triangle::new(
87 Vertex::colored(620.0, 260.0, Color::RED),
88 Vertex::colored(780.0, 260.0, Color::GREEN),
89 Vertex::colored(700.0, 340.0, Color::BLUE),
90 ));
91 rasterize_mesh(&mut fb, &tri_mesh);
92
93 // === Section 7: Transformed shapes (bottom) ===
94
95 // A small square, rotated 45° and translated
96 let sq = shapes::rect(0.0, 0.0, 60.0, 60.0, Color::WHITE);
97 let t = compose_2d(
98 &translate_2d(80.0, 430.0),
99 &compose_2d(
100 &rotate_2d(core::f64::consts::FRAC_PI_4),
101 &translate_2d(-30.0, -30.0), // center the square on origin before rotation
102 ),
103 );
104 rasterize_mesh_transformed(&mut fb, &sq, &t);
105
106 // Scaled circle
107 let small_c = shapes::circle(0.0, 0.0, 30.0, 32, Color::ACCENT_CYAN);
108 let t2 = compose_2d(
109 &translate_2d(220.0, 430.0),
110 &scale_2d(2.0, 1.0), // ellipse via non-uniform scale
111 );
112 rasterize_mesh_transformed(&mut fb, &small_c, &t2);
113
114 // Chain of rotated rectangles (fan)
115 for i in 0..12 {
116 let angle = (i as f64) * core::f64::consts::PI / 6.0;
117 let bar = shapes::rect(
118 0.0,
119 -3.0,
120 40.0,
121 6.0,
122 Color::rgba(0.5 + 0.5 * angle.cos(), 0.5 + 0.5 * angle.sin(), 0.7, 0.8),
123 );
124 let t_fan = compose_2d(&translate_2d(420.0, 460.0), &rotate_2d(angle));
125 rasterize_mesh_transformed(&mut fb, &bar, &t_fan);
126 }
127
128 // === Section 8: Primitive symbols (bottom-right) ===
129 // Draw 16 colored squares in a 4×4 grid representing the Lex Primitiva
130 let prim_colors = [
131 Color::from_hex(0x00CCFF), // σ Sequence
132 Color::from_hex(0x4488FF), // μ Mapping
133 Color::from_hex(0x00CC66), // ς State
134 Color::from_hex(0xCC66FF), // ρ Recursion
135 Color::from_hex(0x666666), // ∅ Void
136 Color::from_hex(0xFFCC00), // ∂ Boundary
137 Color::from_hex(0x008888), // ν Frequency
138 Color::from_hex(0xFFFFFF), // ∃ Existence
139 Color::from_hex(0x006633), // π Persistence
140 Color::from_hex(0xFF4444), // → Causality
141 Color::from_hex(0xCC8800), // κ Comparison
142 Color::from_hex(0x00CCFF), // N Quantity
143 Color::from_hex(0x8800CC), // λ Location
144 Color::from_hex(0x880000), // ∝ Irreversibility
145 Color::from_hex(0x00CC66), // Σ Sum
146 Color::from_hex(0xFFCC00), // × Product
147 ];
148
149 for (i, color) in prim_colors.iter().enumerate() {
150 let row = i / 4;
151 let col = i % 4;
152 let x = 580.0 + col as f64 * 55.0;
153 let y = 380.0 + row as f64 * 55.0;
154 let sq = shapes::rounded_rect(x, y, 45.0, 45.0, 8.0, *color, 6);
155 rasterize_mesh(&mut fb, &sq);
156 }
157
158 // === Output PPM to stdout ===
159 // PPM P6: binary RGB
160 let header = format!("P6\n{} {}\n255\n", WIDTH, HEIGHT);
161 let mut output: Vec<u8> = Vec::with_capacity(header.len() + (WIDTH * HEIGHT * 3) as usize);
162 output.extend_from_slice(header.as_bytes());
163
164 for &pixel in &fb.pixels {
165 output.push(((pixel >> 16) & 0xFF) as u8); // R
166 output.push(((pixel >> 8) & 0xFF) as u8); // G
167 output.push((pixel & 0xFF) as u8); // B
168 }
169
170 // Write binary to stdout
171 use std::io::Write;
172 let stdout = std::io::stdout();
173 let mut lock = stdout.lock();
174 let _ = lock.write_all(&output);
175}