blob: 2e0349c731f9cad46239c53f5de467da3abe60bf [file] [log] [blame]
[email protected]94f206c12012-08-25 00:09:141// 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]55a124d02012-10-22 03:07:135#include "cc/math_util.h"
[email protected]94f206c12012-08-25 00:09:146
[email protected]c9c1ebe2012-11-05 20:46:137#include <cmath>
8#include <limits>
9
[email protected]075ae0342012-11-02 20:02:4110#include "ui/gfx/quad_f.h"
[email protected]aad0a0072012-11-01 18:15:5811#include "ui/gfx/rect.h"
12#include "ui/gfx/rect_conversions.h"
13#include "ui/gfx/rect_f.h"
[email protected]c8686a02012-11-27 08:29:0014#include "ui/gfx/transform.h"
[email protected]c9c1ebe2012-11-05 20:46:1315#include "ui/gfx/vector2d_f.h"
[email protected]94f206c12012-08-25 00:09:1416
[email protected]9c88e562012-09-14 22:21:3017namespace cc {
[email protected]94f206c12012-08-25 00:09:1418
[email protected]ac7c7f52012-11-08 06:26:5019const double MathUtil::PI_DOUBLE = 3.14159265358979323846;
20const float MathUtil::PI_FLOAT = 3.14159265358979323846f;
[email protected]3d9a2b650f2012-11-19 19:32:2521const double MathUtil::EPSILON = 1e-9;
[email protected]ac7c7f52012-11-08 06:26:5022
[email protected]c8686a02012-11-27 08:29:0023static HomogeneousCoordinate projectHomogeneousPoint(const gfx::Transform& transform, const gfx::PointF& p)
[email protected]94f206c12012-08-25 00:09:1424{
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]c8686a02012-11-27 08:29:0029 if (!transform.matrix().getDouble(2, 2))
[email protected]94f206c12012-08-25 00:09:1430 return HomogeneousCoordinate(0, 0, 0, 1);
31
32 double x = p.x();
33 double y = p.y();
[email protected]c8686a02012-11-27 08:29:0034 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]94f206c12012-08-25 00:09:1435 // implicit definition of w = 1;
36
[email protected]c8686a02012-11-27 08:29:0037 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]94f206c12012-08-25 00:09:1441
42 return HomogeneousCoordinate(outX, outY, outZ, outW);
43}
44
[email protected]c8686a02012-11-27 08:29:0045static HomogeneousCoordinate mapHomogeneousPoint(const gfx::Transform& transform, const gfx::Point3F& p)
[email protected]94f206c12012-08-25 00:09:1446{
47 double x = p.x();
48 double y = p.y();
49 double z = p.z();
50 // implicit definition of w = 1;
51
[email protected]c8686a02012-11-27 08:29:0052 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]94f206c12012-08-25 00:09:1456
57 return HomogeneousCoordinate(outX, outY, outZ, outW);
58}
59
60static 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]1d993172012-10-18 18:15:0474 DCHECK(h2.w != h1.w);
[email protected]94f206c12012-08-25 00:09:1475 // 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]1d993172012-10-18 18:15:0476 DCHECK(h1.shouldBeClipped() ^ h2.shouldBeClipped());
[email protected]94f206c12012-08-25 00:09:1477
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]aad0a0072012-11-01 18:15:5889static inline void expandBoundsToIncludePoint(float& xmin, float& xmax, float& ymin, float& ymax, const gfx::PointF& p)
[email protected]94f206c12012-08-25 00:09:1490{
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]aad0a0072012-11-01 18:15:5897static inline void addVertexToClippedQuad(const gfx::PointF& newVertex, gfx::PointF clippedQuad[8], int& numVerticesInClippedQuad)
[email protected]94f206c12012-08-25 00:09:1498{
99 clippedQuad[numVerticesInClippedQuad] = newVertex;
100 numVerticesInClippedQuad++;
101}
102
[email protected]c8686a02012-11-27 08:29:00103gfx::Rect MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::Rect& srcRect)
[email protected]94f206c12012-08-25 00:09:14104{
[email protected]aad0a0072012-11-01 18:15:58105 return gfx::ToEnclosingRect(mapClippedRect(transform, gfx::RectF(srcRect)));
[email protected]94f206c12012-08-25 00:09:14106}
107
[email protected]c8686a02012-11-27 08:29:00108gfx::RectF MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect)
[email protected]94f206c12012-08-25 00:09:14109{
[email protected]2c7cd6d2012-11-28 23:49:26110 if (transform.IsIdentityOrTranslation())
[email protected]c8686a02012-11-27 08:29:00111 return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
[email protected]94f206c12012-08-25 00:09:14112
113 // Apply the transform, but retain the result in homogeneous coordinates.
[email protected]d79757e32012-11-09 02:02:18114 gfx::QuadF q = gfx::QuadF(srcRect);
[email protected]4eb8a372012-11-01 20:16:56115 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]94f206c12012-08-25 00:09:14119
120 return computeEnclosingClippedRect(h1, h2, h3, h4);
121}
122
[email protected]c8686a02012-11-27 08:29:00123gfx::RectF MathUtil::projectClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect)
[email protected]94f206c12012-08-25 00:09:14124{
[email protected]2c7cd6d2012-11-28 23:49:26125 if (transform.IsIdentityOrTranslation())
[email protected]c8686a02012-11-27 08:29:00126 return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
[email protected]d79757e32012-11-09 02:02:18127
[email protected]94f206c12012-08-25 00:09:14128 // Perform the projection, but retain the result in homogeneous coordinates.
[email protected]d79757e32012-11-09 02:02:18129 gfx::QuadF q = gfx::QuadF(srcRect);
[email protected]94f206c12012-08-25 00:09:14130 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]c8686a02012-11-27 08:29:00138void MathUtil::mapClippedQuad(const gfx::Transform& transform, const gfx::QuadF& srcQuad, gfx::PointF clippedQuad[8], int& numVerticesInClippedQuad)
[email protected]94f206c12012-08-25 00:09:14139{
[email protected]4eb8a372012-11-01 20:16:56140 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]94f206c12012-08-25 00:09:14144
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]1d993172012-10-18 18:15:04173 DCHECK(numVerticesInClippedQuad <= 8);
[email protected]94f206c12012-08-25 00:09:14174}
175
[email protected]aad0a0072012-11-01 18:15:58176gfx::RectF MathUtil::computeEnclosingRectOfVertices(gfx::PointF vertices[], int numVertices)
[email protected]94f206c12012-08-25 00:09:14177{
178 if (numVertices < 2)
[email protected]aad0a0072012-11-01 18:15:58179 return gfx::RectF();
[email protected]94f206c12012-08-25 00:09:14180
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]aad0a0072012-11-01 18:15:58189 return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ymin));
[email protected]94f206c12012-08-25 00:09:14190}
191
[email protected]aad0a0072012-11-01 18:15:58192gfx::RectF MathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4)
[email protected]94f206c12012-08-25 00:09:14193{
194 // This function performs clipping as necessary and computes the enclosing 2d
[email protected]aad0a0072012-11-01 18:15:58195 // gfx::RectF of the vertices. Doing these two steps simultaneously allows us to avoid
[email protected]94f206c12012-08-25 00:09:14196 // 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]075ae0342012-11-02 20:02:41201 gfx::QuadF mappedQuad = gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
202 return mappedQuad.BoundingBox();
[email protected]94f206c12012-08-25 00:09:14203 }
204
205 bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3.shouldBeClipped() && h4.shouldBeClipped();
206 if (everythingClipped)
[email protected]aad0a0072012-11-01 18:15:58207 return gfx::RectF();
[email protected]94f206c12012-08-25 00:09:14208
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]aad0a0072012-11-01 18:15:58239 return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ymin));
[email protected]94f206c12012-08-25 00:09:14240}
241
[email protected]c8686a02012-11-27 08:29:00242gfx::QuadF MathUtil::mapQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped)
[email protected]94f206c12012-08-25 00:09:14243{
[email protected]2c7cd6d2012-11-28 23:49:26244 if (transform.IsIdentityOrTranslation()) {
[email protected]075ae0342012-11-02 20:02:41245 gfx::QuadF mappedQuad(q);
[email protected]c8686a02012-11-27 08:29:00246 mappedQuad += gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
[email protected]94f206c12012-08-25 00:09:14247 clipped = false;
248 return mappedQuad;
249 }
250
[email protected]4eb8a372012-11-01 20:16:56251 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]94f206c12012-08-25 00:09:14255
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]075ae0342012-11-02 20:02:41259 return gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
[email protected]94f206c12012-08-25 00:09:14260}
261
[email protected]c8686a02012-11-27 08:29:00262gfx::PointF MathUtil::mapPoint(const gfx::Transform& transform, const gfx::PointF& p, bool& clipped)
[email protected]94f206c12012-08-25 00:09:14263{
[email protected]4eb8a372012-11-01 20:16:56264 HomogeneousCoordinate h = mapHomogeneousPoint(transform, gfx::Point3F(p));
[email protected]94f206c12012-08-25 00:09:14265
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]aad0a0072012-11-01 18:15:58276 return gfx::PointF();
[email protected]94f206c12012-08-25 00:09:14277
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]c8686a02012-11-27 08:29:00285gfx::Point3F MathUtil::mapPoint(const gfx::Transform& transform, const gfx::Point3F& p, bool& clipped)
[email protected]94f206c12012-08-25 00:09:14286{
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]4eb8a372012-11-01 20:16:56299 return gfx::Point3F();
[email protected]94f206c12012-08-25 00:09:14300
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]c8686a02012-11-27 08:29:00308gfx::QuadF MathUtil::projectQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped)
[email protected]94f206c12012-08-25 00:09:14309{
[email protected]075ae0342012-11-02 20:02:41310 gfx::QuadF projectedQuad;
[email protected]94f206c12012-08-25 00:09:14311 bool clippedPoint;
[email protected]075ae0342012-11-02 20:02:41312 projectedQuad.set_p1(projectPoint(transform, q.p1(), clippedPoint));
[email protected]94f206c12012-08-25 00:09:14313 clipped = clippedPoint;
[email protected]075ae0342012-11-02 20:02:41314 projectedQuad.set_p2(projectPoint(transform, q.p2(), clippedPoint));
[email protected]94f206c12012-08-25 00:09:14315 clipped |= clippedPoint;
[email protected]075ae0342012-11-02 20:02:41316 projectedQuad.set_p3(projectPoint(transform, q.p3(), clippedPoint));
[email protected]94f206c12012-08-25 00:09:14317 clipped |= clippedPoint;
[email protected]075ae0342012-11-02 20:02:41318 projectedQuad.set_p4(projectPoint(transform, q.p4(), clippedPoint));
[email protected]94f206c12012-08-25 00:09:14319 clipped |= clippedPoint;
320
321 return projectedQuad;
322}
323
[email protected]c8686a02012-11-27 08:29:00324gfx::PointF MathUtil::projectPoint(const gfx::Transform& transform, const gfx::PointF& p, bool& clipped)
[email protected]94f206c12012-08-25 00:09:14325{
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]aad0a0072012-11-01 18:15:58339 return gfx::PointF();
[email protected]94f206c12012-08-25 00:09:14340
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]c8686a02012-11-27 08:29:00348void MathUtil::flattenTransformTo2d(gfx::Transform& transform)
[email protected]94f206c12012-08-25 00:09:14349{
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]c8686a02012-11-27 08:29:00360 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]94f206c12012-08-25 00:09:14367}
368
[email protected]518ee582012-10-24 18:29:44369static inline float scaleOnAxis(double a, double b, double c)
370{
371 return std::sqrt(a * a + b * b + c * c);
372}
373
[email protected]c8686a02012-11-27 08:29:00374gfx::Vector2dF MathUtil::computeTransform2dScaleComponents(const gfx::Transform& transform)
[email protected]518ee582012-10-24 18:29:44375{
[email protected]2c7cd6d2012-11-28 23:49:26376 if (transform.HasPerspective())
[email protected]aad0a0072012-11-01 18:15:58377 return gfx::Vector2dF(1, 1);
[email protected]c8686a02012-11-27 08:29:00378 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]aad0a0072012-11-01 18:15:58380 return gfx::Vector2dF(xScale, yScale);
[email protected]518ee582012-10-24 18:29:44381}
382
[email protected]c9c1ebe2012-11-05 20:46:13383float MathUtil::smallestAngleBetweenVectors(gfx::Vector2dF v1, gfx::Vector2dF v2)
[email protected]ec4547162012-11-05 16:56:49384{
[email protected]c9c1ebe2012-11-05 20:46:13385 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]ac7c7f52012-11-08 06:26:50388 return static_cast<float>(Rad2Deg(std::acos(dotProduct)));
[email protected]c9c1ebe2012-11-05 20:46:13389}
390
391gfx::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]94f206c12012-08-25 00:09:14395}
396
[email protected]3d9a2b650f2012-11-19 19:32:25397void 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]2c7cd6d2012-11-28 23:49:26400 // hard-coding each matrix instead of calling RotateAbout().
[email protected]3d9a2b650f2012-11-19 19:32:25401 gfx::Transform rotationAboutX;
402 gfx::Transform rotationAboutY;
403 gfx::Transform rotationAboutZ;
404
[email protected]2c7cd6d2012-11-28 23:49:26405 rotationAboutX.RotateAboutXAxis(eulerX);
406 rotationAboutY.RotateAboutYAxis(eulerY);
407 rotationAboutZ.RotateAboutZAxis(eulerZ);
[email protected]3d9a2b650f2012-11-19 19:32:25408
409 gfx::Transform composite = rotationAboutZ * rotationAboutY * rotationAboutX;
410 transform->PreconcatTransform(composite);
411}
412
[email protected]3d9a2b650f2012-11-19 19:32:25413gfx::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
425gfx::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
441gfx::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
476gfx::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]bc5e77c2012-11-05 20:00:49491} // namespace cc