MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TabletNode.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "InputNode.hpp"
4
6
8
9/**
10 * @class TabletContext
11 * @brief Node context carrying a full tablet frame alongside the scalar.
12 *
13 * Every callback registered on a TabletNode receives this, so a threshold
14 * or range callback firing on one axis can read every other axis of the
15 * same hardware sample.
16 *
17 * The frame is a copy rather than a view. The InputValue it came from does
18 * not outlive the dispatch call, so a span would dangle.
19 */
20class MAYAFLUX_API TabletContext : public InputContext {
21public:
22 TabletContext(double value, double raw_value,
23 Core::InputType source, uint32_t device_id)
24 : InputContext(value, raw_value, source, device_id)
25 {
26 }
27
28 std::array<double, Core::TabletFrame::SLOT_COUNT> frame {};
29
30 /** @brief Read one slot of the frame. */
31 [[nodiscard]] double slot(size_t index) const
32 {
33 return index < frame.size() ? frame[index] : 0.0;
34 }
35
36 /** @brief Whether the tool is within sensing range. */
37 [[nodiscard]] bool in_proximity() const
38 {
39 return Core::TabletFrame::in_state(frame, Core::TabletState::IN_PROXIMITY);
40 }
41
42 /** @brief Whether the tool is touching the surface. */
43 [[nodiscard]] bool in_contact() const
44 {
45 return Core::TabletFrame::in_state(frame, Core::TabletState::IN_CONTACT);
46 }
47
48 /** @brief Test a tool button by bit index. */
49 [[nodiscard]] bool button(uint32_t bit) const
50 {
51 const auto mask = static_cast<uint32_t>(frame[Core::TabletFrame::BUTTONS]);
52 return (mask & (1U << bit)) != 0U;
53 }
54};
55
56/**
57 * @struct TabletConfig
58 * @brief Selects which part of a tablet frame becomes the node's scalar.
59 *
60 * One node projects one slot. Several nodes bound to the same tool receive
61 * the identical frame in one dispatch pass, so their outputs stay coherent
62 * without any coordination between them.
63 */
65 size_t slot { Core::TabletFrame::X }; ///< Frame slot driving the scalar
66
67 std::optional<uint32_t> button_bit; ///< When set, extract this BUTTONS bit
68 std::optional<Core::TabletState> state_bit; ///< When set, extract this STATE bit
69
70 /** @brief Horizontal position, 0 to 1. */
71 static TabletConfig position_x() { return {}; }
72
73 /** @brief Vertical position, 0 to 1. */
75 {
76 TabletConfig cfg;
78 return cfg;
79 }
80
81 /** @brief Tip pressure, 0 to 1. */
83 {
84 TabletConfig cfg;
86 return cfg;
87 }
88
89 /** @brief Hover height, 0 to 1. */
91 {
92 TabletConfig cfg;
94 return cfg;
95 }
96
97 /** @brief Tilt about the x axis, degrees. */
99 {
100 TabletConfig cfg;
102 return cfg;
103 }
104
105 /** @brief Tilt about the y axis, degrees. */
107 {
108 TabletConfig cfg;
110 return cfg;
111 }
112
113 /** @brief Barrel rotation, degrees. */
115 {
116 TabletConfig cfg;
118 return cfg;
119 }
120
121 /** @brief Barrel pressure or finger wheel, -1 to 1. */
123 {
124 TabletConfig cfg;
126 return cfg;
127 }
128
129 /** @brief Wheel rotation this frame, degrees. */
131 {
132 TabletConfig cfg;
135 return cfg;
136 }
137
138 /**
139 * @brief Tip contact as 0.0 or 1.0.
140 *
141 * Smoothing is disabled, since an interpolated contact state is not a
142 * meaningful quantity.
143 */
145 {
146 TabletConfig cfg;
150 return cfg;
151 }
152
153 /** @brief Proximity as 0.0 or 1.0, smoothing disabled. */
155 {
156 TabletConfig cfg;
160 return cfg;
161 }
162
163 /**
164 * @brief A tool button as 0.0 or 1.0, smoothing disabled.
165 * @param bit Bit 0 barrel, bit 1 secondary barrel, bit 2 eraser.
166 */
167 static TabletConfig button(uint32_t bit)
168 {
169 TabletConfig cfg;
171 cfg.button_bit = bit;
173 return cfg;
174 }
175
176 TabletConfig& at_slot(size_t index)
177 {
178 slot = index;
179 return *this;
180 }
181};
182
183/**
184 * @class TabletNode
185 * @brief InputNode projecting one slot of a tablet frame.
186 *
187 * Receives InputValue::Type::VECTOR frames from TabletBackend and outputs
188 * the configured slot as a smoothed scalar, while carrying the whole frame
189 * into every callback through TabletContext.
190 *
191 * Multi-axis use is several TabletNode instances bound to the same tool.
192 * InputManager hands each the same InputValue in one dispatch pass, so
193 * their frames are the same hardware sample.
194 *
195 * Contact and proximity edges fire regardless of which slot the node
196 * projects, since they are transitions of the frame rather than of the
197 * scalar.
198 *
199 * Example usage:
200 * @code
201 * auto tools = engine.input().get_backend(Core::InputType::TABLET)->get_devices();
202 *
203 * auto pressure = std::make_shared<TabletNode>(TabletConfig::pressure());
204 * register_input_node(pressure, InputBinding::tablet(tools[0].id));
205 *
206 * pressure->on_contact_begin([](TabletContext& ctx) {
207 * // stroke start, with position and tilt available
208 * });
209 *
210 * pressure->on_threshold_rising(0.5, [](InputContext& ctx) {
211 * // half pressure crossed
212 * });
213 * @endcode
214 */
215class MAYAFLUX_API TabletNode : public InputNode {
216public:
218
219 explicit TabletNode(TabletConfig config = {});
220
221 void save_state() override { }
222 void restore_state() override { }
223
224 NodeContext& get_last_context() override { return m_tablet_context; }
225
226 /**
227 * @brief Callback on every frame, with the full slot set.
228 */
229 void on_frame(FrameCallback callback)
230 {
231 m_frame_callbacks.push_back(std::move(callback));
232 }
233
234 /**
235 * @brief Callback when the tool enters sensing range.
236 */
238 {
239 m_proximity_enter_callbacks.push_back(std::move(callback));
240 }
241
242 /**
243 * @brief Callback when the tool leaves sensing range.
244 */
246 {
247 m_proximity_exit_callbacks.push_back(std::move(callback));
248 }
249
250 /**
251 * @brief Callback when the tip touches the surface.
252 */
254 {
255 m_contact_begin_callbacks.push_back(std::move(callback));
256 }
257
258 /**
259 * @brief Callback when the tip leaves the surface.
260 */
262 {
263 m_contact_end_callbacks.push_back(std::move(callback));
264 }
265
266 /**
267 * @brief Most recent frame received, all slots.
268 */
269 [[nodiscard]] std::array<double, Core::TabletFrame::SLOT_COUNT> get_frame() const
270 {
271 return m_tablet_context.frame;
272 }
273
274protected:
275 double extract_value(const Core::InputValue& value) override;
276
277 void update_context(double value) override;
278
279 void notify_tick(double value) override;
280
281private:
284
285 std::array<double, Core::TabletFrame::SLOT_COUNT> m_frame {};
286 uint32_t m_previous_state {};
287 std::atomic<bool> m_frame_pending { false };
288
289 std::vector<FrameCallback> m_frame_callbacks;
290 std::vector<FrameCallback> m_proximity_enter_callbacks;
291 std::vector<FrameCallback> m_proximity_exit_callbacks;
292 std::vector<FrameCallback> m_contact_begin_callbacks;
293 std::vector<FrameCallback> m_contact_end_callbacks;
294
295 void fire_edges();
296 void fire(const std::vector<FrameCallback>& callbacks);
297};
298
299} // namespace MayaFlux::Nodes::Input
float value
Context for InputNode callbacks - provides input event access.
Definition InputNode.hpp:38
Abstract base class for nodes that receive external input.
bool in_proximity() const
Whether the tool is within sensing range.
bool button(uint32_t bit) const
Test a tool button by bit index.
TabletContext(double value, double raw_value, Core::InputType source, uint32_t device_id)
double slot(size_t index) const
Read one slot of the frame.
bool in_contact() const
Whether the tool is touching the surface.
Node context carrying a full tablet frame alongside the scalar.
TypedHook< TabletContext > FrameCallback
std::vector< FrameCallback > m_proximity_enter_callbacks
void save_state() override
Saves the node's current state for later restoration Recursively cascades through all connected modul...
std::vector< FrameCallback > m_contact_begin_callbacks
std::vector< FrameCallback > m_proximity_exit_callbacks
NodeContext & get_last_context() override
Retrieves the last created context object.
std::vector< FrameCallback > m_frame_callbacks
void on_proximity_exit(FrameCallback callback)
Callback when the tool leaves sensing range.
void on_contact_begin(FrameCallback callback)
Callback when the tip touches the surface.
void on_contact_end(FrameCallback callback)
Callback when the tip leaves the surface.
void restore_state() override
Restores the node's state from the last save Recursively cascades through all connected modulator nod...
void on_proximity_enter(FrameCallback callback)
Callback when the tool enters sensing range.
std::array< double, Core::TabletFrame::SLOT_COUNT > get_frame() const
Most recent frame received, all slots.
std::vector< FrameCallback > m_contact_end_callbacks
void on_frame(FrameCallback callback)
Callback on every frame, with the full slot set.
InputNode projecting one slot of a tablet frame.
Base context class for node callbacks.
Definition Node.hpp:53
InputType
Input backend type enumeration.
@ NONE
No smoothing - immediate value changes (buttons)
std::function< void(ContextT &)> TypedHook
Callback function type for node processing events, parameterised on context type.
Definition NodeUtils.hpp:28
Generic input value container.
@ DISTANCE
0.0 to 1.0, hover height
@ ROTATION
-180.0 to 180.0 degrees
@ X
0.0 to 1.0 across the active area
@ STATE
TabletState bitmask.
@ TILT_Y
-90.0 to 90.0 degrees
@ WHEEL
Degrees of wheel rotation this sample.
@ SLIDER
-1.0 to 1.0, barrel pressure or finger wheel
@ Y
0.0 to 1.0 across the active area
@ TILT_X
-90.0 to 90.0 degrees
@ BUTTONS
Bit 0 barrel, bit 1 secondary barrel, bit 2 eraser.
Configuration for InputNode behavior.
Definition InputNode.hpp:67
static TabletConfig slider()
Barrel pressure or finger wheel, -1 to 1.
std::optional< uint32_t > button_bit
When set, extract this BUTTONS bit.
TabletConfig & at_slot(size_t index)
static TabletConfig tilt_y()
Tilt about the y axis, degrees.
static TabletConfig rotation()
Barrel rotation, degrees.
static TabletConfig position_x()
Horizontal position, 0 to 1.
std::optional< Core::TabletState > state_bit
When set, extract this STATE bit.
static TabletConfig pressure()
Tip pressure, 0 to 1.
static TabletConfig contact()
Tip contact as 0.0 or 1.0.
static TabletConfig button(uint32_t bit)
A tool button as 0.0 or 1.0, smoothing disabled.
size_t slot
Frame slot driving the scalar.
static TabletConfig position_y()
Vertical position, 0 to 1.
static TabletConfig wheel()
Wheel rotation this frame, degrees.
static TabletConfig tilt_x()
Tilt about the x axis, degrees.
static TabletConfig distance()
Hover height, 0 to 1.
static TabletConfig proximity()
Proximity as 0.0 or 1.0, smoothing disabled.
Selects which part of a tablet frame becomes the node's scalar.