Skip to content

Commit 871b0b1

Browse files
committed
Implement pen_changePenHueBy block
1 parent 546c1f3 commit 871b0b1

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed

src/blocks/penblocks.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ void PenBlocks::registerBlocks(IEngine *engine)
2828
engine->addCompileFunction(this, "pen_setPenColorToColor", &compileSetPenColorToColor);
2929
engine->addCompileFunction(this, "pen_changePenSizeBy", &compileChangePenSizeBy);
3030
engine->addCompileFunction(this, "pen_setPenSizeTo", &compileSetPenSizeTo);
31+
engine->addCompileFunction(this, "pen_changePenHueBy", &compileChangePenHueBy);
3132

3233
// Inputs
3334
engine->addInput(this, "COLOR", COLOR);
3435
engine->addInput(this, "SIZE", SIZE);
36+
engine->addInput(this, "HUE", HUE);
3537
}
3638

3739
void PenBlocks::compileClear(Compiler *compiler)
@@ -67,6 +69,12 @@ void PenBlocks::compileSetPenSizeTo(libscratchcpp::Compiler *compiler)
6769
compiler->addFunctionCall(&setPenSizeTo);
6870
}
6971

72+
void PenBlocks::compileChangePenHueBy(libscratchcpp::Compiler *compiler)
73+
{
74+
compiler->addInput(HUE);
75+
compiler->addFunctionCall(&changePenHueBy);
76+
}
77+
7078
unsigned int PenBlocks::clear(VirtualMachine *vm)
7179
{
7280
IPenLayer *penLayer = PenLayer::getProjectPenLayer(vm->engine());
@@ -119,6 +127,18 @@ unsigned int PenBlocks::setPenSizeTo(libscratchcpp::VirtualMachine *vm)
119127
return 1;
120128
}
121129

130+
unsigned int PenBlocks::changePenHueBy(libscratchcpp::VirtualMachine *vm)
131+
{
132+
SpriteModel *model = getSpriteModel(vm);
133+
134+
if (model) {
135+
const double colorChange = vm->getInput(0, 1)->toDouble() / 2;
136+
setOrChangeColorParam(model->penAttributes(), ColorParam::COLOR, colorChange, true);
137+
}
138+
139+
return 1;
140+
}
141+
122142
unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm)
123143
{
124144
SpriteModel *model = getSpriteModel(vm);
@@ -165,3 +185,27 @@ SpriteModel *PenBlocks::getSpriteModel(libscratchcpp::VirtualMachine *vm)
165185
SpriteModel *model = static_cast<SpriteModel *>(sprite->getInterface());
166186
return model;
167187
}
188+
189+
void PenBlocks::setOrChangeColorParam(PenAttributes &penAttributes, ColorParam param, double value, bool change)
190+
{
191+
PenState penState(penAttributes.color);
192+
193+
switch (param) {
194+
case ColorParam::COLOR:
195+
penState.color = wrapClamp(value + (change ? penState.color : 0), 0, 100);
196+
break;
197+
}
198+
199+
const int h = std::round(std::fmod(penState.color * 360 / 100, 360.0));
200+
const int s = std::round(penState.saturation * 2.55);
201+
const int v = std::round(penState.brightness * 2.55);
202+
const int a = std::round((100 - penState.transparency) * 2.55);
203+
penAttributes.color = QColor::fromHsv(h, s, v, a);
204+
}
205+
206+
double PenBlocks::wrapClamp(double n, double min, double max)
207+
{
208+
// TODO: Move this to a separate class
209+
const double range = max - min + 1;
210+
return n - (std::floor((n - min) / range) * range);
211+
}

src/blocks/penblocks.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22

33
#pragma once
44

5+
#include <QColor>
56
#include <scratchcpp/iblocksection.h>
67

78
namespace scratchcpprender
89
{
910

1011
class SpriteModel;
12+
class PenAttributes;
1113

1214
class PenBlocks : public libscratchcpp::IBlockSection
1315
{
1416
public:
1517
enum Inputs
1618
{
1719
COLOR,
18-
SIZE
20+
SIZE,
21+
HUE
1922
};
2023

2124
std::string name() const override;
@@ -28,16 +31,43 @@ class PenBlocks : public libscratchcpp::IBlockSection
2831
static void compileSetPenColorToColor(libscratchcpp::Compiler *compiler);
2932
static void compileChangePenSizeBy(libscratchcpp::Compiler *compiler);
3033
static void compileSetPenSizeTo(libscratchcpp::Compiler *compiler);
34+
static void compileChangePenHueBy(libscratchcpp::Compiler *compiler);
3135

3236
static unsigned int clear(libscratchcpp::VirtualMachine *vm);
3337
static unsigned int penDown(libscratchcpp::VirtualMachine *vm);
3438
static unsigned int penUp(libscratchcpp::VirtualMachine *vm);
3539
static unsigned int setPenColorToColor(libscratchcpp::VirtualMachine *vm);
40+
static unsigned int setPenHueTo(libscratchcpp::VirtualMachine *vm);
3641
static unsigned int changePenSizeBy(libscratchcpp::VirtualMachine *vm);
3742
static unsigned int setPenSizeTo(libscratchcpp::VirtualMachine *vm);
43+
static unsigned int changePenHueBy(libscratchcpp::VirtualMachine *vm);
3844

3945
private:
46+
struct PenState
47+
{
48+
PenState(const QColor &color)
49+
{
50+
QColor hsvColor = color.toHsv();
51+
this->color = hsvColor.hue() * 100 / 360.0;
52+
this->saturation = hsvColor.saturationF() * 100;
53+
this->brightness = hsvColor.valueF() * 100;
54+
this->transparency = 100 * (1 - hsvColor.alphaF());
55+
}
56+
57+
double color = 0;
58+
double saturation = 0;
59+
double brightness = 0;
60+
double transparency = 0;
61+
};
62+
63+
enum class ColorParam
64+
{
65+
COLOR
66+
};
67+
4068
static SpriteModel *getSpriteModel(libscratchcpp::VirtualMachine *vm);
69+
static void setOrChangeColorParam(PenAttributes &penAttributes, ColorParam param, double value, bool change);
70+
static double wrapClamp(double n, double min, double max);
4171
};
4272

4373
} // namespace scratchcpprender

test/blocks/pen_blocks_test.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ TEST_F(PenBlocksTest, RegisterBlocks)
6767
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenColorToColor", &PenBlocks::compileSetPenColorToColor));
6868
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenSizeBy", &PenBlocks::compileChangePenSizeBy));
6969
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_setPenSizeTo", &PenBlocks::compileSetPenSizeTo));
70+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "pen_changePenHueBy", &PenBlocks::compileChangePenHueBy));
7071

7172
// Inputs
7273
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "COLOR", PenBlocks::COLOR));
7374
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SIZE", PenBlocks::SIZE));
75+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "HUE", PenBlocks::HUE));
7476

7577
m_section->registerBlocks(&m_engineMock);
7678
}
@@ -425,3 +427,77 @@ TEST_F(PenBlocksTest, SetPenSizeToImpl)
425427
ASSERT_EQ(vm.registerCount(), 0);
426428
ASSERT_EQ(model.penAttributes().diameter, 1200);
427429
}
430+
431+
TEST_F(PenBlocksTest, ChangePenHueBy)
432+
{
433+
Compiler compiler(&m_engineMock);
434+
435+
// change pen hue by (4.5)
436+
auto block1 = std::make_shared<Block>("a", "pen_changePenHueBy");
437+
addValueInput(block1, "HUE", PenBlocks::HUE, 4.5);
438+
439+
// change pen hue by (null block)
440+
auto block2 = std::make_shared<Block>("b", "pen_changePenHueBy");
441+
addObscuredInput(block2, "HUE", PenBlocks::HUE, createNullBlock("c"));
442+
443+
compiler.init();
444+
445+
EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenHueBy)).WillOnce(Return(2));
446+
compiler.setBlock(block1);
447+
PenBlocks::compileChangePenHueBy(&compiler);
448+
449+
EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenHueBy)).WillOnce(Return(2));
450+
compiler.setBlock(block2);
451+
PenBlocks::compileChangePenHueBy(&compiler);
452+
453+
compiler.end();
454+
455+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT }));
456+
ASSERT_EQ(compiler.constValues().size(), 1);
457+
ASSERT_EQ(compiler.constValues()[0].toDouble(), 4.5);
458+
ASSERT_TRUE(compiler.variables().empty());
459+
ASSERT_TRUE(compiler.lists().empty());
460+
}
461+
462+
TEST_F(PenBlocksTest, ChangePenHueByImpl)
463+
{
464+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
465+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT };
466+
static BlockFunc functions[] = { &PenBlocks::changePenHueBy };
467+
static Value constValues[] = { 125.7, -114.09 };
468+
469+
SpriteModel model;
470+
model.penAttributes().color.setAlpha(150);
471+
Sprite sprite;
472+
sprite.setInterface(&model);
473+
474+
VirtualMachine vm(&sprite, &m_engineMock, nullptr);
475+
vm.setBytecode(bytecode1);
476+
vm.setFunctions(functions);
477+
vm.setConstValues(constValues);
478+
479+
vm.run();
480+
ASSERT_EQ(vm.registerCount(), 0);
481+
ASSERT_EQ(model.penAttributes().color, QColor::fromHsv(103, 255, 255, 150));
482+
483+
vm.reset();
484+
vm.run();
485+
ASSERT_EQ(vm.registerCount(), 0);
486+
ASSERT_EQ(model.penAttributes().color, QColor::fromHsv(329, 255, 255, 150));
487+
488+
vm.reset();
489+
vm.run();
490+
ASSERT_EQ(vm.registerCount(), 0);
491+
ASSERT_EQ(model.penAttributes().color, QColor::fromHsv(192, 255, 255, 150));
492+
493+
vm.reset();
494+
vm.setBytecode(bytecode2);
495+
vm.run();
496+
ASSERT_EQ(vm.registerCount(), 0);
497+
ASSERT_EQ(model.penAttributes().color, QColor::fromHsv(350, 255, 255, 150));
498+
499+
vm.reset();
500+
vm.run();
501+
ASSERT_EQ(vm.registerCount(), 0);
502+
ASSERT_EQ(model.penAttributes().color, QColor::fromHsv(145, 255, 255, 150));
503+
}

0 commit comments

Comments
 (0)