figrs/
v_main.rs

1// --------------------------------------------------------------------------
2// main vertical smush routines (excluding rules)
3
4/*
5      txt1 - A line of text
6      txt2 - A line of text
7      opts - FIGlet options array
8
9      About: Takes in two lines of text and returns one of the following:
10      "valid" - These lines can be smushed together given the current smushing rules
11      "end" - The lines can be smushed, but we're at a stopping point
12      "invalid" - The two lines cannot be smushed together
13  */
14
15// Import necessary libraries and modules
16use std::cmp;
17
18use crate::layout::LayoutType;
19use crate::options::Options;
20use crate::uni_smush::uni_smush;
21use crate::rules::v_rule1_smush;
22use crate::rules::v_rule2_smush;
23use crate::rules::v_rule3_smush;
24use crate::rules::v_rule4_smush;
25use crate::rules::v_rule5_smush;
26
27// Define a function to determine if two lines can be vertically smushed
28fn can_vertical_smush<'a>(txt1: &'a str, txt2: &'a str, opts: &Options) -> &'a str {
29    if opts.fitting_rules.v_layout == LayoutType::FullWidth {
30        return "invalid";
31    }
32
33    let len = cmp::min(txt1.len(), txt2.len());
34
35    if len == 0 {
36        return "invalid";
37    }
38
39    let mut end_smush = false;
40
41    for (ch1, ch2) in txt1.chars().zip(txt2.chars()) {
42        if ch1 != ' ' && ch2 != ' ' {
43            if opts.fitting_rules.v_layout == LayoutType::Fitting {
44                return "invalid";
45            } else if opts.fitting_rules.v_layout == LayoutType::Smushing {
46                return "end";
47            } else {
48                if ch1 == '|' && ch2 == '|' {
49                    continue;
50                }
51                let mut valid_smush = opts.fitting_rules.v_rule1 && v_rule1_smush(ch1, ch2).is_some();
52                valid_smush = !valid_smush && opts.fitting_rules.v_rule2 && v_rule2_smush(ch1, ch2).is_some();
53                valid_smush = !valid_smush && opts.fitting_rules.v_rule3 && v_rule3_smush(ch1, ch2).is_some();
54                valid_smush = !valid_smush && opts.fitting_rules.v_rule4 && v_rule4_smush(ch1, ch2).is_some();
55                end_smush = true;
56                if valid_smush {
57                    return "invalid";
58                }
59            }
60        }
61    }
62
63    if end_smush {
64        "end"
65    } else {
66        "valid"
67    }
68}
69
70// Define a function to calculate the maximum vertical smush distance
71fn get_vertical_smush_dist(lines1: &Vec<String>, lines2: &Vec<String>, opts: &Options) -> usize {
72    let max_dist = lines1.len();
73    let len1 = lines1.len();
74    // let len2 = lines2.len();
75    let mut cur_dist = 1;
76    let mut result = String::new();
77
78    while cur_dist <= max_dist {
79        let sub_lines1 = &lines1[(len1.saturating_sub(cur_dist))..len1];
80        let sub_lines2 = &lines2[0..cmp::min(max_dist, cur_dist)];
81
82        let slen = sub_lines2.len();
83        result.clear();
84
85        for ii in 0..slen {
86            let ret = can_vertical_smush(&sub_lines1[ii], &sub_lines2[ii], opts);
87            if ret == "end" {
88                result = ret.to_string();
89            } else if ret == "invalid" {
90                result = ret.to_string();
91                break;
92            } else if result.is_empty() {
93                result = "valid".to_string();
94            }
95        }
96
97        if result == "invalid" {
98            cur_dist -= 1;
99            break;
100        }
101
102        if result == "end" {
103            break;
104        }
105
106        if result == "valid" {
107            cur_dist += 1;
108        }
109    }
110
111    cmp::min(max_dist, cur_dist)
112}
113
114// Define a function to vertically smush two lines
115fn vertically_smush_lines(line1: &str, line2: &str, opts: &Options) -> String {
116    let mut result = String::new();
117
118    for (ch1, ch2) in line1.chars().zip(line2.chars()) {
119        if ch1 != ' ' && ch2 != ' ' {
120            if opts.fitting_rules.v_layout == LayoutType::Fitting {
121                result.push(uni_smush(ch1, ch2, &' '));
122            } else if opts.fitting_rules.v_layout == LayoutType::Smushing {
123                result.push(uni_smush(ch1, ch2, &' '));
124            } else {
125                let mut valid_smush: Option<char> = None;
126                valid_smush = if opts.fitting_rules.v_rule5 { v_rule5_smush(ch1, ch2) } else { valid_smush };
127                valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule1 { v_rule1_smush(ch1, ch2) } else { valid_smush };
128                valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule2 { v_rule2_smush(ch1, ch2) } else { valid_smush };
129                valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule3 { v_rule3_smush(ch1, ch2) } else { valid_smush };
130                valid_smush = if valid_smush.is_none() && opts.fitting_rules.v_rule4 { v_rule4_smush(ch1, ch2) } else { valid_smush };
131
132                if valid_smush.is_some() {
133                    result.push(valid_smush.unwrap());
134                }
135            }
136        } else {
137            result.push(uni_smush(ch1, ch2, &' '));
138        }
139    }
140
141    result
142}
143
144// Define a function to vertically smush two sets of lines with overlap
145fn vertical_smush(lines1: &Vec<String>, lines2: &Vec<String>, overlap: usize, opts: &Options) -> Vec<String> {
146    let len1 = lines1.len();
147    let len2 = lines2.len();
148    let piece1 = lines1[0..cmp::max(0, len1.saturating_sub(overlap))].to_vec();
149    let piece2_1 = lines1[cmp::max(0, len1.saturating_sub(overlap))..len1].to_vec();
150    let piece2_2 = lines2[0..cmp::min(overlap, len2)].to_vec();
151
152    let mut piece2 = vec![];
153    // let mut result = vec![];
154
155    let len = piece2_1.len();
156    for ii in 0..len {
157        let line: String;
158        if ii >= len2 {
159            line = piece2_1[ii].clone();
160        } else {
161            line = vertically_smush_lines(&piece2_1[ii], &piece2_2[ii], opts);
162        }
163        piece2.push(line);
164    }
165
166    let piece3 = lines2[cmp::min(overlap, len2)..len2].to_vec();
167
168    let mut output = vec![];
169    output.extend_from_slice(&piece1);
170    output.extend_from_slice(&piece2);
171    output.extend_from_slice(&piece3);
172    let scope = output[0].len() - lines1[0].len();
173    pad_lines(&mut output, scope);
174
175    output
176}
177
178
179// Define a function to pad lines with spaces
180fn pad_lines(lines: &mut Vec<String>, num_spaces: usize) {
181    let padding: String = " ".repeat(num_spaces);
182    for line in lines.iter_mut() {
183        line.push_str(&padding);
184    }
185}
186
187// Define a function to smush vertical FIGlet lines
188pub fn smush_vertical_fig_lines(output: &mut Vec<String>, lines: &mut Vec<String>, opts: &Options) -> Vec<String> {
189    let len1 = output[0].len();
190    let len2 = lines[0].len();
191
192    // Add padding to lines if needed
193    if len1 > len2 {
194        pad_lines(lines, len1 - len2);
195    } else if len2 > len1 {
196        pad_lines(output, len2 - len1);
197    }
198
199    // Calculate the maximum vertical smush distance
200    let overlap = get_vertical_smush_dist(output, lines, opts);
201
202    vertical_smush(output, lines, overlap, opts)
203}
204
205