Get json creating the mappings from js

This commit is contained in:
samcake 2015-10-14 17:41:39 -07:00
parent 1302b6a238
commit 12e103c90c
8 changed files with 275 additions and 114 deletions

View file

@ -0,0 +1,71 @@
//
// controllerScriptingExamples.js
// examples
//
// Created by Sam Gondelman on 6/2/15
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Assumes you only have the default keyboard connected
myFirstMapping = function() {
return {
"name": "example mapping from Standard to actions",
"channels": [ {
"from": "Keyboard.A",
"filters": [ {
"type": "clamp",
"params": [0, 1],
}
],
"to": "Actions.LONGITUDINAL_FORWARD",
}, {
"from": "Keyboard.Left",
"filters": [ {
"type": "clamp",
"params": [0, 1],
}, {
"type": "invert"
}
],
"to": "Actions.LONGITUDINAL_BACKWARD",
}, {
"from": "Keyboard.C",
"filters": [ {
"type": "scale",
"params": [2.0],
}
],
"to": "Actions.Yaw",
}, {
"from": "Keyboard.B",
"to": "Actions.Yaw"
}
]
}
}
//Script.include('mapping-test0.json');
var myFirstMappingJSON = myFirstMapping();
print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON));
var mapping = NewControllers.parseMapping(JSON.stringify(myFirstMappingJSON));
Object.keys(Controller.Standard).forEach(function (input) {
print("Controller.Standard." + input + ":" + Controller.Standard[input]);
});
Object.keys(Controller.Hardware).forEach(function (deviceName) {
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
});
});
Object.keys(Controller.Actions).forEach(function (actionName) {
print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]);
});

View file

@ -44,7 +44,7 @@ Filter::Pointer Filter::parse(const QJsonObject& json) {
return Filter::Pointer();
}
Filter::Factory::ClassEntry<ScaleFilter> ScaleFilter::_factoryEntry;
ScaleFilter::FactoryEntryBuilder ScaleFilter::_factoryEntryBuilder;
bool ScaleFilter::parseParameters(const QJsonArray& parameters) {
if (parameters.size() > 1) {
@ -53,7 +53,39 @@ bool ScaleFilter::parseParameters(const QJsonArray& parameters) {
return true;
}
Filter::Factory::ClassEntry<PulseFilter> PulseFilter::_factoryEntry;
InvertFilter::FactoryEntryBuilder InvertFilter::_factoryEntryBuilder;
ClampFilter::FactoryEntryBuilder ClampFilter::_factoryEntryBuilder;
bool ClampFilter::parseParameters(const QJsonArray& parameters) {
if (parameters.size() > 1) {
_min = parameters[0].toDouble();
}
if (parameters.size() > 2) {
_max = parameters[1].toDouble();
}
return true;
}
DeadZoneFilter::FactoryEntryBuilder DeadZoneFilter::_factoryEntryBuilder;
float DeadZoneFilter::apply(float value) const {
float scale = 1.0f / (1.0f - _min);
if (abs(value) < _min) {
return 0.0f;
}
return (value - _min) * scale;
}
bool DeadZoneFilter::parseParameters(const QJsonArray& parameters) {
if (parameters.size() > 1) {
_min = parameters[0].toDouble();
}
return true;
}
PulseFilter::FactoryEntryBuilder PulseFilter::_factoryEntryBuilder;
float PulseFilter::apply(float value) const {
@ -72,5 +104,13 @@ float PulseFilter::apply(float value) const {
}
bool PulseFilter::parseParameters(const QJsonArray& parameters) {
return false;
if (parameters.size() > 1) {
_interval = parameters[0].toDouble();
}
return true;
}
ConstrainToIntegerFilter::FactoryEntryBuilder ConstrainToIntegerFilter::_factoryEntryBuilder;
ConstrainToPositiveIntegerFilter::FactoryEntryBuilder ConstrainToPositiveIntegerFilter::_factoryEntryBuilder;

View file

@ -16,6 +16,8 @@
#include <functional>
#include <map>
#include <GLMHelpers.h>
#include <QtCore/QEasingCurve>
class QJsonObject;
@ -23,40 +25,7 @@ class QJsonArray;
namespace controller {
/*
template <class T> class Factory {
public:
template <class T> class Entry {
public:
virtual T* create() = 0;
};
template <class T, class S> class DefaultEntry{
public:
T* create() { return new S(); }
};
using EntryMap = std::map<std::string, std::unique_ptr<Entry<T>>>;
void registerEntry(const std::string& name, std::unique_ptr<Entry<T>>& entry) {
if (entry) {
_entries[name] = entry;
}
}
T* create(const std::string& name) const {
auto& entryIt = _entries.find(name);
if (entryIt != _entries.end()) {
return (*entryIt).second->create();
}
return nullptr;
}
protected:
EntryMap _entries;
};
*/
// Encapsulates part of a filter chain
class Filter {
public:
@ -67,36 +36,40 @@ namespace controller {
using Lambda = std::function<float(float)>;
// Factory features
virtual bool parseParameters(const QJsonArray& parameters) = 0;
virtual bool parseParameters(const QJsonArray& parameters) { return true; }
class Factory {
public:
class Entry {
public:
virtual Filter* create() = 0;
virtual const std::string& getName() const = 0;
virtual Filter* create() const = 0;
virtual const char* getName() const = 0;
Entry() = default;
virtual ~Entry() = default;
Entry() {};
virtual ~Entry() {};
};
template <class T, std::string name> class ClassEntry {
template <class T> class ClassEntry : public Entry {
public:
Filter* create() override { return (Filter*) new T(); }
const std::string& getName() const override {
return _name
};
virtual Filter* create() const { return (Filter*) new T(); }
virtual const char* getName() const { return T::getName(); };
ClassEntry() : _name(name) {};
ClassEntry() {};
virtual ~ClassEntry() = default;
const std::string _name;
class Builder {
public:
Builder() {
std::shared_ptr<Entry> classEntry(new ClassEntry<T>());
Filter::getFactory().registerEntry(classEntry);
}
};
};
using EntryMap = std::map<std::string, std::shared_ptr<Entry>>;
void registerEntry(const std::shared_ptr<Entry>& entry) {
void registerEntry(std::shared_ptr<Entry>& entry) {
if (entry) {
_entries.insert(EntryMap::value_type(entry->getName(), entry));
}
@ -122,8 +95,9 @@ namespace controller {
}
#define REGISTER_FILTER_CLASS(classEntry, className) \
using FactoryEntry = Filter::Factory::ClassEntry<classEntry, className>;\
static FactoryEntry _factoryEntry;
static const char* getName() { return className; } \
using FactoryEntryBuilder = Filter::Factory::ClassEntry<classEntry>::Builder;\
static FactoryEntryBuilder _factoryEntryBuilder;
namespace controller {
@ -150,6 +124,7 @@ namespace controller {
class ScaleFilter : public Filter {
public:
REGISTER_FILTER_CLASS(ScaleFilter, "scale");
ScaleFilter() {}
ScaleFilter(float scale): _scale(scale) {}
@ -158,38 +133,48 @@ namespace controller {
}
virtual bool parseParameters(const QJsonArray& parameters);
// static Filter::Factory::ClassEntry<ScaleFilter, "scale"> _factoryEntry;
REGISTER_FILTER_CLASS(ScaleFilter, "scale");
private:
float _scale = 1.0f;
};
//class AbstractRangeFilter : public Filter {
//public:
// RangeFilter(float min, float max) : _min(min), _max(max) {}
class InvertFilter : public ScaleFilter {
public:
REGISTER_FILTER_CLASS(InvertFilter, "invert");
InvertFilter() : ScaleFilter(-1.0f) {}
virtual bool parseParameters(const QJsonArray& parameters) { return true; }
//protected:
// const float _min;
// const float _max;
//};
private:
};
///*
//* Constrains will emit the input value on the first call, and every *interval* seconds, otherwise returns 0
//*/
//class PulseFilter : public Filter {
//public:
// PulseFilter(float interval);
// virtual float apply(float value) const override;
class ClampFilter : public Filter {
public:
REGISTER_FILTER_CLASS(ClampFilter, "clamp");
ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {};
//private:
// float _lastEmitTime{ -std::numeric_limits<float>::max() };
// const float _interval;
//};
virtual float apply(float value) const override {
return glm::clamp(value, _min, _max);
}
virtual bool parseParameters(const QJsonArray& parameters) override;
protected:
float _min = 0.0f;
float _max = 1.0f;
};
class DeadZoneFilter : public Filter {
public:
REGISTER_FILTER_CLASS(DeadZoneFilter, "deadZone");
DeadZoneFilter(float min = 0.0) : _min(min) {};
virtual float apply(float value) const override;
virtual bool parseParameters(const QJsonArray& parameters) override;
protected:
float _min = 0.0f;
};
class PulseFilter : public Filter {
public:
REGISTER_FILTER_CLASS(PulseFilter);
REGISTER_FILTER_CLASS(PulseFilter, "pulse");
PulseFilter() {}
PulseFilter(float interval) : _interval(interval) {}
@ -199,15 +184,31 @@ namespace controller {
virtual bool parseParameters(const QJsonArray& parameters);
private:
mutable float _lastEmitTime{ -std::numeric_limits<float>::max() };
mutable float _lastEmitTime{ -::std::numeric_limits<float>::max() };
float _interval = 1.0f;
};
////class DeadzoneFilter : public AbstractRangeFilter {
////public:
//// DeadzoneFilter(float min, float max = 1.0f);
//// virtual float apply(float newValue, float oldValue) override;
////};
class ConstrainToIntegerFilter : public Filter {
public:
REGISTER_FILTER_CLASS(ConstrainToIntegerFilter, "constrainToInteger");
ConstrainToIntegerFilter() {};
virtual float apply(float value) const override {
return glm::sign(value);
}
protected:
};
class ConstrainToPositiveIntegerFilter : public Filter {
public:
REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger");
ConstrainToPositiveIntegerFilter() {};
virtual float apply(float value) const override {
return (value <= 0.0f) ? 0.0f : 1.0f;
}
protected:
};
//class EasingFilter : public Filter {
//public:
@ -236,12 +237,6 @@ namespace controller {
// const float _exponent;
//};
//class ClampFilter : public RangeFilter {
//public:
// ClampFilter(float min = 0.0, float max = 1.0) : RangeFilter(min, max) {};
// virtual float apply(float value) const override;
//};
//class AbsFilter : public Filter {
//public:
// virtual float apply(float value) const override;

View file

@ -12,6 +12,9 @@
#include <QtCore/QRegularExpression>
#include <QJsonDocument>
#include <QJsonObject>
#include <GLMHelpers.h>
#include <DependencyManager.h>
#include <input-plugins/UserInputMapper.h>
@ -22,8 +25,6 @@
#include "impl/MappingBuilderProxy.h"
#include "Logging.h"
static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1;
namespace controller {
@ -161,7 +162,7 @@ namespace controller {
int actionNumber = 0;
qCDebug(controllers) << "Setting up standard actions";
for (const auto& actionName : actionNames) {
UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS);
qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16);
// Expose the IDs to JS
_actions.insert(sanatizeName(actionName), actionInput.getID());
@ -182,6 +183,34 @@ namespace controller {
return new MappingBuilderProxy(*this, mapping);
}
QObject* NewControllerScriptingInterface::parseMapping(const QString& json) {
QJsonObject obj;
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
// check validity of the document
if (!doc.isNull()) {
if (doc.isObject()) {
obj = doc.object();
auto mapping = std::make_shared<Mapping>("default");
auto mappingBuilder = new MappingBuilderProxy(*this, mapping);
mappingBuilder->parse(obj);
_mappingsByName[mapping->_name] = mapping;
} else {
qDebug() << "Mapping json Document is not an object" << endl;
}
} else {
qDebug() << "Invalid JSON...\n" << json << endl;
}
return nullptr;
}
Q_INVOKABLE QObject* newMapping(const QJsonObject& json);
void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
auto iterator = _mappingsByName.find(mappingName);
if (_mappingsByName.end() == iterator) {

View file

@ -40,6 +40,7 @@ namespace controller {
Q_INVOKABLE void update();
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
Q_INVOKABLE QObject* parseMapping(const QString& json);
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
Q_INVOKABLE void disableMapping(const QString& mappingName) {
enableMapping(mappingName, false);

View file

@ -57,9 +57,7 @@ QObject* RouteBuilderProxy::filter(const QScriptValue& expression) {
QObject* RouteBuilderProxy::clamp(float min, float max) {
addFilter([=](float value) {
return glm::clamp(value, min, max);
});
addFilter(Filter::Pointer(new ClampFilter(min, max)));
return this;
}
@ -69,40 +67,28 @@ QObject* RouteBuilderProxy::scale(float multiplier) {
}
QObject* RouteBuilderProxy::invert() {
return scale(-1.0f);
addFilter(Filter::Pointer(new InvertFilter()));
return this;
}
QObject* RouteBuilderProxy::deadZone(float min) {
assert(min < 1.0f);
float scale = 1.0f / (1.0f - min);
addFilter([=](float value) {
if (abs(value) < min) {
return 0.0f;
}
return (value - min) * scale;
});
addFilter(Filter::Pointer(new DeadZoneFilter(min)));
return this;
}
QObject* RouteBuilderProxy::constrainToInteger() {
addFilter([=](float value) {
return glm::sign(value);
});
addFilter(Filter::Pointer(new ConstrainToIntegerFilter()));
return this;
}
QObject* RouteBuilderProxy::constrainToPositiveInteger() {
addFilter([=](float value) {
return (value <= 0.0f) ? 0.0f : 1.0f;
});
addFilter(Filter::Pointer(new ConstrainToPositiveIntegerFilter()));
return this;
}
QObject* RouteBuilderProxy::pulse(float interval) {
Filter::Pointer filter = std::make_shared<PulseFilter>(interval);
addFilter(filter);
addFilter(Filter::Pointer(new PulseFilter(interval)));
return this;
}

View file

@ -12,10 +12,15 @@
#include "UserInputMapper.h"
#include "StandardController.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(userInputMapper)
Q_LOGGING_CATEGORY(userInputMapper, "hifi.userInputMapper")
const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX);
const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice();
const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel();
const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType();
const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType();
const uint16_t UserInputMapper::Input::ACTIONS_DEVICE = INVALID_DEVICE - (uint16)1;
// Default contruct allocate the poutput size with the current hardcoded action channels
UserInputMapper::UserInputMapper() {
@ -31,6 +36,7 @@ UserInputMapper::~UserInputMapper() {
bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
proxy->_name += " (" + QString::number(deviceID) + ")";
_registeredDevices[deviceID] = proxy;
qCDebug(userInputMapper) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID;
return true;
}
@ -65,12 +71,18 @@ void UserInputMapper::resetDevice(uint16 deviceID) {
}
int UserInputMapper::findDevice(QString name) const {
if (_standardDevice && (_standardDevice->getName() == name)) {
return getStandardDeviceID();
}
for (auto device : _registeredDevices) {
if (device.second->_name.split(" (")[0] == name) {
return device.first;
} else if (device.second->_baseName == name) {
return device.first;
}
}
return 0;
return Input::INVALID_DEVICE;
}
QVector<QString> UserInputMapper::getDeviceNames() {
@ -94,10 +106,34 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName
int deviceID = findDevice(deviceName);
if (deviceID != Input::INVALID_DEVICE) {
// getAllInputsForDevice(deviceID);
const auto& deviceProxy = _registeredDevices.at(deviceID);
auto deviceInputs = deviceProxy->getAvailabeInputs();
for (auto input : deviceInputs) {
if (input.second == inputName) {
return input.first;
}
}
qCDebug(userInputMapper) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">";
} else if (deviceName == "Actions") {
deviceID = Input::ACTIONS_DEVICE;
int actionNum = 0;
for (auto action : _actionNames) {
if (action == inputName) {
return Input(Input::ACTIONS_DEVICE, actionNum, ChannelType::AXIS);
}
actionNum++;
}
qCDebug(userInputMapper) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions";
} else {
qCDebug(userInputMapper) << "Couldn\'t find InputDevice named <" << deviceName << ">";
}
} else {
qCDebug(userInputMapper) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName";
}
return Input();

View file

@ -85,6 +85,7 @@ public:
static const uint16 INVALID_DEVICE;
static const uint16 INVALID_CHANNEL;
static const uint16 INVALID_TYPE;
static const uint16 ACTIONS_DEVICE;
};
@ -119,9 +120,11 @@ public:
class DeviceProxy {
public:
DeviceProxy(QString name) { _name = name; }
DeviceProxy(QString name) : _baseName(name), _name(name) {}
const QString& getBaseName() const { return _baseName; }
const QString& getName() const { return _name; }
QString _baseName;
QString _name;
ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; };