Skip to content

Commit

Permalink
Add gfx::RectT slicing methods (aseprite#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
martincapello authored and dacap committed Aug 9, 2024
1 parent f25ea26 commit a971ccd
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
75 changes: 75 additions & 0 deletions gfx/rect.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,81 @@ class RectT {
return *this;
}

// Slices vertically this Rect along the provided px coordinate.
// Sets the left and right rects in the references of the same name.
const RectT& sliceV(T px, RectT& left, RectT& right) const {
if (px < x) {
left = RectT();
right = *this;
}
else if (px > x2()) {
left = *this;
right = RectT();
}
else {
left = RectT(x, y, px - x, h);
right = RectT(px, y, x2() - px, h);
}

return *this;
}

// Slices horizontally this Rect along the provided py coordinate.
// Sets the top and bottom rects in the references of the same name.
const RectT& sliceH(T py, RectT& top, RectT& bottom) const {
if (py < y) {
top = RectT();
bottom = *this;
}
else if (py > y2()) {
top = *this;
bottom = RectT();
}
else {
top = RectT(x, y, w, py - y);
bottom = RectT(x, py, w, y2() - py);
}

return *this;
}

// Slices this rect in nine pieces and returns all the rects in the slices
// output array. The center rect defines the relative coordinates where the
// cuts are going to be made:
//
// this (x, y, w=23, h=7) slices output
// +---------------------+ +--------+-----+------+
// | center (9,2,w=7,h=3)| | [0] | [1] | [2] |
// | +-----+ | +--------+-----+------+
// | | | | => | [3] | [4] | [5] |
// | +-----+ | +--------+-----+------+
// | | | [6] | [7] | [8] |
// +---------------------+ +--------+-----+------+
//
const RectT& nineSlice(const RectT& center, RectT slices[9]) const {
gfx::RectT<T> left, middle, right;

{
gfx::RectT<T> remaining;
this->sliceV(x + center.x, left, remaining);
remaining.sliceV(x + center.x2(), middle, right);
}

left .sliceH(y + center.y , slices[0], left);
middle.sliceH(y + center.y , slices[1], middle);
right .sliceH(y + center.y , slices[2], right);

left .sliceH(y + center.y2(), slices[3], left);
middle.sliceH(y + center.y2(), slices[4], middle);
right .sliceH(y + center.y2(), slices[5], right);

slices[6] = left;
slices[7] = middle;
slices[8] = right;

return *this;
}

};

typedef RectT<int> Rect;
Expand Down
107 changes: 107 additions & 0 deletions gfx/rect_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,113 @@ TEST(Rect, Floor)
EXPECT_EQ(gfx::Rect(-1, -1, 1, 2), gfx::RectF(-0.25, -0.75, 1, 2).floor());
}


TEST(Rect, SliceV)
{
const int x = 3, y = 4;
gfx::Rect l, r;
auto rect = gfx::Rect(x, y, 5, 7);
rect.sliceV(x, l, r);
EXPECT_EQ(gfx::Rect(x,y,0,7), l);
EXPECT_EQ(gfx::Rect(x,y,5,7), r);

rect.sliceV(x-1, l, r);
EXPECT_EQ(gfx::Rect(0,0,0,0), l);
EXPECT_EQ(gfx::Rect(x,y,5,7), r);

rect.sliceV(x+1, l, r);
EXPECT_EQ(gfx::Rect(x,y,1,7), l);
EXPECT_EQ(gfx::Rect(x+1,y,4,7), r);

rect.sliceV(x+4, l, r);
EXPECT_EQ(gfx::Rect(x,y,4,7), l);
EXPECT_EQ(gfx::Rect(x+4,y,1,7), r);

rect.sliceV(x+5, l, r);
EXPECT_EQ(gfx::Rect(x,y,5,7), l);
EXPECT_EQ(gfx::Rect(x+5,y,0,7), r);

rect.sliceV(x+6, l, r);
EXPECT_EQ(gfx::Rect(x,y,5,7), l);
EXPECT_EQ(gfx::Rect(0,0,0,0), r);
}

TEST(Rect, SliceH)
{
const int x = 3, y = 4;
gfx::Rect t, b;
auto rect = gfx::Rect(x, y, 5, 7);
rect.sliceH(y, t, b);
EXPECT_EQ(gfx::Rect(x,y,5,0), t);
EXPECT_EQ(gfx::Rect(x,y,5,7), b);

rect.sliceH(y-1, t, b);
EXPECT_EQ(gfx::Rect(0,0,0,0), t);
EXPECT_EQ(gfx::Rect(x,y,5,7), b);

rect.sliceH(y+1, t, b);
EXPECT_EQ(gfx::Rect(x,y,5,1), t);
EXPECT_EQ(gfx::Rect(x,y+1,5,6), b);

rect.sliceH(y+6, t, b);
EXPECT_EQ(gfx::Rect(x,y,5,6), t);
EXPECT_EQ(gfx::Rect(x,y+6,5,1), b);

rect.sliceH(y+7, t, b);
EXPECT_EQ(gfx::Rect(x,y,5,7), t);
EXPECT_EQ(gfx::Rect(x,y+7,5,0), b);

rect.sliceH(y+8, t, b);
EXPECT_EQ(gfx::Rect(x,y,5,7), t);
EXPECT_EQ(gfx::Rect(0,0,0,0), b);
}

TEST(Rect, NineSlice)
{
const int x = 3, y = 4;
auto rect = gfx::Rect(x, y, 6, 6);
gfx::Rect slices[9];

// Slice using an inner rect.
rect.nineSlice(gfx::Rect(3, 3, 2, 2), slices);
EXPECT_EQ(gfx::Rect(x,y,6,6), rect);
EXPECT_EQ(gfx::Rect(x ,y ,3,3), slices[0]);
EXPECT_EQ(gfx::Rect(x+3,y ,2,3), slices[1]);
EXPECT_EQ(gfx::Rect(x+5,y ,1,3), slices[2]);
EXPECT_EQ(gfx::Rect(x ,y+3,3,2), slices[3]);
EXPECT_EQ(gfx::Rect(x+3,y+3,2,2), slices[4]);
EXPECT_EQ(gfx::Rect(x+5,y+3,1,2), slices[5]);
EXPECT_EQ(gfx::Rect(x ,y+5,3,1), slices[6]);
EXPECT_EQ(gfx::Rect(x+3,y+5,2,1), slices[7]);
EXPECT_EQ(gfx::Rect(x+5,y+5,1,1), slices[8]);

// Slice using a center rect with the same size as the rect being sliced.
rect.nineSlice(gfx::Rect(0, 0, 6, 6), slices);
EXPECT_EQ(gfx::Rect(x,y,6,6), rect);
EXPECT_EQ(gfx::Rect(x ,y ,0,0), slices[0]);
EXPECT_EQ(gfx::Rect(x ,y ,6,0), slices[1]);
EXPECT_EQ(gfx::Rect(x+6,y ,0,0), slices[2]);
EXPECT_EQ(gfx::Rect(x ,y ,0,6), slices[3]);
EXPECT_EQ(gfx::Rect(x ,y ,6,6), slices[4]);
EXPECT_EQ(gfx::Rect(x+6,y ,0,6), slices[5]);
EXPECT_EQ(gfx::Rect(x ,y+6,0,0), slices[6]);
EXPECT_EQ(gfx::Rect(x ,y+6,6,0), slices[7]);
EXPECT_EQ(gfx::Rect(x+6,y+6,0,0), slices[8]);

// Slice using an outer rect.
rect.nineSlice(gfx::Rect(-1, -1, 8, 8), slices);
EXPECT_EQ(gfx::Rect(x,y,6,6), rect);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[0]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[1]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[2]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[3]);
EXPECT_EQ(gfx::Rect(x,y,6,6), slices[4]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[5]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[6]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[7]);
EXPECT_EQ(gfx::Rect(0,0,0,0), slices[8]);
}

int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
Expand Down

0 comments on commit a971ccd

Please sign in to comment.