Working on conditional and filter parsing

This commit is contained in:
Brad Davis 2015-10-21 14:31:22 -07:00
parent 58e5bff9a1
commit b9b03bd842
15 changed files with 453 additions and 211 deletions

View file

@ -1,10 +1,10 @@
{
"name": "Hydra to Standard",
"channels": [
{ "from": "Hydra.LY", "to": "Standard.LY" },
{ "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
{ "from": "Hydra.LX", "to": "Standard.LX" },
{ "from": "Hydra.LT", "to": "Standard.LT" },
{ "from": "Hydra.RY", "to": "Standard.RY" },
{ "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "Hydra.RX", "to": "Standard.RX" },
{ "from": "Hydra.RT", "to": "Standard.RT" },

View file

@ -0,0 +1,43 @@
{
"name": "Standard to Action",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "to": "Actions.Pitch" },
{
"from": "Standard.DU",
"to": "Actions.LONGITUDINAL_FORWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DD",
"to": "Actions.LONGITUDINAL_BACKWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DR",
"to": "Actions.LATERAL_RIGHT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DL",
"to": "Actions.LATERAL_LEFT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
{
"from": "Standard.RT",
"to": "Actions.BOOM_IN",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{
"from": "Standard.LT",
"to": "Actions.BOOM_OUT",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
]
}

View file

@ -5,38 +5,25 @@
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "to": "Actions.Pitch" },
{
"from": "Standard.DU",
"to": "Actions.LONGITUDINAL_FORWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DD",
"to": "Actions.LONGITUDINAL_BACKWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DR",
"to": "Actions.LATERAL_RIGHT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DL",
"to": "Actions.LATERAL_LEFT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
{ "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
{ "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" },
{ "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
{ "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" },
{
"from": "Standard.RT",
"to": "Actions.BOOM_IN",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{
"from": "Standard.LT",
"to": "Actions.BOOM_OUT",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
]

View file

@ -183,32 +183,33 @@ HifiControls.VrDialog {
Xbox { device: root.xbox; label: "XBox"; width: 360 }
Hydra { device: root.hydra; width: 360 }
}
// Row {
// spacing: 8
// ScrollingGraph {
// controlId: Controller.Actions.Yaw
// label: "Yaw"
// min: -3.0
// max: 3.0
// size: 128
// }
//
// ScrollingGraph {
// controlId: Controller.Actions.YAW_LEFT
// label: "Yaw Left"
// min: -3.0
// max: 3.0
// size: 128
// }
//
// ScrollingGraph {
// controlId: Controller.Actions.YAW_RIGHT
// label: "Yaw Right"
// min: -3.0
// max: 3.0
// size: 128
// }
// }
Row {
spacing: 8
ScrollingGraph {
controlId: Controller.Actions.Yaw
label: "Yaw"
min: -3.0
max: 3.0
size: 128
}
ScrollingGraph {
controlId: Controller.Actions.YAW_LEFT
label: "Yaw Left"
min: -3.0
max: 3.0
size: 128
}
ScrollingGraph {
controlId: Controller.Actions.YAW_RIGHT
label: "Yaw Right"
min: -3.0
max: 3.0
size: 128
}
}
}
} // dialog

View file

@ -13,7 +13,7 @@ Item {
property color color: 'black'
function update() {
value = Controller.getValue(controlId);
value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint();
}

View file

@ -17,8 +17,8 @@ Item {
function update() {
value = Qt.vector2d(
Controller.getValue(controlIds[0]),
Controller.getValue(controlIds[1])
controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
controlIds[1] ? Controller.getValue(controlIds[1]) : 0
);
if (root.invertY) {
value.y = value.y * -1.0
@ -27,7 +27,7 @@ Item {
}
Timer {
interval: 50; running: true; repeat: true
interval: 50; running: controlIds; repeat: true
onTriggered: root.update()
}

View file

@ -12,12 +12,14 @@ Item {
property real value: 0
property color color: 'black'
function update() {
value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint();
}
Timer {
interval: 50; running: true; repeat: true
onTriggered: {
root.value = Controller.getValue(root.controlId);
canvas.requestPaint();
}
interval: 50; running: root.controlId; repeat: true
onTriggered: root.update()
}
Canvas {

View file

@ -100,5 +100,20 @@ Item {
width: 16 * root.scale; height: 12 * root.scale
x: (177 * root.scale); y: (45 * root.scale)
}
// Left primary
ToggleButton {
x: 0; y: parent.height - height;
controlId: root.device.LeftPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
// Left primary
ToggleButton {
x: parent.width - width; y: parent.height - height;
controlId: root.device.RightPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
}
}

View file

@ -18,14 +18,4 @@ namespace controller {
return Conditional::Pointer();
}
bool EndpointConditional::satisfied() {
if (!_endpoint) {
return false;
}
auto value = _endpoint->value();
if (value == 0.0f) {
return false;
}
return true;
}
}

View file

@ -17,8 +17,6 @@
#include <shared/Factory.h>
#include "Endpoint.h"
class QJsonValue;
namespace controller {
@ -41,14 +39,6 @@ namespace controller {
static Factory _factory;
};
class EndpointConditional : public Conditional {
public:
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) { }
virtual bool satisfied() override;
private:
Endpoint::Pointer _endpoint;
};
}
#define REGISTER_CONDITIONAL_CLASS(classEntry) \

View file

@ -40,9 +40,12 @@ namespace controller {
virtual void apply(float newValue, float oldValue, const Pointer& source) = 0;
virtual Pose pose() { return Pose(); }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {}
virtual const bool isPose() { return _input.isPose(); }
virtual bool writeable() const { return true; }
virtual bool readable() const { return true; }
virtual void reset() { }
const Input& getInput() { return _input; }
protected:
@ -61,6 +64,26 @@ namespace controller {
ReadLambda _readLambda;
WriteLambda _writeLambda;
};
class VirtualEndpoint : public Endpoint {
public:
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
: Endpoint(id) {
}
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
virtual Pose pose() override { return _currentPose; }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
_currentPose = newValue;
}
protected:
float _currentValue { 0.0f };
Pose _currentPose {};
};
}
#endif

View file

@ -55,6 +55,7 @@ struct Input {
Input(const Input& src) : id(src.id) {}
Input& operator = (const Input& src) { id = src.id; return (*this); }
bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; }
bool operator !=(const Input& right) const { return !(*this == right); }
bool operator < (const Input& src) const { return id < src.id; }
static const Input INVALID_INPUT;

View file

@ -30,6 +30,7 @@ namespace controller {
Pose(const Pose&) = default;
Pose& operator = (const Pose&) = default;
bool operator ==(const Pose& right) const;
bool operator !=(const Pose& right) const { return !(*this == right); }
bool isValid() const { return valid; }
vec3 getTranslation() const { return translation; }
quat getRotation() const { return rotation; }

View file

@ -55,22 +55,45 @@ private:
float _lastValue = 0.0f;
};
class VirtualEndpoint : public Endpoint {
class StandardEndpoint : public VirtualEndpoint {
public:
VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
: Endpoint(id) {
StandardEndpoint(const Input& input) : VirtualEndpoint(input) {}
virtual bool writeable() const override { return !_written; }
virtual bool readable() const override { return !_read; }
virtual void reset() override {
apply(0.0f, 0.0f, Endpoint::Pointer());
apply(Pose(), Pose(), Endpoint::Pointer());
_written = _read = false;
}
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; }
virtual float value() override {
_read = true;
return VirtualEndpoint::value();
}
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
// For standard endpoints, the first NON-ZERO write counts.
if (newValue != 0.0) {
_written = true;
}
VirtualEndpoint::apply(newValue, oldValue, source);
}
virtual Pose pose() override {
_read = true;
return VirtualEndpoint::pose();
}
virtual Pose pose() override { return _currentPose; }
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
_currentPose = newValue;
if (newValue != Pose()) {
_written = true;
}
VirtualEndpoint::apply(newValue, oldValue, source);
}
private:
float _currentValue{ 0.0f };
Pose _currentPose{};
bool _written { false };
bool _read { false };
};
@ -136,12 +159,67 @@ public:
virtual void apply(float newValue, float oldValue, const Pointer& source) {
// Composites are read only
}
private:
Endpoint::Pointer _first;
Endpoint::Pointer _second;
};
class ArrayEndpoint : public Endpoint {
friend class UserInputMapper;
public:
using Pointer = std::shared_ptr<ArrayEndpoint>;
ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { }
virtual float value() override {
return 0.0;
}
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
for (auto& child : _children) {
if (child->writeable()) {
child->apply(newValue, oldValue, source);
}
}
}
virtual bool readable() const override { return false; }
private:
Endpoint::List _children;
};
class AnyEndpoint : public Endpoint {
friend class UserInputMapper;
public:
using Pointer = std::shared_ptr<AnyEndpoint>;
AnyEndpoint() : Endpoint(Input::INVALID_INPUT) {}
virtual float value() override {
float result = 0;
for (auto& child : _children) {
float childResult = child->value();
if (childResult != 0.0f) {
result = childResult;
}
}
return result;
}
virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override {
qFatal("AnyEndpoint is read only");
}
virtual bool writeable() const override { return false; }
virtual bool readable() const override {
for (auto& child : _children) {
if (!child->readable()) {
return false;
}
}
return true;
}
private:
Endpoint::List _children;
};
class InputEndpoint : public Endpoint {
public:
@ -150,40 +228,44 @@ public:
}
virtual float value() override {
_currentValue = 0.0f;
_read = true;
if (isPose()) {
return _currentValue;
return pose().valid ? 1.0f : 0.0f;
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
if (!deviceProxy) {
return _currentValue;
return 0.0f;
}
_currentValue = deviceProxy->getValue(_input, 0);
return _currentValue;
return deviceProxy->getValue(_input, 0);
}
// FIXME need support for writing back to vibration / force feedback effects
virtual void apply(float newValue, float oldValue, const Pointer& source) override {}
virtual Pose pose() override {
_currentPose = Pose();
_read = true;
if (!isPose()) {
return _currentPose;
return Pose();
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
auto deviceProxy = userInputMapper->getDeviceProxy(_input);
if (!deviceProxy) {
return _currentPose;
return Pose();
}
_currentPose = deviceProxy->getPose(_input, 0);
return _currentPose;
return deviceProxy->getPose(_input, 0);
}
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override {
}
virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { }
virtual bool writeable() const { return !_written; }
virtual bool readable() const { return !_read; }
virtual void reset() { _written = _read = false; }
private:
float _currentValue{ 0.0f };
Pose _currentPose{};
bool _written { false };
bool _read { false };
};
class ActionEndpoint : public Endpoint {
@ -194,9 +276,8 @@ public:
virtual float value() override { return _currentValue; }
virtual void apply(float newValue, float oldValue, const Pointer& source) override {
_currentValue += newValue;
if (!(_input == Input::INVALID_INPUT)) {
if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
}
@ -208,12 +289,17 @@ public:
if (!_currentPose.isValid()) {
return;
}
if (!(_input == Input::INVALID_INPUT)) {
if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
}
}
virtual void reset() override {
_currentValue = 0.0f;
_currentPose = Pose();
}
private:
float _currentValue{ 0.0f };
Pose _currentPose{};
@ -254,7 +340,7 @@ void UserInputMapper::registerDevice(InputDevice* device) {
}
Endpoint::Pointer endpoint;
if (input.device == STANDARD_DEVICE) {
endpoint = std::make_shared<VirtualEndpoint>(input);
endpoint = std::make_shared<StandardEndpoint>(input);
} else if (input.device == ACTIONS_DEVICE) {
endpoint = std::make_shared<ActionEndpoint>(input);
} else {
@ -396,20 +482,7 @@ void UserInputMapper::update(float deltaTime) {
}
// Run the mappings code
update();
// Scale all the channel step with the scale
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
if (_externalActionStates[i] != 0) {
_actionStates[i] += _externalActionStates[i];
_externalActionStates[i] = 0.0f;
}
if (_externalPoseStates[i].isValid()) {
_poseStates[i] = _externalPoseStates[i];
_externalPoseStates[i] = Pose();
}
}
runMappings();
// merge the bisected and non-bisected axes for now
fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]);
@ -419,7 +492,6 @@ void UserInputMapper::update(float deltaTime) {
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]);
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
static const float EPSILON = 0.01f;
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
_actionStates[i] *= _actionScales[i];
@ -561,53 +633,62 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
}
void UserInputMapper::update() {
void UserInputMapper::runMappings() {
static auto deviceNames = getDeviceNames();
_overrideValues.clear();
_overrides.clear();
EndpointSet readEndpoints;
EndpointSet writtenEndpoints;
for (auto endpointEntry : this->_endpointsByInput) {
endpointEntry.second->reset();
}
// Now process the current values for each level of the stack
for (auto& mapping : _activeMappings) {
for (const auto& route : mapping->routes) {
const auto& source = route->source;
// Endpoints can only be read once (though a given mapping can route them to
// multiple places). Consider... If the default is to wire the A button to JUMP
// and someone else wires it to CONTEXT_MENU, I don't want both to occur when
// I press the button. The exception is if I'm wiring a control back to itself
// in order to adjust my interface, like inverting the Y axis on an analog stick
if (readEndpoints.count(source)) {
continue;
}
const auto& destination = route->destination;
// THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) {
continue;
}
if (writtenEndpoints.count(destination)) {
continue;
}
if (route->conditional) {
if (!route->conditional->satisfied()) {
continue;
}
}
// Standard controller destinations can only be can only be used once.
if (getStandardDeviceID() == destination->getInput().getDevice()) {
writtenEndpoints.insert(destination);
auto source = route->source;
if (_overrides.count(source)) {
source = _overrides[source];
}
// Endpoints can only be read once (though a given mapping can route them to
// multiple places). Consider... If the default is to wire the A button to JUMP
// and someone else wires it to CONTEXT_MENU, I don't want both to occur when
// I press the button. The exception is if I'm wiring a control back to itself
// in order to adjust my interface, like inverting the Y axis on an analog stick
if (!source->readable()) {
continue;
}
auto input = source->getInput();
float value = source->value();
if (value != 0.0) {
int i = 0;
}
auto destination = route->destination;
// THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) {
continue;
}
// FIXME?, should come before or after the override logic?
if (!destination->writeable()) {
continue;
}
// Only consume the input if the route isn't a loopback.
// This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);`
bool loopback = source == destination;
if (!loopback) {
readEndpoints.insert(source);
bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT);
// Each time we loop back we re-write the override
if (loopback) {
_overrides[source] = destination = std::make_shared<StandardEndpoint>(source->getInput());
}
// Fetch the value, may have been overriden by previous loopback routes
@ -624,14 +705,11 @@ void UserInputMapper::update() {
value = filter->apply(value);
}
if (loopback) {
_overrideValues[source] = value;
} else {
destination->apply(value, 0, source);
}
destination->apply(value, 0, source);
}
}
}
}
Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
@ -741,9 +819,9 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
}
float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const {
auto valuesIterator = _overrideValues.find(endpoint);
if (_overrideValues.end() != valuesIterator) {
return valuesIterator->second;
auto valuesIterator = _overrides.find(endpoint);
if (_overrides.end() != valuesIterator) {
return valuesIterator->second->value();
}
return endpoint->value();
@ -787,27 +865,70 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
return parseMapping(json);
}
const QString JSON_NAME = QStringLiteral("name");
const QString JSON_CHANNELS = QStringLiteral("channels");
const QString JSON_CHANNEL_FROM = QStringLiteral("from");
const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
const QString JSON_CHANNEL_TO = QStringLiteral("to");
const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
static const QString JSON_NAME = QStringLiteral("name");
static const QString JSON_CHANNELS = QStringLiteral("channels");
static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
static const QString JSON_CHANNEL_TO = QStringLiteral("to");
static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
Endpoint::Pointer result;
if (value.isString()) {
auto input = findDeviceInput(value.toString());
return endpointFor(input);
result = endpointFor(input);
} else if (value.isObject()) {
// Endpoint is defined as an object, we expect a js function then
return Endpoint::Pointer();
}
return Endpoint::Pointer();
if (!result) {
qWarning() << "Invalid endpoint definition " << value;
}
return result;
}
class AndConditional : public Conditional {
public:
using Pointer = std::shared_ptr<AndConditional>;
AndConditional(Conditional::List children) : _children(children) { }
virtual bool satisfied() override {
for (auto& conditional : _children) {
if (!conditional->satisfied()) {
return false;
}
}
return true;
}
private:
Conditional::List _children;
};
class EndpointConditional : public Conditional {
public:
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; }
private:
Endpoint::Pointer _endpoint;
};
Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) {
if (value.isString()) {
if (value.isArray()) {
// Support "when" : [ "GamePad.RB", "GamePad.LB" ]
Conditional::List children;
for (auto arrayItem : value.toArray()) {
Conditional::Pointer childConditional = parseConditional(arrayItem);
if (!childConditional) {
return Conditional::Pointer();
}
children.push_back(childConditional);
}
return std::make_shared<AndConditional>(children);
} else if (value.isString()) {
// Support "when" : "GamePad.RB"
auto input = findDeviceInput(value.toString());
auto endpoint = endpointFor(input);
if (!endpoint) {
@ -815,11 +936,85 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value)
}
return std::make_shared<EndpointConditional>(endpoint);
}
}
return Conditional::parse(value);
}
Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) {
Filter::Pointer result;
if (value.isString()) {
result = Filter::getFactory().create(value.toString());
} else if (value.isObject()) {
result = Filter::parse(value.toObject());
}
if (!result) {
qWarning() << "Invalid filter definition " << value;
}
return result;
}
Filter::List UserInputMapper::parseFilters(const QJsonValue& value) {
if (value.isNull()) {
return Filter::List();
}
if (value.isArray()) {
Filter::List result;
auto filtersArray = value.toArray();
for (auto filterValue : filtersArray) {
Filter::Pointer filter = parseFilter(filterValue);
if (!filter) {
return Filter::List();
}
result.push_back(filter);
}
return result;
}
Filter::Pointer filter = parseFilter(value);
if (!filter) {
return Filter::List();
}
return Filter::List({ filter });
}
Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) {
if (value.isArray()) {
ArrayEndpoint::Pointer result = std::make_shared<ArrayEndpoint>();
for (auto arrayItem : value.toArray()) {
Endpoint::Pointer destination = parseEndpoint(arrayItem);
if (!destination) {
return Endpoint::Pointer();
}
result->_children.push_back(destination);
}
return result;
}
return parseEndpoint(value);
}
Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) {
if (value.isArray()) {
AnyEndpoint::Pointer result = std::make_shared<AnyEndpoint>();
for (auto arrayItem : value.toArray()) {
Endpoint::Pointer destination = parseEndpoint(arrayItem);
if (!destination) {
return Endpoint::Pointer();
}
result->_children.push_back(destination);
}
return result;
}
return parseEndpoint(value);
}
Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
if (!value.isObject()) {
return Route::Pointer();
@ -827,47 +1022,37 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
const auto& obj = value.toObject();
Route::Pointer result = std::make_shared<Route>();
result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]);
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
if (!result->source) {
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
return Route::Pointer();
}
result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]);
result->destination = parseDestination(obj[JSON_CHANNEL_TO]);
if (!result->destination) {
qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
return Route::Pointer();
}
if (obj.contains(JSON_CHANNEL_WHEN)) {
auto when = parseConditional(obj[JSON_CHANNEL_WHEN]);
if (!when) {
qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO];
auto conditionalsValue = obj[JSON_CHANNEL_WHEN];
result->conditional = parseConditional(conditionalsValue);
if (!result->conditional) {
qWarning() << "Invalid route conditionals " << conditionalsValue;
return Route::Pointer();
}
result->conditional = when;
}
const auto& filtersValue = obj[JSON_CHANNEL_FILTERS];
// FIXME support strings for filters with no parameters, both in the array and at the top level...
// i.e.
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" },
// and
// { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] },
if (filtersValue.isArray()) {
auto filtersArray = filtersValue.toArray();
for (auto filterValue : filtersArray) {
if (!filterValue.isObject()) {
qWarning() << "Invalid filter " << filterValue;
return Route::Pointer();
}
Filter::Pointer filter = Filter::parse(filterValue.toObject());
if (!filter) {
qWarning() << "Invalid filter " << filterValue;
return Route::Pointer();
}
result->filters.push_back(filter);
if (obj.contains(JSON_CHANNEL_FILTERS)) {
auto filtersValue = obj[JSON_CHANNEL_FILTERS];
result->filters = parseFilters(filtersValue);
if (result->filters.empty()) {
qWarning() << "Invalid route filters " << filtersValue;
return Route::Pointer();
}
}
return result;
}

View file

@ -50,7 +50,7 @@ namespace controller {
using MappingStack = std::list<Mapping::Pointer>;
using InputToEndpointMap = std::map<Input, Endpoint::Pointer>;
using EndpointSet = std::unordered_set<Endpoint::Pointer>;
using ValueMap = std::map<Endpoint::Pointer, float>;
using EndpointOverrideMap = std::map<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPair = std::pair<Endpoint::Pointer, Endpoint::Pointer>;
using EndpointPairMap = std::map<EndpointPair, Endpoint::Pointer>;
using DevicesMap = std::map<int, DeviceProxy::Pointer>;
@ -86,9 +86,9 @@ namespace controller {
int findAction(const QString& actionName) const;
QVector<QString> getActionNames() const;
void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; }
void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; }
void setActionState(Action action, const Pose& value) { _externalPoseStates[toInt(action)] = value; }
void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
static Input makeStandardInput(controller::StandardButtonChannel button);
static Input makeStandardInput(controller::StandardAxisChannel axis);
@ -119,7 +119,7 @@ namespace controller {
void hardwareChanged();
protected:
virtual void update();
virtual void runMappings();
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
@ -128,11 +128,9 @@ namespace controller {
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<float> _externalActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f);
std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
std::vector<Pose> _externalPoseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
glm::mat4 _sensorToWorldMat;
@ -148,16 +146,22 @@ namespace controller {
Endpoint::Pointer endpointFor(const QScriptValue& endpoint);
Endpoint::Pointer endpointFor(const Input& endpoint) const;
Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second);
Mapping::Pointer parseMapping(const QJsonValue& json);
Route::Pointer parseRoute(const QJsonValue& value);
Conditional::Pointer parseConditional(const QJsonValue& value);
Endpoint::Pointer parseDestination(const QJsonValue& value);
Endpoint::Pointer parseSource(const QJsonValue& value);
Endpoint::Pointer parseEndpoint(const QJsonValue& value);
Conditional::Pointer parseConditional(const QJsonValue& value);
static Filter::Pointer parseFilter(const QJsonValue& value);
static Filter::List parseFilters(const QJsonValue& value);
InputToEndpointMap _endpointsByInput;
EndpointToInputMap _inputsByEndpoint;
EndpointPairMap _compositeEndpoints;
ValueMap _overrideValues;
EndpointOverrideMap _overrides;
MappingNameMap _mappingsByName;
Mapping::Pointer _defaultMapping{ std::make_shared<Mapping>("Default") };
MappingDeviceMap _mappingsByDevice;