From affa08fae51ada6702157d88f6296b0f111b0738 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:00:42 +0100 Subject: [PATCH 1/3] Update libscratchcpp to latest master --- libscratchcpp | 2 +- src/spritemodel.cpp | 5 +++++ src/spritemodel.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index 16ab889..2e3d3df 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 16ab889b11c5ab70b4da19eb9b6e1a76afec10e0 +Subproject commit 2e3d3df69476937958a83a01747c0c7c38a48f42 diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 706bed7..997f083 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -145,6 +145,11 @@ libscratchcpp::Rect SpriteModel::boundingRect() const return m_renderedTarget->getBounds(); } +libscratchcpp::Rect SpriteModel::fastBoundingRect() const +{ + return libscratchcpp::Rect(m_sprite->x(), m_sprite->y(), m_sprite->x(), m_sprite->y()); +} + libscratchcpp::Sprite *SpriteModel::sprite() const { return m_sprite; diff --git a/src/spritemodel.h b/src/spritemodel.h index cb4903e..e6669c5 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -55,6 +55,7 @@ class SpriteModel void onBubbleTextChanged(const std::string &text) override; libscratchcpp::Rect boundingRect() const override; + libscratchcpp::Rect fastBoundingRect() const override; libscratchcpp::Sprite *sprite() const; From 1601a0c4ba27e5b9687fb4066e2d10f6ae931356 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:37:13 +0100 Subject: [PATCH 2/3] Add getFastBounds() method to RenderedTarget --- src/irenderedtarget.h | 1 + src/renderedtarget.cpp | 33 ++++++++++ src/renderedtarget.h | 1 + test/mocks/renderedtargetmock.h | 1 + test/renderedtarget/renderedtarget_test.cpp | 72 +++++++++++++++++++++ 5 files changed, 108 insertions(+) diff --git a/src/irenderedtarget.h b/src/irenderedtarget.h index 6d70c57..5c26352 100644 --- a/src/irenderedtarget.h +++ b/src/irenderedtarget.h @@ -71,6 +71,7 @@ class IRenderedTarget : public QNanoQuickItem virtual libscratchcpp::Rect getBounds() const = 0; virtual QRectF getBoundsForBubble() const = 0; + virtual libscratchcpp::Rect getFastBounds() const = 0; virtual QPointF mapFromScene(const QPointF &point) const = 0; diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 7467265..067e420 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -383,6 +383,39 @@ QRectF RenderedTarget::getBoundsForBubble() const return QRectF(QPointF(rect.left(), rect.top()), QPointF(rect.right(), rect.bottom())); } +Rect RenderedTarget::getFastBounds() const +{ + if (!m_costume || !m_skin || !m_texture.isValid()) + return Rect(m_x, m_y, m_x, m_y); + + const double scale = this->scale(); + const double width = this->width() / m_stageScale; + const double height = this->height() / m_stageScale; + const double originX = m_costume->rotationCenterX() * m_size / scale / m_costume->bitmapResolution() - width / 2; + const double originY = -m_costume->rotationCenterY() * m_size / scale / m_costume->bitmapResolution() + height / 2; + const double rot = -rotation() * pi / 180; + + QPointF topLeft = transformPoint(-width / 2, height / 2, originX, originY, rot); + QPointF topRight = transformPoint(width / 2, height / 2, originX, originY, rot); + QPointF bottomRight = transformPoint(width / 2, -height / 2, originX, originY, rot); + QPointF bottomLeft = transformPoint(-width / 2, -height / 2, originX, originY, rot); + + if (m_mirrorHorizontally) { + topLeft.setX(-topLeft.x()); + topRight.setX(-topRight.x()); + bottomRight.setX(-bottomRight.x()); + bottomLeft.setX(-bottomLeft.x()); + } + const auto xList = { topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x() }; + const auto yList = { topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y() }; + const double minX = std::min(xList); + const double maxX = std::max(xList); + const double minY = std::min(yList); + const double maxY = std::max(yList); + + return Rect(minX * scale + m_x, maxY * scale + m_y, maxX * scale + m_x, minY * scale + m_y); +} + QPointF RenderedTarget::mapFromScene(const QPointF &point) const { return QNanoQuickItem::mapFromScene(point); diff --git a/src/renderedtarget.h b/src/renderedtarget.h index 7d80182..75ed7af 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -76,6 +76,7 @@ class RenderedTarget : public IRenderedTarget libscratchcpp::Rect getBounds() const override; Q_INVOKABLE QRectF getBoundsForBubble() const override; + libscratchcpp::Rect getFastBounds() const override; QPointF mapFromScene(const QPointF &point) const override; diff --git a/test/mocks/renderedtargetmock.h b/test/mocks/renderedtargetmock.h index aa31376..695253e 100644 --- a/test/mocks/renderedtargetmock.h +++ b/test/mocks/renderedtargetmock.h @@ -56,6 +56,7 @@ class RenderedTargetMock : public IRenderedTarget MOCK_METHOD(QPointF, mapFromScene, (const QPointF &), (const, override)); MOCK_METHOD(libscratchcpp::Rect, getBounds, (), (const, override)); + MOCK_METHOD(libscratchcpp::Rect, getFastBounds, (), (const, override)); MOCK_METHOD(QRectF, getBoundsForBubble, (), (const, override)); MOCK_METHOD(bool, mirrorHorizontally, (), (const, override)); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 5c2dd07..b595ba8 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -740,3 +740,75 @@ TEST_F(RenderedTargetTest, GetBounds) context.doneCurrent(); } + +TEST_F(RenderedTargetTest, GetFastBounds) +{ + QOpenGLContext context; + QOffscreenSurface surface; + createContextAndSurface(&context, &surface); + QOpenGLExtraFunctions glF(&context); + glF.initializeOpenGLFunctions(); + RenderedTarget target; + + Sprite sprite; + sprite.setX(75.64); + sprite.setY(-120.3); + sprite.setDirection(-46.37); + sprite.setSize(67.98); + SpriteModel spriteModel; + sprite.setInterface(&spriteModel); + target.setSpriteModel(&spriteModel); + EngineMock engine; + target.setEngine(&engine); + auto costume = std::make_shared("", "", "png"); + std::string costumeData = readFileStr("image.png"); + costume->setData(costumeData.size(), static_cast(costumeData.data())); + costume->setRotationCenterX(-15); + costume->setRotationCenterY(48); + costume->setBitmapResolution(3.25); + sprite.addCostume(costume); + + EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480)); + EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360)); + target.loadCostumes(); + target.updateCostume(costume.get()); + target.beforeRedraw(); + + Rect bounds = target.getFastBounds(); + ASSERT_EQ(std::round(bounds.left() * 100) / 100, 65.84); + ASSERT_EQ(std::round(bounds.top() * 100) / 100, -123.92); + ASSERT_EQ(std::round(bounds.right() * 100) / 100, 67.31); + ASSERT_EQ(std::round(bounds.bottom() * 100) / 100, -125.4); + + EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480)); + EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360)); + target.updateRotationStyle(Sprite::RotationStyle::LeftRight); + + bounds = target.getFastBounds(); + ASSERT_EQ(std::round(bounds.left() * 100) / 100, 71.67); + ASSERT_EQ(std::round(bounds.top() * 100) / 100, -110.26); + ASSERT_EQ(std::round(bounds.right() * 100) / 100, 72.5); + ASSERT_EQ(std::round(bounds.bottom() * 100) / 100, -111.51); + + EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480)); + EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360)); + target.setStageScale(20.75); + + bounds = target.getFastBounds(); + ASSERT_EQ(std::round(bounds.left() * 100) / 100, 71.67); + ASSERT_EQ(std::round(bounds.top() * 100) / 100, -110.26); + ASSERT_EQ(std::round(bounds.right() * 100) / 100, 72.5); + ASSERT_EQ(std::round(bounds.bottom() * 100) / 100, -111.51); + + EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480)); + EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360)); + target.updateSize(9780.6); + + bounds = target.getFastBounds(); + ASSERT_EQ(std::round(bounds.left() * 100) / 100, -496.15); + ASSERT_EQ(std::round(bounds.top() * 100) / 100, 1324.22); + ASSERT_EQ(std::round(bounds.right() * 100) / 100, -375.77); + ASSERT_EQ(std::round(bounds.bottom() * 100) / 100, 1143.65); + + context.doneCurrent(); +} From c698762457f43faa2af6127bbdfd1ddaff749d36 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:41:06 +0100 Subject: [PATCH 3/3] Implement SpriteModel::fastBoundingRect() --- src/spritemodel.cpp | 2 +- test/target_models/spritemodel_test.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 997f083..498838c 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -147,7 +147,7 @@ libscratchcpp::Rect SpriteModel::boundingRect() const libscratchcpp::Rect SpriteModel::fastBoundingRect() const { - return libscratchcpp::Rect(m_sprite->x(), m_sprite->y(), m_sprite->x(), m_sprite->y()); + return m_renderedTarget->getFastBounds(); } libscratchcpp::Sprite *SpriteModel::sprite() const diff --git a/test/target_models/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index b2eca9e..7d3409b 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -302,6 +302,22 @@ TEST(SpriteModelTest, BoundingRect) ASSERT_EQ(bounds.bottom(), rect.bottom()); } +TEST(SpriteModelTest, FastBoundingRect) +{ + SpriteModel model; + + RenderedTargetMock renderedTarget; + model.setRenderedTarget(&renderedTarget); + + Rect rect(-1, 1, 1, -1); + EXPECT_CALL(renderedTarget, getFastBounds()).WillOnce(Return(rect)); + Rect bounds = model.fastBoundingRect(); + ASSERT_EQ(bounds.left(), rect.left()); + ASSERT_EQ(bounds.top(), rect.top()); + ASSERT_EQ(bounds.right(), rect.right()); + ASSERT_EQ(bounds.bottom(), rect.bottom()); +} + TEST(SpriteModelTest, RenderedTarget) { SpriteModel model;