1use std::io;
4use std::io::stdout;
5use std::time::Instant;
6use crossterm::event::KeyCode;
7use teng::components::Component;
8use teng::rendering::pixel::Pixel;
9use teng::rendering::renderer::Renderer;
10use teng::util::planarvec::Bounds;
11use teng::util::planarvec2_experimental::ExponentialGrowingBounds;
12use teng::{
13 install_panic_handler, terminal_cleanup, terminal_setup, Game, SetupInfo, SharedState,
14 UpdateInfo,
15};
16use teng::rendering::color::Color;
17use teng::rendering::render::{HalfBlockDisplayRender, Render};
18use teng::util::for_coord_in_line;
19
20fn main() -> io::Result<()> {
21 terminal_setup()?;
22 install_panic_handler();
23
24 let mut game = Game::new_with_custom_buf_writer();
25 game.install_recommended_components();
26 game.add_component(Box::new(FabrikComponent::new()));
27 game.run()?;
28
29 terminal_cleanup()?;
30
31 Ok(())
32}
33
34#[derive(Copy, Clone)]
35struct Point<T> {
36 x: T,
37 y: T,
38}
39
40impl Point<f64> {
41 fn distance(&self, other: &Self) -> f64 {
42 ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
43 }
44}
45
46struct Segment {
47 start: Point<f64>,
48 length: f64,
49}
50
51pub struct FabrikComponent {
52 base_anchor: Option<Point<f64>>,
53 target: Option<Point<f64>>,
54 segments: Vec<Segment>,
55 last_point: Option<Point<f64>>,
56 currently_creating_segment: Option<Point<f64>>,
57 half_block_display_render: HalfBlockDisplayRender,
58}
59
60impl FabrikComponent {
61 pub fn new() -> Self {
62 Self {
63 base_anchor: None,
64 target: None,
65 segments: vec![],
66 last_point: None,
67 currently_creating_segment: None,
68 half_block_display_render: HalfBlockDisplayRender::new(0, 0),
69 }
70 }
71
72 fn forward_reach(&mut self) {
73 let Some(target) = self.target else {
74 return;
75 };
76 let mut last_point = target;
77 self.last_point = Some(target);
78 for segment in self.segments.iter_mut().rev() {
79 let start = last_point;
80 let end = segment.start;
81
82 let length = segment.length;
83 let mut distance = start.distance(&end);
84 if distance < 0.0001 {
85 distance = 0.0001;
86 }
87 let ratio = length / distance;
88
89 let new_end = Point {
90 x: (1.0 - ratio) * start.x + ratio * end.x,
91 y: (1.0 - ratio) * start.y + ratio * end.y,
92 };
93
94 segment.start = new_end;
95
96 last_point = new_end;
97 }
98 }
99
100 fn backward_reach(&mut self) {
101 let Some(base_anchor) = self.base_anchor else {
102 return;
103 };
104 let Some(target) = self.target else {
105 return;
106 };
107 let mut last_point = base_anchor;
108 for i in 0..self.segments.len() {
109 let start = last_point;
110 let end = if i < self.segments.len() - 1 {
111 self.segments[i + 1].start
112 } else {
113 target
114 };
115 let segment = &mut self.segments[i];
116 let length = segment.length;
117 let mut distance = start.distance(&end);
118 if distance < 0.0001 {
119 distance = 0.0001;
120 }
121 let ratio = length / distance;
122
123 let new_end = Point {
124 x: (1.0 - ratio) * start.x + ratio * end.x,
125 y: (1.0 - ratio) * start.y + ratio * end.y,
126 };
127
128 segment.start = start;
129
130 last_point = new_end;
131 }
132
133 self.last_point = Some(last_point);
134 }
135
136 fn render_to_half_block_display(&mut self, mouse_point: Point<f64>) {
137 self.half_block_display_render.clear();
138
139 let segment_line_color = Color::Rgb([255, 255, 255]);
140 let target_color = Color::Rgb([0, 255, 0]);
141 let creating_segment_color = Color::Rgb([255, 0, 0]);
142 let segment_start_color = Color::Rgb([255, 255, 0]);
143
144 let base_anchor_color = Color::Rgb([0, 0, 255]);
145
146 if self.segments.len() > 0 {
147 let mut last_point = self.last_point.unwrap(); for segment in self.segments.iter().rev() {
149 let start = last_point;
150 let end = segment.start;
151
152 let x_start = start.x.floor() as i64;
153 let y_start = start.y.floor() as i64;
154 let x_end = end.x.floor() as i64;
155 let y_end = end.y.floor() as i64;
156
157 for_coord_in_line(true, (x_start, y_start), (x_end, y_end), |x, y| {
158 if x < 0 || y < 0 {
159 return;
160 }
161 self.half_block_display_render.set_color(x as usize, y as usize, segment_line_color);
162 });
163
164 if x_start >= 0 && y_start >= 0 {
165 self.half_block_display_render.set_color(x_start as usize, y_start as usize, segment_start_color);
166 }
167
168 last_point = segment.start;
169 }
170 }
171
172
173 if let Some(start_point) = self.currently_creating_segment {
174 let x_u_start = start_point.x.floor() as usize;
175 let y_u_start = start_point.y.floor() as usize;
176 let x_u_end = mouse_point.x.floor() as usize;
177 let y_u_end = mouse_point.y.floor() as usize;
178
179 for_coord_in_line(false, (x_u_start as i64, y_u_start as i64), (x_u_end as i64, y_u_end as i64), |x, y| {
180 self.half_block_display_render.set_color(x as usize, y as usize, creating_segment_color);
181 });
182 }
183
184 if let Some(target) = self.target {
185 let x_u = target.x.floor() as usize;
186 let y_u = target.y.floor() as usize;
187 self.half_block_display_render.set_color(x_u, y_u, target_color);
188 }
189
190 if let Some(base_anchor) = self.base_anchor {
191 let x_u = base_anchor.x.floor() as usize;
192 let y_u = base_anchor.y.floor() as usize;
193 self.half_block_display_render.set_color(x_u, y_u, base_anchor_color);
194 }
195 }
196}
197
198impl Component for FabrikComponent {
199 fn setup(&mut self, setup_info: &SetupInfo, shared_state: &mut SharedState<()>) {
200 self.on_resize(setup_info.display_info.width(), setup_info.display_info.height(), shared_state);
205 }
206
207 fn on_resize(&mut self, width: usize, height: usize, shared_state: &mut SharedState<()>) {
208 self.half_block_display_render = HalfBlockDisplayRender::new(width, 2 * height);
209 }
210
211 fn update(&mut self, update_info: UpdateInfo, shared_state: &mut SharedState<()>) {
212 let (x, y) = shared_state.mouse_info.last_mouse_pos;
213 let mouse_point = Point {
214 x: x as f64,
215 y: y as f64 * 2.0,
216 };
217
218 if shared_state.mouse_pressed.right {
219 if let Some(last_point) = self.last_point {
221 if self.currently_creating_segment.is_none() {
222 let new_segment = Segment {
223 start: last_point,
224 length: last_point.distance(&mouse_point),
225 };
226 self.segments.push(new_segment);
227 if self.base_anchor.is_none() {
228 self.base_anchor = Some(last_point);
229 }
230 self.last_point = Some(mouse_point);
231 }
232 }
233
234 if let Some(start_point) = self.currently_creating_segment.take() {
235 let length = start_point.distance(&mouse_point);
236 self.last_point = Some(mouse_point);
237 let new_segment = Segment {
238 start: start_point,
239 length,
240 };
241 self.segments.push(new_segment);
242 if self.base_anchor.is_none() {
243 self.base_anchor = Some(start_point);
244 }
245 }
246
247 self.currently_creating_segment = Some(mouse_point);
248 }
249
250 if shared_state.pressed_keys.inner().contains_key(&KeyCode::Esc) {
251 self.currently_creating_segment = None;
252 }
253
254 if shared_state.mouse_info.left_mouse_down {
255 self.target = Some(mouse_point);
257 self.currently_creating_segment = None;
258
259 self.forward_reach();
260 self.backward_reach();
261 }
262
263 shared_state.mouse_events.for_each_linerp_only_fresh(|mi| {
264 if mi.middle_mouse_down {
265 self.currently_creating_segment = None;
266 let mouse_point = Point {
267 x: mi.last_mouse_pos.0 as f64,
268 y: mi.last_mouse_pos.1 as f64 * 2.0,
269 };
270
271 if self.base_anchor.is_none() {
272 self.base_anchor = Some(mouse_point);
273 }
274 let Some(last_point) = self.last_point else {
275 self.last_point = Some(mouse_point);
276 return;
277 };
278
279 let new_segment = Segment {
280 start: last_point,
281 length: last_point.distance(&mouse_point),
282 };
283 self.segments.push(new_segment);
284 self.last_point = Some(mouse_point);
285 }
286 });
287
288 if shared_state.pressed_keys.did_press_char_ignore_case('c') {
296 self.segments.clear();
297 self.base_anchor = None;
298 self.target = None;
299 self.currently_creating_segment = None;
300 self.last_point = None;
301 }
302
303
304 self.render_to_half_block_display(mouse_point);
306 }
307
308 fn render(&self, renderer: &mut dyn Renderer, shared_state: &SharedState<()>, depth_base: i32) {
309 self.half_block_display_render.render(renderer, 0, 0, depth_base);
310 }
311}