Mir

Merge lp:~afrantzis/mir/rectangle-closed-open into lp:~mir-team/mir/trunk

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Robert Ancell
Approved revision: no longer in the source branch.
Merged at revision: 885
Proposed branch: lp:~afrantzis/mir/rectangle-closed-open
Merge into: lp:~mir-team/mir/trunk
Diff against target: 323 lines (+87/-78)
9 files modified
include/shared/mir/geometry/displacement.h (+9/-2)
include/shared/mir/geometry/rectangle.h (+6/-0)
src/server/shell/graphics_display_layout.cpp (+5/-4)
src/server/surfaces/surface_data.cpp (+1/-25)
src/shared/geometry/rectangle.cpp (+4/-5)
src/shared/geometry/rectangles.cpp (+18/-22)
tests/acceptance-tests/test_client_input.cpp (+4/-4)
tests/unit-tests/geometry/test-rectangle.cpp (+28/-0)
tests/unit-tests/surfaces/test_surface_data.cpp (+12/-16)
To merge this branch: bzr merge lp:~afrantzis/mir/rectangle-closed-open
Reviewer Review Type Date Requested Status
Kevin DuBois (community) Approve
Robert Carr (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+176348@code.launchpad.net

Commit message

geometry: Use an closed-open representation for Rectangle end points

Description of the change

geometry: Use an closed-open representation for Rectangle end points

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
Kevin DuBois (kdub) wrote :

I think the changes to the displacement/rectangles is ok, I'm still not sure though how the input coordinate system meshes with the graphics coordinate system though after this. The changes make sense with just graphics coordinates.

particularly, I think the way that these tests (tests/unit-tests/surfaces/test_surface_data.cpp) where before make more sense if the input events come in float/double coordinates instead of integer ones. Robert C, any thoughts on the matter? :)

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

> particularly, I think the way that these tests (tests/unit-tests/surfaces/test_surface_data.cpp)
> where before make more sense if the input events come in float/double coordinates instead of
> integer ones. Robert C, any thoughts on the matter? :)

> // a 1x1 window at (1,1) will get events at (1,1), (1,2) (2,1), (2,2)

This indicates a coordinate system that's closed at both ends ([P1,P2]), with whole values (eg (0,0)) at the center of a pixels. There doesn't seem to be a sane way to express that the input region is a single pixel with this system (assuming integer values, which is what we are using in the external interfaces input has).

The new system is [P1,P2) with points between pixels (pixel center at .5 values) and I think it's a more versatile representation. However, I am not not familiar with android input peculiarities, either, so let's see what Robert has to say. Even if there is a need for a different system for input, I would propose that the input stack continues to use the global mir system for external communications and do any conversions internally. Otherwise we will end up with subtle communication problems between input and the rest of the system.

Revision history for this message
Robert Carr (robertcarr) wrote :

Coordinates are a little weird in the input stack. With pointer coordinates at times having floating point values, but some operations (such as hit testing) seem to involve the InputDispatcher casting to integer, etc...

This is an item that needs some work for me.

In the mean time this LGTM!

Revision history for this message
Robert Carr (robertcarr) :
review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

> > particularly, I think the way that these tests (tests/unit-
> tests/surfaces/test_surface_data.cpp)
> > where before make more sense if the input events come in float/double
> coordinates instead of
> > integer ones. Robert C, any thoughts on the matter? :)
>
> > // a 1x1 window at (1,1) will get events at (1,1), (1,2) (2,1), (2,2)
>
> This indicates a coordinate system that's closed at both ends ([P1,P2]), with
> whole values (eg (0,0)) at the center of a pixels. There doesn't seem to be a
> sane way to express that the input region is a single pixel with this system
> (assuming integer values, which is what we are using in the external
> interfaces input has).
>
> The new system is [P1,P2) with points between pixels (pixel center at .5
> values) and I think it's a more versatile representation. However, I am not
> not familiar with android input peculiarities, either, so let's see what
> Robert has to say. Even if there is a need for a different system for input, I
> would propose that the input stack continues to use the global mir system for
> external communications and do any conversions internally. Otherwise we will
> end up with subtle communication problems between input and the rest of the
> system.

I think we both want 0.5, 0.5 at the middle of the pixel. The only difference was which edges are included in the pixel or not. This solution makes the top and left edges included in the pixel, and the bottom/right not included. I'm ok with this, as each point would correspond to just one pixel.
Since its a bit fuzzier on the input side though, I think this is good to go

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/displacement.h'
2--- include/shared/mir/geometry/displacement.h 2013-07-18 06:44:57 +0000
3+++ include/shared/mir/geometry/displacement.h 2013-07-23 09:49:30 +0000
4@@ -31,13 +31,20 @@
5
6 struct Displacement
7 {
8- DeltaX dx;
9- DeltaY dy;
10+ Displacement() {}
11+ Displacement(Displacement const&) = default;
12+ Displacement& operator=(Displacement const&) = default;
13+
14+ template<typename DeltaXType, typename DeltaYType>
15+ Displacement(DeltaXType&& dx, DeltaYType&& dy) : dx{dx}, dy{dy} {}
16
17 float length_squared() const
18 {
19 return dx.as_float() * dx.as_float() + dy.as_float() * dy.as_float();
20 }
21+
22+ DeltaX dx;
23+ DeltaY dy;
24 };
25
26 inline bool operator==(Displacement const& lhs, Displacement const& rhs)
27
28=== modified file 'include/shared/mir/geometry/rectangle.h'
29--- include/shared/mir/geometry/rectangle.h 2013-07-18 06:44:57 +0000
30+++ include/shared/mir/geometry/rectangle.h 2013-07-23 09:49:30 +0000
31@@ -35,6 +35,12 @@
32 Point top_left;
33 Size size;
34
35+ /**
36+ * The bottom right boundary point of the rectangle.
37+ *
38+ * Note that the returned point is *not* included in the rectangle
39+ * area, that is, the rectangle is represented as [top_left,bottom_right).
40+ */
41 Point bottom_right() const;
42 bool contains(Point const& p) const;
43 };
44
45=== modified file 'src/server/shell/graphics_display_layout.cpp'
46--- src/server/shell/graphics_display_layout.cpp 2013-07-19 14:34:20 +0000
47+++ src/server/shell/graphics_display_layout.cpp 2013-07-23 09:49:30 +0000
48@@ -22,6 +22,7 @@
49
50 #include "mir/geometry/rectangle.h"
51 #include "mir/geometry/rectangles.h"
52+#include "mir/geometry/displacement.h"
53
54 namespace msh = mir::shell;
55 namespace mg = mir::graphics;
56@@ -41,16 +42,16 @@
57 rect.size.width > geom::Width{0} && rect.size.height > geom::Height{0})
58 {
59 auto tl = rect.top_left;
60- auto br = rect.bottom_right();
61+ auto br_closed = rect.bottom_right() - geom::Displacement{1,1};
62
63 geom::Rectangles rectangles;
64 rectangles.add(output);
65
66- rectangles.confine(br);
67+ rectangles.confine(br_closed);
68
69 rect.size =
70- geom::Size{br.x.as_int() - tl.x.as_int() + 1,
71- br.y.as_int() - tl.y.as_int() + 1};
72+ geom::Size{br_closed.x.as_int() - tl.x.as_int() + 1,
73+ br_closed.y.as_int() - tl.y.as_int() + 1};
74 }
75 else
76 {
77
78=== modified file 'src/server/surfaces/surface_data.cpp'
79--- src/server/surfaces/surface_data.cpp 2013-07-16 15:49:19 +0000
80+++ src/server/surfaces/surface_data.cpp 2013-07-23 09:49:30 +0000
81@@ -153,35 +153,11 @@
82 notify_change();
83 }
84
85-namespace
86-{
87-
88-bool rectangle_contains_point(geom::Rectangle const& rectangle, uint32_t px, uint32_t py)
89-{
90- auto width = rectangle.size.width.as_uint32_t();
91- auto height = rectangle.size.height.as_uint32_t();
92- auto x = rectangle.top_left.x.as_uint32_t();
93- auto y = rectangle.top_left.y.as_uint32_t();
94-
95- //TODO: what if (width == 0) || (height == 0) ?
96-
97- if (px < x)
98- return false;
99- else if (py < y)
100- return false;
101- else if (px > x + width)
102- return false;
103- else if (py > y + height)
104- return false;
105- return true;
106-}
107-
108-}
109 bool ms::SurfaceData::contains(geom::Point const& point) const
110 {
111 for (auto const& rectangle : input_rectangles)
112 {
113- if (rectangle_contains_point(rectangle, point.x.as_uint32_t(), point.y.as_uint32_t()))
114+ if (rectangle.contains(point))
115 {
116 return true;
117 }
118
119=== modified file 'src/shared/geometry/rectangle.cpp'
120--- src/shared/geometry/rectangle.cpp 2013-07-18 06:44:57 +0000
121+++ src/shared/geometry/rectangle.cpp 2013-07-23 09:49:30 +0000
122@@ -24,9 +24,8 @@
123
124 geom::Point geom::Rectangle::bottom_right() const
125 {
126- assert(size.width > geom::Width{0} && size.height > geom::Height{0});
127- return {top_left.x.as_int() + size.width.as_int() - 1,
128- top_left.y.as_int() + size.height.as_int() - 1};
129+ return {top_left.x.as_int() + size.width.as_int(),
130+ top_left.y.as_int() + size.height.as_int()};
131 }
132
133 bool geom::Rectangle::contains(Point const& p) const
134@@ -35,6 +34,6 @@
135 return false;
136
137 auto br = bottom_right();
138- return p.x >= top_left.x && p.x <= br.x &&
139- p.y >= top_left.y && p.y <= br.y;
140+ return p.x >= top_left.x && p.x < br.x &&
141+ p.y >= top_left.y && p.y < br.y;
142 }
143
144=== modified file 'src/shared/geometry/rectangles.cpp'
145--- src/shared/geometry/rectangles.cpp 2013-07-19 14:12:57 +0000
146+++ src/shared/geometry/rectangles.cpp 2013-07-23 09:49:30 +0000
147@@ -35,8 +35,8 @@
148 geom::Rectangle rect_from_points(geom::Point const& tl, geom::Point const& br)
149 {
150 return {tl,
151- geom::Size{geom::Width{br.x.as_int() - tl.x.as_int() + 1},
152- geom::Height{br.y.as_int() - tl.y.as_int() + 1}}};
153+ geom::Size{geom::Width{br.x.as_int() - tl.x.as_int()},
154+ geom::Height{br.y.as_int() - tl.y.as_int()}}};
155 }
156
157 }
158@@ -78,9 +78,9 @@
159 {
160 auto br = rect.bottom_right();
161 auto min_x = rect.top_left.x;
162- auto max_x = br.x;
163+ auto max_x = geom::X{br.x.as_int() - 1};
164 auto min_y = rect.top_left.y;
165- auto max_y = br.y;
166+ auto max_y = geom::Y{br.y.as_int() - 1};
167
168 geom::Point confined_point{clamp(point.x, min_x, max_x),
169 clamp(point.y, min_y, max_y)};
170@@ -113,25 +113,21 @@
171
172 for (auto const& rect : rectangles)
173 {
174- if (rect.size.width.as_int() > 0 &&
175- rect.size.height.as_int() > 0)
176- {
177- geom::Point rtl = rect.top_left;
178- geom::Point rbr = rect.bottom_right();
179+ geom::Point rtl = rect.top_left;
180+ geom::Point rbr = rect.bottom_right();
181
182- if (!points_initialized)
183- {
184- tl = rtl;
185- br = rbr;
186- points_initialized = true;
187- }
188- else
189- {
190- tl.x = std::min(rtl.x, tl.x);
191- tl.y = std::min(rtl.y, tl.y);
192- br.x = std::max(rbr.x, br.x);
193- br.y = std::max(rbr.y, br.y);
194- }
195+ if (!points_initialized)
196+ {
197+ tl = rtl;
198+ br = rbr;
199+ points_initialized = true;
200+ }
201+ else
202+ {
203+ tl.x = std::min(rtl.x, tl.x);
204+ tl.y = std::min(rtl.y, tl.y);
205+ br.x = std::max(rbr.x, br.x);
206+ br.y = std::max(rbr.y, br.y);
207 }
208 }
209
210
211=== modified file 'tests/acceptance-tests/test_client_input.cpp'
212--- tests/acceptance-tests/test_client_input.cpp 2013-07-19 14:27:14 +0000
213+++ tests/acceptance-tests/test_client_input.cpp 2013-07-23 09:49:30 +0000
214@@ -370,8 +370,8 @@
215 {
216 wait_until_client_appears(test_client_name);
217
218- fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width,
219- InputClient::surface_height));
220+ fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width - 1,
221+ InputClient::surface_height - 1));
222 fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2));
223 }
224 } server_config;
225@@ -390,8 +390,8 @@
226 // We should see the cursor enter
227 EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1);
228 EXPECT_CALL(*handler, handle_input(
229- MotionEventWithPosition(InputClient::surface_width,
230- InputClient::surface_height))).Times(1)
231+ MotionEventWithPosition(InputClient::surface_width - 1,
232+ InputClient::surface_height - 1))).Times(1)
233 .WillOnce(mt::WakeUp(&events_received));
234 // But we should not receive an event for the second movement outside of our surface!
235 }
236
237=== modified file 'tests/unit-tests/geometry/test-rectangle.cpp'
238--- tests/unit-tests/geometry/test-rectangle.cpp 2013-07-11 16:54:41 +0000
239+++ tests/unit-tests/geometry/test-rectangle.cpp 2013-07-23 09:49:30 +0000
240@@ -43,3 +43,31 @@
241 EXPECT_EQ(Size(), default_rect.size);
242 EXPECT_NE(rect, default_rect);
243 }
244+
245+TEST(geometry, rectangle_bottom_right)
246+{
247+ using namespace testing;
248+ using namespace geom;
249+
250+ Rectangle const rect{{0,0}, {1,1}};
251+ Rectangle const rect_empty{{2,2}, {0,0}};
252+
253+ EXPECT_EQ(Point(1,1), rect.bottom_right());
254+ EXPECT_EQ(Point(2,2), rect_empty.bottom_right());
255+}
256+
257+TEST(geometry, rectangle_contains)
258+{
259+ using namespace testing;
260+ using namespace geom;
261+
262+ Rectangle const rect{{0,0}, {1,1}};
263+ Rectangle const rect_empty{{2,2}, {0,0}};
264+
265+ EXPECT_TRUE(rect.contains({0,0}));
266+ EXPECT_FALSE(rect.contains({0,1}));
267+ EXPECT_FALSE(rect.contains({1,0}));
268+ EXPECT_FALSE(rect.contains({1,1}));
269+
270+ EXPECT_FALSE(rect_empty.contains({2,2}));
271+}
272
273=== modified file 'tests/unit-tests/surfaces/test_surface_data.cpp'
274--- tests/unit-tests/surfaces/test_surface_data.cpp 2013-07-18 14:41:31 +0000
275+++ tests/unit-tests/surfaces/test_surface_data.cpp 2013-07-23 09:49:30 +0000
276@@ -188,17 +188,17 @@
277 surface_state.frame_posted();
278 }
279
280-// a 1x1 window at (1,1) will get events at (1,1), (1,2) (2,1), (2,2)
281+// a 1x1 window at (1,1) will get events at (1,1)
282 TEST_F(SurfaceDataTest, default_region_is_surface_rectangle)
283 {
284 geom::Point pt(1,1);
285 geom::Size one_by_one{geom::Width{1}, geom::Height{1}};
286 ms::SurfaceData surface_state{name, geom::Rectangle{pt, one_by_one}, mock_change_cb};
287
288- std::initializer_list <geom::Point> contained_pt{geom::Point{geom::X{1}, geom::Y{1}},
289- geom::Point{geom::X{1}, geom::Y{2}},
290- geom::Point{geom::X{2}, geom::Y{1}},
291- geom::Point{geom::X{2}, geom::Y{2}}};
292+ std::vector<geom::Point> contained_pt
293+ {
294+ geom::Point{geom::X{1}, geom::Y{1}}
295+ };
296
297 for(auto x = 0; x <= 3; x++)
298 {
299@@ -228,17 +228,13 @@
300 ms::SurfaceData surface_state{name, rect, mock_change_cb};
301 surface_state.set_input_region(rectangles);
302
303- std::initializer_list <geom::Point> contained_pt{//region0 points
304- geom::Point{geom::X{0}, geom::Y{0}},
305- geom::Point{geom::X{0}, geom::Y{1}},
306- geom::Point{geom::X{1}, geom::Y{0}},
307- //overlapping point
308- geom::Point{geom::X{1}, geom::Y{1}},
309- //region1 points
310- geom::Point{geom::X{1}, geom::Y{2}},
311- geom::Point{geom::X{2}, geom::Y{1}},
312- geom::Point{geom::X{2}, geom::Y{2}},
313- };
314+ std::vector<geom::Point> contained_pt
315+ {
316+ //region0 points
317+ geom::Point{geom::X{0}, geom::Y{0}},
318+ //region1 points
319+ geom::Point{geom::X{1}, geom::Y{1}},
320+ };
321
322 for(auto x = 0; x <= 3; x++)
323 {

Subscribers

People subscribed via source and target branches