MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Tendency.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <glm/glm.hpp>
4
5namespace MayaFlux::Kinesis {
6
7/**
8 * @struct Tendency
9 * @brief Typed, composable, stateless callable from domain D to range R
10 * @tparam D Domain type (input)
11 * @tparam R Range type (output)
12 *
13 * Pure mathematical substrate expressing directional pull: a function
14 * that maps a point in the domain to a value in the range. No internal
15 * state, no history, no Vulkan awareness, no analog-era vocabulary.
16 * Parameters that evolve over time are Datum<T> instances captured
17 * in the lambda at construction.
18 *
19 * Composition is handled by free functions in this namespace.
20 */
21template <typename D, typename R>
22struct Tendency {
23 std::function<R(const D&)> fn;
24
25 /**
26 * @brief Evaluate the tendency at a point in the domain
27 * @param d Domain value
28 * @return Range value
29 */
30 R operator()(const D& d) const { return fn(d); }
31
32 /**
33 * @brief Named evaluation (identical to operator())
34 * @param d Domain value
35 * @return Range value
36 */
37 [[nodiscard]] R evaluate(const D& d) const { return fn(d); }
38};
39
40// =========================================================================
41// Type aliases
42// =========================================================================
43
50
51// =========================================================================
52// Composition
53// =========================================================================
54
55/**
56 * @brief Combine two tendencies with a binary operation on their outputs
57 * @tparam D Shared domain type
58 * @tparam R Shared range type
59 * @tparam BinaryOp Callable: (R, R) -> R
60 * @param a First tendency
61 * @param b Second tendency
62 * @param op Binary combiner
63 * @return Combined tendency
64 */
65template <typename D, typename R, typename BinaryOp>
67{
68 return { .fn = [a, b, op](const D& d) -> R {
69 return op(a(d), b(d));
70 } };
71}
72
73/**
74 * @brief Sequential composition: evaluate first, feed result into second
75 * @tparam A Input domain
76 * @tparam B Intermediate type
77 * @tparam C Output range
78 * @param first A -> B tendency
79 * @param second B -> C tendency
80 * @return Composed A -> C tendency
81 */
82template <typename A, typename B, typename C>
84{
85 return { .fn = [first, second](const A& a) -> C {
86 return second(first(a));
87 } };
88}
89
90/**
91 * @brief Uniform scaling of a scalar-output tendency
92 * @tparam D Domain type
93 * @param t Source tendency
94 * @param factor Scale multiplier
95 * @return Scaled tendency
96 */
97template <typename D>
99{
100 return { .fn = [t, factor](const D& d) -> float {
101 return t(d) * factor;
102 } };
103}
104
105/**
106 * @brief Uniform scaling of a vector field
107 * @param t Source tendency
108 * @param factor Scale multiplier
109 * @return Scaled tendency
110 */
111inline VectorField scale(const VectorField& t, float factor)
112{
113 return { .fn = [t, factor](const glm::vec3& d) -> glm::vec3 {
114 return t(d) * factor;
115 } };
116}
117
118/**
119 * @brief Clamp scalar output to [lo, hi]
120 * @tparam D Domain type
121 * @param t Source tendency
122 * @param lo Lower bound
123 * @param hi Upper bound
124 * @return Clamped tendency
125 */
126template <typename D>
127Tendency<D, float> clamp(const Tendency<D, float>& t, float lo, float hi)
128{
129 return { .fn = [t, lo, hi](const D& d) -> float {
130 return std::clamp(t(d), lo, hi);
131 } };
132}
133
134/**
135 * @brief Zero output below threshold, pass through above
136 * @tparam D Domain type
137 * @param t Source tendency
138 * @param thresh Threshold value
139 * @return Thresholded tendency
140 */
141template <typename D>
143{
144 return { .fn = [t, thresh](const D& d) -> float {
145 float v = t(d);
146 return v < thresh ? 0.0F : v;
147 } };
148}
149
150/**
151 * @brief Negate a scalar-output tendency
152 * @tparam D Domain type
153 * @param t Source tendency
154 * @return Negated tendency
155 */
156template <typename D>
158{
159 return { .fn = [t](const D& d) -> float {
160 return -t(d);
161 } };
162}
163
164/**
165 * @brief Negate all components of a vector field
166 * @param t Source tendency
167 * @return Negated tendency
168 */
170{
171 return { .fn = [t](const glm::vec3& d) -> glm::vec3 {
172 return -t(d);
173 } };
174}
175
176/**
177 * @brief Interpolate between two tendencies controlled by a weight tendency
178 * @tparam D Shared domain type
179 * @param a First tendency (weight 0.0)
180 * @param b Second tendency (weight 1.0)
181 * @param weight D -> float tendency producing interpolation factor
182 * @return Blended tendency
183 */
184template <typename D>
186{
187 return { .fn = [a, b, weight](const D& d) -> float {
188 float w = weight(d);
189 return a(d) * (1.0F - w) + b(d) * w;
190 } };
191}
192
193/**
194 * @brief Select between two tendencies based on a predicate tendency
195 * @tparam D Shared domain type
196 * @tparam R Shared range type
197 * @param predicate D -> float tendency (positive selects then_t, non-positive selects else_t)
198 * @param then_t Tendency selected when predicate > 0
199 * @param else_t Tendency selected when predicate <= 0
200 * @return Conditional tendency
201 */
202template <typename D, typename R>
203Tendency<D, R> select(const Tendency<D, float>& predicate, const Tendency<D, R>& then_t, const Tendency<D, R>& else_t)
204{
205 return { .fn = [predicate, then_t, else_t](const D& d) -> R {
206 return predicate(d) > 0.0F ? then_t(d) : else_t(d);
207 } };
208}
209
210/**
211 * @brief Sample a tendency at evenly spaced points into an array
212 * @param f Scalar tendency to sample
213 * @param count Number of points; zero gives an empty array, one gives f(from)
214 * @param from First point of the domain
215 * @param to Last point of the domain
216 * @return f at count points from `from` to `to` inclusive
217 *
218 * The bridge from a curve designed with chain, combine, lerp and the scalar
219 * factories to an array: table_lookup reads such a table over [-1, 1], and
220 * TimeMaps::piecewise_linear over time.
221 */
222[[nodiscard]] inline std::vector<double> tabulate(
223 const Tendency<double, double>& f, size_t count, double from = -1.0, double to = 1.0)
224{
225 std::vector<double> table(count);
226 for (size_t i = 0; i < count; ++i) {
227 const double t = count > 1
228 ? from + (to - from) * static_cast<double>(i) / static_cast<double>(count - 1)
229 : from;
230 table[i] = f(t);
231 }
232 return table;
233}
234
235} // namespace MayaFlux::Kinesis
BufferProcessingChain * chain
size_t a
double weight
size_t b
size_t count
float threshold
Tendency< D, float > invert(const Tendency< D, float > &t)
Negate a scalar-output tendency.
Definition Tendency.hpp:157
Tendency< D, R > select(const Tendency< D, float > &predicate, const Tendency< D, R > &then_t, const Tendency< D, R > &else_t)
Select between two tendencies based on a predicate tendency.
Definition Tendency.hpp:203
Tendency< D, float > scale(const Tendency< D, float > &t, float factor)
Uniform scaling of a scalar-output tendency.
Definition Tendency.hpp:98
Tendency< D, R > combine(const Tendency< D, R > &a, const Tendency< D, R > &b, BinaryOp op)
Combine two tendencies with a binary operation on their outputs.
Definition Tendency.hpp:66
Tendency< D, float > lerp(const Tendency< D, float > &a, const Tendency< D, float > &b, const Tendency< D, float > &weight)
Interpolate between two tendencies controlled by a weight tendency.
Definition Tendency.hpp:185
std::vector< double > tabulate(const Tendency< double, double > &f, size_t count, double from=-1.0, double to=1.0)
Sample a tendency at evenly spaced points into an array.
Definition Tendency.hpp:222
std::function< R(const D &)> fn
Definition Tendency.hpp:23
R operator()(const D &d) const
Evaluate the tendency at a point in the domain.
Definition Tendency.hpp:30
R evaluate(const D &d) const
Named evaluation (identical to operator())
Definition Tendency.hpp:37
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22