Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/irenderedtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
33 changes: 33 additions & 0 deletions src/renderedtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/renderedtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
5 changes: 5 additions & 0 deletions src/spritemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ libscratchcpp::Rect SpriteModel::boundingRect() const
return m_renderedTarget->getBounds();
}

libscratchcpp::Rect SpriteModel::fastBoundingRect() const
{
return m_renderedTarget->getFastBounds();
}

libscratchcpp::Sprite *SpriteModel::sprite() const
{
return m_sprite;
Expand Down
1 change: 1 addition & 0 deletions src/spritemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions test/mocks/renderedtargetmock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
72 changes: 72 additions & 0 deletions test/renderedtarget/renderedtarget_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Costume>("", "", "png");
std::string costumeData = readFileStr("image.png");
costume->setData(costumeData.size(), static_cast<void *>(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();
}
16 changes: 16 additions & 0 deletions test/target_models/spritemodel_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down