Loading...
Searching...
No Matches
Interpolator.hpp
1#pragma once
2#include <ossia/network/value/value.hpp>
3
4#include <halp/controls.hpp>
5#include <halp/dynamic_port.hpp>
6#include <halp/meta.hpp>
7
8#include <cmath>
9
10#include <algorithm>
11
12namespace avnd_tools
13{
14
16{
17 halp_meta(name, "Interpolator")
18 halp_meta(c_name, "avnd_interpolator")
19 halp_meta(author, "ossia team")
20 halp_meta(category, "Control/Data processing")
21 halp_meta(
22 description, "Linear interpolation of multiple values using weight coefficients")
23 halp_meta(uuid, "f47ac10b-58cc-4372-a567-0e02b2c3d479")
24
25 struct
26 {
27 halp::val_port<"Weights", std::vector<float>> weights;
28
29 struct : halp::spinbox_i32<"Input count", halp::range{0, 1024, 2}>
30 {
31 static std::function<void(Interpolator&, int)> on_controller_interaction()
32 {
33 return [](Interpolator& object, int value) {
34 object.inputs.values.request_port_resize(value);
35 };
36 }
37 } controller;
38
39 halp::dynamic_port<halp::val_port<"Value {}", ossia::value>> values;
40 } inputs;
41
42 struct
43 {
44 halp::val_port<"Output", ossia::value> output;
45 } outputs;
46
47 template<typename T>
48 T interpolate_scalar(const std::vector<T>& values, const std::vector<float>& weights) const
49 {
50 if (values.empty() || weights.empty())
51 return T{};
52
53 T result{};
54 float total_weight = 0.0f;
55
56 const int min_size = std::min(values.size(), weights.size());
57 for (int i = 0; i < min_size; ++i)
58 {
59 result += values[i] * weights[i];
60 total_weight += weights[i];
61 }
62
63 if (total_weight != 0.0f)
64 result /= total_weight;
65
66 return result;
67 }
68
69 template<typename VecType>
70 VecType interpolate_vector(const std::vector<VecType>& values, const std::vector<float>& weights) const
71 {
72 if (values.empty() || weights.empty())
73 return VecType{};
74
75 VecType result{};
76 float total_weight = 0.0f;
77
78 const int min_size = std::min(values.size(), weights.size());
79 for (int i = 0; i < min_size; ++i)
80 {
81 for (int j = 0; j < result.size(); ++j)
82 {
83 result[j] += values[i][j] * weights[i];
84 }
85 total_weight += weights[i];
86 }
87
88 if (total_weight != 0.0f)
89 {
90 for (auto& component : result)
91 component /= total_weight;
92 }
93
94 return result;
95 }
96
97 std::vector<ossia::value> interpolate_value_vector(
98 const std::vector<std::vector<ossia::value>>& value_vectors,
99 const std::vector<float>& weights) const
100 {
101 if (value_vectors.empty() || weights.empty())
102 return {};
103
104 const int min_inputs = std::min(value_vectors.size(), weights.size());
105 if (min_inputs == 0)
106 return {};
107
108 const int max_size = std::max_element(
109 value_vectors.begin(), value_vectors.begin() + min_inputs,
110 [](const auto& a, const auto& b) { return a.size() < b.size(); }
111 )->size();
112
113 std::vector<ossia::value> result;
114 result.reserve(max_size);
115
116 for (int elem_idx = 0; elem_idx < max_size; ++elem_idx)
117 {
118 std::vector<ossia::value> element_values;
119 std::vector<float> element_weights;
120
121 for (int input_idx = 0; input_idx < min_inputs; ++input_idx)
122 {
123 if (elem_idx < value_vectors[input_idx].size())
124 {
125 element_values.push_back(value_vectors[input_idx][elem_idx]);
126 element_weights.push_back(weights[input_idx]);
127 }
128 }
129
130 if (!element_values.empty())
131 {
132 result.push_back(interpolate_values(element_values, element_weights));
133 }
134 }
135
136 return result;
137 }
138
139 ossia::value interpolate_values(const std::vector<ossia::value>& values, const std::vector<float>& weights) const
140 {
141 if (values.empty() || weights.empty())
142 return ossia::value{};
143
144 const int min_size = std::min(values.size(), weights.size());
145 if (min_size == 0)
146 return ossia::value{};
147
148 if (min_size == 1)
149 return values[0];
150
151 auto first_type = values[0].get_type();
152 bool all_same_type = std::all_of(values.begin(), values.begin() + min_size,
153 [first_type](const ossia::value& v) { return v.get_type() == first_type; });
154
155 if (!all_same_type)
156 {
157 return values[0];
158 }
159
160 switch (first_type)
161 {
162 case ossia::val_type::FLOAT:
163 {
164 std::vector<float> float_values;
165 for (int i = 0; i < min_size; ++i)
166 float_values.push_back(*values[i].target<float>());
167 return interpolate_scalar(float_values, weights);
168 }
169 case ossia::val_type::INT:
170 {
171 std::vector<int> int_values;
172 for (int i = 0; i < min_size; ++i)
173 int_values.push_back(*values[i].target<int>());
174 float result = interpolate_scalar(
175 std::vector<float>(int_values.begin(), int_values.end()), weights);
176 return static_cast<int>(std::round(result));
177 }
178 case ossia::val_type::VEC2F:
179 {
180 std::vector<ossia::vec2f> vec_values;
181 for (int i = 0; i < min_size; ++i)
182 vec_values.push_back(*values[i].target<ossia::vec2f>());
183 return interpolate_vector(vec_values, weights);
184 }
185 case ossia::val_type::VEC3F:
186 {
187 std::vector<ossia::vec3f> vec_values;
188 for (int i = 0; i < min_size; ++i)
189 vec_values.push_back(*values[i].target<ossia::vec3f>());
190 return interpolate_vector(vec_values, weights);
191 }
192 case ossia::val_type::VEC4F:
193 {
194 std::vector<ossia::vec4f> vec_values;
195 for (int i = 0; i < min_size; ++i)
196 vec_values.push_back(*values[i].target<ossia::vec4f>());
197 return interpolate_vector(vec_values, weights);
198 }
199 case ossia::val_type::LIST:
200 {
201 std::vector<std::vector<ossia::value>> value_vectors;
202 for (int i = 0; i < min_size; ++i)
203 value_vectors.push_back(*values[i].target<std::vector<ossia::value>>());
204 return interpolate_value_vector(value_vectors, weights);
205 }
206 case ossia::val_type::BOOL:
207 {
208 std::vector<float> bool_values;
209 for (int i = 0; i < min_size; ++i)
210 bool_values.push_back(*values[i].target<bool>() ? 1.0f : 0.0f);
211 float result = interpolate_scalar(bool_values, weights);
212 return result >= 0.5f;
213 }
214 default:
215 return values[0];
216 }
217 }
218
219 void operator()()
220 {
221 const auto& weights = inputs.weights.value;
222
223 if (weights.empty() || inputs.values.ports.empty())
224 {
225 outputs.output.value = ossia::value{};
226 return;
227 }
228
229 std::vector<ossia::value> values;
230 for (const auto& port : inputs.values.ports)
231 {
232 values.push_back(port.value);
233 }
234
235 outputs.output.value = interpolate_values(values, weights);
236 }
237};
238
239}
Definition Interpolator.hpp:16