Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(261)

Side by Side Diff: cc/math_util.cc

Issue 12472028: Part 1 of cc/ directory shuffles: base (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
5 #include "cc/math_util.h"
6
7 #include <cmath>
8 #include <limits>
9
10 #include "base/values.h"
11 #include "ui/gfx/quad_f.h"
12 #include "ui/gfx/rect.h"
13 #include "ui/gfx/rect_conversions.h"
14 #include "ui/gfx/rect_f.h"
15 #include "ui/gfx/transform.h"
16 #include "ui/gfx/vector2d_f.h"
17
18 namespace cc {
19
20 const double MathUtil::PI_DOUBLE = 3.14159265358979323846;
21 const float MathUtil::PI_FLOAT = 3.14159265358979323846f;
22 const double MathUtil::EPSILON = 1e-9;
23
24 static HomogeneousCoordinate projectHomogeneousPoint(const gfx::Transform& trans form, const gfx::PointF& p)
25 {
26 // In this case, the layer we are trying to project onto is perpendicular to ray
27 // (point p and z-axis direction) that we are trying to project. This happen s when the
28 // layer is rotated so that it is infinitesimally thin, or when it is co-pla nar with
29 // the camera origin -- i.e. when the layer is invisible anyway.
30 if (!transform.matrix().getDouble(2, 2))
31 return HomogeneousCoordinate(0, 0, 0, 1);
32
33 double x = p.x();
34 double y = p.y();
35 double z = -(transform.matrix().getDouble(2, 0) * x + transform.matrix().get Double(2, 1) * y + transform.matrix().getDouble(2, 3)) / transform.matrix().getD ouble(2, 2);
36 // implicit definition of w = 1;
37
38 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);
39 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);
40 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);
41 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);
42
43 return HomogeneousCoordinate(outX, outY, outZ, outW);
44 }
45
46 static HomogeneousCoordinate mapHomogeneousPoint(const gfx::Transform& transform , const gfx::Point3F& p)
47 {
48 double x = p.x();
49 double y = p.y();
50 double z = p.z();
51 // implicit definition of w = 1;
52
53 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);
54 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);
55 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);
56 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);
57
58 return HomogeneousCoordinate(outX, outY, outZ, outW);
59 }
60
61 static HomogeneousCoordinate computeClippedPointForEdge(const HomogeneousCoordin ate& h1, const HomogeneousCoordinate& h2)
62 {
63 // Points h1 and h2 form a line in 4d, and any point on that line can be rep resented
64 // as an interpolation between h1 and h2:
65 // p = (1-t) h1 + (t) h2
66 //
67 // We want to compute point p such that p.w == epsilon, where epsilon is a s mall
68 // non-zero number. (but the smaller the number is, the higher the risk of o verflow)
69 // To do this, we solve for t in the following equation:
70 // p.w = epsilon = (1-t) * h1.w + (t) * h2.w
71 //
72 // Once paramter t is known, the rest of p can be computed via p = (1-t) h1 + (t) h2.
73
74 // Technically this is a special case of the following assertion, but its a good idea to keep it an explicit sanity check here.
75 DCHECK(h2.w != h1.w);
76 // Exactly one of h1 or h2 (but not both) must be on the negative side of th e w plane when this is called.
77 DCHECK(h1.shouldBeClipped() ^ h2.shouldBeClipped());
78
79 double w = 0.00001; // or any positive non-zero small epsilon
80
81 double t = (w - h1.w) / (h2.w - h1.w);
82
83 double x = (1-t) * h1.x + t * h2.x;
84 double y = (1-t) * h1.y + t * h2.y;
85 double z = (1-t) * h1.z + t * h2.z;
86
87 return HomogeneousCoordinate(x, y, z, w);
88 }
89
90 static inline void expandBoundsToIncludePoint(float& xmin, float& xmax, float& y min, float& ymax, const gfx::PointF& p)
91 {
92 xmin = std::min(p.x(), xmin);
93 xmax = std::max(p.x(), xmax);
94 ymin = std::min(p.y(), ymin);
95 ymax = std::max(p.y(), ymax);
96 }
97
98 static inline void addVertexToClippedQuad(const gfx::PointF& newVertex, gfx::Poi ntF clippedQuad[8], int& numVerticesInClippedQuad)
99 {
100 clippedQuad[numVerticesInClippedQuad] = newVertex;
101 numVerticesInClippedQuad++;
102 }
103
104 gfx::Rect MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::R ect& srcRect)
105 {
106 return gfx::ToEnclosingRect(mapClippedRect(transform, gfx::RectF(srcRect)));
107 }
108
109 gfx::RectF MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx:: RectF& srcRect)
110 {
111 if (transform.IsIdentityOrTranslation())
112 return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().ge tDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
113
114 // Apply the transform, but retain the result in homogeneous coordinates.
115
116 double quad[4 * 2]; // input: 4 x 2D points
117 quad[0] = srcRect.x();
118 quad[1] = srcRect.y();
119 quad[2] = srcRect.right();
120 quad[3] = srcRect.y();
121 quad[4] = srcRect.right();
122 quad[5] = srcRect.bottom();
123 quad[6] = srcRect.x();
124 quad[7] = srcRect.bottom();
125
126 double result[4 * 4]; // output: 4 x 4D homogeneous points
127 transform.matrix().map2(quad, 4, result);
128
129 HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
130 HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
131 HomogeneousCoordinate hc2(result[8], result[9], result[10], result[11]);
132 HomogeneousCoordinate hc3(result[12], result[13], result[14], result[15]);
133 return computeEnclosingClippedRect(hc0, hc1, hc2, hc3);
134 }
135
136 gfx::RectF MathUtil::projectClippedRect(const gfx::Transform& transform, const g fx::RectF& srcRect)
137 {
138 if (transform.IsIdentityOrTranslation())
139 return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().ge tDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
140
141 // Perform the projection, but retain the result in homogeneous coordinates.
142 gfx::QuadF q = gfx::QuadF(srcRect);
143 HomogeneousCoordinate h1 = projectHomogeneousPoint(transform, q.p1());
144 HomogeneousCoordinate h2 = projectHomogeneousPoint(transform, q.p2());
145 HomogeneousCoordinate h3 = projectHomogeneousPoint(transform, q.p3());
146 HomogeneousCoordinate h4 = projectHomogeneousPoint(transform, q.p4());
147
148 return computeEnclosingClippedRect(h1, h2, h3, h4);
149 }
150
151 void MathUtil::mapClippedQuad(const gfx::Transform& transform, const gfx::QuadF& srcQuad, gfx::PointF clippedQuad[8], int& numVerticesInClippedQuad)
152 {
153 HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, gfx::Point3F(srcQu ad.p1()));
154 HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, gfx::Point3F(srcQu ad.p2()));
155 HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, gfx::Point3F(srcQu ad.p3()));
156 HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, gfx::Point3F(srcQu ad.p4()));
157
158 // The order of adding the vertices to the array is chosen so that clockwise / counter-clockwise orientation is retained.
159
160 numVerticesInClippedQuad = 0;
161
162 if (!h1.shouldBeClipped())
163 addVertexToClippedQuad(h1.cartesianPoint2d(), clippedQuad, numVerticesIn ClippedQuad);
164
165 if (h1.shouldBeClipped() ^ h2.shouldBeClipped())
166 addVertexToClippedQuad(computeClippedPointForEdge(h1, h2).cartesianPoint 2d(), clippedQuad, numVerticesInClippedQuad);
167
168 if (!h2.shouldBeClipped())
169 addVertexToClippedQuad(h2.cartesianPoint2d(), clippedQuad, numVerticesIn ClippedQuad);
170
171 if (h2.shouldBeClipped() ^ h3.shouldBeClipped())
172 addVertexToClippedQuad(computeClippedPointForEdge(h2, h3).cartesianPoint 2d(), clippedQuad, numVerticesInClippedQuad);
173
174 if (!h3.shouldBeClipped())
175 addVertexToClippedQuad(h3.cartesianPoint2d(), clippedQuad, numVerticesIn ClippedQuad);
176
177 if (h3.shouldBeClipped() ^ h4.shouldBeClipped())
178 addVertexToClippedQuad(computeClippedPointForEdge(h3, h4).cartesianPoint 2d(), clippedQuad, numVerticesInClippedQuad);
179
180 if (!h4.shouldBeClipped())
181 addVertexToClippedQuad(h4.cartesianPoint2d(), clippedQuad, numVerticesIn ClippedQuad);
182
183 if (h4.shouldBeClipped() ^ h1.shouldBeClipped())
184 addVertexToClippedQuad(computeClippedPointForEdge(h4, h1).cartesianPoint 2d(), clippedQuad, numVerticesInClippedQuad);
185
186 DCHECK(numVerticesInClippedQuad <= 8);
187 }
188
189 gfx::RectF MathUtil::computeEnclosingRectOfVertices(gfx::PointF vertices[], int numVertices)
190 {
191 if (numVertices < 2)
192 return gfx::RectF();
193
194 float xmin = std::numeric_limits<float>::max();
195 float xmax = -std::numeric_limits<float>::max();
196 float ymin = std::numeric_limits<float>::max();
197 float ymax = -std::numeric_limits<float>::max();
198
199 for (int i = 0; i < numVertices; ++i)
200 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, vertices[i]);
201
202 return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ym in));
203 }
204
205 gfx::RectF MathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h1 , const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const Homoge neousCoordinate& h4)
206 {
207 // This function performs clipping as necessary and computes the enclosing 2 d
208 // gfx::RectF of the vertices. Doing these two steps simultaneously allows u s to avoid
209 // the overhead of storing an unknown number of clipped vertices.
210
211 // If no vertices on the quad are clipped, then we can simply return the enc losing rect directly.
212 bool somethingClipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.s houldBeClipped() || h4.shouldBeClipped();
213 if (!somethingClipped) {
214 gfx::QuadF mappedQuad = gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPo int2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
215 return mappedQuad.BoundingBox();
216 }
217
218 bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3. shouldBeClipped() && h4.shouldBeClipped();
219 if (everythingClipped)
220 return gfx::RectF();
221
222
223 float xmin = std::numeric_limits<float>::max();
224 float xmax = -std::numeric_limits<float>::max();
225 float ymin = std::numeric_limits<float>::max();
226 float ymax = -std::numeric_limits<float>::max();
227
228 if (!h1.shouldBeClipped())
229 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h1.cartesianPoint2d() );
230
231 if (h1.shouldBeClipped() ^ h2.shouldBeClipped())
232 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo rEdge(h1, h2).cartesianPoint2d());
233
234 if (!h2.shouldBeClipped())
235 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h2.cartesianPoint2d() );
236
237 if (h2.shouldBeClipped() ^ h3.shouldBeClipped())
238 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo rEdge(h2, h3).cartesianPoint2d());
239
240 if (!h3.shouldBeClipped())
241 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h3.cartesianPoint2d() );
242
243 if (h3.shouldBeClipped() ^ h4.shouldBeClipped())
244 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo rEdge(h3, h4).cartesianPoint2d());
245
246 if (!h4.shouldBeClipped())
247 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h4.cartesianPoint2d() );
248
249 if (h4.shouldBeClipped() ^ h1.shouldBeClipped())
250 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo rEdge(h4, h1).cartesianPoint2d());
251
252 return gfx::RectF(gfx::PointF(xmin, ymin), gfx::SizeF(xmax - xmin, ymax - ym in));
253 }
254
255 gfx::QuadF MathUtil::mapQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped)
256 {
257 if (transform.IsIdentityOrTranslation()) {
258 gfx::QuadF mappedQuad(q);
259 mappedQuad += gfx::Vector2dF(static_cast<float>(transform.matrix().getDo uble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3)));
260 clipped = false;
261 return mappedQuad;
262 }
263
264 HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, gfx::Point3F(q.p1( )));
265 HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, gfx::Point3F(q.p2( )));
266 HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, gfx::Point3F(q.p3( )));
267 HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, gfx::Point3F(q.p4( )));
268
269 clipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped () || h4.shouldBeClipped();
270
271 // Result will be invalid if clipped == true. But, compute it anyway just in case, to emulate existing behavior.
272 return gfx::QuadF(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesian Point2d(), h4.cartesianPoint2d());
273 }
274
275 gfx::PointF MathUtil::mapPoint(const gfx::Transform& transform, const gfx::Point F& p, bool& clipped)
276 {
277 HomogeneousCoordinate h = mapHomogeneousPoint(transform, gfx::Point3F(p));
278
279 if (h.w > 0) {
280 clipped = false;
281 return h.cartesianPoint2d();
282 }
283
284 // The cartesian coordinates will be invalid after dividing by w.
285 clipped = true;
286
287 // Avoid dividing by w if w == 0.
288 if (!h.w)
289 return gfx::PointF();
290
291 // This return value will be invalid because clipped == true, but (1) users of this
292 // code should be ignoring the return value when clipped == true anyway, and (2) this
293 // behavior is more consistent with existing behavior of WebKit transforms i f the user
294 // really does not ignore the return value.
295 return h.cartesianPoint2d();
296 }
297
298 gfx::Point3F MathUtil::mapPoint(const gfx::Transform& transform, const gfx::Poin t3F& p, bool& clipped)
299 {
300 HomogeneousCoordinate h = mapHomogeneousPoint(transform, p);
301
302 if (h.w > 0) {
303 clipped = false;
304 return h.cartesianPoint3d();
305 }
306
307 // The cartesian coordinates will be invalid after dividing by w.
308 clipped = true;
309
310 // Avoid dividing by w if w == 0.
311 if (!h.w)
312 return gfx::Point3F();
313
314 // This return value will be invalid because clipped == true, but (1) users of this
315 // code should be ignoring the return value when clipped == true anyway, and (2) this
316 // behavior is more consistent with existing behavior of WebKit transforms i f the user
317 // really does not ignore the return value.
318 return h.cartesianPoint3d();
319 }
320
321 gfx::QuadF MathUtil::projectQuad(const gfx::Transform& transform, const gfx::Qua dF& q, bool& clipped)
322 {
323 gfx::QuadF projectedQuad;
324 bool clippedPoint;
325 projectedQuad.set_p1(projectPoint(transform, q.p1(), clippedPoint));
326 clipped = clippedPoint;
327 projectedQuad.set_p2(projectPoint(transform, q.p2(), clippedPoint));
328 clipped |= clippedPoint;
329 projectedQuad.set_p3(projectPoint(transform, q.p3(), clippedPoint));
330 clipped |= clippedPoint;
331 projectedQuad.set_p4(projectPoint(transform, q.p4(), clippedPoint));
332 clipped |= clippedPoint;
333
334 return projectedQuad;
335 }
336
337 gfx::PointF MathUtil::projectPoint(const gfx::Transform& transform, const gfx::P ointF& p, bool& clipped)
338 {
339 HomogeneousCoordinate h = projectHomogeneousPoint(transform, p);
340
341 if (h.w > 0) {
342 // The cartesian coordinates will be valid in this case.
343 clipped = false;
344 return h.cartesianPoint2d();
345 }
346
347 // The cartesian coordinates will be invalid after dividing by w.
348 clipped = true;
349
350 // Avoid dividing by w if w == 0.
351 if (!h.w)
352 return gfx::PointF();
353
354 // This return value will be invalid because clipped == true, but (1) users of this
355 // code should be ignoring the return value when clipped == true anyway, and (2) this
356 // behavior is more consistent with existing behavior of WebKit transforms i f the user
357 // really does not ignore the return value.
358 return h.cartesianPoint2d();
359 }
360
361 static inline float scaleOnAxis(double a, double b, double c)
362 {
363 return std::sqrt(a * a + b * b + c * c);
364 }
365
366 gfx::Vector2dF MathUtil::computeTransform2dScaleComponents(const gfx::Transform& transform, float fallbackValue)
367 {
368 if (transform.HasPerspective())
369 return gfx::Vector2dF(fallbackValue, fallbackValue);
370 float xScale = scaleOnAxis(transform.matrix().getDouble(0, 0), transform.mat rix().getDouble(1, 0), transform.matrix().getDouble(2, 0));
371 float yScale = scaleOnAxis(transform.matrix().getDouble(0, 1), transform.mat rix().getDouble(1, 1), transform.matrix().getDouble(2, 1));
372 return gfx::Vector2dF(xScale, yScale);
373 }
374
375 float MathUtil::smallestAngleBetweenVectors(gfx::Vector2dF v1, gfx::Vector2dF v2 )
376 {
377 double dotProduct = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length();
378 // Clamp to compensate for rounding errors.
379 dotProduct = std::max(-1.0, std::min(1.0, dotProduct));
380 return static_cast<float>(Rad2Deg(std::acos(dotProduct)));
381 }
382
383 gfx::Vector2dF MathUtil::projectVector(gfx::Vector2dF source, gfx::Vector2dF des tination)
384 {
385 float projectedLength = gfx::DotProduct(source, destination) / destination.L engthSquared();
386 return gfx::Vector2dF(projectedLength * destination.x(), projectedLength * d estination.y());
387 }
388
389 scoped_ptr<base::Value> MathUtil::asValue(gfx::Size s) {
390 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
391 res->SetDouble("width", s.width());
392 res->SetDouble("height", s.height());
393 return res.PassAs<base::Value>();
394 }
395
396 scoped_ptr<base::Value> MathUtil::asValue(gfx::PointF pt) {
397 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
398 res->SetDouble("x", pt.x());
399 res->SetDouble("y", pt.y());
400 return res.PassAs<base::Value>();
401 }
402
403 scoped_ptr<base::Value> MathUtil::asValue(gfx::QuadF q) {
404 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
405 res->Set("p1", asValue(q.p1()).release());
406 res->Set("p2", asValue(q.p2()).release());
407 res->Set("p3", asValue(q.p3()).release());
408 res->Set("p4", asValue(q.p4()).release());
409 return res.PassAs<base::Value>();
410 }
411
412 scoped_ptr<base::Value> MathUtil::asValueSafely(double value) {
413 return scoped_ptr<base::Value>(base::Value::CreateDoubleValue(
414 std::min(value, std::numeric_limits<double>::max())));
415 }
416
417 scoped_ptr<base::Value> MathUtil::asValueSafely(float value) {
418 return scoped_ptr<base::Value>(base::Value::CreateDoubleValue(
419 std::min(value, std::numeric_limits<float>::max())));
420 }
421
422 } // namespace cc
OLDNEW
« cc/cc.gyp ('K') | « cc/math_util.h ('k') | cc/math_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698