diff options
Diffstat (limited to 'ink_stroke_modeler/params.h')
-rw-r--r-- | ink_stroke_modeler/params.h | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/ink_stroke_modeler/params.h b/ink_stroke_modeler/params.h new file mode 100644 index 0000000..102edba --- /dev/null +++ b/ink_stroke_modeler/params.h @@ -0,0 +1,224 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INK_STROKE_MODELER_PARAMS_H_ +#define INK_STROKE_MODELER_PARAMS_H_ + +#include "absl/status/status.h" +#include "absl/types/variant.h" +#include "ink_stroke_modeler/types.h" + +namespace ink { +namespace stroke_model { + +// These structs contain parameters for tuning the behavior of the stroke +// modeler. +// +// The stroke modeler is unit-agnostic, in both time and space. That is, the +// stroke modeler does not know or care whether the inputs and parameters are +// specified in feet and minutes, meters and seconds, or millimeters and years. +// As such, instead of referring to specific units, we refer to "unit distance" +// and "unit time". +// +// These parameters will need to be "tuned" to your use case. Because of this, +// and because of the modeler's unit-agnosticism, it's impossible to define +// "reasonable" default values for many of the parameters -- these parameters +// instead default to -1, which will cause the validation functions to return an +// error. +// +// Where possible, we've indicated what a good starting point for tuning might +// be, but you'll likely need to adjust these for best results. + +// These parameters are used for modeling the position of the pen. +struct PositionModelerParams { + // The mass of the "weight" being pulled along the path, multiplied by the + // spring constant. + float spring_mass_constant = 11.f / 32400; + + // The ratio of the pen's velocity that is subtracted from the pen's + // acceleration, to simulate drag. + float drag_constant = 72.f; +}; + +// These parameters are used for sampling. +struct SamplingParams { + // The minimum number of modeled inputs to output per unit time. If inputs are + // received at a lower rate, they will be upsampled to produce output of at + // least min_output_rate. If inputs are received at a higher rate, the + // output rate will match the input rate. + double min_output_rate = -1; + + // This determines stop condition for end-of-stroke modeling; if the position + // is within this distance of the final raw input, or if the last update + // iteration moved less than this distance, it stop iterating. + // + // This should be a small distance; a good starting point is 2-3 orders of + // magnitude smaller than the expected distance between input points. + float end_of_stroke_stopping_distance = -1; + + // The maximum number of iterations to perform at the end of the stroke, if it + // does not stop due to the constraints of end_of_stroke_stopping_distance. + int end_of_stroke_max_iterations = 20; +}; + +// These parameters are used modeling the state of the stylus once the position +// has been modeled. +struct StylusStateModelerParams { + // The maximum number of raw inputs to look at when finding the nearest states + // for interpolation. + int max_input_samples = 10; +}; + +// These parameters are used for applying smoothing to the input to reduce +// wobble in the prediction. +struct WobbleSmootherParams { + // The length of the window over which the moving average of speed and + // position are calculated. + // + // A good starting point is 2.5 divided by the expected number of inputs per + // unit time. + Duration timeout{-1}; + + // The range of speeds considered for wobble smoothing. At speed_floor, the + // maximum amount of smoothing is applied. At speed_ceiling, no smoothing is + // applied. + // + // Good starting points are 2% and 3% of the expected speed of the inputs. + float speed_floor = -1; + float speed_ceiling = -1; +}; + +// This struct indicates the "stroke end" prediction strategy should be used, +// which models a prediction as though the last seen input was the +// end-of-stroke. There aren't actually any tunable parameters for this; it uses +// the same PositionModelerParams and SamplingParams as the overall model. Note +// that this "prediction" doesn't actually predict substantially into the +// future, it only allows for very quickly "catching up" to the position of the +// raw input. +struct StrokeEndPredictorParams {}; + +// This struct indicates that the Kalman filter-based prediction strategy should +// be used, and provides the parameters for tuning it. +// +// Unlike the "stroke end" predictor, this strategy can predict an extension +// of the stroke beyond the last Input position, in addition to the "catch up" +// step. +struct KalmanPredictorParams { + // The variance of the noise inherent to the stroke itself. + double process_noise = -1; + + // The variance of the noise that rises from errors in measurement of the + // stroke. + double measurement_noise = -1; + + // The minimum number of inputs received before the Kalman predictor is + // considered stable enough to make a prediction. + int min_stable_iteration = 4; + + // The Kalman filter assumes that input is received in uniform time steps, but + // this is not always the case. We hold on to the most recent input timestamps + // for use in calculating the correction for this. This determines the maximum + // number of timestamps to save. + int max_time_samples = 20; + + // The minimum allowed velocity of the "catch up" portion of the prediction, + // which covers the distance between the last Result (the last corrected + // position) and the + // + // A good starting point is 3 orders of magnitude smaller than the expected + // speed of the inputs. + float min_catchup_velocity = -1; + + // These weights are applied to the acceleration (x²) and jerk (x³) terms of + // the cubic prediction polynomial. The closer they are to zero, the more + // linear the prediction will be. + float acceleration_weight = .5; + float jerk_weight = .1; + + // This value is a hint to the predictor, indicating the desired duration of + // of the portion of the prediction extending beyond the position of the last + // input. The actual duration of that portion of the prediction may be less + // than this, based on the predictor's confidence, but it will never be + // greater. + Duration prediction_interval{-1}; + + // The Kalman predictor uses several heuristics to evaluate confidence in the + // prediction. Each heuristic produces a confidence value between 0 and 1, and + // then we take their product as the total confidence. + // These parameters may be used to tune those heuristics. + struct ConfidenceParams { + // The first heuristic simply increases confidence as we receive more sample + // (i.e. input points). It evaluates to 0 at no samples, and 1 at + // desired_number_of_samples. + int desired_number_of_samples = 20; + + // The second heuristic is based on the distance between the last sample + // and the current estimate. If the distance is 0, it evaluates to 1, and if + // the distance is greater than or equal to max_estimation_distance, it + // evaluates to 0. + // + // A good starting point is 1.5 times measurement_noise. + float max_estimation_distance = -1; + + // The third heuristic is based on the speed of the prediction, which is + // approximated by measuring the from the start of the prediction to the + // projected endpoint (if it were extended for the full + // prediction_interval). It evaluates to 0 at min_travel_speed, and 1 + // at max_travel_speed. + // + // Good starting points are 5% and 25% of the expected speed of the inputs. + float min_travel_speed = -1; + float max_travel_speed = -1; + + // The fourth heuristic is based on the linearity of the prediction, which + // is approximated by comparing the endpoint of the prediction with the + // endpoint of a linear prediction (again, extended for the full + // prediction_interval). It evaluates to 1 at zero distance, and + // baseline_linearity_confidence at a distance of max_linear_deviation. + // + // A good starting point is an 10 times the measurement_noise. + float max_linear_deviation = -1; + float baseline_linearity_confidence = .4; + }; + ConfidenceParams confidence_params; +}; +using PredictionParams = + absl::variant<StrokeEndPredictorParams, KalmanPredictorParams>; + +// This convenience struct is a collection of the parameters for the individual +// parameter structs. +struct StrokeModelParams { + WobbleSmootherParams wobble_smoother_params; + PositionModelerParams position_modeler_params; + SamplingParams sampling_params; + StylusStateModelerParams stylus_state_modeler_params; + PredictionParams prediction_params = StrokeEndPredictorParams{}; +}; + +// These validation functions will return an error if the given parameters are +// invalid. +absl::Status ValidatePositionModelerParams(const PositionModelerParams& params); +absl::Status ValidateSamplingParams(const SamplingParams& params); +absl::Status ValidateStylusStateModelerParams( + const StylusStateModelerParams& params); +absl::Status ValidateWobbleSmootherParams(const WobbleSmootherParams& params); +absl::Status ValidatePredictionParams(const PredictionParams& params); +absl::Status ValidateStrokeModelParams(const StrokeModelParams& params); + +} // namespace stroke_model +} // namespace ink + +#endif // INK_STROKE_MODELER_PARAMS_H_ |