1use std::collections::HashMap;
2
3use crate::{
4 error::{Error, Result},
5 input::{Input, Inputs},
6 render::{Render, RenderQuality},
7 repository::Repository,
8};
9
10pub struct Renders {
11 repo: Repository,
12 inputs: Inputs,
13 renders: HashMap<i32, Render>,
14 target_width: i32,
15 target_height: i32,
16 quality: RenderQuality,
17 peak_cache_count: usize,
18}
19
20impl Renders {
21 pub fn new(repo: Repository, inputs: Inputs, quality: RenderQuality) -> Self {
22 Renders {
23 repo,
24 inputs,
25 renders: HashMap::new(),
26 target_width: -1,
27 target_height: -1,
28 quality,
29 peak_cache_count: 0,
30 }
31 }
32
33 pub fn set_size(&mut self, w: i32, h: i32) {
34 self.target_width = w;
35 self.target_height = h;
36 }
37
38 fn check(&self) -> Result<()> {
39 if self.inputs.is_empty() {
40 return Err(Error::NoInputs);
41 }
42 Ok(())
43 }
44
45 pub fn get_render(&mut self, index: i32) -> Result<&Render> {
46 self.check()?;
47
48 if !self.renders.contains_key(&index) {
49 let mut render = Render::new(self.quality);
50 let mapping = self.repo.take_mapping(index)?;
51 render.attach_mapping(mapping);
52 render.apply_scaled(&self.inputs, self.target_width, self.target_height)?;
53
54 self.renders.insert(index, render);
55
56 if self.renders.len() > self.peak_cache_count {
57 self.peak_cache_count = self.renders.len();
58 }
59 }
60
61 Ok(self.renders.get(&index).unwrap())
62 }
63
64 pub fn get_render_mut(&mut self, index: i32) -> Result<&mut Render> {
65 self.check()?;
66
67 if !self.renders.contains_key(&index) {
68 let mut render = Render::new(self.quality);
69 let mapping = self.repo.take_mapping(index)?;
70 render.attach_mapping(mapping);
71 render.apply_scaled(&self.inputs, self.target_width, self.target_height)?;
72
73 self.renders.insert(index, render);
74
75 if self.renders.len() > self.peak_cache_count {
76 self.peak_cache_count = self.renders.len();
77 }
78 }
79
80 Ok(self.renders.get_mut(&index).unwrap())
81 }
82
83 pub fn remove_render(&mut self, index: i32) {
84 self.renders.remove(&index);
85 self.repo.remove_mapping(index);
86 }
87
88 pub fn remove_mapping(&mut self, index: i32) {
89 self.repo.remove_mapping(index);
90 }
91
92 pub fn length(&self) -> u32 {
93 self.repo.length()
94 }
95
96 pub fn peak(&self) -> usize {
97 self.peak_cache_count
98 }
99
100 pub fn repo(&self) -> &Repository {
101 &self.repo
102 }
103
104 pub fn repo_mut(&mut self) -> &mut Repository {
105 &mut self.repo
106 }
107
108 pub fn inputs(&self) -> &Inputs {
109 &self.inputs
110 }
111
112 pub fn inputs_mut(&mut self) -> &mut Inputs {
113 &mut self.inputs
114 }
115
116 pub fn auto_zoom(&mut self) -> Result<bool> {
117 self.check()?;
118
119 let mut render = Render::new(self.quality);
120 let mapping = self.repo.get_mapping(0)?.clone();
121 render.attach_mapping(mapping);
122
123 render.auto_zoom(&mut self.inputs)
124 }
125
126 pub fn scan(&mut self, frame: i32) -> Result<()> {
128 let frame = if frame < 0 { 0 } else { frame };
129
130 self.check()?;
131
132 if self.inputs.is_empty() {
133 return Ok(());
134 }
135
136 let num_inputs = self.inputs.len();
137
138 for k in 0..num_inputs {
139 let mut render = Render::new(self.quality);
140 let mapping = self.repo.get_mapping(frame)?.clone();
141 render.attach_mapping(mapping);
142
143 let input = &self.inputs[k];
144 let pts = render.get_cloud(input)?;
145
146 let layer = input.layer;
147 let filtered_pts: Vec<_> = pts.iter().filter(|p| p.layer == layer).collect();
148
149 if filtered_pts.is_empty() {
150 continue;
151 }
152
153 let mut x0 = filtered_pts[0].x;
154 let mut y0 = filtered_pts[0].y;
155 let mut x1 = x0;
156 let mut y1 = y0;
157 let mut tx = 0.0f64;
158 let mut ty = 0.0f64;
159
160 for pt in &filtered_pts {
161 if pt.x < x0 {
162 x0 = pt.x;
163 }
164 if pt.x > x1 {
165 x1 = pt.x;
166 }
167 if pt.y < y0 {
168 y0 = pt.y;
169 }
170 if pt.y > y1 {
171 y1 = pt.y;
172 }
173 tx += pt.x;
174 ty += pt.y;
175 }
176
177 let ct = filtered_pts.len() as f64;
178 if ct > 0.0 {
179 let _ = tx / ct;
180 let _ = ty / ct;
181 }
182
183 tx = (x0 + x1) / 2.0;
184 ty = (y0 + y1) / 2.0;
185
186 let dx = x0 + (x1 - x0);
187 let dy = y0 + (y1 - y0);
188
189 let pts_shifted: Vec<_> = filtered_pts
190 .iter()
191 .map(|p| delaunator::Point { x: p.x + dx, y: p.y + dy })
192 .collect();
193
194 if pts_shifted.len() < 3 {
195 continue;
196 }
197
198 let triangulation = delaunator::triangulate(&pts_shifted);
199
200 let input_img = input.get();
201 let input_ref = &self.inputs[k];
202
203 let result = self.tweak_scale(
204 input_img,
205 x0 + dx,
206 y0 + dy,
207 x1 + dx,
208 y1 + dy,
209 dx,
210 dy,
211 tx + dx,
212 ty + dy,
213 &triangulation,
214 &pts_shifted,
215 input_ref,
216 );
217
218 if let Some((scale, _x_adj, y_adj)) = result {
219 let input_mut = &mut self.inputs.get_mut()[k];
220 input_mut.xs /= scale;
221 input_mut.ys /= scale;
222 input_mut.in_y0 += y_adj;
223 }
224 }
225
226 Ok(())
227 }
228
229 fn tweak_scale(
230 &self,
231 img: &image::RgbaImage,
232 x0: f64,
233 y0: f64,
234 x1: f64,
235 y1: f64,
236 dx: f64,
237 dy: f64,
238 tx: f64,
239 ty: f64,
240 triangulation: &delaunator::Triangulation,
241 points: &[delaunator::Point],
242 input: &Input,
243 ) -> Option<(f64, f64, f64)> {
244 let mut actives = 0;
245 let mut ix0 = 0.0f64;
246 let mut iy0 = 0.0f64;
247 let mut ix1 = 0.0f64;
248 let mut iy1 = 0.0f64;
249 let mut first = true;
250
251 for (x, y, pixel) in img.enumerate_pixels() {
252 if Self::is_active(pixel) {
253 actives += 1;
254 let xf = x as f64;
255 let yf = y as f64;
256 if first {
257 ix0 = xf;
258 ix1 = xf;
259 iy0 = yf;
260 iy1 = yf;
261 first = false;
262 } else {
263 if xf > ix1 {
264 ix1 = xf;
265 }
266 if xf < ix0 {
267 ix0 = xf;
268 }
269 if yf > iy1 {
270 iy1 = yf;
271 }
272 if yf < iy0 {
273 iy0 = yf;
274 }
275 }
276 }
277 }
278
279 if actives >= (img.width() * img.height() * 9 / 10) as i32 {
280 return None;
281 }
282
283 let ix = (ix0 + ix1) / 2.0;
284 let iy = (iy0 + iy1) / 2.0;
285 let xt = tx - dx;
286 let yt = ty - dy;
287
288 let blo = 0.1f64;
290 let bhi = 6.0f64;
291 let mut lo = blo;
292 let mut hi = bhi;
293 let mut good_scale = 0.01f64;
294 let mut decent_scale = 1.0f64;
295 let mut decent_scale_value = -1.0f64;
296
297 while (lo - hi).abs() > 0.01 {
298 let curr = (lo + hi) / 2.0;
299 let (nin, nout) = self.tweak_one(
300 img,
301 x0,
302 y0,
303 x1,
304 y1,
305 dx,
306 dy,
307 xt,
308 yt,
309 ix,
310 iy,
311 triangulation,
312 points,
313 curr,
314 );
315
316 let q = if nout == 0 { 1.0 } else { nin as f64 / (nin + nout) as f64 };
317 let v = Self::eval(q, curr);
318
319 if v > decent_scale_value {
320 decent_scale = curr;
321 decent_scale_value = v;
322 }
323
324 if q > 0.9999 {
325 if curr > good_scale {
326 good_scale = curr;
327 }
328 lo = curr;
329 } else {
330 hi = curr;
331 }
332 }
333
334 let steps = 20;
336 for i in 1..=steps {
337 let curr = blo + (bhi - blo) * (i as f64 / steps as f64);
338 let (nin, nout) = self.tweak_one(
339 img,
340 x0,
341 y0,
342 x1,
343 y1,
344 dx,
345 dy,
346 xt,
347 yt,
348 ix,
349 iy,
350 triangulation,
351 points,
352 curr,
353 );
354
355 let q = if nout == 0 { 1.0 } else { nin as f64 / (nin + nout) as f64 };
356 let v = Self::eval(q, curr);
357
358 if v > decent_scale_value {
359 decent_scale = curr;
360 decent_scale_value = v;
361 }
362 }
363
364 good_scale = decent_scale * 0.97;
365
366 let y_adj = Self::remap(good_scale, yt, iy, input.in_y0, input);
367
368 Some((good_scale, 0.0, y_adj))
369 }
370
371 fn eval(q: f64, curr: f64) -> f64 {
372 q * curr.sqrt() + if q >= 0.9999 { 1.0 } else { 0.0 }
373 }
374
375 fn remap(scale: f64, v: f64, i: f64, delta: f64, input: &Input) -> f64 {
376 let scale = 1.0 / scale;
377 i - (scale * v - input.in_scale as f64 / 2.0 * (scale - 1.0) - scale * delta) - delta
378 }
379
380 fn tweak_one(
381 &self,
382 img: &image::RgbaImage,
383 x0: f64,
384 y0: f64,
385 x1: f64,
386 y1: f64,
387 dx: f64,
388 dy: f64,
389 xt: f64,
390 yt: f64,
391 ix: f64,
392 iy: f64,
393 triangulation: &delaunator::Triangulation,
394 points: &[delaunator::Point],
395 scale: f64,
396 ) -> (i32, i32) {
397 let mut nin = 0;
398 let mut nout = 0;
399
400 for (x, y, pixel) in img.enumerate_pixels() {
401 if Self::is_active(pixel) {
402 let xx = dx + (x as f64 - ix) * scale + xt;
403 let yy = dy + (y as f64 - iy) * scale + yt;
404
405 let dud = if xx < x0 || xx > x1 || yy < y0 || yy > y1 {
406 true
407 } else {
408 !Self::point_in_triangulation(xx, yy, triangulation, points, x0, y0, x1, y1)
409 };
410
411 if dud {
412 nout += 1;
413 } else {
414 nin += 1;
415 }
416 }
417 }
418
419 (nin, nout)
420 }
421
422 fn is_active(pixel: &image::Rgba<u8>) -> bool {
423 pixel[3] > 25 && (pixel[0] < 250 || pixel[1] < 250 || pixel[2] < 250)
424 }
425
426 fn point_in_triangulation(
427 x: f64,
428 y: f64,
429 triangulation: &delaunator::Triangulation,
430 points: &[delaunator::Point],
431 x0: f64,
432 y0: f64,
433 x1: f64,
434 y1: f64,
435 ) -> bool {
436 let num_triangles = triangulation.triangles.len() / 3;
437
438 for t in 0..num_triangles {
439 let i0 = triangulation.triangles[t * 3];
440 let i1 = triangulation.triangles[t * 3 + 1];
441 let i2 = triangulation.triangles[t * 3 + 2];
442
443 let p0 = &points[i0];
444 let p1 = &points[i1];
445 let p2 = &points[i2];
446
447 let d01 = ((p0.x - p1.x).powi(2) + (p0.y - p1.y).powi(2)).sqrt();
449 let d12 = ((p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sqrt();
450 let d20 = ((p2.x - p0.x).powi(2) + (p2.y - p0.y).powi(2)).sqrt();
451
452 if d01 > 100.0 || d12 > 100.0 || d20 > 100.0 {
453 continue;
454 }
455
456 if p0.x < x0
457 || p0.x > x1
458 || p0.y < y0
459 || p0.y > y1
460 || p1.x < x0
461 || p1.x > x1
462 || p1.y < y0
463 || p1.y > y1
464 || p2.x < x0
465 || p2.x > x1
466 || p2.y < y0
467 || p2.y > y1
468 {
469 continue;
470 }
471
472 if Self::point_in_triangle(x, y, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y) {
473 return true;
474 }
475 }
476
477 false
478 }
479
480 fn point_in_triangle(
481 x: f64,
482 y: f64,
483 x0: f64,
484 y0: f64,
485 x1: f64,
486 y1: f64,
487 x2: f64,
488 y2: f64,
489 ) -> bool {
490 let area = 0.5 * (-y1 * x2 + y0 * (-x1 + x2) + x0 * (y1 - y2) + x1 * y2);
491 let s = (y0 * x2 - x0 * y2 + (y2 - y0) * x + (x0 - x2) * y) / (2.0 * area);
492 let t = (x0 * y1 - y0 * x1 + (y0 - y1) * x + (x1 - x0) * y) / (2.0 * area);
493
494 s >= 0.0 && t >= 0.0 && (1.0 - s - t) >= 0.0
495 }
496}