Skip to content

Commit edc5ea3

Browse files
authored
Merge pull request #56 from scratchcpp/stage_scaling
Implement stage scaling
2 parents c602272 + 7c9103f commit edc5ea3

File tree

9 files changed

+116
-37
lines changed

9 files changed

+116
-37
lines changed

ScratchCPPGui/ProjectPlayer.qml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import ScratchCPPGui
77

88
ProjectScene {
99
property string fileName
10+
property int stageWidth: 480
11+
property int stageHeight: 360
1012
property alias fps: loader.fps
1113
property alias turboMode: loader.turboMode
1214
property alias cloneLimit: loader.cloneLimit
@@ -21,6 +23,7 @@ ProjectScene {
2123
id: root
2224
clip: true
2325
engine: loader.engine
26+
stageScale: (stageWidth == 0 || stageHeight == 0) ? 1 : Math.min(width / stageWidth, height / stageHeight)
2427
onFileNameChanged: priv.loading = true;
2528

2629
QtObject {
@@ -31,8 +34,8 @@ ProjectScene {
3134
ProjectLoader {
3235
id: loader
3336
fileName: root.fileName
34-
stageWidth: parent.width
35-
stageHeight: parent.height
37+
stageWidth: root.stageWidth
38+
stageHeight: root.stageHeight
3639
onLoadingFinished: {
3740
priv.loading = false;
3841

@@ -48,6 +51,7 @@ ProjectScene {
4851
engine: loader.engine
4952
stageModel: loader.stage
5053
mouseArea: sceneMouseArea
54+
stageScale: root.stageScale
5155
onStageModelChanged: stageModel.renderedTarget = this
5256
}
5357

@@ -68,6 +72,7 @@ ProjectScene {
6872
engine: loader.engine
6973
spriteModel: modelData
7074
mouseArea: sceneMouseArea
75+
stageScale: root.stageScale
7176
transform: Scale { xScale: mirrorHorizontally ? -1 : 1 }
7277
Component.onCompleted: modelData.renderedTarget = this
7378
}

ScratchCPPGui/irenderedtarget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class IRenderedTarget : public QNanoQuickItem
5151
virtual SceneMouseArea *mouseArea() const = 0;
5252
virtual void setMouseArea(SceneMouseArea *newMouseArea) = 0;
5353

54+
virtual double stageScale() const = 0;
55+
virtual void setStageScale(double scale) = 0;
56+
5457
virtual qreal width() const = 0;
5558
virtual void setWidth(qreal width) = 0;
5659

ScratchCPPGui/projectscene.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,26 @@ void ProjectScene::setEngine(IEngine *newEngine)
3737
emit engineChanged();
3838
}
3939

40+
double ProjectScene::stageScale() const
41+
{
42+
return m_stageScale;
43+
}
44+
45+
void ProjectScene::setStageScale(double newStageScale)
46+
{
47+
if (qFuzzyCompare(m_stageScale, newStageScale))
48+
return;
49+
50+
m_stageScale = newStageScale;
51+
Q_ASSERT(m_stageScale > 0);
52+
emit stageScaleChanged();
53+
}
54+
4055
void ProjectScene::handleMouseMove(qreal x, qreal y)
4156
{
4257
if (m_engine) {
43-
m_engine->setMouseX(x - m_engine->stageWidth() / 2.0);
44-
m_engine->setMouseY(-y + m_engine->stageHeight() / 2.0);
58+
m_engine->setMouseX(x / m_stageScale - m_engine->stageWidth() / 2.0);
59+
m_engine->setMouseY(-y / m_stageScale + m_engine->stageHeight() / 2.0);
4560
}
4661
}
4762

ScratchCPPGui/projectscene.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ class ProjectScene : public QQuickItem
1919
Q_OBJECT
2020
QML_ELEMENT
2121
Q_PROPERTY(libscratchcpp::IEngine *engine READ engine WRITE setEngine NOTIFY engineChanged)
22+
Q_PROPERTY(double stageScale READ stageScale WRITE setStageScale NOTIFY stageScaleChanged)
2223

2324
public:
2425
ProjectScene(QQuickItem *parent = nullptr);
2526

2627
libscratchcpp::IEngine *engine() const;
2728
void setEngine(libscratchcpp::IEngine *newEngine);
2829

30+
double stageScale() const;
31+
void setStageScale(double newStageScale);
32+
2933
Q_INVOKABLE void handleMouseMove(qreal x, qreal y);
3034
Q_INVOKABLE void handleMousePress();
3135
Q_INVOKABLE void handleMouseRelease();
@@ -35,11 +39,13 @@ class ProjectScene : public QQuickItem
3539

3640
signals:
3741
void engineChanged();
42+
void stageScaleChanged();
3843

3944
private:
4045
void installKeyHandler(QQuickWindow *window);
4146

4247
libscratchcpp::IEngine *m_engine = nullptr;
48+
double m_stageScale = 1;
4349
KeyEventHandler *m_keyHandler = nullptr;
4450
};
4551

ScratchCPPGui/renderedtarget.cpp

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ void RenderedTarget::loadProperties()
8080

8181
// Coordinates
8282
double clampedSize = std::min(m_size, m_maxSize);
83-
m_x = static_cast<double>(m_engine->stageWidth()) / 2 + sprite->x() - m_costume->rotationCenterX() * clampedSize / m_costume->bitmapResolution() * (m_newMirrorHorizontally ? -1 : 1);
84-
m_y = static_cast<double>(m_engine->stageHeight()) / 2 - sprite->y() - m_costume->rotationCenterY() * clampedSize / m_costume->bitmapResolution();
85-
m_originX = m_costume->rotationCenterX() * clampedSize / m_costume->bitmapResolution();
86-
m_originY = m_costume->rotationCenterY() * clampedSize / m_costume->bitmapResolution();
83+
double stageWidth = m_engine->stageWidth();
84+
double stageHeight = m_engine->stageHeight();
85+
m_x = m_stageScale * (stageWidth / 2 + sprite->x() - m_costume->rotationCenterX() * clampedSize / m_costume->bitmapResolution() * (m_newMirrorHorizontally ? -1 : 1));
86+
m_y = m_stageScale * (stageHeight / 2 - sprite->y() - m_costume->rotationCenterY() * clampedSize / m_costume->bitmapResolution());
87+
m_originX = m_costume->rotationCenterX() * clampedSize * m_stageScale / m_costume->bitmapResolution();
88+
m_originY = m_costume->rotationCenterY() * clampedSize * m_stageScale / m_costume->bitmapResolution();
8789

8890
// Layer
8991
m_z = sprite->layerOrder();
@@ -92,8 +94,10 @@ void RenderedTarget::loadProperties()
9294
mutex.unlock();
9395
} else if (m_stageModel) {
9496
updateCostumeData();
95-
m_x = static_cast<double>(m_engine->stageWidth()) / 2 - m_costume->rotationCenterX() / m_costume->bitmapResolution();
96-
m_y = static_cast<double>(m_engine->stageHeight()) / 2 - m_costume->rotationCenterY() / m_costume->bitmapResolution();
97+
double stageWidth = m_engine->stageWidth();
98+
double stageHeight = m_engine->stageHeight();
99+
m_x = m_stageScale * (stageWidth / 2 - m_costume->rotationCenterX() / m_costume->bitmapResolution());
100+
m_y = m_stageScale * (stageHeight / 2 - m_costume->rotationCenterY() / m_costume->bitmapResolution());
97101
m_originX = m_costume->rotationCenterX() / m_costume->bitmapResolution();
98102
m_originY = m_costume->rotationCenterY() / m_costume->bitmapResolution();
99103
}
@@ -213,6 +217,21 @@ void RenderedTarget::setMouseArea(SceneMouseArea *newMouseArea)
213217
emit mouseAreaChanged();
214218
}
215219

220+
double RenderedTarget::stageScale() const
221+
{
222+
return m_stageScale;
223+
}
224+
225+
void RenderedTarget::setStageScale(double newStageScale)
226+
{
227+
if (qFuzzyCompare(m_stageScale, newStageScale))
228+
return;
229+
230+
m_stageScale = newStageScale;
231+
Q_ASSERT(m_stageScale > 0);
232+
emit stageScaleChanged();
233+
}
234+
216235
qreal RenderedTarget::width() const
217236
{
218237
return QNanoQuickItem::width();
@@ -436,16 +455,16 @@ void RenderedTarget::calculateSize(Target *target, double costumeWidth, double c
436455
{
437456
if (m_costume) {
438457
double bitmapRes = m_costume->bitmapResolution();
439-
m_maxSize = std::min(m_maximumWidth / costumeWidth, m_maximumHeight / costumeHeight);
458+
m_maxSize = std::min(m_maximumWidth / (costumeWidth * m_stageScale), m_maximumHeight / (costumeHeight * m_stageScale));
440459
Sprite *sprite = dynamic_cast<Sprite *>(target);
441460

442461
if (sprite) {
443462
double clampedSize = std::min(m_size, m_maxSize);
444-
m_width = costumeWidth * clampedSize / bitmapRes;
445-
m_height = costumeHeight * clampedSize / bitmapRes;
463+
m_width = costumeWidth * clampedSize * m_stageScale / bitmapRes;
464+
m_height = costumeHeight * clampedSize * m_stageScale / bitmapRes;
446465
} else {
447-
m_width = costumeWidth / bitmapRes;
448-
m_height = costumeHeight / bitmapRes;
466+
m_width = costumeWidth * m_stageScale / bitmapRes;
467+
m_height = costumeHeight * m_stageScale / bitmapRes;
449468
}
450469
}
451470
}
@@ -458,8 +477,8 @@ void RenderedTarget::handleSceneMouseMove(qreal x, qreal y)
458477
Q_ASSERT(m_spriteModel && m_spriteModel->sprite());
459478
Q_ASSERT(m_engine);
460479
Sprite *sprite = m_spriteModel->sprite();
461-
sprite->setX(x - m_engine->stageWidth() / 2.0 - m_dragDeltaX);
462-
sprite->setY(-y + m_engine->stageHeight() / 2.0 - m_dragDeltaY);
480+
sprite->setX(x / m_stageScale - m_engine->stageWidth() / 2.0 - m_dragDeltaX);
481+
sprite->setY(-y / m_stageScale + m_engine->stageHeight() / 2.0 - m_dragDeltaY);
463482
}
464483
}
465484

ScratchCPPGui/renderedtarget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class RenderedTarget : public IRenderedTarget
2525
Q_PROPERTY(SpriteModel *spriteModel READ spriteModel WRITE setSpriteModel NOTIFY spriteModelChanged)
2626
Q_PROPERTY(bool mirrorHorizontally READ mirrorHorizontally NOTIFY mirrorHorizontallyChanged)
2727
Q_PROPERTY(SceneMouseArea *mouseArea READ mouseArea WRITE setMouseArea NOTIFY mouseAreaChanged)
28+
Q_PROPERTY(double stageScale READ stageScale WRITE setStageScale NOTIFY stageScaleChanged)
2829

2930
public:
3031
RenderedTarget(QNanoQuickItem *parent = nullptr);
@@ -47,6 +48,9 @@ class RenderedTarget : public IRenderedTarget
4748
SceneMouseArea *mouseArea() const override;
4849
void setMouseArea(SceneMouseArea *newMouseArea) override;
4950

51+
double stageScale() const override;
52+
void setStageScale(double newStageScale) override;
53+
5054
qreal width() const override;
5155
void setWidth(qreal width) override;
5256

@@ -77,6 +81,7 @@ class RenderedTarget : public IRenderedTarget
7781
void spriteModelChanged();
7882
void mouseAreaChanged();
7983
void mirrorHorizontallyChanged();
84+
void stageScaleChanged();
8085

8186
protected:
8287
QNanoQuickItemPainter *createItemPainter() const override;
@@ -116,6 +121,7 @@ class RenderedTarget : public IRenderedTarget
116121
bool m_newMirrorHorizontally = false;
117122
double m_originX = 0;
118123
double m_originY = 0;
124+
double m_stageScale = 1;
119125
qreal m_maximumWidth = std::numeric_limits<double>::infinity();
120126
qreal m_maximumHeight = std::numeric_limits<double>::infinity();
121127
std::vector<QPointF> m_hullPoints;

test/mocks/renderedtargetmock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class RenderedTargetMock : public IRenderedTarget
3030
MOCK_METHOD(SceneMouseArea *, mouseArea, (), (const, override));
3131
MOCK_METHOD(void, setMouseArea, (SceneMouseArea *), (override));
3232

33+
MOCK_METHOD(double, stageScale, (), (const, override));
34+
MOCK_METHOD(void, setStageScale, (double), (override));
35+
3336
MOCK_METHOD(qreal, width, (), (const, override));
3437
MOCK_METHOD(void, setWidth, (qreal), (override));
3538

test/projectscene/projectscene_test.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using namespace scratchcppgui;
88

99
using ::testing::Return;
1010

11-
TEST(ProjectScene, Engine)
11+
TEST(ProjectSceneTest, Engine)
1212
{
1313
ProjectScene scene;
1414
ASSERT_EQ(scene.engine(), nullptr);
@@ -18,20 +18,30 @@ TEST(ProjectScene, Engine)
1818
ASSERT_EQ(scene.engine(), &engine);
1919
}
2020

21-
TEST(ProjectScene, HandleMouseMove)
21+
TEST(ProjectSceneTest, StageScale)
22+
{
23+
ProjectScene scene;
24+
ASSERT_EQ(scene.stageScale(), 1);
25+
26+
scene.setStageScale(5.79);
27+
ASSERT_EQ(scene.stageScale(), 5.79);
28+
}
29+
30+
TEST(ProjectSceneTest, HandleMouseMove)
2231
{
2332
ProjectScene scene;
2433
EngineMock engine;
2534
scene.setEngine(&engine);
35+
scene.setStageScale(2.5);
2636

2737
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(600));
2838
EXPECT_CALL(engine, stageHeight()).WillOnce(Return(400));
29-
EXPECT_CALL(engine, setMouseX(-253.1));
30-
EXPECT_CALL(engine, setMouseY(216.7));
39+
EXPECT_CALL(engine, setMouseX(-281.24));
40+
EXPECT_CALL(engine, setMouseY(206.68));
3141
scene.handleMouseMove(46.9, -16.7);
3242
}
3343

34-
TEST(ProjectScene, HandleMousePress)
44+
TEST(ProjectSceneTest, HandleMousePress)
3545
{
3646
ProjectScene scene;
3747
EngineMock engine;
@@ -44,7 +54,7 @@ TEST(ProjectScene, HandleMousePress)
4454
scene.handleMousePress();
4555
}
4656

47-
TEST(ProjectScene, HandleMouseRelease)
57+
TEST(ProjectSceneTest, HandleMouseRelease)
4858
{
4959
ProjectScene scene;
5060
EngineMock engine;
@@ -57,7 +67,7 @@ TEST(ProjectScene, HandleMouseRelease)
5767
scene.handleMouseRelease();
5868
}
5969

60-
TEST(ProjectScene, HandleKeyPressAndRelease)
70+
TEST(ProjectSceneTest, HandleKeyPressAndRelease)
6171
{
6272
static const std::unordered_map<Qt::Key, KeyEvent::Type> SPECIAL_KEY_MAP = {
6373
{ Qt::Key_Space, KeyEvent::Type::Space }, { Qt::Key_Left, KeyEvent::Type::Left }, { Qt::Key_Up, KeyEvent::Type::Up }, { Qt::Key_Right, KeyEvent::Type::Right },

test/renderedtarget/renderedtarget_test.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ TEST_F(RenderedTargetTest, LoadAndUpdateProperties)
7474
target.setZ(2.5);
7575
target.setRotation(-78.05);
7676
target.setTransformOriginPoint(QPointF(3.4, 9.7));
77+
target.setStageScale(3.5);
7778

7879
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(544));
7980
EXPECT_CALL(engine, stageHeight()).WillOnce(Return(249));
@@ -87,10 +88,10 @@ TEST_F(RenderedTargetTest, LoadAndUpdateProperties)
8788
ASSERT_EQ(target.transformOriginPoint(), QPointF(3.4, 9.7));
8889

8990
target.updateProperties();
90-
ASSERT_EQ(target.width(), 4);
91-
ASSERT_EQ(target.height(), 6);
92-
ASSERT_EQ(target.x(), 295);
93-
ASSERT_EQ(target.y(), 52.5);
91+
ASSERT_EQ(target.width(), 14);
92+
ASSERT_EQ(target.height(), 21);
93+
ASSERT_EQ(target.x(), 1032.5);
94+
ASSERT_EQ(target.y(), 183.75);
9495
ASSERT_EQ(target.z(), 0);
9596
ASSERT_EQ(target.rotation(), 0);
9697
ASSERT_EQ(target.transformOriginPoint(), QPointF(-23, 72));
@@ -118,6 +119,7 @@ TEST_F(RenderedTargetTest, LoadAndUpdateProperties)
118119
target.setZ(2.5);
119120
target.setRotation(-78.05);
120121
target.setTransformOriginPoint(QPointF(3.4, 9.7));
122+
target.setStageScale(5.23);
121123

122124
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(544));
123125
EXPECT_CALL(engine, stageHeight()).WillOnce(Return(249));
@@ -131,14 +133,14 @@ TEST_F(RenderedTargetTest, LoadAndUpdateProperties)
131133
ASSERT_EQ(target.transformOriginPoint(), QPointF(3.4, 9.7));
132134

133135
target.updateProperties();
134-
ASSERT_EQ(target.width(), 5.7592);
135-
ASSERT_EQ(target.height(), 8.6388);
136-
ASSERT_EQ(std::round(target.x() * 100) / 100, 237.18);
137-
ASSERT_EQ(std::round(target.y() * 100) / 100, -100.93);
136+
ASSERT_EQ(std::round(target.width() * 100) / 100, 30.12);
137+
ASSERT_EQ(std::round(target.height() * 100) / 100, 45.18);
138+
ASSERT_EQ(std::round(target.x() * 100) / 100, 1240.43);
139+
ASSERT_EQ(std::round(target.y() * 100) / 100, -527.84);
138140
ASSERT_EQ(target.z(), 3);
139141
ASSERT_EQ(target.rotation(), -157.16);
140-
ASSERT_EQ(target.transformOriginPoint().x(), -33.1154);
141-
ASSERT_EQ(std::round(target.transformOriginPoint().y() * 100) / 100, 103.67);
142+
ASSERT_EQ(std::round(target.transformOriginPoint().x() * 100) / 100, -173.19);
143+
ASSERT_EQ(std::round(target.transformOriginPoint().y() * 100) / 100, 542.17);
142144
ASSERT_TRUE(mirrorHorizontallySpy.empty());
143145

144146
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(544));
@@ -377,6 +379,7 @@ TEST_F(RenderedTargetTest, LoadSvgCostume)
377379

378380
// Test scale limit
379381
sprite.setSize(maxSize * 250);
382+
target.setStageScale(3.89);
380383

381384
target.loadCostume(costume.get());
382385
ASSERT_TRUE(target.isSvg());
@@ -406,9 +409,9 @@ TEST_F(RenderedTargetTest, LoadSvgCostume)
406409

407410
ASSERT_EQ(std::round(target.width() * 100) / 100, maxWidth);
408411
ASSERT_EQ(std::round(target.height() * 100) / 100, maxHeight);
409-
ASSERT_EQ(std::round(target.scale() * 100) / 100, 2.5);
410-
ASSERT_EQ(std::round(target.x() * 100) / 100, 11126.36);
411-
ASSERT_EQ(std::round(target.y() * 100) / 100, -6593.27);
412+
ASSERT_EQ(std::round(target.scale() * 100) / 100, 9.73);
413+
ASSERT_EQ(std::round(target.x() * 100) / 100, 11963.59);
414+
ASSERT_EQ(std::round(target.y() * 100) / 100, -5887.67);
412415
ASSERT_EQ(std::round(target.transformOriginPoint().x() * 100) / 100, -10836.66);
413416
ASSERT_EQ(std::round(target.transformOriginPoint().y() * 100) / 100, 6837.42);
414417
}
@@ -603,3 +606,12 @@ TEST_F(RenderedTargetTest, MouseArea)
603606
target.setMouseArea(&mouseArea);
604607
ASSERT_EQ(target.mouseArea(), &mouseArea);
605608
}
609+
610+
TEST_F(RenderedTargetTest, StageScale)
611+
{
612+
RenderedTarget target;
613+
ASSERT_EQ(target.stageScale(), 1);
614+
615+
target.setStageScale(6.4);
616+
ASSERT_EQ(target.stageScale(), 6.4);
617+
}

0 commit comments

Comments
 (0)