MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FormFactory.cpp
Go to the documentation of this file.
1#include "FormFactory.hpp"
2
5
7
9 Kinesis::AABB2D bounds,
10 float handle_w,
11 glm::vec3 track_color,
12 glm::vec3 handle_color)
13{
14 GeometryFn<float> fn = [bounds, handle_w, track_color, handle_color](
15 float v, std::vector<uint8_t>& out, Element& el) {
16 float x = bounds.min.x + v * (bounds.width() - handle_w);
17 float yt = bounds.min.y + bounds.height() * 0.35F;
18 float yb = bounds.min.y + bounds.height() * 0.65F;
19
20 Kinesis::AABB2D track { .min = glm::vec2(bounds.min.x, yt), .max = glm::vec2(bounds.max.x, yb) };
21 Kinesis::AABB2D handle { .min = glm::vec2(x, bounds.min.y), .max = glm::vec2(x + handle_w, bounds.max.y) };
22
23 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(track, track_color));
24 auto herts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(handle, handle_color));
25 verts.insert(verts.end(), herts.begin(), herts.end());
26
27 write_verts(out, verts);
28
29 el.bounds_hint = handle;
30 el.contains = {};
31 };
32
33 return { std::move(fn),
35 static_cast<size_t>(8) * sizeof(Kakshya::MeshVertex),
36 drag_with<float>(Kinesis::axis_fraction(bounds, handle_w, true)) };
37}
38
40 Kinesis::AABB2D bounds,
41 float handle_h,
42 glm::vec3 track_color,
43 glm::vec3 handle_color)
44{
45 GeometryFn<float> fn = [bounds, handle_h, track_color, handle_color](
46 float v, std::vector<uint8_t>& out, Element& el) {
47 const float y = bounds.min.y + v * (bounds.height() - handle_h);
48 const float xl = bounds.min.x + bounds.width() * 0.35F;
49 const float xr = bounds.min.x + bounds.width() * 0.65F;
50
51 const Kinesis::AABB2D track { .min = { xl, bounds.min.y }, .max = { xr, bounds.max.y } };
52 const Kinesis::AABB2D handle { .min = { bounds.min.x, y }, .max = { bounds.max.x, y + handle_h } };
53
54 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(track, track_color));
55 auto herts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(handle, handle_color));
56 verts.insert(verts.end(), herts.begin(), herts.end());
57
58 write_verts(out, verts);
59
60 el.bounds_hint = handle;
61 el.contains = {};
62 };
63
64 return { std::move(fn),
66 static_cast<size_t>(8) * sizeof(Kakshya::MeshVertex),
67 drag_with<float>(Kinesis::axis_fraction(bounds, handle_h, false)) };
68}
69
71 Kinesis::AABB2D region,
72 float angle_start,
73 float angle_end,
74 glm::vec3 color)
75{
76 const glm::vec2 center = region.center();
77 const float radius = std::min(region.width(), region.height()) * 0.5F;
78
79 GeometryFn<float> fn = [center, radius, angle_start, angle_end, color](
80 float v, std::vector<uint8_t>& out, Element& el) {
81 const float angle = angle_start + v * (angle_end - angle_start);
82 const glm::vec2 tip = center + radius * glm::vec2(std::cos(angle), std::sin(angle));
83
84 using V = Kakshya::LineVertex;
85 const std::vector<V> verts = {
86 { .position = { center.x, center.y, 0 }, .color = color },
87 { .position = { tip.x, tip.y, 0 }, .color = color },
88 };
89
90 write_verts(out, verts);
91
92 el.bounds_hint = Kinesis::AABB2D::from_ndc(center, glm::vec2(radius));
93 el.contains = Kinesis::circular_bounds(center, radius);
94 };
95
96 return { std::move(fn),
98 static_cast<size_t>(2) * sizeof(Kakshya::LineVertex),
99 drag_with<float>(Kinesis::angle_fraction(center, angle_start, angle_end)) };
100}
101
103 glm::vec3 color,
104 float size,
105 float hit_radius)
106{
107 GeometryFn<glm::vec2> fn = [color, size, hit_radius](glm::vec2 pos, std::vector<uint8_t>& out, Element& el) {
109 .position = { pos.x, pos.y, 0.0F },
110 .color = color,
111 .size = size,
112 });
113 el.bounds_hint = Kinesis::AABB2D::from_ndc(pos, glm::vec2(hit_radius));
114 el.contains = Kinesis::circular_bounds(pos, hit_radius);
115 };
116
117 return { std::move(fn),
119 sizeof(Kakshya::PointVertex),
120 follow_move() };
121}
122
124 Kinesis::AABB2D bounds,
125 glm::vec3 color,
126 float size)
127{
128 GeometryFn<glm::vec2> fn = [bounds, color, size](
129 glm::vec2 v, std::vector<uint8_t>& out, Element& el) {
130 float x = bounds.min.x + v.x * bounds.width();
131 float y = bounds.min.y + v.y * bounds.height();
132
133 using V = Kakshya::PointVertex;
134 std::vector<V> verts = {
135 { .position = { x, y, 0 }, .color = color, .size = size },
136 };
137
138 write_verts(out, verts);
139
140 el.bounds_hint = bounds;
141 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
142 std::array<glm::vec2, 4> {
143 bounds.min,
144 glm::vec2(bounds.max.x, bounds.min.y),
145 bounds.max,
146 glm::vec2(bounds.min.x, bounds.max.y) } });
147 };
148
149 return { std::move(fn),
151 sizeof(Kakshya::PointVertex),
152 drag_with<glm::vec2>(Kinesis::unit_square(bounds)) };
153}
154
156 std::span<const glm::vec2> path,
157 std::shared_ptr<Buffers::FormaBuffer> handle_buf,
158 float half_thickness,
159 glm::vec3 track_color,
160 glm::vec3 fill_color,
161 glm::vec3 handle_color,
162 float handle_size)
163{
164 std::vector<glm::vec2> pts(path.begin(), path.end());
165
166 std::vector<float> seg_lengths;
167 seg_lengths.reserve(pts.size() > 0 ? pts.size() - 1 : 0);
168 float total_len = 0.0F;
169 for (size_t i = 0; i + 1 < pts.size(); ++i) {
170 float l = glm::length(pts[i + 1] - pts[i]);
171 seg_lengths.push_back(l);
172 total_len += l;
173 }
174
175 Kinesis::AABB2D aabb { .min = pts.empty() ? glm::vec2(0.F) : pts[0],
176 .max = pts.empty() ? glm::vec2(0.F) : pts[0] };
177 for (const auto& p : pts) {
178 aabb.min = glm::min(aabb.min, p);
179 aabb.max = glm::max(aabb.max, p);
180 }
181 aabb = aabb.expanded(half_thickness);
182
183 auto project = Kinesis::path_fraction(path);
184 const size_t cap = pts.size() * 4 * sizeof(Kakshya::LineVertex);
185
186 GeometryFn<float> fn = [pts = std::move(pts),
187 seg_lengths = std::move(seg_lengths),
188 total_len,
189 aabb,
190 handle_buf = std::move(handle_buf),
191 half_thickness,
192 track_color,
193 fill_color,
194 handle_color,
195 handle_size](float v, std::vector<uint8_t>& out, Element& el) {
196 if (pts.size() < 2) {
197 out.clear();
198 return;
199 }
200
201 const float target = std::clamp(v, 0.0F, 1.0F) * total_len;
202
203 glm::vec2 handle_pos = pts.front();
204 float accumulated = 0.0F;
205 size_t split_seg = 0;
206 float split_t = 0.0F;
207 for (size_t i = 0; i < seg_lengths.size(); ++i) {
208 if (accumulated + seg_lengths[i] >= target || i + 1 == seg_lengths.size()) {
209 split_t = seg_lengths[i] > 0.0F
210 ? (target - accumulated) / seg_lengths[i]
211 : 0.0F;
212 split_t = std::clamp(split_t, 0.0F, 1.0F);
213 handle_pos = glm::mix(pts[i], pts[i + 1], split_t);
214 split_seg = i;
215 break;
216 }
217 accumulated += seg_lengths[i];
218 }
219
220 auto verts = Kinesis::polyline(pts, track_color);
221
222 auto fill = Kinesis::polyline(
223 std::span<const glm::vec2>(pts).subspan(0, split_seg + 1),
224 fill_color);
225 verts.insert(verts.end(), fill.begin(), fill.end());
226
227 if (split_t > 0.0F) {
228 verts.push_back({ .position = { pts[split_seg].x, pts[split_seg].y, 0.0F }, .color = fill_color });
229 verts.push_back({ .position = { handle_pos.x, handle_pos.y, 0.0F }, .color = fill_color });
230 }
231
232 write_verts(out, verts);
233
234 el.bounds_hint = aabb;
235 el.contains = Kinesis::stroke_bounds(pts, half_thickness);
236
237 if (handle_buf) {
239 .position = { handle_pos.x, handle_pos.y, 0.0F },
240 .color = handle_color,
241 .size = handle_size,
242 };
243 std::vector<uint8_t> hbytes(sizeof(hv));
244 std::memcpy(hbytes.data(), &hv, sizeof(hv));
245 handle_buf->submit(hbytes);
246 }
247 };
248
249 return { std::move(fn),
251 cap,
252 drag_with<float>(std::move(project)) };
253}
254
256 Kinesis::AABB2D region,
257 glm::vec3 color_off,
258 glm::vec3 color_on)
259{
260 GeometryFn<bool> fn = [region, color_off, color_on](
261 bool v, std::vector<uint8_t>& out, Element& el) {
262 write_verts(out, Kakshya::to_mesh_vertices(Kinesis::filled_rect(region, v ? color_on : color_off)));
263 el.bounds_hint = region;
264 el.contains = {};
265 };
266
267 return { std::move(fn),
269 static_cast<size_t>(4) * sizeof(Kakshya::MeshVertex),
270 press_flip() };
271}
272
274 Kinesis::AABB2D bounds,
275 bool horizontal,
276 glm::vec3 fill_color,
277 glm::vec3 track_color)
278{
279 GeometryFn<float> fn = [bounds, horizontal, fill_color, track_color](
280 float v, std::vector<uint8_t>& out, Element& el) {
281 const float t = std::clamp(v, 0.F, 1.F);
282
283 Kinesis::AABB2D fill {}, remainder {};
284 if (horizontal) {
285 const float split = bounds.min.x + t * bounds.width();
286 fill = { .min = bounds.min, .max = { split, bounds.max.y } };
287 remainder = { .min = { split, bounds.min.y }, .max = bounds.max };
288 } else {
289 const float split = bounds.min.y + t * bounds.height();
290 fill = { .min = bounds.min, .max = { bounds.max.x, split } };
291 remainder = { .min = { bounds.min.x, split }, .max = bounds.max };
292 }
293
294 auto verts = Kakshya::to_mesh_vertices(Kinesis::filled_rect(fill, fill_color));
295 auto rest = Kakshya::to_mesh_vertices(Kinesis::filled_rect(remainder, track_color));
296 verts.insert(verts.end(), rest.begin(), rest.end());
297
298 write_verts(out, verts);
299
300 el.bounds_hint = bounds;
301 el.contains = {};
302 };
303
304 return { std::move(fn),
306 static_cast<size_t>(8) * sizeof(Kakshya::MeshVertex) };
307}
308
310 float arm_len,
311 glm::vec3 color,
312 float thickness,
313 float hit_radius)
314{
315 GeometryFn<glm::vec2> fn = [arm_len, color, thickness, hit_radius](
316 glm::vec2 pos, std::vector<uint8_t>& out, Element& el) {
317 using V = Kakshya::LineVertex;
318 const std::array<V, 4> verts { {
319 { .position = { pos.x - arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
320 { .position = { pos.x + arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
321 { .position = { pos.x, pos.y - arm_len, 0.F }, .color = color, .thickness = thickness },
322 { .position = { pos.x, pos.y + arm_len, 0.F }, .color = color, .thickness = thickness },
323 } };
324 write_verts(out, verts);
325 el.bounds_hint = Kinesis::AABB2D::from_ndc(pos, glm::vec2(arm_len));
326 el.contains = Kinesis::circular_bounds(pos, hit_radius);
327 };
328
329 return { std::move(fn),
331 static_cast<size_t>(4) * sizeof(Kakshya::LineVertex),
332 follow_move() };
333}
334
336 Kinesis::AABB2D bounds,
337 glm::vec3 color,
338 float thickness)
339{
340 GeometryFn<std::vector<float>> fn = [bounds, color, thickness](
341 const std::vector<float>& v, std::vector<uint8_t>& out, Element& el) {
342 if (v.size() < 2) {
343 out.clear();
344 el.bounds_hint = bounds;
345 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
346 std::array<glm::vec2, 4> {
347 bounds.min,
348 glm::vec2(bounds.max.x, bounds.min.y),
349 bounds.max,
350 glm::vec2(bounds.min.x, bounds.max.y) } });
351 return;
352 }
353
354 const auto n = v.size();
355 const float x_step = bounds.width() / static_cast<float>(n - 1);
356
357 std::vector<Kakshya::LineVertex> verts;
358 verts.reserve((n - 1) * 2);
359
360 for (size_t i = 0; i + 1 < n; ++i) {
361 const float xa = bounds.min.x + static_cast<float>(i) * x_step;
362 const float xb = bounds.min.x + static_cast<float>(i + 1) * x_step;
363 const float ya = bounds.min.y + std::clamp(v[i], 0.F, 1.F) * bounds.height();
364 const float yb = bounds.min.y + std::clamp(v[i + 1], 0.F, 1.F) * bounds.height();
365
366 verts.push_back({ .position = { xa, ya, 0.F }, .color = color, .thickness = thickness });
367 verts.push_back({ .position = { xb, yb, 0.F }, .color = color, .thickness = thickness });
368 }
369
370 write_verts(out, verts);
371
372 el.bounds_hint = bounds;
373 el.contains = Kinesis::polygon_bounds(std::span<const glm::vec2> {
374 std::array<glm::vec2, 4> {
375 bounds.min,
376 glm::vec2(bounds.max.x, bounds.min.y),
377 bounds.max,
378 glm::vec2(bounds.min.x, bounds.max.y) } });
379 };
380
381 Form<std::vector<float>> form { std::move(fn) };
383 form.wire = paint_over(bounds);
384 return form;
385}
386
388 Context& ctx,
389 uint32_t id,
390 std::shared_ptr<MappedState<std::vector<float>>> state,
391 Kinesis::AABB2D bounds)
392{
393 struct DragState {
394 std::optional<size_t> prev_index;
395 };
396 auto ds = std::make_shared<DragState>();
397
399 [state, bounds, ds](uint32_t, glm::vec2 ndc) {
400 auto& v = state->value;
401 if (v.empty())
402 return;
403
404 const float t = (ndc.x - bounds.min.x) / bounds.width();
405 const float a = (ndc.y - bounds.min.y) / bounds.height();
406 const size_t n = v.size();
407 const size_t idx = static_cast<size_t>(
408 std::clamp(t, 0.F, 1.F) * static_cast<float>(n - 1));
409 const float amp = std::clamp(a, 0.F, 1.F);
410
411 if (ds->prev_index && *ds->prev_index != idx) {
412 const size_t lo = std::min(*ds->prev_index, idx);
413 const size_t hi = std::max(*ds->prev_index, idx);
414 const float v0 = v[*ds->prev_index];
415 const auto span = static_cast<float>(hi - lo);
416 for (size_t i = lo; i <= hi; ++i) {
417 const float f = span > 0.F
418 ? static_cast<float>(i - lo) / span
419 : 1.F;
420 v[i] = glm::mix(v0, amp, f);
421 }
422 } else {
423 v[idx] = amp;
424 }
425
426 ds->prev_index = idx;
427 ++state->version;
428 });
429
430 ctx.on_release(id, IO::MouseButtons::Left, [ds](uint32_t, glm::vec2) {
431 ds->prev_index = std::nullopt;
432 });
433}
434
435} // namespace MayaFlux::Portal::Forma::Geometry
float radius
size_t a
float lo
float hi
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< float(glm::vec2)> path_fraction(std::span< const glm::vec2 > points)
Normalized arc-length position of the closest point on a polyline.
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::function< float(glm::vec2)> angle_fraction(glm::vec2 center, float angle_start, float angle_end) noexcept
Fraction along an angular sweep about center.
std::function< glm::vec2(glm::vec2)> unit_square(AABB2D bounds) noexcept
Unit-square coordinates of a point within bounds.
std::function< float(glm::vec2)> axis_fraction(AABB2D bounds, float handle_extent=0.F, bool horizontal=true) noexcept
Fraction along one axis of bounds, inverse to fader placement.
std::array< Kakshya::Vertex, 4 > filled_rect(Kinesis::AABB2D region, glm::vec3 color)
Generate a filled TRIANGLE_STRIP quad from an AABB2D.
Form< float > radial(Kinesis::AABB2D region, float angle_start, float angle_end, glm::vec3 color)
Geometry function for a radial indicator in NDC space.
auto follow_move()
Produce a Form::wire that writes the cursor position on hover.
Form< glm::vec2 > position_picker(Kinesis::AABB2D bounds, glm::vec3 color, float size)
Geometry function for a 2D position picker in NDC space.
auto paint_over(Kinesis::AABB2D bounds)
Produce a Form::wire that registers canvas painting over bounds.
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.
Form< 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.
Form< 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.
Form< bool > toggle(Kinesis::AABB2D region, glm::vec3 color_off, glm::vec3 color_on)
Geometry function for a boolean toggle in NDC space.
Form< 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.
auto press_flip(IO::MouseButtons btn=IO::MouseButtons::Left)
Produce a Form::wire that registers a press flipping a bool state.
Form< glm::vec2 > point(glm::vec3 color, float size, float hit_radius)
Geometry function for a positioned point in NDC space.
Form< std::vector< float > > drawable_canvas(Kinesis::AABB2D bounds, glm::vec3 color, float thickness)
Geometry function for a drawable curve canvas in NDC space.
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
Form< 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.
Form< glm::vec2 > crosshair(float arm_len, glm::vec3 color, float thickness, float hit_radius)
Geometry function for a crosshair indicator in NDC space.
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 indexed triangle mesh primitives (TRIANGLE_LIST 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
glm::vec2 center() const noexcept
Definition Bounds.hpp:40
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
Graphics::PrimitiveTopology topology
Topology matching the vertex kind geometry writes.
Definition Geometry.hpp:88
A geometry function plus everything its realization requires.
Definition Geometry.hpp:83
Value carrier for a Mapped primitive.
Definition Mapped.hpp:36