diff options
| author | Jacob Palecki <[email protected]> | 2020-07-27 19:31:09 -0700 |
|---|---|---|
| committer | Jacob Palecki <[email protected]> | 2020-07-27 19:31:09 -0700 |
| commit | 3226272a2d49bcbaec72ec735c22b4e0cc2a5c57 (patch) | |
| tree | 84610d7e2bb0d410c0ea9ec409db95314e283c64 | |
| parent | Merge pull request #5 from JacobPalecki/WrapperAndGrapher (diff) | |
| download | rawaccel-3226272a2d49bcbaec72ec735c22b4e0cc2a5c57.tar.xz rawaccel-3226272a2d49bcbaec72ec735c22b4e0cc2a5c57.zip | |
skeleton
| -rw-r--r-- | common/external/tagged-union-single.h | 202 | ||||
| -rw-r--r-- | common/rawaccel-userspace.hpp | 2 | ||||
| -rw-r--r-- | common/rawaccel.hpp | 192 | ||||
| -rw-r--r-- | wrapper/wrapper.cpp | 2 | ||||
| -rw-r--r-- | wrapper/wrapper.hpp | 2 |
5 files changed, 342 insertions, 58 deletions
diff --git a/common/external/tagged-union-single.h b/common/external/tagged-union-single.h new file mode 100644 index 0000000..bcfc1cf --- /dev/null +++ b/common/external/tagged-union-single.h @@ -0,0 +1,202 @@ +#pragma once + +using size_t = decltype(alignof(char)); + +namespace type_traits { + +template< class T > struct remove_cv { typedef T type; }; +template< class T > struct remove_cv<const T> { typedef T type; }; +template< class T > struct remove_cv<volatile T> { typedef T type; }; +template< class T > struct remove_cv<const volatile T> { typedef T type; }; +template< class T > using remove_cv_t = typename remove_cv<T>::type; + +template< class T > struct remove_reference { typedef T type; }; +template< class T > struct remove_reference<T&> { typedef T type; }; +template< class T > struct remove_reference<T&&> { typedef T type; }; +template< class T > using remove_reference_t = typename remove_reference<T>::type; + +template< class T > +struct remove_cvref { + using type = remove_cv_t<remove_reference_t<T>>; +}; +template< class T > using remove_cvref_t = typename remove_cvref<T>::type; + +namespace detail { + +template <class T> struct type_identity { using type = T; }; + +template <class T> +auto try_add_lvalue_reference(int)->type_identity<T&>; +template <class T> +auto try_add_lvalue_reference(...)->type_identity<T>; + +template <class T> +auto try_add_rvalue_reference(int)->type_identity<T&&>; +template <class T> +auto try_add_rvalue_reference(...)->type_identity<T>; + +} // type_traits::detail + +template <class T> struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {}; +template< class T > using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; + +template <class T> struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {}; +template< class T > using add_rvalue_reference_t = typename add_rvalue_reference<T>::type; + +template <typename T, typename U> inline constexpr bool is_same_v = false; +template <typename T> inline constexpr bool is_same_v<T, T> = true; +template <typename T> inline constexpr bool is_void_v = is_same_v<remove_cv_t<T>, void>; + +} // type_traits + +template<class T> type_traits::add_rvalue_reference_t<T> declval() noexcept; + +template <typename T> +inline constexpr T maxv(const T& a, const T& b) { + return (b < a) ? a : b; +} + +template <typename T> +inline constexpr T minv(const T& a, const T& b) { + return (a < b) ? a : b; +} + +template <typename T> +inline constexpr T clampv(const T& v, const T& lo, const T& hi) { + return minv(maxv(v, lo), hi); +} + +template <typename T> +inline constexpr const T& select_ref(bool pred, const T& t, const T& f) { + return pred ? t : f; +} + +template <typename First, typename... Rest> +inline constexpr size_t max_size_of = maxv(sizeof(First), max_size_of<Rest...>); + +template <typename T> +inline constexpr size_t max_size_of<T> = sizeof(T); + +template <typename First, typename... Rest> +inline constexpr size_t max_align_of = maxv(alignof(First), max_align_of<Rest...>); + +template <typename T> +inline constexpr size_t max_align_of<T> = alignof(T); + +namespace detail { + +template <typename... Ts> +struct b1_index_of_impl { + + template <typename...> + struct idx { + static constexpr size_t value = 0; + }; + + template <typename T, typename First, typename... Rest> + struct idx <T, First, Rest...> { + static constexpr size_t value = []() { + if constexpr (type_traits::is_same_v<T, First>) { + return sizeof...(Ts) - sizeof...(Rest); + } + return idx<T, Rest...>::value; + }(); + }; +}; + +} // detail + +template <typename T, typename First, typename... Rest> +inline constexpr int base1_index_of = +detail::b1_index_of_impl<First, Rest...>::template idx<T, First, Rest...>::value; + +/* +Requirements: Every type is trivially-copyable and is not an array type + +Can be initialized to an empty state as if by using +std::variant<std::monostate, First, Rest...> +*/ +template <typename First, typename... Rest> +struct tagged_union { + + // Requirements: The return type of Visitor is default-constructible (or void) + // Returns a value-initialized object when in an empty or invalid state + template<typename Visitor> + inline constexpr auto visit(Visitor vis) { + return visit_impl<Visitor, First, Rest...>(vis); + } + + template<typename Visitor> + inline constexpr auto visit(Visitor vis) const { + return visit_impl<Visitor, First, Rest...>(vis); + } + + template<typename T> + static constexpr int id = base1_index_of<T, First, Rest...>; + + int tag = 0; + + struct storage_t { + alignas(max_align_of<First, Rest...>) char bytes[max_size_of<First, Rest...>]; + + template <typename T> + inline constexpr T& as() { + static_assert(id<T> != 0, "tagged_union can not hold T"); + return reinterpret_cast<T&>(bytes); + } + + template <typename T> + inline constexpr const T& as() const { + static_assert(id<T> != 0, "tagged_union can not hold T"); + return reinterpret_cast<const T&>(bytes); + } + + } storage; + + constexpr tagged_union() noexcept = default; + + template<typename T> + inline constexpr tagged_union(const T& val) noexcept { + tag = id<T>; + storage.template as<T>() = val; + } + + template<typename T> + inline constexpr tagged_union& operator=(const T& val) noexcept { + tag = id<T>; + storage.template as<T>() = val; + return *this; + } + +private: + template<typename Visitor, typename T1, typename... TRest> + inline constexpr auto visit_impl(Visitor vis) const { + if (tag == id<T1>) { + return vis(storage.template as<T1>()); + } + if constexpr (sizeof...(TRest) > 0) { + return visit_impl<Visitor, TRest...>(vis); + } + else { + using ReturnType = decltype(vis(declval<First&>())); + if constexpr (!type_traits::is_void_v<ReturnType>) return ReturnType{}; + } + } + + template<typename Visitor, typename T1, typename... TRest> + inline constexpr auto visit_impl(Visitor vis) { + if (tag == id<T1>) { + return vis(storage.template as<T1>()); + } + if constexpr (sizeof...(TRest) > 0) { + return visit_impl<Visitor, TRest...>(vis); + } + else { + using ReturnType = decltype(vis(declval<First&>())); + if constexpr (!type_traits::is_void_v<ReturnType>) return ReturnType{}; + } + } +}; + +template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<class... Ts> overloaded(Ts...)->overloaded<Ts...>; diff --git a/common/rawaccel-userspace.hpp b/common/rawaccel-userspace.hpp index db50b35..bca6997 100644 --- a/common/rawaccel-userspace.hpp +++ b/common/rawaccel-userspace.hpp @@ -19,7 +19,7 @@ void error(const char* s) { variables parse(int argc, char** argv) { double degrees = 0; vec2d sens = { 1, 1 }; - accel_function::args_t accel_args{}; + args_t accel_args{}; auto make_opt_vec = [](vec2d& v, auto first_flag, auto... rest) { return clipp::option(first_flag, rest...) & ( diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index 095af76..f7f9df7 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -5,6 +5,7 @@ #include "vec2.h" #include "x64-util.hpp" +#include "external/tagged-union-single.h" namespace rawaccel { @@ -55,8 +56,119 @@ struct accel_scale_clamp { void error(const char*); +using milliseconds = double; + +struct args_t { + mode accel_mode = mode::noaccel; + milliseconds time_min = 0.4; + double offset = 0; + double accel = 0; + double lim_exp = 2; + double midpoint = 0; + vec2d weight = { 1, 1 }; + vec2d cap = { 0, 0 }; +}; + +struct accel_implentation { + double b = 0; + double k = 0; + double m = 0; + double offset = 0; + + accel_implentation(args_t args) + { + b = args.accel; + k = args.lim_exp; + m = args.midpoint; + offset = args.offset; + } + + double virtual accelerate(double speed) { return 0; } + void virtual verify(args_t args) { + if (args.accel < 0) error("accel can not be negative, use a negative weight to compensate"); + if (args.time_min <= 0) error("min time must be positive"); + } + + accel_implentation() = default; +}; + +struct accel_linear : accel_implentation { + accel_linear(args_t args) + : accel_implentation(args) {} + + double virtual accelerate(double speed) override { + return b * speed; + } + + void virtual verify(args_t args) override { + accel_implentation::verify(args); + if (args.lim_exp <= 1) error("limit must be greater than 1"); + } +}; + +struct accel_classic : accel_implentation { + accel_classic(args_t args) + : accel_implentation(args) {} + + double virtual accelerate(double speed) override { + return pow(b * speed, k); + + } + + void virtual verify(args_t args) override { + accel_implentation::verify(args); + if (args.lim_exp <= 1) error("exponent must be greater than 1"); + } +}; + +struct accel_logarithmic : accel_implentation { + accel_logarithmic(args_t args) + : accel_implentation(args) {} + + double virtual accelerate(double speed) override { + return log(speed * b + 1); + } + + void virtual verify(args_t args) override { + accel_implentation::verify(args); + if (args.lim_exp <= 1) error("exponent must be greater than 1"); + } +}; + +struct accel_sigmoid : accel_implentation { + accel_sigmoid(args_t args) + : accel_implentation(args) {} + + double virtual accelerate(double speed) override { + return k / (exp(-b * (speed - m)) + 1); + + } + + void virtual verify(args_t args) override { + accel_implentation::verify(args); + if (args.lim_exp <= 1) error("exponent must be greater than 1"); + } +}; + +struct accel_power : accel_implentation { + accel_power(args_t args) + : accel_implentation(args) {} + + double virtual accelerate(double speed) override { + return (offset > 0 && speed < 1) ? 0 : pow(speed*b, k) - 1; + + + } + + void virtual verify(args_t args) override { + accel_implentation::verify(args); + if (args.lim_exp <= 1) error("exponent must be greater than 1"); + } +}; + +using accel_implementation_t = tagged_union<accel_implentation, accel_linear, accel_classic, accel_logarithmic, accel_sigmoid, accel_power>; + struct accel_function { - using milliseconds = double; /* This value is ideally a few microseconds lower than @@ -77,63 +189,13 @@ struct accel_function { // or the exponent for classic and power modes double k = 1; + accel_implementation_t accel = accel_implentation{}; + vec2d weight = { 1, 1 }; vec2<accel_scale_clamp> clamp; - inline vec2d operator()(const vec2d& input, milliseconds time, mode accel_mode) const { - double mag = sqrtsd(input.x * input.x + input.y * input.y); - double time_clamped = clampsd(time, time_min, 100); - double speed = maxsd(mag / time_clamped - speed_offset, 0); - - double accel_val = 0; - - switch (accel_mode) { - case mode::linear: accel_val = b * speed; - break; - case mode::classic: accel_val = pow(b * speed, k); - break; - case mode::natural: accel_val = k - (k * exp(-b * speed)); - break; - case mode::logarithmic: accel_val = log(speed * b + 1); - break; - case mode::sigmoid: accel_val = k / (exp(-b * (speed - m)) + 1); - break; - case mode::power: accel_val = (speed_offset > 0 && speed < 1) ? 0 : pow(speed*b, k) - 1; - break; - default: - break; - } - - double scale_x = weight.x * accel_val + 1; - double scale_y = weight.y * accel_val + 1; - - return { - input.x * clamp.x(scale_x), - input.y * clamp.y(scale_y) - }; - } - - struct args_t { - mode accel_mode = mode::noaccel; - milliseconds time_min = 0.4; - double offset = 0; - double accel = 0; - double lim_exp = 2; - double midpoint = 0; - vec2d weight = { 1, 1 }; - vec2d cap = { 0, 0 }; - }; - accel_function(args_t args) { - // Preconditions to guard against division by zero and - // ensure the C math functions can not return NaN or -Inf. - if (args.accel < 0) error("accel can not be negative, use a negative weight to compensate"); - if (args.time_min <= 0) error("min time must be positive"); - if (args.lim_exp <= 1) { - if (args.accel_mode == mode::classic) error("exponent must be greater than 1"); - if (args.accel_mode == mode::power) { if (args.lim_exp <=0 ) error("exponent must be greater than 0"); } - else error("limit must be greater than 1"); - } + accel = accel_linear(args); time_min = args.time_min; m = args.midpoint; @@ -148,6 +210,26 @@ struct accel_function { clamp.y = accel_scale_clamp(args.cap.y); } + double apply(double speed) const { + return accel.visit([=](auto accel_t) { return accel_t.accelerate(speed); }); + } + + inline vec2d operator()(const vec2d& input, milliseconds time, mode accel_mode) const { + double mag = sqrtsd(input.x * input.x + input.y * input.y); + double time_clamped = clampsd(time, time_min, 100); + double speed = maxsd(mag / time_clamped - speed_offset, 0); + + double accel_val = apply(speed); + + double scale_x = weight.x * accel_val + 1; + double scale_y = weight.y * accel_val + 1; + + return { + input.x * clamp.x(scale_x), + input.y * clamp.y(scale_y) + }; + } + accel_function() = default; }; @@ -159,7 +241,7 @@ struct variables { accel_function accel_fn; vec2d sensitivity = { 1, 1 }; - variables(double degrees, vec2d sens, accel_function::args_t accel_args) + variables(double degrees, vec2d sens, args_t accel_args) : accel_fn(accel_args) { apply_rotate = degrees != 0; diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index df3f796..f3d069a 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -8,7 +8,7 @@ using namespace System; Tuple<double, double>^ ManagedAccel::Accelerate(int x, int y, double time, double mode) { vec2d input_vec2d = {x, y}; - vec2d output = (*accel_instance)(input_vec2d, (accel_function::milliseconds)time, (rawaccel::mode)mode); + vec2d output = (*accel_instance)(input_vec2d, (milliseconds)time, (rawaccel::mode)mode); return gcnew Tuple<double, double>(output.x, output.y); }
\ No newline at end of file diff --git a/wrapper/wrapper.hpp b/wrapper/wrapper.hpp index 24bcadb..a5699ac 100644 --- a/wrapper/wrapper.hpp +++ b/wrapper/wrapper.hpp @@ -18,7 +18,7 @@ public: ManagedAccel(double mode, double offset, double accel, double lim_exp, double midpoint) { - accel_function::args_t args{}; + args_t args{}; args.accel = accel; args.lim_exp = lim_exp; args.midpoint = midpoint; |