From 315c26670d57c497b137ce98238932a53e263c58 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 1 Sep 2024 10:12:08 +0200 Subject: [PATCH 01/40] Set version to 0.7.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d4e5aa..892ae06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(scratchcpp-render VERSION 0.6.0 LANGUAGES CXX) +project(scratchcpp-render VERSION 0.7.0 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) From 7e7c15fada17d54820eb916fc04970c6a7c6a12f Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:07:12 +0200 Subject: [PATCH 02/40] Update libscratchcpp to latest master * color is touching color is going to be implemented later --- libscratchcpp | 2 +- src/spritemodel.cpp | 5 +++++ src/spritemodel.h | 1 + src/stagemodel.cpp | 5 +++++ src/stagemodel.h | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index 363bec1..f74e244 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 363bec1b8658a25cb558021f09907bdf3b69f80f +Subproject commit f74e2448f5f174be7c0fcc9d826102a4588c6b47 diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 70e440e..6e4abb4 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -175,6 +175,11 @@ bool SpriteModel::touchingColor(const libscratchcpp::Value &color) const return m_renderedTarget->touchingColor(color); } +bool SpriteModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +{ + return false; +} + libscratchcpp::Sprite *SpriteModel::sprite() const { return m_sprite; diff --git a/src/spritemodel.h b/src/spritemodel.h index 52e2076..c7a1354 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -63,6 +63,7 @@ class SpriteModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; bool touchingColor(const libscratchcpp::Value &color) const override; + bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; libscratchcpp::Sprite *sprite() const; diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 3290424..67245ec 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -112,6 +112,11 @@ bool StageModel::touchingColor(const libscratchcpp::Value &color) const return m_renderedTarget->touchingColor(color); } +bool StageModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +{ + return false; +} + void StageModel::loadCostume() { if (m_renderedTarget && m_stage) diff --git a/src/stagemodel.h b/src/stagemodel.h index b5423e8..326face 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -49,6 +49,7 @@ class StageModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; bool touchingColor(const libscratchcpp::Value &color) const override; + bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; Q_INVOKABLE void loadCostume(); From f3d5f1a9f6cd0ae2e989934796b47904b744b88c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:18:40 +0200 Subject: [PATCH 03/40] fix #136: Show loading screen in load() --- src/ProjectPlayer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 2355484..7baffa5 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -28,11 +28,11 @@ ProjectScene { id: root engine: loader.engine stageScale: (stageWidth == 0 || stageHeight == 0) ? 1 : Math.min(width / stageWidth, height / stageHeight) - onFileNameChanged: priv.loading = true; onLoaded: priv.loaded = true onFailedToLoad: priv.loaded = false function load(fileName) { + priv.loading = true; loader.fileName = fileName; } From 50f84b6a95e22151feda924f4a5341f5afed910c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:23:49 +0200 Subject: [PATCH 04/40] Update EngineMock --- test/mocks/enginemock.h | 1 + 1 file changed, 1 insertion(+) diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 2d9aaa7..a974ac5 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -120,6 +120,7 @@ class EngineMock : public IEngine MOCK_METHOD(const std::vector> &, targets, (), (const, override)); MOCK_METHOD(void, setTargets, (const std::vector> &), (override)); MOCK_METHOD(Target *, targetAt, (int), (const, override)); + MOCK_METHOD(void, getVisibleTargets, (std::vector &), (const, override)); MOCK_METHOD(int, findTarget, (const std::string &), (const, override)); MOCK_METHOD(void, moveSpriteToFront, (Sprite * sprite), (override)); From 2357cd4c2277283d4c1fde07dd1501642ffa6598 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:51:35 +0200 Subject: [PATCH 05/40] RenderedTarget: Use IEngine::getVisibleTargets() --- src/renderedtarget.cpp | 37 +++------------------ src/renderedtarget.h | 1 - test/renderedtarget/renderedtarget_test.cpp | 28 +++++++++++++++- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 1f20b54..2550bf4 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -678,10 +678,13 @@ bool RenderedTarget::touchingClones(const std::vector & bool RenderedTarget::touchingColor(const Value &color) const { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 + if (!m_engine) + return false; + QRgb rgb = convertColor(color); std::vector targets; - getVisibleTargets(targets); + m_engine->getVisibleTargets(targets); QRectF myRect = touchingBounds(); std::vector candidates; @@ -870,38 +873,6 @@ CpuTextureManager *RenderedTarget::textureManager() const return m_textureManager.get(); } -void RenderedTarget::getVisibleTargets(std::vector &dst) const -{ - dst.clear(); - - if (!m_engine) - return; - - const auto &targets = m_engine->targets(); - - for (auto target : targets) { - Q_ASSERT(target); - - if (target->isStage()) - dst.push_back(target.get()); - else { - Sprite *sprite = static_cast(target.get()); - - if (sprite->visible()) - dst.push_back(target.get()); - - const auto &clones = sprite->clones(); - - for (auto clone : clones) { - if (clone->visible()) - dst.push_back(clone.get()); - } - } - } - - std::sort(dst.begin(), dst.end(), [](Target *t1, Target *t2) { return t1->layerOrder() > t2->layerOrder(); }); -} - QRectF RenderedTarget::touchingBounds() const { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L1330-L1350 diff --git a/src/renderedtarget.h b/src/renderedtarget.h index e752e1c..692aebd 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -130,7 +130,6 @@ class RenderedTarget : public IRenderedTarget QPointF mapFromStageWithOriginPoint(const QPointF &scenePoint) const; QPointF mapFromScratchToLocal(const QPointF &point) const; CpuTextureManager *textureManager() const; - void getVisibleTargets(std::vector &dst) const; QRectF touchingBounds() const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 850600e..484bdd7 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -23,6 +23,8 @@ using namespace libscratchcpp; using ::testing::Return; using ::testing::ReturnRef; +using ::testing::Invoke; +using ::testing::_; class RenderedTargetTest : public testing::Test { @@ -1093,6 +1095,31 @@ TEST_F(RenderedTargetTest, TouchingColor) sprite2->setLayerOrder(3); const std::vector> targets = { stage, sprite, sprite1 }; + EXPECT_CALL(engine, getVisibleTargets(_)).WillRepeatedly(Invoke([&targets](std::vector &dst) { + dst.clear(); + + for (auto target : targets) { + ASSERT_TRUE(target); + + if (target->isStage()) + dst.push_back(target.get()); + else { + Sprite *sprite = static_cast(target.get()); + + if (sprite->visible()) + dst.push_back(target.get()); + + const auto &clones = sprite->clones(); + + for (auto clone : clones) { + if (clone->visible()) + dst.push_back(clone.get()); + } + } + } + + std::sort(dst.begin(), dst.end(), [](Target *t1, Target *t2) { return t1->layerOrder() > t2->layerOrder(); }); + })); QQuickItem parent; parent.setWidth(480); @@ -1128,7 +1155,6 @@ TEST_F(RenderedTargetTest, TouchingColor) target.beforeRedraw(); Rect penBounds(5, 1, 6, -5); - EXPECT_CALL(engine, targets()).WillRepeatedly(ReturnRef(targets)); EXPECT_CALL(stageTarget, stageModel()).WillRepeatedly(Return(&stageModel)); EXPECT_CALL(target1, stageModel()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(target2, stageModel()).WillRepeatedly(Return(nullptr)); From e129996bfde79bdc8bbc80ee5809b66ed86dfd06 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Sep 2024 20:35:00 +0200 Subject: [PATCH 06/40] Add getUniformValuesForEffects() method to ShaderManager --- src/shadermanager.cpp | 21 +++++++++++++++----- src/shadermanager.h | 1 + test/shadermanager/shadermanager_test.cpp | 24 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/shadermanager.cpp b/src/shadermanager.cpp index a87f9b7..928e32f 100644 --- a/src/shadermanager.cpp +++ b/src/shadermanager.cpp @@ -101,12 +101,10 @@ QOpenGLShaderProgram *ShaderManager::getShaderProgram(const std::unordered_mapsecond; } -void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues) +void ShaderManager::getUniformValuesForEffects(const std::unordered_map &effectValues, std::unordered_map &dst) { - // Set the texture unit - program->setUniformValue(TEXTURE_UNIT_UNIFORM, textureUnit); + dst.clear(); - // Set the uniform values for the enabled effects and reset the other effects for (const auto &[effect, name] : EFFECT_TO_NAME) { const auto it = effectValues.find(effect); double value; @@ -117,10 +115,23 @@ void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, value = it->second; auto converter = EFFECT_CONVERTER.at(effect); - program->setUniformValue(EFFECT_UNIFORM_NAME.at(effect), converter(value)); + dst[effect] = converter(value); } } +void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues) +{ + // Set the texture unit + program->setUniformValue(TEXTURE_UNIT_UNIFORM, textureUnit); + + // Set uniform values + std::unordered_map values; + getUniformValuesForEffects(effectValues, values); + + for (const auto &[effect, value] : values) + program->setUniformValue(EFFECT_UNIFORM_NAME.at(effect), value); +} + void ShaderManager::registerEffects() { // Register graphic effects in libscratchcpp diff --git a/src/shadermanager.h b/src/shadermanager.h index dde8245..edcb614 100644 --- a/src/shadermanager.h +++ b/src/shadermanager.h @@ -26,6 +26,7 @@ class ShaderManager : public QObject static ShaderManager *instance(); QOpenGLShaderProgram *getShaderProgram(const std::unordered_map &effectValues); + static void getUniformValuesForEffects(const std::unordered_map &effectValues, std::unordered_map &dst); void setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues); private: diff --git a/test/shadermanager/shadermanager_test.cpp b/test/shadermanager/shadermanager_test.cpp index ad6a68d..6bbe0a2 100644 --- a/test/shadermanager/shadermanager_test.cpp +++ b/test/shadermanager/shadermanager_test.cpp @@ -179,6 +179,8 @@ TEST_F(ShaderManagerTest, ColorEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Color; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -190,28 +192,34 @@ TEST_F(ShaderManagerTest, ColorEffectValue) std::unordered_map effects = { { effect, 64.9 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.3245f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -395.7; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 100.0f) / 100.0f, 0.02f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 579.05; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 100.0f) / 100.0f, 0.9f); + ASSERT_EQ(values.at(effect), value); program.release(); } @@ -222,6 +230,8 @@ TEST_F(ShaderManagerTest, BrightnessEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Brightness; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -233,28 +243,34 @@ TEST_F(ShaderManagerTest, BrightnessEffectValue) std::unordered_map effects = { { effect, 4.6 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.046f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -102.9; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, -1.0f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 353.2; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 1.0f); + ASSERT_EQ(values.at(effect), value); program.release(); } @@ -265,6 +281,8 @@ TEST_F(ShaderManagerTest, GhostEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Ghost; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -276,28 +294,34 @@ TEST_F(ShaderManagerTest, GhostEffectValue) std::unordered_map effects = { { effect, 58.5 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 1000.0f) / 1000.0f, 0.415f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -20.8; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 1.0f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 248.2; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.0f); + ASSERT_EQ(values.at(effect), value); program.release(); } From bf71a0eff0dc1f34a6a9a471819ddd097e3accc4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Sep 2024 20:35:45 +0200 Subject: [PATCH 07/40] Add EffectTransform class --- src/CMakeLists.txt | 2 + src/effecttransform.cpp | 106 +++++++++++++++ src/effecttransform.h | 21 +++ test/CMakeLists.txt | 1 + test/effecttransform/CMakeLists.txt | 14 ++ test/effecttransform/effecttransform_test.cpp | 123 ++++++++++++++++++ 6 files changed, 267 insertions(+) create mode 100644 src/effecttransform.cpp create mode 100644 src/effecttransform.h create mode 100644 test/effecttransform/CMakeLists.txt create mode 100644 test/effecttransform/effecttransform_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdf033a..0b2eb1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,8 @@ qt_add_qml_module(scratchcpp-render textbubblepainter.h cputexturemanager.cpp cputexturemanager.h + effecttransform.cpp + effecttransform.h blocks/penextension.cpp blocks/penextension.h blocks/penblocks.cpp diff --git a/src/effecttransform.cpp b/src/effecttransform.cpp new file mode 100644 index 0000000..4ea1b70 --- /dev/null +++ b/src/effecttransform.cpp @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "effecttransform.h" + +using namespace scratchcpprender; + +QRgb EffectTransform::transformColor(const std::unordered_map &effectValues, QRgb color) +{ + // https://github.com/scratchfoundation/scratch-render/blob/e075e5f5ebc95dec4a2718551624ad587c56f0a6/src/EffectTransform.js#L40-L119 + // If the color is fully transparent, don't bother attempting any transformations. + if (qAlpha(color) == 0) + return color; + + QColor inOutColor = QColor::fromRgba(color); + + std::unordered_map uniforms; + ShaderManager::getUniformValuesForEffects(effectValues, uniforms); + + const bool enableColor = uniforms[ShaderManager::Effect::Color] != 0; + const bool enableBrightness = uniforms[ShaderManager::Effect::Brightness] != 0; + + if (enableColor || enableBrightness) { + // gl_FragColor.rgb /= gl_FragColor.a + epsilon; + // Here, we're dividing by the (previously pre-multiplied) alpha to ensure HSV is properly calculated + // for partially transparent pixels. + const float alpha = inOutColor.alphaF(); + + if (alpha == 0) { + inOutColor.setRed(255); + inOutColor.setGreen(255); + inOutColor.setBlue(255); + } else { + inOutColor.setRedF(inOutColor.redF() / alpha); + inOutColor.setGreenF(inOutColor.greenF() / alpha); + inOutColor.setBlueF(inOutColor.blueF() / alpha); + } + + if (enableColor) { + // vec3 hsv = convertRGB2HSV(gl_FragColor.xyz); + QColor hsv = inOutColor.toHsv(); + + // this code forces grayscale values to be slightly saturated + // so that some slight change of hue will be visible + // const float minLightness = 0.11 / 2.0; + const float minV = 0.11f / 2.0f; + // const float minSaturation = 0.09; + const float minS = 0.09f; + // if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness); + if (hsv.valueF() < minV) { + hsv.setHsvF(0.0f, 1.0f, minV); + // else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z); + } else if (hsv.saturationF() < minS) { + hsv.setHsvF(0.0f, minS, hsv.valueF()); + } + + // hsv.x = mod(hsv.x + u_color, 1.0); + // if (hsv.x < 0.0) hsv.x += 1.0; + float hue = std::fmod(uniforms[ShaderManager::Effect::Color] + hsv.hueF(), 1.0f); + + if (hue < 0.0f) + hue += 1.0f; + + hsv.setHsvF(hue, hsv.saturationF(), hsv.valueF()); + + // gl_FragColor.rgb = convertHSV2RGB(hsl); + inOutColor = hsv.toRgb(); + } + + if (enableBrightness) { + const float brightness = uniforms[ShaderManager::Effect::Brightness] * 255.0f; + // gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); + inOutColor.setRed(std::clamp(inOutColor.red() + brightness, 0.0f, 255.0f)); + inOutColor.setGreen(std::clamp(inOutColor.green() + brightness, 0.0f, 255.0f)); + inOutColor.setBlue(std::clamp(inOutColor.blue() + brightness, 0.0f, 255.0f)); + } + + // gl_FragColor.rgb *= gl_FragColor.a + epsilon; + // Now we're doing the reverse, premultiplying by the alpha once again. + inOutColor.setRedF(inOutColor.redF() * alpha); + inOutColor.setGreenF(inOutColor.greenF() * alpha); + inOutColor.setBlueF(inOutColor.blueF() * alpha); + + // Restore alpha + inOutColor.setAlphaF(alpha); + } + + const float ghost = uniforms[ShaderManager::Effect::Ghost]; + + if (ghost != 1) { + // gl_FragColor *= u_ghost + inOutColor.setRedF(inOutColor.redF() * ghost); + inOutColor.setGreenF(inOutColor.greenF() * ghost); + inOutColor.setBlueF(inOutColor.blueF() * ghost); + inOutColor.setAlphaF(inOutColor.alphaF() * ghost); + } + + return inOutColor.rgba(); +} + +void EffectTransform::transformPoint(const std::unordered_map &effectValues, const QVector2D &vec, QVector2D &dst) +{ + // TODO: Implement remaining effects + dst = vec; +} diff --git a/src/effecttransform.h b/src/effecttransform.h new file mode 100644 index 0000000..6992f25 --- /dev/null +++ b/src/effecttransform.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include "shadermanager.h" + +namespace scratchcpprender +{ + +class EffectTransform +{ + public: + EffectTransform() = delete; + + static QRgb transformColor(const std::unordered_map &effectValues, QRgb color); + static void transformPoint(const std::unordered_map &effectValues, const QVector2D &vec, QVector2D &dst); +}; + +} // namespace scratchcpprender diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dcd6bef..0159df6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,3 +41,4 @@ add_subdirectory(graphicseffect) add_subdirectory(shadermanager) add_subdirectory(textbubbleshape) add_subdirectory(textbubblepainter) +add_subdirectory(effecttransform) diff --git a/test/effecttransform/CMakeLists.txt b/test/effecttransform/CMakeLists.txt new file mode 100644 index 0000000..89e9206 --- /dev/null +++ b/test/effecttransform/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable( + effecttransform_test + effecttransform_test.cpp +) + +target_link_libraries( + effecttransform_test + GTest::gtest_main + scratchcpp-render + qnanopainter +) + +add_test(effecttransform_test) +gtest_discover_tests(effecttransform_test) diff --git a/test/effecttransform/effecttransform_test.cpp b/test/effecttransform/effecttransform_test.cpp new file mode 100644 index 0000000..d5f92fa --- /dev/null +++ b/test/effecttransform/effecttransform_test.cpp @@ -0,0 +1,123 @@ +#include +#include + +#include "../common.h" + +using namespace scratchcpprender; + +class EffectTransformTest : public testing::Test +{ + public: + void SetUp() override { } + + std::unordered_map m_effects; +}; + +TEST_F(EffectTransformTest, NoEffect) +{ + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(0, 255, 255, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 255, 255, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + QVector2D dst; + EffectTransform::transformPoint(m_effects, QVector2D(0.5, -0.3), dst); + ASSERT_EQ(dst, QVector2D(0.5, -0.3)); +} + +TEST_F(EffectTransformTest, ColorEffect) +{ + // 100 + m_effects[ShaderManager::Effect::Color] = 100; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(0, 255, 255)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 100, 100, 128)); + + // 175 + m_effects[ShaderManager::Effect::Color] = 175; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 0, 191)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(100, 128, 107, 128)); +} + +TEST_F(EffectTransformTest, BrightnessEffect) +{ + // -100 + m_effects[ShaderManager::Effect::Brightness] = -100; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(0, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 128)); + + // -50 + m_effects[ShaderManager::Effect::Brightness] = -50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(127, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(36, 64, 64, 128)); + + // 50 + m_effects[ShaderManager::Effect::Brightness] = 50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 127, 127)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 128, 128, 128)); + + // 100 + m_effects[ShaderManager::Effect::Brightness] = 100; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 255, 255)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 128, 128, 128)); +} + +TEST_F(EffectTransformTest, GhostEffect) +{ + // 25 + m_effects[ShaderManager::Effect::Ghost] = 25; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(191, 0, 0, 191)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(75, 191, 150, 96)); + + // 50 + m_effects[ShaderManager::Effect::Ghost] = 50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 0, 0, 128)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(50, 128, 100, 64)); + + // 100 + m_effects[ShaderManager::Effect::Ghost] = 100; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 0)); +} From 449d736725c35e0a10d3fa01a6d852234c9b5332 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:23:55 +0200 Subject: [PATCH 08/40] CpuTextureManager: Refactor point color methods --- src/cputexturemanager.cpp | 40 ++++++++--- src/cputexturemanager.h | 6 +- src/renderedtarget.cpp | 2 +- test/texture/cputexturemanager_test.cpp | 91 +++++++++++++++++++------ 4 files changed, 105 insertions(+), 34 deletions(-) diff --git a/src/cputexturemanager.cpp b/src/cputexturemanager.cpp index c97b37d..f8daf67 100644 --- a/src/cputexturemanager.cpp +++ b/src/cputexturemanager.cpp @@ -2,6 +2,7 @@ #include "cputexturemanager.h" #include "texture.h" +#include "effecttransform.h" using namespace scratchcpprender; @@ -51,10 +52,36 @@ const std::vector &CpuTextureManager::getTextureConvexHullPoints(const T return it->second; } -bool CpuTextureManager::textureContainsPoint(const Texture &texture, const QPointF &localPoint) +QRgb CpuTextureManager::getPointColor(const Texture &texture, int x, int y, const std::unordered_map &effects) +{ + const int width = texture.width(); + const int height = texture.height(); + + if (!effects.empty()) { + // Get local position with effect transform + QVector2D transformedCoords; + const QVector2D localCoords(x / static_cast(width), y / static_cast(height)); + EffectTransform::transformPoint(effects, localCoords, transformedCoords); + x = transformedCoords.x() * width; + y = transformedCoords.y() * height; + } + + if ((x < 0 || x >= width) || (y < 0 || y >= height)) + return qRgba(0, 0, 0, 0); + + GLubyte *pixels = getTextureData(texture); + QRgb color = qRgba(pixels[(y * width + x) * 4], pixels[(y * width + x) * 4 + 1], pixels[(y * width + x) * 4 + 2], pixels[(y * width + x) * 4 + 3]); + + if (effects.empty()) + return color; + else + return EffectTransform::transformColor(effects, color); +} + +bool CpuTextureManager::textureContainsPoint(const Texture &texture, const QPointF &localPoint, const std::unordered_map &effects) { // https://github.com/scratchfoundation/scratch-render/blob/7b823985bc6fe92f572cc3276a8915e550f7c5e6/src/Silhouette.js#L219-L226 - return getPointAlpha(texture, localPoint.x(), localPoint.y()) > 0; + return qAlpha(getPointColor(texture, localPoint.x(), localPoint.y(), effects)) > 0; } void CpuTextureManager::removeTexture(const Texture &texture) @@ -137,12 +164,3 @@ bool CpuTextureManager::addTexture(const Texture &texture) return true; } - -int CpuTextureManager::getPointAlpha(const Texture &texture, int x, int y) -{ - if ((x < 0 || x >= texture.width()) || (y < 0 || y >= texture.height())) - return 0; - - GLubyte *pixels = getTextureData(texture); - return pixels[(y * texture.width() + x) * 4 + 3]; -} diff --git a/src/cputexturemanager.h b/src/cputexturemanager.h index df19588..4abdb5a 100644 --- a/src/cputexturemanager.h +++ b/src/cputexturemanager.h @@ -6,6 +6,8 @@ #include #include +#include "shadermanager.h" + namespace scratchcpprender { @@ -20,13 +22,13 @@ class CpuTextureManager GLubyte *getTextureData(const Texture &texture); const std::vector &getTextureConvexHullPoints(const Texture &texture); - bool textureContainsPoint(const Texture &texture, const QPointF &localPoint); + QRgb getPointColor(const Texture &texture, int x, int y, const std::unordered_map &effects); + bool textureContainsPoint(const Texture &texture, const QPointF &localPoint, const std::unordered_map &effects); void removeTexture(const Texture &texture); private: bool addTexture(const Texture &texture); - int getPointAlpha(const Texture &texture, int x, int y); std::unordered_map m_textureData; std::unordered_map> m_convexHullPoints; diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 2550bf4..267a80c 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -817,7 +817,7 @@ void RenderedTarget::updateHullPoints() bool RenderedTarget::containsLocalPoint(const QPointF &point) const { - return textureManager()->textureContainsPoint(m_cpuTexture, point); + return textureManager()->textureContainsPoint(m_cpuTexture, point, m_graphicEffects); } QPointF RenderedTarget::transformPoint(double scratchX, double scratchY, double originX, double originY, double rot) const diff --git a/test/texture/cputexturemanager_test.cpp b/test/texture/cputexturemanager_test.cpp index 2d0ec05..436eb32 100644 --- a/test/texture/cputexturemanager_test.cpp +++ b/test/texture/cputexturemanager_test.cpp @@ -129,6 +129,49 @@ TEST_F(CpuTextureManagerTest, TextureDataAndHullPoints) context.doneCurrent(); } +TEST_F(CpuTextureManagerTest, GetPointColor) +{ + // Create OpenGL context + QOpenGLContext context; + QOffscreenSurface surface; + createContextAndSurface(&context, &surface); + + // Paint + QNanoPainter painter; + ImagePainter imgPainter(&painter, "image.png"); + + // Read texture data + Texture texture(imgPainter.fbo()->texture(), imgPainter.fbo()->size()); + + // Test + CpuTextureManager manager; + ASSERT_EQ(manager.getPointColor(texture, 0, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 1, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 2, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 3, 0, {}), qRgba(0, 0, 0, 0)); + + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 }, {})); + + ASSERT_EQ(manager.getPointColor(texture, 0, 1, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 1, 1, {}), qRgb(0, 0, 255)); + ASSERT_EQ(manager.getPointColor(texture, 2, 1, {}), qRgb(255, 0, 255)); + ASSERT_EQ(manager.getPointColor(texture, 3, 1, {}), qRgb(255, 128, 128)); + + std::unordered_map effects = { { ShaderManager::Effect::Color, 50 } }; + ASSERT_EQ(manager.getPointColor(texture, 1, 1, effects), qRgb(255, 0, 128)); + ASSERT_EQ(manager.getPointColor(texture, 2, 1, effects), qRgb(255, 128, 0)); + ASSERT_EQ(manager.getPointColor(texture, 3, 1, effects), qRgb(192, 255, 128)); + + // TODO: Test point transform (graphic effects that change shape) + + // Cleanup + context.doneCurrent(); +} + TEST_F(CpuTextureManagerTest, TextureContainsPoint) { // Create OpenGL context @@ -145,26 +188,34 @@ TEST_F(CpuTextureManagerTest, TextureContainsPoint) // Test CpuTextureManager manager; - ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 0 })); - - ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 })); - - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 2 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 2 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 2 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.5, 2.1 })); - - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.3, 3.5 })); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 0 }, {})); + + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 }, {})); + + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 2 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 2 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 2 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.5, 2.1 }, {})); + + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, {})); + + std::unordered_map effects = { { ShaderManager::Effect::Ghost, 100 } }; + ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, effects)); + + // TODO: Test point transform (graphic effects that change shape) // Cleanup context.doneCurrent(); From 125bb06a274ec60324d0529a3e38369afdccf3f5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:37:39 +0200 Subject: [PATCH 09/40] RenderedTarget: Use CpuTextureManager::getPointColor() to get color --- src/renderedtarget.cpp | 6 +----- test/renderedtarget/renderedtarget_test.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 267a80c..d73e797 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -636,11 +636,7 @@ QRgb RenderedTarget::colorAtScratchPoint(double x, double y) const if ((x < 0 || x >= width) || (y < 0 || y >= height)) return qRgba(0, 0, 0, 0); - GLubyte *data = textureManager()->getTextureData(m_cpuTexture); - const int index = (y * width + x) * 4; // RGBA channels - Q_ASSERT(index >= 0 && index < width * height * 4); - // TODO: Apply graphic effects (#117) - return qRgba(data[index], data[index + 1], data[index + 2], data[index + 3]); + return textureManager()->getPointColor(m_cpuTexture, x, y, m_graphicEffects); } bool RenderedTarget::touchingClones(const std::vector &clones) const diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 484bdd7..d8883d5 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -459,6 +459,15 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_EQ(target.colorAtScratchPoint(-225, 162), 4286611456); // [3, 3] ASSERT_EQ(target.colorAtScratchPoint(-224.7, 161.5), 4286611456); // [3.3, 3.5] + target.setGraphicEffect(ShaderManager::Effect::Color, 50); + ASSERT_EQ(target.colorAtScratchPoint(-227, 162), 4286595072); // [1, 3] + ASSERT_EQ(target.colorAtScratchPoint(-226, 162), 4294934720); // [2, 3] + ASSERT_EQ(target.colorAtScratchPoint(-225, 162), 4278222912); // [3, 3] + ASSERT_EQ(target.colorAtScratchPoint(-224.7, 161.5), 4278222912); // [3.3, 3.5] + target.setGraphicEffect(ShaderManager::Effect::Color, 0); + + // TODO: Test point transform (graphic effects that change shape) + // Cleanup context.doneCurrent(); } From b000c61af9536715c4b7a228bef104266992592d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:45:51 +0200 Subject: [PATCH 10/40] fix #141: Ignore invisible sprites in contains point methods --- src/renderedtarget.cpp | 4 ++-- test/renderedtarget/renderedtarget_test.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 2550bf4..33132e6 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -599,7 +599,7 @@ const std::vector &RenderedTarget::hullPoints() const bool RenderedTarget::contains(const QPointF &point) const { - if (!m_costume || !m_texture.isValid() || !m_cpuTexture.isValid() || !parentItem()) + if (!isVisible() || !m_costume || !m_texture.isValid() || !m_cpuTexture.isValid() || !parentItem()) return false; const double scaleRatio = m_skin->getTextureScale(m_texture) / m_skin->getTextureScale(m_cpuTexture); @@ -612,7 +612,7 @@ bool RenderedTarget::contains(const QPointF &point) const bool RenderedTarget::containsScratchPoint(double x, double y) const { - if (!m_engine || !m_skin || !m_costume) + if (!isVisible() || !m_engine || !m_skin || !m_costume) return false; return containsLocalPoint(mapFromScratchToLocal(QPointF(x, y))); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 484bdd7..87ef277 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -403,6 +403,12 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_TRUE(target.contains({ 3, 3 })); ASSERT_TRUE(target.contains({ 3.3, 3.5 })); + // Regtest for #141 + target.setVisible(false); + ASSERT_FALSE(target.contains({ 1, 3 })); + ASSERT_FALSE(target.contains({ 2, 3 })); + target.setVisible(true); + // Test contains() with horizontal mirroring target.updateRotationStyle(Sprite::RotationStyle::LeftRight); target.updateDirection(-45); @@ -437,6 +443,12 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_TRUE(target.containsScratchPoint(-225, 162)); // [3, 3] ASSERT_TRUE(target.containsScratchPoint(-224.7, 161.5)); // [3.3, 3.5] + // Regtest for #141 + target.setVisible(false); + ASSERT_FALSE(target.containsScratchPoint(-227, 162)); // [1, 3] + ASSERT_FALSE(target.containsScratchPoint(-226, 162)); // [2, 3] + target.setVisible(true); + // Test colorAtScratchPoint() ASSERT_EQ(target.colorAtScratchPoint(-228, 165), 0); // [0, 0] ASSERT_EQ(target.colorAtScratchPoint(-227, 165), 0); // [1, 0] From 64ad83dff75e6fa3554ff953ddb3ffab0a15954c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:49:16 +0200 Subject: [PATCH 11/40] RenderedTarget: Cache transformed hull points Resolves: #118 --- src/renderedtarget.cpp | 63 ++++++++++++++++++++++++++++++------------ src/renderedtarget.h | 5 +++- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index bb5d2b5..4044e02 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -371,31 +371,16 @@ void RenderedTarget::setHeight(qreal height) Rect RenderedTarget::getBounds() const { // https://github.com/scratchfoundation/scratch-render/blob/c3ede9c3d54769730c7b023021511e2aba167b1f/src/Rectangle.js#L33-L55 - if (!m_costume || !m_skin || !m_texture.isValid() || !m_cpuTexture.isValid()) - return Rect(m_x, m_y, m_x, m_y); - - const double textureScale = m_skin->getTextureScale(m_cpuTexture); - const double bitmapRes = m_costume->bitmapResolution(); - const double width = m_cpuTexture.width() * m_size / textureScale; - const double height = m_cpuTexture.height() * m_size / textureScale; - const double originX = m_costume->rotationCenterX() * m_size / bitmapRes - width / 2; - const double originY = -m_costume->rotationCenterY() * m_size / bitmapRes + height / 2; - const double rot = -rotation() * pi / 180; - const double sinRot = std::sin(rot); - const double cosRot = std::cos(rot); double left = std::numeric_limits::infinity(); double top = -std::numeric_limits::infinity(); double right = -std::numeric_limits::infinity(); double bottom = std::numeric_limits::infinity(); - const std::vector &points = hullPoints(); + const std::vector &points = transformedHullPoints(); for (const QPointF &point : points) { - double x = point.x() * m_size / textureScale / bitmapRes - width / 2; - double y = height / 2 - point.y() * m_size / textureScale / bitmapRes; - const QPointF transformed = transformPoint(x, y, originX, originY, sinRot, cosRot); - x = transformed.x() * (m_mirrorHorizontally ? -1 : 1); - y = transformed.y(); + const double x = point.x(); + const double y = point.y(); if (x < left) left = x; @@ -733,6 +718,8 @@ void RenderedTarget::calculatePos() else setTransformOrigin(QQuickItem::Center); } + + m_transformedHullDirty = true; } void RenderedTarget::calculateRotation() @@ -764,6 +751,8 @@ void RenderedTarget::calculateRotation() if (m_mirrorHorizontally != oldMirrorHorizontally) emit mirrorHorizontallyChanged(); } + + m_transformedHullDirty = true; } void RenderedTarget::calculateSize() @@ -779,6 +768,8 @@ void RenderedTarget::calculateSize() if (wasValid && m_cpuTexture.handle() != oldTexture) m_convexHullDirty = true; + + m_transformedHullDirty = true; } } @@ -811,6 +802,42 @@ void RenderedTarget::updateHullPoints() // TODO: Apply graphic effects (#117) } +const std::vector &RenderedTarget::transformedHullPoints() const +{ + // https://github.com/scratchfoundation/scratch-render/blob/9fe90e8f4c2da35d4684359f84b69c264d884133/src/Drawable.js#L594-L616 + if (!m_transformedHullDirty) + return m_transformedHullPoints; + + m_transformedHullPoints.clear(); + + if (!m_costume || !m_skin || !m_texture.isValid() || !m_cpuTexture.isValid()) + return m_transformedHullPoints; + + const double textureScale = m_skin->getTextureScale(m_cpuTexture); + const double bitmapRes = m_costume->bitmapResolution(); + const double width = m_cpuTexture.width() * m_size / textureScale; + const double height = m_cpuTexture.height() * m_size / textureScale; + const double originX = m_costume->rotationCenterX() * m_size / bitmapRes - width / 2; + const double originY = -m_costume->rotationCenterY() * m_size / bitmapRes + height / 2; + const double rot = -rotation() * pi / 180; + const double sinRot = std::sin(rot); + const double cosRot = std::cos(rot); + + const std::vector &points = hullPoints(); + m_transformedHullPoints.reserve(points.size()); + + for (const QPoint &point : points) { + double x = point.x() * m_size / textureScale / bitmapRes - width / 2; + double y = height / 2 - point.y() * m_size / textureScale / bitmapRes; + const QPointF transformed = transformPoint(x, y, originX, originY, sinRot, cosRot); + x = transformed.x() * (m_mirrorHorizontally ? -1 : 1); + y = transformed.y(); + m_transformedHullPoints.push_back(QPointF(x, y)); + } + + return m_transformedHullPoints; +} + bool RenderedTarget::containsLocalPoint(const QPointF &point) const { return textureManager()->textureContainsPoint(m_cpuTexture, point, m_graphicEffects); diff --git a/src/renderedtarget.h b/src/renderedtarget.h index 692aebd..a69494b 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -124,6 +124,7 @@ class RenderedTarget : public IRenderedTarget void handleSceneMouseMove(qreal x, qreal y); bool convexHullPointsNeeded() const; void updateHullPoints(); + const std::vector &transformedHullPoints() const; bool containsLocalPoint(const QPointF &point) const; QPointF transformPoint(double scratchX, double scratchY, double originX, double originY, double rot) const; QPointF transformPoint(double scratchX, double scratchY, double originX, double originY, double sinRot, double cosRot) const; @@ -169,7 +170,9 @@ class RenderedTarget : public IRenderedTarget qreal m_maximumHeight = std::numeric_limits::infinity(); bool m_convexHullDirty = true; std::vector m_hullPoints; - bool m_clicked = false; // left mouse button only! + bool m_transformedHullDirty = true; + mutable std::vector m_transformedHullPoints; // NOTE: Use transformedHullPoints(); + bool m_clicked = false; // left mouse button only! double m_dragX = 0; double m_dragY = 0; double m_dragDeltaX = 0; From c1468b1baa8fae591e52a85a7202c877ef9f1e15 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:39:43 +0200 Subject: [PATCH 12/40] Update libscratchcpp to latest master --- libscratchcpp | 2 +- test/mocks/enginemock.h | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libscratchcpp b/libscratchcpp index f74e244..537ff52 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit f74e2448f5f174be7c0fcc9d826102a4588c6b47 +Subproject commit 537ff52a7500d17d07df7ea31c7023358824e344 diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index a974ac5..48032d4 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -19,9 +19,9 @@ class EngineMock : public IEngine MOCK_METHOD(void, start, (), (override)); MOCK_METHOD(void, stop, (), (override)); MOCK_METHOD(VirtualMachine *, startScript, (std::shared_ptr, Target *), (override)); - MOCK_METHOD(void, broadcast, (int), (override)); - MOCK_METHOD(void, broadcastByPtr, (Broadcast *), (override)); - MOCK_METHOD(void, startBackdropScripts, (Broadcast *), (override)); + MOCK_METHOD(void, broadcast, (int, VirtualMachine *), (override)); + MOCK_METHOD(void, broadcastByPtr, (Broadcast *, VirtualMachine *), (override)); + MOCK_METHOD(void, startBackdropScripts, (Broadcast *, VirtualMachine *), (override)); MOCK_METHOD(void, stopScript, (VirtualMachine *), (override)); MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override)); MOCK_METHOD(void, initClone, (std::shared_ptr), (override)); @@ -82,9 +82,6 @@ class EngineMock : public IEngine MOCK_METHOD(bool, spriteFencingEnabled, (), (const, override)); MOCK_METHOD(void, setSpriteFencingEnabled, (bool), (override)); - MOCK_METHOD(bool, broadcastRunning, (unsigned int), (override)); - MOCK_METHOD(bool, broadcastByPtrRunning, (Broadcast *), (override)); - MOCK_METHOD(void, requestRedraw, (), (override)); MOCK_METHOD(ITimer *, timer, (), (const, override)); From 03ffc205f5480f9f419d4267ca15721c3bb4ae07 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:37:49 +0200 Subject: [PATCH 13/40] Add project running API to ProjectLoader --- src/projectloader.cpp | 13 ++++++++++++- src/projectloader.h | 5 +++++ test/projectloader/projectloader_test.cpp | 23 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/projectloader.cpp b/src/projectloader.cpp index 3090487..6344911 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -128,6 +128,11 @@ bool ProjectLoader::loadStatus() const return m_loadStatus; } +bool ProjectLoader::running() const +{ + return m_running; +} + IEngine *ProjectLoader::engine() const { if (m_loadThread.isRunning()) @@ -216,9 +221,15 @@ void ProjectLoader::timerEvent(QTimerEvent *event) if (m_loadThread.isRunning()) return; - if (m_engine) + if (m_engine) { m_engine->step(); + if (m_running != m_engine->isRunning()) { + m_running = !m_running; + emit runningChanged(); + } + } + event->accept(); } diff --git a/src/projectloader.h b/src/projectloader.h index 8c949c9..286cade 100644 --- a/src/projectloader.h +++ b/src/projectloader.h @@ -24,6 +24,7 @@ class ProjectLoader : public QObject QML_ELEMENT Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) Q_PROPERTY(bool loadStatus READ loadStatus NOTIFY loadStatusChanged) + Q_PROPERTY(bool running READ running NOTIFY runningChanged) Q_PROPERTY(libscratchcpp::IEngine *engine READ engine NOTIFY engineChanged) Q_PROPERTY(StageModel *stage READ stage NOTIFY stageChanged) Q_PROPERTY(QQmlListProperty sprites READ sprites NOTIFY spritesChanged) @@ -48,6 +49,8 @@ class ProjectLoader : public QObject bool loadStatus() const; + bool running() const; + libscratchcpp::IEngine *engine() const; void setEngine(libscratchcpp::IEngine *engine); @@ -96,6 +99,7 @@ class ProjectLoader : public QObject void fileNameChanged(); void loadStatusChanged(); void loadingFinished(); + void runningChanged(); void engineChanged(); void stageChanged(); void spritesChanged(); @@ -135,6 +139,7 @@ class ProjectLoader : public QObject QString m_fileName; QFuture m_loadThread; libscratchcpp::Project m_project; + bool m_running = false; libscratchcpp::IEngine *m_engine = nullptr; QMutex m_engineMutex; bool m_loadStatus = false; diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index ea0350e..d3414be 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -163,12 +163,35 @@ TEST_F(ProjectLoaderTest, StartStop) TEST_F(ProjectLoaderTest, TimerEvent) { ProjectLoader loader; + ASSERT_FALSE(loader.running()); EngineMock engine; loader.setEngine(&engine); QTimerEvent event(0); + QSignalSpy runningSpy(&loader, &ProjectLoader::runningChanged); EXPECT_CALL(engine, step()); + EXPECT_CALL(engine, isRunning()).WillOnce(Return(false)); QCoreApplication::sendEvent(&loader, &event); + ASSERT_FALSE(loader.running()); + ASSERT_TRUE(runningSpy.empty()); + + EXPECT_CALL(engine, step()); + EXPECT_CALL(engine, isRunning()).WillOnce(Return(true)); + QCoreApplication::sendEvent(&loader, &event); + ASSERT_TRUE(loader.running()); + ASSERT_EQ(runningSpy.size(), 1); + + EXPECT_CALL(engine, step()); + EXPECT_CALL(engine, isRunning()).WillOnce(Return(true)); + QCoreApplication::sendEvent(&loader, &event); + ASSERT_TRUE(loader.running()); + ASSERT_EQ(runningSpy.size(), 1); + + EXPECT_CALL(engine, step()); + EXPECT_CALL(engine, isRunning()).WillOnce(Return(false)); + QCoreApplication::sendEvent(&loader, &event); + ASSERT_FALSE(loader.running()); + ASSERT_EQ(runningSpy.size(), 2); } TEST_F(ProjectLoaderTest, QuestionAsked) From 5727bde2c4bf025a1076ccd5426e2d40dc95fdd9 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:37:59 +0200 Subject: [PATCH 14/40] ProjectPlayer: Add running property --- src/ProjectPlayer.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 7baffa5..5d12c3d 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -11,6 +11,7 @@ ProjectScene { readonly property string fileName: loader.fileName property int stageWidth: 480 property int stageHeight: 360 + readonly property bool running: loader.running property alias fps: loader.fps property alias turboMode: loader.turboMode property alias cloneLimit: loader.cloneLimit From adaf9e06ee1b98e40752e7fb2b27a7b78f98a9a3 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:33:09 +0200 Subject: [PATCH 15/40] Add touchingColor() with mask param to RenderedTarget --- src/irenderedtarget.h | 1 + src/renderedtarget.cpp | 113 +++++++++++++------- src/renderedtarget.h | 5 +- test/mocks/renderedtargetmock.h | 1 + test/renderedtarget/renderedtarget_test.cpp | 27 +++++ 5 files changed, 109 insertions(+), 38 deletions(-) diff --git a/src/irenderedtarget.h b/src/irenderedtarget.h index 7925cd0..14cb457 100644 --- a/src/irenderedtarget.h +++ b/src/irenderedtarget.h @@ -93,6 +93,7 @@ class IRenderedTarget : public QNanoQuickItem virtual bool touchingClones(const std::vector &clones) const = 0; virtual bool touchingColor(const libscratchcpp::Value &color) const = 0; + virtual bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const = 0; }; } // namespace scratchcpprender diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 4044e02..1963b67 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -658,43 +658,12 @@ bool RenderedTarget::touchingClones(const std::vector & bool RenderedTarget::touchingColor(const Value &color) const { - // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 - if (!m_engine) - return false; - - QRgb rgb = convertColor(color); - - std::vector targets; - m_engine->getVisibleTargets(targets); - - QRectF myRect = touchingBounds(); - std::vector candidates; - QRectF bounds = candidatesBounds(myRect, targets, candidates); - - if (colorMatches(rgb, qRgb(255, 255, 255))) { - // The color we're checking for is the background color which spans the entire stage - bounds = myRect; - - if (bounds.isEmpty()) - return false; - } else if (candidates.empty()) { - // If not checking for the background color, we can return early if there are no candidate drawables - return false; - } - - // Loop through the points of the union - for (int y = bounds.top(); y <= bounds.bottom(); y++) { - for (int x = bounds.left(); x <= bounds.right(); x++) { - if (this->containsScratchPoint(x, y)) { - QRgb pixelColor = sampleColor3b(x, y, candidates); - - if (colorMatches(rgb, pixelColor)) - return true; - } - } - } + return touchingColor(color, false, Value()); +} - return false; +bool RenderedTarget::touchingColor(const Value &color, const Value &mask) const +{ + return touchingColor(color, true, mask); } void RenderedTarget::calculatePos() @@ -896,6 +865,70 @@ CpuTextureManager *RenderedTarget::textureManager() const return m_textureManager.get(); } +bool RenderedTarget::touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const +{ + // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 + if (!m_engine) + return false; + + QRgb rgb = convertColor(color); + QRgb mask3b; + double ghostValue = 0; + + if (hasMask) { + // Ignore ghost effect when checking mask + auto it = m_graphicEffects.find(ShaderManager::Effect::Ghost); + + if (it != m_graphicEffects.cend()) { + ghostValue = it->second; + m_graphicEffects.erase(ShaderManager::Effect::Ghost); + } + + mask3b = convertColor(mask); + } + + std::vector targets; + m_engine->getVisibleTargets(targets); + + QRectF myRect = touchingBounds(); + std::vector candidates; + QRectF bounds = candidatesBounds(myRect, targets, candidates); + + if (colorMatches(rgb, qRgb(255, 255, 255))) { + // The color we're checking for is the background color which spans the entire stage + bounds = myRect; + + if (bounds.isEmpty()) + return false; + } else if (candidates.empty()) { + // If not checking for the background color, we can return early if there are no candidate drawables + return false; + } + + // Loop through the points of the union + for (int y = bounds.top(); y <= bounds.bottom(); y++) { + for (int x = bounds.left(); x <= bounds.right(); x++) { + if (hasMask ? maskMatches(colorAtScratchPoint(x, y), mask3b) : this->containsScratchPoint(x, y)) { + QRgb pixelColor = sampleColor3b(x, y, candidates); + + if (colorMatches(rgb, pixelColor)) { + // Restore ghost effect value + if (hasMask && ghostValue != 0) + m_graphicEffects[ShaderManager::Effect::Ghost] = ghostValue; + + return true; + } + } + } + } + + // Restore ghost effect value + if (hasMask && ghostValue != 0) + m_graphicEffects[ShaderManager::Effect::Ghost] = ghostValue; + + return false; +} + QRectF RenderedTarget::touchingBounds() const { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L1330-L1350 @@ -1051,7 +1084,13 @@ QRgb RenderedTarget::convertColor(const libscratchcpp::Value &color) bool RenderedTarget::colorMatches(QRgb a, QRgb b) { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L77-L81 - return (qRed(a) & 0b11111000) == (qRed(b) & 0b11111000) && (qGreen(a) & 0b11111000) == (qGreen(b) & 0b11111000) && (qBlue(a) & 0b11110000) == (qBlue(b) & 0b11110000); + return qAlpha(a) > 0 && (qRed(a) & 0b11111000) == (qRed(b) & 0b11111000) && (qGreen(a) & 0b11111000) == (qGreen(b) & 0b11111000) && (qBlue(a) & 0b11110000) == (qBlue(b) & 0b11110000); +} + +bool RenderedTarget::maskMatches(QRgb a, QRgb b) +{ + // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L59-L65 + return (qRed(a) & 0b11111000) == (qRed(b) & 0b11111000) && (qGreen(a) & 0b11111000) == (qGreen(b) & 0b11111000) && (qBlue(a) & 0b11111000) == (qBlue(b) & 0b11111000); } QRgb RenderedTarget::sampleColor3b(double x, double y, const std::vector &targets) const diff --git a/src/renderedtarget.h b/src/renderedtarget.h index a69494b..bcbfae3 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -102,6 +102,7 @@ class RenderedTarget : public IRenderedTarget bool touchingClones(const std::vector &) const override; bool touchingColor(const libscratchcpp::Value &color) const override; + bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; signals: void engineChanged(); @@ -131,6 +132,7 @@ class RenderedTarget : public IRenderedTarget QPointF mapFromStageWithOriginPoint(const QPointF &scenePoint) const; QPointF mapFromScratchToLocal(const QPointF &point) const; CpuTextureManager *textureManager() const; + bool touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const; QRectF touchingBounds() const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; @@ -139,6 +141,7 @@ class RenderedTarget : public IRenderedTarget static void clampRect(libscratchcpp::Rect &rect, double left, double right, double bottom, double top); static QRgb convertColor(const libscratchcpp::Value &color); static bool colorMatches(QRgb a, QRgb b); + static bool maskMatches(QRgb a, QRgb b); QRgb sampleColor3b(double x, double y, const std::vector &targets) const; libscratchcpp::IEngine *m_engine = nullptr; @@ -156,7 +159,7 @@ class RenderedTarget : public IRenderedTarget Texture m_cpuTexture; // without stage scale mutable std::shared_ptr m_textureManager; // NOTE: Use textureManager()! std::unique_ptr m_glF; - std::unordered_map m_graphicEffects; + mutable std::unordered_map m_graphicEffects; double m_size = 1; double m_x = 0; double m_y = 0; diff --git a/test/mocks/renderedtargetmock.h b/test/mocks/renderedtargetmock.h index a120bb6..bdb610a 100644 --- a/test/mocks/renderedtargetmock.h +++ b/test/mocks/renderedtargetmock.h @@ -78,6 +78,7 @@ class RenderedTargetMock : public IRenderedTarget MOCK_METHOD(bool, touchingClones, (const std::vector &), (const, override)); MOCK_METHOD(bool, touchingColor, (const libscratchcpp::Value &), (const, override)); + MOCK_METHOD(bool, touchingColor, (const libscratchcpp::Value &, const libscratchcpp::Value &), (const, override)); MOCK_METHOD(QNanoQuickItemPainter *, createItemPainter, (), (const, override)); MOCK_METHOD(void, hoverEnterEvent, (QHoverEvent *), (override)); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 806cd9d..dff6953 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -1269,6 +1269,33 @@ TEST_F(RenderedTargetTest, TouchingColor) EXPECT_CALL(stageTarget, colorAtScratchPoint).Times(0); ASSERT_FALSE(target.touchingColor(color3)); + // Mask (color is touching color) + EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); + EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); + EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6, 2, -8))); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color4.toInt())); + EXPECT_CALL(target1, colorAtScratchPoint(3, -3)).WillOnce(Return(color1.toInt())); + ASSERT_TRUE(target.touchingColor(color5, color3)); + + EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5))); + EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5))); + EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6, 2, -8))); + EXPECT_CALL(target2, colorAtScratchPoint).Times(0); + EXPECT_CALL(target1, colorAtScratchPoint).Times(0); + EXPECT_CALL(penLayer, colorAtScratchPoint).Times(0); + EXPECT_CALL(stageTarget, colorAtScratchPoint).Times(0); + ASSERT_FALSE(target.touchingColor(color3, color3)); + + // Ghost effect shouldn't affect mask check + target.setGraphicEffect(ShaderManager::Effect::Ghost, 100); + EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); + EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); + EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6, 2, -8))); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color4.toInt())); + EXPECT_CALL(target1, colorAtScratchPoint(3, -3)).WillOnce(Return(color1.toInt())); + ASSERT_TRUE(target.touchingColor(color5, color3)); + ASSERT_EQ(target.graphicEffects().at(ShaderManager::Effect::Ghost), 100); + // Out of bounds: top left target.updateX(-300); target.updateY(200); From ebc60c8041242d631b2a70dfe33e8458fcbf4327 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:39:14 +0200 Subject: [PATCH 16/40] Call touchingColor() with mask param from target models --- src/spritemodel.cpp | 2 +- src/stagemodel.cpp | 2 +- test/target_models/spritemodel_test.cpp | 16 +++++++++++----- test/target_models/stagemodel_test.cpp | 16 +++++++++++----- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 6e4abb4..19ec382 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -177,7 +177,7 @@ bool SpriteModel::touchingColor(const libscratchcpp::Value &color) const bool SpriteModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const { - return false; + return m_renderedTarget->touchingColor(color, mask); } libscratchcpp::Sprite *SpriteModel::sprite() const diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 67245ec..972feaf 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -114,7 +114,7 @@ bool StageModel::touchingColor(const libscratchcpp::Value &color) const bool StageModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const { - return false; + return m_renderedTarget->touchingColor(color, mask); } void StageModel::loadCostume() diff --git a/test/target_models/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index 5b406e0..8ec74b9 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -388,12 +388,18 @@ TEST(SpriteModelTest, TouchingColor) RenderedTargetMock renderedTarget; model.setRenderedTarget(&renderedTarget); - Value color = 123; - EXPECT_CALL(renderedTarget, touchingColor(color)).WillOnce(Return(false)); - ASSERT_FALSE(model.touchingColor(color)); + Value color1 = 123, color2 = 456; + EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(false)); + ASSERT_FALSE(model.touchingColor(color1)); - EXPECT_CALL(renderedTarget, touchingColor(color)).WillOnce(Return(true)); - ASSERT_TRUE(model.touchingColor(color)); + EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(true)); + ASSERT_TRUE(model.touchingColor(color1)); + + EXPECT_CALL(renderedTarget, touchingColor(color1, color2)).WillOnce(Return(false)); + ASSERT_FALSE(model.touchingColor(color1, color2)); + + EXPECT_CALL(renderedTarget, touchingColor(color1, color2)).WillOnce(Return(true)); + ASSERT_TRUE(model.touchingColor(color1, color2)); } TEST(SpriteModelTest, RenderedTarget) diff --git a/test/target_models/stagemodel_test.cpp b/test/target_models/stagemodel_test.cpp index 672916d..a6d4d37 100644 --- a/test/target_models/stagemodel_test.cpp +++ b/test/target_models/stagemodel_test.cpp @@ -181,12 +181,18 @@ TEST(StageModelTest, TouchingColor) RenderedTargetMock renderedTarget; model.setRenderedTarget(&renderedTarget); - Value color = 123; - EXPECT_CALL(renderedTarget, touchingColor(color)).WillOnce(Return(false)); - ASSERT_FALSE(model.touchingColor(color)); + Value color1 = 123, color2 = 456; + EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(false)); + ASSERT_FALSE(model.touchingColor(color1)); - EXPECT_CALL(renderedTarget, touchingColor(color)).WillOnce(Return(true)); - ASSERT_TRUE(model.touchingColor(color)); + EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(true)); + ASSERT_TRUE(model.touchingColor(color1)); + + EXPECT_CALL(renderedTarget, touchingColor(color1, color2)).WillOnce(Return(false)); + ASSERT_FALSE(model.touchingColor(color1, color2)); + + EXPECT_CALL(renderedTarget, touchingColor(color1, color2)).WillOnce(Return(true)); + ASSERT_TRUE(model.touchingColor(color1, color2)); } TEST(StageModelTest, RenderedTarget) From fe0d32bab89b95291e0c4904311ac5aff03b4334 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:40:25 +0200 Subject: [PATCH 17/40] README: Mark touching color blocks as finished --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 670e6b9..d38feeb 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ int main(int argc, char **argv) { - [x] Clones - [x] Sprite dragging - [x] Touching sprite block -- [ ] Touching color blocks (color is touching color is not implemented yet) +- [x] Touching color blocks - [x] Pen blocks - [x] Monitors - [ ] Graphics effects (color, brightness and ghost are implemented) From 8872d946b48bcdb115e17a6869dfb0c899a5fa48 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:25:06 +0200 Subject: [PATCH 18/40] Revert "fix #141: Ignore invisible sprites in contains point methods" This reverts commit b000c61af9536715c4b7a228bef104266992592d. --- src/renderedtarget.cpp | 4 ++-- test/renderedtarget/renderedtarget_test.cpp | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 1963b67..70dc369 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -584,7 +584,7 @@ const std::vector &RenderedTarget::hullPoints() const bool RenderedTarget::contains(const QPointF &point) const { - if (!isVisible() || !m_costume || !m_texture.isValid() || !m_cpuTexture.isValid() || !parentItem()) + if (!m_costume || !m_texture.isValid() || !m_cpuTexture.isValid() || !parentItem()) return false; const double scaleRatio = m_skin->getTextureScale(m_texture) / m_skin->getTextureScale(m_cpuTexture); @@ -597,7 +597,7 @@ bool RenderedTarget::contains(const QPointF &point) const bool RenderedTarget::containsScratchPoint(double x, double y) const { - if (!isVisible() || !m_engine || !m_skin || !m_costume) + if (!m_engine || !m_skin || !m_costume) return false; return containsLocalPoint(mapFromScratchToLocal(QPointF(x, y))); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index dff6953..f3f9b9a 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -403,12 +403,6 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_TRUE(target.contains({ 3, 3 })); ASSERT_TRUE(target.contains({ 3.3, 3.5 })); - // Regtest for #141 - target.setVisible(false); - ASSERT_FALSE(target.contains({ 1, 3 })); - ASSERT_FALSE(target.contains({ 2, 3 })); - target.setVisible(true); - // Test contains() with horizontal mirroring target.updateRotationStyle(Sprite::RotationStyle::LeftRight); target.updateDirection(-45); @@ -443,12 +437,6 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_TRUE(target.containsScratchPoint(-225, 162)); // [3, 3] ASSERT_TRUE(target.containsScratchPoint(-224.7, 161.5)); // [3.3, 3.5] - // Regtest for #141 - target.setVisible(false); - ASSERT_FALSE(target.containsScratchPoint(-227, 162)); // [1, 3] - ASSERT_FALSE(target.containsScratchPoint(-226, 162)); // [2, 3] - target.setVisible(true); - // Test colorAtScratchPoint() ASSERT_EQ(target.colorAtScratchPoint(-228, 165), 0); // [0, 0] ASSERT_EQ(target.colorAtScratchPoint(-227, 165), 0); // [1, 0] From de66d838cae305acd7683f5a84c71ea2c6414781 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:49:37 +0200 Subject: [PATCH 19/40] fix #141: Do not send mouse events to invisible sprites --- src/mouseeventhandler.cpp | 2 +- test/mouseeventhandler/mouseeventhandler_test.cpp | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/mouseeventhandler.cpp b/src/mouseeventhandler.cpp index 18876ea..e1dde78 100644 --- a/src/mouseeventhandler.cpp +++ b/src/mouseeventhandler.cpp @@ -165,7 +165,7 @@ void MouseEventHandler::forwardPointEvent(QSinglePointEvent *event, QQuickItem * // contains() expects position in the item's coordinate system QPointF localPos = sprite->mapFromScene(event->scenePosition()); - if (sprite->contains(localPos)) { + if (static_cast(sprite->scratchTarget())->visible() && sprite->contains(localPos)) { hoveredItem = sprite; break; } diff --git a/test/mouseeventhandler/mouseeventhandler_test.cpp b/test/mouseeventhandler/mouseeventhandler_test.cpp index c034158..49a2ae3 100644 --- a/test/mouseeventhandler/mouseeventhandler_test.cpp +++ b/test/mouseeventhandler/mouseeventhandler_test.cpp @@ -281,20 +281,24 @@ TEST(MouseEventHandlerTest, MouseMoveEvent) TEST(MouseEventHandlerTest, MousePressReleaseEvent) { MouseEventHandler handler; - RenderedTargetMock stage, renderedTarget1, renderedTarget2, renderedTarget3; - SpriteModel model1, model2, model3; + RenderedTargetMock stage, renderedTarget1, renderedTarget2, renderedTarget3, renderedTarget4; + SpriteModel model1, model2, model3, model4; model1.setRenderedTarget(&renderedTarget1); model2.setRenderedTarget(&renderedTarget2); model3.setRenderedTarget(&renderedTarget3); - Sprite sprite1, sprite2, sprite3; + model4.setRenderedTarget(&renderedTarget4); + Sprite sprite1, sprite2, sprite3, sprite4; sprite1.setLayerOrder(2); sprite2.setLayerOrder(1); sprite3.setLayerOrder(3); + sprite4.setLayerOrder(4); + sprite4.setVisible(false); ProjectLoader loader; auto sprites = loader.sprites(); sprites.append(&sprites, &model1); sprites.append(&sprites, &model2); sprites.append(&sprites, &model3); + sprites.append(&sprites, &model4); handler.setStage(&stage); handler.setProjectLoader(&loader); QPointingDevice dev; @@ -307,6 +311,7 @@ TEST(MouseEventHandlerTest, MousePressReleaseEvent) EXPECT_CALL(renderedTarget1, scratchTarget()).WillRepeatedly(Return(&sprite1)); EXPECT_CALL(renderedTarget2, scratchTarget()).WillRepeatedly(Return(&sprite2)); EXPECT_CALL(renderedTarget3, scratchTarget()).WillRepeatedly(Return(&sprite3)); + EXPECT_CALL(renderedTarget4, scratchTarget()).WillRepeatedly(Return(&sprite4)); emit loader.spritesChanged(); EXPECT_CALL(renderedTarget1, mapFromScene(scenePos)).WillRepeatedly(Return(localPos)); @@ -387,6 +392,7 @@ TEST(MouseEventHandlerTest, MousePressReleaseEvent) EXPECT_CALL(renderedTarget3, contains(localPos)).WillOnce(Return(false)); EXPECT_CALL(renderedTarget1, contains(localPos)).WillOnce(Return(false)); EXPECT_CALL(renderedTarget2, contains(localPos)).WillOnce(Return(false)); + EXPECT_CALL(renderedTarget4, contains).Times(0); EXPECT_CALL(stage, mousePressEvent(_)).WillOnce(WithArgs<0>(Invoke(checkPressEvent))); ASSERT_TRUE(handler.eventFilter(nullptr, &pressEvent)); ASSERT_EQ(pressedSpy.count(), 1); From fa575f91b1674da734e3209a062b744d2777667f Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:40:04 +0200 Subject: [PATCH 20/40] Add unsupportedBlocks property to ProjectLoader --- src/projectloader.cpp | 14 ++++++++++++++ src/projectloader.h | 5 +++++ test/projectloader/projectloader_test.cpp | 21 +++++++++++++++++++++ test/unsupported_blocks.sb3 | Bin 0 -> 2106 bytes 4 files changed, 40 insertions(+) create mode 100644 test/unsupported_blocks.sb3 diff --git a/src/projectloader.cpp b/src/projectloader.cpp index 6344911..cc6a3ee 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -188,6 +188,11 @@ const QList &ProjectLoader::monitorList() const return m_monitors; } +const QStringList &ProjectLoader::unsupportedBlocks() const +{ + return m_unsupportedBlocks; +} + void ProjectLoader::start() { if (m_loadThread.isRunning()) @@ -300,6 +305,15 @@ void ProjectLoader::load() return; } + // Get unsupported blocks + const auto &unsupportedBlocks = m_engine->unsupportedBlocks(); + m_unsupportedBlocks.clear(); + + for (const std::string &opcode : unsupportedBlocks) + m_unsupportedBlocks.push_back(QString::fromStdString(opcode)); + + emit unsupportedBlocksChanged(); + m_engineMutex.unlock(); emit loadStatusChanged(); diff --git a/src/projectloader.h b/src/projectloader.h index 286cade..a645afa 100644 --- a/src/projectloader.h +++ b/src/projectloader.h @@ -30,6 +30,7 @@ class ProjectLoader : public QObject Q_PROPERTY(QQmlListProperty sprites READ sprites NOTIFY spritesChanged) Q_PROPERTY(QQmlListProperty clones READ clones NOTIFY clonesChanged) Q_PROPERTY(QQmlListProperty monitors READ monitors NOTIFY monitorsChanged) + Q_PROPERTY(QStringList unsupportedBlocks READ unsupportedBlocks NOTIFY unsupportedBlocksChanged) Q_PROPERTY(double fps READ fps WRITE setFps NOTIFY fpsChanged) Q_PROPERTY(bool turboMode READ turboMode WRITE setTurboMode NOTIFY turboModeChanged) Q_PROPERTY(unsigned int stageWidth READ stageWidth WRITE setStageWidth NOTIFY stageWidthChanged) @@ -65,6 +66,8 @@ class ProjectLoader : public QObject QQmlListProperty monitors(); const QList &monitorList() const; + const QStringList &unsupportedBlocks() const; + Q_INVOKABLE void start(); Q_INVOKABLE void stop(); @@ -105,6 +108,7 @@ class ProjectLoader : public QObject void spritesChanged(); void clonesChanged(); void monitorsChanged(); + void unsupportedBlocksChanged(); void fpsChanged(); void turboModeChanged(); void stageWidthChanged(); @@ -147,6 +151,7 @@ class ProjectLoader : public QObject QList m_sprites; QList m_clones; QList m_monitors; + QStringList m_unsupportedBlocks; double m_fps = 30; bool m_turboMode = false; unsigned int m_stageWidth = 480; diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index d3414be..96ee1f2 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -32,6 +32,7 @@ class ProjectLoaderTest : public testing::Test QSignalSpy clonesSpy(loader, &ProjectLoader::clonesChanged); QSignalSpy monitorsSpy(loader, &ProjectLoader::monitorsChanged); QSignalSpy monitorAddedSpy(loader, &ProjectLoader::monitorAdded); + QSignalSpy unsupportedBlocksSpy(loader, &ProjectLoader::unsupportedBlocksChanged); loader->setFileName(fileName); @@ -60,6 +61,7 @@ class ProjectLoaderTest : public testing::Test ASSERT_EQ(clonesSpy.count(), 1); ASSERT_EQ(monitorsSpy.count(), loader->monitorList().size() + 1); ASSERT_EQ(monitorAddedSpy.count(), loader->monitorList().size()); + ASSERT_EQ(unsupportedBlocksSpy.count(), 1); } }; @@ -107,6 +109,25 @@ TEST_F(ProjectLoaderTest, Load) ASSERT_EQ(valueMonitorModel->color(), QColor::fromString("#FF8C1A")); } +TEST_F(ProjectLoaderTest, UnsupportedBlocks) +{ + ProjectLoader loader; + ASSERT_TRUE(loader.fileName().isEmpty()); + ASSERT_FALSE(loader.loadStatus()); + ASSERT_TRUE(loader.stage()); + + loader.setFileName("unsupported_blocks.sb3"); + loader.start(); // wait until it loads + + auto engine = loader.engine(); + const auto &blocks = loader.unsupportedBlocks(); + const auto &refBlocks = engine->unsupportedBlocks(); + ASSERT_EQ(blocks.size(), refBlocks.size()); + + for (const QString &opcode : blocks) + ASSERT_NE(refBlocks.find(opcode.toStdString()), refBlocks.cend()); +} + TEST_F(ProjectLoaderTest, Clones) { ProjectLoader loader; diff --git a/test/unsupported_blocks.sb3 b/test/unsupported_blocks.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..7098a6541e5407b4cd03d024d87b350443ce34ee GIT binary patch literal 2106 zcma)-XHXLg633HBJt8WJAc$xvQbaL8s4t))AVN?;2tD+!1gUwCCP5Gd6=_lo0Zae| zLW{v$H$<|FHYzXK8+j?;ijFKnO6LB;ime z{Z=4X002lm4gi1;rokcj0B_H*3jv|{pm+9xc9Uk%y>np=6$&{WcfWtF7ddTAB^Fw~ zs#Q9bE{smcJi~yJHyCrde3}7ek6&*&spm3hc`F*eXG1T+BxRR~A(5hBhQ<8Rcy$FC zu{(#)Z-QI2fIrht1iQ(OMvBHd+cEVg^q;@IeUWxky!6FPDEB$Syh5*L`_hJO+8-jo zoFwyvc~H?@gh{?$W{{)*s!>SX@|UzDMJ(#2g-rqn*lc;jU5D71Z+}XSY|I<_$*QrPqkG6h8}3XPfhKoG%q;*-(eEu$Z(?FN$hglq7rA~Io?kwQi$#%s16^NRN`Pis1h?nTeLoU zme3QXL{Up+qN_y`=;=*NC=9JyXf*VY&;%1OUV})WWp~O6@f!?$;#AiRtRPB%@5s)_ z=bsC>5i4c(pNHhg!u{mbw95S8vb-cc6RF3>O1vM^gm-RKYZ)c>F~jyC-63JnqzWXGGggZ2Wm_;KR(>LO%5qRksl>wEG@#MaV? z@Ai)nlg(HKll~^XHZg*<3Kj4E;3`vY)8GiVEqEXS#LkX|onv>71TDKl0#2*^`Z-bt z3iQ}6yj1@Cs4yQu`=x;zA@gjbQKuR%;+Yc{;wCxp0+B7|&40$wY5^hdsTgY_Hi2W& z6NM9Adz`3WE$GWjX4W4=mw0kB?ldW;bSP|m32m!+X`n&X^Vm+s0#EnI6kG3Xu$fks zqr=lv8DR{6(#;-x(@A@DjHKL-E5loLpL12WC4)6+{*{q{i^QWUC%?wt#!l&6I2B}D zJUynPWk8I}_fs7o-wl+TY=K~fMo)xVus(~eF7Our)Y=)GMgAtaF*HQ_Qk?tg{%8Qs zX2N8~mM@r{*>^o?cilgoP?^H@j;ZR#iZ+}<&QNfz8WEk5B_HzuzviL4TxIH3>U_IP z`1Lz84|f;oV{u72r>Ii%AX>8sPl|P21uGAwS?$&T=;PRY^NY?Zkf#0!bK$STZoQ{? zizF6Oyfp?rK2W|v^|48Y7hY?wN{Fsh@lwiP(v@YFA3nC-KIS1YL32OCTWP2>5RBb& zE1WLR@!e}Isq!(JTq0|#1<;VB4{q$U;t)hdqk!_$ZvM0oe$hovv%fEq1U==PaeE}U zH!zCi8_#CGL}FK~XJnH}cjW}jH={Xa?-}~MYww)k;d)!trHP#c+qM#3KR<+Vie@-s;MVZ7!`DtXTICd(V(WV4F*G1ch|JwF_U%BUOJV zcHHy%V2JBcoN^4E)$y~*a2@RHHA>HkVn#^ohi4`|RCc`L{xIbe6*man)=maXkVHuz zN@CN!IQve@d$0`&KOw|7xEvk?6Nl4G?>CC+NGBO;W)rI}(HB!0Aiq3e zQyqb(0sC<+`vIqY%lX=lspN1lrbVrPWtDq5Tl;0s6MDkoM-Kdsmp6w4Z@oTrPk@aR zqOlb(TcYf?`!_7h+S~dUlnS*`9l_{?D>zgOXbqkAajgpZCgSmTxYoH7N9++4qrPZ9 zZQ2%c+7*_G1Le#w?#cVm{Hy)6!1}362mlrBJj2dHgqnCCw#Ni)M$??c`zjHHAr)_~HDZZVzlG zgeONlPj_KyV9cyk?uTfR%kpme!p3KNQ)=x^=|#PoxL#rLlwSbL|3X-D?@?gJ0lK;l zB!3`JFLgKquIY6Vp#k^t^6~LdS69EN>3z}L6XETpqv?JjG{X0mg}G?L8R&G>gE59+ z8%?vN`0Dk32;JF8<^g5DO_@uS0bzphA!iiN}eEplbTiNNyO{ z0hUo+M6~E0(z%kD?dN~jRGAz0PXlnVO1JgR)+?0G=lSGc#9CTl&D%1OJDY7MoYy}c z%9WA9zX_keM&cK^qP@>&p!=yLPS_12vASg*cW@LC1O@zWG9O&_m-s8E|L*+zL;hoH bJs|!6^Bzlc{)0^b0CbS42Q^3V-`&3e4sy|z literal 0 HcmV?d00001 From 1fa0db02f6a38d1a7036b0ca7d821239918df5e6 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:40:25 +0200 Subject: [PATCH 21/40] ProjectPlayer: Add unsupportedBlocks property --- src/ProjectPlayer.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 5d12c3d..b0b200c 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -12,6 +12,7 @@ ProjectScene { property int stageWidth: 480 property int stageHeight: 360 readonly property bool running: loader.running + readonly property list unsupportedBlocks: loader.unsupportedBlocks property alias fps: loader.fps property alias turboMode: loader.turboMode property alias cloneLimit: loader.cloneLimit From 5c2490442f5c26ac350c591ea1f532b220629512 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:31:24 +0200 Subject: [PATCH 22/40] Update libscratchcpp to latest master --- libscratchcpp | 2 +- src/listmonitorlistmodel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libscratchcpp b/libscratchcpp index 537ff52..77f0e66 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 537ff52a7500d17d07df7ea31c7023358824e344 +Subproject commit 77f0e66cb63c979bc6324d2285b606db0f863f8f diff --git a/src/listmonitorlistmodel.cpp b/src/listmonitorlistmodel.cpp index 3cd7fd7..a055bcc 100644 --- a/src/listmonitorlistmodel.cpp +++ b/src/listmonitorlistmodel.cpp @@ -57,7 +57,7 @@ QVariant ListMonitorListModel::data(const QModelIndex &index, int role) const if (!m_list || index.row() < 0 || index.row() >= m_list->size()) return QVariant(); - return QString::fromStdString((*m_list)[index.row()].toString()); + return QString::fromStdString(libscratchcpp::Value((*m_list)[index.row()]).toString()); } QHash ListMonitorListModel::roleNames() const From 9c7b60b247d05a274f18a974883f874f0c76443c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:20:37 +0200 Subject: [PATCH 23/40] Update EngineMock --- test/mocks/enginemock.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 48032d4..07b0cc0 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -18,12 +18,12 @@ class EngineMock : public IEngine MOCK_METHOD(void, start, (), (override)); MOCK_METHOD(void, stop, (), (override)); - MOCK_METHOD(VirtualMachine *, startScript, (std::shared_ptr, Target *), (override)); - MOCK_METHOD(void, broadcast, (int, VirtualMachine *), (override)); - MOCK_METHOD(void, broadcastByPtr, (Broadcast *, VirtualMachine *), (override)); - MOCK_METHOD(void, startBackdropScripts, (Broadcast *, VirtualMachine *), (override)); - MOCK_METHOD(void, stopScript, (VirtualMachine *), (override)); - MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override)); + MOCK_METHOD(Thread *, startScript, (std::shared_ptr, Target *), (override)); + MOCK_METHOD(void, broadcast, (int, Thread *, bool), (override)); + MOCK_METHOD(void, broadcastByPtr, (Broadcast *, Thread *, bool), (override)); + MOCK_METHOD(void, startBackdropScripts, (Broadcast *, Thread *, bool), (override)); + MOCK_METHOD(void, stopScript, (Thread *), (override)); + MOCK_METHOD(void, stopTarget, (Target *, Thread *), (override)); MOCK_METHOD(void, initClone, (std::shared_ptr), (override)); MOCK_METHOD(void, deinitClone, (std::shared_ptr), (override)); @@ -38,7 +38,7 @@ class EngineMock : public IEngine MOCK_METHOD(void, stopEventLoop, (), (override)); MOCK_METHOD(sigslot::signal<> &, aboutToRender, (), (override)); - MOCK_METHOD(sigslot::signal &, threadAboutToStop, (), (override)); + MOCK_METHOD(sigslot::signal &, threadAboutToStop, (), (override)); MOCK_METHOD(sigslot::signal<> &, stopped, (), (override)); MOCK_METHOD(bool, isRunning, (), (const, override)); @@ -130,6 +130,8 @@ class EngineMock : public IEngine MOCK_METHOD(const std::vector> &, monitors, (), (const, override)); MOCK_METHOD(void, setMonitors, (const std::vector> &), (override)); + MOCK_METHOD(Monitor *, createVariableMonitor, (std::shared_ptr, const std::string &, const std::string &, int, BlockComp), (override)); + MOCK_METHOD(Monitor *, createListMonitor, (std::shared_ptr, const std::string &, const std::string &, int, BlockComp), (override)); MOCK_METHOD(sigslot::signal &, monitorAdded, (), (override)); MOCK_METHOD((sigslot::signal &), monitorRemoved, (), (override)); From 988a138f24ce83dc832b1905990b24a65c6c40d6 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:34:22 +0200 Subject: [PATCH 24/40] Update ListMonitorListModel test --- .../listmonitorlistmodel_test.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/monitor_models/listmonitorlistmodel_test.cpp b/test/monitor_models/listmonitorlistmodel_test.cpp index 91584b3..42c1539 100644 --- a/test/monitor_models/listmonitorlistmodel_test.cpp +++ b/test/monitor_models/listmonitorlistmodel_test.cpp @@ -26,8 +26,8 @@ TEST(ListMonitorListModelTest, LoadData) QSignalSpy removeSpy(&model, &ListMonitorListModel::rowsRemoved); List list1("", ""); - list1.push_back(1); - list1.push_back(2); + list1.append(1); + list1.append(2); model.setList(&list1); ASSERT_TRUE(dataChangedSpy.empty()); ASSERT_EQ(aboutToResetSpy.count(), 1); @@ -87,8 +87,8 @@ TEST(ListMonitorListModelTest, AddRows) QSignalSpy removeSpy(&model, &ListMonitorListModel::rowsRemoved); List list1("", ""); - list1.push_back(1); - list1.push_back(2); + list1.append(1); + list1.append(2); model.setList(&list1); ASSERT_TRUE(dataChangedSpy.empty()); ASSERT_EQ(aboutToResetSpy.count(), 1); @@ -98,9 +98,9 @@ TEST(ListMonitorListModelTest, AddRows) ASSERT_TRUE(aboutToRemoveSpy.empty()); ASSERT_TRUE(removeSpy.empty()); - list1.push_back(9); - list1.push_back(8); - list1.push_back(7); + list1.append(9); + list1.append(8); + list1.append(7); model.setList(&list1); ASSERT_EQ(dataChangedSpy.count(), 2); ASSERT_EQ(aboutToResetSpy.count(), 1); @@ -147,9 +147,9 @@ TEST(ListMonitorListModelTest, RemoveRows) QSignalSpy removeSpy(&model, &ListMonitorListModel::rowsRemoved); List list1("", ""); - list1.push_back(1); - list1.push_back(2); - list1.push_back(3); + list1.append(1); + list1.append(2); + list1.append(3); model.setList(&list1); ASSERT_TRUE(dataChangedSpy.empty()); ASSERT_EQ(aboutToResetSpy.count(), 1); @@ -190,9 +190,9 @@ TEST(ListMonitorListModelTest, RowCount) { ListMonitorListModel model; List list("", ""); - list.push_back(1); - list.push_back(2); - list.push_back(3); + list.append(1); + list.append(2); + list.append(3); model.setList(&list); ASSERT_EQ(model.rowCount(QModelIndex()), list.size()); } @@ -201,9 +201,9 @@ TEST(ListMonitorListModelTest, Data) { ListMonitorListModel model; List list("", ""); - list.push_back(1); - list.push_back(2); - list.push_back(3); + list.append(1); + list.append(2); + list.append(3); model.setList(&list); ASSERT_EQ(model.data(model.index(0), 0).toString(), "1"); ASSERT_EQ(model.data(model.index(1), 0).toString(), "2"); From 061b54052e2ece9b51cd8d218b408e3d9a8ae19e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:38:49 +0200 Subject: [PATCH 25/40] Update ListMonitorModel test --- test/monitor_models/listmonitormodel_test.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/monitor_models/listmonitormodel_test.cpp b/test/monitor_models/listmonitormodel_test.cpp index 86029f6..1b00f00 100644 --- a/test/monitor_models/listmonitormodel_test.cpp +++ b/test/monitor_models/listmonitormodel_test.cpp @@ -31,19 +31,19 @@ TEST(ListMonitorModelTest, OnValueChanged) VirtualMachine vm; List list1("", ""); - list1.push_back(1); - list1.push_back(2); + list1.append(1); + list1.append(2); List list2("", ""); - list2.push_back(1); - list2.push_back(2); - list2.push_back(3); - list2.push_back(4); + list2.append(1); + list2.append(2); + list2.append(3); + list2.append(4); List list3("", ""); - list3.push_back(1); - list3.push_back(2); - list3.push_back(3); + list3.append(1); + list3.append(2); + list3.append(3); List *lists[] = { &list1, &list2, &list3 }; vm.setLists(lists); From d31c850ed66b8e9b0f3aae586d854157254eef59 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:39:00 +0200 Subject: [PATCH 26/40] Update ProjectLoader test --- test/projectloader/projectloader_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index 96ee1f2..c58fa50 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -98,7 +98,7 @@ TEST_F(ProjectLoaderTest, Load) ASSERT_EQ(sprites[1]->sprite(), engine->targetAt(2)); const auto &monitors = loader.monitorList(); - ASSERT_EQ(monitors.size(), 11); + ASSERT_EQ(monitors.size(), 10); ListMonitorModel *listMonitorModel = dynamic_cast(monitors[0]); ASSERT_EQ(listMonitorModel->monitor(), engine->monitors().at(0).get()); From 063ff386091db6f5074bc5c0c4dc75f6f491b592 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:11:18 +0200 Subject: [PATCH 27/40] Update libscratchcpp to latest master --- libscratchcpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index 77f0e66..24e3067 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 77f0e66cb63c979bc6324d2285b606db0f863f8f +Subproject commit 24e306767e356988a9823a0ee63f477004a62a6e From d18b326bf2b0151c513fd9befda30d57d20aac95 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:30:00 +0200 Subject: [PATCH 28/40] Update libscratchcpp to latest master --- libscratchcpp | 2 +- src/CMakeLists.txt | 2 - src/blocks/penblocks.cpp | 7 ++- src/blocks/penblocks.h | 5 +- src/blocks/penextension.cpp | 24 --------- src/blocks/penextension.h | 19 ------- src/listmonitormodel.cpp | 4 +- src/listmonitormodel.h | 2 +- src/monitormodel.cpp | 18 +++++-- src/monitormodel.h | 6 ++- src/projectloader.cpp | 12 ++--- src/valuemonitormodel.cpp | 4 +- src/valuemonitormodel.h | 2 +- test/blocks/CMakeLists.txt | 18 ------- test/blocks/pen_blocks_test.cpp | 53 +++++++++---------- test/blocks/penextension_test.cpp | 39 -------------- test/mocks/enginemock.h | 17 +++--- .../{blocksectionmock.h => extensionmock.h} | 8 +-- test/monitor_models/monitormodel_test.cpp | 28 +++++----- test/projectloader/projectloader_test.cpp | 4 +- 20 files changed, 92 insertions(+), 182 deletions(-) delete mode 100644 src/blocks/penextension.cpp delete mode 100644 src/blocks/penextension.h delete mode 100644 test/blocks/penextension_test.cpp rename test/mocks/{blocksectionmock.h => extensionmock.h} (58%) diff --git a/libscratchcpp b/libscratchcpp index 24e3067..572a2ea 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 24e306767e356988a9823a0ee63f477004a62a6e +Subproject commit 572a2ea490558eb6b8c7446975f25216ec900b1f diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b2eb1c..b681546 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,8 +74,6 @@ qt_add_qml_module(scratchcpp-render cputexturemanager.h effecttransform.cpp effecttransform.h - blocks/penextension.cpp - blocks/penextension.h blocks/penblocks.cpp blocks/penblocks.h ) diff --git a/src/blocks/penblocks.cpp b/src/blocks/penblocks.cpp index 5c3e1cd..8905786 100644 --- a/src/blocks/penblocks.cpp +++ b/src/blocks/penblocks.cpp @@ -26,7 +26,12 @@ const std::unordered_map std::string PenBlocks::name() const { - return "Pen"; + return "pen"; +} + +std::string PenBlocks::description() const +{ + return "Pen blocks"; } void PenBlocks::registerBlocks(IEngine *engine) diff --git a/src/blocks/penblocks.h b/src/blocks/penblocks.h index 53f6596..0feb01a 100644 --- a/src/blocks/penblocks.h +++ b/src/blocks/penblocks.h @@ -3,7 +3,7 @@ #pragma once #include -#include +#include namespace scratchcpprender { @@ -11,7 +11,7 @@ namespace scratchcpprender class SpriteModel; class PenState; -class PenBlocks : public libscratchcpp::IBlockSection +class PenBlocks : public libscratchcpp::IExtension { public: enum Inputs @@ -25,6 +25,7 @@ class PenBlocks : public libscratchcpp::IBlockSection }; std::string name() const override; + std::string description() const override; void registerBlocks(libscratchcpp::IEngine *engine) override; diff --git a/src/blocks/penextension.cpp b/src/blocks/penextension.cpp deleted file mode 100644 index 1226d08..0000000 --- a/src/blocks/penextension.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later - -#include - -#include "penextension.h" -#include "penblocks.h" - -using namespace scratchcpprender; -using namespace libscratchcpp; - -std::string PenExtension::name() const -{ - return "pen"; -} - -std::string PenExtension::description() const -{ - return "Pen extension"; -} - -void PenExtension::registerSections(IEngine *engine) -{ - engine->registerSection(std::make_shared()); -} diff --git a/src/blocks/penextension.h b/src/blocks/penextension.h deleted file mode 100644 index 1e7363d..0000000 --- a/src/blocks/penextension.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later - -#pragma once - -#include - -namespace scratchcpprender -{ - -class PenExtension : public libscratchcpp::IExtension -{ - public: - std::string name() const override; - std::string description() const override; - - void registerSections(libscratchcpp::IEngine *engine) override; -}; - -} // namespace scratchcpprender diff --git a/src/listmonitormodel.cpp b/src/listmonitormodel.cpp index 2a42315..2182303 100644 --- a/src/listmonitormodel.cpp +++ b/src/listmonitormodel.cpp @@ -12,8 +12,8 @@ ListMonitorModel::ListMonitorModel(QObject *parent) : { } -ListMonitorModel::ListMonitorModel(libscratchcpp::IBlockSection *section, QObject *parent) : - MonitorModel(section, parent) +ListMonitorModel::ListMonitorModel(libscratchcpp::IExtension *extension, QObject *parent) : + MonitorModel(extension, parent) { m_listModel = new ListMonitorListModel(this); } diff --git a/src/listmonitormodel.h b/src/listmonitormodel.h index 45a6e8b..4034e03 100644 --- a/src/listmonitormodel.h +++ b/src/listmonitormodel.h @@ -21,7 +21,7 @@ class ListMonitorModel : public MonitorModel public: ListMonitorModel(QObject *parent = nullptr); - ListMonitorModel(libscratchcpp::IBlockSection *section, QObject *parent = nullptr); + ListMonitorModel(libscratchcpp::IExtension *extension, QObject *parent = nullptr); void onValueChanged(const libscratchcpp::VirtualMachine *vm) override; diff --git a/src/monitormodel.cpp b/src/monitormodel.cpp index 9554276..ec2fc24 100644 --- a/src/monitormodel.cpp +++ b/src/monitormodel.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include "monitormodel.h" @@ -13,14 +13,14 @@ MonitorModel::MonitorModel(QObject *parent) : { } -MonitorModel::MonitorModel(libscratchcpp::IBlockSection *section, QObject *parent) : +MonitorModel::MonitorModel(libscratchcpp::IExtension *extension, QObject *parent) : QObject(parent) { - if (!section) + if (!extension) return; - // TODO: Get the color from the block section - std::string name = section->name(); + // TODO: Get the color from the extension + std::string name = extension->name(); if (name == "Motion") m_color = QColor::fromString("#4C97FF"); else if (name == "Looks") @@ -69,6 +69,14 @@ void MonitorModel::onVisibleChanged(bool visible) emit visibleChanged(); } +void MonitorModel::onXChanged(int x) +{ +} + +void MonitorModel::onYChanged(int y) +{ +} + libscratchcpp::Monitor *MonitorModel::monitor() const { return m_monitor; diff --git a/src/monitormodel.h b/src/monitormodel.h index 329a284..8ad7f73 100644 --- a/src/monitormodel.h +++ b/src/monitormodel.h @@ -10,7 +10,7 @@ namespace libscratchcpp { -class IBlockSection; +class IExtension; } @@ -43,12 +43,14 @@ class MonitorModel Q_ENUM(Type) MonitorModel(QObject *parent = nullptr); - MonitorModel(libscratchcpp::IBlockSection *section, QObject *parent = nullptr); + MonitorModel(libscratchcpp::IExtension *extension, QObject *parent = nullptr); void init(libscratchcpp::Monitor *monitor) override final; virtual void onValueChanged(const libscratchcpp::VirtualMachine *vm) override { } void onVisibleChanged(bool visible) override final; + void onXChanged(int x) override final; + void onYChanged(int y) override final; libscratchcpp::Monitor *monitor() const; diff --git a/src/projectloader.cpp b/src/projectloader.cpp index cc6a3ee..73158ab 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -12,7 +12,7 @@ #include "valuemonitormodel.h" #include "listmonitormodel.h" #include "renderedtarget.h" -#include "blocks/penextension.h" +#include "blocks/penblocks.h" using namespace scratchcpprender; using namespace libscratchcpp; @@ -35,7 +35,7 @@ ProjectLoader::ProjectLoader(QObject *parent) : initTimer(); // Register pen blocks - ScratchConfiguration::registerExtension(std::make_shared()); + ScratchConfiguration::registerExtension(std::make_shared()); } ProjectLoader::~ProjectLoader() @@ -381,20 +381,20 @@ void ProjectLoader::deleteClone(SpriteModel *model) void ProjectLoader::addMonitor(Monitor *monitor) { - auto section = monitor->blockSection(); + auto extension = monitor->extension(); - if (!section) + if (!extension) return; MonitorModel *model = nullptr; switch (monitor->mode()) { case Monitor::Mode::List: - model = new ListMonitorModel(section.get()); + model = new ListMonitorModel(extension); break; default: - model = new ValueMonitorModel(section.get()); + model = new ValueMonitorModel(extension); break; } diff --git a/src/valuemonitormodel.cpp b/src/valuemonitormodel.cpp index 1e55a5d..78d8c90 100644 --- a/src/valuemonitormodel.cpp +++ b/src/valuemonitormodel.cpp @@ -16,8 +16,8 @@ ValueMonitorModel::ValueMonitorModel(QObject *parent) : { } -ValueMonitorModel::ValueMonitorModel(IBlockSection *section, QObject *parent) : - MonitorModel(section, parent) +ValueMonitorModel::ValueMonitorModel(IExtension *extension, QObject *parent) : + MonitorModel(extension, parent) { } diff --git a/src/valuemonitormodel.h b/src/valuemonitormodel.h index 8f8b3ff..c256c74 100644 --- a/src/valuemonitormodel.h +++ b/src/valuemonitormodel.h @@ -28,7 +28,7 @@ class ValueMonitorModel : public MonitorModel Q_ENUM(Mode) ValueMonitorModel(QObject *parent = nullptr); - ValueMonitorModel(libscratchcpp::IBlockSection *section, QObject *parent = nullptr); + ValueMonitorModel(libscratchcpp::IExtension *extension, QObject *parent = nullptr); void onValueChanged(const libscratchcpp::VirtualMachine *vm) override; diff --git a/test/blocks/CMakeLists.txt b/test/blocks/CMakeLists.txt index 6d6151d..b3b8888 100644 --- a/test/blocks/CMakeLists.txt +++ b/test/blocks/CMakeLists.txt @@ -15,21 +15,3 @@ target_link_libraries( add_test(pen_blocks_test) gtest_discover_tests(pen_blocks_test) - -# penextension_test -add_executable( - penextension_test - penextension_test.cpp -) - -target_link_libraries( - penextension_test - GTest::gtest_main - GTest::gmock_main - scratchcpp-render - scratchcpprender_mocks - ${QT_LIBS} -) - -add_test(penextension_test) -gtest_discover_tests(penextension_test) diff --git a/test/blocks/pen_blocks_test.cpp b/test/blocks/pen_blocks_test.cpp index 52d9712..8428c82 100644 --- a/test/blocks/pen_blocks_test.cpp +++ b/test/blocks/pen_blocks_test.cpp @@ -20,7 +20,7 @@ using ::testing::Return; class PenBlocksTest : public testing::Test { public: - void SetUp() override { m_section = std::make_unique(); } + void SetUp() override { m_extension = std::make_unique(); } void addValueInput(std::shared_ptr block, const std::string &name, PenBlocks::Inputs id, const Value &value) const { @@ -77,46 +77,41 @@ class PenBlocksTest : public testing::Test return block; } - std::unique_ptr m_section; + std::unique_ptr m_extension; EngineMock m_engineMock; }; TEST_F(PenBlocksTest, Name) { - ASSERT_EQ(m_section->name(), "Pen"); -} - -TEST_F(PenBlocksTest, CategoryVisible) -{ - ASSERT_TRUE(m_section->categoryVisible()); + ASSERT_EQ(m_extension->name(), "pen"); } TEST_F(PenBlocksTest, RegisterBlocks) { // Blocks - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_clear", &PenBlocks::compileClear)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_stamp", &PenBlocks::compileStamp)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_penDown", &PenBlocks::compilePenDown)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_penUp", &PenBlocks::compilePenUp)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenColorToColor", &PenBlocks::compileSetPenColorToColor)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenColorParamBy", &PenBlocks::compileChangePenColorParamBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenColorParamTo", &PenBlocks::compileSetPenColorParamTo)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenSizeBy", &PenBlocks::compileChangePenSizeBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenSizeTo", &PenBlocks::compileSetPenSizeTo)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenShadeBy", &PenBlocks::compileChangePenShadeBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenShadeToNumber", &PenBlocks::compileSetPenShadeToNumber)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenHueBy", &PenBlocks::compileChangePenHueBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenHueToNumber", &PenBlocks::compileSetPenHueToNumber)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_clear", &PenBlocks::compileClear)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_stamp", &PenBlocks::compileStamp)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_penDown", &PenBlocks::compilePenDown)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_penUp", &PenBlocks::compilePenUp)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenColorToColor", &PenBlocks::compileSetPenColorToColor)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenColorParamBy", &PenBlocks::compileChangePenColorParamBy)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenColorParamTo", &PenBlocks::compileSetPenColorParamTo)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenSizeBy", &PenBlocks::compileChangePenSizeBy)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenSizeTo", &PenBlocks::compileSetPenSizeTo)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenShadeBy", &PenBlocks::compileChangePenShadeBy)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenShadeToNumber", &PenBlocks::compileSetPenShadeToNumber)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenHueBy", &PenBlocks::compileChangePenHueBy)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenHueToNumber", &PenBlocks::compileSetPenHueToNumber)); // Inputs - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "COLOR", PenBlocks::COLOR)); - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "COLOR_PARAM", PenBlocks::COLOR_PARAM)); - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "VALUE", PenBlocks::VALUE)); - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SIZE", PenBlocks::SIZE)); - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SHADE", PenBlocks::SHADE)); - EXPECT_CALL(m_engineMock, addInput(m_section.get(), "HUE", PenBlocks::HUE)); - - m_section->registerBlocks(&m_engineMock); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "COLOR", PenBlocks::COLOR)); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "COLOR_PARAM", PenBlocks::COLOR_PARAM)); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "VALUE", PenBlocks::VALUE)); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "SIZE", PenBlocks::SIZE)); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "SHADE", PenBlocks::SHADE)); + EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "HUE", PenBlocks::HUE)); + + m_extension->registerBlocks(&m_engineMock); } TEST_F(PenBlocksTest, Clear) diff --git a/test/blocks/penextension_test.cpp b/test/blocks/penextension_test.cpp deleted file mode 100644 index 009a497..0000000 --- a/test/blocks/penextension_test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -#include "../common.h" - -using namespace scratchcpprender; -using namespace libscratchcpp; - -using ::testing::WithArgs; -using ::testing::Invoke; -using ::testing::_; - -TEST(PenExtensionTest, Name) -{ - PenExtension ext; - ASSERT_EQ(ext.name(), "pen"); -} - -TEST(PenExtensionTest, Description) -{ - PenExtension ext; - ASSERT_EQ(ext.description(), "Pen extension"); -} - -TEST(PenExtensionTest, IncludeByDefault) -{ - PenExtension ext; - ASSERT_FALSE(ext.includeByDefault()); -} - -TEST(PenExtensionTest, RegisterSections) -{ - PenExtension ext; - EngineMock engine; - - EXPECT_CALL(engine, registerSection(_)).WillOnce(WithArgs<0>(Invoke([](std::shared_ptr section) { ASSERT_TRUE(dynamic_cast(section.get())); }))); - ext.registerSections(&engine); -} diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 07b0cc0..c0c72e8 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -86,18 +86,17 @@ class EngineMock : public IEngine MOCK_METHOD(ITimer *, timer, (), (const, override)); - MOCK_METHOD(void, registerSection, (std::shared_ptr), (override)); MOCK_METHOD(unsigned int, functionIndex, (BlockFunc), (override)); MOCK_METHOD(const std::vector &, blockFunctions, (), (const, override)); - MOCK_METHOD(void, addCompileFunction, (IBlockSection *, const std::string &, BlockComp), (override)); - MOCK_METHOD(void, addHatPredicateCompileFunction, (IBlockSection *, const std::string &, HatPredicateCompileFunc), (override)); - MOCK_METHOD(void, addMonitorNameFunction, (IBlockSection *, const std::string &, MonitorNameFunc), (override)); - MOCK_METHOD(void, addMonitorChangeFunction, (IBlockSection *, const std::string &, MonitorChangeFunc), (override)); - MOCK_METHOD(void, addHatBlock, (IBlockSection *, const std::string &), (override)); - MOCK_METHOD(void, addInput, (IBlockSection *, const std::string &, int), (override)); - MOCK_METHOD(void, addField, (IBlockSection *, const std::string &, int), (override)); - MOCK_METHOD(void, addFieldValue, (IBlockSection *, const std::string &, int), (override)); + MOCK_METHOD(void, addCompileFunction, (IExtension *, const std::string &, BlockComp), (override)); + MOCK_METHOD(void, addHatPredicateCompileFunction, (IExtension *, const std::string &, HatPredicateCompileFunc), (override)); + MOCK_METHOD(void, addMonitorNameFunction, (IExtension *, const std::string &, MonitorNameFunc), (override)); + MOCK_METHOD(void, addMonitorChangeFunction, (IExtension *, const std::string &, MonitorChangeFunc), (override)); + MOCK_METHOD(void, addHatBlock, (IExtension *, const std::string &), (override)); + MOCK_METHOD(void, addInput, (IExtension *, const std::string &, int), (override)); + MOCK_METHOD(void, addField, (IExtension *, const std::string &, int), (override)); + MOCK_METHOD(void, addFieldValue, (IExtension *, const std::string &, int), (override)); MOCK_METHOD(const std::vector> &, broadcasts, (), (const, override)); MOCK_METHOD(void, setBroadcasts, (const std::vector> &), (override)); diff --git a/test/mocks/blocksectionmock.h b/test/mocks/extensionmock.h similarity index 58% rename from test/mocks/blocksectionmock.h rename to test/mocks/extensionmock.h index 1963f11..9c5e9a2 100644 --- a/test/mocks/blocksectionmock.h +++ b/test/mocks/extensionmock.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include using namespace libscratchcpp; @@ -8,12 +8,14 @@ using namespace libscratchcpp; namespace scratchcpprender { -class BlockSectionMock : public IBlockSection +class ExtensionMock : public IExtension { public: MOCK_METHOD(std::string, name, (), (const, override)); - MOCK_METHOD(bool, categoryVisible, (), (const, override)); + MOCK_METHOD(std::string, description, (), (const, override)); + MOCK_METHOD(void, registerBlocks, (IEngine * engine), (override)); + MOCK_METHOD(void, onInit, (IEngine * engine), (override)); }; } // namespace scratchcpprender diff --git a/test/monitor_models/monitormodel_test.cpp b/test/monitor_models/monitormodel_test.cpp index 4b47e90..4efe629 100644 --- a/test/monitor_models/monitormodel_test.cpp +++ b/test/monitor_models/monitormodel_test.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include "../common.h" @@ -98,47 +98,47 @@ TEST(MonitorModelTest, Color) ASSERT_EQ(model.color(), Qt::green); } - BlockSectionMock section; + ExtensionMock extension; { // Invalid - EXPECT_CALL(section, name()).WillOnce(Return("")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), Qt::green); } { // Motion - EXPECT_CALL(section, name()).WillOnce(Return("Motion")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("Motion")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), QColor::fromString("#4C97FF")); } { // Looks - EXPECT_CALL(section, name()).WillOnce(Return("Looks")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("Looks")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), QColor::fromString("#9966FF")); } { // Sound - EXPECT_CALL(section, name()).WillOnce(Return("Sound")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("Sound")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), QColor::fromString("#CF63CF")); } { // Variables - EXPECT_CALL(section, name()).WillOnce(Return("Variables")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("Variables")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), QColor::fromString("#FF8C1A")); } { // Lists - EXPECT_CALL(section, name()).WillOnce(Return("Lists")); - MonitorModel model(§ion); + EXPECT_CALL(extension, name()).WillOnce(Return("Lists")); + MonitorModel model(&extension); ASSERT_EQ(model.color(), QColor::fromString("#FF661A")); } } diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index c58fa50..db430a3 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -77,7 +77,7 @@ TEST_F(ProjectLoaderTest, Constructors) ASSERT_EQ(loader2.parent(), &loader1); // Pen extension should be registered - ASSERT_TRUE(dynamic_cast(ScratchConfiguration::getExtension("pen"))); + ASSERT_TRUE(dynamic_cast(ScratchConfiguration::getExtension("pen"))); } TEST_F(ProjectLoaderTest, Load) From 4610e766656f30145f612a69a50e0de547b55fe5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:17:51 +0200 Subject: [PATCH 29/40] fix #146: Ignore color effects in textureContainsPoint() --- src/cputexturemanager.cpp | 21 ++++++++++++++++++++- test/texture/cputexturemanager_test.cpp | 6 ------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cputexturemanager.cpp b/src/cputexturemanager.cpp index f8daf67..2174cf4 100644 --- a/src/cputexturemanager.cpp +++ b/src/cputexturemanager.cpp @@ -81,7 +81,26 @@ QRgb CpuTextureManager::getPointColor(const Texture &texture, int x, int y, cons bool CpuTextureManager::textureContainsPoint(const Texture &texture, const QPointF &localPoint, const std::unordered_map &effects) { // https://github.com/scratchfoundation/scratch-render/blob/7b823985bc6fe92f572cc3276a8915e550f7c5e6/src/Silhouette.js#L219-L226 - return qAlpha(getPointColor(texture, localPoint.x(), localPoint.y(), effects)) > 0; + const int width = texture.width(); + const int height = texture.height(); + int x = localPoint.x(); + int y = localPoint.y(); + + if (!effects.empty()) { + // Get local position with effect transform + QVector2D transformedCoords; + const QVector2D localCoords(x / static_cast(width), y / static_cast(height)); + EffectTransform::transformPoint(effects, localCoords, transformedCoords); + x = transformedCoords.x() * width; + y = transformedCoords.y() * height; + } + + if ((x < 0 || x >= width) || (y < 0 || y >= height)) + return false; + + GLubyte *pixels = getTextureData(texture); + QRgb color = qRgba(pixels[(y * width + x) * 4], pixels[(y * width + x) * 4 + 1], pixels[(y * width + x) * 4 + 2], pixels[(y * width + x) * 4 + 3]); + return qAlpha(color) > 0; } void CpuTextureManager::removeTexture(const Texture &texture) diff --git a/test/texture/cputexturemanager_test.cpp b/test/texture/cputexturemanager_test.cpp index 436eb32..e2634fa 100644 --- a/test/texture/cputexturemanager_test.cpp +++ b/test/texture/cputexturemanager_test.cpp @@ -209,12 +209,6 @@ TEST_F(CpuTextureManagerTest, TextureContainsPoint) ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 3 }, {})); ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, {})); - std::unordered_map effects = { { ShaderManager::Effect::Ghost, 100 } }; - ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 3 }, effects)); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 3 }, effects)); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 3 }, effects)); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, effects)); - // TODO: Test point transform (graphic effects that change shape) // Cleanup From 4badf6a0fa65963c8fd1a827660f89b3b6e51de8 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:28:24 +0200 Subject: [PATCH 30/40] Update libscratchcpp to v0.11.0 --- libscratchcpp | 2 +- src/graphicseffect.cpp | 15 ++++++ src/graphicseffect.h | 2 + src/renderedtarget.cpp | 3 -- src/spritemodel.cpp | 56 +++++++++++---------- src/spritemodel.h | 3 -- src/stagemodel.cpp | 56 +++++++++++---------- src/stagemodel.h | 3 -- test/graphicseffect/graphicseffect_test.cpp | 29 +++++++++++ test/mocks/enginemock.h | 16 +++--- test/renderedtarget/renderedtarget_test.cpp | 3 +- test/target_models/spritemodel_test.cpp | 19 ++++--- test/target_models/stagemodel_test.cpp | 23 +++++---- 13 files changed, 140 insertions(+), 90 deletions(-) diff --git a/libscratchcpp b/libscratchcpp index 572a2ea..bc5e5a8 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 572a2ea490558eb6b8c7446975f25216ec900b1f +Subproject commit bc5e5a8d7cb25148affaf008096dc71167e6328b diff --git a/src/graphicseffect.cpp b/src/graphicseffect.cpp index cb07d26..e78384c 100644 --- a/src/graphicseffect.cpp +++ b/src/graphicseffect.cpp @@ -4,6 +4,11 @@ using namespace scratchcpprender; +static std::unordered_map> EFFECT_RANGE = { + { ShaderManager::Effect::Ghost, { 0, 100 } }, + { ShaderManager::Effect::Brightness, { -100, 100 } } +}; + GraphicsEffect::GraphicsEffect(ShaderManager::Effect effect, const std::string &name) : m_effect(effect), m_name(name) @@ -19,3 +24,13 @@ std::string GraphicsEffect::name() const { return m_name; } + + +double GraphicsEffect::clamp(double value) const +{ + // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L523-L538 + if(m_effect == ShaderManager::Effect::Ghost || m_effect == ShaderManager::Effect::Brightness) + return std::clamp(value, EFFECT_RANGE[m_effect].first, EFFECT_RANGE[m_effect].second); + + return value; +} diff --git a/src/graphicseffect.h b/src/graphicseffect.h index 2719fca..17a4305 100644 --- a/src/graphicseffect.h +++ b/src/graphicseffect.h @@ -17,6 +17,8 @@ class GraphicsEffect : public libscratchcpp::IGraphicsEffect ShaderManager::Effect effect() const; std::string name() const override; + double clamp(double value) const override; + private: ShaderManager::Effect m_effect = static_cast(0); std::string m_name; diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 70dc369..348aabf 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -503,9 +503,6 @@ void RenderedTarget::mouseMoveEvent(QMouseEvent *event) m_dragDeltaX = m_engine->mouseX() - sprite->x(); m_dragDeltaY = m_engine->mouseY() - sprite->y(); m_mouseArea->setDraggedSprite(this); - - // Move the sprite to the front layer - m_engine->moveSpriteToFront(sprite); } } diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 19ec382..4207d39 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "spritemodel.h" #include "renderedtarget.h" @@ -19,7 +20,35 @@ SpriteModel::SpriteModel(QObject *parent) : void SpriteModel::init(libscratchcpp::Sprite *sprite) { + if (!sprite) + return; + m_sprite = sprite; + + m_sprite->bubble()->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { + if (type == libscratchcpp::TextBubble::Type::Say) { + if (m_bubbleType == TextBubbleShape::Type::Say) + return; + + m_bubbleType = TextBubbleShape::Type::Say; + } else { + if (m_bubbleType == TextBubbleShape::Type::Think) + return; + + m_bubbleType = TextBubbleShape::Type::Think; + } + + emit bubbleTypeChanged(); + }); + + m_sprite->bubble()->textChanged().connect([this](const std::string &text) { + QString newText = QString::fromStdString(text); + + if (m_bubbleText != newText) { + m_bubbleText = newText; + emit bubbleTextChanged(); + } + }); } void SpriteModel::deinitClone() @@ -113,33 +142,6 @@ void SpriteModel::onGraphicsEffectsCleared() m_renderedTarget->clearGraphicEffects(); } -void SpriteModel::onBubbleTypeChanged(libscratchcpp::Target::BubbleType type) -{ - if (type == libscratchcpp::Target::BubbleType::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); -} - -void SpriteModel::onBubbleTextChanged(const std::string &text) -{ - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } -} - int SpriteModel::costumeWidth() const { return m_renderedTarget->costumeWidth(); diff --git a/src/spritemodel.h b/src/spritemodel.h index c7a1354..2c5ef96 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -51,9 +51,6 @@ class SpriteModel void onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) override; void onGraphicsEffectsCleared() override; - void onBubbleTypeChanged(libscratchcpp::Target::BubbleType type) override; - void onBubbleTextChanged(const std::string &text) override; - int costumeWidth() const override; int costumeHeight() const override; diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 972feaf..73481e9 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include +#include #include "stagemodel.h" #include "renderedtarget.h" @@ -15,7 +16,35 @@ StageModel::StageModel(QObject *parent) : void StageModel::init(libscratchcpp::Stage *stage) { + if (!stage) + return; + m_stage = stage; + + m_stage->bubble()->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { + if (type == libscratchcpp::TextBubble::Type::Say) { + if (m_bubbleType == TextBubbleShape::Type::Say) + return; + + m_bubbleType = TextBubbleShape::Type::Say; + } else { + if (m_bubbleType == TextBubbleShape::Type::Think) + return; + + m_bubbleType = TextBubbleShape::Type::Think; + } + + emit bubbleTypeChanged(); + }); + + m_stage->bubble()->textChanged().connect([this](const std::string &text) { + QString newText = QString::fromStdString(text); + + if (m_bubbleText != newText) { + m_bubbleText = newText; + emit bubbleTextChanged(); + } + }); } void StageModel::onCostumeChanged(libscratchcpp::Costume *costume) @@ -50,33 +79,6 @@ void StageModel::onGraphicsEffectsCleared() m_renderedTarget->clearGraphicEffects(); } -void StageModel::onBubbleTypeChanged(libscratchcpp::Target::BubbleType type) -{ - if (type == libscratchcpp::Target::BubbleType::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); -} - -void StageModel::onBubbleTextChanged(const std::string &text) -{ - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } -} - int StageModel::costumeWidth() const { return m_renderedTarget->costumeWidth(); diff --git a/src/stagemodel.h b/src/stagemodel.h index 326face..af7693d 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -37,9 +37,6 @@ class StageModel void onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) override; void onGraphicsEffectsCleared() override; - void onBubbleTypeChanged(libscratchcpp::Target::BubbleType type) override; - void onBubbleTextChanged(const std::string &text) override; - int costumeWidth() const override; int costumeHeight() const override; diff --git a/test/graphicseffect/graphicseffect_test.cpp b/test/graphicseffect/graphicseffect_test.cpp index 7ea7338..bdd7238 100644 --- a/test/graphicseffect/graphicseffect_test.cpp +++ b/test/graphicseffect/graphicseffect_test.cpp @@ -27,3 +27,32 @@ TEST(GraphicsEffectTest, Constructor) ASSERT_EQ(iface->name(), "brightness"); } } + +TEST(GraphicsEffectTest, Clamp) +{ + { + GraphicsEffect effect(ShaderManager::Effect::Color, "color"); + ASSERT_EQ(effect.clamp(-500), -500); + ASSERT_EQ(effect.clamp(0), 0); + ASSERT_EQ(effect.clamp(500), 500); + } + + { + GraphicsEffect effect(ShaderManager::Effect::Brightness, "brightness"); + ASSERT_EQ(effect.clamp(-125), -100); + ASSERT_EQ(effect.clamp(-100), -100); + ASSERT_EQ(effect.clamp(0), 0); + ASSERT_EQ(effect.clamp(100), 100); + ASSERT_EQ(effect.clamp(125), 100); + } + + { + GraphicsEffect effect(ShaderManager::Effect::Ghost, "ghost"); + ASSERT_EQ(effect.clamp(-50), 0); + ASSERT_EQ(effect.clamp(0), 0); + ASSERT_EQ(effect.clamp(100), 100); + ASSERT_EQ(effect.clamp(125), 100); + } + + // TODO: Test remaining effects +} diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index c0c72e8..5a9714e 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -106,10 +106,10 @@ class EngineMock : public IEngine MOCK_METHOD(void, addWhenTouchingObjectScript, (std::shared_ptr), (override)); MOCK_METHOD(void, addGreenFlagScript, (std::shared_ptr), (override)); - MOCK_METHOD(void, addBroadcastScript, (std::shared_ptr, int, Broadcast *), (override)); - MOCK_METHOD(void, addBackdropChangeScript, (std::shared_ptr, int), (override)); + MOCK_METHOD(void, addBroadcastScript, (std::shared_ptr, Field *, Broadcast *), (override)); + MOCK_METHOD(void, addBackdropChangeScript, (std::shared_ptr, Field *), (override)); MOCK_METHOD(void, addCloneInitScript, (std::shared_ptr), (override)); - MOCK_METHOD(void, addKeyPressScript, (std::shared_ptr, int), (override)); + MOCK_METHOD(void, addKeyPressScript, (std::shared_ptr, Field *), (override)); MOCK_METHOD(void, addTargetClickScript, (std::shared_ptr), (override)); MOCK_METHOD(void, addWhenGreaterThanScript, (std::shared_ptr), (override)); @@ -119,11 +119,11 @@ class EngineMock : public IEngine MOCK_METHOD(void, getVisibleTargets, (std::vector &), (const, override)); MOCK_METHOD(int, findTarget, (const std::string &), (const, override)); - MOCK_METHOD(void, moveSpriteToFront, (Sprite * sprite), (override)); - MOCK_METHOD(void, moveSpriteToBack, (Sprite * sprite), (override)); - MOCK_METHOD(void, moveSpriteForwardLayers, (Sprite * sprite, int layers), (override)); - MOCK_METHOD(void, moveSpriteBackwardLayers, (Sprite * sprite, int layers), (override)); - MOCK_METHOD(void, moveSpriteBehindOther, (Sprite * sprite, Sprite *other), (override)); + MOCK_METHOD(void, moveDrawableToFront, (Drawable *), (override)); + MOCK_METHOD(void, moveDrawableToBack, (Drawable *), (override)); + MOCK_METHOD(void, moveDrawableForwardLayers, (Drawable *, int), (override)); + MOCK_METHOD(void, moveDrawableBackwardLayers, (Drawable *, int), (override)); + MOCK_METHOD(void, moveDrawableBehindOther, (Drawable *, Drawable *), (override)); MOCK_METHOD(Stage *, stage, (), (const, override)); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index f3f9b9a..8a9e6a5 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -568,7 +568,6 @@ TEST_F(RenderedTargetTest, SpriteDragging) QCoreApplication::sendEvent(&target, &pressEvent); EXPECT_CALL(engine, mouseX()).WillOnce(Return(67.95)); EXPECT_CALL(engine, mouseY()).WillOnce(Return(2.1)); - EXPECT_CALL(engine, moveSpriteToFront(&sprite)); QCoreApplication::sendEvent(&target, &moveEvent); ASSERT_EQ(sprite.x(), 64.08); ASSERT_EQ(sprite.y(), -6.86); @@ -1090,7 +1089,7 @@ TEST_F(RenderedTargetTest, TouchingColor) EXPECT_CALL(engine, cloneLimit()).WillOnce(Return(-1)); EXPECT_CALL(engine, initClone); EXPECT_CALL(engine, requestRedraw); - EXPECT_CALL(engine, moveSpriteBehindOther); + EXPECT_CALL(engine, moveDrawableBehindOther); auto sprite2 = sprite1->clone(); StageModel stageModel; SpriteModel model, model1, model2; diff --git a/test/target_models/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index 8ec74b9..7abf6e8 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -248,22 +249,24 @@ TEST(SpriteModelTest, OnGraphicsEffectsCleared) TEST(SpriteModelTest, OnBubbleTypeChanged) { SpriteModel model; + Sprite sprite; + model.init(&sprite); QSignalSpy spy(&model, &SpriteModel::bubbleTypeChanged); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); - model.onBubbleTypeChanged(Target::BubbleType::Think); + sprite.bubble()->setType(TextBubble::Type::Think); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Think); ASSERT_EQ(spy.count(), 1); - model.onBubbleTypeChanged(Target::BubbleType::Think); + sprite.bubble()->setType(TextBubble::Type::Think); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Think); ASSERT_EQ(spy.count(), 1); - model.onBubbleTypeChanged(Target::BubbleType::Say); + sprite.bubble()->setType(TextBubble::Type::Say); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); ASSERT_EQ(spy.count(), 2); - model.onBubbleTypeChanged(Target::BubbleType::Say); + sprite.bubble()->setType(TextBubble::Type::Say); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); ASSERT_EQ(spy.count(), 2); } @@ -271,18 +274,20 @@ TEST(SpriteModelTest, OnBubbleTypeChanged) TEST(SpriteModelTest, OnBubbleTextChanged) { SpriteModel model; + Sprite sprite; + model.init(&sprite); QSignalSpy spy(&model, &SpriteModel::bubbleTextChanged); ASSERT_TRUE(model.bubbleText().isEmpty()); - model.onBubbleTextChanged("Hello!"); + sprite.bubble()->setText("Hello!"); ASSERT_EQ(model.bubbleText(), "Hello!"); ASSERT_EQ(spy.count(), 1); - model.onBubbleTextChanged("Hello!"); + sprite.bubble()->setText("Hello!"); ASSERT_EQ(model.bubbleText(), "Hello!"); ASSERT_EQ(spy.count(), 1); - model.onBubbleTextChanged("test"); + sprite.bubble()->setText("test"); ASSERT_EQ(model.bubbleText(), "test"); ASSERT_EQ(spy.count(), 2); } diff --git a/test/target_models/stagemodel_test.cpp b/test/target_models/stagemodel_test.cpp index a6d4d37..da21e43 100644 --- a/test/target_models/stagemodel_test.cpp +++ b/test/target_models/stagemodel_test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -70,44 +71,48 @@ TEST(StageModelTest, OnGraphicsEffectsCleared) model.onGraphicsEffectsCleared(); } -TEST(StageModelTest, OnBubbleTypeChanged) +TEST(StageModelTest, BubbleTypeChange) { StageModel model; + Stage stage; + model.init(&stage); QSignalSpy spy(&model, &StageModel::bubbleTypeChanged); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); - model.onBubbleTypeChanged(Target::BubbleType::Think); + stage.bubble()->setType(TextBubble::Type::Think); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Think); ASSERT_EQ(spy.count(), 1); - model.onBubbleTypeChanged(Target::BubbleType::Think); + stage.bubble()->setType(TextBubble::Type::Think); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Think); ASSERT_EQ(spy.count(), 1); - model.onBubbleTypeChanged(Target::BubbleType::Say); + stage.bubble()->setType(TextBubble::Type::Say); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); ASSERT_EQ(spy.count(), 2); - model.onBubbleTypeChanged(Target::BubbleType::Say); + stage.bubble()->setType(TextBubble::Type::Say); ASSERT_EQ(model.bubbleType(), TextBubbleShape::Type::Say); ASSERT_EQ(spy.count(), 2); } -TEST(StageModelTest, OnBubbleTextChanged) +TEST(StageModelTest, BubbleTextChange) { StageModel model; + Stage stage; + model.init(&stage); QSignalSpy spy(&model, &StageModel::bubbleTextChanged); ASSERT_TRUE(model.bubbleText().isEmpty()); - model.onBubbleTextChanged("Hello!"); + stage.bubble()->setText("Hello!"); ASSERT_EQ(model.bubbleText(), "Hello!"); ASSERT_EQ(spy.count(), 1); - model.onBubbleTextChanged("Hello!"); + stage.bubble()->setText("Hello!"); ASSERT_EQ(model.bubbleText(), "Hello!"); ASSERT_EQ(spy.count(), 1); - model.onBubbleTextChanged("test"); + stage.bubble()->setText("test"); ASSERT_EQ(model.bubbleText(), "test"); ASSERT_EQ(spy.count(), 2); } From 331571f8fc57c0289df2c616230b86f4c0079038 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:50:39 +0200 Subject: [PATCH 31/40] Temporarily force Qt 6.7.2 --- .github/workflows/build.yml | 2 +- .github/workflows/utests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c228c0c..19a752b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: include: - qt_version: '6.6' qt_arch: 'gcc_64' - - qt_version: '6.7' + - qt_version: '6.7.2' qt_arch: 'linux_gcc_64' runs-on: ubuntu-latest diff --git a/.github/workflows/utests.yml b/.github/workflows/utests.yml index bc84c7b..616004e 100644 --- a/.github/workflows/utests.yml +++ b/.github/workflows/utests.yml @@ -16,7 +16,7 @@ jobs: include: - qt_version: '6.6' qt_arch: 'gcc_64' - - qt_version: '6.7' + - qt_version: '6.7.2' qt_arch: 'linux_gcc_64' runs-on: ubuntu-latest From 6e98e9352b6eb05dc17b739440e8392e0b271ba4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:52:35 +0200 Subject: [PATCH 32/40] MonitorModel: Only show positioned monitors --- src/monitormodel.cpp | 2 +- test/monitor_models/monitormodel_test.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/monitormodel.cpp b/src/monitormodel.cpp index ec2fc24..3233f54 100644 --- a/src/monitormodel.cpp +++ b/src/monitormodel.cpp @@ -49,7 +49,7 @@ QString MonitorModel::name() const bool MonitorModel::visible() const { if (m_monitor) - return m_monitor->visible(); + return m_monitor->visible() && !m_monitor->needsAutoPosition(); else return false; } diff --git a/test/monitor_models/monitormodel_test.cpp b/test/monitor_models/monitormodel_test.cpp index 4efe629..0c5acf6 100644 --- a/test/monitor_models/monitormodel_test.cpp +++ b/test/monitor_models/monitormodel_test.cpp @@ -71,6 +71,9 @@ TEST(MonitorModelTest, Visible) Monitor monitor("", ""); monitor.setVisible(true); model.init(&monitor); + ASSERT_FALSE(model.visible()); + + monitor.autoPosition({}); ASSERT_TRUE(model.visible()); monitor.setVisible(false); From e8a397b6b560038aeb3c318eaa4b823a58081a9d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:17:15 +0200 Subject: [PATCH 33/40] MonitorModel: Emit signals in position change methods --- src/monitormodel.cpp | 4 ++++ test/monitor_models/monitormodel_test.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/monitormodel.cpp b/src/monitormodel.cpp index 3233f54..60bd8db 100644 --- a/src/monitormodel.cpp +++ b/src/monitormodel.cpp @@ -71,10 +71,14 @@ void MonitorModel::onVisibleChanged(bool visible) void MonitorModel::onXChanged(int x) { + emit xChanged(); + emit visibleChanged(); } void MonitorModel::onYChanged(int y) { + emit yChanged(); + emit visibleChanged(); } libscratchcpp::Monitor *MonitorModel::monitor() const diff --git a/test/monitor_models/monitormodel_test.cpp b/test/monitor_models/monitormodel_test.cpp index 0c5acf6..7d2a57c 100644 --- a/test/monitor_models/monitormodel_test.cpp +++ b/test/monitor_models/monitormodel_test.cpp @@ -156,6 +156,13 @@ TEST(MonitorModelTest, X) monitor.setX(-2); ASSERT_EQ(model.x(), -2); + + QSignalSpy xSpy(&model, &MonitorModel::xChanged); + QSignalSpy visibleSpy(&model, &MonitorModel::visibleChanged); + + model.onXChanged(-2); + ASSERT_EQ(xSpy.count(), 1); + ASSERT_EQ(visibleSpy.count(), 1); } TEST(MonitorModelTest, Y) @@ -168,6 +175,14 @@ TEST(MonitorModelTest, Y) monitor.setY(-8); ASSERT_EQ(model.y(), -8); + + QSignalSpy ySpy(&model, &MonitorModel::yChanged); + QSignalSpy visibleSpy(&model, &MonitorModel::visibleChanged); + + model.onYChanged(-8); + ASSERT_EQ(model.y(), -8); + ASSERT_EQ(ySpy.count(), 1); + ASSERT_EQ(visibleSpy.count(), 1); } TEST(MonitorModelTest, Width) From 849286829bd5657c5d7bbfc65f79c02da3622159 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:23:07 +0200 Subject: [PATCH 34/40] ProjectLoader: Auto-position new monitors --- src/projectloader.cpp | 11 +++++++++++ src/projectloader.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/projectloader.cpp b/src/projectloader.cpp index 73158ab..85a53ee 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -227,6 +227,11 @@ void ProjectLoader::timerEvent(QTimerEvent *event) return; if (m_engine) { + for (Monitor *monitor : m_unpositionedMonitors) + monitor->autoPosition(m_engine->monitors()); + + m_unpositionedMonitors.clear(); + m_engine->step(); if (m_running != m_engine->isRunning()) { @@ -245,6 +250,7 @@ void ProjectLoader::callLoad(ProjectLoader *loader) void ProjectLoader::load() { + m_unpositionedMonitors.clear(); m_loadStatus = m_project.load(); m_engineMutex.lock(); m_engine = m_project.engine().get(); @@ -401,6 +407,11 @@ void ProjectLoader::addMonitor(Monitor *monitor) if (!model) return; + if (monitor->needsAutoPosition()) { + if (std::find(m_unpositionedMonitors.begin(), m_unpositionedMonitors.end(), monitor) == m_unpositionedMonitors.end()) + m_unpositionedMonitors.push_back(monitor); + } + model->moveToThread(qApp->thread()); model->setParent(this); monitor->setInterface(model); diff --git a/src/projectloader.h b/src/projectloader.h index a645afa..21f95e6 100644 --- a/src/projectloader.h +++ b/src/projectloader.h @@ -151,6 +151,7 @@ class ProjectLoader : public QObject QList m_sprites; QList m_clones; QList m_monitors; + std::vector m_unpositionedMonitors; QStringList m_unsupportedBlocks; double m_fps = 30; bool m_turboMode = false; From 49764d802b2d4ce65a9b1c9d3ca2cdee08cbe683 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:35:33 +0200 Subject: [PATCH 35/40] Add bubbleLayer property to SpriteModel --- src/spritemodel.cpp | 5 +++++ src/spritemodel.h | 4 ++++ test/target_models/spritemodel_test.cpp | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 4207d39..9aca0db 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -261,4 +261,9 @@ const QString &SpriteModel::bubbleText() const return m_bubbleText; } +int SpriteModel::bubbleLayer() const +{ + return m_sprite ? m_sprite->bubble()->layerOrder() : 0; +} + } // namespace scratchcpprender diff --git a/src/spritemodel.h b/src/spritemodel.h index 2c5ef96..e54c47c 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -28,6 +28,7 @@ class SpriteModel Q_PROPERTY(IPenLayer *penLayer READ penLayer WRITE setPenLayer NOTIFY penLayerChanged) Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) + Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: SpriteModel(QObject *parent = nullptr); @@ -82,6 +83,8 @@ class SpriteModel const QString &bubbleText() const; + int bubbleLayer() const; + signals: void renderedTargetChanged(); void penLayerChanged(); @@ -89,6 +92,7 @@ class SpriteModel void bubbleTextChanged(); void cloned(SpriteModel *cloneModel); void cloneDeleted(SpriteModel *clone); + void bubbleLayerChanged(); private: libscratchcpp::Sprite *m_sprite = nullptr; diff --git a/test/target_models/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index 7abf6e8..3b1a776 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -466,3 +466,15 @@ TEST(SpriteModelTest, PenDown) ASSERT_FALSE(model.penDown()); ASSERT_FALSE(model.penState().penDown); } + +TEST(SpriteModelTest, BubbleLayer) +{ + SpriteModel model; + Sprite sprite; + model.init(&sprite); + QSignalSpy spy(&model, &SpriteModel::bubbleLayerChanged); + + sprite.bubble()->setLayerOrder(5); + ASSERT_EQ(model.bubbleLayer(), 5); + // TODO: Use spy here +} From 45c4c6670b376b0a6329898b4d78de34bb72b506 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:35:46 +0200 Subject: [PATCH 36/40] Add bubbleLayer property to StageModel --- src/stagemodel.cpp | 5 +++++ src/stagemodel.h | 4 ++++ test/target_models/stagemodel_test.cpp | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 73481e9..31f8f58 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -154,3 +154,8 @@ const QString &StageModel::bubbleText() const { return m_bubbleText; } + +int StageModel::bubbleLayer() const +{ + return m_stage ? m_stage->bubble()->layerOrder() : 0; +} diff --git a/src/stagemodel.h b/src/stagemodel.h index af7693d..269e11c 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -22,6 +22,7 @@ class StageModel Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) + Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: explicit StageModel(QObject *parent = nullptr); @@ -59,10 +60,13 @@ class StageModel const QString &bubbleText() const; + int bubbleLayer() const; + signals: void renderedTargetChanged(); void bubbleTypeChanged(); void bubbleTextChanged(); + void bubbleLayerChanged(); private: libscratchcpp::Stage *m_stage = nullptr; diff --git a/test/target_models/stagemodel_test.cpp b/test/target_models/stagemodel_test.cpp index da21e43..629f758 100644 --- a/test/target_models/stagemodel_test.cpp +++ b/test/target_models/stagemodel_test.cpp @@ -225,3 +225,15 @@ TEST(StageModelTest, RenderedTarget) stage.setCostumeIndex(2); model.loadCostume(); } + +TEST(StageModelTest, BubbleLayer) +{ + StageModel model; + Stage stage; + model.init(&stage); + QSignalSpy spy(&model, &StageModel::bubbleLayerChanged); + + stage.bubble()->setLayerOrder(5); + ASSERT_EQ(model.bubbleLayer(), 5); + // TODO: Use spy here +} From 09362edc2f2e5306352c6c6038a64f0273cd2b05 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:36:21 +0200 Subject: [PATCH 37/40] ProjectPlayer: Move text bubbles out of targets --- src/ProjectPlayer.qml | 55 ++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index b0b200c..86fa183 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -119,6 +119,29 @@ ProjectScene { color: priv.loading || !priv.loaded ? "transparent" : "white" clip: true + Component { + id: renderedTextBubble + + Loader { + property QtObject model: null + active: model ? model.bubbleText !== "" : false + z: model ? model.bubbleLayer : 0 + + sourceComponent: TextBubble { + type: model.bubbleType + text: model.bubbleText + target: model.renderedTarget + stageScale: root.stageScale + stageWidth: root.stageWidth + stageHeight: root.stageHeight + x: target.x + y: target.y + } + + Component.onCompleted: model = modelData + } + } + RenderedTarget { id: stageTarget engine: loader.engine @@ -129,17 +152,9 @@ ProjectScene { } Loader { - readonly property alias model: stageTarget.stageModel + readonly property alias modelData: stageTarget.stageModel active: model ? model.bubbleText !== "" : false - - sourceComponent: TextBubble { - type: model ? model.bubbleType : TextBubbleShape.Say - text: model ? model.bubbleText : "" - target: stageTarget - stageScale: root.stageScale - stageWidth: root.stageWidth - stageHeight: root.stageHeight - } + sourceComponent: renderedTextBubble } PenLayer { @@ -220,20 +235,6 @@ ProjectScene { Component.onCompleted: transform = targetItem.transform[0] } }*/ - - Loader { - readonly property alias model: targetItem.spriteModel - active: model ? model.bubbleText !== "" : false - - sourceComponent: TextBubble { - type: model ? model.bubbleType : TextBubbleShape.Say - text: model ? model.bubbleText : "" - target: targetItem - stageScale: root.stageScale - stageWidth: root.stageWidth - stageHeight: root.stageHeight - } - } } } @@ -249,6 +250,12 @@ ProjectScene { delegate: renderedSprite } + Repeater { + id: textBubbles + model: loader.sprites + delegate: renderedTextBubble + } + SceneMouseArea { id: sceneMouseArea anchors.fill: parent From 5bde065c32e7b66f57bc029d867150c6e98c84cc Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:36:40 +0200 Subject: [PATCH 38/40] Set width and height of monitor models --- src/internal/ListMonitor.qml | 2 ++ src/internal/ValueMonitor.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/internal/ListMonitor.qml b/src/internal/ListMonitor.qml index 36e02d4..5195e7e 100644 --- a/src/internal/ListMonitor.qml +++ b/src/internal/ListMonitor.qml @@ -17,6 +17,8 @@ Rectangle { border.color: Qt.rgba(0.765, 0.8, 0.85, 1) radius: 5 visible: model ? model.visible : true + onWidthChanged: if(model) model.width = width + onHeightChanged: if(model) model.height = height QtObject { id: priv diff --git a/src/internal/ValueMonitor.qml b/src/internal/ValueMonitor.qml index 74dd34f..a1bfb5a 100644 --- a/src/internal/ValueMonitor.qml +++ b/src/internal/ValueMonitor.qml @@ -15,6 +15,8 @@ Rectangle { width: layout.implicitWidth + priv.horizontalMargins * 2 height: layout.implicitHeight + priv.verticalMargins * 2 visible: model ? model.visible : true + onWidthChanged: if(model) model.width = width + onHeightChanged: if(model) model.height = height QtObject { id: priv From 8bef51b28fc24d239d6a3d0169898152a524dd87 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:36:55 +0200 Subject: [PATCH 39/40] Fix value monitor margins --- src/internal/ValueMonitor.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/ValueMonitor.qml b/src/internal/ValueMonitor.qml index a1bfb5a..5a46723 100644 --- a/src/internal/ValueMonitor.qml +++ b/src/internal/ValueMonitor.qml @@ -21,7 +21,7 @@ Rectangle { QtObject { id: priv readonly property int horizontalMargins: 9 - readonly property double verticalMargins: 2 + readonly property double verticalMargins: 2.5 readonly property color bgColor: Qt.rgba(0.9, 0.94, 1, 1) } From 594b90d3b92f4e8f9bad44027bbf28f5fda2bb4a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:37:22 +0200 Subject: [PATCH 40/40] ProjectLoader: Clear sprites before loading project --- src/projectloader.cpp | 8 ++++++++ test/projectloader/projectloader_test.cpp | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/projectloader.cpp b/src/projectloader.cpp index 85a53ee..d8c6fea 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -251,6 +251,11 @@ void ProjectLoader::callLoad(ProjectLoader *loader) void ProjectLoader::load() { m_unpositionedMonitors.clear(); + m_sprites.clear(); + m_clones.clear(); + emit spritesChanged(); + emit clonesChanged(); + m_loadStatus = m_project.load(); m_engineMutex.lock(); m_engine = m_project.engine().get(); @@ -262,6 +267,7 @@ void ProjectLoader::load() emit loadingFinished(); emit engineChanged(); emit spritesChanged(); + emit clonesChanged(); return; } @@ -308,6 +314,7 @@ void ProjectLoader::load() emit loadingFinished(); emit engineChanged(); emit spritesChanged(); + emit clonesChanged(); return; } @@ -327,6 +334,7 @@ void ProjectLoader::load() emit engineChanged(); emit stageChanged(); emit spritesChanged(); + emit clonesChanged(); } void ProjectLoader::initTimer() diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index db430a3..5684efa 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -57,8 +57,8 @@ class ProjectLoaderTest : public testing::Test ASSERT_EQ(loadingFinishedSpy.count(), 1); ASSERT_EQ(engineSpy.count(), 2); ASSERT_EQ(stageSpy.count(), 1); - ASSERT_EQ(spritesSpy.count(), 2); - ASSERT_EQ(clonesSpy.count(), 1); + ASSERT_EQ(spritesSpy.count(), 3); + ASSERT_EQ(clonesSpy.count(), 3); ASSERT_EQ(monitorsSpy.count(), loader->monitorList().size() + 1); ASSERT_EQ(monitorAddedSpy.count(), loader->monitorList().size()); ASSERT_EQ(unsupportedBlocksSpy.count(), 1); @@ -137,13 +137,13 @@ TEST_F(ProjectLoaderTest, Clones) load(&loader, "clones.sb3"); ASSERT_TRUE(cloneCreatedSpy.empty()); ASSERT_TRUE(cloneDeletedSpy.empty()); - ASSERT_EQ(clonesChangedSpy.count(), 1); + ASSERT_EQ(clonesChangedSpy.count(), 3); auto engine = loader.engine(); engine->run(); ASSERT_EQ(cloneCreatedSpy.count(), 3); ASSERT_EQ(cloneDeletedSpy.count(), 0); - ASSERT_EQ(clonesChangedSpy.count(), 4); + ASSERT_EQ(clonesChangedSpy.count(), 6); const auto &sprites = loader.spriteList(); const auto &clones = loader.cloneList(); @@ -162,7 +162,7 @@ TEST_F(ProjectLoaderTest, Clones) clones[1]->sprite()->deleteClone(); ASSERT_EQ(cloneCreatedSpy.count(), 3); ASSERT_EQ(cloneDeletedSpy.count(), 1); - ASSERT_EQ(clonesChangedSpy.count(), 5); + ASSERT_EQ(clonesChangedSpy.count(), 7); ASSERT_EQ(clones.size(), 2); }