diff --git a/interface/resources/icons/tablet-icons/mic-mute-a.svg b/interface/resources/icons/tablet-icons/mic-mute-a.svg index 4b199c8e01..9dc2c53443 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-a.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-a.svg @@ -1,70 +1,25 @@ - + - -image/svg+xml \ No newline at end of file + + + + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/mic-mute-i.svg b/interface/resources/icons/tablet-icons/mic-mute-i.svg index 69feec7c17..9dc2c53443 100644 --- a/interface/resources/icons/tablet-icons/mic-mute-i.svg +++ b/interface/resources/icons/tablet-icons/mic-mute-i.svg @@ -1,21 +1,25 @@ - + + + - - - + + - - + diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 9ef151b32e..9a84418b3a 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -65,6 +65,12 @@ Windows.Window { root.dynamicContent.fromScript(message); } } + + function clearDebugWindow() { + if (root.dynamicContent && root.dynamicContent.clearWindow) { + root.dynamicContent.clearWindow(); + } + } // Handle message traffic from our loaded QML to the script that launched us signal sendToScript(var message); diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 929752f925..3225dd9f53 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -59,6 +59,7 @@ Rectangle { AudioControls.CheckBox { text: qsTr("Mute microphone"); + isRedCheck: true; checked: Audio.muted; onClicked: { Audio.muted = checked; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad49a82770..34b599654e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5593,6 +5593,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get().data(), &ScriptEngines::onErrorMessage); connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get().data(), &ScriptEngines::onWarningMessage); connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get().data(), &ScriptEngines::onInfoMessage); + connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get().data(), &ScriptEngines::onClearDebugWindow); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 530e356137..17de15e32b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -386,7 +386,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef return 0; } - qint64 clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; + int64_t clockSkew = 0; + uint64_t maxPingRoundTrip = 33333; // two frames periods at 60 fps + if (args.sourceNode) { + clockSkew = args.sourceNode->getClockSkewUsec(); + const float MSECS_PER_USEC = 1000; + maxPingRoundTrip += args.sourceNode->getPingMs() * MSECS_PER_USEC; + } BufferParser parser(data, bytesLeftToRead); @@ -653,7 +659,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef const QUuid& myNodeID = nodeList->getSessionUUID(); bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); - // pack SimulationOwner and terse update properties near each other // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. @@ -678,7 +683,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) { // entity-server is trying to clear our ownership (probably at our own request) // but we actually want to own it, therefore we ignore this clear event - // and pretend that we own it (we assume we'll recover it soon) + // and pretend that we own it (e.g. we assume we'll receive ownership soon) // However, for now, when the server uses a newer time than what we sent, listen to what we're told. if (overwriteLocalData) { @@ -690,16 +695,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // recompute weOwnSimulation for later weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); } - } else if (newSimOwner.getID().isNull() && _simulationOwner.pendingTake(lastEditedFromBufferAdjusted)) { - // entity-server is trying to clear someone else's ownership - // but we want to own it, therefore we ignore this clear event - // and pretend that we own it (we assume we'll get it soon) + } else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) { + // we sent a bid before this packet could have been sent from the server + // so we ignore it and pretend we own the object's simulation weOwnSimulation = true; - if (!_simulationOwner.isNull()) { - // someone else really did own it - markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); - somethingChanged = true; - _simulationOwner.clearCurrentOwner(); + if (newSimOwner.getID().isNull()) { + // entity-server is trying to clear someone else's ownership + // but we want to own it, therefore we ignore this clear event + if (!_simulationOwner.isNull()) { + // someone else really did own it + markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); + somethingChanged = true; + _simulationOwner.clearCurrentOwner(); + } } } else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) { // entity-server tells us that we have simulation ownership while we never requested this for this EntityItem, diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index 4bb5d850a9..1a1790b0e0 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -23,14 +23,16 @@ const uint16_t ObjectActionTractor::tractorVersion = 1; ObjectActionTractor::ObjectActionTractor(const QUuid& id, EntityItemPointer ownerEntity) : ObjectAction(DYNAMIC_TYPE_TRACTOR, id, ownerEntity), - _positionalTarget(glm::vec3(0.0f)), - _desiredPositionalTarget(glm::vec3(0.0f)), + _positionalTarget(0.0f), + _desiredPositionalTarget(0.0f), _linearTimeScale(FLT_MAX), - _positionalTargetSet(true), - _rotationalTarget(glm::quat()), - _desiredRotationalTarget(glm::quat()), + _positionalTargetSet(false), + _rotationalTarget(), + _desiredRotationalTarget(), _angularTimeScale(FLT_MAX), - _rotationalTargetSet(true) { + _rotationalTargetSet(true), + _linearVelocityTarget(0.0f) +{ #if WANT_DEBUG qCDebug(physics) << "ObjectActionTractor::ObjectActionTractor"; #endif @@ -77,7 +79,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { glm::quat rotation; glm::vec3 position; - glm::vec3 linearVelocity; glm::vec3 angularVelocity; bool linearValid = false; @@ -117,7 +118,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { linearValid = true; linearTractorCount++; position += positionForAction; - linearVelocity += linearVelocityForAction; } } } @@ -126,9 +126,18 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { withWriteLock([&]{ if (linearValid && linearTractorCount > 0) { position /= linearTractorCount; - linearVelocity /= linearTractorCount; + if (_positionalTargetSet) { + _lastPositionTarget = _positionalTarget; + } else { + _lastPositionTarget = position; + } _positionalTarget = position; - _linearVelocityTarget = linearVelocity; + if (deltaTimeStep > EPSILON) { + // blend the new velocity with the old (low-pass filter) + glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (position - _lastPositionTarget); + const float blend = 0.25f; + _linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity; + } _positionalTargetSet = true; _active = true; } @@ -169,19 +178,19 @@ void ObjectActionTractor::updateActionWorker(btScalar deltaTimeStep) { } if (_linearTimeScale < MAX_TRACTOR_TIMESCALE) { - btVector3 targetVelocity(0.0f, 0.0f, 0.0f); + btVector3 offsetVelocity(0.0f, 0.0f, 0.0f); btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget); float offsetLength = offset.length(); if (offsetLength > FLT_EPSILON) { float speed = glm::min(offsetLength / _linearTimeScale, TRACTOR_MAX_SPEED); - targetVelocity = (-speed / offsetLength) * offset; + offsetVelocity = (-speed / offsetLength) * offset; if (speed > rigidBody->getLinearSleepingThreshold()) { forceBodyNonStatic(); rigidBody->activate(); } } // this action is aggresively critically damped and defeats the current velocity - rigidBody->setLinearVelocity(targetVelocity); + rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget) + offsetVelocity); } if (_angularTimeScale < MAX_TRACTOR_TIMESCALE) { diff --git a/libraries/physics/src/ObjectActionTractor.h b/libraries/physics/src/ObjectActionTractor.h index c629d84998..a17526f5f9 100644 --- a/libraries/physics/src/ObjectActionTractor.h +++ b/libraries/physics/src/ObjectActionTractor.h @@ -36,6 +36,7 @@ protected: glm::vec3 _positionalTarget; glm::vec3 _desiredPositionalTarget; + glm::vec3 _lastPositionTarget; float _linearTimeScale; bool _positionalTargetSet; diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp new file mode 100644 index 0000000000..f3ceee63f7 --- /dev/null +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -0,0 +1,182 @@ +// +// ConsoleScriptingInterface.cpp +// libraries/script-engine/src +// +// Created by NeetBhagat on 6/1/17. +// Copyright 2014 High Fidelity, Inc. +// +// ConsoleScriptingInterface is responsible for following functionality +// Printing logs with various tags and grouping on debug Window and Logs/log file. +// Debugging functionalities like Timer start-end, assertion, trace. +// To use these functionalities, use "console" object in Javascript files. +// For examples please refer "scripts/developer/tests/consoleObjectTest.js" +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ConsoleScriptingInterface.h" +#include "ScriptEngine.h" + +#define INDENTATION 4 // 1 Tab - 4 spaces +const QString LINE_SEPARATOR = "\n "; +const QString SPACE_SEPARATOR = " "; +const QString STACK_TRACE_FORMAT = "\n[Stacktrace]%1%2"; +QList ConsoleScriptingInterface::_groupDetails = QList(); + +QScriptValue ConsoleScriptingInterface::info(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptInfoMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::log(QScriptContext* context, QScriptEngine* engine) { + QString message = appendArguments(context); + if (_groupDetails.count() == 0) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(message); + } + } else { + logGroupMessage(message, engine); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::debug(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::warn(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptWarningMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::error(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::exception(QScriptContext* context, QScriptEngine* engine) { + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(appendArguments(context)); + } + return QScriptValue::NullValue; +} + +void ConsoleScriptingInterface::time(QString labelName) { + _timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC()); + QString message = QString("%1: Timer started").arg(labelName); + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->scriptPrintedMessage(message); + } +} + +void ConsoleScriptingInterface::timeEnd(QString labelName) { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + if (!_timerDetails.contains(labelName)) { + scriptEngine->scriptErrorMessage("No such label found " + labelName); + return; + } + + if (_timerDetails.value(labelName).isNull()) { + _timerDetails.remove(labelName); + scriptEngine->scriptErrorMessage("Invalid start time for " + labelName); + return; + } + QDateTime _startTime = _timerDetails.value(labelName); + QDateTime _endTime = QDateTime::currentDateTime().toUTC(); + qint64 diffInMS = _startTime.msecsTo(_endTime); + + QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS)); + _timerDetails.remove(labelName); + + scriptEngine->scriptPrintedMessage(message); + } +} + +QScriptValue ConsoleScriptingInterface::assertion(QScriptContext* context, QScriptEngine* engine) { + QString message; + bool condition = false; + for (int i = 0; i < context->argumentCount(); i++) { + if (i == 0) { + condition = context->argument(i).toBool(); // accept first value as condition + } else { + message += SPACE_SEPARATOR + context->argument(i).toString(); // accept other parameters as message + } + } + + QString assertionResult; + if (!condition) { + if (message.isEmpty()) { + assertionResult = "Assertion failed"; + } else { + assertionResult = QString("Assertion failed : %1").arg(message); + } + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptErrorMessage(assertionResult); + } + } + return QScriptValue::NullValue; +} + +void ConsoleScriptingInterface::trace() { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->scriptPrintedMessage + (QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR, + scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR))); + } +} + +void ConsoleScriptingInterface::clear() { + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->clearDebugLogWindow(); + } +} + +QScriptValue ConsoleScriptingInterface::group(QScriptContext* context, QScriptEngine* engine) { + logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0).toString()); + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::groupCollapsed(QScriptContext* context, QScriptEngine* engine) { + logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label + _groupDetails.push_back(context->argument(0).toString()); + return QScriptValue::NullValue; +} + +QScriptValue ConsoleScriptingInterface::groupEnd(QScriptContext* context, QScriptEngine* engine) { + ConsoleScriptingInterface::_groupDetails.removeLast(); + return QScriptValue::NullValue; +} + +QString ConsoleScriptingInterface::appendArguments(QScriptContext* context) { + QString message; + for (int i = 0; i < context->argumentCount(); i++) { + if (i > 0) { + message += SPACE_SEPARATOR; + } + message += context->argument(i).toString(); + } + return message; +} + +void ConsoleScriptingInterface::logGroupMessage(QString message, QScriptEngine* engine) { + int _addSpaces = _groupDetails.count() * INDENTATION; + QString logMessage; + for (int i = 0; i < _addSpaces; i++) { + logMessage.append(SPACE_SEPARATOR); + } + logMessage.append(message); + if (ScriptEngine* scriptEngine = qobject_cast(engine)) { + scriptEngine->scriptPrintedMessage(logMessage); + } +} diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.h b/libraries/script-engine/src/ConsoleScriptingInterface.h new file mode 100644 index 0000000000..444ea93504 --- /dev/null +++ b/libraries/script-engine/src/ConsoleScriptingInterface.h @@ -0,0 +1,56 @@ +// +// ConsoleScriptingInterface.h +// libraries/script-engine/src +// +// Created by NeetBhagat on 6/1/17. +// Copyright 2014 High Fidelity, Inc. +// +// ConsoleScriptingInterface is responsible for following functionality +// Printing logs with various tags and grouping on debug Window and Logs/log file. +// Debugging functionalities like Timer start-end, assertion, trace. +// To use these functionalities, use "console" object in Javascript files. +// For examples please refer "scripts/developer/tests/consoleObjectTest.js" +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_ConsoleScriptingInterface_h +#define hifi_ConsoleScriptingInterface_h + +#include +#include +#include +#include +#include + +// Scriptable interface of "console" object. Used exclusively in the JavaScript API +class ConsoleScriptingInterface : public QObject, protected QScriptable { + Q_OBJECT +public: + static QScriptValue info(QScriptContext* context, QScriptEngine* engine); + static QScriptValue log(QScriptContext* context, QScriptEngine* engine); + static QScriptValue debug(QScriptContext* context, QScriptEngine* engine); + static QScriptValue warn(QScriptContext* context, QScriptEngine* engine); + static QScriptValue error(QScriptContext* context, QScriptEngine* engine); + static QScriptValue exception(QScriptContext* context, QScriptEngine* engine); + static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine); + static QScriptValue group(QScriptContext* context, QScriptEngine* engine); + static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine); + static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine); + +public slots: + void time(QString labelName); + void timeEnd(QString labelName); + void trace(); + void clear(); + +private: + QHash _timerDetails; + static QList _groupDetails; + static void logGroupMessage(QString message, QScriptEngine* engine); + static QString appendArguments(QScriptContext* context); +}; + +#endif // hifi_ConsoleScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 61bf601019..8c64589c3d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -472,20 +472,24 @@ void ScriptEngine::scriptErrorMessage(const QString& message) { } void ScriptEngine::scriptWarningMessage(const QString& message) { - qCWarning(scriptengine) << message; + qCWarning(scriptengine) << qPrintable(message); emit warningMessage(message, getFilename()); } void ScriptEngine::scriptInfoMessage(const QString& message) { - qCInfo(scriptengine) << message; + qCInfo(scriptengine) << qPrintable(message); emit infoMessage(message, getFilename()); } void ScriptEngine::scriptPrintedMessage(const QString& message) { - qCDebug(scriptengine) << message; + qCDebug(scriptengine) << qPrintable(message); emit printedMessage(message, getFilename()); } +void ScriptEngine::clearDebugLogWindow() { + emit clearDebugWindow(); +} + // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... @@ -668,8 +672,18 @@ void ScriptEngine::init() { registerGlobalObject("Mat4", &_mat4Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("Messages", DependencyManager::get().data()); - registerGlobalObject("File", new FileScriptingInterface(this)); + registerGlobalObject("console", &_consoleScriptingInterface); + registerFunction("console", "info", ConsoleScriptingInterface::info, currentContext()->argumentCount()); + registerFunction("console", "log", ConsoleScriptingInterface::log, currentContext()->argumentCount()); + registerFunction("console", "debug", ConsoleScriptingInterface::debug, currentContext()->argumentCount()); + registerFunction("console", "warn", ConsoleScriptingInterface::warn, currentContext()->argumentCount()); + registerFunction("console", "error", ConsoleScriptingInterface::error, currentContext()->argumentCount()); + registerFunction("console", "exception", ConsoleScriptingInterface::exception, currentContext()->argumentCount()); + registerFunction("console", "assert", ConsoleScriptingInterface::assertion, currentContext()->argumentCount()); + registerFunction("console", "group", ConsoleScriptingInterface::group, 1); + registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); + registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 010cdfbc75..9da8603814 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -41,6 +41,7 @@ #include "ScriptCache.h" #include "ScriptUUID.h" #include "Vec3.h" +#include "ConsoleScriptingInterface.h" #include "SettingHandle.h" class QScriptEngineDebugger; @@ -225,7 +226,7 @@ public: void scriptWarningMessage(const QString& message); void scriptInfoMessage(const QString& message); void scriptPrintedMessage(const QString& message); - + void clearDebugLogWindow(); int getNumRunningEntityScripts() const; bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; @@ -245,6 +246,7 @@ signals: void warningMessage(const QString& message, const QString& scriptName); void infoMessage(const QString& message, const QString& scriptName); void runningStateChanged(); + void clearDebugWindow(); void loadScript(const QString& scriptName, bool isUserLoaded); void reloadScript(const QString& scriptName, bool isUserLoaded); void doneRunning(); @@ -305,6 +307,7 @@ protected: Vec3 _vec3Library; Mat4 _mat4Library; ScriptUUID _uuidLibrary; + ConsoleScriptingInterface _consoleScriptingInterface; std::atomic _isUserLoaded { false }; bool _isReloading { false }; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index b310e137b7..69de067d10 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -54,6 +54,10 @@ void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptN emit infoMessage(message, scriptName); } +void ScriptEngines::onClearDebugWindow() { + emit clearDebugWindow(); +} + void ScriptEngines::onErrorLoadingScript(const QString& url) { emit errorLoadingScript(url); } diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 5152c3952a..91dc54a0ec 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -79,6 +79,7 @@ signals: void warningMessage(const QString& message, const QString& engineName); void infoMessage(const QString& message, const QString& engineName); void errorLoadingScript(const QString& url); + void clearDebugWindow(); public slots: void onPrintedMessage(const QString& message, const QString& scriptName); @@ -86,6 +87,7 @@ public slots: void onWarningMessage(const QString& message, const QString& scriptName); void onInfoMessage(const QString& message, const QString& scriptName); void onErrorLoadingScript(const QString& url); + void onClearDebugWindow(); protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 58d39448ac..f5bb880957 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -153,6 +153,9 @@ void QmlWindowClass::sendToQml(const QVariant& message) { QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); } +void QmlWindowClass::clearDebugWindow() { + QMetaObject::invokeMethod(asQuickItem(), "clearDebugWindow", Qt::QueuedConnection); +} void QmlWindowClass::emitScriptEvent(const QVariant& scriptMessage) { if (QThread::currentThread() != thread()) { diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 95777718bf..4f604133a5 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -53,6 +53,7 @@ public slots: // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); + void clearDebugWindow(); // QmlWindow content may include WebView requiring EventBridge. void emitScriptEvent(const QVariant& scriptMessage); diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 30a050e667..6dd116089a 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -49,4 +49,8 @@ ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) { sendToLogWindow("INFO", message, scriptFileName); }); -}()); // END LOCAL_SCOPE \ No newline at end of file +ScriptDiscoveryService.clearDebugWindow.connect(function() { + window.clearDebugWindow(); +}); + +}()); diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index 20fa24358d..2370803335 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -40,6 +40,10 @@ Rectangle { } textArea.append(message); } + + function clearWindow() { + textArea.remove(0,textArea.length); + } } diff --git a/scripts/developer/tests/consoleObjectTest.js b/scripts/developer/tests/consoleObjectTest.js new file mode 100644 index 0000000000..a59652dc09 --- /dev/null +++ b/scripts/developer/tests/consoleObjectTest.js @@ -0,0 +1,114 @@ +// Examples and understanding of console object. Include console methods like +// info, log, debug, warn, error, exception, trace, clear, asserts, group, groupCollapsed, groupEnd, time, timeEnd. +// Useful in debugging and exclusively made for JavaScript files. +// To view the logs click on Developer -> script logs [logs on debug window] and for text file logs go to Logs/log file. + +main(); + +function main() { + + var someObject = { str: "Some text", id: 5 }; + var someValue = 5; + + // console.info examples + console.info("[console.info] Hello World."); + console.info(5 + 6); + console.info(someObject.str); + console.info(a = 2 * 6); + console.info(someValue); + console.info('someObject id ' + someObject.id); + console.info('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.log examples + console.log("[console.log] Hello World"); + console.log(5 + 6); + console.log(someObject.str); + console.log(a = 2 * 6); + console.log(someValue); + console.log('someObject id ' + someObject.id); + console.log('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.debug examples + console.debug("[console.debug] Hello World."); + console.debug(5 + 6); + console.debug(someObject.str); + console.debug(a = 2 * 6); + console.debug(someValue); + console.debug('someObject id ' + someObject.id); + console.debug('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.warn examples + console.warn("[console.warn] This is warning message."); + console.warn(5 + 6); + console.warn(someObject.str); + console.warn(a = 2 * 6); + console.warn(someValue); + console.warn('someObject id ' + someObject.id); + console.warn('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6); + + // console.error examples + console.error('An error occurred!'); + console.error('An error occurred! ', 'Value = ', someValue); + console.error('An error occurred! ' + 'Value = ' + someValue); + + // console.exception examples + console.exception('An exception occurred!'); + console.exception('An exception occurred! ', 'Value = ', someValue); + console.exception('An exception occurred! ' + 'Value = ' + someValue); + + // console.trace examples + function fooA() { + function fooB() { + function fooC() { + console.trace(); + } + fooC(); + } + fooB(); + } + fooA(); + + // console.assert() examples + var valA = 1, valB = "1"; + console.assert(valA === valB, "Value A doesn't equal to B"); + console.assert(valA === valB); + console.assert(5 === 5, "5 equals to 5"); + console.assert(5 === 5); + console.assert(5 > 6, "5 is not greater than 6"); + console.assert(5 > 6, "5 is not greater than 6", "This assertion will fail"); + + // console.group() examples. + console.group("Group 1"); + console.log("Sentence 1"); + console.log("Sentence 2"); + console.group("Group 2"); + console.log("Sentence 3"); + console.log("Sentence 4"); + console.groupCollapsed("Group 3"); + console.log("Sentence 5"); + console.log("Sentence 6"); + console.groupEnd(); + console.log("Sentence 7"); + console.groupEnd(); + console.log("Sentence 8"); + console.groupEnd(); + console.log("Sentence 9"); + + // console.time(),console.timeEnd() examples + console.time('MyTimer'); + // Do some process + sleep(1000); + console.timeEnd('MyTimer'); + + // use console.clear() to clean Debug Window logs + // console.clear(); +} + +function sleep(milliseconds) { + var start = new Date().getTime(); + for (var i = 0; i < 1e7; i++) { + if ((new Date().getTime() - start) > milliseconds){ + break; + } + } +} diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a3583e7808..a83d2159bb 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -622,7 +622,7 @@ var toolBar = (function () { })); isActive = active; activeButton.editProperties({isActive: isActive}); - + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); if (!isActive) { @@ -1519,6 +1519,8 @@ function importSVO(importURL) { // entities after they're imported so that they're all the correct distance in front of and with geometric mean // centered on the avatar/camera direction. var deltaPosition = Vec3.ZERO; + var entityPositions = []; + var entityParentIDs = []; var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]); var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; @@ -1534,10 +1536,9 @@ function importSVO(importURL) { var targetPosition = getPositionToCreateEntity(); var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. - var entityPositions = []; for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", - "registrationPoint", "rotation"]); + "registrationPoint", "rotation", "parentID"]); var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, properties.registrationPoint, properties.dimensions, properties.rotation); var delta = Vec3.subtract(adjustedPosition, properties.position); @@ -1546,6 +1547,7 @@ function importSVO(importURL) { deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), deltaPerpendicular); entityPositions[i] = properties.position; + entityParentIDs[i] = properties.parentID; } deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); @@ -1562,9 +1564,11 @@ function importSVO(importURL) { if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { - Entities.editEntity(pastedEntityIDs[i], { - position: Vec3.sum(deltaPosition, entityPositions[i]) - }); + if (Uuid.isNull(entityParentIDs[i])) { + Entities.editEntity(pastedEntityIDs[i], { + position: Vec3.sum(deltaPosition, entityPositions[i]) + }); + } } } }