Skip to content

Commit 350c8c3

Browse files
authored
Merge pull request #298 from scratchcpp/sprite_bounding_rect
Add Sprite::boundingRect() method
2 parents f24ce30 + fb2805b commit 350c8c3

File tree

14 files changed

+436
-3
lines changed

14 files changed

+436
-3
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ target_sources(scratchcpp
5050
include/scratchcpp/keyevent.h
5151
include/scratchcpp/iimageformat.h
5252
include/scratchcpp/iimageformatfactory.h
53+
include/scratchcpp/rect.h
5354
)
5455

5556
add_library(zip SHARED

include/scratchcpp/rect.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "global.h"
6+
#include "spimpl.h"
7+
8+
namespace libscratchcpp
9+
{
10+
11+
class RectPrivate;
12+
13+
/*! The Rect class represents a rectangle. */
14+
class LIBSCRATCHCPP_EXPORT Rect
15+
{
16+
public:
17+
Rect(double left, double top, double right, double bottom);
18+
Rect();
19+
20+
double left() const;
21+
void setLeft(double left);
22+
23+
double top() const;
24+
void setTop(double top);
25+
26+
double right() const;
27+
void setRight(double right);
28+
29+
double bottom() const;
30+
void setBottom(double bottom);
31+
32+
double width() const;
33+
double height() const;
34+
35+
private:
36+
spimpl::impl_ptr<RectPrivate> impl;
37+
};
38+
39+
} // namespace libscratchcpp

include/scratchcpp/sprite.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace libscratchcpp
88
{
99

1010
class ISpriteHandler;
11+
class Rect;
1112
class SpritePrivate;
1213

1314
/*! \brief The Sprite class represents a Scratch sprite. */
@@ -62,6 +63,8 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
6263
void setRotationStyle(const std::string &newRotationStyle);
6364
void setRotationStyle(const char *newRotationStyle);
6465

66+
Rect boundingRect() const;
67+
6568
private:
6669
Target *dataSource() const override;
6770

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ target_sources(scratchcpp
66
scratchconfiguration.cpp
77
scratchconfiguration_p.cpp
88
scratchconfiguration_p.h
9+
rect.cpp
10+
rect_p.cpp
11+
rect_p.h
912
)
1013

1114
add_subdirectory(blocks)

src/rect.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include <scratchcpp/rect.h>
4+
#include <cmath>
5+
#include "rect_p.h"
6+
7+
using namespace libscratchcpp;
8+
9+
/*! Constructs Rect. */
10+
Rect::Rect(double left, double top, double right, double bottom) :
11+
impl(spimpl::make_impl<RectPrivate>(left, top, right, bottom))
12+
{
13+
}
14+
15+
/*! \copydoc Rect() */
16+
Rect::Rect() :
17+
impl(spimpl::make_impl<RectPrivate>())
18+
{
19+
}
20+
21+
/*! Returns the x-coordinate of the left edge. */
22+
double Rect::left() const
23+
{
24+
return impl->left;
25+
}
26+
27+
/*! Sets the x-coordinate of the left edge. */
28+
void Rect::setLeft(double left)
29+
{
30+
impl->left = left;
31+
}
32+
33+
/*! Returns the y-coordinate of the top edge. */
34+
double Rect::top() const
35+
{
36+
return impl->top;
37+
}
38+
39+
/*! Sets the y-coordinate of the top edge. */
40+
void Rect::setTop(double top)
41+
{
42+
impl->top = top;
43+
}
44+
45+
/*! Returns the x-coordinate of the right edge. */
46+
double Rect::right() const
47+
{
48+
return impl->right;
49+
}
50+
51+
/*! Sets the x-coordinate of the right edge. */
52+
void Rect::setRight(double right)
53+
{
54+
impl->right = right;
55+
}
56+
57+
/*! Returns the y-coordinate of the bottom edge. */
58+
double Rect::bottom() const
59+
{
60+
return impl->bottom;
61+
}
62+
63+
/*! Sets the y-coordinate of the bottom edge. */
64+
void Rect::setBottom(double bottom)
65+
{
66+
impl->bottom = bottom;
67+
}
68+
69+
/*! Returns the width of the rectangle. */
70+
double Rect::width() const
71+
{
72+
return std::abs(impl->right - impl->left);
73+
}
74+
75+
/*! Returns the height of the rectangle. */
76+
double Rect::height() const
77+
{
78+
return std::abs(impl->top - impl->bottom);
79+
}

src/rect_p.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "rect_p.h"
4+
5+
namespace libscratchcpp
6+
{
7+
8+
RectPrivate::RectPrivate(double left, double top, double right, double bottom) :
9+
left(left),
10+
top(top),
11+
right(right),
12+
bottom(bottom)
13+
{
14+
}
15+
16+
RectPrivate::RectPrivate()
17+
{
18+
}
19+
20+
} // namespace libscratchcpp

src/rect_p.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
namespace libscratchcpp
6+
{
7+
8+
struct RectPrivate
9+
{
10+
RectPrivate(double left, double top, double right, double bottom);
11+
RectPrivate();
12+
13+
double left = 0;
14+
double top = 0;
15+
double right = 0;
16+
double bottom = 0;
17+
};
18+
19+
} // namespace libscratchcpp

src/scratch/sprite.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <scratchcpp/variable.h>
77
#include <scratchcpp/list.h>
88
#include <scratchcpp/costume.h>
9+
#include <scratchcpp/rect.h>
910
#include <cassert>
1011

1112
#include "sprite_p.h"
@@ -15,7 +16,7 @@ using namespace libscratchcpp;
1516
/*! Constructs Sprite. */
1617
Sprite::Sprite() :
1718
Target(),
18-
impl(spimpl::make_unique_impl<SpritePrivate>())
19+
impl(spimpl::make_unique_impl<SpritePrivate>(this))
1920
{
2021
}
2122

@@ -297,6 +298,15 @@ void Sprite::setRotationStyle(const char *newRotationStyle)
297298
setRotationStyle(std::string(newRotationStyle));
298299
}
299300

301+
/*! Returns the bounding rectangle of the sprite. */
302+
Rect Sprite::boundingRect() const
303+
{
304+
Rect ret;
305+
impl->getBoundingRect(&ret);
306+
307+
return ret;
308+
}
309+
300310
Target *Sprite::dataSource() const
301311
{
302312
return impl->cloneRoot;

src/scratch/sprite_p.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include <scratchcpp/ispritehandler.h>
4+
#include <scratchcpp/rect.h>
5+
#include <scratchcpp/costume.h>
6+
#include <cmath>
47

58
#include "sprite_p.h"
69

710
using namespace libscratchcpp;
811

9-
SpritePrivate::SpritePrivate()
12+
static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20
13+
14+
SpritePrivate::SpritePrivate(Sprite *sprite) :
15+
sprite(sprite)
1016
{
1117
}
1218

@@ -28,3 +34,59 @@ void SpritePrivate::setCostumeData(const char *data)
2834
if (iface)
2935
iface->onCostumeChanged(data);
3036
}
37+
38+
void SpritePrivate::getBoundingRect(Rect *out) const
39+
{
40+
assert(out);
41+
assert(sprite);
42+
// TODO: Make currentCostume() return the costume
43+
auto costume = sprite->costumeAt(sprite->currentCostume() - 1);
44+
45+
if (!costume) {
46+
out->setLeft(0);
47+
out->setTop(0);
48+
out->setRight(0);
49+
out->setBottom(0);
50+
return;
51+
}
52+
53+
double cosTheta = std::cos((90 - direction) * pi / 180);
54+
double sinTheta = std::sin((90 - direction) * pi / 180);
55+
double maxX = 0, maxY = 0, minX = 0, minY = 0;
56+
bool firstPixel = true;
57+
unsigned int width = costume->width();
58+
unsigned int height = costume->height();
59+
double rotationCenterX = width / 2.0 + costume->rotationCenterX();
60+
double rotationCenterY = height / 2.0 + costume->rotationCenterY();
61+
Rgb **bitmap = costume->bitmap();
62+
63+
for (unsigned int y = 0; y < height; y++) {
64+
for (unsigned int x = 0; x < width; x++) {
65+
if (bitmap[y][x] != rgba(0, 0, 0, 0)) {
66+
double rotatedX = ((x - rotationCenterX) * cosTheta - (y - rotationCenterY) * sinTheta);
67+
double rotatedY = ((x - rotationCenterX) * sinTheta + (y - rotationCenterY) * cosTheta);
68+
69+
if (firstPixel) {
70+
firstPixel = false;
71+
minX = maxX = rotatedX;
72+
minY = maxY = rotatedY;
73+
} else {
74+
if (rotatedX < minX)
75+
minX = rotatedX;
76+
else if (rotatedX > maxX)
77+
maxX = rotatedX;
78+
79+
if (rotatedY < minY)
80+
minY = rotatedY;
81+
else if (rotatedY > maxY)
82+
maxY = rotatedY;
83+
}
84+
}
85+
}
86+
}
87+
88+
out->setLeft(x + minX);
89+
out->setTop(y + maxY);
90+
out->setRight(x + maxX);
91+
out->setBottom(y + minY);
92+
}

src/scratch/sprite_p.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
namespace libscratchcpp
88
{
99

10+
class Rect;
11+
1012
struct SpritePrivate
1113
{
12-
SpritePrivate();
14+
SpritePrivate(Sprite *sprite);
1315
SpritePrivate(const SpritePrivate &) = delete;
1416

1517
void removeClone(Sprite *clone);
1618

1719
void setCostumeData(const char *data);
20+
void getBoundingRect(Rect *out) const;
1821

22+
Sprite *sprite = nullptr;
1923
ISpriteHandler *iface = nullptr;
2024
Sprite *cloneRoot = nullptr;
2125
Sprite *cloneParent = nullptr;

0 commit comments

Comments
 (0)