[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 1 | // Copyright 2012 The Chromium 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 | |
[email protected] | 55a124d0 | 2012-10-22 03:07:13 | [diff] [blame] | 5 | #include "cc/math_util.h" |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 6 | |
[email protected] | c9c1ebe | 2012-11-05 20:46:13 | [diff] [blame] | 7 | #include <cmath> |
| 8 | #include <limits> |
| 9 | |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 10 | #include "ui/gfx/quad_f.h" |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 11 | #include "ui/gfx/rect.h" |
| 12 | #include "ui/gfx/rect_conversions.h" |
| 13 | #include "ui/gfx/rect_f.h" |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 14 | #include "ui/gfx/transform.h" |
[email protected] | c9c1ebe | 2012-11-05 20:46:13 | [diff] [blame] | 15 | #include "ui/gfx/vector2d_f.h" |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 16 | |
[email protected] | 9c88e56 | 2012-09-14 22:21:30 | [diff] [blame] | 17 | namespace cc { |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 18 | |
[email protected] | ac7c7f5 | 2012-11-08 06:26:50 | [diff] [blame] | 19 | const double MathUtil::PI_DOUBLE = 3.14159265358979323846; |
| 20 | const float MathUtil::PI_FLOAT = 3.14159265358979323846f; |
[email protected] | 3d9a2b650f | 2012-11-19 19:32:25 | [diff] [blame] | 21 | const double MathUtil::EPSILON = 1e-9; |
[email protected] | ac7c7f5 | 2012-11-08 06:26:50 | [diff] [blame] | 22 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 23 | static HomogeneousCoordinate projectHomogeneousPoint(const gfx::Transform& transform, const gfx::PointF& p) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 24 | { |
| 25 | // In this case, the layer we are trying to project onto is perpendicular to ray |
| 26 | // (point p and z-axis direction) that we are trying to project. This happens when the |
| 27 | // layer is rotated so that it is infinitesimally thin, or when it is co-planar with |
| 28 | // the camera origin -- i.e. when the layer is invisible anyway. |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 29 | if (!transform.matrix().getDouble(2, 2)) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 30 | return HomogeneousCoordinate(0, 0, 0, 1); |
| 31 | |
| 32 | double x = p.x(); |
| 33 | double y = p.y(); |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 34 | double z = -(transform.matrix().getDouble(2, 0) * x + transform.matrix().getDouble(2, 1) * y + transform.matrix().getDouble(2, 3)) / transform.matrix().getDouble(2, 2); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 35 | // implicit definition of w = 1; |
| 36 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 37 | double outX = x * transform.matrix().getDouble(0, 0) + y * transform.matrix().getDouble(0, 1) + z * transform.matrix().getDouble(0, 2) + transform.matrix().getDouble(0, 3); |
| 38 | double outY = x * transform.matrix().getDouble(1, 0) + y * transform.matrix().getDouble(1, 1) + z * transform.matrix().getDouble(1, 2) + transform.matrix().getDouble(1, 3); |
| 39 | double outZ = x * transform.matrix().getDouble(2, 0) + y * transform.matrix().getDouble(2, 1) + z * transform.matrix().getDouble(2, 2) + transform.matrix().getDouble(2, 3); |
| 40 | double outW = x * transform.matrix().getDouble(3, 0) + y * transform.matrix().getDouble(3, 1) + z * transform.matrix().getDouble(3, 2) + transform.matrix().getDouble(3, 3); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 41 | |
| 42 | return HomogeneousCoordinate(outX, outY, outZ, outW); |
| 43 | } |
| 44 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 45 | static HomogeneousCoordinate mapHomogeneousPoint(const gfx::Transform& transform, const gfx::Point3F& p) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 46 | { |
| 47 | double x = p.x(); |
| 48 | double y = p.y(); |
| 49 | double z = p.z(); |
| 50 | // implicit definition of w = 1; |
| 51 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 52 | double outX = x * transform.matrix().getDouble(0, 0) + y * transform.matrix().getDouble(0, 1) + z * transform.matrix().getDouble(0, 2) + transform.matrix().getDouble(0, 3); |
| 53 | double outY = x * transform.matrix().getDouble(1, 0) + y * transform.matrix().getDouble(1, 1) + z * transform.matrix().getDouble(1, 2) + transform.matrix().getDouble(1, 3); |
| 54 | double outZ = x * transform.matrix().getDouble(2, 0) + y * transform.matrix().getDouble(2, 1) + z * transform.matrix().getDouble(2, 2) + transform.matrix().getDouble(2, 3); |
| 55 | double outW = x * transform.matrix().getDouble(3, 0) + y * transform.matrix().getDouble(3, 1) + z * transform.matrix().getDouble(3, 2) + transform.matrix().getDouble(3, 3); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 56 | |
| 57 | return HomogeneousCoordinate(outX, outY, outZ, outW); |
| 58 | } |
| 59 | |
| 60 | static HomogeneousCoordinate computeClippedPointForEdge(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2) |
| 61 | { |
| 62 | // Points h1 and h2 form a line in 4d, and any point on that line can be represented |
| 63 | // as an interpolation between h1 and h2: |
| 64 | // p = (1-t) h1 + (t) h2 |
| 65 | // |
| 66 | // We want to compute point p such that p.w == epsilon, where epsilon is a small |
| 67 | // non-zero number. (but the smaller the number is, the higher the risk of overflow) |
| 68 | // To do this, we solve for t in the following equation: |
| 69 | // p.w = epsilon = (1-t) * h1.w + (t) * h2.w |
| 70 | // |
| 71 | // Once paramter t is known, the rest of p can be computed via p = (1-t) h1 + (t) h2. |
| 72 | |
| 73 | // Technically this is a special case of the following assertion, but its a good idea to keep it an explicit sanity check here. |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 74 | DCHECK(h2.w != h1.w); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 75 | // Exactly one of h1 or h2 (but not both) must be on the negative side of the w plane when this is called. |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 76 | DCHECK(h1.shouldBeClipped() ^ h2.shouldBeClipped()); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 77 | |
| 78 | double w = 0.00001; // or any positive non-zero small epsilon |
| 79 | |
| 80 | double t = (w - h1.w) / (h2.w - h1.w); |
| 81 | |
| 82 | double x = (1-t) * h1.x + t * h2.x; |
| 83 | double y = (1-t) * h1.y + t * h2.y; |
| 84 | double z = (1-t) * h1.z + t * h2.z; |
| 85 | |
| 86 | return HomogeneousCoordinate(x, y, z, w); |
| 87 | } |
| 88 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 89 | static inline void expandBoundsToIncludePoint(float& xmin, float& xmax, float& ymin, float& ymax, const gfx::PointF& p) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 90 | { |
| 91 | xmin = std::min(p.x(), xmin); |
| 92 | xmax = std::max(p.x(), xmax); |
| 93 | ymin = std::min(p.y(), ymin); |
| 94 | ymax = std::max(p.y(), ymax); |
| 95 | } |
| 96 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 97 | static inline void addVertexToClippedQuad(const gfx::PointF& newVertex, gfx::PointF clippedQuad[8], int& numVerticesInClippedQuad) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 98 | { |
| 99 | clippedQuad[numVerticesInClippedQuad] = newVertex; |
| 100 | numVerticesInClippedQuad++; |
| 101 | } |
| 102 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 103 | gfx::Rect MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::Rect& srcRect) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 104 | { |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 105 | return gfx::ToEnclosingRect(mapClippedRect(transform, gfx::RectF(srcRect))); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 106 | } |
| 107 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 108 | gfx::RectF MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 109 | { |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 110 | if (transform.IsIdentityOrTranslation()) |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 111 | return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 112 | |
| 113 | // Apply the transform, but retain the result in homogeneous coordinates. |
[email protected] | d79757e3 | 2012-11-09 02:02:18 | [diff] [blame] | 114 | gfx::QuadF q = gfx::QuadF(srcRect); |
[email protected] | 4eb8a37 | 2012-11-01 20:16:56 | [diff] [blame] | 115 | HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, gfx::Point3F(q.p1())); |
| 116 | HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, gfx::Point3F(q.p2())); |
| 117 | HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, gfx::Point3F(q.p3())); |
| 118 | HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, gfx::Point3F(q.p4())); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 119 | |
| 120 | return computeEnclosingClippedRect(h1, h2, h3, h4); |
| 121 | } |
| 122 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 123 | gfx::RectF MathUtil::projectClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 124 | { |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 125 | if (transform.IsIdentityOrTranslation()) |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 126 | return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); |
[email protected] | d79757e3 | 2012-11-09 02:02:18 | [diff] [blame] | 127 | |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 128 | // Perform the projection, but retain the result in homogeneous coordinates. |
[email protected] | d79757e3 | 2012-11-09 02:02:18 | [diff] [blame] | 129 | gfx::QuadF q = gfx::QuadF(srcRect); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 130 | HomogeneousCoordinate h1 = projectHomogeneousPoint(transform, q.p1()); |
| 131 | HomogeneousCoordinate h2 = projectHomogeneousPoint(transform, q.p2()); |
| 132 | HomogeneousCoordinate h3 = projectHomogeneousPoint(transform, q.p3()); |
| 133 | HomogeneousCoordinate h4 = projectHomogeneousPoint(transform, q.p4()); |
| 134 | |
| 135 | return computeEnclosingClippedRect(h1, h2, h3, h4); |
| 136 | } |
| 137 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 138 | void MathUtil::mapClippedQuad(const gfx::Transform& transform, const gfx::QuadF& srcQuad, gfx::PointF clippedQuad[8], int& numVerticesInClippedQuad) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 139 | { |
[email protected] | 4eb8a37 | 2012-11-01 20:16:56 | [diff] [blame] | 140 | HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, gfx::Point3F(srcQuad.p1())); |
| 141 | HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, gfx::Point3F(srcQuad.p2())); |
| 142 | HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, gfx::Point3F(srcQuad.p3())); |
| 143 | HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, gfx::Point3F(srcQuad.p4())); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 144 | |
| 145 | // The order of adding the vertices to the array is chosen so that clockwise / counter-clockwise orientation is retained. |
| 146 | |
| 147 | numVerticesInClippedQuad = 0; |
| 148 | |
| 149 | if (!h1.shouldBeClipped()) |
| 150 | addVertexToClippedQuad(h1.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 151 | |
| 152 | if (h1.shouldBeClipped() ^ h2.shouldBeClipped()) |
| 153 | addVertexToClippedQuad(computeClippedPointForEdge(h1, h2).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 154 | |
| 155 | if (!h2.shouldBeClipped()) |
| 156 | addVertexToClippedQuad(h2.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 157 | |
| 158 | if (h2.shouldBeClipped() ^ h3.shouldBeClipped()) |
| 159 | addVertexToClippedQuad(computeClippedPointForEdge(h2, h3).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 160 | |
| 161 | if (!h3.shouldBeClipped()) |
| 162 | addVertexToClippedQuad(h3.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 163 | |
| 164 | if (h3.shouldBeClipped() ^ h4.shouldBeClipped()) |
| 165 | addVertexToClippedQuad(computeClippedPointForEdge(h3, h4).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 166 | |
| 167 | if (!h4.shouldBeClipped()) |
| 168 | addVertexToClippedQuad(h4.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 169 | |
| 170 | if (h4.shouldBeClipped() ^ h1.shouldBeClipped()) |
| 171 | addVertexToClippedQuad(computeClippedPointForEdge(h4, h1).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad); |
| 172 | |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 173 | DCHECK(numVerticesInClippedQuad <= 8); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 174 | } |
| 175 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 176 | gfx::RectF MathUtil::computeEnclosingRectOfVertices(gfx::PointF vertices[], int numVertices) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 177 | { |
| 178 | if (numVertices < 2) |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 179 | return gfx::RectF(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 180 | |
| 181 | float xmin = std::numeric_limits<float>::max(); |
| 182 | float xmax = -std::numeric_limits<float>::max(); |
| 183 | float ymin = std::numeric_limits<float>::max(); |
| 184 | float ymax = -std::numeric_limits<float>::max(); |
| 185 | |
| 186 | for (int i = 0; i < numVertices; ++i) |
| 187 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, vertices[i]); |
| 188 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 189 | return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ymin)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 190 | } |
| 191 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 192 | gfx::RectF MathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 193 | { |
| 194 | // This function performs clipping as necessary and computes the enclosing 2d |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 195 | // gfx::RectF of the vertices. Doing these two steps simultaneously allows us to avoid |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 196 | // the overhead of storing an unknown number of clipped vertices. |
| 197 | |
| 198 | // If no vertices on the quad are clipped, then we can simply return the enclosing rect directly. |
| 199 | bool somethingClipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped(); |
| 200 | if (!somethingClipped) { |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 201 | gfx::QuadF mappedQuad = gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d()); |
| 202 | return mappedQuad.BoundingBox(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3.shouldBeClipped() && h4.shouldBeClipped(); |
| 206 | if (everythingClipped) |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 207 | return gfx::RectF(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 208 | |
| 209 | |
| 210 | float xmin = std::numeric_limits<float>::max(); |
| 211 | float xmax = -std::numeric_limits<float>::max(); |
| 212 | float ymin = std::numeric_limits<float>::max(); |
| 213 | float ymax = -std::numeric_limits<float>::max(); |
| 214 | |
| 215 | if (!h1.shouldBeClipped()) |
| 216 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h1.cartesianPoint2d()); |
| 217 | |
| 218 | if (h1.shouldBeClipped() ^ h2.shouldBeClipped()) |
| 219 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h1, h2).cartesianPoint2d()); |
| 220 | |
| 221 | if (!h2.shouldBeClipped()) |
| 222 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h2.cartesianPoint2d()); |
| 223 | |
| 224 | if (h2.shouldBeClipped() ^ h3.shouldBeClipped()) |
| 225 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h2, h3).cartesianPoint2d()); |
| 226 | |
| 227 | if (!h3.shouldBeClipped()) |
| 228 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h3.cartesianPoint2d()); |
| 229 | |
| 230 | if (h3.shouldBeClipped() ^ h4.shouldBeClipped()) |
| 231 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h3, h4).cartesianPoint2d()); |
| 232 | |
| 233 | if (!h4.shouldBeClipped()) |
| 234 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h4.cartesianPoint2d()); |
| 235 | |
| 236 | if (h4.shouldBeClipped() ^ h1.shouldBeClipped()) |
| 237 | expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h4, h1).cartesianPoint2d()); |
| 238 | |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 239 | return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ymin)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 240 | } |
| 241 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 242 | gfx::QuadF MathUtil::mapQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 243 | { |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 244 | if (transform.IsIdentityOrTranslation()) { |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 245 | gfx::QuadF mappedQuad(q); |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 246 | mappedQuad += gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 247 | clipped = false; |
| 248 | return mappedQuad; |
| 249 | } |
| 250 | |
[email protected] | 4eb8a37 | 2012-11-01 20:16:56 | [diff] [blame] | 251 | HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, gfx::Point3F(q.p1())); |
| 252 | HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, gfx::Point3F(q.p2())); |
| 253 | HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, gfx::Point3F(q.p3())); |
| 254 | HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, gfx::Point3F(q.p4())); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 255 | |
| 256 | clipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped(); |
| 257 | |
| 258 | // Result will be invalid if clipped == true. But, compute it anyway just in case, to emulate existing behavior. |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 259 | return gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d()); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 260 | } |
| 261 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 262 | gfx::PointF MathUtil::mapPoint(const gfx::Transform& transform, const gfx::PointF& p, bool& clipped) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 263 | { |
[email protected] | 4eb8a37 | 2012-11-01 20:16:56 | [diff] [blame] | 264 | HomogeneousCoordinate h = mapHomogeneousPoint(transform, gfx::Point3F(p)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 265 | |
| 266 | if (h.w > 0) { |
| 267 | clipped = false; |
| 268 | return h.cartesianPoint2d(); |
| 269 | } |
| 270 | |
| 271 | // The cartesian coordinates will be invalid after dividing by w. |
| 272 | clipped = true; |
| 273 | |
| 274 | // Avoid dividing by w if w == 0. |
| 275 | if (!h.w) |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 276 | return gfx::PointF(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 277 | |
| 278 | // This return value will be invalid because clipped == true, but (1) users of this |
| 279 | // code should be ignoring the return value when clipped == true anyway, and (2) this |
| 280 | // behavior is more consistent with existing behavior of WebKit transforms if the user |
| 281 | // really does not ignore the return value. |
| 282 | return h.cartesianPoint2d(); |
| 283 | } |
| 284 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 285 | gfx::Point3F MathUtil::mapPoint(const gfx::Transform& transform, const gfx::Point3F& p, bool& clipped) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 286 | { |
| 287 | HomogeneousCoordinate h = mapHomogeneousPoint(transform, p); |
| 288 | |
| 289 | if (h.w > 0) { |
| 290 | clipped = false; |
| 291 | return h.cartesianPoint3d(); |
| 292 | } |
| 293 | |
| 294 | // The cartesian coordinates will be invalid after dividing by w. |
| 295 | clipped = true; |
| 296 | |
| 297 | // Avoid dividing by w if w == 0. |
| 298 | if (!h.w) |
[email protected] | 4eb8a37 | 2012-11-01 20:16:56 | [diff] [blame] | 299 | return gfx::Point3F(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 300 | |
| 301 | // This return value will be invalid because clipped == true, but (1) users of this |
| 302 | // code should be ignoring the return value when clipped == true anyway, and (2) this |
| 303 | // behavior is more consistent with existing behavior of WebKit transforms if the user |
| 304 | // really does not ignore the return value. |
| 305 | return h.cartesianPoint3d(); |
| 306 | } |
| 307 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 308 | gfx::QuadF MathUtil::projectQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 309 | { |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 310 | gfx::QuadF projectedQuad; |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 311 | bool clippedPoint; |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 312 | projectedQuad.set_p1(projectPoint(transform, q.p1(), clippedPoint)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 313 | clipped = clippedPoint; |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 314 | projectedQuad.set_p2(projectPoint(transform, q.p2(), clippedPoint)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 315 | clipped |= clippedPoint; |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 316 | projectedQuad.set_p3(projectPoint(transform, q.p3(), clippedPoint)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 317 | clipped |= clippedPoint; |
[email protected] | 075ae034 | 2012-11-02 20:02:41 | [diff] [blame] | 318 | projectedQuad.set_p4(projectPoint(transform, q.p4(), clippedPoint)); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 319 | clipped |= clippedPoint; |
| 320 | |
| 321 | return projectedQuad; |
| 322 | } |
| 323 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 324 | gfx::PointF MathUtil::projectPoint(const gfx::Transform& transform, const gfx::PointF& p, bool& clipped) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 325 | { |
| 326 | HomogeneousCoordinate h = projectHomogeneousPoint(transform, p); |
| 327 | |
| 328 | if (h.w > 0) { |
| 329 | // The cartesian coordinates will be valid in this case. |
| 330 | clipped = false; |
| 331 | return h.cartesianPoint2d(); |
| 332 | } |
| 333 | |
| 334 | // The cartesian coordinates will be invalid after dividing by w. |
| 335 | clipped = true; |
| 336 | |
| 337 | // Avoid dividing by w if w == 0. |
| 338 | if (!h.w) |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 339 | return gfx::PointF(); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 340 | |
| 341 | // This return value will be invalid because clipped == true, but (1) users of this |
| 342 | // code should be ignoring the return value when clipped == true anyway, and (2) this |
| 343 | // behavior is more consistent with existing behavior of WebKit transforms if the user |
| 344 | // really does not ignore the return value. |
| 345 | return h.cartesianPoint2d(); |
| 346 | } |
| 347 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 348 | void MathUtil::flattenTransformTo2d(gfx::Transform& transform) |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 349 | { |
| 350 | // Set both the 3rd row and 3rd column to (0, 0, 1, 0). |
| 351 | // |
| 352 | // One useful interpretation of doing this operation: |
| 353 | // - For x and y values, the new transform behaves effectively like an orthographic |
| 354 | // projection was added to the matrix sequence. |
| 355 | // - For z values, the new transform overrides any effect that the transform had on |
| 356 | // z, and instead it preserves the z value for any points that are transformed. |
| 357 | // - Because of linearity of transforms, this flattened transform also preserves the |
| 358 | // effect that any subsequent (post-multiplied) transforms would have on z values. |
| 359 | // |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 360 | transform.matrix().setDouble(2, 0, 0); |
| 361 | transform.matrix().setDouble(2, 1, 0); |
| 362 | transform.matrix().setDouble(0, 2, 0); |
| 363 | transform.matrix().setDouble(1, 2, 0); |
| 364 | transform.matrix().setDouble(2, 2, 1); |
| 365 | transform.matrix().setDouble(3, 2, 0); |
| 366 | transform.matrix().setDouble(2, 3, 0); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 367 | } |
| 368 | |
[email protected] | 518ee58 | 2012-10-24 18:29:44 | [diff] [blame] | 369 | static inline float scaleOnAxis(double a, double b, double c) |
| 370 | { |
| 371 | return std::sqrt(a * a + b * b + c * c); |
| 372 | } |
| 373 | |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 374 | gfx::Vector2dF MathUtil::computeTransform2dScaleComponents(const gfx::Transform& transform) |
[email protected] | 518ee58 | 2012-10-24 18:29:44 | [diff] [blame] | 375 | { |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 376 | if (transform.HasPerspective()) |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 377 | return gfx::Vector2dF(1, 1); |
[email protected] | c8686a0 | 2012-11-27 08:29:00 | [diff] [blame] | 378 | float xScale = scaleOnAxis(transform.matrix().getDouble(0, 0), transform.matrix().getDouble(1, 0), transform.matrix().getDouble(2, 0)); |
| 379 | float yScale = scaleOnAxis(transform.matrix().getDouble(0, 1), transform.matrix().getDouble(1, 1), transform.matrix().getDouble(2, 1)); |
[email protected] | aad0a007 | 2012-11-01 18:15:58 | [diff] [blame] | 380 | return gfx::Vector2dF(xScale, yScale); |
[email protected] | 518ee58 | 2012-10-24 18:29:44 | [diff] [blame] | 381 | } |
| 382 | |
[email protected] | c9c1ebe | 2012-11-05 20:46:13 | [diff] [blame] | 383 | float MathUtil::smallestAngleBetweenVectors(gfx::Vector2dF v1, gfx::Vector2dF v2) |
[email protected] | ec454716 | 2012-11-05 16:56:49 | [diff] [blame] | 384 | { |
[email protected] | c9c1ebe | 2012-11-05 20:46:13 | [diff] [blame] | 385 | double dotProduct = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length(); |
| 386 | // Clamp to compensate for rounding errors. |
| 387 | dotProduct = std::max(-1.0, std::min(1.0, dotProduct)); |
[email protected] | ac7c7f5 | 2012-11-08 06:26:50 | [diff] [blame] | 388 | return static_cast<float>(Rad2Deg(std::acos(dotProduct))); |
[email protected] | c9c1ebe | 2012-11-05 20:46:13 | [diff] [blame] | 389 | } |
| 390 | |
| 391 | gfx::Vector2dF MathUtil::projectVector(gfx::Vector2dF source, gfx::Vector2dF destination) |
| 392 | { |
| 393 | float projectedLength = gfx::DotProduct(source, destination) / destination.LengthSquared(); |
| 394 | return gfx::Vector2dF(projectedLength * destination.x(), projectedLength * destination.y()); |
[email protected] | 94f206c1 | 2012-08-25 00:09:14 | [diff] [blame] | 395 | } |
| 396 | |
[email protected] | 3d9a2b650f | 2012-11-19 19:32:25 | [diff] [blame] | 397 | void MathUtil::rotateEulerAngles(gfx::Transform* transform, double eulerX, double eulerY, double eulerZ) |
| 398 | { |
| 399 | // TODO (shawnsingh): make this implementation faster and more accurate by |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 400 | // hard-coding each matrix instead of calling RotateAbout(). |
[email protected] | 3d9a2b650f | 2012-11-19 19:32:25 | [diff] [blame] | 401 | gfx::Transform rotationAboutX; |
| 402 | gfx::Transform rotationAboutY; |
| 403 | gfx::Transform rotationAboutZ; |
| 404 | |
[email protected] | 2c7cd6d | 2012-11-28 23:49:26 | [diff] [blame] | 405 | rotationAboutX.RotateAboutXAxis(eulerX); |
| 406 | rotationAboutY.RotateAboutYAxis(eulerY); |
| 407 | rotationAboutZ.RotateAboutZAxis(eulerZ); |
[email protected] | 3d9a2b650f | 2012-11-19 19:32:25 | [diff] [blame] | 408 | |
| 409 | gfx::Transform composite = rotationAboutZ * rotationAboutY * rotationAboutX; |
| 410 | transform->PreconcatTransform(composite); |
| 411 | } |
| 412 | |
[email protected] | 3d9a2b650f | 2012-11-19 19:32:25 | [diff] [blame] | 413 | gfx::Transform MathUtil::inverse(const gfx::Transform& transform) |
| 414 | { |
| 415 | gfx::Transform result; |
| 416 | bool invertedSuccessfully = transform.GetInverse(&result); |
| 417 | |
| 418 | if (invertedSuccessfully) |
| 419 | return result; |
| 420 | |
| 421 | // If transform was un-invertible, then just return identity. |
| 422 | return gfx::Transform(); |
| 423 | } |
| 424 | |
| 425 | gfx::Transform MathUtil::to2dTransform(const gfx::Transform& transform) |
| 426 | { |
| 427 | gfx::Transform result = transform; |
| 428 | SkMatrix44& matrix = result.matrix(); |
| 429 | matrix.setDouble(0, 2, 0); |
| 430 | matrix.setDouble(1, 2, 0); |
| 431 | matrix.setDouble(2, 2, 1); |
| 432 | matrix.setDouble(3, 2, 0); |
| 433 | |
| 434 | matrix.setDouble(2, 0, 0); |
| 435 | matrix.setDouble(2, 1, 0); |
| 436 | matrix.setDouble(2, 3, 0); |
| 437 | |
| 438 | return result; |
| 439 | } |
| 440 | |
| 441 | gfx::Transform MathUtil::createGfxTransform(double m11, double m12, double m13, double m14, |
| 442 | double m21, double m22, double m23, double m24, |
| 443 | double m31, double m32, double m33, double m34, |
| 444 | double m41, double m42, double m43, double m44) |
| 445 | { |
| 446 | gfx::Transform result; |
| 447 | SkMatrix44& matrix = result.matrix(); |
| 448 | |
| 449 | // Initialize column 1 |
| 450 | matrix.setDouble(0, 0, m11); |
| 451 | matrix.setDouble(1, 0, m12); |
| 452 | matrix.setDouble(2, 0, m13); |
| 453 | matrix.setDouble(3, 0, m14); |
| 454 | |
| 455 | // Initialize column 2 |
| 456 | matrix.setDouble(0, 1, m21); |
| 457 | matrix.setDouble(1, 1, m22); |
| 458 | matrix.setDouble(2, 1, m23); |
| 459 | matrix.setDouble(3, 1, m24); |
| 460 | |
| 461 | // Initialize column 3 |
| 462 | matrix.setDouble(0, 2, m31); |
| 463 | matrix.setDouble(1, 2, m32); |
| 464 | matrix.setDouble(2, 2, m33); |
| 465 | matrix.setDouble(3, 2, m34); |
| 466 | |
| 467 | // Initialize column 4 |
| 468 | matrix.setDouble(0, 3, m41); |
| 469 | matrix.setDouble(1, 3, m42); |
| 470 | matrix.setDouble(2, 3, m43); |
| 471 | matrix.setDouble(3, 3, m44); |
| 472 | |
| 473 | return result; |
| 474 | } |
| 475 | |
| 476 | gfx::Transform MathUtil::createGfxTransform(double a, double b, double c, |
| 477 | double d, double e, double f) |
| 478 | { |
| 479 | gfx::Transform result; |
| 480 | SkMatrix44& matrix = result.matrix(); |
| 481 | matrix.setDouble(0, 0, a); |
| 482 | matrix.setDouble(1, 0, b); |
| 483 | matrix.setDouble(0, 1, c); |
| 484 | matrix.setDouble(1, 1, d); |
| 485 | matrix.setDouble(0, 3, e); |
| 486 | matrix.setDouble(1, 3, f); |
| 487 | |
| 488 | return result; |
| 489 | } |
| 490 | |
[email protected] | bc5e77c | 2012-11-05 20:00:49 | [diff] [blame] | 491 | } // namespace cc |