MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Geometry.cpp
Go to the documentation of this file.
1#include "Geometry.hpp"
2
6
8
10 Kinesis::AABB2D bounds,
11 float handle_w,
12 glm::vec3 track_color,
13 glm::vec3 handle_color)
14{
15 return [bounds, handle_w, track_color, handle_color](
16 float v, std::vector<uint8_t>& out, Element& el) {
17 float x = bounds.min.x + v * (bounds.width() - handle_w);
18 float yt = bounds.min.y + bounds.height() * 0.35F;
19 float yb = bounds.min.y + bounds.height() * 0.65F;
20
21 Kinesis::AABB2D track { .min = glm::vec2(bounds.min.x, yt), .max = glm::vec2(bounds.max.x, yb) };
22 Kinesis::AABB2D handle { .min = glm::vec2(x, bounds.min.y), .max = glm::vec2(x + handle_w, bounds.max.y) };
23
24 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(track, track_color));
25 auto herts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(handle, handle_color));
26 verts.insert(verts.end(), herts.begin(), herts.end());
27
28 write_verts(out, verts);
29
30 el.bounds_hint = handle;
31 el.contains = {};
32 };
33}
34
36 Kinesis::AABB2D bounds,
37 float handle_h,
38 glm::vec3 track_color,
39 glm::vec3 handle_color)
40{
41 return [bounds, handle_h, track_color, handle_color](
42 float v, std::vector<uint8_t>& out, Element& el) {
43 const float y = bounds.min.y + v * (bounds.height() - handle_h);
44 const float xl = bounds.min.x + bounds.width() * 0.35F;
45 const float xr = bounds.min.x + bounds.width() * 0.65F;
46
47 const Kinesis::AABB2D track { .min = { xl, bounds.min.y }, .max = { xr, bounds.max.y } };
48 const Kinesis::AABB2D handle { .min = { bounds.min.x, y }, .max = { bounds.max.x, y + handle_h } };
49
50 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(track, track_color));
51 auto herts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(handle, handle_color));
52 verts.insert(verts.end(), herts.begin(), herts.end());
53
54 write_verts(out, verts);
55
56 el.bounds_hint = handle;
57 el.contains = {};
58 };
59}
60
62 glm::vec2 center,
63 float radius,
64 float angle_start,
65 float angle_end,
66 glm::vec3 color)
67{
68 return [center, radius, angle_start, angle_end, color](
69 float v, std::vector<uint8_t>& out, Element& el) {
70 float angle = angle_start + v * (angle_end - angle_start);
71 glm::vec2 tip = center + radius * glm::vec2(std::cos(angle), std::sin(angle));
72
73 using V = Kakshya::LineVertex;
74 std::vector<V> verts = {
75 { .position = { center.x, center.y, 0 }, .color = color },
76 { .position = { tip.x, tip.y, 0 }, .color = color },
77 };
78
79 write_verts(out, verts);
80
81 el.bounds_hint = Kinesis::AABB2D::from_ndc(center, glm::vec2(radius));
82 el.contains = Kinesis::circular_bounds(center, radius);
83 };
84}
85
87 glm::vec3 color,
88 float size,
89 float hit_radius)
90{
91 return [color, size, hit_radius](glm::vec2 pos, std::vector<uint8_t>& out, Element& el) {
93 .position = { pos.x, pos.y, 0.0F },
94 .color = color,
95 .size = size,
96 });
97 el.bounds_hint = Kinesis::AABB2D::from_ndc(pos, glm::vec2(hit_radius));
98 el.contains = Kinesis::circular_bounds(pos, hit_radius);
99 };
100}
101
103 Kinesis::AABB2D bounds,
104 glm::vec3 color,
105 float size)
106{
107 return [bounds, color, size](
108 glm::vec2 v, std::vector<uint8_t>& out, Element& el) {
109 float x = bounds.min.x + v.x * bounds.width();
110 float y = bounds.min.y + v.y * bounds.height();
111
112 using V = Kakshya::PointVertex;
113 std::vector<V> verts = {
114 { .position = { x, y, 0 }, .color = color, .size = size },
115 };
116
117 write_verts(out, verts);
118
119 el.bounds_hint = bounds;
120 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
121 std::array<glm::vec2, 4> {
122 bounds.min,
123 glm::vec2(bounds.max.x, bounds.min.y),
124 bounds.max,
125 glm::vec2(bounds.min.x, bounds.max.y) } });
126 };
127}
128
130 std::span<const glm::vec2> path,
131 std::shared_ptr<Buffers::FormaBuffer> handle_buf,
132 float half_thickness,
133 glm::vec3 track_color,
134 glm::vec3 fill_color,
135 glm::vec3 handle_color,
136 float handle_size)
137{
138 std::vector<glm::vec2> pts(path.begin(), path.end());
139
140 std::vector<float> seg_lengths;
141 seg_lengths.reserve(pts.size() > 0 ? pts.size() - 1 : 0);
142 float total_len = 0.0F;
143 for (size_t i = 0; i + 1 < pts.size(); ++i) {
144 float l = glm::length(pts[i + 1] - pts[i]);
145 seg_lengths.push_back(l);
146 total_len += l;
147 }
148
149 Kinesis::AABB2D aabb { .min = pts.empty() ? glm::vec2(0.F) : pts[0],
150 .max = pts.empty() ? glm::vec2(0.F) : pts[0] };
151 for (const auto& p : pts) {
152 aabb.min = glm::min(aabb.min, p);
153 aabb.max = glm::max(aabb.max, p);
154 }
155 aabb = aabb.expanded(half_thickness);
156
157 return [pts = std::move(pts),
158 seg_lengths = std::move(seg_lengths),
159 total_len,
160 aabb,
161 handle_buf = std::move(handle_buf),
162 half_thickness,
163 track_color,
164 fill_color,
165 handle_color,
166 handle_size](float v, std::vector<uint8_t>& out, Element& el) {
167 if (pts.size() < 2) {
168 out.clear();
169 return;
170 }
171
172 const float target = std::clamp(v, 0.0F, 1.0F) * total_len;
173
174 glm::vec2 handle_pos = pts.front();
175 float accumulated = 0.0F;
176 size_t split_seg = 0;
177 float split_t = 0.0F;
178 for (size_t i = 0; i < seg_lengths.size(); ++i) {
179 if (accumulated + seg_lengths[i] >= target || i + 1 == seg_lengths.size()) {
180 split_t = seg_lengths[i] > 0.0F
181 ? (target - accumulated) / seg_lengths[i]
182 : 0.0F;
183 split_t = std::clamp(split_t, 0.0F, 1.0F);
184 handle_pos = glm::mix(pts[i], pts[i + 1], split_t);
185 split_seg = i;
186 break;
187 }
188 accumulated += seg_lengths[i];
189 }
190
191 auto verts = Kinesis::polyline(pts, track_color);
192
193 auto fill = Kinesis::polyline(
194 std::span<const glm::vec2>(pts).subspan(0, split_seg + 1),
195 fill_color);
196 verts.insert(verts.end(), fill.begin(), fill.end());
197
198 if (split_t > 0.0F) {
199 verts.push_back({ .position = { pts[split_seg].x, pts[split_seg].y, 0.0F }, .color = fill_color });
200 verts.push_back({ .position = { handle_pos.x, handle_pos.y, 0.0F }, .color = fill_color });
201 }
202
203 write_verts(out, verts);
204
205 el.bounds_hint = aabb;
206 el.contains = Kinesis::stroke_bounds(pts, half_thickness);
207
208 if (handle_buf) {
210 .position = { handle_pos.x, handle_pos.y, 0.0F },
211 .color = handle_color,
212 .size = handle_size,
213 };
214 std::vector<uint8_t> hbytes(sizeof(hv));
215 std::memcpy(hbytes.data(), &hv, sizeof(hv));
216 handle_buf->submit(hbytes);
217 }
218 };
219}
220
222 Kinesis::AABB2D region,
223 glm::vec3 color_off,
224 glm::vec3 color_on)
225{
226 return [region, color_off, color_on](
227 bool v, std::vector<uint8_t>& out, Element& el) {
228 write_verts(out, Kakshya::to_mesh_vertices(Kinesis::filled_rect(region, v ? color_on : color_off)));
229 el.bounds_hint = region;
230 el.contains = {};
231 };
232}
233
235 Kinesis::AABB2D bounds,
236 bool horizontal,
237 glm::vec3 fill_color,
238 glm::vec3 track_color)
239{
240 return [bounds, horizontal, fill_color, track_color](
241 float v, std::vector<uint8_t>& out, Element& el) {
242 const float t = std::clamp(v, 0.F, 1.F);
243
244 Kinesis::AABB2D fill {}, remainder {};
245 if (horizontal) {
246 const float split = bounds.min.x + t * bounds.width();
247 fill = { .min = bounds.min, .max = { split, bounds.max.y } };
248 remainder = { .min = { split, bounds.min.y }, .max = bounds.max };
249 } else {
250 const float split = bounds.min.y + t * bounds.height();
251 fill = { .min = bounds.min, .max = { bounds.max.x, split } };
252 remainder = { .min = { bounds.min.x, split }, .max = bounds.max };
253 }
254
255 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(fill, fill_color));
256 auto rest = Kakshya::to_mesh_vertices(Kinesis::filled_rect(remainder, track_color));
257 verts.insert(verts.end(), rest.begin(), rest.end());
258
259 write_verts(out, verts);
260
261 el.bounds_hint = bounds;
262 el.contains = {};
263 };
264}
265
267 float arm_len,
268 glm::vec3 color,
269 float thickness,
270 float hit_radius)
271{
272 return [arm_len, color, thickness, hit_radius](
273 glm::vec2 pos, std::vector<uint8_t>& out, Element& el) {
274 using V = Kakshya::LineVertex;
275 const std::array<V, 4> verts { {
276 { .position = { pos.x - arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
277 { .position = { pos.x + arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
278 { .position = { pos.x, pos.y - arm_len, 0.F }, .color = color, .thickness = thickness },
279 { .position = { pos.x, pos.y + arm_len, 0.F }, .color = color, .thickness = thickness },
280 } };
281 write_verts(out, verts);
282 el.bounds_hint = Kinesis::AABB2D::from_ndc(pos, glm::vec2(arm_len));
283 el.contains = Kinesis::circular_bounds(pos, hit_radius);
284 };
285}
286
288 Kinesis::AABB2D bounds,
289 glm::vec3 color,
290 float thickness)
291{
292 return [bounds, color, thickness](
293 const std::vector<float>& v, std::vector<uint8_t>& out, Element& el) {
294 if (v.size() < 2) {
295 out.clear();
296 el.bounds_hint = bounds;
297 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
298 std::array<glm::vec2, 4> {
299 bounds.min,
300 glm::vec2(bounds.max.x, bounds.min.y),
301 bounds.max,
302 glm::vec2(bounds.min.x, bounds.max.y) } });
303 return;
304 }
305
306 const auto n = v.size();
307 const float x_step = bounds.width() / static_cast<float>(n - 1);
308
309 std::vector<Kakshya::LineVertex> verts;
310 verts.reserve((n - 1) * 2);
311
312 for (size_t i = 0; i + 1 < n; ++i) {
313 const float xa = bounds.min.x + static_cast<float>(i) * x_step;
314 const float xb = bounds.min.x + static_cast<float>(i + 1) * x_step;
315 const float ya = bounds.min.y + std::clamp(v[i], 0.F, 1.F) * bounds.height();
316 const float yb = bounds.min.y + std::clamp(v[i + 1], 0.F, 1.F) * bounds.height();
317
318 verts.push_back({ .position = { xa, ya, 0.F }, .color = color, .thickness = thickness });
319 verts.push_back({ .position = { xb, yb, 0.F }, .color = color, .thickness = thickness });
320 }
321
322 write_verts(out, verts);
323
324 el.bounds_hint = bounds;
325 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
326 std::array<glm::vec2, 4> {
327 bounds.min,
328 glm::vec2(bounds.max.x, bounds.min.y),
329 bounds.max,
330 glm::vec2(bounds.min.x, bounds.max.y) } });
331 };
332}
333
335 Context& ctx,
336 uint32_t id,
337 std::shared_ptr<MappedState<std::vector<float>>> state,
338 Kinesis::AABB2D bounds)
339{
340 struct DragState {
341 std::optional<size_t> prev_index;
342 };
343 auto ds = std::make_shared<DragState>();
344
346 [state, bounds, ds](uint32_t, glm::vec2 ndc) {
347 auto& v = state->value;
348 if (v.empty())
349 return;
350
351 const float t = (ndc.x - bounds.min.x) / bounds.width();
352 const float a = (ndc.y - bounds.min.y) / bounds.height();
353 const size_t n = v.size();
354 const size_t idx = static_cast<size_t>(
355 std::clamp(t, 0.F, 1.F) * static_cast<float>(n - 1));
356 const float amp = std::clamp(a, 0.F, 1.F);
357
358 if (ds->prev_index && *ds->prev_index != idx) {
359 const size_t lo = std::min(*ds->prev_index, idx);
360 const size_t hi = std::max(*ds->prev_index, idx);
361 const float v0 = v[*ds->prev_index];
362 const auto span = static_cast<float>(hi - lo);
363 for (size_t i = lo; i <= hi; ++i) {
364 const float f = span > 0.F
365 ? static_cast<float>(i - lo) / span
366 : 1.F;
367 v[i] = glm::mix(v0, amp, f);
368 }
369 } else {
370 v[idx] = amp;
371 }
372
373 ds->prev_index = idx;
374 ++state->version;
375 });
376
377 ctx.on_release(id, IO::MouseButtons::Left, [ds](uint32_t, glm::vec2) {
378 ds->prev_index = std::nullopt;
379 });
380}
381
382} // namespace MayaFlux::Portal::Forma::Geometry
Illustrative geometry functions for common Mapped use cases.
size_t a
void on_release(uint32_t id, IO::MouseButtons btn, PressFn fn)
Called when a mouse button is released over an element.
Definition Context.cpp:38
void on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn)
Called on each mouse-move event while btn is held, tracking the element where the drag began even whe...
Definition Context.cpp:48
Event wiring between a Layer and a window surface.
Definition Context.hpp:40
std::vector< MeshVertex > to_mesh_vertices(std::span< const Vertex > vertices, glm::vec2 weight_range)
Batch-project raw Vertex vector to MeshVertex.
std::function< bool(glm::vec2)> polygon_bounds(std::span< const glm::vec2 > vertices)
Containment test for a convex or concave polygon.
Definition Bounds.hpp:227
std::vector< Kakshya::LineVertex > polyline(std::span< const glm::vec2 > pts, glm::vec3 color, float thickness)
Polyline as a LINE_LIST (open path, 2 * (pts.size() - 1) vertices).
std::function< bool(glm::vec2)> stroke_bounds(std::span< const glm::vec2 > points, float half_thickness)
Containment test for a polyline with a uniform half-thickness.
Definition Bounds.hpp:267
std::function< bool(glm::vec2)> circular_bounds(glm::vec2 center, float radius) noexcept
Containment test for a circle.
Definition Bounds.hpp:208
std::array< Kakshya::Vertex, 4 > filled_rect(Kinesis::AABB2D region, glm::vec3 color)
Generate a filled TRIANGLE_STRIP quad from an AABB2D.
GeometryFn< glm::vec2 > point(glm::vec3 color, float size, float hit_radius)
Geometry function for a positioned point in NDC space.
Definition Geometry.cpp:86
GeometryFn< bool > toggle(Kinesis::AABB2D region, glm::vec3 color_off, glm::vec3 color_on)
Geometry function for a boolean toggle in NDC space.
Definition Geometry.cpp:221
GeometryFn< float > stroke_slider(std::span< const glm::vec2 > path, std::shared_ptr< Buffers::FormaBuffer > handle_buf, float half_thickness, glm::vec3 track_color, glm::vec3 fill_color, glm::vec3 handle_color, float handle_size)
Geometry function for a value scrubber along an arbitrary polyline.
Definition Geometry.cpp:129
GeometryFn< glm::vec2 > crosshair(float arm_len, glm::vec3 color, float thickness, float hit_radius)
Geometry function for a crosshair indicator in NDC space.
Definition Geometry.cpp:266
GeometryFn< glm::vec2 > position_picker(Kinesis::AABB2D bounds, glm::vec3 color, float size)
Geometry function for a 2D position picker in NDC space.
Definition Geometry.cpp:102
GeometryFn< float > radial(glm::vec2 center, float radius, float angle_start, float angle_end, glm::vec3 color)
Geometry function for a radial indicator in NDC space.
Definition Geometry.cpp:61
GeometryFn< std::vector< float > > drawable_canvas(Kinesis::AABB2D bounds, glm::vec3 color, float thickness)
Geometry function for a drawable curve canvas in NDC space.
Definition Geometry.cpp:287
GeometryFn< float > vertical_fader(Kinesis::AABB2D bounds, float handle_h, glm::vec3 track_color, glm::vec3 handle_color)
Geometry function for a vertical fader in NDC space.
Definition Geometry.cpp:35
void wire_canvas_drag(Context &ctx, uint32_t id, std::shared_ptr< MappedState< std::vector< float > > > state, Kinesis::AABB2D bounds)
Wire drag interaction for a drawable canvas element.
Definition Geometry.cpp:334
GeometryFn< float > level_meter(Kinesis::AABB2D bounds, bool horizontal, glm::vec3 fill_color, glm::vec3 track_color)
Geometry function for a level meter in NDC space.
Definition Geometry.cpp:234
GeometryFn< float > horizontal_fader(Kinesis::AABB2D bounds, float handle_w, glm::vec3 track_color, glm::vec3 handle_color)
Geometry function for a horizontal fader in NDC space.
Definition Geometry.cpp:9
void write_verts(std::vector< uint8_t > &out, const std::vector< V > &verts)
Write a vertex array into a GeometryFn output buffer.
Definition Geometry.hpp:35
std::function< void(T value, std::vector< uint8_t > &out_bytes, Element &element)> GeometryFn
Geometry function signature.
Definition Mapped.hpp:64
Vertex type for line primitives (LINE_LIST / LINE_STRIP topology)
Vertex type for point primitives (POINT_LIST topology)
static AABB2D from_ndc(glm::vec2 center, glm::vec2 half) noexcept
Definition Bounds.hpp:52
float height() const noexcept
Definition Bounds.hpp:38
bool contains(glm::vec2 p) const noexcept
Definition Bounds.hpp:25
float width() const noexcept
Definition Bounds.hpp:37
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:21
bool contains(const glm::vec3 &p) const noexcept
Definition Bounds.hpp:147
AABB3D expanded(float margin) const noexcept
Definition Bounds.hpp:170
A bounded, renderable region on a window surface.
Definition Element.hpp:58
Value carrier for a Mapped primitive.
Definition Mapped.hpp:36