mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 11:55:25 +02:00
Working on controller refactoring
This commit is contained in:
parent
c2da6600f5
commit
9e4a7a6226
15 changed files with 701 additions and 98 deletions
libraries
controllers/src/controllers
Endpoint.hFilter.cppFilter.hMapping.cppMapping.hNewControllerScriptingInterface.cppNewControllerScriptingInterface.h
impl
shared/src
tests/controllers
|
@ -13,6 +13,8 @@
|
|||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
class QScriptValue;
|
||||
|
||||
namespace Controllers {
|
||||
/*
|
||||
* Encapsulates a particular input / output,
|
||||
|
@ -20,13 +22,14 @@ namespace Controllers {
|
|||
*/
|
||||
class Endpoint {
|
||||
public:
|
||||
virtual float value() = 0;
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0;
|
||||
virtual float value() { return 0; } // = 0;
|
||||
virtual void apply(float newValue, float oldValue, const Endpoint& source) {} // = 0;
|
||||
|
||||
using Pointer = std::shared_ptr<Endpoint>;
|
||||
using List = std::list<Pointer>;
|
||||
|
||||
static const List& getHardwareEndpoints();
|
||||
static Pointer getEndpoint(const QScriptValue& value);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,30 +13,6 @@
|
|||
|
||||
namespace Controllers {
|
||||
|
||||
class ScaleFilter : public Filter {
|
||||
public:
|
||||
virtual float apply(float newValue, float oldValue) {
|
||||
return newValue * _scale;
|
||||
}
|
||||
|
||||
private:
|
||||
float _scale{ 1.0 };
|
||||
};
|
||||
|
||||
class PulseFilter : public Filter {
|
||||
public:
|
||||
virtual float apply(float newValue, float oldValue) {
|
||||
// ???
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
float _lastEmitValue{ 0 };
|
||||
float _lastEmitTime{ 0 };
|
||||
float _interval{ -1.0f };
|
||||
};
|
||||
|
||||
|
||||
Filter::Pointer Filter::parse(const QJsonObject& json) {
|
||||
// FIXME parse the json object and determine the instance type to create
|
||||
return Filter::Pointer();
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
|
||||
#include <QtCore/QEasingCurve>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
|
@ -20,14 +24,155 @@ namespace Controllers {
|
|||
// Encapsulates part of a filter chain
|
||||
class Filter {
|
||||
public:
|
||||
virtual float apply(float newValue, float oldValue) = 0;
|
||||
virtual float apply(float value) const = 0;
|
||||
|
||||
using Pointer = std::shared_ptr<Filter>;
|
||||
using List = std::list<Pointer>;
|
||||
using Lambda = std::function<float(float)>;
|
||||
|
||||
static Filter::Pointer parse(const QJsonObject& json);
|
||||
};
|
||||
|
||||
|
||||
class LambdaFilter : public Filter {
|
||||
public:
|
||||
LambdaFilter(Lambda f) : _function(f) {};
|
||||
|
||||
virtual float apply(float value) const {
|
||||
return _function(value);
|
||||
}
|
||||
|
||||
private:
|
||||
Lambda _function;
|
||||
};
|
||||
|
||||
class ScriptFilter : public Filter {
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
//class ScaleFilter : public Filter {
|
||||
//public:
|
||||
// ScaleFilter(float scale);
|
||||
// virtual float apply(float scale) const override {
|
||||
// return value * _scale;
|
||||
// }
|
||||
|
||||
//private:
|
||||
// const float _scale;
|
||||
//};
|
||||
|
||||
//class AbstractRangeFilter : public Filter {
|
||||
//public:
|
||||
// RangeFilter(float min, float max) : _min(min), _max(max) {}
|
||||
|
||||
//protected:
|
||||
// const float _min;
|
||||
// const float _max;
|
||||
//};
|
||||
|
||||
///*
|
||||
//* 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;
|
||||
|
||||
//private:
|
||||
// float _lastEmitTime{ -std::numeric_limits<float>::max() };
|
||||
// const float _interval;
|
||||
//};
|
||||
|
||||
////class DeadzoneFilter : public AbstractRangeFilter {
|
||||
////public:
|
||||
//// DeadzoneFilter(float min, float max = 1.0f);
|
||||
//// virtual float apply(float newValue, float oldValue) override;
|
||||
////};
|
||||
|
||||
//class EasingFilter : public Filter {
|
||||
//public:
|
||||
// virtual float apply(float value) const override;
|
||||
|
||||
//private:
|
||||
// QEasingCurve _curve;
|
||||
//};
|
||||
|
||||
//// GLSL style filters
|
||||
//class StepFilter : public Filter {
|
||||
//public:
|
||||
// StepFilter(float edge) : _edge(edge) {};
|
||||
// virtual float apply(float value) const override;
|
||||
|
||||
//private:
|
||||
// const float _edge;
|
||||
//};
|
||||
|
||||
//class PowFilter : public Filter {
|
||||
//public:
|
||||
// PowFilter(float exponent) : _exponent(exponent) {};
|
||||
// virtual float apply(float value) const override;
|
||||
|
||||
//private:
|
||||
// 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;
|
||||
//};
|
||||
|
||||
//class SignFilter : public Filter {
|
||||
//public:
|
||||
// virtual float apply(float value) const override;
|
||||
//};
|
||||
|
||||
//class FloorFilter : public Filter {
|
||||
//public:
|
||||
// virtual float apply(float value) const override {
|
||||
// return floor(newValue);
|
||||
// }
|
||||
//};
|
||||
|
||||
//class CeilFilter : public Filter {
|
||||
//public:
|
||||
// virtual float apply(float value) const override {
|
||||
// return ceil(newValue);
|
||||
// }
|
||||
//};
|
||||
|
||||
//class FractFilter : public Filter {
|
||||
//public:
|
||||
// virtual float apply(float value) const override {
|
||||
// return fract(newValue);
|
||||
// }
|
||||
//};
|
||||
|
||||
//class MinFilter : public Filter {
|
||||
//public:
|
||||
// MinFilter(float mine) : _min(min) {};
|
||||
|
||||
// virtual float apply(float value) const override {
|
||||
// return glm::min(_min, newValue);
|
||||
// }
|
||||
|
||||
//private:
|
||||
// const float _min;
|
||||
//};
|
||||
|
||||
//class MaxFilter : public Filter {
|
||||
//public:
|
||||
// MaxFilter(float max) : _max(max) {};
|
||||
// virtual float apply(float newValue, float oldValue) override;
|
||||
//private:
|
||||
// const float _max;
|
||||
//};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,3 +2,62 @@
|
|||
|
||||
namespace Controllers {
|
||||
}
|
||||
|
||||
// class MappingsStack {
|
||||
// std::list<Mapping> _stack;
|
||||
// ValueMap _lastValues;
|
||||
//
|
||||
// void update() {
|
||||
// EndpointList hardwareInputs = getHardwareEndpoints();
|
||||
// ValueMap currentValues;
|
||||
//
|
||||
// for (auto input : hardwareInputs) {
|
||||
// currentValues[input] = input->value();
|
||||
// }
|
||||
//
|
||||
// // Now process the current values for each level of the stack
|
||||
// for (auto& mapping : _stack) {
|
||||
// update(mapping, currentValues);
|
||||
// }
|
||||
//
|
||||
// _lastValues = currentValues;
|
||||
// }
|
||||
//
|
||||
// void update(Mapping& mapping, ValueMap& values) {
|
||||
// ValueMap updates;
|
||||
// EndpointList consumedEndpoints;
|
||||
// for (const auto& entry : values) {
|
||||
// Endpoint* endpoint = entry.first;
|
||||
// if (!mapping._channelMappings.count(endpoint)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const Mapping::List& routes = mapping._channelMappings[endpoint];
|
||||
// consumedEndpoints.push_back(endpoint);
|
||||
// for (const auto& route : routes) {
|
||||
// float lastValue = 0;
|
||||
// if (mapping._lastValues.count(endpoint)) {
|
||||
// lastValue = mapping._lastValues[endpoint];
|
||||
// }
|
||||
// float value = entry.second;
|
||||
// for (const auto& filter : route._filters) {
|
||||
// value = filter->apply(value, lastValue);
|
||||
// }
|
||||
// updates[route._destination] = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Update the last seen values
|
||||
// mapping._lastValues = values;
|
||||
//
|
||||
// // Remove all the consumed inputs
|
||||
// for (auto endpoint : consumedEndpoints) {
|
||||
// values.erase(endpoint);
|
||||
// }
|
||||
//
|
||||
// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest)
|
||||
// for (const auto& entry : updates) {
|
||||
// values[entry.first] = entry.second;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Mapping_h
|
||||
#define hifi_Controllers_Mapping_h
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
@ -14,6 +26,8 @@ namespace Controllers {
|
|||
public:
|
||||
// Map of source channels to route lists
|
||||
using Map = std::map<Endpoint::Pointer, Route::List>;
|
||||
using Pointer = std::shared_ptr<Mapping>;
|
||||
using List = std::list<Pointer>;
|
||||
|
||||
Map _channelMappings;
|
||||
ValueMap _lastValues;
|
||||
|
@ -22,62 +36,6 @@ namespace Controllers {
|
|||
QString serialize();
|
||||
};
|
||||
|
||||
// class MappingsStack {
|
||||
// std::list<Mapping> _stack;
|
||||
// ValueMap _lastValues;
|
||||
//
|
||||
// void update() {
|
||||
// EndpointList hardwareInputs = getHardwareEndpoints();
|
||||
// ValueMap currentValues;
|
||||
//
|
||||
// for (auto input : hardwareInputs) {
|
||||
// currentValues[input] = input->value();
|
||||
// }
|
||||
//
|
||||
// // Now process the current values for each level of the stack
|
||||
// for (auto& mapping : _stack) {
|
||||
// update(mapping, currentValues);
|
||||
// }
|
||||
//
|
||||
// _lastValues = currentValues;
|
||||
// }
|
||||
//
|
||||
// void update(Mapping& mapping, ValueMap& values) {
|
||||
// ValueMap updates;
|
||||
// EndpointList consumedEndpoints;
|
||||
// for (const auto& entry : values) {
|
||||
// Endpoint* endpoint = entry.first;
|
||||
// if (!mapping._channelMappings.count(endpoint)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const Mapping::List& routes = mapping._channelMappings[endpoint];
|
||||
// consumedEndpoints.push_back(endpoint);
|
||||
// for (const auto& route : routes) {
|
||||
// float lastValue = 0;
|
||||
// if (mapping._lastValues.count(endpoint)) {
|
||||
// lastValue = mapping._lastValues[endpoint];
|
||||
// }
|
||||
// float value = entry.second;
|
||||
// for (const auto& filter : route._filters) {
|
||||
// value = filter->apply(value, lastValue);
|
||||
// }
|
||||
// updates[route._destination] = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Update the last seen values
|
||||
// mapping._lastValues = values;
|
||||
//
|
||||
// // Remove all the consumed inputs
|
||||
// for (auto endpoint : consumedEndpoints) {
|
||||
// values.erase(endpoint);
|
||||
// }
|
||||
//
|
||||
// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest)
|
||||
// for (const auto& entry : updates) {
|
||||
// values[entry.first] = entry.second;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
#include "NewControllerScriptingInterface.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
#include "impl/MappingBuilderProxy.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
QObject* NewControllerScriptingInterface::newMapping() {
|
||||
qDebug() << "Creating new Mapping proxy";
|
||||
return new MappingBuilderProxy(std::make_shared<Mapping>());
|
||||
}
|
||||
|
||||
float NewControllerScriptingInterface::getValue(const QScriptValue& source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace controllers
|
||||
|
||||
|
||||
|
||||
|
||||
// class MappingsStack {
|
||||
// std::list<Mapping> _stack;
|
||||
// ValueMap _lastValues;
|
||||
//
|
||||
// void update() {
|
||||
// EndpointList hardwareInputs = getHardwareEndpoints();
|
||||
// ValueMap currentValues;
|
||||
//
|
||||
// for (auto input : hardwareInputs) {
|
||||
// currentValues[input] = input->value();
|
||||
// }
|
||||
//
|
||||
// // Now process the current values for each level of the stack
|
||||
// for (auto& mapping : _stack) {
|
||||
// update(mapping, currentValues);
|
||||
// }
|
||||
//
|
||||
// _lastValues = currentValues;
|
||||
// }
|
||||
//
|
||||
// void update(Mapping& mapping, ValueMap& values) {
|
||||
// ValueMap updates;
|
||||
// EndpointList consumedEndpoints;
|
||||
// for (const auto& entry : values) {
|
||||
// Endpoint* endpoint = entry.first;
|
||||
// if (!mapping._channelMappings.count(endpoint)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// const Mapping::List& routes = mapping._channelMappings[endpoint];
|
||||
// consumedEndpoints.push_back(endpoint);
|
||||
// for (const auto& route : routes) {
|
||||
// float lastValue = 0;
|
||||
// if (mapping._lastValues.count(endpoint)) {
|
||||
// lastValue = mapping._lastValues[endpoint];
|
||||
// }
|
||||
// float value = entry.second;
|
||||
// for (const auto& filter : route._filters) {
|
||||
// value = filter->apply(value, lastValue);
|
||||
// }
|
||||
// updates[route._destination] = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Update the last seen values
|
||||
// mapping._lastValues = values;
|
||||
//
|
||||
// // Remove all the consumed inputs
|
||||
// for (auto endpoint : consumedEndpoints) {
|
||||
// values.erase(endpoint);
|
||||
// }
|
||||
//
|
||||
// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest)
|
||||
// for (const auto& entry : updates) {
|
||||
// values[entry.first] = entry.second;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//var mapping = Controller.newMapping();
|
||||
//mapping.map(hydra.LeftButton0, actions.ContextMenu);
|
||||
//mapping.map(hydra.LeftButton0).to(xbox.RT);
|
||||
//mapping.from(xbox.RT).constrainToBoolean().invert().to(actions.Foo)
|
||||
// mapping.from(xbox.RY).invert().deadZone(0.2).to(actions.Pitch)
|
||||
// mapping.from(xbox.RY).filter(function(newValue, oldValue) {
|
||||
// return newValue * 2.0
|
||||
//}).to(actions.Pitch)
|
||||
|
||||
//mapping.from(function(time) {
|
||||
// return Math.cos(time);
|
||||
// }).to(actions.Pitch);
|
||||
|
||||
// mapping.mapFromFunction(function() {
|
||||
// return x;
|
||||
// }, actions.ContextMenu);
|
||||
|
||||
// mapping.from(xbox.LY).clamp(0, 1).to(actions.Forward);
|
||||
// mapping.from(xbox.LY).clamp(-1, 0).to(actions.Backward);
|
||||
// mapping.from(xbox.RY).clamp(0, 1).to(actions.Forward);
|
||||
// mapping.from(xbox.RS).to();
|
||||
// mapping.from(xbox.ALL).to();
|
||||
|
||||
// mapping.from(xbox.RY).to(function(...) { ... });
|
||||
// mapping.from(xbox.RY).pass();
|
||||
|
||||
// mapping.suppress() ≅ mapping.to(null)
|
||||
// mapping.pass() ≅ mapping.to(fromControl)
|
||||
|
||||
// mapping.from(keyboard.RightParen).invert().to(actions.Yaw)
|
||||
// mapping.from(keyboard.LeftParen).to(actions.Yaw)
|
||||
|
||||
// mapping.from(hydra.LX).pulse(MIN_SNAP_TIME, 3.0).to(Actions.Yaw)
|
||||
|
||||
// mapping.from(keyboard.LeftParen).pulse(MIN_SNAP_TIME).to(Actions.Yaw)
|
||||
// // Enable and disable as above
|
||||
|
||||
// mappingSnap.from(hydra.LX).to(function(newValue, oldValue) {
|
||||
// timeSinceLastYaw += deltaTime
|
||||
|
||||
#include "NewControllerScriptingInterface.moc"
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_NewControllerScriptingInterface_h
|
||||
#define hifi_Controllers_NewControllerScriptingInterface_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
#include "Mapping.h"
|
||||
|
||||
class QScriptValue;
|
||||
|
||||
namespace Controllers {
|
||||
class NewControllerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QObject* newMapping();
|
||||
Q_INVOKABLE float getValue(const QScriptValue& source);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "MappingBuilderProxy.h"
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "RouteBuilderProxy.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
QObject* MappingBuilderProxy::from(const QString& source) {
|
||||
qDebug() << "Creating new Route builder proxy from " << source;
|
||||
auto route = Route::Pointer(new Route());
|
||||
route->_source = endpointFor(source);
|
||||
return new RouteBuilderProxy(this, route);
|
||||
}
|
||||
|
||||
Endpoint::Pointer MappingBuilderProxy::endpointFor(const QString& endpoint) {
|
||||
static QHash<QString, Endpoint::Pointer> ENDPOINTS;
|
||||
if (!ENDPOINTS.contains(endpoint)) {
|
||||
ENDPOINTS[endpoint] = std::make_shared<Endpoint>();
|
||||
}
|
||||
return ENDPOINTS[endpoint];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Impl_MappingBuilderProxy_h
|
||||
#define hifi_Controllers_Impl_MappingBuilderProxy_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include "../Mapping.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
class MappingBuilderProxy : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MappingBuilderProxy(Mapping::Pointer mapping)
|
||||
: _mapping(mapping) { }
|
||||
|
||||
Q_INVOKABLE QObject* from(const QString& fromEndpoint);
|
||||
|
||||
protected:
|
||||
friend class RouteBuilderProxy;
|
||||
Endpoint::Pointer endpointFor(const QString& endpoint);
|
||||
Mapping::Pointer _mapping;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
#include "RouteBuilderProxy.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "MappingBuilderProxy.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
void RouteBuilderProxy::to(const QString& destination) {
|
||||
qDebug() << "Completed route: " << destination;
|
||||
auto sourceEndpoint = _route->_source;
|
||||
auto& mapping = _parent->_mapping;
|
||||
mapping->_channelMappings[sourceEndpoint].push_back(_route);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::clamp(float min, float max) {
|
||||
addFilter([=](float value) {
|
||||
return glm::clamp(value, min, max);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::scale(float multiplier) {
|
||||
addFilter([=](float value) {
|
||||
return value * multiplier;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::invert() {
|
||||
return scale(-1.0f);
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::deadZone(float min) {
|
||||
assert(min < 1.0f);
|
||||
float scale = 1.0f / (1.0f - min);
|
||||
addFilter([=](float value) {
|
||||
if (value < min) {
|
||||
return 0.0f;
|
||||
}
|
||||
return (value - min) * scale;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::constrainToInteger() {
|
||||
addFilter([=](float value) {
|
||||
return glm::sign(value);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::constrainToPositiveInteger() {
|
||||
addFilter([=](float value) {
|
||||
return (value <= 0.0f) ? 0.0f : 1.0f;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
void RouteBuilderProxy::addFilter(Filter::Lambda lambda) {
|
||||
Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda);
|
||||
addFilter(filterPointer);
|
||||
}
|
||||
|
||||
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
|
||||
_route->_filters.push_back(filter);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/09
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Impl_RouteBuilderProxy_h
|
||||
#define hifi_Controllers_Impl_RouteBuilderProxy_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include "../Filter.h"
|
||||
#include "../Route.h"
|
||||
|
||||
namespace Controllers {
|
||||
|
||||
class MappingBuilderProxy;
|
||||
|
||||
class RouteBuilderProxy : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RouteBuilderProxy(MappingBuilderProxy* parent, Route::Pointer route)
|
||||
: _parent(parent), _route(route) { }
|
||||
|
||||
Q_INVOKABLE void to(const QString& destination);
|
||||
Q_INVOKABLE QObject* clamp(float min, float max);
|
||||
Q_INVOKABLE QObject* scale(float multiplier);
|
||||
Q_INVOKABLE QObject* invert();
|
||||
Q_INVOKABLE QObject* deadZone(float min);
|
||||
Q_INVOKABLE QObject* constrainToInteger();
|
||||
Q_INVOKABLE QObject* constrainToPositiveInteger();
|
||||
|
||||
private:
|
||||
void addFilter(Filter::Lambda lambda);
|
||||
void addFilter(Filter::Pointer filter);
|
||||
Route::Pointer _route;
|
||||
MappingBuilderProxy* _parent;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -15,6 +15,7 @@
|
|||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <time.h>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) {
|
|||
::usecTimestampNowAdjust = clockSkew;
|
||||
}
|
||||
|
||||
static qint64 TIME_REFERENCE = 0; // in usec
|
||||
static std::once_flag usecTimestampNowIsInitialized;
|
||||
static QElapsedTimer timestampTimer;
|
||||
|
||||
quint64 usecTimestampNow(bool wantDebug) {
|
||||
static bool usecTimestampNowIsInitialized = false;
|
||||
static qint64 TIME_REFERENCE = 0; // in usec
|
||||
static QElapsedTimer timestampTimer;
|
||||
|
||||
if (!usecTimestampNowIsInitialized) {
|
||||
TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
|
||||
std::call_once(usecTimestampNowIsInitialized, [&] {
|
||||
TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
|
||||
timestampTimer.start();
|
||||
usecTimestampNowIsInitialized = true;
|
||||
}
|
||||
});
|
||||
|
||||
quint64 now;
|
||||
quint64 nsecsElapsed = timestampTimer.nsecsElapsed();
|
||||
quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec
|
||||
quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec
|
||||
|
||||
// QElapsedTimer may not advance if the CPU has gone to sleep. In which case it
|
||||
// will begin to deviate from real time. We detect that here, and reset if necessary
|
||||
quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();
|
||||
quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs
|
||||
quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs
|
||||
int possibleSkew = msecsEstimate - msecsCurrentTime;
|
||||
const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated
|
||||
const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated
|
||||
if (abs(possibleSkew) > TOLERANCE) {
|
||||
// reset our TIME_REFERENCE and timer
|
||||
TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
|
||||
TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
|
||||
timestampTimer.restart();
|
||||
now = TIME_REFERENCE + ::usecTimestampNowAdjust;
|
||||
|
||||
|
@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) {
|
|||
return now;
|
||||
}
|
||||
|
||||
float secTimestampNow() {
|
||||
static const auto START_TIME = usecTimestampNow();
|
||||
const auto nowUsecs = usecTimestampNow();
|
||||
const auto nowMsecs = nowUsecs / USECS_PER_MSEC;
|
||||
return (float)nowMsecs / MSECS_PER_SECOND;
|
||||
}
|
||||
|
||||
float randFloat() {
|
||||
return (rand() % 10000)/10000.0f;
|
||||
}
|
||||
|
|
|
@ -65,9 +65,14 @@ inline bool operator!=(const xColor& lhs, const xColor& rhs)
|
|||
// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.
|
||||
const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";
|
||||
|
||||
// Equivalent to time_t but in usecs instead of secs
|
||||
quint64 usecTimestampNow(bool wantDebug = false);
|
||||
void usecTimestampNowForceClockSkew(int clockSkew);
|
||||
|
||||
// Number of seconds expressed since the first call to this function, expressed as a float
|
||||
// Maximum accuracy in msecs
|
||||
float ssecTimestampNow();
|
||||
|
||||
float randFloat();
|
||||
int randIntInRange (int min, int max);
|
||||
float randFloatInRange (float min,float max);
|
||||
|
|
43
tests/controllers/qml/main.qml
Normal file
43
tests/controllers/qml/main.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Dialogs 1.0
|
||||
import com.highfidelity.test 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
visible: true
|
||||
|
||||
AppHook {
|
||||
|
||||
}
|
||||
|
||||
// NewControllers {
|
||||
// id: newControllers
|
||||
// }
|
||||
|
||||
Rectangle {
|
||||
id: page
|
||||
width: 320; height: 480
|
||||
color: "lightgray"
|
||||
Text {
|
||||
id: helloText
|
||||
text: "Hello world!"
|
||||
y: 30
|
||||
anchors.horizontalCenter: page.horizontalCenter
|
||||
font.pointSize: 24; font.bold: true
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
var newMapping = NewControllers.newMapping();
|
||||
console.log("Mapping Object " + newMapping);
|
||||
var routeBuilder = newMapping.from("Hello");
|
||||
console.log("Route Builder " + routeBuilder);
|
||||
routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -27,12 +27,73 @@
|
|||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQml/QQmlApplicationEngine>
|
||||
#include <QtQml/QQmlContext>
|
||||
|
||||
#include <controllers/NewControllerScriptingInterface.h>
|
||||
|
||||
const QString& getQmlDir() {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/";
|
||||
qDebug() << "Qml Path: " << dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
class AppHook : public QQuickItem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AppHook() {
|
||||
qDebug() << "Hook Created";
|
||||
}
|
||||
};
|
||||
|
||||
using namespace Controllers;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Register our component type with QML.
|
||||
qmlRegisterType<AppHook>("com.highfidelity.test", 1, 0, "AppHook");
|
||||
//qmlRegisterType<NewControllerScriptingInterface>("com.highfidelity.test", 1, 0, "NewControllers");
|
||||
QGuiApplication app(argc, argv);
|
||||
QWindow window;
|
||||
QQmlApplicationEngine engine(getQmlDir() + "main.qml");
|
||||
engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface());
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//QQmlEngine engine;
|
||||
//QQmlComponent *component = new QQmlComponent(&engine);
|
||||
//
|
||||
//QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
|
||||
//
|
||||
//component->loadUrl(QUrl("main.qml"));
|
||||
//
|
||||
//if (!component->isReady()) {
|
||||
// qWarning("%s", qPrintable(component->errorString()));
|
||||
// return -1;
|
||||
//}
|
||||
//
|
||||
//QObject *topLevel = component->create();
|
||||
//QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
|
||||
//
|
||||
//QSurfaceFormat surfaceFormat = window->requestedFormat();
|
||||
//window->setFormat(surfaceFormat);
|
||||
//window->show();
|
||||
//
|
||||
//rc = app.exec();
|
||||
//
|
||||
//delete component;
|
||||
//return rc;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
|
|
Loading…
Reference in a new issue