1// Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qabstractphysicaldevicebackendnode_p.h"
5#include "qabstractphysicaldevicebackendnode_p_p.h"
6
7#include <Qt3DInput/qabstractphysicaldevice.h>
8#include <Qt3DInput/qaxissetting.h>
9#include <Qt3DInput/qinputaspect.h>
10
11#include <cmath>
12#include <algorithm>
13
14#include <Qt3DInput/private/inputhandler_p.h>
15#include <Qt3DInput/private/inputmanagers_p.h>
16#include <Qt3DInput/private/qinputaspect_p.h>
17#include <Qt3DCore/private/qabstractaspect_p.h>
18
19QT_BEGIN_NAMESPACE
20
21namespace {
22
23constexpr int signum(float v)
24{
25 return (v > 0.0f) - (v < 0.0f);
26}
27
28}
29
30namespace Qt3DInput {
31
32QAbstractPhysicalDeviceBackendNodePrivate::QAbstractPhysicalDeviceBackendNodePrivate(Qt3DCore::QBackendNode::Mode mode)
33 : Qt3DCore::QBackendNodePrivate(mode)
34 , m_axisSettings()
35 , m_inputAspect(nullptr)
36{
37}
38
39void QAbstractPhysicalDeviceBackendNodePrivate::addAxisSetting(int axisIdentifier, Qt3DCore::QNodeId axisSettingsId)
40{
41 Input::AxisIdSetting axisIdSetting;
42 axisIdSetting.m_axisIdentifier = axisIdentifier;
43 axisIdSetting.m_axisSettingsId = axisSettingsId;
44
45 // Replace if already present, otherwise append
46 const auto end = m_axisSettings.end();
47 for (auto it = m_axisSettings.begin(); it != end; ++it) {
48 if (it->m_axisIdentifier == axisIdentifier) {
49 *it = axisIdSetting;
50 return;
51 }
52 }
53 m_axisSettings.push_back(t: axisIdSetting);
54}
55
56void QAbstractPhysicalDeviceBackendNodePrivate::removeAxisSetting(Qt3DCore::QNodeId axisSettingsId)
57{
58 const auto end = m_axisSettings.end();
59 for (auto it = m_axisSettings.begin(); it != end; ++it) {
60 if (it->m_axisSettingsId == axisSettingsId) {
61 m_axisSettings.erase(pos: it);
62 return;
63 }
64 }
65}
66
67Input::MovingAverage &QAbstractPhysicalDeviceBackendNodePrivate::getOrCreateFilter(int axisIdentifier)
68{
69 const auto end = m_axisFilters.end();
70 for (auto it = m_axisFilters.begin(); it != end; ++it) {
71 if (it->m_axisIdentifier == axisIdentifier)
72 return it->m_filter;
73 }
74
75 Input::AxisIdFilter axisIdFilter;
76 axisIdFilter.m_axisIdentifier = axisIdentifier;
77 m_axisFilters.push_back(t: axisIdFilter);
78 return m_axisFilters.last().m_filter;
79}
80
81Input::AxisSetting *QAbstractPhysicalDeviceBackendNodePrivate::getAxisSetting(Qt3DCore::QNodeId axisSettingId) const
82{
83 Q_Q(const QAbstractPhysicalDeviceBackendNode);
84 QInputAspectPrivate *aspectPrivate = static_cast<QInputAspectPrivate *>(Qt3DCore::QAbstractAspectPrivate::get(aspect: q->inputAspect()));
85 Input::InputHandler *handler = aspectPrivate->m_inputHandler.data();
86 Input::AxisSetting *axisSetting = handler->axisSettingManager()->getOrCreateResource(id: axisSettingId);
87 return axisSetting;
88}
89
90QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QBackendNode::Mode mode)
91 : Input::BackendNode(*new QAbstractPhysicalDeviceBackendNodePrivate(mode))
92{
93}
94
95QAbstractPhysicalDeviceBackendNode::QAbstractPhysicalDeviceBackendNode(QAbstractPhysicalDeviceBackendNodePrivate &dd)
96 : Input::BackendNode(dd)
97{
98}
99
100void QAbstractPhysicalDeviceBackendNode::cleanup()
101{
102 Q_D(QAbstractPhysicalDeviceBackendNode);
103 QBackendNode::setEnabled(false);
104 d->m_axisSettings.clear();
105 d->m_axisFilters.clear();
106 d->m_inputAspect = nullptr;
107}
108
109void QAbstractPhysicalDeviceBackendNode::syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime)
110{
111 Q_D(QAbstractPhysicalDeviceBackendNode);
112 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
113 const Qt3DInput::QAbstractPhysicalDevice *node = qobject_cast<const Qt3DInput::QAbstractPhysicalDevice *>(object: frontEnd);
114 if (!node)
115 return;
116
117 auto settings = Qt3DCore::qIdsForNodes(nodes: node->axisSettings());
118 std::sort(first: std::begin(cont&: settings), last: std::end(cont&: settings));
119 Qt3DCore::QNodeIdVector addedSettings;
120 Qt3DCore::QNodeIdVector removedSettings;
121 std::set_difference(first1: std::begin(cont&: settings), last1: std::end(cont&: settings),
122 first2: std::begin(cont&: d->m_currentAxisSettingIds), last2: std::end(cont&: d->m_currentAxisSettingIds),
123 result: std::inserter(x&: addedSettings, i: addedSettings.end()));
124 std::set_difference(first1: std::begin(cont&: d->m_currentAxisSettingIds), last1: std::end(cont&: d->m_currentAxisSettingIds),
125 first2: std::begin(cont&: settings), last2: std::end(cont&: settings),
126 result: std::inserter(x&: removedSettings, i: removedSettings.end()));
127 d->m_currentAxisSettingIds = settings;
128
129 for (const auto &axisSettingId: std::as_const(t&: addedSettings)) {
130 Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId);
131 const auto axisIds = axisSetting->axes();
132 for (int axisId : axisIds)
133 d->addAxisSetting(axisIdentifier: axisId, axisSettingsId: axisSettingId);
134 }
135 for (const auto &axisSettingId: std::as_const(t&: removedSettings))
136 d->removeAxisSetting(axisSettingsId: axisSettingId);
137}
138
139void QAbstractPhysicalDeviceBackendNode::setInputAspect(QInputAspect *aspect)
140{
141 Q_D(QAbstractPhysicalDeviceBackendNode);
142 d->m_inputAspect = aspect;
143}
144
145QInputAspect *QAbstractPhysicalDeviceBackendNode::inputAspect() const
146{
147 Q_D(const QAbstractPhysicalDeviceBackendNode);
148 return d->m_inputAspect;
149}
150
151float QAbstractPhysicalDeviceBackendNode::processedAxisValue(int axisIdentifier)
152{
153 Q_D(QAbstractPhysicalDeviceBackendNode);
154
155 // Find axis settings for this axis (if any)
156 Qt3DCore::QNodeId axisSettingId;
157 const auto end = d->m_axisSettings.cend();
158 for (auto it = d->m_axisSettings.cbegin(); it != end; ++it) {
159 if (it->m_axisIdentifier == axisIdentifier) {
160 axisSettingId = it->m_axisSettingsId;
161 break;
162 }
163 }
164
165 const float rawAxisValue = axisValue(axisIdentifier);
166 if (axisSettingId.isNull()) {
167 // No special processing. Just return the raw value
168 return rawAxisValue;
169 } else {
170 // Process the raw value in accordance with the settings
171 Input::AxisSetting *axisSetting = d->getAxisSetting(axisSettingId);
172 Q_ASSERT(axisSetting);
173 float val = rawAxisValue;
174
175 // Low pass smoothing
176 if (axisSetting->isSmoothEnabled()) {
177 // Get the filter corresponding to this axis
178 Input::MovingAverage &filter = d->getOrCreateFilter(axisIdentifier);
179 filter.addSample(sample: val);
180 val = filter.average();
181 }
182
183 // Deadzone handling
184 const float d = axisSetting->deadZoneRadius();
185 if (!qFuzzyIsNull(f: d)) {
186 if (std::abs(x: val) <= d) {
187 val = 0.0f;
188 } else {
189 // Calculate value that goes from 0 to 1 linearly from the boundary of
190 // the dead zone up to 1. That is we with a dead zone value of d, we do not
191 // want a step change from 0 to d when the axis leaves the deadzone. Instead
192 // we want to increase the gradient of the line so that it goes from 0 to 1
193 // over the range d to 1. So instead of having y = x, the equation of the
194 // line becomes
195 //
196 // y = x / (1-d) - d / (1-d) = (x - d) / (1 - d)
197 //
198 // for positive values, and
199 //
200 // y = x / (1-d) + d / (1-d) = (x + d) / (1 - d)
201 //
202 // for negative values.
203 val = (val - signum(v: val) * d) / (1.0f - d);
204 }
205 }
206
207 return val;
208 }
209
210}
211
212} // Qt3DInput
213
214QT_END_NAMESPACE
215

source code of qt3d/src/input/backend/qabstractphysicaldevicebackendnode.cpp