@@ -20,7 +20,7 @@ enum Color {
20
20
}
21
21
22
22
#[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , ValueEnum ) ]
23
- enum BackGround {
23
+ pub enum BackGround {
24
24
Black ,
25
25
White ,
26
26
}
@@ -42,8 +42,8 @@ struct Cli {
42
42
output : String ,
43
43
44
44
/// 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 > > ,
47
47
48
48
/// Color used for background
49
49
#[ arg( short, long, value_enum, value_parser, default_value_t = BackGround :: Black ) ]
@@ -61,14 +61,7 @@ struct Cli {
61
61
fn main ( ) {
62
62
env_logger:: init ( ) ;
63
63
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 \n ERROR: 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) ;
72
65
let transform_accuracy = if args. phred {
73
66
transform:: transform_accuracy_phred
74
67
} else {
@@ -78,7 +71,6 @@ fn main() {
78
71
for f in args. input {
79
72
utils:: is_file ( & f) . unwrap_or_else ( |_| panic ! ( "Input file {f} is invalid" , ) ) ;
80
73
let hashmap = extract_data:: bam_to_hashmap ( & f, args. threads , transform_accuracy) ;
81
- println ! ( "{:?}" , hashmap) ;
82
74
if args. normalize {
83
75
hashmaps. push ( extract_data:: log_transform_hashmap ( hashmap) ) ;
84
76
} else {
@@ -88,13 +80,32 @@ fn main() {
88
80
plot_heatmap (
89
81
hashmaps,
90
82
args. background ,
91
- args . color ,
83
+ colors ,
92
84
& args. output ,
93
85
transform_accuracy,
94
86
args. phred ,
95
87
) ;
96
88
}
97
89
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 \n ERROR: 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
+
98
109
fn max_of_hashmaps ( hashmaps : & Vec < HashMap < ( usize , usize ) , i32 > > ) -> f32 {
99
110
let mut maxes = vec ! [ ] ;
100
111
for h in hashmaps {
@@ -114,41 +125,62 @@ fn reads_to_intensity(
114
125
hashmap : & HashMap < ( usize , usize ) , i32 > ,
115
126
color : Color ,
116
127
maxval : f32 ,
128
+ background : BackGround ,
117
129
) -> HashMap < ( usize , usize ) , Array1 < u8 > > {
118
130
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
+ } ,
124
151
} ;
125
152
let mut new_hashmap = HashMap :: new ( ) ;
126
153
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 ;
128
155
let entry = new_hashmap
129
156
. entry ( ( * length, * accuracy) )
130
157
. 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
+ }
132
163
}
133
164
new_hashmap
134
165
}
135
166
136
167
fn combine_hashmaps (
137
168
hashmaps : & Vec < HashMap < ( usize , usize ) , i32 > > ,
138
169
colors : Vec < Color > ,
170
+ background : BackGround
139
171
) -> Vec < HashMap < ( usize , usize ) , Array1 < u8 > > > {
140
172
let maxval = max_of_hashmaps ( hashmaps) ;
141
173
let mut new_hashmaps = vec ! [ ] ;
142
174
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 ) ) ;
144
176
}
145
177
new_hashmaps
146
178
}
147
179
148
180
fn plot_heatmap (
149
181
hashmaps : Vec < HashMap < ( usize , usize ) , i32 > > ,
150
182
background : BackGround ,
151
- color : Vec < Color > ,
183
+ chosen_color : Vec < Color > ,
152
184
output : & str ,
153
185
transform_accuracy : fn ( f32 ) -> usize ,
154
186
phred : bool ,
@@ -160,31 +192,32 @@ fn plot_heatmap(
160
192
161
193
if hashmaps. len ( ) == 1 {
162
194
// Creating a plot with just a single dataset
195
+ let hashmap = & hashmaps[ 0 ] ;
163
196
info ! (
164
197
"Constructing figure with {} colored pixels" ,
165
- hashmaps [ 0 ] . values( ) . len( )
198
+ hashmap . values( ) . len( )
166
199
) ;
167
200
// All counts are scaled to the max value
168
201
let max_value = hashmaps[ 0 ]
169
202
. values ( )
170
203
. max ( )
171
204
. expect ( "ERROR could not get max value of histogram" ) ;
172
205
// 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 {
174
207
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 ] ) } ,
181
214
} ;
182
215
image. put_pixel ( * length as u32 , * accuracy as u32 , color) ;
183
216
}
184
217
} else {
185
218
// Creating a plot of multiple datasets
186
219
let default = arr1 ( & [ 0 , 0 , 0 ] ) ;
187
- let hashmaps = combine_hashmaps ( & hashmaps, color ) ;
220
+ let hashmaps = combine_hashmaps ( & hashmaps, chosen_color , background ) ;
188
221
// Iterate over the first hashmap, and call .get for the remaining hashmaps
189
222
// If that bin is unused in one of the remaining hashmaps the default (0, 0, 0) is added
190
223
for ( ( length, accuracy) , arr) in & hashmaps[ 0 ] {
@@ -200,15 +233,7 @@ fn plot_heatmap(
200
233
}
201
234
}
202
235
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) ;
212
237
213
238
info ! ( "Saving image" ) ;
214
239
image. save ( output) . expect ( "Error while saving image" ) ;
@@ -237,7 +262,7 @@ fn test_single_file() {
237
262
vec ! [ hashmap] ,
238
263
BackGround :: Black ,
239
264
vec ! [ Color :: Purple ] ,
240
- "accuracy_heatmap1 .png" ,
265
+ "accuracy_heatmap_percent_on_black .png" ,
241
266
transform:: transform_accuracy_percent,
242
267
false ,
243
268
) ;
@@ -255,7 +280,7 @@ fn test_single_file_from_de() {
255
280
vec ! [ hashmap] ,
256
281
BackGround :: Black ,
257
282
vec ! [ Color :: Purple ] ,
258
- "accuracy_heatmap4 .png" ,
283
+ "accuracy_heatmap_percent_on_black_from_de .png" ,
259
284
transform:: transform_accuracy_percent,
260
285
false ,
261
286
) ;
@@ -272,7 +297,7 @@ fn test_single_file_black_phred() {
272
297
vec ! [ hashmap] ,
273
298
BackGround :: Black ,
274
299
vec ! [ Color :: Purple ] ,
275
- "accuracy_heatmap3 .png" ,
300
+ "accuracy_heatmap_phred_on_black .png" ,
276
301
transform:: transform_accuracy_percent,
277
302
true ,
278
303
) ;
@@ -289,7 +314,7 @@ fn test_single_file_phred() {
289
314
vec ! [ hashmap] ,
290
315
BackGround :: White ,
291
316
vec ! [ Color :: Red ] ,
292
- "accuracy_heatmap2 .png" ,
317
+ "accuracy_heatmap_phred_on_white .png" ,
293
318
transform:: transform_accuracy_phred,
294
319
true ,
295
320
) ;
0 commit comments