Skip to content

Commit 4425f65

Browse files
committed
Add touchingClones() method to RenderedTarget
1 parent a20b0f8 commit 4425f65

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

src/irenderedtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class IRenderedTarget : public QNanoQuickItem
8686
virtual const std::vector<QPoint> &hullPoints() const = 0;
8787

8888
virtual bool containsScratchPoint(double x, double y) const = 0;
89+
90+
virtual bool touchingClones(const std::vector<libscratchcpp::Sprite *> &clones) const = 0;
8991
};
9092

9193
} // namespace scratchcpprender

src/renderedtarget.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,58 @@ bool RenderedTarget::containsScratchPoint(double x, double y) const
592592
return contains(mapFromItem(parentItem(), QPointF(x, y)));
593593
}
594594

595+
bool RenderedTarget::touchingClones(const std::vector<libscratchcpp::Sprite *> &clones) const
596+
{
597+
// https://github.com/scratchfoundation/scratch-render/blob/941562438fe3dd6e7d98d9387607d535dcd68d24/src/RenderWebGL.js#L967-L1002
598+
// TODO: Use Rect methods and do not use QRects
599+
Rect scratchRect = getFastBounds();
600+
const QRectF myRect(QPointF(scratchRect.left(), scratchRect.bottom()), QPointF(scratchRect.right(), scratchRect.top()));
601+
QRectF united;
602+
std::vector<IRenderedTarget *> candidates;
603+
604+
// Calculate the union of the bounding rectangle intersections
605+
for (auto clone : clones) {
606+
Q_ASSERT(clone);
607+
608+
if (!clone)
609+
continue;
610+
611+
SpriteModel *model = static_cast<SpriteModel *>(clone->getInterface());
612+
Q_ASSERT(model);
613+
614+
if (model) {
615+
// Calculate the intersection of the bounding rectangles
616+
IRenderedTarget *candidate = model->renderedTarget();
617+
Q_ASSERT(candidate);
618+
scratchRect = candidate->getFastBounds();
619+
QRectF rect(QPointF(scratchRect.left(), scratchRect.bottom()), QPointF(scratchRect.right(), scratchRect.top()));
620+
QRectF intersected = myRect.intersected(rect);
621+
622+
// Add it to the union
623+
united = united.united(intersected);
624+
625+
candidates.push_back(candidate);
626+
}
627+
}
628+
629+
if (united.isEmpty() || candidates.empty())
630+
return false;
631+
632+
// Loop through the points of the union
633+
for (int y = united.top(); y <= united.bottom(); y++) {
634+
for (int x = united.left(); x <= united.right(); x++) {
635+
if (this->containsScratchPoint(x, y)) {
636+
for (IRenderedTarget *candidate : candidates) {
637+
if (candidate->containsScratchPoint(x, y))
638+
return true;
639+
}
640+
}
641+
}
642+
}
643+
644+
return false;
645+
}
646+
595647
void RenderedTarget::calculatePos()
596648
{
597649
if (!m_skin || !m_costume || !m_engine)

src/renderedtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class RenderedTarget : public IRenderedTarget
9595
Q_INVOKABLE bool contains(const QPointF &point) const override;
9696
bool containsScratchPoint(double x, double y) const override;
9797

98+
bool touchingClones(const std::vector<libscratchcpp::Sprite *> &) const override;
99+
98100
signals:
99101
void engineChanged();
100102
void stageModelChanged();

test/mocks/renderedtargetmock.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class RenderedTargetMock : public IRenderedTarget
7272
MOCK_METHOD(bool, contains, (const QPointF &), (const, override));
7373
MOCK_METHOD(bool, containsScratchPoint, (double, double), (const, override));
7474

75+
MOCK_METHOD(bool, touchingClones, (const std::vector<libscratchcpp::Sprite *> &), (const, override));
76+
7577
MOCK_METHOD(QNanoQuickItemPainter *, createItemPainter, (), (const, override));
7678
MOCK_METHOD(void, hoverEnterEvent, (QHoverEvent *), (override));
7779
MOCK_METHOD(void, hoverLeaveEvent, (QHoverEvent *), (override));

test/renderedtarget/renderedtarget_test.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <scratchcpp/costume.h>
1212
#include <scratchcpp/rect.h>
1313
#include <enginemock.h>
14+
#include <renderedtargetmock.h>
1415

1516
#include "../common.h"
1617

@@ -795,3 +796,108 @@ TEST_F(RenderedTargetTest, GetFastBounds)
795796

796797
context.doneCurrent();
797798
}
799+
800+
TEST_F(RenderedTargetTest, TouchingClones)
801+
{
802+
EngineMock engine;
803+
Sprite sprite, clone1, clone2;
804+
SpriteModel model, model1, model2;
805+
model.init(&sprite);
806+
clone1.setInterface(&model1);
807+
clone2.setInterface(&model2);
808+
809+
QQuickItem parent;
810+
parent.setWidth(480);
811+
parent.setHeight(360);
812+
813+
RenderedTarget target(&parent);
814+
target.setEngine(&engine);
815+
target.setSpriteModel(&model);
816+
817+
RenderedTargetMock target1, target2;
818+
model1.setRenderedTarget(&target1);
819+
model2.setRenderedTarget(&target2);
820+
821+
// Create OpenGL context
822+
QOpenGLContext context;
823+
QOffscreenSurface surface;
824+
createContextAndSurface(&context, &surface);
825+
826+
// Load costume
827+
EXPECT_CALL(engine, stageWidth()).WillRepeatedly(Return(480));
828+
EXPECT_CALL(engine, stageHeight()).WillRepeatedly(Return(360));
829+
auto costume = std::make_shared<Costume>("", "", "png");
830+
std::string costumeData = readFileStr("image.png");
831+
costume->setData(costumeData.size(), static_cast<void *>(costumeData.data()));
832+
sprite.addCostume(costume);
833+
target.loadCostumes();
834+
target.updateCostume(costume.get());
835+
target.setWidth(3);
836+
target.setHeight(3);
837+
838+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5)));
839+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 1.8, -8)));
840+
EXPECT_CALL(target1, containsScratchPoint(1, -3)).WillOnce(Return(false));
841+
EXPECT_CALL(target2, containsScratchPoint(1, -3)).WillOnce(Return(false));
842+
EXPECT_CALL(target1, containsScratchPoint(2, -3)).WillOnce(Return(false));
843+
EXPECT_CALL(target2, containsScratchPoint(2, -3)).WillOnce(Return(false));
844+
EXPECT_CALL(target1, containsScratchPoint(3, -3)).WillOnce(Return(false));
845+
EXPECT_CALL(target2, containsScratchPoint(3, -3)).WillOnce(Return(false));
846+
EXPECT_CALL(target1, containsScratchPoint(1, -2)).WillOnce(Return(false));
847+
EXPECT_CALL(target2, containsScratchPoint(1, -2)).WillOnce(Return(false));
848+
EXPECT_CALL(target1, containsScratchPoint(3, -2)).WillOnce(Return(false));
849+
EXPECT_CALL(target2, containsScratchPoint(3, -2)).WillOnce(Return(false));
850+
EXPECT_CALL(target1, containsScratchPoint(1, -1)).WillOnce(Return(false));
851+
EXPECT_CALL(target2, containsScratchPoint(1, -1)).WillOnce(Return(false));
852+
EXPECT_CALL(target1, containsScratchPoint(2, -1)).WillOnce(Return(false));
853+
EXPECT_CALL(target2, containsScratchPoint(2, -1)).WillOnce(Return(false));
854+
EXPECT_CALL(target1, containsScratchPoint(3, -1)).WillOnce(Return(false));
855+
EXPECT_CALL(target2, containsScratchPoint(3, -1)).WillOnce(Return(false));
856+
ASSERT_FALSE(target.touchingClones({ &clone1, &clone2 }));
857+
858+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5)));
859+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 1.8, -8)));
860+
EXPECT_CALL(target1, containsScratchPoint(1, -3)).WillOnce(Return(false));
861+
EXPECT_CALL(target2, containsScratchPoint(1, -3)).WillOnce(Return(false));
862+
EXPECT_CALL(target1, containsScratchPoint(2, -3)).WillOnce(Return(true));
863+
ASSERT_TRUE(target.touchingClones({ &clone1, &clone2 }));
864+
865+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5)));
866+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 1.8, -8)));
867+
EXPECT_CALL(target1, containsScratchPoint(1, -3)).WillOnce(Return(false));
868+
EXPECT_CALL(target2, containsScratchPoint(1, -3)).WillOnce(Return(false));
869+
EXPECT_CALL(target1, containsScratchPoint(1, -2)).WillOnce(Return(false));
870+
EXPECT_CALL(target2, containsScratchPoint(1, -2)).WillOnce(Return(false));
871+
EXPECT_CALL(target1, containsScratchPoint(1, -1)).WillOnce(Return(false));
872+
EXPECT_CALL(target2, containsScratchPoint(1, -1)).WillOnce(Return(false));
873+
ASSERT_FALSE(target.touchingClones({ &clone1, &clone2 }));
874+
875+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5)));
876+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6.5, 1.8, -8)));
877+
EXPECT_CALL(target1, containsScratchPoint(2, -3)).WillOnce(Return(false));
878+
EXPECT_CALL(target2, containsScratchPoint(2, -3)).WillOnce(Return(false));
879+
EXPECT_CALL(target1, containsScratchPoint(3, -3)).WillOnce(Return(false));
880+
EXPECT_CALL(target2, containsScratchPoint(3, -3)).WillOnce(Return(false));
881+
EXPECT_CALL(target1, containsScratchPoint(3, -2)).WillOnce(Return(false));
882+
EXPECT_CALL(target2, containsScratchPoint(3, -2)).WillOnce(Return(false));
883+
EXPECT_CALL(target1, containsScratchPoint(2, -1)).WillOnce(Return(false));
884+
EXPECT_CALL(target2, containsScratchPoint(2, -1)).WillOnce(Return(false));
885+
EXPECT_CALL(target1, containsScratchPoint(3, -1)).WillOnce(Return(false));
886+
EXPECT_CALL(target2, containsScratchPoint(3, -1)).WillOnce(Return(false));
887+
ASSERT_FALSE(target.touchingClones({ &clone1, &clone2 }));
888+
889+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5)));
890+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6.5, 1.8, -8)));
891+
EXPECT_CALL(target1, containsScratchPoint(2, -3)).WillOnce(Return(false));
892+
EXPECT_CALL(target2, containsScratchPoint(2, -3)).WillOnce(Return(true));
893+
ASSERT_TRUE(target.touchingClones({ &clone1, &clone2 }));
894+
895+
EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5)));
896+
EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6.5, 1.8, -8)));
897+
EXPECT_CALL(target1, containsScratchPoint).Times(0);
898+
EXPECT_CALL(target2, containsScratchPoint).Times(0);
899+
ASSERT_FALSE(target.touchingClones({ &clone1, &clone2 }));
900+
901+
// Cleanup
902+
context.doneCurrent();
903+
}

0 commit comments

Comments
 (0)