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

Subscribers

People subscribed via source and target branches