1use crate::types::*;
4
5impl Signal {
6 pub fn new(signal_type: SignalType, intensity: f64, position: Position, emitter: AgentId, tick: Tick) -> Self {
8 Self {
9 signal_type,
10 intensity,
11 position,
12 emitter,
13 tick,
14 }
15 }
16
17 pub fn decay(&mut self, rate: f64) {
19 self.intensity *= 1.0 - rate;
20 }
21
22 pub fn is_below_threshold(&self, threshold: f64) -> bool {
24 self.intensity < threshold
25 }
26}
27
28impl Gradient {
29 pub fn new(signal_type: SignalType, direction: Position, magnitude: f64) -> Self {
31 Self {
32 signal_type,
33 direction,
34 magnitude,
35 }
36 }
37}
38
39pub fn compute_gradient(signals: &[&Signal], from: &Position) -> Option<Gradient> {
45 if signals.is_empty() {
46 return None;
47 }
48
49 let signal_type = signals[0].signal_type.clone();
50 let mut weighted_x = 0.0;
51 let mut weighted_y = 0.0;
52 let mut total_intensity = 0.0;
53
54 for signal in signals {
55 let dx = signal.position.x - from.x;
56 let dy = signal.position.y - from.y;
57 let dist = (dx * dx + dy * dy).sqrt().max(0.001); let weight = signal.intensity / dist;
61 weighted_x += dx * weight;
62 weighted_y += dy * weight;
63 total_intensity += signal.intensity;
64 }
65
66 if total_intensity < f64::EPSILON {
67 return None;
68 }
69
70 let magnitude = (weighted_x * weighted_x + weighted_y * weighted_y).sqrt();
71 if magnitude < f64::EPSILON {
72 return None;
73 }
74
75 Some(Gradient::new(
76 signal_type,
77 Position::new(weighted_x / magnitude, weighted_y / magnitude),
78 magnitude,
79 ))
80}