Skip to content

Commit de0af3d

Browse files
committed
assign colors differently when using white background
should solve #8
1 parent 0e2af96 commit de0af3d

File tree

1 file changed

+69
-44
lines changed

1 file changed

+69
-44
lines changed

src/main.rs

+69-44
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ enum Color {
2020
}
2121

2222
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
23-
enum BackGround {
23+
pub enum BackGround {
2424
Black,
2525
White,
2626
}
@@ -42,8 +42,8 @@ struct Cli {
4242
output: String,
4343

4444
/// Color used for heatmap
45-
#[arg(short, long, value_enum, value_parser, num_args = 0..=3, default_values_t = [Color::Red, Color::Blue, Color::Green])]
46-
color: Vec<Color>,
45+
#[arg(short, long, value_enum, value_parser, num_args = 0..=3)]
46+
color: Option<Vec<Color>>,
4747

4848
/// Color used for background
4949
#[arg(short, long, value_enum, value_parser, default_value_t = BackGround::Black)]
@@ -61,14 +61,7 @@ struct Cli {
6161
fn main() {
6262
env_logger::init();
6363
let args = Cli::parse();
64-
// check if there are equal number of arguments for the input and color parameters
65-
if args.input.len() != args.color.len() {
66-
panic!(
67-
"\n\nERROR: number of input files ({}) and colors ({}) do not match!",
68-
args.input.len(),
69-
args.color.len()
70-
);
71-
}
64+
let colors = assign_colors(&args);
7265
let transform_accuracy = if args.phred {
7366
transform::transform_accuracy_phred
7467
} else {
@@ -78,7 +71,6 @@ fn main() {
7871
for f in args.input {
7972
utils::is_file(&f).unwrap_or_else(|_| panic!("Input file {f} is invalid",));
8073
let hashmap = extract_data::bam_to_hashmap(&f, args.threads, transform_accuracy);
81-
println!("{:?}", hashmap);
8274
if args.normalize {
8375
hashmaps.push(extract_data::log_transform_hashmap(hashmap));
8476
} else {
@@ -88,13 +80,32 @@ fn main() {
8880
plot_heatmap(
8981
hashmaps,
9082
args.background,
91-
args.color,
83+
colors,
9284
&args.output,
9385
transform_accuracy,
9486
args.phred,
9587
);
9688
}
9789

90+
fn assign_colors(args: &Cli) -> Vec<Color> {
91+
// check if there are equal number of arguments for the input and color parameters
92+
let default_colors = [Color::Red, Color::Blue, Color::Green];
93+
let colors = match &args.color {
94+
Some(c) => {
95+
if c.len() != args.input.len() {
96+
panic!(
97+
"\n\nERROR: number of input files ({}) and colors ({}) do not match!",
98+
args.input.len(),
99+
c.len()
100+
);
101+
}
102+
c
103+
}
104+
None => &default_colors.iter().cycle().take(args.input.len()).cloned().collect::<Vec<Color>>(),
105+
};
106+
colors.to_owned()
107+
}
108+
98109
fn max_of_hashmaps(hashmaps: &Vec<HashMap<(usize, usize), i32>>) -> f32 {
99110
let mut maxes = vec![];
100111
for h in hashmaps {
@@ -114,41 +125,62 @@ fn reads_to_intensity(
114125
hashmap: &HashMap<(usize, usize), i32>,
115126
color: Color,
116127
maxval: f32,
128+
background: BackGround,
117129
) -> HashMap<(usize, usize), Array1<u8>> {
118130
let color = match color {
119-
Color::Red => arr1(&[1, 0, 0]),
120-
Color::Green => arr1(&[0, 1, 0]),
121-
Color::Blue => arr1(&[0, 0, 1]),
122-
Color::Purple => arr1(&[1, 0, 1]),
123-
Color::Yellow => arr1(&[1, 1, 0]),
131+
Color::Red => match background {
132+
BackGround::White => arr1(&[255.0, 0.0, 0.0]),
133+
BackGround::Black => arr1(&[1.0, 0.0, 0.0]),
134+
},
135+
Color::Green => match background {
136+
BackGround::White => arr1(&[0.0, 255.0, 0.0]),
137+
BackGround::Black => arr1(&[0.0, 1.0, 0.0]),
138+
},
139+
Color::Blue => match background {
140+
BackGround::White => arr1(&[0.0, 0.0, 255.0]),
141+
BackGround::Black => arr1(&[0.0, 0.0, 1.0]),
142+
},
143+
Color::Purple => match background {
144+
BackGround::White => arr1(&[255.0, 0.0, 255.0]),
145+
BackGround::Black => arr1(&[1.0, 0.0, 1.0]),
146+
},
147+
Color::Yellow => match background {
148+
BackGround::White => arr1(&[255.0, 255.0, 0.0]),
149+
BackGround::Black => arr1(&[1.0, 1.0, 0.0]),
150+
},
124151
};
125152
let mut new_hashmap = HashMap::new();
126153
for ((length, accuracy), count) in hashmap {
127-
let intensity = (*count as f32 / maxval * 255.0) as u8;
154+
let intensity = *count as f32 / maxval * 255.0;
128155
let entry = new_hashmap
129156
.entry((*length, *accuracy))
130157
.or_insert(arr1(&[0, 0, 0]));
131-
*entry = color.clone() * intensity;
158+
if background == BackGround::White {
159+
*entry = (color.clone() * (intensity / 255.0)).mapv(|x| (x * 255.0) as u8);
160+
} else {
161+
*entry = (color.clone() * intensity).mapv(|x| x as u8);
162+
}
132163
}
133164
new_hashmap
134165
}
135166

136167
fn combine_hashmaps(
137168
hashmaps: &Vec<HashMap<(usize, usize), i32>>,
138169
colors: Vec<Color>,
170+
background: BackGround
139171
) -> Vec<HashMap<(usize, usize), Array1<u8>>> {
140172
let maxval = max_of_hashmaps(hashmaps);
141173
let mut new_hashmaps = vec![];
142174
for (hashmap, color) in hashmaps.iter().zip(colors) {
143-
new_hashmaps.push(reads_to_intensity(hashmap, color, maxval));
175+
new_hashmaps.push(reads_to_intensity(hashmap, color, maxval, background));
144176
}
145177
new_hashmaps
146178
}
147179

148180
fn plot_heatmap(
149181
hashmaps: Vec<HashMap<(usize, usize), i32>>,
150182
background: BackGround,
151-
color: Vec<Color>,
183+
chosen_color: Vec<Color>,
152184
output: &str,
153185
transform_accuracy: fn(f32) -> usize,
154186
phred: bool,
@@ -160,31 +192,32 @@ fn plot_heatmap(
160192

161193
if hashmaps.len() == 1 {
162194
// Creating a plot with just a single dataset
195+
let hashmap = &hashmaps[0];
163196
info!(
164197
"Constructing figure with {} colored pixels",
165-
hashmaps[0].values().len()
198+
hashmap.values().len()
166199
);
167200
// All counts are scaled to the max value
168201
let max_value = hashmaps[0]
169202
.values()
170203
.max()
171204
.expect("ERROR could not get max value of histogram");
172205
// Iterate over the hashmap to fill in bins and color pixels accordingly
173-
for ((length, accuracy), count) in &hashmaps[0] {
206+
for ((length, accuracy), count) in hashmap {
174207
let intensity = (*count as f32 / *max_value as f32 * 255.0) as u8;
175-
let color = match color[0] {
176-
Color::Red => Rgb([intensity, 0, 0]),
177-
Color::Green => Rgb([0, intensity, 0]),
178-
Color::Blue => Rgb([0, 0, intensity]),
179-
Color::Purple => Rgb([intensity, 0, intensity]),
180-
Color::Yellow => Rgb([intensity, intensity, 0]),
208+
let color = match chosen_color[0] {
209+
Color::Red => if background == BackGround::White { Rgb([255, 255 - intensity, 255 - intensity]) } else { Rgb([intensity, 0, 0]) },
210+
Color::Green => if background == BackGround::White { Rgb([255 - intensity, 255, 255 - intensity]) } else { Rgb([0, intensity, 0]) },
211+
Color::Blue => if background == BackGround::White { Rgb([255 - intensity, 255 - intensity, 255]) } else { Rgb([0, 0, intensity]) },
212+
Color::Purple => if background == BackGround::White { Rgb([255, 255 - intensity, 255]) } else { Rgb([intensity, 0, intensity]) },
213+
Color::Yellow => if background == BackGround::White { Rgb([255, 255, 255 - intensity]) } else { Rgb([intensity, intensity, 0]) },
181214
};
182215
image.put_pixel(*length as u32, *accuracy as u32, color);
183216
}
184217
} else {
185218
// Creating a plot of multiple datasets
186219
let default = arr1(&[0, 0, 0]);
187-
let hashmaps = combine_hashmaps(&hashmaps, color);
220+
let hashmaps = combine_hashmaps(&hashmaps, chosen_color, background);
188221
// Iterate over the first hashmap, and call .get for the remaining hashmaps
189222
// If that bin is unused in one of the remaining hashmaps the default (0, 0, 0) is added
190223
for ((length, accuracy), arr) in &hashmaps[0] {
@@ -200,15 +233,7 @@ fn plot_heatmap(
200233
}
201234
}
202235
info!("Adding axis ticks");
203-
// Use white text on a black background and vice versa
204-
image = match background {
205-
BackGround::Black => {
206-
axis_ticks::add_ticks(image, transform_accuracy, phred, Rgb([255, 255, 255]))
207-
}
208-
BackGround::White => {
209-
axis_ticks::add_ticks(image, transform_accuracy, phred, Rgb([0, 0, 0]))
210-
}
211-
};
236+
image = axis_ticks::add_ticks(image, transform_accuracy, phred, background);
212237

213238
info!("Saving image");
214239
image.save(output).expect("Error while saving image");
@@ -237,7 +262,7 @@ fn test_single_file() {
237262
vec![hashmap],
238263
BackGround::Black,
239264
vec![Color::Purple],
240-
"accuracy_heatmap1.png",
265+
"accuracy_heatmap_percent_on_black.png",
241266
transform::transform_accuracy_percent,
242267
false,
243268
);
@@ -255,7 +280,7 @@ fn test_single_file_from_de() {
255280
vec![hashmap],
256281
BackGround::Black,
257282
vec![Color::Purple],
258-
"accuracy_heatmap4.png",
283+
"accuracy_heatmap_percent_on_black_from_de.png",
259284
transform::transform_accuracy_percent,
260285
false,
261286
);
@@ -272,7 +297,7 @@ fn test_single_file_black_phred() {
272297
vec![hashmap],
273298
BackGround::Black,
274299
vec![Color::Purple],
275-
"accuracy_heatmap3.png",
300+
"accuracy_heatmap_phred_on_black.png",
276301
transform::transform_accuracy_percent,
277302
true,
278303
);
@@ -289,7 +314,7 @@ fn test_single_file_phred() {
289314
vec![hashmap],
290315
BackGround::White,
291316
vec![Color::Red],
292-
"accuracy_heatmap2.png",
317+
"accuracy_heatmap_phred_on_white.png",
293318
transform::transform_accuracy_phred,
294319
true,
295320
);

0 commit comments

Comments
 (0)