1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
extern crate ndarray;
use ndarray::*;
use std::f32;

pub struct Character {
    pub rho: f32,
    pub theta: f32,
    pub string: String
}

fn new_char(theta: f32, rho: f32, string: &'static str) -> Character {
    Character { rho: rho, theta: theta, string: string.to_string() }
}

impl Character {
    pub fn score(&self, theta: f32, rho: f32) -> f32 {
        (self.theta - theta).abs() * 50. + (self.rho - rho).abs()
    }
}

pub struct AsciiArtFilter {
    pub character_list: Vec<Character>
}

pub fn default() -> AsciiArtFilter {
    let character_list = vec![
        new_char(0., 0.25, "| "),
        new_char(0., 0.5,  "|"),
        new_char(0., 0.75, " |"),
        new_char(f32::asin(1./f32::sqrt(5.)), 1./f32::sqrt(5.), "/ "),
        new_char(f32::asin(1./f32::sqrt(5.)), 2./f32::sqrt(5.), " /"),
        new_char(f32::asin(1./f32::sqrt(5.)), 1.5/f32::sqrt(5.), "/"),
        new_char(1./4.*f32::consts::PI, 1./f32::sqrt(32.), "\" "),
        new_char(1./4.*f32::consts::PI, 1./f32::sqrt(2.),  "/"),
        new_char(1./4.*f32::consts::PI, 7./f32::sqrt(32.), " ."),
        new_char(1./2.*f32::consts::PI, 0.25, " ̄"),
        new_char(1./2.*f32::consts::PI, 0.5,  "ー"),
        new_char(1./2.*f32::consts::PI, 0.75, "_"),
        new_char(3./4.*f32::consts::PI, 3./f32::sqrt(32.), ". "),
        new_char(3./4.*f32::consts::PI, 0.,  "\"),
        new_char(3./4.*f32::consts::PI, -3./f32::sqrt(32.),  " \""),
        new_char(
            f32::consts::PI - f32::asin(1./f32::sqrt(5.)),
            0.,
            "\\ "),
        new_char(
            f32::consts::PI - f32::asin(1./f32::sqrt(5.)),
            - 0.5/f32::sqrt(5.),
            "\"),
        new_char(
            f32::consts::PI - f32::asin(1./f32::sqrt(5.)),
            - 1./f32::sqrt(5.),
            " \\"),
    ];

    let aaf: AsciiArtFilter = AsciiArtFilter {
        character_list: character_list
    };

    return aaf;
}

impl AsciiArtFilter {
    pub fn run(&self, img: Array3<f32>) -> String {
        let mut result = String::from("");
        for yi in 0..img.shape()[0] {
            for xi in 0..img.shape()[1] {
                let slope = img[[yi, xi, 0]];
                let rho = img[[yi, xi, 1]];
                let character = self.nearest_character(slope, rho);
                result += &character;
            }
            result += "\n";
        }
        return result;
    }

    fn nearest_character(&self, theta: f32, rho: f32) -> String {
        if theta.is_nan() || rho.is_nan() {
            return String::from(" ");
        }
        let minimum_character_by_theta_opt = self.character_list.iter()
                                            .min_by(|left, right|
                                                left.score(theta, rho)
                                                    .partial_cmp(&right.score(theta, rho))
                                                    .unwrap());
        let minimum_character_by_theta = minimum_character_by_theta_opt.unwrap();
        return minimum_character_by_theta.string.clone();
    }
}