From c246205692e94c4e4500788baabc66a0c3986d06 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 15:04:45 -0700 Subject: [PATCH 01/43] Base implementation of ArrayBuffer --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ++++++++++++++++++ libraries/script-engine/src/ArrayBuffer.h | 56 ++++++++++ .../src/ArrayBufferPrototype.cpp | 29 +++++ .../script-engine/src/ArrayBufferPrototype.h | 32 ++++++ 4 files changed, 222 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBuffer.h create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.cpp create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.h diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp new file mode 100644 index 0000000000..561a8cd5bf --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.cpp @@ -0,0 +1,105 @@ +// +// ArrayBuffer.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 "ArrayBufferPrototype.h" + +#include "ArrayBuffer.h" + +Q_DECLARE_METATYPE(QByteArray*) +Q_DECLARE_METATYPE(ArrayBuffer*) + +ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + + _byteLength = engine->toStringHandle(QLatin1String("byteLength")); + +// _proto = engine->newQObject(new ArrayBufferPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration +// | QScriptEngine::ExcludeSuperClassMethods +// | QScriptEngine::ExcludeSuperClassProperties); + QScriptValue global = engine->globalObject(); + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); +} + +QScriptValue ArrayBuffer::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { + ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); + QScriptValue arg = ctx->argument(0); + int size = arg.toInt32(); + return cls ? cls->newInstance(size) : QScriptValue(); +} + +QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBuffer::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBuffer::name() const { + return QLatin1String("ArrayBuffer"); +} + +QScriptValue ArrayBuffer::prototype() const { + return _proto; +} + +QScriptValue ArrayBuffer::constructor() const { + return _ctor; +} + +QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) +{ + QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); + ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return eng->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBuffer.h new file mode 100644 index 0000000000..f951a1248a --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.h @@ -0,0 +1,56 @@ +// +// ArrayBuffer.h +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBuffer_h +#define hifi_ArrayBuffer_h + +#include +#include +#include +#include +#include +#include +#include + +class ArrayBuffer : public QObject, public QScriptClass { +public: + ArrayBuffer(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size = 0); + QScriptValue newInstance(const QByteArray& ba); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, + uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, + uint id); + + QString name() const; + QScriptValue prototype() const; + QScriptValue constructor() const; + +private: + static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + + static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); + static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); + + QScriptValue _proto; + QScriptValue _ctor; + + // JS Object attributes + QScriptString _byteLength; +}; + +#endif // hifi_ArrayBuffer_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp new file mode 100644 index 0000000000..01736cc511 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -0,0 +1,29 @@ +// +// ArrayBufferPrototype.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 + +#include "ArrayBufferPrototype.h" + +ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray ArrayBufferPrototype::slice(long begin, long end) const { + if (end == -1) { + return thisArrayBuffer().mid(begin); + } else { + return thisArrayBuffer().mid(begin, end); + } +} + +QByteArray ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); +} diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h new file mode 100644 index 0000000000..829fb8e3ec --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -0,0 +1,32 @@ +// +// ArrayBufferPrototype.h +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBufferPrototype_h +#define hifi_ArrayBufferPrototype_h + +#include +#include +#include +#include + +class ArrayBufferPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + ArrayBufferPrototype(QObject* parent = NULL); + +public slots: + QByteArray slice(long begin, long end = -1) const; + +private: + QByteArray thisArrayBuffer() const; +}; + +#endif // hifi_ArrayBufferPrototype_h \ No newline at end of file From f9b739a0d1500864495f2da7f6066abcd016b8c4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:02:33 -0700 Subject: [PATCH 02/43] Functional ArrayBuffer class following Qt standard --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ---------------- .../script-engine/src/ArrayBufferClass.cpp | 119 ++++++++++++++++++ .../src/{ArrayBuffer.h => ArrayBufferClass.h} | 27 ++-- .../src/ArrayBufferPrototype.cpp | 14 +-- .../script-engine/src/ArrayBufferPrototype.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 3 + 6 files changed, 143 insertions(+), 127 deletions(-) delete mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBufferClass.cpp rename libraries/script-engine/src/{ArrayBuffer.h => ArrayBufferClass.h} (67%) diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp deleted file mode 100644 index 561a8cd5bf..0000000000 --- a/libraries/script-engine/src/ArrayBuffer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// ArrayBuffer.cpp -// -// -// Created by Clement on 7/3/14. -// Copyright 2014 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 "ArrayBufferPrototype.h" - -#include "ArrayBuffer.h" - -Q_DECLARE_METATYPE(QByteArray*) -Q_DECLARE_METATYPE(ArrayBuffer*) - -ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - - _byteLength = engine->toStringHandle(QLatin1String("byteLength")); - -// _proto = engine->newQObject(new ArrayBufferPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration -// | QScriptEngine::ExcludeSuperClassMethods -// | QScriptEngine::ExcludeSuperClassProperties); - QScriptValue global = engine->globalObject(); - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); -} - -QScriptValue ArrayBuffer::newInstance(unsigned long size) { - engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { - ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); - QScriptValue arg = ctx->argument(0); - int size = arg.toInt32(); - return cls ? cls->newInstance(size) : QScriptValue(); -} - -QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - // if the property queried is byteLength, only handle read access - return flags &= HandlesReadAccess; - } - return 0; // No access -} - -QScriptValue ArrayBuffer::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - return ba->length(); - } - return QScriptValue(); -} - -QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id) { - return QScriptValue::Undeletable; -} - -QString ArrayBuffer::name() const { - return QLatin1String("ArrayBuffer"); -} - -QScriptValue ArrayBuffer::prototype() const { - return _proto; -} - -QScriptValue ArrayBuffer::constructor() const { - return _ctor; -} - -QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) -{ - QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); - ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); - if (!cls) { - return eng->newVariant(QVariant::fromValue(ba)); - } - return cls->newInstance(ba); -} - -void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { - ba = qvariant_cast(obj.data().toVariant()); -} - diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp new file mode 100644 index 0000000000..099e77b485 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -0,0 +1,119 @@ +// +// ArrayBufferClass.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 "ArrayBufferPrototype.h" + +#include "ArrayBufferClass.h" + +static const QString CLASS_NAME = "ArrayBuffer"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new ArrayBufferPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue ArrayBufferClass::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { + ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls) { + return QScriptValue(); + } + + QScriptValue arg = context->argument(0); + unsigned long size = arg.toInt32(); + QScriptValue newObject = cls->newInstance(size); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBufferClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBufferClass::name() const { + return _name.toString(); +} + +QScriptValue ArrayBufferClass::prototype() const { + return _proto; +} + +QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) { + QScriptValue ctor = engine->globalObject().property(CLASS_NAME); + ArrayBufferClass* cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return engine->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBufferClass::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBufferClass.h similarity index 67% rename from libraries/script-engine/src/ArrayBuffer.h rename to libraries/script-engine/src/ArrayBufferClass.h index f951a1248a..b106d27934 100644 --- a/libraries/script-engine/src/ArrayBuffer.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -1,5 +1,5 @@ // -// ArrayBuffer.h +// ArrayBufferClass.h // // // Created by Clement on 7/3/14. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ArrayBuffer_h -#define hifi_ArrayBuffer_h +#ifndef hifi_ArrayBufferClass_h +#define hifi_ArrayBufferClass_h #include #include @@ -20,28 +20,26 @@ #include #include -class ArrayBuffer : public QObject, public QScriptClass { +class ArrayBufferClass : public QObject, public QScriptClass { + Q_OBJECT public: - ArrayBuffer(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size = 0); + ArrayBufferClass(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue& object, - const QScriptString& name, - uint id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, - uint id); + const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; - QScriptValue constructor() const; private: - static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); @@ -50,7 +48,8 @@ private: QScriptValue _ctor; // JS Object attributes + QScriptString _name; QScriptString _byteLength; }; -#endif // hifi_ArrayBuffer_h \ No newline at end of file +#endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 01736cc511..087ab3d177 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -11,19 +11,19 @@ #include +#include "ArrayBufferClass.h" + #include "ArrayBufferPrototype.h" +Q_DECLARE_METATYPE(QByteArray*) + ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - if (end == -1) { - return thisArrayBuffer().mid(begin); - } else { - return thisArrayBuffer().mid(begin, end); - } + return thisArrayBuffer()->mid(begin, end); } -QByteArray ArrayBufferPrototype::thisArrayBuffer() const { - return qscriptvalue_cast(thisObject().data()); +QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 829fb8e3ec..52caf5c90a 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -26,7 +26,7 @@ public slots: QByteArray slice(long begin, long end = -1) const; private: - QByteArray thisArrayBuffer() const; + QByteArray* thisArrayBuffer() const; }; #endif // hifi_ArrayBufferPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e93a7125b9..fab50784a2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,6 +32,7 @@ #include #include "AnimationObject.h" +#include "ArrayBufferClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -211,6 +212,8 @@ void ScriptEngine::init() { return; // only initialize once } + new ArrayBufferClass(&_engine); + _isInitialized = true; _voxelsScriptingInterface.init(); From 96a1390d8f658328158823be0140e216ef5083e7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:58:53 -0700 Subject: [PATCH 03/43] Fixed up ArrayBuffer::slice() behaviour --- .../src/ArrayBufferPrototype.cpp | 25 ++++++++++++++++++- .../script-engine/src/ArrayBufferPrototype.h | 5 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 087ab3d177..45810f39c5 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,8 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include + #include "ArrayBufferClass.h" #include "ArrayBufferPrototype.h" @@ -21,7 +24,27 @@ ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - return thisArrayBuffer()->mid(begin, end); + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + end = (end < 0) ? ba->size() + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + + return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); +} + +QByteArray ArrayBufferPrototype::slice(long begin) const { + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + + return ba->mid(begin, -1); } QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 52caf5c90a..6f3c8bbd4d 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -22,8 +22,9 @@ class ArrayBufferPrototype : public QObject, public QScriptable { public: ArrayBufferPrototype(QObject* parent = NULL); -public slots: - QByteArray slice(long begin, long end = -1) const; + public slots: + QByteArray slice(long begin, long end) const; + QByteArray slice(long begin) const; private: QByteArray* thisArrayBuffer() const; From 05d74e2bb1988a87c5d5170a661015ddc8ecc6db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:59:57 -0700 Subject: [PATCH 04/43] Added ArrayBuffer unit tests --- examples/typedArraysUnitTest.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/typedArraysUnitTest.js diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js new file mode 100644 index 0000000000..98f69f3e71 --- /dev/null +++ b/examples/typedArraysUnitTest.js @@ -0,0 +1,22 @@ +// +// typedArraysunitTest.js +// examples +// +// Created by Clément Brisset on 7/7/14 +// Copyright 2014 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 +// + +Script.include("Test.js"); + +test("ArrayBuffer", function(finished) { + this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); + + this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); + + this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); +}); \ No newline at end of file From 1f6e1b9509635a49f3d5de90088a8e62484b73d7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:08:11 -0700 Subject: [PATCH 05/43] Modifications and improvments of the ArrayBuffers --- libraries/script-engine/src/ArrayBufferClass.cpp | 9 +++++++-- libraries/script-engine/src/ArrayBufferClass.h | 2 +- .../script-engine/src/ArrayBufferPrototype.cpp | 13 +++++-------- libraries/script-engine/src/ArrayBufferPrototype.h | 8 +++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 099e77b485..d003125c1e 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -42,7 +42,7 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc engine->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(unsigned long size) { +QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); return engine()->newObject(this, data); @@ -60,7 +60,12 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* } QScriptValue arg = context->argument(0); - unsigned long size = arg.toInt32(); + + if (!arg.isValid() || !arg.isNumber()) { + return QScriptValue(); + } + + quint32 size = arg.toInt32(); QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index b106d27934..2f863e77a5 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -24,7 +24,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size); + QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 45810f39c5..37db5f3eec 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include - #include #include "ArrayBufferClass.h" @@ -23,26 +20,26 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } -QByteArray ArrayBufferPrototype::slice(long begin, long end) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin, qint32 end) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; end = (end < 0) ? ba->size() + end : end; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); - end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); + end = glm::clamp(end, 0, (ba->size() - 1)); return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); } -QByteArray ArrayBufferPrototype::slice(long begin) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); return ba->mid(begin, -1); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 6f3c8bbd4d..09d4596f28 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -12,19 +12,17 @@ #ifndef hifi_ArrayBufferPrototype_h #define hifi_ArrayBufferPrototype_h -#include #include #include -#include class ArrayBufferPrototype : public QObject, public QScriptable { Q_OBJECT public: ArrayBufferPrototype(QObject* parent = NULL); - public slots: - QByteArray slice(long begin, long end) const; - QByteArray slice(long begin) const; +public slots: + QByteArray slice(qint32 begin, qint32 end) const; + QByteArray slice(qint32 begin) const; private: QByteArray* thisArrayBuffer() const; From 13fe8b579fc1eeed993a44731b50e29cc497538c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:06 -0700 Subject: [PATCH 06/43] Added ArrayBufferView scriptable objects --- .../src/ArrayBufferViewClass.cpp | 103 ++++++++++++++++++ .../script-engine/src/ArrayBufferViewClass.h | 47 ++++++++ 2 files changed, 150 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.cpp create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp new file mode 100644 index 0000000000..73d833dab4 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -0,0 +1,103 @@ +// +// ArrayBufferViewClass.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 "ArrayBufferViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : +QObject(engine), +QScriptClass(engine) { + // Save string handles for quick lookup + _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); +} + +QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bufferName || name == _byteOffsetName || name == _byteLengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return 0; // No access +} + +QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bufferName) { + return object.data().property(_bufferName); + } + if (name == _byteOffsetName) { + return object.data().property(_byteOffsetName); + } + if (name == _byteLengthName) { + return object.data().property(_byteLengthName); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, +// const QScriptString& name, +// QueryFlags flags, uint* id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// int pos = name.toArrayIndex(&ok); +// +// // Check that name is a valid index and arrayBuffer exists +// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { +// *id = pos; // save pos to avoid recomputation +// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//QScriptValue DataViewClass::property(const QScriptValue &object, +// const QScriptString &name, uint id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// name.toArrayIndex(&ok); +// +// if (ok && arrayBuffer) { +// return (*arrayBuffer)[id]; +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//void DataViewClass::setProperty(QScriptValue &object, +// const QScriptString &name, +// uint id, const QScriptValue &value) { +// QByteArray *ba = qscriptvalue_cast(object.data()); +// if (!ba) +// return; +// if (name == length) { +// resize(*ba, value.toInt32()); +// } else { +// qint32 pos = id; +// if (pos < 0) +// return; +// if (ba->size() <= pos) +// resize(*ba, pos + 1); +// (*ba)[pos] = char(value.toInt32()); +// } +//} +// +//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, +// const QScriptString& name, uint id) { +// return QScriptValue::Undeletable; +//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h new file mode 100644 index 0000000000..eb4a372843 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -0,0 +1,47 @@ +// +// ArrayBufferViewClass.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBufferViewClass_h +#define hifi_ArrayBufferViewClass_h + +#include +#include +#include +#include +#include +#include +#include + +static const QString BUFFER_PROPERTY_NAME = "buffer"; +static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +class ArrayBufferViewClass : public QObject, public QScriptClass { + Q_OBJECT +public: + ArrayBufferViewClass(QScriptEngine* engine); + + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); +protected: + // JS Object attributes + QScriptString _bufferName; + QScriptString _byteOffsetName; + QScriptString _byteLengthName; +}; + +#endif // hifi_ArrayBufferViewClass_h \ No newline at end of file From 2e480764088cba80f67dbc9fa7f73a86f0d116cc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:45 -0700 Subject: [PATCH 07/43] Added first draft of DataView scriptable objects --- libraries/script-engine/src/DataViewClass.cpp | 81 +++++++ libraries/script-engine/src/DataViewClass.h | 36 +++ .../script-engine/src/DataViewPrototype.cpp | 209 ++++++++++++++++++ .../script-engine/src/DataViewPrototype.h | 68 ++++++ 4 files changed, 394 insertions(+) create mode 100644 libraries/script-engine/src/DataViewClass.cpp create mode 100644 libraries/script-engine/src/DataViewClass.h create mode 100644 libraries/script-engine/src/DataViewPrototype.cpp create mode 100644 libraries/script-engine/src/DataViewPrototype.h diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp new file mode 100644 index 0000000000..7afd43e71c --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -0,0 +1,81 @@ +// +// DataViewClass.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 "DataViewPrototype.h" + +#include "DataViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +static const QString DATA_VIEW_NAME = "DataView"; + +DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new DataViewPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, byteLentgh); + + return engine()->newObject(this, data); +} + +QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { + DataViewClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue bufferArg = context->argument(0); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + + QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; + if (!arrayBuffer) { + return QScriptValue(); + } + + QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QString DataViewClass::name() const { + return _name.toString(); +} + +QScriptValue DataViewClass::prototype() const { + return _proto; +} \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h new file mode 100644 index 0000000000..7e4d427f09 --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.h @@ -0,0 +1,36 @@ +// +// DataViewClass.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_DataViewClass_h +#define hifi_DataViewClass_h + +#include "ArrayBufferViewClass.h" + +class DataViewClass : public ArrayBufferViewClass { + Q_OBJECT +public: + DataViewClass(QScriptEngine* engine); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + + QString name() const; + QScriptValue prototype() const; + +private: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; +}; + + +#endif // hifi_DataViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp new file mode 100644 index 0000000000..345df743b4 --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -0,0 +1,209 @@ +// +// DataViewPrototype.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 + +#include + +#include "DataViewClass.h" + +#include "DataViewPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* DataViewPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { + quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); + quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); + qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + qDebug() << "Offset: " << offset << ", Size: " << size; + offset += viewOffset; + qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + return (offset + size) <= viewOffset + viewLength; +} + +///////////////// GETTERS //////////////////////////// + +qint8 DataViewPrototype::getInt8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(qint8))) { + qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); + + return (qint8)thisArrayBuffer()->at(byteOffset); + } + qDebug() << "42 powaaaa!!!"; + return 42; +} + +quint8 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + } + return 42; +} + +qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +///////////////// SETTERS //////////////////////////// + +void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { + if (realOffset(byteOffset, sizeof(qint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + + diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h new file mode 100644 index 0000000000..a024e8938f --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -0,0 +1,68 @@ +// +// DataViewPrototype.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_DataViewPrototype_h +#define hifi_DataViewPrototype_h + +#include +#include + +class DataViewPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + DataViewPrototype(QObject* parent = NULL); + +public slots: + // Gets the value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be fetched from any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether a big-endian or little-endian value should be + // read. If false or undefined, a big-endian value is read. + // + // These methods raise an exception if they would read + // beyond the end of the view. + qint8 getInt8(quint32 byteOffset); + quint8 getUint8(quint32 byteOffset); + qint16 getInt16(quint32 byteOffset, bool littleEndian = false); + quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt32(quint32 byteOffset, bool littleEndian = false); + quint32 getUint32(quint32 byteOffset, bool littleEndian = false); + float getFloat32(quint32 byteOffset, bool littleEndian = false); + double getFloat64(quint32 byteOffset, bool littleEndian = false); + + // Stores a value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be stored at any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether the value should be stored in big-endian or + // little-endian byte order. If false or undefined, the value is + // stored in big-endian byte order. + // + // These methods raise an exception if they would write + // beyond the end of the view. + void setInt8(quint32 byteOffset, qint8 value); + void setUint8(quint32 byteOffset, quint8 value); + void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + +private: + QByteArray* thisArrayBuffer() const; + bool realOffset(quint32& offset, size_t size) const; +}; + +#endif // hifi_DataViewPrototype_h \ No newline at end of file From 7fe88a4426cb609757733a7cbf087b4608e71620 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:10:22 -0700 Subject: [PATCH 08/43] Hooked up ArrayBuffer, ArrayBufferView and DataView to ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fab50784a2..59a9563c56 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -33,6 +33,8 @@ #include "AnimationObject.h" #include "ArrayBufferClass.h" +#include "ArrayBufferViewClass.h" +#include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -213,6 +215,8 @@ void ScriptEngine::init() { } new ArrayBufferClass(&_engine); + new ArrayBufferViewClass(&_engine); + new DataViewClass(&_engine); _isInitialized = true; From 4b2e4b7b28a35a33f0dc167225047e6efbf195b5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:59:27 -0700 Subject: [PATCH 09/43] Some type modifications in DataViewPrototype --- .../script-engine/src/DataViewPrototype.cpp | 66 +++++++++++-------- .../script-engine/src/DataViewPrototype.h | 18 ++--- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 345df743b4..2965f98809 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -38,23 +38,31 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint8 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(quint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { - qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); - - return (qint8)thisArrayBuffer()->at(byteOffset); - } - qDebug() << "42 powaaaa!!!"; - return 42; -} - -quint8 DataViewPrototype::getUint8(quint32 byteOffset) { - if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + qint8 result; + stream >> result; + return result; } return 42; } -qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + quint8 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); @@ -67,13 +75,13 @@ qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 42; } -quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint16 result; stream >> result; return result; } @@ -86,7 +94,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + qint32 result; stream >> result; return result; } @@ -99,7 +107,7 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint32 result; stream >> result; return result; } @@ -112,7 +120,7 @@ float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + float result; stream >> result; return result; } @@ -125,7 +133,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + double result; stream >> result; return result; } @@ -134,48 +142,48 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { +void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint8)value; } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { +void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint8)value; } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { +void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint16)value; } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { +void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint16)value; } } -void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint32)value; } } @@ -184,7 +192,7 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint32)value; } } diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index a024e8938f..97c94c58ce 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,10 +31,10 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint8 getInt8(quint32 byteOffset); - quint8 getUint8(quint32 byteOffset); - qint16 getInt16(quint32 byteOffset, bool littleEndian = false); - quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(quint32 byteOffset); + quint32 getUint8(quint32 byteOffset); + qint32 getInt16(quint32 byteOffset, bool littleEndian = false); + quint32 getUint16(quint32 byteOffset, bool littleEndian = false); qint32 getInt32(quint32 byteOffset, bool littleEndian = false); quint32 getUint32(quint32 byteOffset, bool littleEndian = false); float getFloat32(quint32 byteOffset, bool littleEndian = false); @@ -51,11 +51,11 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint8 value); - void setUint8(quint32 byteOffset, quint8 value); - void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt8(quint32 byteOffset, qint32 value); + void setUint8(quint32 byteOffset, quint32 value); + void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); From 28438d66b1a5e31f25b085cf6bda2fc6fbeefe93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:27:23 -0700 Subject: [PATCH 10/43] Fixed exceptions/endian issues with DataViews --- libraries/script-engine/src/DataViewClass.cpp | 23 ++++++- libraries/script-engine/src/DataViewClass.h | 2 +- .../script-engine/src/DataViewPrototype.cpp | 64 ++++++++++++++----- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 7afd43e71c..38a85e55a8 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -54,15 +54,32 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } QScriptValue bufferArg = context->argument(0); - QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); - QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(); QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; if (!arrayBuffer) { + engine->evaluate("throw \"TypeError: 1st argument not a ArrayBuffer\""); return QScriptValue(); } + if (byteOffsetArg.isNumber() && + (byteOffsetArg.toInt32() < 0 || + byteOffsetArg.toInt32() >= arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + if (byteLengthArg.isNumber() && + (byteLengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + byteLengthArg.toInt32() > arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } - QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + QScriptValue newObject = cls->newInstance(bufferArg, + (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() + : 0, + (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() + : arrayBuffer->size()); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 7e4d427f09..6b6d6d25e7 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -18,7 +18,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: DataViewClass(QScriptEngine* engine); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; QScriptValue prototype() const; diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 2965f98809..17734e109c 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -29,10 +29,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - qDebug() << "Offset: " << offset << ", Size: " << size; + //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -47,7 +47,8 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint8(quint32 byteOffset) { @@ -59,85 +60,92 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); float result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); double result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } ///////////////// SETTERS //////////////////////////// @@ -148,6 +156,8 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { stream.skipRawData(byteOffset); stream << (qint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -157,6 +167,8 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { stream.skipRawData(byteOffset); stream << (quint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -164,8 +176,11 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -173,8 +188,11 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -182,8 +200,11 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -191,8 +212,11 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -200,8 +224,11 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -209,8 +236,11 @@ void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool little if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } From a76ebae771c6bd4e0e5e73e94492b8219d224659 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:28:50 -0700 Subject: [PATCH 11/43] Added typed array unit test --- examples/typedArraysUnitTest.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 98f69f3e71..6bd35365e8 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,12 +11,31 @@ Script.include("Test.js"); -test("ArrayBuffer", function(finished) { - this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); +test('ArrayBuffer', function(finished) { + this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); -}); \ No newline at end of file +}); + +test('DataView constructors', function (finished) { + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); From b2b17019658055b9c08221421e3fdff4ebd17eee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 17:47:09 -0700 Subject: [PATCH 12/43] - Bunch of fixes to typed arrays - First working prototype for Int8Array - Base classes for all other typed arrays --- .../script-engine/src/ArrayBufferClass.cpp | 28 ++- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferViewClass.cpp | 13 +- .../script-engine/src/ArrayBufferViewClass.h | 13 +- libraries/script-engine/src/DataViewClass.cpp | 14 +- libraries/script-engine/src/DataViewClass.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 28 ++- libraries/script-engine/src/ScriptEngine.h | 6 + libraries/script-engine/src/TypedArrays.cpp | 233 ++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 104 ++++++++ 10 files changed, 412 insertions(+), 37 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrays.cpp create mode 100644 libraries/script-engine/src/TypedArrays.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d003125c1e..d86e229a1a 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ArrayBufferPrototype.h" +#include "ScriptEngine.h" #include "ArrayBufferClass.h" @@ -19,16 +21,20 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - QScriptValue global = engine->globalObject(); +ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { + qDebug() << "Created with engine: " << engine(); + qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(CLASS_NAME.toLatin1()); - _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _name = engine()->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new ArrayBufferPrototype(this), + _proto = engine()->newQObject(new ArrayBufferPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -36,15 +42,17 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + QScriptEngine* eng = engine(); + QVariant variant = QVariant::fromValue(QByteArray(size, 0)); + QScriptValue data = eng->newVariant(variant); return engine()->newObject(this, data); } diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index 2f863e77a5..c56b659eff 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -20,13 +20,17 @@ #include #include +class ScriptEngine; + class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferClass(QScriptEngine* engine); + ArrayBufferClass(ScriptEngine* scriptEngine); QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); + ScriptEngine* getScriptEngine() { return _scriptEngine; } + QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); @@ -50,6 +54,8 @@ private: // JS Object attributes QScriptString _name; QScriptString _byteLength; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index 73d833dab4..a01f239512 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -13,13 +13,14 @@ Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : -QObject(engine), -QScriptClass(engine) { +ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { // Save string handles for quick lookup - _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); - _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); - _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine()->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); } QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index eb4a372843..acc061c8a3 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -20,6 +20,8 @@ #include #include +#include "ScriptEngine.h" + static const QString BUFFER_PROPERTY_NAME = "buffer"; static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; @@ -27,21 +29,24 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferViewClass(QScriptEngine* engine); + ArrayBufferViewClass(ScriptEngine* scriptEngine); + ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; QScriptString _byteOffsetName; QScriptString _byteLengthName; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 38a85e55a8..4a9420e4e9 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -17,14 +17,14 @@ Q_DECLARE_METATYPE(QByteArray*) static const QString DATA_VIEW_NAME = "DataView"; -DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { - QScriptValue global = engine->globalObject(); +DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass(scriptEngine) { + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + _name = engine()->toStringHandle(DATA_VIEW_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new DataViewPrototype(this), + _proto = engine()->newQObject(new DataViewPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -32,10 +32,10 @@ DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engin _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 6b6d6d25e7..b87803f4b4 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -17,7 +17,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: - DataViewClass(QScriptEngine* engine); + DataViewClass(ScriptEngine* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f9161b9e51..73983161ff 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,13 +32,13 @@ #include #include "AnimationObject.h" -#include "ArrayBufferClass.h" #include "ArrayBufferViewClass.h" #include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "XMLHttpRequestClass.h" VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; @@ -93,8 +93,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -118,13 +125,21 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - + + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { @@ -141,6 +156,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { + qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } @@ -213,10 +229,6 @@ void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once } - - new ArrayBufferClass(&_engine); - new ArrayBufferViewClass(&_engine); - new DataViewClass(&_engine); _isInitialized = true; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5b01b8124a..3dda172044 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -25,6 +25,7 @@ #include #include "AbstractControllerScriptingInterface.h" +#include "ArrayBufferClass.h" #include "Quat.h" #include "ScriptUUID.h" #include "Vec3.h" @@ -56,6 +57,9 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } + QScriptEngine* getScriptEngine() { return &_engine; } + ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); @@ -147,6 +151,8 @@ private: Vec3 _vec3Library; ScriptUUID _uuidLibrary; AnimationCache _animationCache; + + ArrayBufferClass* _arrayBufferClass; QHash _outgoingScriptAudioSequenceNumbers; }; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp new file mode 100644 index 0000000000..dc208635b8 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -0,0 +1,233 @@ +// +// TypedArrays.cpp +// +// +// Created by Clement on 7/9/14. +// Copyright 2014 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 "ScriptEngine.h" + +#include "TypedArrays.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) { + _bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1()); + _lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1()); + _name = engine()->toStringHandle(name.toLatin1()); +} + +QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { + TypedArray* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue newObject; + QScriptValue bufferArg = context->argument(0); + QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + TypedArray* typedArray = qscriptvalue_cast(bufferArg); + + if (arrayBuffer) { + if (context->argumentCount() == 1) { + // Case for entire ArrayBuffer + newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size()); + } else { + QScriptValue byteOffsetArg = context->argument(1); + if (!byteOffsetArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); + return QScriptValue(); + } + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + quint32 byteOffset = byteOffsetArg.toInt32(); + + if (context->argumentCount() == 2) { + // case for end of ArrayBuffer + newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + } else { + + QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); + if (!lengthArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\""); + return QScriptValue(); + } + if (lengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + lengthArg.toInt32() * cls->_bytesPerElement > arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } + quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + + // case for well-defined range + newObject = cls->newInstance(bufferArg, byteOffset, length); + } + } + } else if (typedArray) { + // case for another reference to another TypedArray + newObject = cls->newInstance(bufferArg, true); + } else if (context->argument(0).isNumber()) { + // case for new ArrayBuffer + newObject = cls->newInstance(context->argument(0).toInt32()); + } else { + // TODO: Standard array case + } + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bytesPerElementName || name == _lengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); +} + +QScriptValue TypedArray::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bytesPerElementName) { + return QScriptValue(_bytesPerElement); + } + if (name == _lengthName) { + return object.data().property(_lengthName); + } + return ArrayBufferViewClass::property(object, name, id); +} + +QScriptValue::PropertyFlags TypedArray::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString TypedArray::name() const { + return _name.toString(); +} + +QScriptValue TypedArray::prototype() const { + return _proto; +} + +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + QScriptValue global = engine()->globalObject(); + + _bytesPerElement = sizeof(qint8); + + // build prototype +// _proto = engine->newQObject(new DataViewPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration | +// QScriptEngine::ExcludeSuperClassMethods | +// QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(name(), _ctor); +} + +QScriptValue Int8ArrayClass::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { + // TODO + assert(false); + if (isTypedArray) { + QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); + ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); + quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); + quint32 byteLength = array.data().property(_byteLengthName).toInt32(); + quint32 length = byteLength / _bytesPerElement; + QByteArray newArray = originalArray->mid(byteOffset, byteLength); + + if (byteLength % _bytesPerElement != 0) { + length += 1; + byteLength = length * _bytesPerElement; + } + + + return newInstance(arrayBufferClass->newInstance(newArray), + array.data().property(_byteOffsetName).toInt32(), + array.data().property(_lengthName).toInt32()); + } + return QScriptValue(); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + +QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + + return TypedArray::queryProperty(object, name, flags, id); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + + qint8 result; + stream >> result; + return result; + } + + return TypedArray::property(object, name, id); +} + +void Int8ArrayClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value) { + qDebug() << "ID: " << id << ", Value: " << value.toInt32(); + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + + qDebug() << "Adding: " << (qint8)value.toInt32(); + stream << (qint8)value.toInt32(); + } +} + + + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h new file mode 100644 index 0000000000..ea43ca2494 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.h @@ -0,0 +1,104 @@ +// +// TypedArrays.h +// +// +// Created by Clement on 7/9/14. +// Copyright 2014 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 +// + +#ifndef hifi_TypedArrays_h +#define hifi_TypedArrays_h + +#include "ArrayBufferViewClass.h" + +static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT"; +static const QString LENGTH_PROPERTY_NAME = "length"; + +static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; + +class TypedArray : public ArrayBufferViewClass { + Q_OBJECT +public: + TypedArray(ScriptEngine* scriptEngine, QString name); + virtual QScriptValue newInstance(quint32 length) = 0; + virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; + virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + + virtual QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); + + QString name() const; + QScriptValue prototype() const; + +protected: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; + QScriptString _bytesPerElementName; + QScriptString _lengthName; + + quint32 _bytesPerElement; +}; + +class Int8ArrayClass : public TypedArray { + Q_OBJECT +public: + Int8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue newInstance(quint32 length); + QScriptValue newInstance(QScriptValue array, bool isTypedArray); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value); +}; + +class Uint8ArrayClass : public TypedArray { + +}; + +class Uint8ClampedArrayClass : public TypedArray { + +}; + +class Int16ArrayClass : public TypedArray { + +}; + +class Uint16ArrayClass : public TypedArray { + +}; + +class Int32ArrayClass : public TypedArray { + +}; + +class Uint32ArrayClass : public TypedArray { + +}; + +class Float32ArrayClass : public TypedArray { + +}; + +class Float64ArrayClass : public TypedArray { + +}; + +#endif // hifi_TypedArrays_h \ No newline at end of file From b4eb6a2eb7a618120a83d09fb9aa73b5e7eb5a36 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 10:09:49 -0700 Subject: [PATCH 13/43] Some refactoring --- libraries/script-engine/src/TypedArrays.cpp | 63 +++++++++------------ libraries/script-engine/src/TypedArrays.h | 3 - 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index dc208635b8..121870443f 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -19,6 +19,22 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi _bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1()); _lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1()); _name = engine()->toStringHandle(name.toLatin1()); + + QScriptValue global = engine()->globalObject(); + + // build prototype TODO + // _proto = engine->newQObject(new DataViewPrototype(this), + // QScriptEngine::QtOwnership, + // QScriptEngine::SkipMethodsInEnumeration | + // QScriptEngine::ExcludeSuperClassMethods | + // QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(_name, _ctor); } QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { @@ -93,6 +109,18 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, if (name == _bytesPerElementName || name == _lengthName) { return flags &= HandlesReadAccess; // Only keep read access flags } + + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); } @@ -121,23 +149,7 @@ QScriptValue TypedArray::prototype() const { } Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - QScriptValue global = engine()->globalObject(); - _bytesPerElement = sizeof(qint8); - - // build prototype -// _proto = engine->newQObject(new DataViewPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration | -// QScriptEngine::ExcludeSuperClassMethods | -// QScriptEngine::ExcludeSuperClassProperties); - - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine()->newFunction(construct, _proto); - _ctor.setData(engine()->toScriptValue(this)); - - engine()->globalObject().setProperty(name(), _ctor); } QScriptValue Int8ArrayClass::newInstance(quint32 length) { @@ -180,23 +192,6 @@ QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset return engine()->newObject(this, data); } -QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); - quint32 length = object.data().property(_lengthName).toInt32(); - bool ok = false; - int pos = name.toArrayIndex(&ok); - - // Check that name is a valid index and arrayBuffer exists - if (ok && pos >= 0 && pos < length) { - *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation - return HandlesReadAccess | HandlesWriteAccess; // Read/Write access - } - - return TypedArray::queryProperty(object, name, flags, id); -} - QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); @@ -218,13 +213,11 @@ QScriptValue Int8ArrayClass::property(const QScriptValue &object, void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { - qDebug() << "ID: " << id << ", Value: " << value.toInt32(); QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); - qDebug() << "Adding: " << (qint8)value.toInt32(); stream << (qint8)value.toInt32(); } } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index ea43ca2494..2c63d83f93 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -59,9 +59,6 @@ public: QScriptValue newInstance(QScriptValue array, bool isTypedArray); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); void setProperty(QScriptValue &object, From 0e5af5afeab679aad91a891fd03dc9146d089839 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:32:55 -0700 Subject: [PATCH 14/43] More work on typed arrays --- libraries/script-engine/src/TypedArrays.cpp | 341 ++++++++++++++++---- libraries/script-engine/src/TypedArrays.h | 89 ++++- 2 files changed, 351 insertions(+), 79 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 121870443f..d6ffc8f5a6 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "ScriptEngine.h" #include "TypedArrays.h" @@ -22,12 +24,12 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptValue global = engine()->globalObject(); - // build prototype TODO - // _proto = engine->newQObject(new DataViewPrototype(this), - // QScriptEngine::QtOwnership, - // QScriptEngine::SkipMethodsInEnumeration | - // QScriptEngine::ExcludeSuperClassMethods | - // QScriptEngine::ExcludeSuperClassProperties); + // build prototype + _proto = engine()->newQObject(new TypedArrayPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); _proto.setPrototype(global.property("Object").property("prototype")); @@ -37,16 +39,49 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi engine()->globalObject().setProperty(_name, _ctor); } +QScriptValue TypedArray::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue TypedArray::newInstance(QScriptValue array) { + if (array.property("length").isValid()) { + quint32 length = array.property("length").toInt32(); + QScriptValue newArray = newInstance(length); + for (int i = 0; i < length; ++i) { + QScriptValue value = array.property(QString::number(i)); + setProperty(newArray, engine()->toStringHandle(QString::number(i)), + i * _bytesPerElement, (value.isNumber()) ? value : QScriptValue(0)); + } + return newArray; + } + return QScriptValue(); +} + +QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); - if (!cls || context->argumentCount() < 1) { + if (!cls) { return QScriptValue(); } + if (context->argumentCount() == 0) { + return cls->newInstance(0); + } + QScriptValue newObject; QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); - TypedArray* typedArray = qscriptvalue_cast(bufferArg); if (arrayBuffer) { if (context->argumentCount() == 1) { @@ -58,15 +93,22 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); return QScriptValue(); } - if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } + if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } quint32 byteOffset = byteOffsetArg.toInt32(); if (context->argumentCount() == 2) { // case for end of ArrayBuffer - newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } + quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement; + newObject = cls->newInstance(bufferArg, byteOffset, length); } else { QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); @@ -79,20 +121,17 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + quint32 length = lengthArg.toInt32(); // case for well-defined range newObject = cls->newInstance(bufferArg, byteOffset, length); } } - } else if (typedArray) { - // case for another reference to another TypedArray - newObject = cls->newInstance(bufferArg, true); } else if (context->argument(0).isNumber()) { // case for new ArrayBuffer newObject = cls->newInstance(context->argument(0).toInt32()); } else { - // TODO: Standard array case + newObject = cls->newInstance(bufferArg); } if (context->isCalledAsConstructor()) { @@ -148,79 +187,257 @@ QScriptValue TypedArray::prototype() const { return _proto; } -Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - _bytesPerElement = sizeof(qint8); +void TypedArray::setBytesPerElement(quint32 bytesPerElement) { + _bytesPerElement = bytesPerElement; + _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -QScriptValue Int8ArrayClass::newInstance(quint32 length) { - ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); - QScriptValue buffer = array->newInstance(length * _bytesPerElement); - return newInstance(buffer, 0, length); +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { } -QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { - // TODO - assert(false); - if (isTypedArray) { - QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); - ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); - quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); - quint32 byteLength = array.data().property(_byteLengthName).toInt32(); - quint32 length = byteLength / _bytesPerElement; - QByteArray newArray = originalArray->mid(byteOffset, byteLength); - - if (byteLength % _bytesPerElement != 0) { - length += 1; - byteLength = length * _bytesPerElement; +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray()) { + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + // TODO throw an error maybe? + return; } - - - return newInstance(arrayBufferClass->newInstance(newArray), - array.data().property(_byteOffsetName).toInt32(), - array.data().property(_lengthName).toInt32()); + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } + // TODO handle typed arrays +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); } return QScriptValue(); } -QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { - QScriptValue data = engine()->newObject(); - data.setProperty(_bufferName, buffer); - data.setProperty(_byteOffsetName, byteOffset); - data.setProperty(_byteLengthName, length * _bytesPerElement); - data.setProperty(_lengthName, length); - - return engine()->newObject(this, data); +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); +template +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { bool ok = false; name.toArrayIndex(&ok); - + if (ok && arrayBuffer) { QDataStream stream(*arrayBuffer); stream.skipRawData(id); - qint8 result; + T result; stream >> result; return result; } - - return TypedArray::property(object, name, id); + return QScriptValue(); } -void Int8ArrayClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - if (ba && value.isNumber()) { - QDataStream stream(ba, QIODevice::ReadWrite); +template +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { + if (arrayBuffer && value.isNumber()) { + QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); - stream << (qint8)value.toInt32(); + stream << (T)value.toNumber(); } } +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint8)); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint16)); +} + +QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint16)); +} + +QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint32)); +} + +QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint32)); +} + +QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(float)); +} + +QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(double)); +} + +QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2c63d83f93..37d341b2ad 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -18,20 +18,28 @@ static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT"; static const QString LENGTH_PROPERTY_NAME = "length"; static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; +static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array"; +static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array"; +static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array"; +static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array"; +static const QString UINT_32_ARRAY_CLASS_NAME = "Uint32Array"; +static const QString FLOAT_32_ARRAY_CLASS_NAME = "Float32Array"; +static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array"; class TypedArray : public ArrayBufferViewClass { Q_OBJECT public: TypedArray(ScriptEngine* scriptEngine, QString name); - virtual QScriptValue newInstance(quint32 length) = 0; - virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; - virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + virtual QScriptValue newInstance(quint32 length); + virtual QScriptValue newInstance(QScriptValue array); + virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); @@ -41,6 +49,8 @@ public: protected: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + void setBytesPerElement(quint32 bytesPerElement); + QScriptValue _proto; QScriptValue _ctor; @@ -49,53 +59,98 @@ protected: QScriptString _lengthName; quint32 _bytesPerElement; + + friend class TypedArrayPrototype; +}; + +class TypedArrayPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + TypedArrayPrototype(QObject* parent = NULL); + +public slots: + void set(QScriptValue array, quint32 offset = 0); + QScriptValue subarray(qint32 begin); + QScriptValue subarray(qint32 begin, qint32 end); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; }; class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 length); - QScriptValue newInstance(QScriptValue array, bool isTypedArray); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); - void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ClampedArrayClass : public TypedArray { - + // TODO }; class Int16ArrayClass : public TypedArray { + Q_OBJECT +public: + Int16ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint16ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint16ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Int32ArrayClass : public TypedArray { + Q_OBJECT +public: + Int32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint32ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Float32ArrayClass : public TypedArray { + Q_OBJECT +public: + Float32ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Float64ArrayClass : public TypedArray { + Q_OBJECT +public: + Float64ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From ff378b60acf9eddebbd680788c1955c82190b93e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:24 -0700 Subject: [PATCH 15/43] Added functionnalities to JS UnitTests --- examples/Test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 056ec3cbbf..646ae5c71d 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -65,4 +65,16 @@ UnitTest.prototype.assertNull = function(value, message) { if (value !== null) { throw new AssertionException(value, null, message); } -}; +} + +UnitTest.prototype.arrayEqual = function(array1, array2, message) { + this.numAssertions++; + if (array1.length !== array2.length) { + throw new AssertionException(array1.length , array2.length , message); + } + for (var i = 0; i < array1.length; ++i) { + if (array1[i] !== array2[i]) { + throw new AssertionException(array1[i], array2[i], message); + } + } +} \ No newline at end of file From 3070ac457f12e141a8df6997bcd2580cdf177ead Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:57 -0700 Subject: [PATCH 16/43] Typed arrays bug fixing --- libraries/script-engine/src/DataViewClass.cpp | 9 ++---- libraries/script-engine/src/ScriptEngine.cpp | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 4a9420e4e9..938770ecf5 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -74,12 +74,9 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - - QScriptValue newObject = cls->newInstance(bufferArg, - (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() - : 0, - (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() - : arrayBuffer->size()); + quint32 byteOffset = (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() : 0; + quint32 byteLength = (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() : arrayBuffer->size() - byteOffset; + QScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73983161ff..4074beb88e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -97,11 +97,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _arrayBufferClass(NULL) { _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -134,11 +138,15 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QUrl url(scriptURL); _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; From 02c951fba5ed9a4c22e1fadc5102454e1f9b45a2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:34:17 -0700 Subject: [PATCH 17/43] Added bunch of Typed arrays unit tests --- examples/typedArraysUnitTest.js | 343 ++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 6bd35365e8..de9b487c32 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -19,6 +19,9 @@ test('ArrayBuffer', function(finished) { this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); + + // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); + // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,3 +42,343 @@ test('DataView constructors', function (finished) { // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); + + +test('ArrayBufferView', function () { + var ab = new ArrayBuffer(48); + var i32 = new Int32Array(ab, 16); + i32.set([1, 2, 3, 4, 5, 6, 7, 8]); + + this.assertEquals(i32.buffer, ab, 'ArrayBuffers equal'); + this.assertEquals(i32.byteOffset, 16, 'byteOffset'); + this.assertEquals(i32.byteLength, 32, 'byteLength'); + + var da = new DataView(i32.buffer, 8); + this.assertEquals(da.buffer, ab, 'DataView: ArrayBuffers equal'); + this.assertEquals(da.byteOffset, 8, 'DataView: byteOffset'); + this.assertEquals(da.byteLength, 40, 'DataView: byteLength'); +}); + +test('TypedArrays', function () { + var a; + + this.assertEquals(Int8Array.BYTES_PER_ELEMENT, 1, 'Int8Array.BYTES_PER_ELEMENT'); + a = new Int8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'int8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'int8Array.byteLength'); + + this.assertEquals(Uint8Array.BYTES_PER_ELEMENT, 1, 'Uint8Array.BYTES_PER_ELEMENT'); + a = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'uint8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'uint8Array.byteLength'); + + this.assertEquals(Int16Array.BYTES_PER_ELEMENT, 2, 'Int16Array.BYTES_PER_ELEMENT'); + a = new Int16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'int16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'int16Array.byteLength'); + + this.assertEquals(Uint16Array.BYTES_PER_ELEMENT, 2, 'Uint16Array.BYTES_PER_ELEMENT'); + a = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'uint16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'uint16Array.byteLength'); + + this.assertEquals(Int32Array.BYTES_PER_ELEMENT, 4, 'Int32Array.BYTES_PER_ELEMENT'); + a = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'int32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'int32Array.byteLength'); + + this.assertEquals(Uint32Array.BYTES_PER_ELEMENT, 4, 'Uint32Array.BYTES_PER_ELEMENT'); + a = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'uint32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'uint32Array.byteLength'); + + this.assertEquals(Float32Array.BYTES_PER_ELEMENT, 4, 'Float32Array.BYTES_PER_ELEMENT'); + a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'float32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'float32Array.byteLength'); + + this.assertEquals(Float64Array.BYTES_PER_ELEMENT, 8, 'Float64Array.BYTES_PER_ELEMENT'); + a = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 8, 'float64Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float64Array.byteOffset'); + this.assertEquals(a.byteLength, 64, 'float64Array.byteLength'); +}); + + +test('typed array constructors', function () { + this.arrayEqual(new Int8Array({ length: 3 }), [0, 0, 0], 'array equal -1'); + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + + var int8 = new Int8Array(); + this.assertEquals(int8.length, 0, 'no args 0'); + // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); + // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + + int8 = new Int8Array(4); + this.assertEquals(int8.BYTES_PER_ELEMENT, 1); + this.assertEquals(int8.length, 4, 'length 1'); + this.assertEquals(int8.byteLength, 4, 'length 2'); + this.assertEquals(int8.byteOffset, 0, 'length 3'); + this.assertEquals(int8.get(-1), undefined, 'length, out of bounds 4'); + this.assertEquals(int8.get(4), undefined, 'length, out of bounds 5'); + + int8 = new Int8Array([1, 2, 3, 4, 5, 6]); + this.assertEquals(int8.length, 6, 'array 6'); + this.assertEquals(int8.byteLength, 6, 'array 7'); + this.assertEquals(int8.byteOffset, 0, 'array 8'); + this.assertEquals(int8.get(3), 4, 'array 9'); + this.assertEquals(int8.get(-1), undefined, 'array, out of bounds 10'); + this.assertEquals(int8.get(6), undefined, 'array, out of bounds 11'); + + int8 = new Int8Array(rawbuf); + this.assertEquals(int8.length, 8, 'buffer 12'); + this.assertEquals(int8.byteLength, 8, 'buffer 13'); + this.assertEquals(int8.byteOffset, 0, 'buffer 14'); + this.assertEquals(int8.get(7), 7, 'buffer 15'); + int8.set([111]); + this.assertEquals(int8.get(0), 111, 'buffer 16'); + this.assertEquals(int8.get(-1), undefined, 'buffer, out of bounds 17'); + this.assertEquals(int8.get(8), undefined, 'buffer, out of bounds 18'); + + int8 = new Int8Array(rawbuf, 2); + this.assertEquals(int8.length, 6, 'buffer, byteOffset 19'); + this.assertEquals(int8.byteLength, 6, 'buffer, byteOffset 20'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset 21'); + this.assertEquals(int8.get(5), 7, 'buffer, byteOffset 22'); + int8.set([112]); + this.assertEquals(int8.get(0), 112, 'buffer 23'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, out of bounds 24'); + this.assertEquals(int8.get(6), undefined, 'buffer, byteOffset, out of bounds 25'); + + int8 = new Int8Array(rawbuf, 8); + this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); + + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); + // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + + int8 = new Int8Array(rawbuf, 2, 4); + this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); + this.assertEquals(int8.byteLength, 4, 'buffer, byteOffset, length 32'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset, length 33'); + this.assertEquals(int8.get(3), 5, 'buffer, byteOffset, length 34'); + int8.set([113]); + this.assertEquals(int8.get(0), 113, 'buffer, byteOffset, length 35'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); + this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); + + // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + +test('TypedArray clone constructor', function () { + var src = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + var dst = new Int32Array(src); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '1'); + src.set([99]); + this.arrayEqual(src, [99, 2, 3, 4, 5, 6, 7, 8], '2'); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '3'); +}); + + +test('conversions', function () { + var uint8 = new Uint8Array([1, 2, 3, 4]), + uint16 = new Uint16Array(uint8.buffer), + uint32 = new Uint32Array(uint8.buffer); + + // Note: can't probe individual bytes without endianness awareness + this.arrayEqual(uint8, [1, 2, 3, 4]); + uint16.set([0xffff]); + this.arrayEqual(uint8, [0xff, 0xff, 3, 4]); + uint16.set([0xeeee], 1); + this.arrayEqual(uint8, [0xff, 0xff, 0xee, 0xee]); + uint32.set([0x11111111]); + this.assertEquals(uint16.get(0), 0x1111); + this.assertEquals(uint16.get(1), 0x1111); + this.arrayEqual(uint8, [0x11, 0x11, 0x11, 0x11]); +}); + + +test('signed/unsigned conversions', function () { + + var int8 = new Int8Array(1), uint8 = new Uint8Array(int8.buffer); + uint8.set([123]); + this.assertEquals(int8.get(0), 123, 'int8/uint8'); + uint8.set([161]); + this.assertEquals(int8.get(0), -95, 'int8/uint8'); + int8.set([-120]); + this.assertEquals(uint8.get(0), 136, 'uint8/int8'); + int8.set([-1]); + this.assertEquals(uint8.get(0), 0xff, 'uint8/int8'); + + var int16 = new Int16Array(1), uint16 = new Uint16Array(int16.buffer); + uint16.set([3210]); + this.assertEquals(int16.get(0), 3210, 'int16/uint16'); + uint16.set([49232]); + this.assertEquals(int16.get(0), -16304, 'int16/uint16'); + int16.set([-16384]); + this.assertEquals(uint16.get(0), 49152, 'uint16/int16'); + int16.set([-1]); + this.assertEquals(uint16.get(0), 0xffff, 'uint16/int16'); + + var int32 = new Int32Array(1), uint32 = new Uint32Array(int32.buffer); + uint32.set([0x80706050]); + this.assertEquals(int32.get(0), -2140118960, 'int32/uint32'); + int32.set([-2023406815]); + this.assertEquals(uint32.get(0), 0x87654321, 'uint32/int32'); + int32.set([-1]); + this.assertEquals(uint32.get(0), 0xffffffff, 'uint32/int32'); +}); + + +test('Int32Array round trips', function () { + var i32 = new Int32Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0x80000000 >> 0, + 0x7fffffff >> 0, + 0x12345678 >> 0, + 0x87654321 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i32.set([datum]); + this.assertEquals(datum, i32.get(0), String(datum)); + } +}); + + +test('Int16Array round trips', function () { + var i16 = new Int16Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0xffff8000 >> 0, + 0x00007fff >> 0, + 0x00001234 >> 0, + 0xffff8765 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i16.set([datum]); + this.assertEquals(datum, i16.get(0), String(datum)); + } +}); + + +test('Int8Array round trips', function () { + var i8 = new Int8Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -45, + 0xffffff80 >> 0, + 0x0000007f >> 0, + 0x00000012 >> 0, + 0xffffff87 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i8.set([datum]); + this.assertEquals(datum, i8.get(0), String(datum)); + } +}); + +test('TypedArray setting', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + var b = new Int32Array(5); + b.set(a); + this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); + // raises(function () { b.set(a, 1); }); + + b.set(new Int32Array([99, 98]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); + + b.set(new Int32Array([99, 98, 97]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); + + // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + // raises(function () { b.set([101, 102, 103, 104], 4); }); + + // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] + // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] + // a2 = [ ^, ^, ^, ^ ] + var ab = new ArrayBuffer(8); + var a1 = new Uint8Array(ab); + for (var i = 0; i < a1.length; i += 1) { a1.set([i], i); } + var a2 = new Uint8Array(ab, 4); + a1.set(a2, 2); + this.arrayEqual(a1, [0, 1, 4, 5, 6, 7, 6, 7]); + this.arrayEqual(a2, [6, 7, 6, 7]); +}); + + +test('TypedArray.subarray', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + this.arrayEqual(a.subarray(3), [4, 5]); + this.arrayEqual(a.subarray(1, 3), [2, 3]); + this.arrayEqual(a.subarray(-3), [3, 4, 5]); + this.arrayEqual(a.subarray(-3, -1), [3, 4]); + this.arrayEqual(a.subarray(3, 2), []); + this.arrayEqual(a.subarray(-2, -3), []); + this.arrayEqual(a.subarray(4, 1), []); + this.arrayEqual(a.subarray(-1, -4), []); + this.arrayEqual(a.subarray(1).subarray(1), [3, 4, 5]); + this.arrayEqual(a.subarray(1, 4).subarray(1, 2), [3]); +}); + + +test('DataView constructors', function () { + + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // Chrome allows no arguments, throws if non-ArrayBuffer + //stricterEqual(new DataView().buffer.byteLength, 0, 'no arguments'); + + // Safari (iOS 5) does not + //raises(function () { return new DataView(); }, TypeError, 'no arguments'); + + // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); + + + + From d36943524064737fab4b03f3ed035c602c4ed867 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 13:25:47 -0700 Subject: [PATCH 18/43] added dirty fix for NaN _nextOutputTrailingLoudness --- assignment-client/src/audio/AudioMixer.cpp | 26 ++++++++++++++++++- .../audio/src/PositionalAudioRingBuffer.cpp | 21 ++++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 459f8a4b59..c856969de1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,6 +366,11 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf } void AudioMixer::prepareMixForListeningNode(Node* node) { + + static int k = 0; + k++; + bool debug = (k % 20) == 0; + AvatarAudioRingBuffer* nodeRingBuffer = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer(); // zero out the client mix for this node @@ -381,11 +386,30 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; + if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() - && otherNodeBuffer->getNextOutputTrailingLoudness() > 0) { + && otherNodeBuffer->getNextOutputTrailingLoudness() > 0.0f) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); + } else { + + //if (debug) { + printf("\nWILL NOT MIX!!!\n"); + printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); + printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); + + if (otherNodeBuffer->getType() == PositionalAudioRingBuffer::Microphone) + printf("\t avatar buffer...\n"); + else + { + printf("\t inj buffer %s\n", ((InjectedAudioRingBuffer*)otherNodeBuffer)->getStreamIdentifier().toString().toLatin1().data()); + } + + printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); + printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); + printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); + //} } } } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 411b02400d..8cba6d72b0 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -32,6 +32,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: _shouldLoopbackForNode(false), _shouldOutputStarveDebug(true), _isStereo(isStereo), + _nextOutputTrailingLoudness(0.0f), _listenerUnattenuatedZone(NULL), _lastFrameReceivedTime(0), _interframeTimeGapStatsForJitterCalc(TIME_GAPS_FOR_JITTER_CALC_INTERVAL_SAMPLES, TIME_GAPS_FOR_JITTER_CALC_WINDOW_INTERVALS), @@ -121,27 +122,35 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { // ForBoundarySamples means that we expect the number of samples not to roll of the end of the ring buffer float nextLoudness = 0; - for (int i = 0; i < _numFrameSamples; ++i) { - nextLoudness += fabsf(_nextOutput[i]); + if (samplesAvailable() >= _numFrameSamples) { + for (int i = 0; i < _numFrameSamples; ++i) { + nextLoudness += fabsf(_nextOutput[i]); + } + nextLoudness /= _numFrameSamples; + nextLoudness /= MAX_SAMPLE_VALUE; } - nextLoudness /= _numFrameSamples; - nextLoudness /= MAX_SAMPLE_VALUE; - const int TRAILING_AVERAGE_FRAMES = 100; const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; const float LOUDNESS_EPSILON = 0.000001f; + float oldNextOutputTrailingLoudness = _nextOutputTrailingLoudness; if (nextLoudness >= _nextOutputTrailingLoudness) { _nextOutputTrailingLoudness = nextLoudness; } else { _nextOutputTrailingLoudness = (_nextOutputTrailingLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); - + if (_nextOutputTrailingLoudness < LOUDNESS_EPSILON) { _nextOutputTrailingLoudness = 0; } } + + // fixes bug on Windows where _nextOutputTrailingLoudness sometimes becomes NaN. In that case, + // revert _nextOutputTrailingLoudness to its previous value + if (isNaN(_nextOutputTrailingLoudness)) { + _nextOutputTrailingLoudness = oldNextOutputTrailingLoudness; + } } bool PositionalAudioRingBuffer::shouldBeAddedToMix() { From 90c931ea45f24fb89f58f6fda562d4f5f95aedf3 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 15:07:50 -0700 Subject: [PATCH 19/43] removed Audio.cpp initialization list warning; updated Injector removal conditions --- assignment-client/src/audio/AudioMixer.cpp | 4 ++-- assignment-client/src/audio/AudioMixerClientData.cpp | 3 +-- interface/src/Audio.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c856969de1..6df23ca32f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -394,7 +394,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); } else { - //if (debug) { + if (debug) { printf("\nWILL NOT MIX!!!\n"); printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); @@ -409,7 +409,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); - //} + } } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 94bbdc6a6b..b6e9f8883e 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -154,13 +154,12 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = *i; - const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 100; + const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 200; if (audioBuffer->willBeAddedToMix()) { audioBuffer->shiftReadPosition(audioBuffer->getSamplesPerFrame()); audioBuffer->setWillBeAddedToMix(false); } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector - && audioBuffer->hasStarted() && audioBuffer->isStarved() && audioBuffer->getConsecutiveNotMixedCount() > INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD) { // this is an empty audio buffer that has starved, safe to delete // also delete its sequence number stats diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 3689ff0143..bc2da96f6e 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -115,9 +115,9 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _audioMixerAvatarStreamAudioStats(), _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), - _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), _starveCount(0), - _consecutiveNotMixedCount(0) + _consecutiveNotMixedCount(0), + _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); From 82e9aa8bb7835a442c37a22fa0796e6fe9f78ea0 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 16:08:15 -0700 Subject: [PATCH 20/43] made stats easier to read; reverted injected stream deletion conditions --- .../src/audio/AudioMixerClientData.cpp | 3 +- interface/src/ui/Stats.cpp | 40 +++++++++++-------- libraries/shared/src/SharedUtil.cpp | 14 +++++++ libraries/shared/src/SharedUtil.h | 4 ++ 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b6e9f8883e..94bbdc6a6b 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -154,12 +154,13 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = *i; - const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 200; + const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 100; if (audioBuffer->willBeAddedToMix()) { audioBuffer->shiftReadPosition(audioBuffer->getSamplesPerFrame()); audioBuffer->setWillBeAddedToMix(false); } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector + && audioBuffer->hasStarted() && audioBuffer->isStarved() && audioBuffer->getConsecutiveNotMixedCount() > INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD) { // this is an empty audio buffer that has starved, safe to delete // also delete its sequence number stats diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 15a54e42a6..69e7fde209 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -346,26 +346,28 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); - char downstreamAudioStatsString[30]; + char downstreamAudioStatsString[512]; AudioStreamStats downstreamAudioStreamStats = audio->getDownstreamAudioStreamStats(); - sprintf(downstreamAudioStatsString, " mix: %.1f%%/%.1f%%, %u/?/%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, + sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/?/%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, downstreamAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, downstreamAudioStreamStats._ringBufferFramesAvailable, downstreamAudioStreamStats._ringBufferDesiredJitterBufferFrames); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - sprintf(downstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", downstreamAudioStreamStats._timeGapMin, - downstreamAudioStreamStats._timeGapMax, downstreamAudioStreamStats._timeGapAverage, + sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(downstreamAudioStreamStats._timeGapMin).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapMax).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapAverage).toLatin1().data(), downstreamAudioStreamStats._ringBufferStarveCount, downstreamAudioStreamStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - sprintf(downstreamAudioStatsString, " %llu/%llu/%.2f, %u/?", downstreamAudioStreamStats._timeGapWindowMin, - downstreamAudioStreamStats._timeGapWindowMax, downstreamAudioStreamStats._timeGapWindowAverage, + sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/?", formatUsecTime(downstreamAudioStreamStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapWindowAverage).toLatin1().data(), downstreamAudioStreamStats._ringBufferConsecutiveNotMixedCount); verticalOffset += STATS_PELS_PER_LINE; @@ -376,11 +378,11 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamLabelString, color); - char upstreamAudioStatsString[30]; + char upstreamAudioStatsString[512]; const AudioStreamStats& audioMixerAvatarAudioStreamStats = audio->getAudioMixerAvatarStreamAudioStats(); - sprintf(upstreamAudioStatsString, " mic: %.1f%%/%.1f%%, %u/%u/%u", audioMixerAvatarAudioStreamStats._packetStreamStats.getLostRate()*100.0f, + sprintf(upstreamAudioStatsString, " mic: %.2f%%/%.2f%%, %u/%u/%u", audioMixerAvatarAudioStreamStats._packetStreamStats.getLostRate()*100.0f, audioMixerAvatarAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, audioMixerAvatarAudioStreamStats._ringBufferFramesAvailable, audioMixerAvatarAudioStreamStats._ringBufferCurrentJitterBufferFrames, audioMixerAvatarAudioStreamStats._ringBufferDesiredJitterBufferFrames); @@ -388,15 +390,17 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", audioMixerAvatarAudioStreamStats._timeGapMin, - audioMixerAvatarAudioStreamStats._timeGapMax, audioMixerAvatarAudioStreamStats._timeGapAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMin).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMax).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapAverage).toLatin1().data(), audioMixerAvatarAudioStreamStats._ringBufferStarveCount, audioMixerAvatarAudioStreamStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", audioMixerAvatarAudioStreamStats._timeGapWindowMin, - audioMixerAvatarAudioStreamStats._timeGapWindowMax, audioMixerAvatarAudioStreamStats._timeGapWindowAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowAverage).toLatin1().data(), audioMixerAvatarAudioStreamStats._ringBufferConsecutiveNotMixedCount, audioMixerAvatarAudioStreamStats._ringBufferSilentFramesDropped); verticalOffset += STATS_PELS_PER_LINE; @@ -404,7 +408,7 @@ void Stats::display( foreach(const AudioStreamStats& injectedStreamAudioStats, audioMixerInjectedStreamAudioStatsMap) { - sprintf(upstreamAudioStatsString, " inj: %.1f%%/%.1f%%, %u/%u/%u", injectedStreamAudioStats._packetStreamStats.getLostRate()*100.0f, + sprintf(upstreamAudioStatsString, " inj: %.2f%%/%.2f%%, %u/%u/%u", injectedStreamAudioStats._packetStreamStats.getLostRate()*100.0f, injectedStreamAudioStats._packetStreamWindowStats.getLostRate() * 100.0f, injectedStreamAudioStats._ringBufferFramesAvailable, injectedStreamAudioStats._ringBufferCurrentJitterBufferFrames, injectedStreamAudioStats._ringBufferDesiredJitterBufferFrames); @@ -412,15 +416,17 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", injectedStreamAudioStats._timeGapMin, - injectedStreamAudioStats._timeGapMax, injectedStreamAudioStats._timeGapAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapMin).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapMax).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapAverage).toLatin1().data(), injectedStreamAudioStats._ringBufferStarveCount, injectedStreamAudioStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", injectedStreamAudioStats._timeGapWindowMin, - injectedStreamAudioStats._timeGapWindowMax, injectedStreamAudioStats._timeGapWindowAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapWindowAverage).toLatin1().data(), injectedStreamAudioStats._ringBufferConsecutiveNotMixedCount, injectedStreamAudioStats._ringBufferSilentFramesDropped); verticalOffset += STATS_PELS_PER_LINE; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index e4d2e1c835..23d33969f4 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -837,3 +837,17 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f QByteArray createByteArray(const glm::vec3& vector) { return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z); } + +QString formatUsecTime(float usecs, int prec) { + QString result; + if (usecs > USECS_PER_MINUTE) { + result = QString::number(usecs / USECS_PER_MINUTE, 'f', prec) + "min"; + } else if (usecs > USECS_PER_SECOND) { + result = QString::number(usecs / USECS_PER_SECOND, 'f', prec) + 's'; + } else if (usecs > USECS_PER_MSEC) { + result = QString::number(usecs / USECS_PER_MSEC, 'f', prec) + "ms"; + } else { + result = QString::number(usecs, 'f', prec) + "us"; + } + return result; +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index e5c2a0afc9..81ec2ab810 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -57,7 +57,9 @@ static const float METERS_PER_MILLIMETER = 0.001f; static const float MILLIMETERS_PER_METER = 1000.0f; static const quint64 USECS_PER_MSEC = 1000; static const quint64 MSECS_PER_SECOND = 1000; +static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; +static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; const int BITS_IN_BYTE = 8; @@ -189,4 +191,6 @@ bool isNaN(float value); QByteArray createByteArray(const glm::vec3& vector); +QString formatUsecTime(float usecs, int prec = 3); + #endif // hifi_SharedUtil_h From b9fe5b2ef1dbf03456ea582d034826224b956573 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:19 -0700 Subject: [PATCH 21/43] Added JS exceptions + fixed up floating precision mess up --- .../script-engine/src/ArrayBufferClass.cpp | 12 +++- .../script-engine/src/ArrayBufferClass.h | 2 +- libraries/script-engine/src/DataViewClass.cpp | 2 +- .../script-engine/src/DataViewPrototype.cpp | 42 ++++++++----- .../script-engine/src/DataViewPrototype.h | 34 +++++----- libraries/script-engine/src/TypedArrays.cpp | 63 ++++++++++++++----- libraries/script-engine/src/TypedArrays.h | 2 +- 7 files changed, 105 insertions(+), 52 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d86e229a1a..4f85eeeae2 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -48,7 +48,17 @@ _scriptEngine(scriptEngine) { engine()->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(quint32 size) { +QScriptValue ArrayBufferClass::newInstance(qint32 size) { + const qint32 MAX_LENGTH = 100000000; + if (size < 0) { + engine()->evaluate("throw \"ArgumentError: negative length\""); + return QScriptValue(); + } + if (size > MAX_LENGTH) { + engine()->evaluate("throw \"ArgumentError: absurd length\""); + return QScriptValue(); + } + engine()->reportAdditionalMemoryCost(size); QScriptEngine* eng = engine(); QVariant variant = QVariant::fromValue(QByteArray(size, 0)); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c56b659eff..c7b4d447dd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -26,7 +26,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 size); + QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); ScriptEngine* getScriptEngine() { return _scriptEngine; } diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 938770ecf5..efb45daf3f 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -64,7 +64,7 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } if (byteOffsetArg.isNumber() && (byteOffsetArg.toInt32() < 0 || - byteOffsetArg.toInt32() >= arrayBuffer->size())) { + byteOffsetArg.toInt32() > arrayBuffer->size())) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 17734e109c..95f3fdf3f6 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -26,7 +26,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { +bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { + if (offset < 0) { + return false; + } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; @@ -38,7 +41,7 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint32 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -51,7 +54,7 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { return 0; } -quint32 DataViewPrototype::getUint8(quint32 byteOffset) { +quint32 DataViewPrototype::getUint8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -64,7 +67,7 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { return 0; } -qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -78,7 +81,7 @@ qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -92,7 +95,7 @@ quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { return 0; } -qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -106,7 +109,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -120,25 +123,28 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { +float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); float result; stream >> result; + qDebug() << "Get: " << result; return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); return 0; } -double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { +double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); double result; stream >> result; @@ -150,7 +156,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { +void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -161,7 +167,7 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { +void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -172,7 +178,7 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -184,7 +190,7 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -196,7 +202,7 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -208,7 +214,7 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -220,11 +226,12 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { +void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); stream << value; } else { @@ -232,11 +239,12 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE } } -void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { +void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); stream << value; } else { diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index 97c94c58ce..b7061a388e 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,14 +31,14 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint32 getInt8(quint32 byteOffset); - quint32 getUint8(quint32 byteOffset); - qint32 getInt16(quint32 byteOffset, bool littleEndian = false); - quint32 getUint16(quint32 byteOffset, bool littleEndian = false); - qint32 getInt32(quint32 byteOffset, bool littleEndian = false); - quint32 getUint32(quint32 byteOffset, bool littleEndian = false); - float getFloat32(quint32 byteOffset, bool littleEndian = false); - double getFloat64(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(qint32 byteOffset); + quint32 getUint8(qint32 byteOffset); + qint32 getInt16(qint32 byteOffset, bool littleEndian = false); + quint32 getUint16(qint32 byteOffset, bool littleEndian = false); + qint32 getInt32(qint32 byteOffset, bool littleEndian = false); + quint32 getUint32(qint32 byteOffset, bool littleEndian = false); + float getFloat32(qint32 byteOffset, bool littleEndian = false); + double getFloat64(qint32 byteOffset, bool littleEndian = false); // Stores a value of the given type at the specified byte offset // from the start of the view. There is no alignment constraint; @@ -51,18 +51,18 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint32 value); - void setUint8(quint32 byteOffset, quint32 value); - void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); - void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + void setInt8(qint32 byteOffset, qint32 value); + void setUint8(qint32 byteOffset, quint32 value); + void setInt16(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint32(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(qint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(qint32 byteOffset, double value, bool littleEndian = false); private: QByteArray* thisArrayBuffer() const; - bool realOffset(quint32& offset, size_t size) const; + bool realOffset(qint32& offset, size_t size) const; }; #endif // hifi_DataViewPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index d6ffc8f5a6..102c477c04 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -56,6 +56,7 @@ QScriptValue TypedArray::newInstance(QScriptValue array) { } return newArray; } + engine()->evaluate("throw \"ArgumentError: not an array\""); return QScriptValue(); } @@ -200,19 +201,23 @@ QByteArray* TypedArrayPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { TypedArray* typedArray = static_cast(parent()); - if (array.isArray()) { + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } quint32 length = array.property("length").toInt32(); if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - // TODO throw an error maybe? + engine()->evaluate("throw \"ArgumentError: array does not fit\""); return; } for (int i = 0; i < length; ++i) { thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); } - // TODO handle typed arrays } QScriptValue TypedArrayPrototype::subarray(qint32 begin) { @@ -411,15 +416,31 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + float result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + stream << (float)value.toNumber(); + } } Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { @@ -428,16 +449,30 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + double result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + stream << (double)value.toNumber(); + } } - - diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 37d341b2ad..2eff3e84b9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -69,7 +69,7 @@ public: TypedArrayPrototype(QObject* parent = NULL); public slots: - void set(QScriptValue array, quint32 offset = 0); + void set(QScriptValue array, qint32 offset = 0); QScriptValue subarray(qint32 begin); QScriptValue subarray(qint32 begin, qint32 end); From 75573a4c06a67cc17f8725296b5589dd52e0219c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:50 -0700 Subject: [PATCH 22/43] Added exception checking to unit tests --- examples/Test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 646ae5c71d..caecfc5af7 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -20,7 +20,7 @@ test = function(name, func) { unitTest.run(); print(" Success: " + unitTest.numAssertions + " assertions passed"); } catch (error) { - print(" Failure: " + error.message); + print(" Failure: " + error.name + " " + error.message); } }; @@ -30,6 +30,12 @@ AssertionException = function(expected, actual, message) { this.name = 'AssertionException'; }; +UnthrownException = function(message) { + print("Creating exception"); + this.message = message + "\n"; + this.name = 'UnthrownException'; +}; + UnitTest = function(name, func) { this.numAssertions = 0; this.func = func; @@ -77,4 +83,15 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { throw new AssertionException(array1[i], array2[i], message); } } +} + +UnitTest.prototype.raises = function(func, message) { + this.numAssertions++; + try { + func(); + } catch (error) { + return; + } + + throw new UnthrownException(message); } \ No newline at end of file From b86890033c2fde9f2ef2b9bd0530216cf0663c4c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 19:35:17 -0500 Subject: [PATCH 23/43] Moved hair to separate class --- interface/src/Hair.cpp | 203 +++++++++++++++++++++++- interface/src/Hair.h | 33 +++- interface/src/avatar/Avatar.cpp | 254 +++--------------------------- interface/src/avatar/Avatar.h | 14 +- interface/src/avatar/MyAvatar.cpp | 22 ++- 5 files changed, 262 insertions(+), 264 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index a23312eba1..635ea7ee0f 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -15,21 +15,212 @@ #include "Util.h" #include "world.h" +const float HAIR_DAMPING = 0.99f; +const float CONSTRAINT_RELAXATION = 10.0f; +const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); +const float HAIR_ACCELERATION_COUPLING = 0.025f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; +const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; +const float HAIR_STIFFNESS = 0.0000f; +const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); +const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); +const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); +const float MAX_WIND_STRENGTH = 0.02f; -Hair::Hair() { - qDebug() << "Creating Hair"; + +Hair::Hair(int strands, + int links, + float radius, + float linkLength, + float hairThickness) : + _acceleration(0.0f), + _angularVelocity(0.0f), + _gravity(0.0f) +{ + _strands = strands; + _links = links; + _radius = radius; + _linkLength = linkLength; + _hairThickness = hairThickness; + _hairPosition = new glm::vec3[_strands * _links]; + _hairOriginalPosition = new glm::vec3[_strands * _links]; + _hairLastPosition = new glm::vec3[_strands * _links]; + _hairQuadDelta = new glm::vec3[_strands * _links]; + _hairNormals = new glm::vec3[_strands * _links]; + _hairColors = new glm::vec3[_strands * _links]; + _hairIsMoveable = new int[_strands * _links]; + _hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others + const float FACE_WIDTH = PI / 4.0f; + glm::vec3 thisVertex; + for (int strand = 0; strand < _strands; strand++) { + float strandAngle = randFloat() * PI; + float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); + float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); + glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); + thisStrand *= _radius; + + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + // Clear constraints + for (int link2 = 0; link2 < HAIR_CONSTRAINTS; link2++) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link2] = -1; + } + if (vertexIndex % _links == 0) { + // start of strand + thisVertex = thisStrand; + } else { + thisVertex+= glm::normalize(thisStrand) * _linkLength; + // Set constraints to vertex before and maybe vertex after in strand + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS] = vertexIndex - 1; + if (link < (_links - 1)) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1; + } + } + _hairPosition[vertexIndex] = thisVertex; + _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; + + _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); + _hairQuadDelta[vertexIndex] *= 1.f - (link / _links); + _hairNormals[vertexIndex] = glm::normalize(randVector()); + if (randFloat() < elevation / PI_OVER_TWO) { + _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); + } else { + _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)_links); + } + } + } } void Hair::simulate(float deltaTime) { + deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); + glm::vec3 acceleration = _acceleration; + if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { + acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; + } + + float windIntensity = randFloat() * MAX_WIND_STRENGTH; + + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + if (vertexIndex % _links == 0) { + // Base Joint - no integration + } else { + // + // Vertlet Integration + // + // Add velocity from last position, with damping + glm::vec3 thisPosition = _hairPosition[vertexIndex]; + glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; + _hairPosition[vertexIndex] += diff * HAIR_DAMPING; + // Resolve collision with head sphere + if (glm::length(_hairPosition[vertexIndex]) < _radius) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * + (_radius - glm::length(_hairPosition[vertexIndex])); + } + // Resolve collision with hands + /* + if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); + } + if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); + } + */ + + + // Add a little gravity + _hairPosition[vertexIndex] += HAIR_GRAVITY * deltaTime; + + // Add linear acceleration of the avatar body + _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; + + // Add stiffness (like hair care products do) + _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) + * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; + + // Add some wind + glm::vec3 wind = WIND_DIRECTION * windIntensity; + _hairPosition[vertexIndex] += wind * deltaTime; + + const float ANGULAR_VELOCITY_MIN = 0.001f; + // Add angular acceleration of the avatar body + if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { + glm::vec3 yawVector = _hairPosition[vertexIndex]; + yawVector.y = 0.f; + if (glm::length(yawVector) > EPSILON) { + float radius = glm::length(yawVector); + yawVector = glm::normalize(yawVector); + float angle = atan2f(yawVector.x, -yawVector.z) + PI; + glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 pitchVector = _hairPosition[vertexIndex]; + pitchVector.x = 0.f; + if (glm::length(pitchVector) > EPSILON) { + float radius = glm::length(pitchVector); + pitchVector = glm::normalize(pitchVector); + float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; + glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 rollVector = _hairPosition[vertexIndex]; + rollVector.z = 0.f; + if (glm::length(rollVector) > EPSILON) { + float radius = glm::length(rollVector); + pitchVector = glm::normalize(rollVector); + float angle = atan2f(rollVector.x, rollVector.y) + PI; + glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + } + + // Iterate length constraints to other links + for (int link = 0; link < HAIR_CONSTRAINTS; link++) { + if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) { + // If there is a constraint, try to enforce it + glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; + _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime; + } + } + // Store start position for next vertlet pass + _hairLastPosition[vertexIndex] = thisPosition; + } + } + } + } void Hair::render() { // // Before calling this function, translate/rotate to the origin of the owning object - glPushMatrix(); - glColor3f(1.0f, 1.0f, 0.0f); - glutSolidSphere(1.0f, 15, 15); - glPopMatrix(); + // + glBegin(GL_QUADS); + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links - 1; link++) { + int vertexIndex = strand * _links + link; + glColor3fv(&_hairColors[vertexIndex].x); + glNormal3fv(&_hairNormals[vertexIndex].x); + glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); + + glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); + } + } + glEnd(); } + diff --git a/interface/src/Hair.h b/interface/src/Hair.h index e4dc3778c3..dfb90cae21 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -21,14 +21,45 @@ #include "InterfaceConfig.h" #include "Util.h" +const int HAIR_CONSTRAINTS = 2; + +const int DEFAULT_HAIR_STRANDS = 50; +const int DEFAULT_HAIR_LINKS = 10; +const float DEFAULT_HAIR_RADIUS = 0.25; +const float DEFAULT_HAIR_LINK_LENGTH = 0.05; +const float DEFAULT_HAIR_THICKNESS = 0.015; class Hair { public: - Hair(); + Hair(int strands = DEFAULT_HAIR_STRANDS, + int links = DEFAULT_HAIR_LINKS, + float radius = DEFAULT_HAIR_RADIUS, + float linkLength = DEFAULT_HAIR_LINK_LENGTH, + float hairThickness = DEFAULT_HAIR_THICKNESS); void simulate(float deltaTime); void render(); + void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; } + void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; } + void setGravity(const glm::vec3& gravity) { _gravity = gravity; } private: + int _strands; + int _links; + float _linkLength; + float _hairThickness; + float _radius; + glm::vec3* _hairPosition; + glm::vec3* _hairOriginalPosition; + glm::vec3* _hairLastPosition; + glm::vec3* _hairQuadDelta; + glm::vec3* _hairNormals; + glm::vec3* _hairColors; + int* _hairIsMoveable; + int* _hairConstraints; + glm::vec3 _acceleration; + glm::vec3 _angularVelocity; + glm::vec3 _gravity; + }; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4efb03b3f5..a826ad699f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,6 +46,7 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; Avatar::Avatar() : AvatarData(), + _hair(), _skeletonModel(this), _bodyYawDelta(0.0f), _velocity(0.0f, 0.0f, 0.0f), @@ -60,9 +61,9 @@ Avatar::Avatar() : _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), + _numLocalLights(1), _initialized(false), - _shouldRenderBillboard(true), - _numLocalLights(1) + _shouldRenderBillboard(true) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -82,8 +83,7 @@ void Avatar::init() { _skeletonModel.init(); _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); - initializeHair(); - + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { _localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f); _localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f); @@ -168,13 +168,11 @@ void Avatar::simulate(float deltaTime) { } if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { PerformanceTimer perfTimer("hair"); - simulateHair(deltaTime); + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } - - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } - } // update position by velocity, and subtract the change added earlier for gravity @@ -426,233 +424,17 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { getHand()->render(false, modelRenderMode); } getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } - } -} - -// -// Constants for the Hair Simulation -// - -const float HAIR_LENGTH = 0.2f; -const float HAIR_LINK_LENGTH = HAIR_LENGTH / HAIR_LINKS; -const float HAIR_DAMPING = 0.99f; -const float HEAD_RADIUS = 0.21f; -const float CONSTRAINT_RELAXATION = 10.0f; -const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); -const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; -const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_THICKNESS = 0.015f; -const float HAIR_STIFFNESS = 0.0000f; -const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); -const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); -const float MAX_WIND_STRENGTH = 0.02f; -const float FINGER_LENGTH = 0.25f; -const float FINGER_RADIUS = 0.10f; - -void Avatar::renderHair() { - // - // Render the avatar's moveable hair - // - - glm::vec3 headPosition = getHead()->getPosition(); - glPushMatrix(); - glTranslatef(headPosition.x, headPosition.y, headPosition.z); - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glBegin(GL_QUADS); - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS - 1; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - glColor3fv(&_hairColors[vertexIndex].x); - glNormal3fv(&_hairNormals[vertexIndex].x); - glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); - - glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); - } - } - glEnd(); - - glPopMatrix(); - -} - -void Avatar::simulateHair(float deltaTime) { - - deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); - glm::vec3 acceleration = getAcceleration(); - if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { - acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; - } - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - acceleration = acceleration * rotation; - glm::vec3 angularVelocity = getAngularVelocity() + getHead()->getAngularVelocity(); - - // Get hand positions to allow touching hair - glm::vec3 leftHandPosition, rightHandPosition; - getSkeletonModel().getLeftHandPosition(leftHandPosition); - getSkeletonModel().getRightHandPosition(rightHandPosition); - leftHandPosition -= getHead()->getPosition(); - rightHandPosition -= getHead()->getPosition(); - glm::quat leftRotation, rightRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); - leftHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(leftRotation); - rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(rightRotation); - leftHandPosition = leftHandPosition * rotation; - rightHandPosition = rightHandPosition * rotation; - - float windIntensity = randFloat() * MAX_WIND_STRENGTH; - - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - if (vertexIndex % HAIR_LINKS == 0) { - // Base Joint - no integration - } else { - // - // Vertlet Integration - // - // Add velocity from last position, with damping - glm::vec3 thisPosition = _hairPosition[vertexIndex]; - glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; - _hairPosition[vertexIndex] += diff * HAIR_DAMPING; - // Resolve collision with head sphere - if (glm::length(_hairPosition[vertexIndex]) < HEAD_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * - (HEAD_RADIUS - glm::length(_hairPosition[vertexIndex])); - } - // Resolve collision with hands - if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); - } - if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); - } - - - // Add a little gravity - _hairPosition[vertexIndex] += HAIR_GRAVITY * rotation * deltaTime; - - // Add linear acceleration of the avatar body - _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; - - // Add stiffness (like hair care products do) - _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS; - - // Add some wind - glm::vec3 wind = WIND_DIRECTION * windIntensity; - _hairPosition[vertexIndex] += wind * deltaTime; - - const float ANGULAR_VELOCITY_MIN = 0.001f; - // Add angular acceleration of the avatar body - if (glm::length(angularVelocity) > ANGULAR_VELOCITY_MIN) { - glm::vec3 yawVector = _hairPosition[vertexIndex]; - yawVector.y = 0.f; - if (glm::length(yawVector) > EPSILON) { - float radius = glm::length(yawVector); - yawVector = glm::normalize(yawVector); - float angle = atan2f(yawVector.x, -yawVector.z) + PI; - glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 pitchVector = _hairPosition[vertexIndex]; - pitchVector.x = 0.f; - if (glm::length(pitchVector) > EPSILON) { - float radius = glm::length(pitchVector); - pitchVector = glm::normalize(pitchVector); - float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; - glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 rollVector = _hairPosition[vertexIndex]; - rollVector.z = 0.f; - if (glm::length(rollVector) > EPSILON) { - float radius = glm::length(rollVector); - pitchVector = glm::normalize(rollVector); - float angle = atan2f(rollVector.x, rollVector.y) + PI; - glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - } - - // Iterate length constraints to other links - for (int link = 0; link < HAIR_MAX_CONSTRAINTS; link++) { - if (_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link] > -1) { - // If there is a constraint, try to enforce it - glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; - _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - HAIR_LINK_LENGTH) * CONSTRAINT_RELAXATION * deltaTime; - } - } - // Store start position for next vertlet pass - _hairLastPosition[vertexIndex] = thisPosition; - } - } - } -} - -void Avatar::initializeHair() { - const float FACE_WIDTH = PI / 4.0f; - glm::vec3 thisVertex; - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - float strandAngle = randFloat() * PI; - float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); - float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); - glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); - thisStrand *= HEAD_RADIUS; - - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - // Clear constraints - for (int link2 = 0; link2 < HAIR_MAX_CONSTRAINTS; link2++) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link2] = -1; - } - if (vertexIndex % HAIR_LINKS == 0) { - // start of strand - thisVertex = thisStrand; - } else { - thisVertex+= glm::normalize(thisStrand) * HAIR_LINK_LENGTH; - // Set constraints to vertex before and maybe vertex after in strand - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS] = vertexIndex - 1; - if (link < (HAIR_LINKS - 1)) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + 1] = vertexIndex + 1; - } - } - _hairPosition[vertexIndex] = thisVertex; - _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; - _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; - - _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * HAIR_THICKNESS, 0.f, sin(strandAngle) * HAIR_THICKNESS); - _hairQuadDelta[vertexIndex] *= 1.f - (link / HAIR_LINKS); - _hairNormals[vertexIndex] = glm::normalize(randVector()); - if (randFloat() < elevation / PI_OVER_TWO) { - _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)HAIR_LINKS); - } else { - _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)HAIR_LINKS); - } - - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8c14fc9ed0..8dbeda25dc 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,7 +164,7 @@ signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); protected: - QVector _hairs; + Hair _hair; SkeletonModel _skeletonModel; QVector _attachmentModels; float _bodyYawDelta; @@ -210,18 +210,6 @@ protected: virtual void renderAttachments(RenderMode renderMode); virtual void updateJointMappings(); - - glm::vec3 _hairPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairOriginalPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairLastPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairQuadDelta[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairNormals[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairColors[HAIR_STRANDS * HAIR_LINKS]; - int _hairIsMoveable[HAIR_STRANDS * HAIR_LINKS]; - int _hairConstraints[HAIR_STRANDS * HAIR_LINKS * 2]; // Hair can link to two others - void renderHair(); - void simulateHair(float deltaTime); - void initializeHair(); private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04e7b628c9..93463556cc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -185,10 +185,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("hair"); if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - simulateHair(deltaTime); - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(getGravity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } } @@ -884,11 +884,17 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance(); if (shouldRenderHead(cameraPos, renderMode)) { getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } getHand()->render(true, modelRenderMode); From 5a8569fcd5539d42abd345027ff52785539b10cd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 17:45:52 -0700 Subject: [PATCH 24/43] Handling NaN values for floats --- examples/typedArraysUnitTest.js | 339 +++++++++++++++++- .../script-engine/src/DataViewPrototype.cpp | 22 +- .../script-engine/src/DataViewPrototype.h | 4 +- libraries/script-engine/src/TypedArrays.cpp | 6 + 4 files changed, 343 insertions(+), 28 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index de9b487c32..a466c9104f 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,6 +11,26 @@ Script.include("Test.js"); +// e.g. extractbits([0xff, 0x80, 0x00, 0x00], 23, 30); inclusive +function extractbits(bytes, lo, hi) { + var out = 0; + bytes = bytes.slice(); // make a copy + var lsb = bytes.pop(), sc = 0, sh = 0; + + for (; lo > 0; lo--, hi--) { + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + for (; hi >= 0; hi--) { + out = out | (lsb & 0x01) << sh++; + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + return out; +} + test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); @@ -20,8 +40,8 @@ test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); - // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); - // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); + this.raises(function () { return new ArrayBuffer(-1); }, 'negative length'); + this.raises(function () { return new ArrayBuffer(0x80000000); }, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,8 +59,8 @@ test('DataView constructors', function (finished) { d.setUint32(0, 0x12345678); this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, 'non-ArrayBuffer argument'); }); @@ -118,8 +138,8 @@ test('typed array constructors', function () { var int8 = new Int8Array(); this.assertEquals(int8.length, 0, 'no args 0'); - // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); - // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + this.raises(function () { return new Int8Array(-1); }, 'bogus length'); + this.raises(function () { return new Int8Array(0x80000000); }, 'bogus length'); int8 = new Int8Array(4); this.assertEquals(int8.BYTES_PER_ELEMENT, 1); @@ -160,10 +180,10 @@ test('typed array constructors', function () { int8 = new Int8Array(rawbuf, 8); this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); - // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); - // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + this.raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + this.raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + this.raises(function () { return new Int32Array(rawbuf, -1); }, 'invalid byteOffset 29'); + this.raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); int8 = new Int8Array(rawbuf, 2, 4); this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); @@ -175,9 +195,9 @@ test('typed array constructors', function () { this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); - // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); }); test('TypedArray clone constructor', function () { @@ -240,6 +260,189 @@ test('signed/unsigned conversions', function () { }); +test('IEEE754 single precision unpacking', function () { + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat32(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xbf, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0x80, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0x80, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x80, 0x7f, 0xff, 0xff]), -1.1754942106924411E-38, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x01]), -1.4012984643248170E-45, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x01]), 1.4012984643248170E-45, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x7f, 0xff, 0xff]), 1.1754942106924411E-38, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x80, 0x00, 0x00]), 1.1754943508222875E-38, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0x7f, 0xff, 0xff]), 3.4028234663852886E+38, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0x80, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0x80, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xbf, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xc0, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + +test('IEEE754 single precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(4), dv = new DataView(uint8.buffer); + dv.setFloat32(0, v); + var bytes = []; + for (var i = 0; i < 4; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0x80, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-3.4028235677973366e+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + this.arrayEqual(toBytes(-3.402824E+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + + this.arrayEqual(toBytes(-3.4028234663852886E+38), [0xff, 0x7f, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-1.1754943508222875E-38), [0x80, 0x80, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-1.1754942106924411E-38), [0x80, 0x7f, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-1.4012984643248170E-45), [0x80, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-7.006492321624085e-46), [0x80, 0x00, 0x00, 0x00], '-Underflow'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00], '+0'); + + this.arrayEqual(toBytes(7.006492321624085e-46), [0x00, 0x00, 0x00, 0x00], '+Underflow'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(1.4012984643248170E-45), [0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(1.1754942106924411E-38), [0x00, 0x7f, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(1.1754943508222875E-38), [0x00, 0x80, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(3.4028234663852886E+38), [0x7f, 0x7f, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+Infinity), [0x7f, 0x80, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 31, 31), + exponent = extractbits(nanbytes, 23, 30), + fraction = extractbits(nanbytes, 0, 22); + this.assertEquals(exponent === 255 && fraction !== 0, true, 'NaN'); +}); + + +test('IEEE754 double precision unpacking', function () { + + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat64(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -1.7976931348623157E+308, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -2.2250738585072014E-308, '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -2.2250738585072010E-308, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), -4.9406564584124654E-324, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), 4.9406564584124654E-324, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 2.2250738585072010E-308, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), 2.2250738585072014E-308, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 1.7976931348623157E+308, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + + +test('IEEE754 double precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(8), + dv = new DataView(uint8.buffer); + dv.setFloat64(0, v); + var bytes = []; + for (var i = 0; i < 8; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-1.7976931348623157E+308), [0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-2.2250738585072014E-308), [0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-2.2250738585072010E-308), [0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-4.9406564584124654E-324), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(4.9406564584124654E-324), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(2.2250738585072010E-308), [0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(2.2250738585072014E-308), [0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(1.7976931348623157E+308), [0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+Infinity), [0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 63, 63), + exponent = extractbits(nanbytes, 52, 62), + fraction = extractbits(nanbytes, 0, 51); + this.assertEquals(exponent === 2047 && fraction !== 0, true, 'NaN'); +}); + test('Int32Array round trips', function () { var i32 = new Int32Array([0]); var data = [ @@ -319,8 +522,8 @@ test('TypedArray setting', function () { b.set(new Int32Array([99, 98, 97]), 2); this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); - // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); - // raises(function () { b.set([101, 102, 103, 104], 4); }); + this.raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + this.raises(function () { b.set([101, 102, 103, 104], 4); }); // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] @@ -374,11 +577,113 @@ test('DataView constructors', function () { //raises(function () { return new DataView(); }, TypeError, 'no arguments'); // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); +test('DataView accessors', function () { + var u = new Uint8Array(8), d = new DataView(u.buffer); + this.arrayEqual(u, [0, 0, 0, 0, 0, 0, 0, 0], '1'); + + d.setUint8(0, 255); + this.arrayEqual(u, [0xff, 0, 0, 0, 0, 0, 0, 0], '2'); + + d.setInt8(1, -1); + this.arrayEqual(u, [0xff, 0xff, 0, 0, 0, 0, 0, 0], '3'); + + d.setUint16(2, 0x1234); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0, 0, 0, 0]), '4'; + + d.setInt16(4, -1); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0xff, 0xff, 0, 0], '5'); + + d.setUint32(1, 0x12345678); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x78, 0xff, 0, 0], '6'); + + d.setInt32(4, -2023406815); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x87, 0x65, 0x43, 0x21], '7'); + + d.setFloat32(2, 1.2E+38); + this.arrayEqual(u, [0xff, 0x12, 0x7e, 0xb4, 0x8e, 0x52, 0x43, 0x21], '8'); + + d.setFloat64(0, -1.2345678E+301); + this.arrayEqual(u, [0xfe, 0x72, 0x6f, 0x51, 0x5f, 0x61, 0x77, 0xe5], '9'); + + u.set([0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87]); + this.assertEquals(d.getUint8(0), 128, '10'); + this.assertEquals(d.getInt8(1), -127, '11'); + this.assertEquals(d.getUint16(2), 33411, '12'); + this.assertEquals(d.getInt16(3), -31868, '13'); + this.assertEquals(d.getUint32(4), 2223343239, '14'); + this.assertEquals(d.getInt32(2), -2105310075, '15'); + this.assertEquals(d.getFloat32(2), -1.932478247535851e-37, '16'); + this.assertEquals(d.getFloat64(0), -3.116851295377095e-306, '17'); + +}); + + +test('DataView endian', function () { + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + var d; + + d = new DataView(rawbuf); + this.assertEquals(d.byteLength, 8, 'buffer'); + this.assertEquals(d.byteOffset, 0, 'buffer'); + this.raises(function () { d.getUint8(-2); }); // Chrome bug for index -, DOMException, 'bounds for buffer'? + this.raises(function () { d.getUint8(8); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(8, 0); }, 'bounds for buffer'); + + d = new DataView(rawbuf, 2); + this.assertEquals(d.byteLength, 6, 'buffer, byteOffset'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset'); + this.assertEquals(d.getUint8(5), 7, 'buffer, byteOffset'); + this.raises(function () { d.getUint8(-2); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.getUint8(6); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(6, 0); }, 'bounds for buffer, byteOffset'); + + d = new DataView(rawbuf, 8); + this.assertEquals(d.byteLength, 0, 'buffer, byteOffset'); + + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, 9); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + + d = new DataView(rawbuf, 2, 4); + this.assertEquals(d.byteLength, 4, 'buffer, byteOffset, length'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset, length'); + this.assertEquals(d.getUint8(3), 5, 'buffer, byteOffset, length'); + this.raises(function () { return d.getUint8(-2); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.getUint8(4); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(4, 0); }, 'bounds for buffer, byteOffset, length'); + + this.raises(function () { return new DataView(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + + +test('Typed Array getters/setters', function () { + // Only supported if Object.defineProperty() is fully supported on non-DOM objects. + try { + var o = {}; + Object.defineProperty(o, 'x', { get: function() { return 1; } }); + if (o.x !== 1) throw Error(); + } catch (_) { + ok(true); + return; + } + + var bytes = new Uint8Array([1, 2, 3, 4]), + uint32s = new Uint32Array(bytes.buffer); + + this.assertEquals(bytes[1], 2); + uint32s[0] = 0xffffffff; + this.assertEquals(bytes[1], 0xff); +}); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 95f3fdf3f6..29264e9bc5 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -32,10 +32,7 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -123,7 +120,7 @@ quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -132,14 +129,17 @@ float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { float result; stream >> result; - qDebug() << "Get: " << result; - return result; + if (isNaN(result)) { + return QScriptValue(NAN); + } + + return QScriptValue(result); } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } -double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -148,10 +148,14 @@ double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(); + } + return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } ///////////////// SETTERS //////////////////////////// diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index b7061a388e..da0261dad4 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -37,8 +37,8 @@ public slots: quint32 getUint16(qint32 byteOffset, bool littleEndian = false); qint32 getInt32(qint32 byteOffset, bool littleEndian = false); quint32 getUint32(qint32 byteOffset, bool littleEndian = false); - float getFloat32(qint32 byteOffset, bool littleEndian = false); - double getFloat64(qint32 byteOffset, bool littleEndian = false); + QScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false); + QScriptValue getFloat64(qint32 byteOffset, bool littleEndian = false); // Stores a value of the given type at the specified byte offset // from the start of the view. There is no alignment constraint; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 102c477c04..106b824f76 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -426,6 +426,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri float result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); @@ -459,6 +462,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); From 0b3e41a541ba85feb9d299a9a1f3c5c5bbc09841 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 19:58:13 -0500 Subject: [PATCH 25/43] remove wind from hair, fix gravity --- interface/src/Hair.cpp | 39 +++++++------------------------ interface/src/avatar/MyAvatar.cpp | 2 +- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 635ea7ee0f..bd2092cf3f 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -17,16 +17,12 @@ const float HAIR_DAMPING = 0.99f; const float CONSTRAINT_RELAXATION = 10.0f; -const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; const float HAIR_STIFFNESS = 0.0000f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); -const float MAX_WIND_STRENGTH = 0.02f; - Hair::Hair(int strands, int links, @@ -99,8 +95,6 @@ void Hair::simulate(float deltaTime) { acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; } - float windIntensity = randFloat() * MAX_WIND_STRENGTH; - for (int strand = 0; strand < _strands; strand++) { for (int link = 0; link < _links; link++) { int vertexIndex = strand * _links + link; @@ -114,40 +108,25 @@ void Hair::simulate(float deltaTime) { glm::vec3 thisPosition = _hairPosition[vertexIndex]; glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; _hairPosition[vertexIndex] += diff * HAIR_DAMPING; - // Resolve collision with head sphere + + // Resolve collisions with sphere if (glm::length(_hairPosition[vertexIndex]) < _radius) { _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * (_radius - glm::length(_hairPosition[vertexIndex])); } - // Resolve collision with hands - /* - if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); - } - if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); - } - */ + // Add gravity + _hairPosition[vertexIndex] += _gravity * deltaTime; - // Add a little gravity - _hairPosition[vertexIndex] += HAIR_GRAVITY * deltaTime; - - // Add linear acceleration of the avatar body + // Add linear acceleration _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; - // Add stiffness (like hair care products do) + // Add stiffness to return to original position _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; - // Add some wind - glm::vec3 wind = WIND_DIRECTION * windIntensity; - _hairPosition[vertexIndex] += wind * deltaTime; - + // Add angular acceleration const float ANGULAR_VELOCITY_MIN = 0.001f; - // Add angular acceleration of the avatar body if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { glm::vec3 yawVector = _hairPosition[vertexIndex]; yawVector.y = 0.f; @@ -178,7 +157,7 @@ void Hair::simulate(float deltaTime) { } } - // Iterate length constraints to other links + // Impose link constraints for (int link = 0; link < HAIR_CONSTRAINTS; link++) { if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) { // If there is a constraint, try to enforce it @@ -186,12 +165,12 @@ void Hair::simulate(float deltaTime) { _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime; } } + // Store start position for next vertlet pass _hairLastPosition[vertexIndex] = thisPosition; } } } - } void Hair::render() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 93463556cc..3ec6652889 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -187,7 +187,7 @@ void MyAvatar::simulate(float deltaTime) { if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); - _hair.setGravity(getGravity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); _hair.simulate(deltaTime); } } From d493b92d7b36c462b337fc5705078ce917ffe634 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 20:23:59 -0500 Subject: [PATCH 26/43] tweaked defaults --- interface/src/Hair.cpp | 2 +- interface/src/Hair.h | 4 ++-- interface/src/avatar/Avatar.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index bd2092cf3f..824e273049 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -20,7 +20,7 @@ const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_STIFFNESS = 0.0000f; +const float HAIR_STIFFNESS = 0.0003f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); diff --git a/interface/src/Hair.h b/interface/src/Hair.h index dfb90cae21..0ca507ee23 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -25,8 +25,8 @@ const int HAIR_CONSTRAINTS = 2; const int DEFAULT_HAIR_STRANDS = 50; const int DEFAULT_HAIR_LINKS = 10; -const float DEFAULT_HAIR_RADIUS = 0.25; -const float DEFAULT_HAIR_LINK_LENGTH = 0.05; +const float DEFAULT_HAIR_RADIUS = 0.15; +const float DEFAULT_HAIR_LINK_LENGTH = 0.03; const float DEFAULT_HAIR_THICKNESS = 0.015; class Hair { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8dbeda25dc..e29a2de71e 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -222,6 +222,7 @@ private: float getBillboardSize() const; + }; #endif // hifi_Avatar_h From 6d4bb0f4fd96cc5d623b0d623058ba83aafdc862 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 18:27:26 -0700 Subject: [PATCH 27/43] Added Uint8ClampedArray --- examples/Test.js | 2 +- examples/typedArraysUnitTest.js | 19 ++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 2 ++ libraries/script-engine/src/TypedArrays.cpp | 27 ++++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 8 +++++- 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/examples/Test.js b/examples/Test.js index caecfc5af7..36dee7bd90 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -80,7 +80,7 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { } for (var i = 0; i < array1.length; ++i) { if (array1[i] !== array2[i]) { - throw new AssertionException(array1[i], array2[i], message); + throw new AssertionException(array1[i], array2[i], i + " " + message); } } } diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index a466c9104f..859bc9b9b3 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -687,3 +687,22 @@ test('Typed Array getters/setters', function () { this.assertEquals(bytes[1], 0xff); }); + +test('Uint8ClampedArray', function () { + this.assertEquals(Uint8ClampedArray.BYTES_PER_ELEMENT, 1, 'Uint8ClampedArray.BYTES_PER_ELEMENT'); + var a = new Uint8ClampedArray([-Infinity, -Number.MAX_VALUE, -1, -Number.MIN_VALUE, -0, + 0, Number.MIN_VALUE, 1, 1.1, 1.9, 255, 255.1, 255.9, 256, Number.MAX_VALUE, Infinity, + NaN]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1); + this.assertEquals(a.byteOffset, 0); + this.assertEquals(a.byteLength, 17); + this.arrayEqual(a, [0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0], "array test"); +}); + +test('Regression Tests', function() { + // Bug: https://github.com/inexorabletash/polyfill/issues/16 + var minFloat32 = 1.401298464324817e-45; + var truncated = new Float32Array([-minFloat32 / 2 - Math.pow(2, -202)]).get(0); + this.assertEquals(truncated, -minFloat32, 'smallest 32 bit float should not truncate to zero'); +}); + diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 4074beb88e..0279e2ea27 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -100,6 +100,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); @@ -141,6 +142,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 106b824f76..6b461fc6fb 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -342,6 +342,33 @@ void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &nam setPropertyHelper(ba, name, id, value); } +Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint8ClampedArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + if (value.toNumber() > 255) { + stream << (quint8)255; + } else if (value.toNumber() < 0) { + stream << (quint8)0; + } else { + stream << (quint8)glm::clamp(qRound(value.toNumber()), 0, 255); + } + } +} + Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint16)); } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2eff3e84b9..bacb067add 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -19,6 +19,7 @@ static const QString LENGTH_PROPERTY_NAME = "length"; static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array"; +static const QString UINT_8_CLAMPED_ARRAY_CLASS_NAME = "Uint8ClampedArray"; static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array"; static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array"; static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array"; @@ -98,7 +99,12 @@ public: }; class Uint8ClampedArrayClass : public TypedArray { - // TODO + Q_OBJECT +public: + Uint8ClampedArrayClass(ScriptEngine* scriptEngine); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Int16ArrayClass : public TypedArray { From aa65084ae1c8199f122e03b5d36e43e3d9c56b17 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 23:08:41 -0700 Subject: [PATCH 28/43] Code cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 15 ++++++++++- libraries/script-engine/src/ScriptEngine.cpp | 27 ++----------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 4f85eeeae2..f8299266fd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -12,12 +12,13 @@ #include #include "ArrayBufferPrototype.h" +#include "DataViewClass.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "ArrayBufferClass.h" static const QString CLASS_NAME = "ArrayBuffer"; -static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) @@ -46,6 +47,18 @@ _scriptEngine(scriptEngine) { _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); + + // Registering other array types + new DataViewClass(scriptEngine); + new Int8ArrayClass(scriptEngine); + new Uint8ArrayClass(scriptEngine); + new Uint8ClampedArrayClass(scriptEngine); + new Int16ArrayClass(scriptEngine); + new Uint16ArrayClass(scriptEngine); + new Int32ArrayClass(scriptEngine); + new Uint32ArrayClass(scriptEngine); + new Float32ArrayClass(scriptEngine); + new Float64ArrayClass(scriptEngine); } QScriptValue ArrayBufferClass::newInstance(qint32 size) { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0279e2ea27..a224b9fa33 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,19 +94,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -131,25 +120,13 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); - // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { From 27cdcd9ada874f56d5be16097e3f09e74df788e4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 17:03:38 +0200 Subject: [PATCH 29/43] Fix compiler warnings for g++ 4.7. This fixes nine compiler warnings like: /opt/highfidelity/hifi/hifi/libraries/particles/src/Particle.cpp:371:43: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] The best way to avoid this (without using an ugly (int) cast) is to bring everything to one side and compare with zero, which results in the same assembly code after optimization (it's the same to the compiler). --- libraries/particles/src/Particle.cpp | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 76890afafe..814da636c1 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -368,7 +368,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t editID; // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(editID) > length) { + if (length - processedBytes - sizeof(editID) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -387,7 +387,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t creatorTokenID; // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(creatorTokenID) > length) { + if (length - processedBytes - sizeof(creatorTokenID) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -426,7 +426,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lastEdited // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(newParticle._lastEdited) > length) { + if (length - processedBytes - sizeof(newParticle._lastEdited) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -439,7 +439,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // properties included bits uint16_t packetContainsBits = 0; if (!isNewParticle) { - if (processedBytes + sizeof(packetContainsBits) > length) { + if (length - processedBytes - sizeof(packetContainsBits) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -452,7 +452,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // radius if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { - if (processedBytes + sizeof(newParticle._radius) > length) { + if (length - processedBytes - sizeof(newParticle._radius) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -464,7 +464,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // position if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { - if (processedBytes + sizeof(newParticle._position) > length) { + if (length - processedBytes - sizeof(newParticle._position) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -476,7 +476,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // color if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { - if (processedBytes + sizeof(newParticle._color) > length) { + if (length - processedBytes - sizeof(newParticle._color) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -488,7 +488,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // velocity if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { - if (processedBytes + sizeof(newParticle._velocity) > length) { + if (length - processedBytes - sizeof(newParticle._velocity) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -500,7 +500,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // gravity if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { - if (processedBytes + sizeof(newParticle._gravity) > length) { + if (length - processedBytes - sizeof(newParticle._gravity) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -512,7 +512,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // damping if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { - if (processedBytes + sizeof(newParticle._damping) > length) { + if (length - processedBytes - sizeof(newParticle._damping) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -524,7 +524,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lifetime if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { - if (processedBytes + sizeof(newParticle._lifetime) > length) { + if (length - processedBytes - sizeof(newParticle._lifetime) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -537,7 +537,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // TODO: make inHand and shouldDie into single bits // inHand if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { - if (processedBytes + sizeof(newParticle._inHand) > length) { + if (length - processedBytes - sizeof(newParticle._inHand) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -549,7 +549,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // shouldDie if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { - if (processedBytes + sizeof(newParticle._shouldDie) > length) { + if (length - processedBytes - sizeof(newParticle._shouldDie) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -562,7 +562,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // script if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength; - if (processedBytes + sizeof(scriptLength) > length) { + if (length - processedBytes - sizeof(scriptLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -571,7 +571,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(scriptLength); processedBytes += sizeof(scriptLength); - if (processedBytes + scriptLength > length) { + if (length - processedBytes - scriptLength < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -585,7 +585,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelURL if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { uint16_t modelURLLength; - if (processedBytes + sizeof(modelURLLength) > length) { + if (length - processedBytes - sizeof(modelURLLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -594,7 +594,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(modelURLLength); processedBytes += sizeof(modelURLLength); - if (processedBytes + modelURLLength > length) { + if (length - processedBytes - modelURLLength < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -607,7 +607,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelScale if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { - if (processedBytes + sizeof(newParticle._modelScale) > length) { + if (length - processedBytes - sizeof(newParticle._modelScale) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -619,7 +619,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelTranslation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { - if (processedBytes + sizeof(newParticle._modelTranslation) > length) { + if (length - processedBytes - sizeof(newParticle._modelTranslation) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -632,7 +632,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelRotation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { const int expectedBytesForPackedQuat = sizeof(uint16_t) * 4; // this is how we pack the quats - if (processedBytes + expectedBytesForPackedQuat > length) { + if (length - processedBytes - expectedBytesForPackedQuat < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer From bd212c26d0960e9a29eaf9e628b693d22a62a6ac Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 17:10:39 +0200 Subject: [PATCH 30/43] Fix initializer list order (avoiding compiler warning). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the compiler warning: In file included from /opt/highfidelity/hifi/hifi/interface/src/Application.h:45:0, from /opt/highfidelity/hifi/hifi/interface/src/Audio.cpp:42: /opt/highfidelity/hifi/hifi/interface/src/Audio.h: In constructor ‘Audio::Audio(int16_t, QObject*)’: /opt/highfidelity/hifi/hifi/interface/src/Audio.h:268:30: warning: ‘Audio::_interframeTimeGapStats’ will be initialized after [-Wreorder] /opt/highfidelity/hifi/hifi/interface/src/Audio.h:259:9: warning: ‘int Audio::_starveCount’ [-Wreorder] /opt/highfidelity/hifi/hifi/interface/src/Audio.cpp:63:1: warning: when initialized here [-Wreorder] by fixing the initializer list of class Audio. Note that I omitted _audioMixerAvatarStreamAudioStats() - which is a default constructor that is called anyway (it's the default!) and therefore doesn't belong in the initializer list. --- interface/src/Audio.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 3689ff0143..6a26563e36 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -112,12 +112,11 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _scopeInput(0), _scopeOutputLeft(0), _scopeOutputRight(0), - _audioMixerAvatarStreamAudioStats(), + _starveCount(0), + _consecutiveNotMixedCount(0), _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), - _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), - _starveCount(0), - _consecutiveNotMixedCount(0) + _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); From ea98b58b405b1599b1d55449b4b0b3a097fe7cc4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:29:01 +0200 Subject: [PATCH 31/43] Debug support: allow printing of glm types to QDebug. This adds support for serialization to QDebug of glm::vec3, glm::quat and glm::mat4. Output example: qDebug().nospace() << "Calling PhysicsEntity::findRayIntersection(" << origin << ", " << direction << ", &distance) const; numShapes = " << numShapes; leads to: [2014-07-13T20:24:47] Calling PhysicsEntity::findRayIntersection({type='glm::vec3', x=5222.45, y=2159.05, z=6527.79}, {type='glm::vec3', x=0, y=-0.119145, z=-0.992877}, &distance) const; numShapes = 0 Note that we explicitly don't return dbg.space() because we want to be able to print these things comma separated as follows: {...}, {...}, ... as opposed to {...} , {...} etc. I changed the already existing operator<< for Box to the more general case, where it just prints its members and doesn't mess with the internals of its members. The result is more verbose, but also more recognizable when in the future everything will look the same, allowing for speed reading the debug output. The constructor of Box needed to made explicit because it was too annoying that when one forgets to #include "StreamUtils.h" that writing a glm::vec3 resulted in printing out a Box, implicitly converted from the vector. --- libraries/metavoxels/src/MetavoxelUtil.cpp | 6 ++--- libraries/metavoxels/src/MetavoxelUtil.h | 2 +- libraries/shared/src/StreamUtils.cpp | 31 ++++++++++++++++++++++ libraries/shared/src/StreamUtils.h | 13 ++++++--- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index f2f434b24d..b0233f04cd 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -26,6 +26,7 @@ #include "MetavoxelUtil.h" #include "ScriptCache.h" +#include "StreamUtils.h" static int scriptHashType = qRegisterMetaType(); static int parameterizedURLType = qRegisterMetaType(); @@ -310,9 +311,8 @@ Box operator*(const glm::mat4& matrix, const Box& box) { return newBox; } -QDebug& operator<<(QDebug& out, const Box& box) { - return out << '(' << box.minimum.x << box.minimum.y << box.minimum.z << ") (" << - box.maximum.x << box.maximum.y << box.maximum.z << ')'; +QDebug& operator<<(QDebug& dbg, const Box& box) { + return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximumx=" << box.maximum << "}"; } QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 34c2bac6be..83aac1318e 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -42,7 +42,7 @@ public: STREAM glm::vec3 minimum; STREAM glm::vec3 maximum; - Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); + explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); bool contains(const glm::vec3& point) const; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 5356c45a51..1723068eee 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -95,3 +95,34 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { #endif // DEBUG +#ifndef QT_NO_DEBUG_STREAM +#include + +QDebug& operator<<(QDebug& dbg, const glm::vec3& v) { + dbg.nospace() << "{type='glm::vec3'" + ", x=" << v.x << + ", y=" << v.y << + ", z=" << v.z << + "}"; + return dbg; +} + +QDebug& operator<<(QDebug& dbg, const glm::quat& q) { + dbg.nospace() << "{type='glm::quat'" + ", x=" << q.x << + ", y=" << q.y << + ", z=" << q.z << + ", w=" << q.w << + "}"; + return dbg; +} + +QDebug& operator<<(QDebug& dbg, const glm::mat4& m) { + dbg.nospace() << "{type='glm::mat4', ["; + for (int j = 0; j < 4; ++j) { + dbg << ' ' << m[0][j] << ' ' << m[1][j] << ' ' << m[2][j] << ' ' << m[3][j] << ';'; + } + return dbg << " ]}"; +} + +#endif // QT_NO_DEBUG_STREAM diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index 2a42a3ea7b..4f74e895d2 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -38,13 +38,20 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); // less common utils can be enabled with DEBUG #ifdef DEBUG -#include "CollisionInfo.h" -#include "SphereShape.h" -#include "CapsuleShape.h" +class CollisionInfo; +class SphereShape; +class CapsuleShape.h; std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); std::ostream& operator<<(std::ostream& s, const SphereShape& shape); std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); #endif // DEBUG +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +// Add support for writing these to qDebug()> +QDebug& operator<<(QDebug& s, const glm::vec3& v); +QDebug& operator<<(QDebug& s, const glm::quat& q); +QDebug& operator<<(QDebug& s, const glm::mat4& m); +#endif // QT_NO_DEBUG_STREAM #endif // hifi_StreamUtils_h From 961eca432746bc8e1fb1b949d04cb7c8d3ef2de7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:51:19 +0200 Subject: [PATCH 32/43] Fix typo in previous commit --- libraries/metavoxels/src/MetavoxelUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index b0233f04cd..d35b9a698a 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -312,7 +312,7 @@ Box operator*(const glm::mat4& matrix, const Box& box) { } QDebug& operator<<(QDebug& dbg, const Box& box) { - return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximumx=" << box.maximum << "}"; + return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximum=" << box.maximum << "}"; } QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { From 13c2c69d0666805a9324a59468037dbe7cd90ba9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:54:27 +0200 Subject: [PATCH 33/43] Fix another typo in previous commit --- libraries/shared/src/StreamUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index 4f74e895d2..c9356eaca2 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -48,7 +48,7 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); #ifndef QT_NO_DEBUG_STREAM class QDebug; -// Add support for writing these to qDebug()> +// Add support for writing these to qDebug(). QDebug& operator<<(QDebug& s, const glm::vec3& v); QDebug& operator<<(QDebug& s, const glm::quat& q); QDebug& operator<<(QDebug& s, const glm::mat4& m); From 147a4119cbdf8532ed61608a62d9536eca202f77 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 21:54:07 +0200 Subject: [PATCH 34/43] Fix linker problem This fixes the linker problem: /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tvec3 const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tquat const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tmat4x4 const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here collect2: error: ld returned 1 exit status Note these are the serializers to std::ostream that were already there, not the ones that I added (which are to QDebug). This bug was already there, I only got these errors now after adding some debug code. The reason is obvious: they are already defined in libshared.a (through StreamUtils.cpp). No need to define them again in ./tests/physics/src/PhysicsTestUtil.cpp --- tests/physics/src/PhysicsTestUtil.cpp | 20 +------------------- tests/physics/src/PhysicsTestUtil.h | 3 --- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/tests/physics/src/PhysicsTestUtil.cpp b/tests/physics/src/PhysicsTestUtil.cpp index f176d5e637..a11d4f2add 100644 --- a/tests/physics/src/PhysicsTestUtil.cpp +++ b/tests/physics/src/PhysicsTestUtil.cpp @@ -12,25 +12,7 @@ #include #include "PhysicsTestUtil.h" - -std::ostream& operator<<(std::ostream& s, const glm::vec3& v) { - s << "<" << v.x << "," << v.y << "," << v.z << ">"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const glm::quat& q) { - s << "<" << q.x << "," << q.y << "," << q.z << "," << q.w << ">"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const glm::mat4& m) { - s << "["; - for (int j = 0; j < 4; ++j) { - s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";"; - } - s << " ]"; - return s; -} +#include "StreamUtils.h" std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) { s << "[penetration=" << c._penetration diff --git a/tests/physics/src/PhysicsTestUtil.h b/tests/physics/src/PhysicsTestUtil.h index 998a18ff5d..c93545dd76 100644 --- a/tests/physics/src/PhysicsTestUtil.h +++ b/tests/physics/src/PhysicsTestUtil.h @@ -21,9 +21,6 @@ const glm::vec3 xAxis(1.f, 0.f, 0.f); const glm::vec3 yAxis(0.f, 1.f, 0.f); const glm::vec3 zAxis(0.f, 0.f, 1.f); -std::ostream& operator<<(std::ostream& s, const glm::vec3& v); -std::ostream& operator<<(std::ostream& s, const glm::quat& q); -std::ostream& operator<<(std::ostream& s, const glm::mat4& m); std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); #endif // hifi_PhysicsTestUtil_h From 2d4194cae501d3dfba678e17856b161185d76bc4 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 13 Jul 2014 23:33:02 -0700 Subject: [PATCH 35/43] fixes per review, clap animation requires double-tap to start --- examples/clap.js | 12 +++++------- interface/src/Hair.cpp | 10 ++++------ interface/src/avatar/Avatar.cpp | 1 - 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/clap.js b/examples/clap.js index 28835c1f00..6e70979c2e 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -94,23 +94,21 @@ function playClap(volume, position) { Audio.playSound(claps[clip], options); } -var FASTEST_CLAP_INTERVAL = 100.0; -var SLOWEST_CLAP_INTERVAL = 2000.0; +var FASTEST_CLAP_INTERVAL = 150.0; +var SLOWEST_CLAP_INTERVAL = 750.0; Controller.keyPressEvent.connect(function(event) { if(event.text == "SHIFT") { if (!clickClappingNow) { clickClappingNow = true; clickStartTime = new Date(); - playClap(1.0, Camera.getPosition()); lastClapFrame = 0; - MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false); } else { - // Adjust animation speed for measured clicking interval + // start or adjust clapping speed based on the duration between clicks clickEndTime = new Date(); - var milliseconds = clickEndTime - clickStartTime; + var milliseconds = Math.max(clickEndTime - clickStartTime, FASTEST_CLAP_INTERVAL); clickStartTime = new Date(); - if ((milliseconds < SLOWEST_CLAP_INTERVAL) && (milliseconds > FASTEST_CLAP_INTERVAL)) { + if (milliseconds < SLOWEST_CLAP_INTERVAL) { clapRate = ANIMATION_FRAMES_PER_CLAP * (1000.0 / milliseconds); playClap(1.0, Camera.getPosition()); MyAvatar.stopAnimation(clapAnimation); diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 824e273049..4dc9b9cf98 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -20,7 +20,7 @@ const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_STIFFNESS = 0.0003f; +const float HAIR_STIFFNESS = 0.005f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); @@ -72,12 +72,10 @@ Hair::Hair(int strands, _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1; } } - _hairPosition[vertexIndex] = thisVertex; - _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; - _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex; _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); - _hairQuadDelta[vertexIndex] *= 1.f - (link / _links); + _hairQuadDelta[vertexIndex] *= 1.f - ((float)link / _links); _hairNormals[vertexIndex] = glm::normalize(randVector()); if (randFloat() < elevation / PI_OVER_TWO) { _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); @@ -123,7 +121,7 @@ void Hair::simulate(float deltaTime) { // Add stiffness to return to original position _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; + * powf(1.f - (float)link / _links, 2.f) * HAIR_STIFFNESS; // Add angular acceleration const float ANGULAR_VELOCITY_MIN = 0.001f; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ff49d7394b..fd8f2d836b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,7 +46,6 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; Avatar::Avatar() : AvatarData(), - _hair(), _skeletonModel(this), _bodyYawDelta(0.0f), _velocity(0.0f, 0.0f, 0.0f), From 4b7ba08932e3584108853fa9ba6d6c794d366370 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 14 Jul 2014 08:53:10 -0700 Subject: [PATCH 36/43] code review fix --- interface/src/Hair.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 4dc9b9cf98..e07b624d89 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -29,15 +29,15 @@ Hair::Hair(int strands, float radius, float linkLength, float hairThickness) : + _strands(strands), + _links(links), + _linkLength(linkLength), + _hairThickness(hairThickness), + _radius(radius), _acceleration(0.0f), _angularVelocity(0.0f), _gravity(0.0f) { - _strands = strands; - _links = links; - _radius = radius; - _linkLength = linkLength; - _hairThickness = hairThickness; _hairPosition = new glm::vec3[_strands * _links]; _hairOriginalPosition = new glm::vec3[_strands * _links]; _hairLastPosition = new glm::vec3[_strands * _links]; From a59cef31979f09a07bd11641965a5858df2474b5 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 10:05:37 -0700 Subject: [PATCH 37/43] removed debug code in AudioMixer --- assignment-client/src/audio/AudioMixer.cpp | 26 +--------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 6df23ca32f..c86d37e283 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,11 +366,6 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf } void AudioMixer::prepareMixForListeningNode(Node* node) { - - static int k = 0; - k++; - bool debug = (k % 20) == 0; - AvatarAudioRingBuffer* nodeRingBuffer = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer(); // zero out the client mix for this node @@ -385,31 +380,12 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { // enumerate the ARBs attached to the otherNode and add all that should be added to mix for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; - - + if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() && otherNodeBuffer->getNextOutputTrailingLoudness() > 0.0f) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); - } else { - - if (debug) { - printf("\nWILL NOT MIX!!!\n"); - printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); - printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); - - if (otherNodeBuffer->getType() == PositionalAudioRingBuffer::Microphone) - printf("\t avatar buffer...\n"); - else - { - printf("\t inj buffer %s\n", ((InjectedAudioRingBuffer*)otherNodeBuffer)->getStreamIdentifier().toString().toLatin1().data()); - } - - printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); - printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); - printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); - } } } } From 30fd4e27d05d08a0a71a1a2bc515ba4868c9ed93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 10:43:01 -0700 Subject: [PATCH 38/43] pre-CR cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 16 +- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferPrototype.cpp | 1 - .../src/ArrayBufferViewClass.cpp | 60 +----- .../script-engine/src/ArrayBufferViewClass.h | 10 +- libraries/script-engine/src/DataViewClass.cpp | 5 +- .../script-engine/src/DataViewPrototype.cpp | 22 +- libraries/script-engine/src/ScriptEngine.cpp | 1 - libraries/script-engine/src/ScriptEngine.h | 2 +- .../script-engine/src/TypedArrayPrototype.cpp | 106 ++++++++++ .../script-engine/src/TypedArrayPrototype.h | 33 +++ libraries/script-engine/src/TypedArrays.cpp | 191 +++++------------- libraries/script-engine/src/TypedArrays.h | 54 ++--- 13 files changed, 241 insertions(+), 268 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrayPrototype.cpp create mode 100644 libraries/script-engine/src/TypedArrayPrototype.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index f8299266fd..ab33b5ffe7 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -23,10 +23,9 @@ static const QString CLASS_NAME = "ArrayBuffer"; Q_DECLARE_METATYPE(QByteArray*) ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { - qDebug() << "Created with engine: " << engine(); qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); QScriptValue global = engine()->globalObject(); @@ -40,15 +39,16 @@ _scriptEngine(scriptEngine) { QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); // Registering other array types + // The script engine is there parent so it'll delete them with itself new DataViewClass(scriptEngine); new Int8ArrayClass(scriptEngine); new Uint8ArrayClass(scriptEngine); @@ -87,11 +87,10 @@ QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { + // return if callee (function called) is not of type ArrayBuffer return QScriptValue(); } - QScriptValue arg = context->argument(0); - if (!arg.isValid() || !arg.isNumber()) { return QScriptValue(); } @@ -100,6 +99,7 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { + // if called with keyword new, replace this object. context->setThisObject(newObject); return engine->undefinedValue(); } @@ -118,8 +118,8 @@ QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& obj return 0; // No access } -QScriptValue ArrayBufferClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { QByteArray* ba = qscriptvalue_cast(object.data()); if (ba && name == _byteLength) { return ba->length(); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c7b4d447dd..f7ad8ad4bd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -29,19 +29,19 @@ public: QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); - ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; + ScriptEngine* getEngine() { return _scriptEngine; } + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 37db5f3eec..53ebebc740 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -12,7 +12,6 @@ #include #include "ArrayBufferClass.h" - #include "ArrayBufferPrototype.h" Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index a01f239512..aad2e6add7 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -14,8 +14,8 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { // Save string handles for quick lookup _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); @@ -32,8 +32,8 @@ QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& return 0; // No access } -QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferViewClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bufferName) { return object.data().property(_bufferName); } @@ -50,55 +50,3 @@ QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptVal const QScriptString& name, uint id) { return QScriptValue::Undeletable; } - -//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, -// const QScriptString& name, -// QueryFlags flags, uint* id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// int pos = name.toArrayIndex(&ok); -// -// // Check that name is a valid index and arrayBuffer exists -// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { -// *id = pos; // save pos to avoid recomputation -// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//QScriptValue DataViewClass::property(const QScriptValue &object, -// const QScriptString &name, uint id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// name.toArrayIndex(&ok); -// -// if (ok && arrayBuffer) { -// return (*arrayBuffer)[id]; -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//void DataViewClass::setProperty(QScriptValue &object, -// const QScriptString &name, -// uint id, const QScriptValue &value) { -// QByteArray *ba = qscriptvalue_cast(object.data()); -// if (!ba) -// return; -// if (name == length) { -// resize(*ba, value.toInt32()); -// } else { -// qint32 pos = id; -// if (pos < 0) -// return; -// if (ba->size() <= pos) -// resize(*ba, pos + 1); -// (*ba)[pos] = char(value.toInt32()); -// } -//} -// -//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, -// const QScriptString& name, uint id) { -// return QScriptValue::Undeletable; -//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index acc061c8a3..b673ebf280 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -34,12 +34,12 @@ public: ScriptEngine* getScriptEngine() { return _scriptEngine; } virtual QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index efb45daf3f..a65bdff617 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -29,12 +29,11 @@ DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass( QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(name(), _ctor); } @@ -47,7 +46,7 @@ QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, return engine()->newObject(this, data); } -QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue DataViewClass::construct(QScriptContext* context, QScriptEngine* engine) { DataViewClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls || context->argumentCount() < 1) { return QScriptValue(); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 29264e9bc5..91f7e01b50 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -36,8 +36,6 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { return (offset + size) <= viewOffset + viewLength; } -///////////////// GETTERS //////////////////////////// - qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); @@ -149,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(); + return QScriptValue(NAN); } return result; @@ -158,8 +156,6 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) return QScriptValue(); } -///////////////// SETTERS //////////////////////////// - void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); @@ -167,7 +163,7 @@ void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { stream << (qint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -178,7 +174,7 @@ void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { stream << (quint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -190,7 +186,7 @@ void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -202,7 +198,7 @@ void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleE stream << (quint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -214,7 +210,7 @@ void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -226,7 +222,7 @@ void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleE stream << (quint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -239,7 +235,7 @@ void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEn stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -252,7 +248,7 @@ void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleE stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a224b9fa33..cd7c2670ec 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -143,7 +143,6 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { - qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3dda172044..0eda74914f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -57,7 +57,7 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } - QScriptEngine* getScriptEngine() { return &_engine; } + QScriptEngine* getEngine() { return &_engine; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } /// sets the script contents, will return false if failed, will fail if script is already running diff --git a/libraries/script-engine/src/TypedArrayPrototype.cpp b/libraries/script-engine/src/TypedArrayPrototype.cpp new file mode 100644 index 0000000000..37274dd080 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.cpp @@ -0,0 +1,106 @@ +// +// TypedArrayPrototype.cpp +// +// +// Created by Clement on 7/14/14. +// Copyright 2014 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 "TypedArrays.h" + +#include "TypedArrayPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + engine()->evaluate("throw \"ArgumentError: array does not fit\""); + return; + } + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); + } +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); + } + return QScriptValue(); +} + +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } +} diff --git a/libraries/script-engine/src/TypedArrayPrototype.h b/libraries/script-engine/src/TypedArrayPrototype.h new file mode 100644 index 0000000000..86d578ace0 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.h @@ -0,0 +1,33 @@ +// +// TypedArrayPrototype.h +// +// +// Created by Clement on 7/14/14. +// Copyright 2014 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 +// + +#ifndef hifi_TypedArrayPrototype_h +#define hifi_TypedArrayPrototype_h + +#include "ArrayBufferViewClass.h" + +class TypedArrayPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + TypedArrayPrototype(QObject* parent = NULL); + +public slots: + void set(QScriptValue array, qint32 offset = 0); + QScriptValue subarray(qint32 begin); + QScriptValue subarray(qint32 begin, qint32 end); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; +}; + +#endif // hifi_TypedArrayPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 6b461fc6fb..72e84859c4 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -12,6 +12,7 @@ #include #include "ScriptEngine.h" +#include "TypedArrayPrototype.h" #include "TypedArrays.h" @@ -30,12 +31,11 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(_name, _ctor); } @@ -46,8 +46,9 @@ QScriptValue TypedArray::newInstance(quint32 length) { } QScriptValue TypedArray::newInstance(QScriptValue array) { - if (array.property("length").isValid()) { - quint32 length = array.property("length").toInt32(); + const QString ARRAY_LENGTH_HANDLE = "length"; + if (array.property(ARRAY_LENGTH_HANDLE).isValid()) { + quint32 length = array.property(ARRAY_LENGTH_HANDLE).toInt32(); QScriptValue newArray = newInstance(length); for (int i = 0; i < length; ++i) { QScriptValue value = array.property(QString::number(i)); @@ -70,12 +71,11 @@ QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, qu return engine()->newObject(this, data); } -QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { return QScriptValue(); } - if (context->argumentCount() == 0) { return cls->newInstance(0); } @@ -84,6 +84,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + // parse arguments if (arrayBuffer) { if (context->argumentCount() == 1) { // Case for entire ArrayBuffer @@ -136,6 +137,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin } if (context->isCalledAsConstructor()) { + // if called with the new keyword, replace this object context->setThisObject(newObject); return engine->undefinedValue(); } @@ -164,8 +166,8 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, return ArrayBufferViewClass::queryProperty(object, name, flags, id); } -QScriptValue TypedArray::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue TypedArray::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bytesPerElementName) { return QScriptValue(_bytesPerElement); } @@ -193,98 +195,10 @@ void TypedArray::setBytesPerElement(quint32 bytesPerElement) { _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { -} - -QByteArray* TypedArrayPrototype::thisArrayBuffer() const { - QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); - return qscriptvalue_cast(bufferObject.data()); -} - -void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { - TypedArray* typedArray = static_cast(parent()); - if (array.isArray() || typedArray) { - if (offset < 0) { - engine()->evaluate("throw \"ArgumentError: negative offset\""); - } - quint32 length = array.property("length").toInt32(); - if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - engine()->evaluate("throw \"ArgumentError: array does not fit\""); - return; - } - for (int i = 0; i < length; ++i) { - thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); - } - } else { - engine()->evaluate("throw \"ArgumentError: not an array\""); - } -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); - qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); - qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); - qint32 bytesPerElement = typedArray->_bytesPerElement; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); - qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); - qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); - qint32 bytesPerElement = typedArray->_bytesPerElement; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - end = (end < 0) ? length + end : end; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - end = glm::clamp(end, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - length = (end - begin > 0) ? end - begin : 0; - return typedArray->newInstance(arrayBuffer, byteOffset, length); -} - -QScriptValue TypedArrayPrototype::get(quint32 index) { - TypedArray* typedArray = static_cast(parent()); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), - name, - QScriptClass::HandlesReadAccess, &id); - if (QScriptClass::HandlesReadAccess & flags) { - return typedArray->property(thisObject(), name, id); - } - return QScriptValue(); -} - -void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue object = thisObject(); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(object, - name, - QScriptClass::HandlesWriteAccess, &id); - if (QScriptClass::HandlesWriteAccess & flags) { - typedArray->setProperty(object, name, id, value); - } -} - +// templated helper functions +// don't work for floats as they require single precision settings template -QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString& name, uint id) { bool ok = false; name.toArrayIndex(&ok); @@ -300,7 +214,7 @@ QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString & } template -void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint id, const QScriptValue& value) { if (arrayBuffer && value.isNumber()) { QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); @@ -313,15 +227,15 @@ Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEn setBytesPerElement(sizeof(qint8)); } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Int8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -329,16 +243,15 @@ Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -346,16 +259,15 @@ Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : Typ setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint8ClampedArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -373,16 +285,15 @@ Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint16)); } -QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Int16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -390,16 +301,15 @@ Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint16)); } -QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -407,16 +317,15 @@ Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint32)); } -QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Int32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -424,16 +333,16 @@ Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint32)); } -QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Uint32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -441,8 +350,7 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(float)); } -QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -461,9 +369,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -477,8 +385,7 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(double)); } -QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -497,9 +404,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index bacb067add..a1a288c3c9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -64,29 +64,13 @@ protected: friend class TypedArrayPrototype; }; -class TypedArrayPrototype : public QObject, public QScriptable { - Q_OBJECT -public: - TypedArrayPrototype(QObject* parent = NULL); - -public slots: - void set(QScriptValue array, qint32 offset = 0); - QScriptValue subarray(qint32 begin); - QScriptValue subarray(qint32 begin, qint32 end); - - QScriptValue get(quint32 index); - void set(quint32 index, QScriptValue& value); -private: - QByteArray* thisArrayBuffer() const; -}; - class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ArrayClass : public TypedArray { @@ -94,8 +78,8 @@ class Uint8ArrayClass : public TypedArray { public: Uint8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ClampedArrayClass : public TypedArray { @@ -103,8 +87,8 @@ class Uint8ClampedArrayClass : public TypedArray { public: Uint8ClampedArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Int16ArrayClass : public TypedArray { @@ -112,8 +96,8 @@ class Int16ArrayClass : public TypedArray { public: Int16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint16ArrayClass : public TypedArray { @@ -121,16 +105,17 @@ class Uint16ArrayClass : public TypedArray { public: Uint16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Int32ArrayClass : public TypedArray { Q_OBJECT public: Int32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint32ArrayClass : public TypedArray { @@ -138,8 +123,8 @@ class Uint32ArrayClass : public TypedArray { public: Uint32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Float32ArrayClass : public TypedArray { @@ -147,16 +132,17 @@ class Float32ArrayClass : public TypedArray { public: Float32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Float64ArrayClass : public TypedArray { Q_OBJECT public: Float64ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From cb5a9bf6688e3728ac1783422e033b82e55d463b Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 10:43:02 -0700 Subject: [PATCH 39/43] moved some const defs to avoid redefinition --- libraries/shared/src/SharedUtil.cpp | 3 +++ libraries/shared/src/SharedUtil.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 23d33969f4..b5be502ed5 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -839,6 +839,9 @@ QByteArray createByteArray(const glm::vec3& vector) { } QString formatUsecTime(float usecs, int prec) { + static const quint64 SECONDS_PER_MINUTE = 60; + static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; + QString result; if (usecs > USECS_PER_MINUTE) { result = QString::number(usecs / USECS_PER_MINUTE, 'f', prec) + "min"; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 81ec2ab810..6bb39f7e12 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -57,9 +57,7 @@ static const float METERS_PER_MILLIMETER = 0.001f; static const float MILLIMETERS_PER_METER = 1000.0f; static const quint64 USECS_PER_MSEC = 1000; static const quint64 MSECS_PER_SECOND = 1000; -static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; -static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; const int BITS_IN_BYTE = 8; From afa43a9d7e8ce19f0f35aa708cea7b19a1c0fcba Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 11:06:10 -0700 Subject: [PATCH 40/43] Fix win build fail --- examples/typedArraysUnitTest.js | 2 +- libraries/script-engine/src/DataViewPrototype.cpp | 4 ++-- libraries/script-engine/src/TypedArrays.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 859bc9b9b3..e86a07289d 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -514,7 +514,7 @@ test('TypedArray setting', function () { var b = new Int32Array(5); b.set(a); this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); - // raises(function () { b.set(a, 1); }); + this.raises(function () { b.set(a, 1); }); b.set(new Int32Array([99, 98]), 2); this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 91f7e01b50..8bab574f33 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -128,7 +128,7 @@ QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return QScriptValue(result); @@ -147,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 72e84859c4..751d1385e5 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -362,7 +362,7 @@ QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScri float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; } @@ -397,7 +397,7 @@ QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScri double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; } From a37e24aeb48981dd6252b5a0f0ee03ca24a5eeef Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 11:08:00 -0700 Subject: [PATCH 41/43] updated domain page stats to use formated usec time strings --- .../src/audio/AudioMixerClientData.cpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 94bbdc6a6b..6559b57959 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -280,12 +280,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped: ?" + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); if (avatarRingBuffer) { @@ -299,12 +299,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped:" + QString::number(streamStats._ringBufferSilentFramesDropped) + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); } else { result = "mic unknown"; } @@ -321,12 +321,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped:" + QString::number(streamStats._ringBufferSilentFramesDropped) + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); } } return result; From 935f28aa35d0be5fe5d6d738473205529f207925 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 14 Jul 2014 21:18:37 +0200 Subject: [PATCH 42/43] Type fix. That that even compiled :/ --- libraries/shared/src/StreamUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index c9356eaca2..6d00c0e354 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -40,7 +40,7 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); #ifdef DEBUG class CollisionInfo; class SphereShape; -class CapsuleShape.h; +class CapsuleShape; std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); std::ostream& operator<<(std::ostream& s, const SphereShape& shape); std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); From 6bf06989db1d6c5ec9052d60b27ab917a6e37c14 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 14 Jul 2014 15:24:47 -0600 Subject: [PATCH 43/43] Adding place/user go to logging --- interface/src/location/LocationManager.cpp | 16 ++++++++++++---- libraries/networking/src/UserActivityLogger.cpp | 12 ++++++++++++ libraries/networking/src/UserActivityLogger.h | 1 + 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 32172d6e38..cbf7d3dfd0 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -13,6 +13,7 @@ #include "Application.h" #include "LocationManager.h" +#include const QString GET_USER_ADDRESS = "/api/v1/users/%1/address"; const QString GET_PLACE_ADDRESS = "/api/v1/places/%1"; @@ -60,23 +61,30 @@ void LocationManager::createNamedLocation(NamedLocation* namedLocation) { } void LocationManager::goTo(QString destination) { - + const QString USER_DESTINATION_TYPE = "user"; + const QString PLACE_DESTINATION_TYPE = "place"; + const QString OTHER_DESTINATION_TYPE = "coordinate_or_username"; + if (destination.startsWith("@")) { // remove '@' and go to user - goToUser(destination.remove(0, 1)); + QString destinationUser = destination.remove(0, 1); + UserActivityLogger::getInstance().wentTo(USER_DESTINATION_TYPE, destinationUser); + goToUser(destinationUser); return; } if (destination.startsWith("#")) { // remove '#' and go to named place - goToPlace(destination.remove(0, 1)); + QString destinationPlace = destination.remove(0, 1); + UserActivityLogger::getInstance().wentTo(PLACE_DESTINATION_TYPE, destinationPlace); + goToPlace(destinationPlace); return; } // go to coordinate destination or to Username if (!goToDestination(destination)) { destination = QString(QUrl::toPercentEncoding(destination)); - + UserActivityLogger::getInstance().wentTo(OTHER_DESTINATION_TYPE, destination); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "goToAddressFromResponse"; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 5b20c82263..0217fb9106 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -146,3 +146,15 @@ void UserActivityLogger::loadedScript(QString scriptName) { logAction(ACTION_NAME, actionDetails); } + +void UserActivityLogger::wentTo(QString destinationType, QString destinationName) { + const QString ACTION_NAME = "went_to"; + QJsonObject actionDetails; + const QString DESTINATION_TYPE_KEY = "destination_type"; + const QString DESTINATION_NAME_KEY = "detination_name"; + + actionDetails.insert(DESTINATION_TYPE_KEY, destinationType); + actionDetails.insert(DESTINATION_NAME_KEY, destinationName); + + logAction(ACTION_NAME, actionDetails); +} diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 4823143234..7c48a72b73 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -35,6 +35,7 @@ public slots: void changedDomain(QString domainURL); void connectedDevice(QString typeOfDevice, QString deviceName); void loadedScript(QString scriptName); + void wentTo(QString destinationType, QString destinationName); private slots: void requestFinished(const QJsonObject& object);