MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Layer.cpp
Go to the documentation of this file.
1#include "Layer.hpp"
2
4
5// =============================================================================
6// Element registration
7// =============================================================================
8
10{
11 element.id = m_next_id++;
12 m_index[element.id] = m_elements.size();
13 m_elements.push_back(std::move(element));
14 return Slot(*this, m_elements.back().id);
15}
16
17bool Layer::remove(uint32_t id)
18{
19 if (!get(id))
20 return false;
21
22 const std::vector<uint32_t> ids = closure(id);
23
24 for (uint32_t cid : ids) {
25 if (auto* el = get(cid); el && el->buffer)
26 el->buffer.retire();
27 std::erase_if(m_elements, [cid](const Element& e) { return e.id == cid; });
28 m_index_stale = true;
29 }
30
31 for (uint32_t cid : ids)
32 m_relations.erase(cid);
33
34 for (auto& [primary_id, related] : m_relations) {
35 for (uint32_t cid : ids)
36 std::erase(related, cid);
37 }
38 std::erase_if(m_relations, [](const auto& kv) { return kv.second.empty(); });
39
40 return true;
41}
42
44{
45 m_elements.clear();
46 m_index.clear();
47 m_index_stale = false;
48 m_relations.clear();
49}
50
51// =============================================================================
52// Element mutation
53// =============================================================================
54
55bool Layer::set_bounds(uint32_t id, Kinesis::AABB2D bounds)
56{
57 if (auto* el = get(id)) {
58 el->bounds_hint = bounds;
59 return true;
60 }
61 return false;
62}
63
64bool Layer::set_contains(uint32_t id, std::function<bool(glm::vec2)> fn)
65{
66 if (auto* el = get(id)) {
67 el->contains = std::move(fn);
68 return true;
69 }
70 return false;
71}
72
73bool Layer::set_clip(uint32_t id, std::optional<Kinesis::AABB2D> clip)
74{
75 if (auto* el = get(id)) {
76 el->clip = clip;
77 return true;
78 }
79 return false;
80}
81
82bool Layer::set_interactive(uint32_t id, bool interactive)
83{
84 if (auto* el = get(id)) {
85 el->interactive = interactive;
86 return true;
87 }
88 return false;
89}
90
91bool Layer::set_visible(uint32_t id, bool visible)
92{
93 if (!get(id))
94 return false;
95
96 for (uint32_t member : closure(id)) {
97 auto* el = get(member);
98 if (!el)
99 continue;
100
101 el->visible = visible;
102 el->buffer.set_visible(visible);
103 }
104 return true;
105}
106
107void Layer::set_all_visible(bool visible)
108{
109 for (const auto& el : m_elements) {
110 set_visible(el.id, visible);
111 }
112}
113
114bool Layer::bring_to_front(uint32_t id)
115{
116 if (!get(id))
117 return false;
118
119 const std::vector<uint32_t> ids = closure(id);
120
121 auto raise = [this](uint32_t target) {
122 auto it = std::ranges::find_if(m_elements,
123 [target](const Element& e) { return e.id == target; });
124 if (it == m_elements.end())
125 return;
126 std::rotate(it, it + 1, m_elements.end());
127 m_index_stale = true;
128
129 if (it = m_elements.end() - 1; it->buffer)
130 it->buffer.bring_to_front(++m_next_front_priority);
131 };
132
133 for (size_t i = 1; i < ids.size(); ++i)
134 raise(ids[i]);
135 raise(ids.front());
136
137 return true;
138}
139
140bool Layer::send_to_back(uint32_t id)
141{
142 if (!get(id))
143 return false;
144
145 const std::vector<uint32_t> ids = closure(id);
146
147 auto lower = [this](uint32_t target) {
148 auto it = std::ranges::find_if(m_elements,
149 [target](const Element& e) { return e.id == target; });
150 if (it == m_elements.end())
151 return;
152 std::rotate(m_elements.begin(), it, it + 1);
153 m_index_stale = true;
154
155 if (auto& el = m_elements.front(); el.buffer)
156 el.buffer.send_to_back(--m_next_back_priority);
157 };
158
159 for (size_t i = 1; i < ids.size(); ++i)
160 lower(ids[i]);
161 lower(ids.front());
162
163 return true;
164}
165
166// =============================================================================
167// Spatial queries
168// =============================================================================
169
170std::optional<uint32_t> Layer::hit_test(glm::vec2 ndc) const
171{
172 for (const auto& m_element : std::views::reverse(m_elements)) {
173 if (test_element(m_element, ndc))
174 return m_element.id;
175 }
176 return std::nullopt;
177}
178
179std::vector<uint32_t> Layer::hit_test_all(glm::vec2 ndc) const
180{
181 std::vector<uint32_t> result;
182 for (const auto& m_element : std::views::reverse(m_elements)) {
183 if (test_element(m_element, ndc))
184 result.push_back(m_element.id);
185 }
186 return result;
187}
188
189std::optional<uint32_t> Layer::hit_test(
190 double px, double py, uint32_t win_w, uint32_t win_h) const
191{
192 return hit_test(to_ndc(px, py, win_w, win_h));
193}
194
195std::vector<uint32_t> Layer::hit_test_all(
196 double px, double py, uint32_t win_w, uint32_t win_h) const
197{
198 return hit_test_all(to_ndc(px, py, win_w, win_h));
199}
200
201// =========================================================================
202// Relations
203// =========================================================================
204
205bool Layer::relate(uint32_t primary_id, uint32_t related_id)
206{
207 auto* p = get(primary_id);
208 auto* r = get(related_id);
209 if (!p || !r || primary_id == related_id)
210 return false;
211
212 auto& rel = m_relations[primary_id];
213 if (std::ranges::find(rel, related_id) == rel.end())
214 rel.push_back(related_id);
215
216 r->visible = p->visible;
217 r->buffer.set_visible(p->visible);
218
219 return true;
220}
221
222bool Layer::unrelate(uint32_t primary_id, uint32_t related_id)
223{
224 auto it = m_relations.find(primary_id);
225 if (it == m_relations.end())
226 return false;
227 auto& rel = it->second;
228 auto rit = std::ranges::find(rel, related_id);
229 if (rit == rel.end())
230 return false;
231 rel.erase(rit);
232 if (rel.empty())
233 m_relations.erase(it);
234 return true;
235}
236
237std::vector<uint32_t> Layer::related_ids(uint32_t primary_id) const
238{
239 auto it = m_relations.find(primary_id);
240 return it != m_relations.end() ? it->second : std::vector<uint32_t> {};
241}
242
243std::vector<uint32_t> Layer::closure(uint32_t id) const
244{
245 std::vector<uint32_t> result { id };
246
247 for (size_t i = 0; i < result.size(); ++i) {
248 auto it = m_relations.find(result[i]);
249 if (it == m_relations.end())
250 continue;
251 for (uint32_t rel_id : it->second) {
252 if (std::ranges::find(result, rel_id) == result.end())
253 result.push_back(rel_id);
254 }
255 }
256
257 return result;
258}
259
260// =============================================================================
261// Introspection
262// =============================================================================
263
264void Layer::reindex() const
265{
266 m_index.clear();
267 for (size_t i = 0; i < m_elements.size(); ++i)
268 m_index[m_elements[i].id] = i;
269 m_index_stale = false;
270}
271
272const Element* Layer::get(uint32_t id) const
273{
274 if (m_index_stale)
275 reindex();
276 const auto it = m_index.find(id);
277 return it != m_index.end() ? &m_elements[it->second] : nullptr;
278}
279
280Element* Layer::get(uint32_t id)
281{
282 return const_cast<Element*>(std::as_const(*this).get(id));
283}
284
285// =============================================================================
286// Internal
287// =============================================================================
288
289glm::vec2 Layer::to_ndc(double px, double py, uint32_t w, uint32_t h) noexcept
290{
291 auto ndc3 = Kinesis::to_ndc(px, py, w, h);
292 return { ndc3.x, ndc3.y };
293}
294
295bool Layer::test_element(const Element& el, glm::vec2 ndc) noexcept
296{
297 if (!el.interactive || !el.visible)
298 return false;
299 if (el.clip && !el.clip->contains(ndc))
300 return false;
301 if (el.bounds_hint && !el.bounds_hint->contains(ndc))
302 return false;
303 if (el.contains)
304 return el.contains(ndc);
305 return el.bounds_hint.has_value();
306}
307
308} // namespace MayaFlux::Portal::Forma
uint32_t h
Definition InkPress.cpp:29
std::vector< uint64_t > ids
uint32_t py
uint32_t px
Handle returned by Layer::add, carrying the assigned element id.
Definition Layer.hpp:52
bool set_interactive(uint32_t id, bool interactive)
Definition Layer.cpp:82
bool set_visible(uint32_t id, bool visible)
Definition Layer.cpp:91
bool bring_to_front(uint32_t id)
Move element to the top of the hit-test and paint order (drawn last), cascading to every related elem...
Definition Layer.cpp:114
void clear()
Remove all elements.
Definition Layer.cpp:43
bool relate(uint32_t primary_id, uint32_t related_id)
Record that related_id belongs with primary_id.
Definition Layer.cpp:205
std::vector< uint32_t > closure(uint32_t id) const
Return id plus every id transitively related to it.
Definition Layer.cpp:243
const Element * get(uint32_t id) const
Definition Layer.cpp:272
std::vector< uint32_t > related_ids(uint32_t primary_id) const
Return the ids related to primary_id, or empty if none.
Definition Layer.cpp:237
bool set_bounds(uint32_t id, Kinesis::AABB2D bounds)
Replace the bounds_hint on an existing element.
Definition Layer.cpp:55
bool remove(uint32_t id)
Remove an element by id, cascading to every related id.
Definition Layer.cpp:17
std::vector< Element > m_elements
Definition Layer.hpp:313
std::vector< uint32_t > hit_test_all(glm::vec2 ndc) const
Return all interactive, visible elements containing ndc, ordered front-to-back (topmost first,...
Definition Layer.cpp:179
static glm::vec2 to_ndc(double px, double py, uint32_t win_w, uint32_t win_h) noexcept
Definition Layer.cpp:289
bool unrelate(uint32_t primary_id, uint32_t related_id)
Remove a specific relation between two elements.
Definition Layer.cpp:222
void set_all_visible(bool visible)
Set every element in the layer visible or hidden.
Definition Layer.cpp:107
std::unordered_map< uint32_t, size_t > m_index
Definition Layer.hpp:314
bool send_to_back(uint32_t id)
Move element to the bottom of the hit-test and paint order (drawn first), cascading to every related ...
Definition Layer.cpp:140
Slot add(Element element)
Add an element to the layer.
Definition Layer.cpp:9
bool set_contains(uint32_t id, std::function< bool(glm::vec2)> fn)
Replace the contains callable on an existing element.
Definition Layer.cpp:64
std::unordered_map< uint32_t, std::vector< uint32_t > > m_relations
Definition Layer.hpp:321
bool set_clip(uint32_t id, std::optional< Kinesis::AABB2D > clip)
Confine hit testing of an element to a region, or lift the confinement with std::nullopt.
Definition Layer.cpp:73
std::optional< uint32_t > hit_test(glm::vec2 ndc) const
Return the topmost interactive, visible element containing ndc.
Definition Layer.cpp:170
static bool test_element(const Element &el, glm::vec2 ndc) noexcept
Definition Layer.cpp:295
glm::vec3 to_ndc(double window_x, double window_y, uint32_t width, uint32_t height)
Convert window pixel coordinates to NDC.
bool contains(glm::vec2 p) const noexcept
Definition Bounds.hpp:67
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
FormaTarget buffer
Where this region's rendered output is written: a buffer of its own, or a slice of one it shares.
Definition Element.hpp:82
uint32_t id
Stable id assigned by Layer::add. Never zero.
Definition Element.hpp:63
A bounded, renderable region on a window surface.
Definition Element.hpp:61