Skip to content

Commit d3a2c9c

Browse files
committed
Implement motion_movesteps block
1 parent 69f27f0 commit d3a2c9c

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

src/blocks/motionblocks.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,45 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <scratchcpp/iengine.h>
4+
#include <scratchcpp/compiler.h>
5+
#include <scratchcpp/sprite.h>
6+
37
#include "motionblocks.h"
48

59
using namespace libscratchcpp;
610

11+
static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20
12+
713
std::string MotionBlocks::name() const
814
{
915
return "Motion";
1016
}
1117

1218
void MotionBlocks::registerBlocks(IEngine *engine)
1319
{
20+
// Blocks
21+
engine->addCompileFunction(this, "motion_movesteps", &compileMoveSteps);
22+
23+
// Inputs
24+
engine->addInput(this, "STEPS", STEPS);
25+
}
26+
27+
void MotionBlocks::compileMoveSteps(Compiler *compiler)
28+
{
29+
compiler->addInput(STEPS);
30+
compiler->addFunctionCall(&moveSteps);
31+
}
32+
33+
unsigned int MotionBlocks::moveSteps(VirtualMachine *vm)
34+
{
35+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
36+
37+
if (sprite) {
38+
double dir = sprite->direction();
39+
double steps = vm->getInput(0, 1)->toDouble();
40+
sprite->setX(sprite->x() + std::sin(dir * pi / 180) * steps);
41+
sprite->setY(sprite->y() + std::cos(dir * pi / 180) * steps);
42+
}
43+
44+
return 1;
1445
}

src/blocks/motionblocks.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@ namespace libscratchcpp
1111
class MotionBlocks : public IBlockSection
1212
{
1313
public:
14+
enum Inputs
15+
{
16+
STEPS
17+
};
18+
1419
std::string name() const override;
1520

1621
void registerBlocks(IEngine *engine) override;
22+
23+
static void compileMoveSteps(Compiler *compiler);
24+
25+
static unsigned int moveSteps(VirtualMachine *vm);
1726
};
1827

1928
} // namespace libscratchcpp

test/blocks/motion_blocks_test.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <scratchcpp/compiler.h>
22
#include <scratchcpp/block.h>
3+
#include <scratchcpp/input.h>
4+
#include <scratchcpp/sprite.h>
35
#include <enginemock.h>
46

57
#include "../common.h"
@@ -19,6 +21,18 @@ class MotionBlocksTest : public testing::Test
1921
m_section->registerBlocks(&m_engine);
2022
}
2123

24+
// For any motion block
25+
std::shared_ptr<Block> createMotionBlock(const std::string &id, const std::string &opcode) const { return std::make_shared<Block>(id, opcode); }
26+
27+
void addValueInput(std::shared_ptr<Block> block, const std::string &name, MotionBlocks::Inputs id, const Value &value) const
28+
{
29+
auto input = std::make_shared<Input>(name, Input::Type::Shadow);
30+
input->setPrimaryValue(value);
31+
input->setInputId(id);
32+
block->addInput(input);
33+
block->updateInputMap();
34+
}
35+
2236
std::unique_ptr<IBlockSection> m_section;
2337
EngineMock m_engineMock;
2438
Engine m_engine;
@@ -36,5 +50,53 @@ TEST_F(MotionBlocksTest, CategoryVisible)
3650

3751
TEST_F(MotionBlocksTest, RegisterBlocks)
3852
{
53+
// Blocks
54+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_movesteps", &MotionBlocks::compileMoveSteps));
55+
56+
// Inputs
57+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "STEPS", MotionBlocks::STEPS));
58+
3959
m_section->registerBlocks(&m_engineMock);
4060
}
61+
62+
TEST_F(MotionBlocksTest, MoveSteps)
63+
{
64+
Compiler compiler(&m_engineMock);
65+
66+
// move (30.25) steps
67+
auto block = std::make_shared<Block>("a", "motion_movesteps");
68+
addValueInput(block, "STEPS", MotionBlocks::STEPS, 30.25);
69+
70+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::moveSteps)).WillOnce(Return(0));
71+
72+
compiler.init();
73+
compiler.setBlock(block);
74+
MotionBlocks::compileMoveSteps(&compiler);
75+
compiler.end();
76+
77+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }));
78+
ASSERT_EQ(compiler.constValues().size(), 1);
79+
ASSERT_EQ(compiler.constValues()[0].toDouble(), 30.25);
80+
}
81+
82+
TEST_F(MotionBlocksTest, MoveStepsImpl)
83+
{
84+
static unsigned int bytecode[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
85+
static BlockFunc functions[] = { &MotionBlocks::moveSteps };
86+
static Value constValues[] = { 30.25 };
87+
88+
Sprite sprite;
89+
sprite.setX(5.2);
90+
sprite.setY(-0.25);
91+
sprite.setDirection(-61.42);
92+
93+
VirtualMachine vm(&sprite, nullptr, nullptr);
94+
vm.setBytecode(bytecode);
95+
vm.setFunctions(functions);
96+
vm.setConstValues(constValues);
97+
vm.run();
98+
99+
ASSERT_EQ(vm.registerCount(), 0);
100+
ASSERT_EQ(std::round(sprite.x() * 100) / 100, -21.36);
101+
ASSERT_EQ(std::round(sprite.y() * 100) / 100, 14.22);
102+
}

0 commit comments

Comments
 (0)