1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::rulesets::structs::{
finger_action::FingerAction,
finger_state::FingerState,
hand::Hand,
strain_solver_hit_object::StrainSolverHitObject,
};
/// Data point that is represented by a group of hitobjects.
/// Used for calculating strain value at a given time for a given hand.
#[derive(Debug, Clone)]
pub struct StrainSolverData {
/// Chorded Hit Objects at the current start time
pub hit_objects: Vec<StrainSolverHitObject>,
/// Is determined by the following StrainSolverData object on the current hand.
/// It should be defined externally from this class.
pub next_strain_solver_data_on_current_hand: Option<Box<StrainSolverData>>,
/// When the current action/pattern starts
pub start_time: f32,
/// When the longest LN in self.hit_objects ends
pub end_time: f32,
/// Base strain value calculated by its action
pub action_strain_coefficient: f32,
/// Strain multiplier determined by pattern difficulty
pub pattern_strain_multiplier: f32,
/// Multiplier that gets added to any pattern that could be manipulated via rolls.
pub roll_manipulation_strain_multiplier: f32,
/// Multiplier that gets applied to any pattern that could be manipulated via long jacks.
pub jack_manipulation_strain_multiplier: f32,
/// Total strain value for this data point
pub total_strain_value: f32,
/// Hand that this data point represents
pub hand: Hand,
/// Finger Action that this data point represents
pub finger_action: FingerAction,
/// Duration of FingerAction in ms
pub finger_action_duration_ms: f32,
/// Pattern that this data point represents.
/// TODO: replace with enum
pub pattern: String,
/// Is an index value of this hand's finger state. (Determined by every finger's state)
pub finger_state: FingerState,
}
impl StrainSolverData {
/// Data used to represent a point in time and other variables that influence difficulty.
pub fn new(hit_object: StrainSolverHitObject, rate: f32) -> Self {
let start_time = hit_object.hit_object.start_time as f32 / rate;
let end_time = hit_object.hit_object.end_time as f32 / rate;
Self {
hit_objects: vec![hit_object],
next_strain_solver_data_on_current_hand: None,
start_time,
end_time,
action_strain_coefficient: 1.0,
pattern_strain_multiplier: 1.0,
roll_manipulation_strain_multiplier: 1.0,
jack_manipulation_strain_multiplier: 1.0,
total_strain_value: 0.0,
hand: Hand::Left, // Default value
finger_action: FingerAction::None,
finger_action_duration_ms: 0.0,
pattern: String::new(),
finger_state: FingerState::NONE,
}
}
/// Is determined by if this data point has more than one hit object (per hand)
pub fn hand_chord(&self) -> bool {
self.hit_objects.len() > 1
}
/// Calculate the strain value of this current point.
pub fn calculate_strain_value(&mut self) {
// Calculate the strain value of each individual object and add to total
for hit_object in &mut self.hit_objects {
hit_object.strain_value = self.action_strain_coefficient
* self.pattern_strain_multiplier
* self.roll_manipulation_strain_multiplier
* self.jack_manipulation_strain_multiplier
* hit_object.ln_strain_multiplier;
self.total_strain_value += hit_object.strain_value;
}
// Average the strain value between the objects
if !self.hit_objects.is_empty() {
self.total_strain_value /= self.hit_objects.len() as f32;
}
}
pub fn solve_finger_state(&mut self) {
for hit_object in &self.hit_objects {
self.finger_state |= hit_object.finger_state;
}
}
}