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