Mir

Merge lp:~vanvugt/mir/Rectangle-contains-Rectangle into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: no longer in the source branch.
Merged at revision: 1135
Proposed branch: lp:~vanvugt/mir/Rectangle-contains-Rectangle
Merge into: lp:mir
Diff against target: 143 lines (+110/-1)
3 files modified
include/shared/mir/geometry/rectangle.h (+9/-0)
src/shared/geometry/rectangle.cpp (+10/-0)
tests/unit-tests/geometry/test-rectangle.cpp (+91/-1)
To merge this branch: bzr merge lp:~vanvugt/mir/Rectangle-contains-Rectangle
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Review via email: mp+190065@code.launchpad.net

Commit message

Add method for testing if Rectangle::contains(Rectangle), which is the basis
of any occlusion detection.

Description of the change

Prerequisite for fixing bug 1227739.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

22 + return r.top_left.x >= top_left.x &&
23 + r.top_left.x.as_int() + r.size.width.as_int() <=
24 + top_left.x.as_int() + size.width.as_int() &&
25 + r.top_left.y >= top_left.y &&
26 + r.top_left.y.as_int() + r.size.height.as_int() <=
27 + top_left.y.as_int() + size.height.as_int();

I do find this a bit hard to parse. I'd write something like:

    auto const btmright = bottom_right();
    auto const r_btmright = r.bottom_right();

    return r.top_left.x >= top_left.x && r.top_left.y >= top_left.y &&
           r_btmright.x <= btmright.x && r_btmright.y <= btmright.y;

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

The current code allows an empty rectangle (x,y)(0x0) to contain another empty rectangle (x,y)(0x0).

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alexandros,

I believe that is acceptable for emptiness to contain emptiness. It certainly doesn't break any intended use cases I can think of, or represent any problem in theory. Does it?

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> I believe that is acceptable for emptiness to contain emptiness. It certainly doesn't break
> any intended use cases I can think of, or represent any problem in theory. Does it?

I think that for our use cases it may be a bit surprising for an empty rectangle to contain anything at all, but I can't think of a concrete example where this would be a major problem. It just means that all code needs to be ready to handle empty rectangles, even if it's run conditionally after a rect.contains(rect) check.

From a theoretical POV, if an empty rectangle is just the empty set of pixels, then we need (x,y)(0x0) to be contained by any (x',y')(0x0). Right now (x,y)(0x0) only contains itself.

I think we should be consistent by either making empty rectangles not contain anything, or by making all empty rectangles contain all other empty rectangles, although it may not make any difference practically either way.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> The current code allows an empty rectangle (x,y)(0x0) to contain another empty
> rectangle (x,y)(0x0).

What is special about empty rectangles?

It seems perfectly reasonable that two rectangles of the same size and location each contain the other. (Or that a rectangle contains itself.)

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> What is special about empty rectangles?

> It seems perfectly reasonable that two rectangles of the same size and location
> each contain the other. (Or that a rectangle contains itself.)

It depends on what model we are using for rectangles in our discrete 2D domain. If we consider a rectangle to be a set (in the formal sense) of pixels, then we should be following the rules of sets. In this case, all rectangles with W=0 H=0 map to the empty set, so they should all contain each other.

If we are not following the set model, and then we can make our own rules for cases like this.

Anyway, since this is not likely to have any impact in practice (with the exception of "It just means that all code needs to be ready to handle empty rectangles, even if it's run conditionally after a rect.contains(rect) check"), I am OK with what is proposed, and we can reconsider if it becomes a problem.

I would like to see a comment describing the expected behavior, though (=> Needs Fixing).

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I have no preference for the behaviour of empty rectangles. And I think we should leave the behaviour "officially undefined" until there's a good enough reason to define it.

If we declare rules for empty rectangles now without having a reason, we risk needing to change them in future if a use case is found.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> I have no preference for the behaviour of empty rectangles. And I think we should leave the behaviour
> "officially undefined" until there's a good enough reason to define it.

Empty rectangles are not an edge case we can ignore, we need some form of support for them. We are implicitly defining the behavior, and code will eventually depend on it in some way, even if it's in the form of extra logic to avoid "undefined" behavior. So, UB gains us nothing really. We are better off defining the behavior and if we need to change it in the future, then so be it.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

100 + EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));
104 + EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));

125 + EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));
129 + EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));

Is there a reason to recheck for these?

Having a unit test for the expected behavior of empty rectangles is, of course, desirable, but I think a comment in the code would be more helpful for users of the class.

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

nit:
int const vs const int
most other places we have "int const"
http://unity.ubuntu.com/mir/cppguide/index.html?showone=Use_of_const#Use_of_const

i'm fine with empty rectangles being treated as points (or lines).

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alexandros:
Fixed and fixed. I think there was some copy/paste or search/replace mistake there.

Kevin:
The cppguide says it "favors" the late const form without providing any solid justification other than loosely mentioning "This keeps the const with the type modifier (& or *)". So firstly, that's not a justification. The C/C++ language specs clearly define "const" as applying to the type and not the type modifier. So late const is misleading in the least. It creates confusion if you try to express "const T * const p", which is more clearly stated with prefix const. And the suffix const form is syntactically impossible for expressing "T * const p". So I think we need to update the cppguide... I mentioned this on the mailing list some time back.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'include/shared/mir/geometry/rectangle.h'
--- include/shared/mir/geometry/rectangle.h 2013-08-28 03:41:48 +0000
+++ include/shared/mir/geometry/rectangle.h 2013-10-15 03:28:47 +0000
@@ -43,6 +43,15 @@
43 */43 */
44 Point bottom_right() const;44 Point bottom_right() const;
45 bool contains(Point const& p) const;45 bool contains(Point const& p) const;
46
47 /**
48 * Test if the rectangle contains another.
49 *
50 * Note that an empty rectangle can still contain other empty rectangles,
51 * which are treated as points or lines of thickness zero.
52 */
53 bool contains(Rectangle const& r) const;
54
46 bool overlaps(Rectangle const& r) const;55 bool overlaps(Rectangle const& r) const;
47};56};
4857
4958
=== modified file 'src/shared/geometry/rectangle.cpp'
--- src/shared/geometry/rectangle.cpp 2013-08-28 03:41:48 +0000
+++ src/shared/geometry/rectangle.cpp 2013-10-15 03:28:47 +0000
@@ -28,6 +28,16 @@
28 top_left.y.as_int() + size.height.as_int()};28 top_left.y.as_int() + size.height.as_int()};
29}29}
3030
31bool geom::Rectangle::contains(Rectangle const& r) const
32{
33 return r.top_left.x >= top_left.x &&
34 r.top_left.x.as_int() + r.size.width.as_int() <=
35 top_left.x.as_int() + size.width.as_int() &&
36 r.top_left.y >= top_left.y &&
37 r.top_left.y.as_int() + r.size.height.as_int() <=
38 top_left.y.as_int() + size.height.as_int();
39}
40
31bool geom::Rectangle::contains(Point const& p) const41bool geom::Rectangle::contains(Point const& p) const
32{42{
33 if (size.width == geom::Width{0} || size.height == geom::Height{0})43 if (size.width == geom::Width{0} || size.height == geom::Height{0})
3444
=== modified file 'tests/unit-tests/geometry/test-rectangle.cpp'
--- tests/unit-tests/geometry/test-rectangle.cpp 2013-08-28 03:41:48 +0000
+++ tests/unit-tests/geometry/test-rectangle.cpp 2013-10-15 03:28:47 +0000
@@ -56,7 +56,97 @@
56 EXPECT_EQ(Point(2,2), rect_empty.bottom_right());56 EXPECT_EQ(Point(2,2), rect_empty.bottom_right());
57}57}
5858
59TEST(geometry, rectangle_contains)59TEST(geometry, rectangle_contains_rectangle)
60{
61 using namespace testing;
62 using namespace geom;
63
64 const int left = 3;
65 const int top = 3;
66 const int width = 3;
67 const int height = 3;
68 const Rectangle r{{left,top}, {width,height}};
69
70 const int right = left + width;
71 const int bottom = top + height;
72
73 // Test for _every_ rectangle contained in r
74 for (int y = top; y <= bottom; y++)
75 {
76 for (int x = left; x <= right; x++)
77 {
78 const int max_height = bottom - y;
79 const int max_width = right - x;
80 for (int h = 0; h <= max_height; h++)
81 {
82 for (int w = 0; w <= max_width; w++)
83 {
84 EXPECT_TRUE(r.contains(Rectangle{{x,y}, {w,h}}));
85 }
86 }
87
88 EXPECT_FALSE(r.contains(Rectangle{{x,y}, {max_width+1,max_height}}));
89 EXPECT_FALSE(r.contains(Rectangle{{x,y}, {max_width,max_height+1}}));
90 }
91 }
92
93 EXPECT_FALSE(r.contains(Rectangle{{2,2}, {5,5}}));
94 EXPECT_FALSE(r.contains(Rectangle{{2,2}, {1,1}}));
95 EXPECT_FALSE(r.contains(Rectangle{{-4,-3}, {10,10}}));
96
97 EXPECT_FALSE(r.contains(Rectangle{{1,3}, {3,3}}));
98 EXPECT_FALSE(r.contains(Rectangle{{3,1}, {3,3}}));
99 EXPECT_FALSE(r.contains(Rectangle{{5,3}, {3,3}}));
100 EXPECT_FALSE(r.contains(Rectangle{{3,5}, {3,3}}));
101
102 EXPECT_FALSE(r.contains(Rectangle{{9,9}, {1,1}}));
103 EXPECT_FALSE(r.contains(Rectangle{{-6,-7}, {3,4}}));
104}
105
106TEST(geometry, empty_rectangle_contains_point_only)
107{
108 using namespace testing;
109 using namespace geom;
110
111 const int left = 3;
112 const int top = 3;
113 const Rectangle r{{left,top}, {0,0}};
114
115 EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,0}}));
116
117 EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));
118 EXPECT_FALSE(r.contains(Rectangle{{left,top}, {0,1}}));
119 EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,1}}));
120 EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {3,0}}));
121 EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {0,0}}));
122 EXPECT_FALSE(r.contains(Rectangle{{left,top+1}, {0,0}}));
123}
124
125TEST(geometry, elongated_empty_rectangle_contains_points_only)
126{
127 using namespace testing;
128 using namespace geom;
129
130 const int left = 3;
131 const int top = 3;
132 const Rectangle r{{left,top}, {0,3}};
133
134 EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,0}}));
135 EXPECT_TRUE(r.contains(Rectangle{{left,top+1}, {0,0}}));
136 EXPECT_TRUE(r.contains(Rectangle{{left,top+2}, {0,0}}));
137 EXPECT_TRUE(r.contains(Rectangle{{left,top+3}, {0,0}}));
138
139 EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,1}}));
140 EXPECT_TRUE(r.contains(Rectangle{{left,top+1}, {0,2}}));
141
142 EXPECT_FALSE(r.contains(Rectangle{{left,top+4}, {0,0}}));
143 EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}}));
144 EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,1}}));
145 EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {3,0}}));
146 EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {0,0}}));
147}
148
149TEST(geometry, rectangle_contains_point)
60{150{
61 using namespace testing;151 using namespace testing;
62 using namespace geom;152 using namespace geom;

Subscribers

People subscribed via source and target branches