1extern crate image;
6extern crate rand;
7
8use super::{Coordinate, Hash};
9use image::Rgba;
10use rand::{distributions::Uniform, Rng};
11
12use std::{
13 cmp::{max, min},
14 f64,
15 mem::swap,
16};
17
18pub fn find<T: Hash>(element: u64, list: &[T]) -> Option<&T> {
22 list.iter().find(|&x| x.hash() == element)
23}
24
25pub fn range_color(
59 falloff: i16,
60 base: image::Rgba<u8>,
61 base_geo: Coordinate,
62 to_geo: Coordinate,
63) -> image::Rgba<u8> {
64 let diff = (base_geo - to_geo).abs();
65 let x_scale: f64 = f64::from(diff.x) / f64::from(falloff);
66 let y_scale: f64 = f64::from(diff.y) / f64::from(falloff);
67 let max_multi: f64 =
68 f64::from(i32::from(base[0]) + i32::from(base[1]) + i32::from(base[2]) / 3);
69 let modify = (-max_multi * (x_scale + y_scale) / 2.0) as i32;
70
71 image::Rgba([
72 border(base[0], modify),
73 border(base[1], modify),
74 border(base[2], modify),
75 border(base[3], 0),
76 ])
77}
78
79pub fn roll<T: Into<u32>>(min: T, max: T) -> u32 {
92 let mut rng = rand::thread_rng();
93 rng.sample(Uniform::new(min.into(), max.into()))
94}
95
96pub fn random_item(list: &[String]) -> &String {
100 let roll = roll(0, list.len() as u32);
101 &list[roll as usize]
102}
103
104pub fn border(a: u8, b: i32) -> u8 {
119 max(min(i32::from(a) + b, 255 as i32), 0) as u8
120}
121
122pub fn gen_rgba() -> Rgba<u8> {
135 (0..4).fold(super::consts::DEFAULT_RGBA, |mut acc, x| {
136 acc.data[x] = acc.data[x].saturating_add(roll(0u8, u8::max_value()) as u8);
137 acc
138 })
139}
140
141pub fn seed_rgba(seed: u64) -> Rgba<u8> {
145 let max = 254;
146 let r = seed % max;
147 let g = (seed + 75) % max;
148 let b = (seed + 150) % max;
149
150 Rgba([r as u8, g as u8, b as u8, 255])
151}
152
153pub fn plot(a: Coordinate, b: Coordinate) -> Vec<Coordinate> {
184 plot_type(a, b, &plot_bresenham)
185}
186
187pub fn plot_type(
188 mut a: Coordinate,
189 mut b: Coordinate,
190 plot_kind: &Fn(Coordinate, Coordinate) -> Vec<Coordinate>,
191) -> Vec<Coordinate> {
192 if a.lt(0) || b.lt(0) {
194 let add = Coordinate::new(max(-a.x, -b.x), max(-a.y, -b.y));
195 plot_type(a + add, b + add, &plot_bresenham)
196 .iter()
197 .fold(vec![], |mut acc, c| {
198 acc.push(*c - add);
199 acc
200 })
201 } else if a.x == b.x {
203 (min(a.y, b.y)..max(a.y, b.y))
204 .map(|y| Coordinate::new(a.x, y))
205 .collect()
206 } else {
207 if b.x < a.x {
208 swap(&mut a, &mut b);
209 }
210 plot_kind(a, b)
211 }
212}
213
214pub fn plot_bresenham(mut from: Coordinate, to: Coordinate) -> Vec<Coordinate> {
224 let delta = to - from;
225 let delta_x = f64::from(delta.x);
226 let delta_y = f64::from(delta.y);
227
228 if delta_x == 0.00 {
229 panic!("Bresenham does not support straight vertical lines!");
230 }
231
232 let delta_err: f64 = (delta_y / delta_x).abs();
233 let mut error: f64 = 0.00;
234
235 let mut plot: Vec<Coordinate> = Vec::new();
236 let mut last_y = from.y;
237
238 for x in min(from.x, to.x)..=max(from.x, to.x) {
239 for y in min(last_y, from.y)..=max(last_y, from.y) {
240 plot.push(Coordinate::new(x as i16, y));
241 }
242 last_y = from.y;
243 error += delta_err;
244 while error >= 0.50 {
245 from.y += f64::signum(delta_y) as i16;
246 error -= 1.00;
247 }
248 }
249 plot
250}
251
252pub fn plot_rectangle(mut from: Coordinate, to: Coordinate) -> Vec<Coordinate> {
256 let mut plot: Vec<Coordinate> = Vec::new();
257 let mut delta = Coordinate::new(1, 1);
258
259 if from.x > to.x {
260 delta.x = -1;
261 }
262
263 if from.y > to.y {
264 delta.y = -1;
265 }
266
267 while from.x != to.x {
268 from.x += delta.x;
269 plot.push(from);
270 }
271
272 while from.y != to.y {
273 from.y += delta.y;
274 plot.push(from);
275 }
276
277 plot
278}
279
280pub fn midpoint(a: Coordinate, b: Coordinate) -> Coordinate {
297 Coordinate::new((a.x + b.x) / 2, (a.y + b.y) / 2)
298}
299
300pub fn plot_ellipse(mut from: Coordinate, to: Coordinate) -> Vec<Coordinate> {
311 let min = Coordinate::new(min(from.x, to.x), min(from.y, to.y));
312 let _max = Coordinate::new(max(from.x, to.x), max(from.y, to.y));
313 let _t_max = midpoint(from, to);
314
315 let mut result = Vec::new();
316 let t = f64::consts::PI / 2.0; let mut theta: f64 = 0.5; let diff = from - to;
319 let step: f64 = t / 4.5; let c = min;
321 let r: f64 = f64::from(diff.x / 2).abs(); debug!(
324 "r: {} |t: {} |s: {} |from: {} |to: {} |diff: {} |c: {}",
325 r, t, step, from, to, diff, c
326 );
327
328 let mut s = Coordinate::new(1, 1);
330 if from.y > to.y {
331 s.y = -1;
332 }
333
334 while t > theta {
335 let point = from + coordinate!(r * theta.cos(), f64::from(s.y) * r * theta.sin() / 2.0);
336 debug!("Theta: {} | Coordinate: {} <- {}", theta, point, from);
337 let mut line = plot_type(from, point, &plot_bresenham);
338 result.append(&mut line);
339 from = point;
340 theta += step;
341 }
342 result.append(&mut plot_type(from, to, &plot_bresenham));
343 debug!("{:#?}", result);
344 result
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_border() {
353 assert_eq!(border(0, 0), 0);
354 assert_eq!(border(0, -55), 0);
355 assert_eq!(border(100, 100), 200);
356 assert_eq!(border(255, 255), 255);
357 assert_eq!(border(0, 255), 255);
358 assert_eq!(border(255, 0), 255);
359 assert_eq!(border(255, -255), 0);
360 }
361
362 #[test]
363 fn test_roll() {
364 let res = roll(0u32, 5);
365 assert!(res <= 5);
366 }
367
368 #[test]
369 fn test_random_item() {
370 let strings = ["a".to_string(), "b".to_string()];
371 let res = random_item(&strings);
372 let res = res == &strings[0] || res == &strings[1];
373 assert!(res);
374 }
375
376 #[test]
377 fn test_gen_rgba() {
378 for _ in 0..5 {
379 let _ = gen_rgba();
380 }
381 }
382
383 #[test]
384 fn test_seed_rgba() {
385 let seed = 9611;
386 for i in 0..30 {
387 let _ = seed_rgba(seed * i);
388 }
389 }
390
391 #[test]
392 fn test_seed_rgba_consistancy() {
393 let seed = 9611;
394 let res = seed_rgba(seed);
395 for _ in 0..30 {
396 assert_eq!(seed_rgba(seed), res);
397 }
398 }
399
400 #[test]
401 fn test_plot_start_and_end() {
402 let c1 = Coordinate::new(0, 0);
403 let c2 = Coordinate::new(10, 10);
404 let plot = plot(c1, c2);
405 assert!(c1 == plot[0] && c2 == plot[plot.len() - 1]);
406 }
407
408 #[test]
409 fn test_plot_straight_line() {
410 let c1 = Coordinate::new(0, 0);
411 let c2 = Coordinate::new(0, 10);
412 let plot = plot(c1, c2);
413 for i in 0..10 {
414 assert_eq!(plot[i].y, i as i16);
415 }
416 }
417}