ink_stroke_modeler_rs/params.rs
1/// all parameters for the modeler
2#[derive(Debug, Clone, PartialEq, PartialOrd, Copy)]
3pub struct ModelerParams {
4 /// these parameters are used to apply smoothing to the input to reduce
5 /// wobble in the prediction
6 ///
7 /// The length of the window over which the moving average of speed and position is calculated
8 ///
9 /// Check if this can't be done with the rust time types as this probably comes from a
10 /// conversion to float (DURATION)
11 ///
12 /// A good starting point is
13 /// <math>
14 /// <mrow>
15 /// <mn>2.5</mn>
16 /// <mi>/</mi>
17 /// <msub>
18 /// <mi>f</mi>
19 /// <mtext>input rate</mtext>
20 /// </msub>
21 /// </mrow>
22 /// </math>
23 /// Should be positive
24 pub wobble_smoother_timeout: f64,
25 /// The range of speed considered for wobble smoothing.
26 /// At [ModelerParams::wobble_smoother_speed_floor] the maximum
27 /// amount of smoothing is applied. At [ModelerParams::wobble_smoother_speed_ceiling],
28 /// no smoothing is applied
29 ///
30 /// Good starting points are 2 - 3 % of the expected speed of the inputs
31 /// Should be positive and the speed floor smaller than the ceiling
32 pub wobble_smoother_speed_floor: f64,
33 pub wobble_smoother_speed_ceiling: f64,
34 /// The mass of the "weight" being pulled along the path, multiplied by the spring constant.
35 ///
36 /// Should be positive
37 pub position_modeler_spring_mass_constant: f64,
38 /// The ratio of the pen's velocity that is subtracted from the pen's acceleration per unit time, to simulate drag.
39 ///
40 /// Should be positive
41 pub position_modeler_drag_constant: f64,
42 /// The minimum number of modeled inputs to output per unit time. If inputs are received at a lower rate,
43 /// they will be upsampled to produce output of atleast [ModelerParams::sampling_min_output_rate].
44 /// If inputs are received at a higher rate, the output rate will match the input rate.
45 ///
46 /// Should be positive
47 pub sampling_min_output_rate: f64,
48 /// This determines the stop condition for the end-of-stroke modeling
49 /// If the position is within this distance of the final raw input, or
50 /// if the last update iteration moved less than this distance,
51 /// it stops iterating.
52 ///
53 /// this should be a small distance, good heuristic is
54 /// 2-3 orders of magnitude smaller than the expected distance
55 /// between input points
56 ///
57 /// Should be positive
58 pub sampling_end_of_stroke_stopping_distance: f64,
59 /// The maximum number of iterations to perform at the end of the stroke,
60 /// if it does not stop due to the constraint of the `sampling_end_of_stroke_stopping_distance`
61 ///
62 /// Should be positive and is capped at 1000 (to limit the memory requirements)
63 pub sampling_end_of_stroke_max_iterations: usize,
64 /// Maximum number of outputs to generate per call to Update or Predict.
65 /// related to issues if input events are received with too long of a delay
66 /// See what's done in the rnote call and on this end to limit things like this
67 ///
68 /// Should be strictly positive
69 pub sampling_max_outputs_per_call: usize,
70 /// the maximum number of raw inputs to look at when
71 /// searching for the nearest states when interpolating
72 ///
73 /// Should be strictly positive
74 pub stylus_state_modeler_max_input_samples: usize,
75}
76
77impl ModelerParams {
78 /// [ModelerParams::wobble_smoother_timeout] : 0.04,\
79 /// [ModelerParams::wobble_smoother_speed_floor] : 1.31,\
80 /// [ModelerParams::wobble_smoother_speed_ceiling] : 1.44,\
81 /// [ModelerParams::position_modeler_spring_mass_constant] : 11.0 / 32400.0,\
82 /// [ModelerParams::position_modeler_drag_constant] : 72.0,\
83 /// [ModelerParams::sampling_min_output_rate] : 180.0,\
84 /// [ModelerParams::sampling_end_of_stroke_stopping_distance] : 0.001,\
85 /// [ModelerParams::sampling_end_of_stroke_max_iterations] : 20,\
86 /// [ModelerParams::sampling_max_outputs_per_call] : 20,\
87 /// [ModelerParams::stylus_state_modeler_max_input_samples] : 10,
88 pub fn suggested() -> Self {
89 Self {
90 wobble_smoother_timeout: 0.04,
91 wobble_smoother_speed_floor: 1.31,
92 wobble_smoother_speed_ceiling: 1.44,
93 position_modeler_spring_mass_constant: 11.0 / 32400.0,
94 position_modeler_drag_constant: 72.0,
95 sampling_min_output_rate: 180.0,
96 sampling_end_of_stroke_stopping_distance: 0.001,
97 sampling_end_of_stroke_max_iterations: 20,
98 sampling_max_outputs_per_call: 20,
99 stylus_state_modeler_max_input_samples: 10,
100 }
101 }
102
103 /// validate the parameters as being correct, returns a error string with
104 /// the reasons otherwise
105 pub fn validate(self) -> Result<Self, String> {
106 let parameter_tests = [
107 self.position_modeler_spring_mass_constant > 0.0,
108 self.position_modeler_drag_constant > 0.0,
109 self.sampling_min_output_rate > 0.0,
110 self.sampling_end_of_stroke_stopping_distance > 0.0,
111 self.sampling_end_of_stroke_max_iterations > 0,
112 self.sampling_end_of_stroke_max_iterations < 1000,
113 self.sampling_max_outputs_per_call > 0,
114 self.wobble_smoother_timeout > 0.0,
115 self.wobble_smoother_speed_floor > 0.0,
116 self.wobble_smoother_speed_ceiling > 0.0,
117 self.wobble_smoother_speed_floor < self.wobble_smoother_speed_ceiling,
118 ];
119
120 let errors = vec![
121 "`position_modeler_spring_mass_constant` is not positive; ",
122 "`position_modeler_drag_constant` is not positive; ",
123 "`sampling_min_output_rate` is not positive; ",
124 "`sampling_end_of_stroke_stopping_distance` is not positive; ",
125 "`sampling_end_of_stroke_max_iterations` is not positive; ",
126 "`sampling_end_of_stroke_max_iterations` is too large (>1000); ",
127 "`sampling_max_outputs_per_call` is not positive; ",
128 "`wobble_smoother_timeout` is not positive; ",
129 "`wobble_smoother_speed_floor` is not positive; ",
130 "`wobble_smoother_speed_ceiling` is not positive; ",
131 "`wobble_smoother_speed_floor` should be strictly smaller than `wobble_smoother_speed_ceiling`",
132 ];
133
134 let tests_passed = parameter_tests.iter().fold(true, |acc, x| acc & x);
135
136 if tests_passed {
137 Ok(self)
138 } else {
139 //Collect errors
140 let error_acc = parameter_tests.iter().zip(errors).filter(|x| !*(x.0)).fold(
141 String::from("the following errors occurred : "),
142 |acc, x| acc + x.1,
143 );
144
145 Err(error_acc)
146 }
147 }
148}
149
150#[cfg(test)]
151mod test_params {
152 // import parent
153 use super::super::*;
154 #[test]
155 fn validation_modeler_params() {
156 let s = (ModelerParams {
157 wobble_smoother_timeout: -1.0,
158 wobble_smoother_speed_floor: -1.0,
159 wobble_smoother_speed_ceiling: -1.0,
160 position_modeler_spring_mass_constant: -1.0,
161 position_modeler_drag_constant: -1.0,
162 sampling_min_output_rate: -1.0,
163 sampling_end_of_stroke_stopping_distance: -1.0,
164 sampling_end_of_stroke_max_iterations: 0,
165 sampling_max_outputs_per_call: 0,
166 stylus_state_modeler_max_input_samples: 0,
167 })
168 .validate();
169 match s {
170 Ok(_) => assert!(false),
171 Err(_) => assert!(true),
172 }
173 }
174}