OSSIA
Open Scenario System for Interactive Application
gradient.hpp
1 #pragma once
2 #include <ossia/dataflow/graph_node.hpp>
3 #include <ossia/dataflow/node_process.hpp>
4 #include <ossia/dataflow/port.hpp>
5 #include <ossia/detail/flat_map.hpp>
6 #include <ossia/detail/math.hpp>
8 #include <ossia/network/base/parameter.hpp>
9 #include <ossia/network/dataspace/color.hpp>
11 
12 namespace ossia::nodes
13 {
14 class gradient final : public ossia::graph_node
15 {
16 public:
17  using grad_type = ossia::flat_map<double, ossia::hunter_lab>;
18 
19  static auto clamp_color(ossia::argb col)
20  {
21  using namespace std;
22  for(size_t i = 0; i < col.dataspace_value.size(); i++)
23  col.dataspace_value[i] = ossia::clamp<float>(col.dataspace_value[i], 0.f, 1.f);
24 
25  return col;
26  }
27 
28  gradient()
29  {
30  ossia::outlet_ptr vp = new ossia::value_outlet;
31  vp->target<ossia::value_port>()->type = ossia::argb_u{};
32  m_outlets.push_back(std::move(vp));
33  }
34 
35  void set_gradient(grad_type t) { m_data = std::move(t); }
36 
37  const ossia::unit_t& get_unit() const noexcept
38  {
39  auto outlet = m_outlets.back();
40  if(auto unit = outlet->target<ossia::value_port>()->type.target<ossia::unit_t>())
41  {
42  return *unit;
43  }
44  else
45  {
46  static const ossia::unit_t default_unit = ossia::rgb_u{};
47  return default_unit;
48  }
49  }
50 
51  ossia::value get_color(ossia::argb c)
52  {
53  ossia::value res = clamp_color(c).dataspace_value;
54  return ossia::convert(res, ossia::argb_u{}, get_unit());
55  }
56 
57  void handle_before_first(const ossia::token_request& tk, int64_t tick_start, double position)
58  {
59  auto& out = *m_outlets[0]->target<ossia::value_port>();
60  auto beg = m_data.begin();
61 
62  if(beg->first >= position)
63  {
64  out.write_value(get_color(ossia::argb{beg->second}), tick_start);
65  }
66  else if(!mustTween)
67  {
68  out.write_value(get_color(ossia::argb{beg->second}), tick_start);
69  }
70  else
71  {
72  if(!tween)
73  {
74  auto addr = m_outlets[0]->address.target<ossia::net::parameter_base*>();
75  if(addr && *addr)
76  {
77  // TODO if the curve is in another unit, we have to convert it to the
78  // correct unit.
79  tween = ossia::argb{ossia::convert<ossia::vec4f>((*addr)->value())};
80  }
81  else
82  {
83  tween = ossia::argb{beg->second};
84  }
85  }
86  out.write_value(
87  ease_color(0., *tween, beg->first, beg->second, position), tick_start);
88  }
89  }
90 
91  ossia::time_value process_dur;
92  void run(const ossia::token_request& t, ossia::exec_state_facade e) noexcept override
93  {
94  if(this->process_dur.impl <= 0)
95  return;
96 
97  auto& out = *m_outlets[0]->target<ossia::value_port>();
98 
99  const auto [tick_start, d] = e.timings(t);
100  const double pos = t.position() * double(t.parent_duration.impl) / double(this->process_dur.impl);
101 
102  switch(m_data.size())
103  {
104  case 0:
105  out.write_value(get_color(ossia::argb{0., 0., 0., 0.}), tick_start);
106  return;
107  case 1:
108  handle_before_first(t, tick_start, pos);
109  return;
110  default: {
111  auto it_next = m_data.lower_bound(pos);
112  // Before start
113  if(it_next == m_data.begin())
114  {
115  handle_before_first(t, tick_start, pos);
116  }
117  // past end
118  else if(it_next == m_data.end())
119  {
120  out.write_value(get_color(ossia::argb{m_data.rbegin()->second}), tick_start);
121  }
122  else
123  {
124  auto it_prev = it_next;
125  --it_prev;
126 
127  out.write_value(
128  ease_color(
129  it_prev->first, it_prev->second, it_next->first, it_next->second,
130  pos),
131  tick_start);
132  }
133  }
134  }
135  }
136 
137  ossia::value ease_color(
138  double prev_pos, ossia::hunter_lab prev, double next_pos, ossia::hunter_lab next,
139  double pos)
140  {
141  // Interpolate in La*b* domain
142  const auto coeff = (pos - prev_pos) / (next_pos - prev_pos);
143 
144  ossia::hunter_lab res;
145  ossia::easing::ease e{};
146  res.dataspace_value = ossia::make_vec(
147  e(prev.dataspace_value[0], next.dataspace_value[0], coeff),
148  e(prev.dataspace_value[1], next.dataspace_value[1], coeff),
149  e(prev.dataspace_value[2], next.dataspace_value[2], coeff));
150 
151  return get_color(ossia::argb{res});
152  }
153 
154 public:
155  std::optional<ossia::argb> tween;
156 
157 private:
158  grad_type m_data;
159 
160 public:
161  bool mustTween{};
162 };
163 
164 class gradient_process final : public ossia::node_process
165 {
166 public:
167  using ossia::node_process::node_process;
168  void start() override {
169  static_cast<gradient*>(node.get())->tween = std::nullopt;
170  }
171 };
172 }
The parameter_base class.
Definition: ossia/network/base/parameter.hpp:48
The value class.
Definition: value.hpp:173
The time_value class.
Definition: ossia/editor/scenario/time_value.hpp:28
Definition: dataspace.hpp:24