summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/geometry/quad_f.cc
blob: 8ed8b91700fbdce1e264d8591ccdd5360bc7b66b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/geometry/quad_f.h"

#include <limits>

#include "base/strings/stringprintf.h"

namespace gfx {

void QuadF::operator=(const RectF& rect) {
  p1_ = PointF(rect.x(), rect.y());
  p2_ = PointF(rect.right(), rect.y());
  p3_ = PointF(rect.right(), rect.bottom());
  p4_ = PointF(rect.x(), rect.bottom());
}

std::string QuadF::ToString() const {
  return base::StringPrintf("%s;%s;%s;%s",
                            p1_.ToString().c_str(),
                            p2_.ToString().c_str(),
                            p3_.ToString().c_str(),
                            p4_.ToString().c_str());
}

static inline bool WithinEpsilon(float a, float b) {
  return std::abs(a - b) < std::numeric_limits<float>::epsilon();
}

bool QuadF::IsRectilinear() const {
  return
      (WithinEpsilon(p1_.x(), p2_.x()) && WithinEpsilon(p2_.y(), p3_.y()) &&
       WithinEpsilon(p3_.x(), p4_.x()) && WithinEpsilon(p4_.y(), p1_.y())) ||
      (WithinEpsilon(p1_.y(), p2_.y()) && WithinEpsilon(p2_.x(), p3_.x()) &&
       WithinEpsilon(p3_.y(), p4_.y()) && WithinEpsilon(p4_.x(), p1_.x()));
}

bool QuadF::IsCounterClockwise() const {
  // This math computes the signed area of the quad. Positive area
  // indicates the quad is clockwise; negative area indicates the quad is
  // counter-clockwise. Note carefully: this is backwards from conventional
  // math because our geometric space uses screen coordiantes with y-axis
  // pointing downards.
  // Reference: http://mathworld.wolfram.com/PolygonArea.html.
  // The equation can be written:
  // Signed area = determinant1 + determinant2 + determinant3 + determinant4
  // In practise, Refactoring the computation of adding determinants so that
  // reducing the number of operations. The equation is:
  // Signed area = element1 + element2 - element3 - element4

  float p24 = p2_.y() - p4_.y();
  float p31 = p3_.y() - p1_.y();

  // Up-cast to double so this cannot overflow.
  double element1 = static_cast<double>(p1_.x()) * p24;
  double element2 = static_cast<double>(p2_.x()) * p31;
  double element3 = static_cast<double>(p3_.x()) * p24;
  double element4 = static_cast<double>(p4_.x()) * p31;

  return element1 + element2 < element3 + element4;
}

static inline bool PointIsInTriangle(const PointF& point,
                                     const PointF& r1,
                                     const PointF& r2,
                                     const PointF& r3) {
  // Compute the barycentric coordinates (u, v, w) of |point| relative to the
  // triangle (r1, r2, r3) by the solving the system of equations:
  //   1) point = u * r1 + v * r2 + w * r3
  //   2) u + v + w = 1
  // This algorithm comes from Christer Ericson's Real-Time Collision Detection.

  Vector2dF r31 = r1 - r3;
  Vector2dF r32 = r2 - r3;
  Vector2dF r3p = point - r3;

  // Promote to doubles so all the math below is done with doubles, because
  // otherwise it gets incorrect results on arm64.
  double r31x = r31.x();
  double r31y = r31.y();
  double r32x = r32.x();
  double r32y = r32.y();

  double denom = r32y * r31x - r32x * r31y;
  double u = (r32y * r3p.x() - r32x * r3p.y()) / denom;
  double v = (r31x * r3p.y() - r31y * r3p.x()) / denom;
  double w = 1.0 - u - v;

  // Use the barycentric coordinates to test if |point| is inside the
  // triangle (r1, r2, r2).
  return (u >= 0) && (v >= 0) && (w >= 0);
}

bool QuadF::Contains(const PointF& point) const {
  return PointIsInTriangle(point, p1_, p2_, p3_)
      || PointIsInTriangle(point, p1_, p3_, p4_);
}

void QuadF::Scale(float x_scale, float y_scale) {
  p1_.Scale(x_scale, y_scale);
  p2_.Scale(x_scale, y_scale);
  p3_.Scale(x_scale, y_scale);
  p4_.Scale(x_scale, y_scale);
}

void QuadF::operator+=(const Vector2dF& rhs) {
  p1_ += rhs;
  p2_ += rhs;
  p3_ += rhs;
  p4_ += rhs;
}

void QuadF::operator-=(const Vector2dF& rhs) {
  p1_ -= rhs;
  p2_ -= rhs;
  p3_ -= rhs;
  p4_ -= rhs;
}

QuadF operator+(const QuadF& lhs, const Vector2dF& rhs) {
  QuadF result = lhs;
  result += rhs;
  return result;
}

QuadF operator-(const QuadF& lhs, const Vector2dF& rhs) {
  QuadF result = lhs;
  result -= rhs;
  return result;
}

}  // namespace gfx