Skip to content

Commit f95c9de

Browse files
committed
fmt config, early work on filters
1 parent 08ba703 commit f95c9de

9 files changed

Lines changed: 417 additions & 523 deletions

File tree

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ tokio = { version = "0.2", features = ["blocking"] }
2020
rayon = "1.4.0"
2121
nfd2 = "=0.2.1"
2222
line_drawing = "=0.8.0"
23+
enum_dispatch = "=0.3.3"
2324

2425
# [profile.release]
2526
# lto = true

rustfmt.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
max_width = 180

src/filters.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use crate::heatmap_analyser::{Class, Death, PlayerEntity, Team};
2+
use enum_dispatch::enum_dispatch;
3+
use tf_demo_parser::demo::vector::Vector;
4+
5+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6+
pub enum OrderedOperator {
7+
Equal,
8+
NotEqual,
9+
Greater,
10+
Smaller,
11+
GreaterOrEqual,
12+
SmallerOrEqual,
13+
}
14+
15+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16+
pub enum UnorderedOperator {
17+
Is,
18+
IsNot,
19+
}
20+
21+
#[enum_dispatch]
22+
pub enum Filter {
23+
KillerTeamFilter,
24+
VictimTeamFilter,
25+
KillerClassFilter,
26+
VictimClassFilter,
27+
KillerElevationFilter,
28+
VictimElevationFilter,
29+
Distance2DFilter,
30+
Distance3DFilter,
31+
}
32+
33+
#[enum_dispatch(Filter)]
34+
pub trait FilterTrait {
35+
fn apply(&self, death: &Death) -> bool;
36+
}
37+
38+
pub struct KillerTeamFilter {
39+
pub op: UnorderedOperator,
40+
pub team: Team,
41+
}
42+
43+
impl FilterTrait for KillerTeamFilter {
44+
fn apply(&self, death: &Death) -> bool {
45+
match (&death.killer_entity_state, self.op) {
46+
(Some(PlayerEntity { team, .. }), UnorderedOperator::Is) => *team == self.team,
47+
(Some(PlayerEntity { team, .. }), UnorderedOperator::IsNot) => *team != self.team,
48+
(None, _) => false,
49+
}
50+
}
51+
}
52+
53+
pub struct VictimTeamFilter {
54+
pub op: UnorderedOperator,
55+
pub team: Team,
56+
}
57+
58+
impl FilterTrait for VictimTeamFilter {
59+
fn apply(&self, death: &Death) -> bool {
60+
match (&death.victim_entity_state, self.op) {
61+
(Some(PlayerEntity { team, .. }), UnorderedOperator::Is) => *team == self.team,
62+
(Some(PlayerEntity { team, .. }), UnorderedOperator::IsNot) => *team != self.team,
63+
(None, _) => false,
64+
}
65+
}
66+
}
67+
68+
pub struct KillerClassFilter {
69+
pub op: UnorderedOperator,
70+
pub class: Class,
71+
}
72+
73+
impl FilterTrait for KillerClassFilter {
74+
fn apply(&self, death: &Death) -> bool {
75+
match (&death.killer_entity_state, self.op) {
76+
(Some(PlayerEntity { class, .. }), UnorderedOperator::Is) => *class == self.class,
77+
(Some(PlayerEntity { class, .. }), UnorderedOperator::IsNot) => *class != self.class,
78+
(None, _) => false,
79+
}
80+
}
81+
}
82+
83+
pub struct VictimClassFilter {
84+
pub op: UnorderedOperator,
85+
pub class: Class,
86+
}
87+
88+
impl FilterTrait for VictimClassFilter {
89+
fn apply(&self, death: &Death) -> bool {
90+
match (&death.victim_entity_state, self.op) {
91+
(Some(PlayerEntity { class, .. }), UnorderedOperator::Is) => *class == self.class,
92+
(Some(PlayerEntity { class, .. }), UnorderedOperator::IsNot) => *class != self.class,
93+
(None, _) => false,
94+
}
95+
}
96+
}
97+
98+
pub struct KillerElevationFilter {
99+
pub op: OrderedOperator,
100+
pub z: f32,
101+
}
102+
103+
impl FilterTrait for KillerElevationFilter {
104+
fn apply(&self, death: &Death) -> bool {
105+
match (&death.killer_entity_state, self.op) {
106+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Equal) => *z == self.z,
107+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::NotEqual) => *z != self.z,
108+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Greater) => *z > self.z,
109+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Smaller) => *z < self.z,
110+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::GreaterOrEqual) => *z >= self.z,
111+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::SmallerOrEqual) => *z <= self.z,
112+
(None, _) => false,
113+
}
114+
}
115+
}
116+
117+
pub struct VictimElevationFilter {
118+
pub op: OrderedOperator,
119+
pub z: f32,
120+
}
121+
122+
impl FilterTrait for VictimElevationFilter {
123+
fn apply(&self, death: &Death) -> bool {
124+
match (&death.victim_entity_state, self.op) {
125+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Equal) => *z == self.z,
126+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::NotEqual) => *z != self.z,
127+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Greater) => *z > self.z,
128+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::Smaller) => *z < self.z,
129+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::GreaterOrEqual) => *z >= self.z,
130+
(Some(PlayerEntity { position: Vector { z, .. }, .. }), OrderedOperator::SmallerOrEqual) => *z <= self.z,
131+
(None, _) => false,
132+
}
133+
}
134+
}
135+
136+
pub struct Distance2DFilter {
137+
pub op: OrderedOperator,
138+
pub distance: f32,
139+
}
140+
141+
impl FilterTrait for Distance2DFilter {
142+
fn apply(&self, death: &Death) -> bool {
143+
if let (Some(killer_entity), Some(victim_entity)) = (&death.killer_entity_state, &death.victim_entity_state) {
144+
let distance_x = killer_entity.position.x - victim_entity.position.x;
145+
let distance_y = killer_entity.position.y - victim_entity.position.y;
146+
let distance = (distance_x * distance_x + distance_y * distance_y).sqrt();
147+
match self.op {
148+
OrderedOperator::Equal => distance == self.distance,
149+
OrderedOperator::NotEqual => distance != self.distance,
150+
OrderedOperator::Greater => distance > self.distance,
151+
OrderedOperator::Smaller => distance < self.distance,
152+
OrderedOperator::GreaterOrEqual => distance >= self.distance,
153+
OrderedOperator::SmallerOrEqual => distance <= self.distance,
154+
}
155+
} else {
156+
false
157+
}
158+
}
159+
}
160+
161+
pub struct Distance3DFilter {
162+
pub op: OrderedOperator,
163+
pub distance: f32,
164+
}
165+
166+
impl FilterTrait for Distance3DFilter {
167+
fn apply(&self, death: &Death) -> bool {
168+
if let (Some(killer_entity), Some(victim_entity)) = (&death.killer_entity_state, &death.victim_entity_state) {
169+
let distance_x = killer_entity.position.x - victim_entity.position.x;
170+
let distance_y = killer_entity.position.y - victim_entity.position.y;
171+
let distance_z = killer_entity.position.z - victim_entity.position.z;
172+
let distance = (distance_x * distance_x + distance_y * distance_y + distance_z * distance_z).sqrt();
173+
match self.op {
174+
OrderedOperator::Equal => distance == self.distance,
175+
OrderedOperator::NotEqual => distance != self.distance,
176+
OrderedOperator::Greater => distance > self.distance,
177+
OrderedOperator::Smaller => distance < self.distance,
178+
OrderedOperator::GreaterOrEqual => distance >= self.distance,
179+
OrderedOperator::SmallerOrEqual => distance <= self.distance,
180+
}
181+
} else {
182+
false
183+
}
184+
}
185+
}

src/heatmap.rs

Lines changed: 24 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,7 @@ pub struct HeatMapGenerator {
6565
}
6666

6767
impl HeatMapGenerator {
68-
pub fn new(
69-
pos_x: f32,
70-
pos_y: f32,
71-
screen_width: u32,
72-
screen_height: u32,
73-
scale: f32,
74-
coords_type: CoordsType,
75-
) -> Self {
68+
pub fn new(pos_x: f32, pos_y: f32, screen_width: u32, screen_height: u32, scale: f32, coords_type: CoordsType) -> Self {
7669
let screen_width = screen_width as f32;
7770
let screen_height = screen_height as f32;
7871
let aspect_ratio = screen_width / screen_height;
@@ -100,62 +93,46 @@ impl HeatMapGenerator {
10093
}
10194
}
10295

103-
pub fn generate_heatmap<'a>(
104-
&self,
105-
heatmap_type: HeatmapType,
106-
deaths: impl IntoIterator<Item = &'a Death>,
107-
image: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
108-
) {
96+
pub fn generate_heatmap<'a>(&self, heatmap_type: HeatmapType, deaths: impl IntoIterator<Item = &'a Death>, image: &mut ImageBuffer<Rgb<u8>, Vec<u8>>) {
10997
// lines
11098
if heatmap_type == HeatmapType::Lines {
11199
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),
100+
LinSrgba::new(0.0, 0.0, 1.0, 1.0),
101+
LinSrgba::new(1.0, 1.0, 0.0, 1.0),
102+
103+
// LinSrgba::new(0.0, 0.6, 1.0, 1.0),
104+
// LinSrgba::new(0.067, 0.8, 1.0, 1.0),
105+
// LinSrgba::new(0.33, 1.0, 0.33, 1.0),
106+
// LinSrgba::new(1.0, 0.0, 0.0, 1.0),
107+
// LinSrgba::new(1.0, 0.8, 0.0, 1.0),
108+
117109
// LinSrgba::new(1.0, 0.0, 0.0, 1.0),
118110
// LinSrgba::new(1.0, 1.0, 0.0, 1.0),
119111
// LinSrgba::new(0.0, 1.0, 0.0, 1.0),
120112
// LinSrgba::new(0.0, 1.0, 1.0, 1.0),
121113
// LinSrgba::new(0.0, 0.0, 1.0, 1.0),
122114
]);
123115
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();
116+
if let (Some(killer_entity), Some(victim_entity)) = (&death.killer_entity_state, &death.victim_entity_state) {
117+
let killer_coords = self.game_coords_to_screen_coords(killer_entity.position.x, killer_entity.position.y);
118+
let victim_coords = self.game_coords_to_screen_coords(victim_entity.position.x, victim_entity.position.y);
119+
let points: Vec<((i32, i32), f32)> = line_drawing::XiaolinWu::<f32, i32>::new(killer_coords, victim_coords).collect();
138120

139121
// 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
140122
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-
}
123+
Some(((first_point_x, first_point_y), _)) => (*first_point_x as f32, *first_point_y as f32),
144124
None => continue,
145125
};
146126
let dist_killer_x = killer_coords.0 - first_point_x;
147127
let dist_killer_y = killer_coords.1 - first_point_y;
148128
let dist_victim_x = victim_coords.0 - first_point_x;
149129
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;
130+
let invert_gradient = dist_killer_x * dist_killer_x + dist_killer_y * dist_killer_y > dist_victim_x * dist_victim_x + dist_victim_y * dist_victim_y;
153131

154132
let len = points.len() as f32;
155133
for (index, ((x, y), alpha)) in points.iter().enumerate() {
156134
let (x, y) = (*x, *y);
157-
if y < 0 || y >= image.height() as i32 || x < 0 || x >= image.width() as i32
158-
{
135+
if y < 0 || y >= image.height() as i32 || x < 0 || x >= image.width() as i32 {
159136
continue;
160137
}
161138
let color = if invert_gradient {
@@ -166,12 +143,9 @@ impl HeatMapGenerator {
166143
let pixel = image.get_pixel_mut(x as u32, y as u32);
167144
if let [r, g, b] = pixel.channels() {
168145
*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,
146+
((alpha * color.red + (1.0 - alpha) * (*r as f32 / 255.0)) * 255.0) as u8,
147+
((alpha * color.green + (1.0 - alpha) * (*g as f32 / 255.0)) * 255.0) as u8,
148+
((alpha * color.blue + (1.0 - alpha) * (*b as f32 / 255.0)) * 255.0) as u8,
175149
]);
176150
} else {
177151
unreachable!();
@@ -235,15 +209,9 @@ impl HeatMapGenerator {
235209
let heat_color = heatmap_gradient.get(intensity);
236210
if let [r, g, b] = pixel.channels() {
237211
*pixel = Rgb::from([
238-
((heat_color.alpha * heat_color.red
239-
+ (1.0 - heat_color.alpha) * (*r as f32 / 255.0))
240-
* 255.0) as u8,
241-
((heat_color.alpha * heat_color.green
242-
+ (1.0 - heat_color.alpha) * (*g as f32 / 255.0))
243-
* 255.0) as u8,
244-
((heat_color.alpha * heat_color.blue
245-
+ (1.0 - heat_color.alpha) * (*b as f32 / 255.0))
246-
* 255.0) as u8,
212+
((heat_color.alpha * heat_color.red + (1.0 - heat_color.alpha) * (*r as f32 / 255.0)) * 255.0) as u8,
213+
((heat_color.alpha * heat_color.green + (1.0 - heat_color.alpha) * (*g as f32 / 255.0)) * 255.0) as u8,
214+
((heat_color.alpha * heat_color.blue + (1.0 - heat_color.alpha) * (*b as f32 / 255.0)) * 255.0) as u8,
247215
]);
248216
} else {
249217
unreachable!();

0 commit comments

Comments
 (0)