MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FormFactory.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Geometry.hpp"
5
7
9
11
12/**
13 * @brief Register a drag handler writing a projected cursor position to @p state.
14 *
15 * The projection carries the geometry-specific mapping.
16 * Kinesis::Projection supplies the inverses matching the geometry functions
17 * above: axis_fraction for the faders, unit_square for position_picker,
18 * angle_fraction for radial, path_fraction for stroke_slider.
19 *
20 * @param ctx Context owning the element's callbacks.
21 * @param id Element id returned from create_element.
22 * @param state MappedState written on each drag event.
23 * @param project NDC to value mapping.
24 * @param btn Mouse button that must be held.
25 */
26template <typename T>
28 Context& ctx,
29 uint32_t id,
30 std::shared_ptr<MappedState<T>> state,
31 std::function<T(glm::vec2)> project,
33{
34 if (!state || !project)
35 return;
36
37 ctx.wire_drag(
38 id,
39 [state = std::move(state), project = std::move(project)](glm::vec2 ndc) {
40 state->write(project(ndc));
41 },
42 btn);
43}
44
45/**
46 * @brief Wire drag interaction for a drawable canvas element.
47 *
48 * Registers a left-button drag callback on @p ctx that maps NDC cursor
49 * position to a sample index and amplitude, writes into @p state, and
50 * increments the version. Linear interpolation fills the range between the
51 * previously touched index and the current one, preventing sparse samples
52 * under fast drag.
53 *
54 * @param ctx Context owning the element.
55 * @param id Element id from the Mapped.
56 * @param state MappedState<vector<float>> to write into.
57 * @param bounds Canvas NDC bounds — must match those passed to drawable_canvas().
58 */
59MAYAFLUX_API void wire_canvas_drag(
60 Context& ctx,
61 uint32_t id,
62 const std::shared_ptr<MappedState<std::vector<float>>>& state,
63 Kinesis::AABB2D bounds);
64
65/**
66 * @brief Produce a Form::wire that registers a drag writing a projected
67 * cursor position.
68 *
69 * @param project NDC to value mapping, from Kinesis::Projection.
70 * @param btn Mouse button that must be held.
71 */
72template <typename T>
73[[nodiscard]] auto drag_with(
74 std::function<T(glm::vec2)> project,
76{
77 return [project = std::move(project), btn](
78 Context& ctx, uint32_t id, std::shared_ptr<MappedState<T>> state) {
79 wire_drag<T>(ctx, id, std::move(state), project, btn);
80 };
81}
82
83/**
84 * @brief Produce a Form::wire that registers a press flipping a bool state.
85 */
86[[nodiscard]] inline auto press_flip(IO::MouseButtons btn = IO::MouseButtons::Left)
87{
88 return [btn](Context& ctx, uint32_t id, std::shared_ptr<MappedState<bool>> state) {
89 ctx.on_press(id, btn, [state = std::move(state)](uint32_t, glm::vec2) {
90 state->write(!state->value);
91 });
92 };
93}
94
95/**
96 * @brief Produce a Form::wire that registers canvas painting over @p bounds.
97 */
98[[nodiscard]] inline auto paint_over(Kinesis::AABB2D bounds)
99{
100 return [bounds](Context& ctx, uint32_t id,
101 const std::shared_ptr<MappedState<std::vector<float>>>& state) {
102 wire_canvas_drag(ctx, id, state, bounds);
103 };
104}
105
106/**
107 * @brief Produce a Form::wire that writes the cursor position on hover.
108 *
109 * No button held. For geometry whose value is an NDC position rather than
110 * a normalized parameter.
111 */
112[[nodiscard]] inline auto follow_move()
113{
114 return [](Context& ctx, uint32_t id, std::shared_ptr<MappedState<glm::vec2>> state) {
115 ctx.on_move(id, [state = std::move(state)](uint32_t, glm::vec2 ndc) {
116 state->write(ndc);
117 });
118 };
119}
120
121// =============================================================================
122// Horizontal fader
123//
124// Value in [0, 1] moves a handle quad along a track quad.
125// Two quads: track (static) and handle (value-driven).
126// Both written as TRIANGLE_STRIP pairs into a single buffer.
127//
128// Track: full extent of bounds.
129// Handle: small square at x = bounds.min.x + value * bounds.width().
130//
131// Hit region follows the handle, updated on every sync().
132// =============================================================================
133
134/**
135 * @brief Geometry function for a horizontal fader in NDC space.
136 * @param bounds Full extent of the fader in NDC.
137 * @param handle_w Handle width in NDC units.
138 * @param track_color Track quad color.
139 * @param handle_color Handle quad color.
140 * @param live_offset Optional. Current translation of the drawn fader,
141 * e.g. a Scrollable's live scroll offset. Shifts both
142 * the rendered geometry and the drag's cursor mapping.
143 */
144[[nodiscard]] MAYAFLUX_API Form<float> horizontal_fader(
145 Kinesis::AABB2D bounds,
146 float handle_w,
147 glm::vec3 track_color = glm::vec3(0.3F),
148 glm::vec3 handle_color = glm::vec3(0.9F),
149 const std::function<glm::vec2()>& live_offset = {});
150
151// =============================================================================
152// Vertical fader
153//
154// Symmetric counterpart to horizontal_fader. Value in [0, 1] moves a handle
155// quad upward along a track quad.
156//
157// Track: thin vertical band through the center of bounds.
158// Handle: small square at y = bounds.min.y + value * (bounds.height() - handle_h).
159//
160// Hit region follows the handle, updated on every sync().
161// Topology: TRIANGLE_STRIP (two quad pairs via to_mesh_vertices).
162// =============================================================================
163
164/**
165 * @brief Geometry function for a vertical fader in NDC space.
166 * @param bounds Full extent of the fader in NDC.
167 * @param handle_h Handle height in NDC units.
168 * @param track_color Track quad color.
169 * @param handle_color Handle quad color.
170 * @param live_offset Optional. Current translation of the drawn fader.
171 */
172[[nodiscard]] MAYAFLUX_API Form<float> vertical_fader(
173 Kinesis::AABB2D bounds,
174 float handle_h,
175 glm::vec3 track_color = glm::vec3(0.3F),
176 glm::vec3 handle_color = glm::vec3(0.9F),
177 const std::function<glm::vec2()>& live_offset = {});
178
179// =============================================================================
180// Radial / arc
181//
182// Value in [0, 1] sweeps an indicator line around the centre of a region.
183// angle_start and angle_end in radians, measured from +X, CCW.
184// Produces a LINE_LIST of two vertices: centre to indicator tip.
185//
186// The radius is the region's smaller half-extent, so the sweep is inscribed
187// regardless of aspect. For a centre and radius, build the region with
188// Kinesis::AABB2D::from_ndc(centre, glm::vec2(radius)).
189// =============================================================================
190
191/**
192 * @brief Geometry function for a radial indicator in NDC space.
193 * @param region NDC region the arc is inscribed in.
194 * @param angle_start Start angle in radians (value = 0).
195 * @param angle_end End angle in radians (value = 1).
196 * @param color Line color.
197 * @param live_offset Optional. Current translation of the drawn indicator.
198 */
199[[nodiscard]] MAYAFLUX_API Form<float> radial(
200 Kinesis::AABB2D region,
201 float angle_start,
202 float angle_end,
203 glm::vec3 color = glm::vec3(0.9F),
204 const std::function<glm::vec2()>& live_offset = {});
205
206// =============================================================================
207// Point
208//
209// Value is glm::vec2 in NDC space. Produces a single POINT_LIST vertex.
210// Hit region is a circle centered on the point.
211// Use as a cursor follower, node handle, or any positioned point primitive.
212// =============================================================================
213
214/**
215 * @brief Geometry function for a positioned point in NDC space.
216 *
217 * Value type is glm::vec2 (NDC position). Renders a single PointVertex
218 * and sets a circular hit region centered on the position. Suitable as
219 * a cursor follower, node handle, or any draggable point primitive.
220 *
221 * @param color Point color.
222 * @param size Point size in pixels.
223 * @param hit_radius Hit region radius in NDC units.
224 */
225[[nodiscard]] MAYAFLUX_API Form<glm::vec2> point(
226 glm::vec3 color = glm::vec3(1.0F),
227 float size = 10.0F,
228 float hit_radius = 0.04F);
229
230// =============================================================================
231// 2D position picker
232//
233// Value is glm::vec2 in [0,1]x[0,1], mapped to a point inside bounds.
234// Produces a single POINT_LIST vertex at the mapped position.
235// =============================================================================
236
237/**
238 * @brief Geometry function for a 2D position picker in NDC space.
239 * @param bounds Full extent of the pick area in NDC.
240 * @param color Point color.
241 * @param size Point size in pixels.
242 * @param live_offset Optional. Current translation of the drawn area.
243 */
244[[nodiscard]] MAYAFLUX_API Form<glm::vec2> position_picker(
245 Kinesis::AABB2D bounds,
246 glm::vec3 color = glm::vec3(0.9F),
247 float size = 8.0F,
248 const std::function<glm::vec2()>& live_offset = {});
249
250// =============================================================================
251// Stroke slider
252//
253// Value in [0, 1] positions a handle point along a user-supplied polyline.
254// Renders two layers: the full path as a LINE_LIST in track_color, a
255// highlighted prefix segment from path start to the handle position in
256// fill_color, and a PointVertex handle on a caller-supplied secondary buffer.
257//
258// The secondary buffer must be a POINT_LIST FormaBuffer registered with the
259// same BufferManager and window before this function is called. The caller
260// adds it as a separate Element and relates it to the path element via
261// Layer::relate_to so removal and visibility cascade.
262//
263// Hit region: stroke_bounds over the full path at half_thickness.
264// bounds_hint: tight AABB enclosing all path points.
265//
266// Topology for the path buffer: LINE_LIST.
267// Topology for the handle buffer: POINT_LIST.
268// =============================================================================
269
270/**
271 * @brief Geometry function for a value scrubber along an arbitrary polyline.
272 *
273 * Value in [0, 1] maps to arc-length position along @p path. The full path
274 * is rendered as a LINE_LIST in @p track_color; the prefix from the path
275 * start to the handle position is rendered in @p fill_color. The handle
276 * itself is a PointVertex submitted to @p handle_buf each sync.
277 *
278 * @param path Ordered polyline vertices in NDC. Copied into closure.
279 * @param handle_buf POINT_LIST FormaBuffer for the handle point.
280 * Must be registered and have setup_rendering called.
281 * @param half_thickness Hit region half-thickness in NDC units.
282 * @param track_color Color of the full path.
283 * @param fill_color Color of the prefix segment up to the handle.
284 * @param handle_color Color of the handle point.
285 * @param handle_size Handle point size in pixels.
286 * @param live_offset Optional. Current translation of the drawn path.
287 */
288[[nodiscard]] MAYAFLUX_API Form<float> stroke_slider(
289 std::span<const glm::vec2> path,
290 FormaTarget handle_buf,
291 float half_thickness = 0.02F,
292 glm::vec3 track_color = glm::vec3(0.3F),
293 glm::vec3 fill_color = glm::vec3(0.2F, 0.6F, 1.0F),
294 glm::vec3 handle_color = glm::vec3(0.95F),
295 float handle_size = 10.0F,
296 const std::function<glm::vec2()>& live_offset = {});
297
298// =============================================================================
299// Toggle
300//
301// Value type: bool. Renders a filled rect in one of two colors.
302// The geometry function is purely visual — it carries no interaction.
303// The caller wires on_press to flip state->value:
304//
305// surface.ctx().on_press(el.element.id, IO::MouseButtons::Left,
306// [state = el.state](uint32_t, glm::vec2) {
307// state->write(!state->value);
308// });
309//
310// Topology: TRIANGLE_STRIP (4 MeshVertex via to_mesh_vertices).
311// =============================================================================
312
313/**
314 * @brief Geometry function for a boolean toggle in NDC space.
315 *
316 * Renders @p region as a filled rect in @p color_off or @p color_on based
317 * on the current bool value. Interaction is the caller's responsibility.
318 *
319 * @param region NDC bounds of the toggle.
320 * @param color_off Fill color when false.
321 * @param color_on Fill color when true.
322 * @param live_offset Optional. Current translation of the drawn toggle -
323 * the wire (press_flip) does no coordinate math itself,
324 * so only the geometry needs this.
325 */
326[[nodiscard]] MAYAFLUX_API Form<bool> toggle(
327 Kinesis::AABB2D region,
328 glm::vec3 color_off = glm::vec3(0.25F),
329 glm::vec3 color_on = glm::vec3(0.2F, 0.7F, 0.4F),
330 const std::function<glm::vec2()>& live_offset = {});
331
332// =============================================================================
333// Level meter
334//
335// Value type: float in [0, 1]. Renders a filled bar from the origin edge of
336// bounds proportional to the value, with the remainder as a second color.
337// No handle, no hit region. Suitable for audio level, progress, any scalar readout.
338//
339// Orientation:
340// horizontal = true - bar grows left to right
341// horizontal = false - bar grows bottom to top
342//
343// Topology: TRIANGLE_STRIP (two quad pairs via to_mesh_vertices).
344// =============================================================================
345
346/**
347 * @brief Geometry function for a level meter in NDC space.
348 *
349 * No interaction. Drive from a node via bridge().at(el.state).bind(node).
350 *
351 * @param bounds Full extent of the meter in NDC.
352 * @param horizontal True for left-to-right fill, false for bottom-to-top.
353 * @param fill_color Color of the active (filled) portion.
354 * @param track_color Color of the inactive remainder.
355 * @param live_offset Optional. Current translation of the drawn meter.
356 */
357[[nodiscard]] MAYAFLUX_API Form<float> level_meter(
358 Kinesis::AABB2D bounds,
359 bool horizontal = true,
360 glm::vec3 fill_color = glm::vec3(0.2F, 0.7F, 0.3F),
361 glm::vec3 track_color = glm::vec3(0.15F),
362 const std::function<glm::vec2()>& live_offset = {});
363
364// =============================================================================
365// Crosshair
366//
367// Value type: glm::vec2 (NDC position). Renders two LINE_LIST segments —
368// horizontal and vertical — crossing at the value position.
369// Hit region: circle centered on the position.
370//
371// Pairs naturally with position_picker sharing the same MappedState<glm::vec2>.
372// Topology: LINE_LIST (4 LineVertex).
373// =============================================================================
374
375/**
376 * @brief Geometry function for a crosshair indicator in NDC space.
377 *
378 * @param arm_len Half-length of each arm in NDC units.
379 * @param color Line color.
380 * @param thickness Line thickness (maps to LineVertex::thickness).
381 * @param hit_radius Hit region radius in NDC units.
382 */
383[[nodiscard]] MAYAFLUX_API Form<glm::vec2> crosshair(
384 float arm_len = 0.04F,
385 glm::vec3 color = glm::vec3(0.9F),
386 float thickness = 1.F,
387 float hit_radius = 0.05F);
388
389// =============================================================================
390// Drawable canvas
391//
392// value is vector<float> of N samples in [0, 1] mapped to the Y axis.
393// X positions are distributed evenly across bounds.
394// Renders as LINE_LIST: N-1 segments connecting adjacent samples.
395//
396// on_drag callback pattern:
397// ctx->on_drag(el.element.id, IO::MouseButtons::Left,
398// [&el, bounds](uint32_t, glm::vec2 ndc) {
399// auto& v = el.state->value;
400// const float t = (ndc.x - bounds.min.x) / bounds.width();
401// const size_t i = static_cast<size_t>(
402// std::clamp(t, 0.F, 1.F) * (v.size() - 1));
403// const float a = (ndc.y - bounds.min.y) / bounds.height();
404// v[i] = std::clamp(a, 0.F, 1.F);
405// ++el.state->version;
406// });
407//
408// For sparse-sample prevention at high drag speed, interpolate between the
409// previous and current index before calling state->write().
410//
411// Topology: LINE_LIST.
412// =============================================================================
413
414/**
415 * @brief Geometry function for a drawable curve canvas in NDC space.
416 *
417 * Renders the sample vector as a LINE_LIST polyline. Each adjacent sample
418 * pair becomes one segment. The hit region covers the full canvas bounds.
419 *
420 * @param bounds Canvas extent in NDC.
421 * @param color Line color.
422 * @param thickness LineVertex thickness value.
423 * @param live_offset Optional. Current translation of the drawn canvas -
424 * geometry only. paint_over()/wire_canvas_drag() do
425 * their own inline cursor math, not routed through
426 * Kinesis::Projection, and are not offset-aware here.
427 * @param samples Sample count the canvas will hold. Sizes the buffer so
428 * the first draw does not reallocate. Zero keeps the
429 * default capacity.
430 */
431[[nodiscard]] MAYAFLUX_API Form<std::vector<float>> drawable_canvas(
432 Kinesis::AABB2D bounds,
433 glm::vec3 color = glm::vec3(0.8F),
434 float thickness = 1.5F,
435 const std::function<glm::vec2()>& live_offset = {},
436 size_t samples = 0);
437
438// =============================================================================
439// Scroll indicator
440//
441// Value type: glm::vec2, matching Scrollable::indicator()'s
442// (position_frac, extent_frac) contract exactly. x is 0 at the top of
443// content, 1 at the fully scrolled bottom; y is the viewport/content
444// height ratio. Renders a single filled bar within track: length is
445// extent_frac of track's height, position slides from track's top to its
446// bottom as position_frac goes 0 -> 1.
447//
448// bounds_hint is the full, fixed track rather than the (small, moving)
449// bar itself, so Scrollable::indicator()'s drag wiring accepts a grab
450// anywhere along the track, not just precisely on the current bar.
451//
452// The obvious default, not a privileged one: any GeometryFn<glm::vec2>
453// reacting to the same pair works identically with
454// Scrollable::indicator(). A squiggle, a sampled texture, anything
455// computable.
456//
457// Topology: TRIANGLE_STRIP (4 MeshVertex via to_mesh_vertices).
458// =============================================================================
459
460/**
461 * @brief Geometry function for Scrollable's default scroll indicator visual.
462 *
463 * @param track NDC region the bar travels within, the same region passed
464 * to Scrollable::indicator()'s own track parameter.
465 * @param color Bar fill color.
466 */
467[[nodiscard]] MAYAFLUX_API Form<glm::vec2> scroll_indicator(
468 Kinesis::AABB2D track,
469 glm::vec3 color = glm::vec3(0.5F, 0.5F, 0.55F));
470
471} // namespace MayaFlux::Portal::Forma::Geometry
glm::vec3 color
Definition Decoder.cpp:97
bool size
Illustrative geometry functions for common Mapped use cases.
NDC-to-value mappings, inverse to the forward placement performed by geometry functions.
Context & wire_drag(uint32_t id, std::function< void(glm::vec2)> sink, IO::MouseButtons btn=IO::MouseButtons::Left)
Register a drag handler receiving only the cursor position.
Definition Context.cpp:219
void on_move(uint32_t id, MoveFn fn)
Called each move event while the cursor is over an element.
Definition Context.cpp:48
void on_press(uint32_t id, IO::MouseButtons btn, PressFn fn)
Called when a mouse button is pressed over an element.
Definition Context.cpp:38
Event wiring between a Layer and a window surface.
Definition Context.hpp:41
MouseButtons
Enumeration for mouse buttons.
Definition Keys.hpp:147
Form< float > level_meter(Kinesis::AABB2D bounds, bool horizontal, glm::vec3 fill_color, glm::vec3 track_color, const std::function< glm::vec2()> &live_offset)
Geometry function for a level meter in NDC space.
Form< std::vector< float > > drawable_canvas(Kinesis::AABB2D bounds, glm::vec3 color, float thickness, const std::function< glm::vec2()> &live_offset, size_t samples)
Geometry function for a drawable curve canvas in NDC space.
Form< float > vertical_fader(Kinesis::AABB2D bounds, float handle_h, glm::vec3 track_color, glm::vec3 handle_color, const std::function< glm::vec2()> &live_offset)
Geometry function for a vertical fader in NDC space.
void wire_canvas_drag(Context &ctx, uint32_t id, const std::shared_ptr< MappedState< std::vector< float > > > &state, Kinesis::AABB2D bounds)
Wire drag interaction for a drawable canvas element.
auto follow_move()
Produce a Form::wire that writes the cursor position on hover.
Form< glm::vec2 > scroll_indicator(Kinesis::AABB2D track, glm::vec3 color)
Geometry function for Scrollable's default scroll indicator visual.
Form< float > radial(Kinesis::AABB2D region, float angle_start, float angle_end, glm::vec3 color, const std::function< glm::vec2()> &live_offset)
Geometry function for a radial indicator in NDC space.
auto paint_over(Kinesis::AABB2D bounds)
Produce a Form::wire that registers canvas painting over bounds.
Form< bool > toggle(Kinesis::AABB2D region, glm::vec3 color_off, glm::vec3 color_on, const std::function< glm::vec2()> &live_offset)
Geometry function for a boolean toggle in NDC space.
auto drag_with(std::function< T(glm::vec2)> project, IO::MouseButtons btn=IO::MouseButtons::Left)
Produce a Form::wire that registers a drag writing a projected cursor position.
Form< glm::vec2 > position_picker(Kinesis::AABB2D bounds, glm::vec3 color, float size, const std::function< glm::vec2()> &live_offset)
Geometry function for a 2D position picker in NDC space.
Form< float > horizontal_fader(Kinesis::AABB2D bounds, float handle_w, glm::vec3 track_color, glm::vec3 handle_color, const std::function< glm::vec2()> &live_offset)
Geometry function for a horizontal fader 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< float > stroke_slider(std::span< const glm::vec2 > path, FormaTarget handle_buf, float half_thickness, glm::vec3 track_color, glm::vec3 fill_color, glm::vec3 handle_color, float handle_size, const std::function< glm::vec2()> &live_offset)
Geometry function for a value scrubber along an arbitrary polyline.
void wire_drag(Context &ctx, uint32_t id, std::shared_ptr< MappedState< T > > state, std::function< T(glm::vec2)> project, IO::MouseButtons btn=IO::MouseButtons::Left)
Register a drag handler writing a projected cursor position to state.
Form< glm::vec2 > crosshair(float arm_len, glm::vec3 color, float thickness, float hit_radius)
Geometry function for a crosshair indicator in NDC space.
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
Value carrier for a Mapped primitive.
Definition Mapped.hpp:40