Skip to content

Commit 26090e4

Browse files
bderodnfield
authored andcommitted
Add mixed type specializations on algebraic ops in TPoint (flutter#21)
Also adds missing RHS operator overloads for TSize. For any algebraic ops involving TPoint and TSize: 1. `TPoint` takes precedent over `TSize`. 2. Floating point types take precedent over integer types. 3. If there's a tie (for example: `TPoint<int> + TPoint<long long>`), the LHS takes precedent.
1 parent 542b8c4 commit 26090e4

File tree

5 files changed

+272
-8
lines changed

5 files changed

+272
-8
lines changed

impeller/geometry/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ impeller_component("geometry") {
2929
"shear.h",
3030
"size.cc",
3131
"size.h",
32+
"type_traits.cc",
33+
"type_traits.h",
3234
"vector.cc",
3335
"vector.h",
3436
]

impeller/geometry/geometry_unittests.cc

+157
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) {
225225
ASSERT_EQ(s2.height, 2u);
226226
}
227227

228+
{
229+
Size s1(1.0, 2.0);
230+
Point p1 = static_cast<Point>(s1);
231+
ASSERT_EQ(p1.x, 1u);
232+
ASSERT_EQ(p1.y, 2u);
233+
}
234+
228235
{
229236
Rect r1(1.0, 2.0, 3.0, 4.0);
230237
IRect r2 = static_cast<IRect>(r1);
@@ -235,6 +242,156 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) {
235242
}
236243
}
237244

245+
TEST(GeometryTest, CanPerformAlgebraicPointOps) {
246+
{
247+
IPoint p1(1, 2);
248+
IPoint p2 = p1 + IPoint(1, 2);
249+
ASSERT_EQ(p2.x, 2u);
250+
ASSERT_EQ(p2.y, 4u);
251+
}
252+
253+
{
254+
IPoint p1(3, 6);
255+
IPoint p2 = p1 - IPoint(1, 2);
256+
ASSERT_EQ(p2.x, 2u);
257+
ASSERT_EQ(p2.y, 4u);
258+
}
259+
260+
{
261+
IPoint p1(1, 2);
262+
IPoint p2 = p1 * IPoint(2, 3);
263+
ASSERT_EQ(p2.x, 2u);
264+
ASSERT_EQ(p2.y, 6u);
265+
}
266+
267+
{
268+
IPoint p1(2, 6);
269+
IPoint p2 = p1 / IPoint(2, 3);
270+
ASSERT_EQ(p2.x, 1u);
271+
ASSERT_EQ(p2.y, 2u);
272+
}
273+
}
274+
275+
TEST(GeometryTest, PointIntegerCoercesToFloat) {
276+
// Integer on LHS, float on RHS
277+
{
278+
IPoint p1(1, 2);
279+
Point p2 = p1 + Point(1, 2);
280+
ASSERT_FLOAT_EQ(p2.x, 2u);
281+
ASSERT_FLOAT_EQ(p2.y, 4u);
282+
}
283+
284+
{
285+
IPoint p1(3, 6);
286+
Point p2 = p1 - Point(1, 2);
287+
ASSERT_FLOAT_EQ(p2.x, 2u);
288+
ASSERT_FLOAT_EQ(p2.y, 4u);
289+
}
290+
291+
{
292+
IPoint p1(1, 2);
293+
Point p2 = p1 * Point(2, 3);
294+
ASSERT_FLOAT_EQ(p2.x, 2u);
295+
ASSERT_FLOAT_EQ(p2.y, 6u);
296+
}
297+
298+
{
299+
IPoint p1(2, 6);
300+
Point p2 = p1 / Point(2, 3);
301+
ASSERT_FLOAT_EQ(p2.x, 1u);
302+
ASSERT_FLOAT_EQ(p2.y, 2u);
303+
}
304+
305+
// Float on LHS, integer on RHS
306+
{
307+
Point p1(1, 2);
308+
Point p2 = p1 + IPoint(1, 2);
309+
ASSERT_FLOAT_EQ(p2.x, 2u);
310+
ASSERT_FLOAT_EQ(p2.y, 4u);
311+
}
312+
313+
{
314+
Point p1(3, 6);
315+
Point p2 = p1 - IPoint(1, 2);
316+
ASSERT_FLOAT_EQ(p2.x, 2u);
317+
ASSERT_FLOAT_EQ(p2.y, 4u);
318+
}
319+
320+
{
321+
Point p1(1, 2);
322+
Point p2 = p1 * IPoint(2, 3);
323+
ASSERT_FLOAT_EQ(p2.x, 2u);
324+
ASSERT_FLOAT_EQ(p2.y, 6u);
325+
}
326+
327+
{
328+
Point p1(2, 6);
329+
Point p2 = p1 / IPoint(2, 3);
330+
ASSERT_FLOAT_EQ(p2.x, 1u);
331+
ASSERT_FLOAT_EQ(p2.y, 2u);
332+
}
333+
}
334+
335+
TEST(GeometryTest, SizeCoercesToPoint) {
336+
// Point on LHS, Size on RHS
337+
{
338+
IPoint p1(1, 2);
339+
IPoint p2 = p1 + ISize(1, 2);
340+
ASSERT_EQ(p2.x, 2u);
341+
ASSERT_EQ(p2.y, 4u);
342+
}
343+
344+
{
345+
IPoint p1(3, 6);
346+
IPoint p2 = p1 - ISize(1, 2);
347+
ASSERT_EQ(p2.x, 2u);
348+
ASSERT_EQ(p2.y, 4u);
349+
}
350+
351+
{
352+
IPoint p1(1, 2);
353+
IPoint p2 = p1 * ISize(2, 3);
354+
ASSERT_EQ(p2.x, 2u);
355+
ASSERT_EQ(p2.y, 6u);
356+
}
357+
358+
{
359+
IPoint p1(2, 6);
360+
IPoint p2 = p1 / ISize(2, 3);
361+
ASSERT_EQ(p2.x, 1u);
362+
ASSERT_EQ(p2.y, 2u);
363+
}
364+
365+
// Size on LHS, Point on RHS
366+
{
367+
ISize p1(1, 2);
368+
IPoint p2 = p1 + IPoint(1, 2);
369+
ASSERT_EQ(p2.x, 2u);
370+
ASSERT_EQ(p2.y, 4u);
371+
}
372+
373+
{
374+
ISize p1(3, 6);
375+
IPoint p2 = p1 - IPoint(1, 2);
376+
ASSERT_EQ(p2.x, 2u);
377+
ASSERT_EQ(p2.y, 4u);
378+
}
379+
380+
{
381+
ISize p1(1, 2);
382+
IPoint p2 = p1 * IPoint(2, 3);
383+
ASSERT_EQ(p2.x, 2u);
384+
ASSERT_EQ(p2.y, 6u);
385+
}
386+
387+
{
388+
ISize p1(2, 6);
389+
IPoint p2 = p1 / IPoint(2, 3);
390+
ASSERT_EQ(p2.x, 1u);
391+
ASSERT_EQ(p2.y, 2u);
392+
}
393+
}
394+
238395
TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
239396
{
240397
auto deg = Degrees{90.0};

impeller/geometry/point.h

+82-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "impeller/geometry/scalar.h"
1313
#include "impeller/geometry/size.h"
14+
#include "impeller/geometry/type_traits.h"
1415

1516
namespace impeller {
1617

@@ -27,6 +28,11 @@ struct TPoint {
2728
explicit constexpr TPoint(const TPoint<U>& other)
2829
: TPoint(static_cast<Type>(other.x), static_cast<Type>(other.y)) {}
2930

31+
template <class U>
32+
explicit constexpr TPoint(const TSize<U>& other)
33+
: TPoint(static_cast<Type>(other.width),
34+
static_cast<Type>(other.height)) {}
35+
3036
constexpr TPoint(Type x, Type y) : x(x), y(y) {}
3137

3238
static constexpr TPoint<Type> MakeXY(Type x, Type y) { return {x, y}; }
@@ -45,16 +51,18 @@ struct TPoint {
4551
return {x + p.x, y + p.y};
4652
}
4753

48-
constexpr TPoint operator+(const TSize<Type>& s) const {
49-
return {x + s.width, y + s.height};
54+
template <class U>
55+
constexpr TPoint operator+(const TSize<U>& s) const {
56+
return {x + static_cast<Type>(s.width), y + static_cast<Type>(s.height)};
5057
}
5158

5259
constexpr TPoint operator-(const TPoint& p) const {
5360
return {x - p.x, y - p.y};
5461
}
5562

56-
constexpr TPoint operator-(const TSize<Type>& s) const {
57-
return {x - s.width, y - s.height};
63+
template <class U>
64+
constexpr TPoint operator-(const TSize<U>& s) const {
65+
return {x - static_cast<Type>(s.width), y - static_cast<Type>(s.height)};
5866
}
5967

6068
constexpr TPoint operator*(Scalar scale) const {
@@ -65,8 +73,9 @@ struct TPoint {
6573
return {x * p.x, y * p.y};
6674
}
6775

68-
constexpr TPoint operator*(const TSize<Type>& s) const {
69-
return {x * s.width, y * s.height};
76+
template <class U>
77+
constexpr TPoint operator*(const TSize<U>& s) const {
78+
return {x * static_cast<Type>(s.width), y * static_cast<Type>(s.height)};
7079
}
7180

7281
constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; }
@@ -75,8 +84,9 @@ struct TPoint {
7584
return {x / p.x, y / p.y};
7685
}
7786

78-
constexpr TPoint operator/(const TSize<Type>& s) const {
79-
return {x / s.width, y / s.height};
87+
template <class U>
88+
constexpr TPoint operator/(const TSize<U>& s) const {
89+
return {x / static_cast<Type>(s.width), y / static_cast<Type>(s.height)};
8090
}
8191

8292
constexpr Type GetDistanceSquared(const TPoint& p) const {
@@ -112,6 +122,70 @@ struct TPoint {
112122
constexpr bool IsZero() const { return x == 0 && y == 0; }
113123
};
114124

125+
// Specializations for mixed (float & integer) algebraic operations.
126+
127+
template <class F, class I, class = MixedOp<F, I>>
128+
constexpr TPoint<F> operator+(const TPoint<F>& p1, const TPoint<I>& p2) {
129+
return {p1.x + static_cast<F>(p2.x), p1.y + static_cast<F>(p2.y)};
130+
}
131+
132+
template <class F, class I, class = MixedOp<F, I>>
133+
constexpr TPoint<F> operator+(const TPoint<I>& p1, const TPoint<F>& p2) {
134+
return p2 + p1;
135+
}
136+
137+
template <class F, class I, class = MixedOp<F, I>>
138+
constexpr TPoint<F> operator-(const TPoint<F>& p1, const TPoint<I>& p2) {
139+
return {p1.x - static_cast<F>(p2.x), p1.y - static_cast<F>(p2.y)};
140+
}
141+
142+
template <class F, class I, class = MixedOp<F, I>>
143+
constexpr TPoint<F> operator-(const TPoint<I>& p1, const TPoint<F>& p2) {
144+
return {static_cast<F>(p1.x) - p2.x, static_cast<F>(p1.y) - p2.y};
145+
}
146+
147+
template <class F, class I, class = MixedOp<F, I>>
148+
constexpr TPoint<F> operator*(const TPoint<F>& p1, const TPoint<I>& p2) {
149+
return {p1.x * static_cast<F>(p2.x), p1.y * static_cast<F>(p2.y)};
150+
}
151+
152+
template <class F, class I, class = MixedOp<F, I>>
153+
constexpr TPoint<F> operator*(const TPoint<I>& p1, const TPoint<F>& p2) {
154+
return p2 * p1;
155+
}
156+
157+
template <class F, class I, class = MixedOp<F, I>>
158+
constexpr TPoint<F> operator/(const TPoint<F>& p1, const TPoint<I>& p2) {
159+
return {p1.x / static_cast<F>(p2.x), p1.y / static_cast<F>(p2.y)};
160+
}
161+
162+
template <class F, class I, class = MixedOp<F, I>>
163+
constexpr TPoint<F> operator/(const TPoint<I>& p1, const TPoint<F>& p2) {
164+
return {static_cast<F>(p1.x) / p2.x, static_cast<F>(p1.y) / p2.y};
165+
}
166+
167+
// RHS algebraic operations with TSize.
168+
169+
template <class T, class U>
170+
constexpr TPoint<T> operator+(const TSize<U>& s, const TPoint<T>& p) {
171+
return p + s;
172+
}
173+
174+
template <class T, class U>
175+
constexpr TPoint<T> operator-(const TSize<U>& s, const TPoint<T>& p) {
176+
return {static_cast<T>(s.width) - p.x, static_cast<T>(s.height) - p.y};
177+
}
178+
179+
template <class T, class U>
180+
constexpr TPoint<T> operator*(const TSize<U>& s, const TPoint<T>& p) {
181+
return p * s;
182+
}
183+
184+
template <class T, class U>
185+
constexpr TPoint<T> operator/(const TSize<U>& s, const TPoint<T>& p) {
186+
return {static_cast<T>(s.width) / p.x, static_cast<T>(s.height) / p.y};
187+
}
188+
115189
using Point = TPoint<Scalar>;
116190
using IPoint = TPoint<int64_t>;
117191

impeller/geometry/type_traits.cc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "type_traits.h"
6+
7+
namespace impeller {
8+
9+
//
10+
11+
} // namespace impeller

impeller/geometry/type_traits.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <type_traits>
8+
9+
namespace impeller {
10+
11+
template <class F,
12+
class I,
13+
class = std::enable_if_t<std::is_floating_point_v<F> &&
14+
std::is_integral_v<I>>>
15+
struct MixedOp_ : public std::true_type {};
16+
17+
template <class F, class I>
18+
using MixedOp = typename MixedOp_<F, I>::type;
19+
20+
} // namespace impeller

0 commit comments

Comments
 (0)