@@ -30,6 +30,7 @@ impl Display for CoordsType {
3030pub enum HeatmapType {
3131 VictimPosition ,
3232 KillerPosition ,
33+ Lines ,
3334}
3435
3536impl Default for HeatmapType {
@@ -43,6 +44,7 @@ impl Display for HeatmapType {
4344 match self {
4445 HeatmapType :: VictimPosition => write ! ( f, "Victim position" ) ,
4546 HeatmapType :: KillerPosition => write ! ( f, "Killer position" ) ,
47+ HeatmapType :: Lines => write ! ( f, "Killer -> victim lines" ) ,
4648 }
4749 }
4850}
@@ -104,11 +106,84 @@ impl HeatMapGenerator {
104106 deaths : impl IntoIterator < Item = & ' a Death > ,
105107 image : & mut ImageBuffer < Rgb < u8 > , Vec < u8 > > ,
106108 ) {
107- let nb_pixels = ( image. width ( ) * image. height ( ) ) as usize ;
108- let mut intensities = Vec :: with_capacity ( nb_pixels) ;
109- intensities. resize_with ( nb_pixels, || 0.0 ) ;
110- let mut max_intensity = f32:: NEG_INFINITY ;
111- let gradient = Gradient :: new ( vec ! [
109+ // lines
110+ if heatmap_type == HeatmapType :: Lines {
111+ let line_gradient = Gradient :: new ( vec ! [
112+ LinSrgba :: new( 0.0 , 0.6 , 1.0 , 1.0 ) ,
113+ LinSrgba :: new( 0.067 , 0.8 , 1.0 , 1.0 ) ,
114+ LinSrgba :: new( 0.33 , 1.0 , 0.33 , 1.0 ) ,
115+ LinSrgba :: new( 1.0 , 0.0 , 0.0 , 1.0 ) ,
116+ LinSrgba :: new( 1.0 , 0.8 , 0.0 , 1.0 ) ,
117+ // LinSrgba::new(1.0, 0.0, 0.0, 1.0),
118+ // LinSrgba::new(1.0, 1.0, 0.0, 1.0),
119+ // LinSrgba::new(0.0, 1.0, 0.0, 1.0),
120+ // LinSrgba::new(0.0, 1.0, 1.0, 1.0),
121+ // LinSrgba::new(0.0, 0.0, 1.0, 1.0),
122+ ] ) ;
123+ for death in deaths {
124+ if let ( Some ( killer_entity) , Some ( victim_entity) ) =
125+ ( & death. killer_entity_state , & death. victim_entity_state )
126+ {
127+ let killer_coords = self . game_coords_to_screen_coords (
128+ killer_entity. position . x ,
129+ killer_entity. position . y ,
130+ ) ;
131+ let victim_coords = self . game_coords_to_screen_coords (
132+ victim_entity. position . x ,
133+ victim_entity. position . y ,
134+ ) ;
135+ let points: Vec < ( ( i32 , i32 ) , f32 ) > =
136+ line_drawing:: XiaolinWu :: < f32 , i32 > :: new ( killer_coords, victim_coords)
137+ . collect ( ) ;
138+
139+ // this is needed because the line drawing algorithm doesn't always go in the start-end order, we need to check what order was used and invert the gradient as needed
140+ let ( first_point_x, first_point_y) = match points. get ( 0 ) {
141+ Some ( ( ( first_point_x, first_point_y) , _) ) => {
142+ ( * first_point_x as f32 , * first_point_y as f32 )
143+ }
144+ None => continue ,
145+ } ;
146+ let dist_killer_x = killer_coords. 0 - first_point_x;
147+ let dist_killer_y = killer_coords. 1 - first_point_y;
148+ let dist_victim_x = victim_coords. 0 - first_point_x;
149+ let dist_victim_y = victim_coords. 1 - first_point_y;
150+ let invert_gradient = dist_killer_x * dist_killer_x
151+ + dist_killer_y * dist_killer_y
152+ > dist_victim_x * dist_victim_x + dist_victim_y * dist_victim_y;
153+
154+ let len = points. len ( ) as f32 ;
155+ for ( index, ( ( x, y) , alpha) ) in points. iter ( ) . enumerate ( ) {
156+ let ( x, y) = ( * x, * y) ;
157+ if y < 0 || y >= image. height ( ) as i32 || x < 0 || x >= image. width ( ) as i32
158+ {
159+ continue ;
160+ }
161+ let color = if invert_gradient {
162+ line_gradient. get ( 1.0 - ( ( index + 1 ) as f32 / len) )
163+ } else {
164+ line_gradient. get ( ( index + 1 ) as f32 / len)
165+ } ;
166+ let pixel = image. get_pixel_mut ( x as u32 , y as u32 ) ;
167+ if let [ r, g, b] = pixel. channels ( ) {
168+ * pixel = Rgb :: from ( [
169+ ( ( alpha * color. red + ( 1.0 - alpha) * ( * r as f32 / 255.0 ) ) * 255.0 )
170+ as u8 ,
171+ ( ( alpha * color. green + ( 1.0 - alpha) * ( * g as f32 / 255.0 ) )
172+ * 255.0 ) as u8 ,
173+ ( ( alpha * color. blue + ( 1.0 - alpha) * ( * b as f32 / 255.0 ) ) * 255.0 )
174+ as u8 ,
175+ ] ) ;
176+ } else {
177+ unreachable ! ( ) ;
178+ }
179+ }
180+ }
181+ }
182+ return ;
183+ }
184+
185+ // heatmap
186+ let heatmap_gradient = Gradient :: new ( vec ! [
112187 // LinSrgba::new(0.0, 0.0, 0.0, 0.0),
113188 LinSrgba :: new( 0.0 , 0.0 , 1.0 , 0.0 ) ,
114189 LinSrgba :: new( 0.0 , 1.0 , 1.0 , 0.25 ) ,
@@ -117,10 +192,15 @@ impl HeatMapGenerator {
117192 LinSrgba :: new( 1.0 , 0.0 , 0.0 , 1.0 ) ,
118193 // LinSrgba::new(1.0, 1.0, 1.0, 1.0),
119194 ] ) ;
195+ let nb_pixels = ( image. width ( ) * image. height ( ) ) as usize ;
196+ let mut intensities = Vec :: with_capacity ( nb_pixels) ;
197+ intensities. resize_with ( nb_pixels, || 0.0 ) ;
198+ let mut max_intensity = f32:: NEG_INFINITY ;
120199 for death in deaths {
121200 let entity_state = match heatmap_type {
122201 HeatmapType :: VictimPosition => & death. victim_entity_state ,
123202 HeatmapType :: KillerPosition => & death. killer_entity_state ,
203+ HeatmapType :: Lines => unreachable ! ( ) ,
124204 } ;
125205 if let Some ( entity_state) = entity_state {
126206 let game_coords = entity_state. position ;
@@ -152,7 +232,7 @@ impl HeatMapGenerator {
152232 }
153233 for ( pixel, base_intensity) in image. pixels_mut ( ) . zip ( intensities) {
154234 let intensity = base_intensity / max_intensity;
155- let heat_color = gradient . get ( intensity) ;
235+ let heat_color = heatmap_gradient . get ( intensity) ;
156236 if let [ r, g, b] = pixel. channels ( ) {
157237 * pixel = Rgb :: from ( [
158238 ( ( heat_color. alpha * heat_color. red
0 commit comments