OLD | NEW |
| (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 "config.h" | |
6 | |
7 #include "CCMathUtil.h" | |
8 | |
9 #include "FloatPoint.h" | |
10 #include "FloatQuad.h" | |
11 #include "IntRect.h" | |
12 #include <public/WebTransformationMatrix.h> | |
13 | |
14 using WebKit::WebTransformationMatrix; | |
15 | |
16 namespace cc { | |
17 | |
18 static HomogeneousCoordinate projectHomogeneousPoint(const WebTransformationMatr
ix& transform, const FloatPoint& p) | |
19 { | |
20 // In this case, the layer we are trying to project onto is perpendicular to
ray | |
21 // (point p and z-axis direction) that we are trying to project. This happen
s when the | |
22 // layer is rotated so that it is infinitesimally thin, or when it is co-pla
nar with | |
23 // the camera origin -- i.e. when the layer is invisible anyway. | |
24 if (!transform.m33()) | |
25 return HomogeneousCoordinate(0, 0, 0, 1); | |
26 | |
27 double x = p.x(); | |
28 double y = p.y(); | |
29 double z = -(transform.m13() * x + transform.m23() * y + transform.m43()) /
transform.m33(); | |
30 // implicit definition of w = 1; | |
31 | |
32 double outX = x * transform.m11() + y * transform.m21() + z * transform.m31(
) + transform.m41(); | |
33 double outY = x * transform.m12() + y * transform.m22() + z * transform.m32(
) + transform.m42(); | |
34 double outZ = x * transform.m13() + y * transform.m23() + z * transform.m33(
) + transform.m43(); | |
35 double outW = x * transform.m14() + y * transform.m24() + z * transform.m34(
) + transform.m44(); | |
36 | |
37 return HomogeneousCoordinate(outX, outY, outZ, outW); | |
38 } | |
39 | |
40 static HomogeneousCoordinate mapHomogeneousPoint(const WebTransformationMatrix&
transform, const FloatPoint3D& p) | |
41 { | |
42 double x = p.x(); | |
43 double y = p.y(); | |
44 double z = p.z(); | |
45 // implicit definition of w = 1; | |
46 | |
47 double outX = x * transform.m11() + y * transform.m21() + z * transform.m31(
) + transform.m41(); | |
48 double outY = x * transform.m12() + y * transform.m22() + z * transform.m32(
) + transform.m42(); | |
49 double outZ = x * transform.m13() + y * transform.m23() + z * transform.m33(
) + transform.m43(); | |
50 double outW = x * transform.m14() + y * transform.m24() + z * transform.m34(
) + transform.m44(); | |
51 | |
52 return HomogeneousCoordinate(outX, outY, outZ, outW); | |
53 } | |
54 | |
55 static HomogeneousCoordinate computeClippedPointForEdge(const HomogeneousCoordin
ate& h1, const HomogeneousCoordinate& h2) | |
56 { | |
57 // Points h1 and h2 form a line in 4d, and any point on that line can be rep
resented | |
58 // as an interpolation between h1 and h2: | |
59 // p = (1-t) h1 + (t) h2 | |
60 // | |
61 // We want to compute point p such that p.w == epsilon, where epsilon is a s
mall | |
62 // non-zero number. (but the smaller the number is, the higher the risk of o
verflow) | |
63 // To do this, we solve for t in the following equation: | |
64 // p.w = epsilon = (1-t) * h1.w + (t) * h2.w | |
65 // | |
66 // Once paramter t is known, the rest of p can be computed via p = (1-t) h1
+ (t) h2. | |
67 | |
68 // Technically this is a special case of the following assertion, but its a
good idea to keep it an explicit sanity check here. | |
69 ASSERT(h2.w != h1.w); | |
70 // Exactly one of h1 or h2 (but not both) must be on the negative side of th
e w plane when this is called. | |
71 ASSERT(h1.shouldBeClipped() ^ h2.shouldBeClipped()); | |
72 | |
73 double w = 0.00001; // or any positive non-zero small epsilon | |
74 | |
75 double t = (w - h1.w) / (h2.w - h1.w); | |
76 | |
77 double x = (1-t) * h1.x + t * h2.x; | |
78 double y = (1-t) * h1.y + t * h2.y; | |
79 double z = (1-t) * h1.z + t * h2.z; | |
80 | |
81 return HomogeneousCoordinate(x, y, z, w); | |
82 } | |
83 | |
84 static inline void expandBoundsToIncludePoint(float& xmin, float& xmax, float& y
min, float& ymax, const FloatPoint& p) | |
85 { | |
86 xmin = std::min(p.x(), xmin); | |
87 xmax = std::max(p.x(), xmax); | |
88 ymin = std::min(p.y(), ymin); | |
89 ymax = std::max(p.y(), ymax); | |
90 } | |
91 | |
92 static inline void addVertexToClippedQuad(const FloatPoint& newVertex, FloatPoin
t clippedQuad[8], int& numVerticesInClippedQuad) | |
93 { | |
94 clippedQuad[numVerticesInClippedQuad] = newVertex; | |
95 numVerticesInClippedQuad++; | |
96 } | |
97 | |
98 IntRect CCMathUtil::mapClippedRect(const WebTransformationMatrix& transform, con
st IntRect& srcRect) | |
99 { | |
100 return enclosingIntRect(mapClippedRect(transform, FloatRect(srcRect))); | |
101 } | |
102 | |
103 FloatRect CCMathUtil::mapClippedRect(const WebTransformationMatrix& transform, c
onst FloatRect& srcRect) | |
104 { | |
105 if (transform.isIdentityOrTranslation()) { | |
106 FloatRect mappedRect(srcRect); | |
107 mappedRect.move(static_cast<float>(transform.m41()), static_cast<float>(
transform.m42())); | |
108 return mappedRect; | |
109 } | |
110 | |
111 // Apply the transform, but retain the result in homogeneous coordinates. | |
112 FloatQuad q = FloatQuad(FloatRect(srcRect)); | |
113 HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, q.p1()); | |
114 HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, q.p2()); | |
115 HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, q.p3()); | |
116 HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, q.p4()); | |
117 | |
118 return computeEnclosingClippedRect(h1, h2, h3, h4); | |
119 } | |
120 | |
121 FloatRect CCMathUtil::projectClippedRect(const WebTransformationMatrix& transfor
m, const FloatRect& srcRect) | |
122 { | |
123 // Perform the projection, but retain the result in homogeneous coordinates. | |
124 FloatQuad q = FloatQuad(FloatRect(srcRect)); | |
125 HomogeneousCoordinate h1 = projectHomogeneousPoint(transform, q.p1()); | |
126 HomogeneousCoordinate h2 = projectHomogeneousPoint(transform, q.p2()); | |
127 HomogeneousCoordinate h3 = projectHomogeneousPoint(transform, q.p3()); | |
128 HomogeneousCoordinate h4 = projectHomogeneousPoint(transform, q.p4()); | |
129 | |
130 return computeEnclosingClippedRect(h1, h2, h3, h4); | |
131 } | |
132 | |
133 void CCMathUtil::mapClippedQuad(const WebTransformationMatrix& transform, const
FloatQuad& srcQuad, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad) | |
134 { | |
135 HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, srcQuad.p1()); | |
136 HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, srcQuad.p2()); | |
137 HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, srcQuad.p3()); | |
138 HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, srcQuad.p4()); | |
139 | |
140 // The order of adding the vertices to the array is chosen so that clockwise
/ counter-clockwise orientation is retained. | |
141 | |
142 numVerticesInClippedQuad = 0; | |
143 | |
144 if (!h1.shouldBeClipped()) | |
145 addVertexToClippedQuad(h1.cartesianPoint2d(), clippedQuad, numVerticesIn
ClippedQuad); | |
146 | |
147 if (h1.shouldBeClipped() ^ h2.shouldBeClipped()) | |
148 addVertexToClippedQuad(computeClippedPointForEdge(h1, h2).cartesianPoint
2d(), clippedQuad, numVerticesInClippedQuad); | |
149 | |
150 if (!h2.shouldBeClipped()) | |
151 addVertexToClippedQuad(h2.cartesianPoint2d(), clippedQuad, numVerticesIn
ClippedQuad); | |
152 | |
153 if (h2.shouldBeClipped() ^ h3.shouldBeClipped()) | |
154 addVertexToClippedQuad(computeClippedPointForEdge(h2, h3).cartesianPoint
2d(), clippedQuad, numVerticesInClippedQuad); | |
155 | |
156 if (!h3.shouldBeClipped()) | |
157 addVertexToClippedQuad(h3.cartesianPoint2d(), clippedQuad, numVerticesIn
ClippedQuad); | |
158 | |
159 if (h3.shouldBeClipped() ^ h4.shouldBeClipped()) | |
160 addVertexToClippedQuad(computeClippedPointForEdge(h3, h4).cartesianPoint
2d(), clippedQuad, numVerticesInClippedQuad); | |
161 | |
162 if (!h4.shouldBeClipped()) | |
163 addVertexToClippedQuad(h4.cartesianPoint2d(), clippedQuad, numVerticesIn
ClippedQuad); | |
164 | |
165 if (h4.shouldBeClipped() ^ h1.shouldBeClipped()) | |
166 addVertexToClippedQuad(computeClippedPointForEdge(h4, h1).cartesianPoint
2d(), clippedQuad, numVerticesInClippedQuad); | |
167 | |
168 ASSERT(numVerticesInClippedQuad <= 8); | |
169 } | |
170 | |
171 FloatRect CCMathUtil::computeEnclosingRectOfVertices(FloatPoint vertices[], int
numVertices) | |
172 { | |
173 if (numVertices < 2) | |
174 return FloatRect(); | |
175 | |
176 float xmin = std::numeric_limits<float>::max(); | |
177 float xmax = -std::numeric_limits<float>::max(); | |
178 float ymin = std::numeric_limits<float>::max(); | |
179 float ymax = -std::numeric_limits<float>::max(); | |
180 | |
181 for (int i = 0; i < numVertices; ++i) | |
182 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, vertices[i]); | |
183 | |
184 return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin)
); | |
185 } | |
186 | |
187 FloatRect CCMathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h
1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const Homog
eneousCoordinate& h4) | |
188 { | |
189 // This function performs clipping as necessary and computes the enclosing 2
d | |
190 // FloatRect of the vertices. Doing these two steps simultaneously allows us
to avoid | |
191 // the overhead of storing an unknown number of clipped vertices. | |
192 | |
193 // If no vertices on the quad are clipped, then we can simply return the enc
losing rect directly. | |
194 bool somethingClipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.s
houldBeClipped() || h4.shouldBeClipped(); | |
195 if (!somethingClipped) { | |
196 FloatQuad mappedQuad = FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoin
t2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d()); | |
197 return mappedQuad.boundingBox(); | |
198 } | |
199 | |
200 bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3.
shouldBeClipped() && h4.shouldBeClipped(); | |
201 if (everythingClipped) | |
202 return FloatRect(); | |
203 | |
204 | |
205 float xmin = std::numeric_limits<float>::max(); | |
206 float xmax = -std::numeric_limits<float>::max(); | |
207 float ymin = std::numeric_limits<float>::max(); | |
208 float ymax = -std::numeric_limits<float>::max(); | |
209 | |
210 if (!h1.shouldBeClipped()) | |
211 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h1.cartesianPoint2d()
); | |
212 | |
213 if (h1.shouldBeClipped() ^ h2.shouldBeClipped()) | |
214 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo
rEdge(h1, h2).cartesianPoint2d()); | |
215 | |
216 if (!h2.shouldBeClipped()) | |
217 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h2.cartesianPoint2d()
); | |
218 | |
219 if (h2.shouldBeClipped() ^ h3.shouldBeClipped()) | |
220 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo
rEdge(h2, h3).cartesianPoint2d()); | |
221 | |
222 if (!h3.shouldBeClipped()) | |
223 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h3.cartesianPoint2d()
); | |
224 | |
225 if (h3.shouldBeClipped() ^ h4.shouldBeClipped()) | |
226 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo
rEdge(h3, h4).cartesianPoint2d()); | |
227 | |
228 if (!h4.shouldBeClipped()) | |
229 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h4.cartesianPoint2d()
); | |
230 | |
231 if (h4.shouldBeClipped() ^ h1.shouldBeClipped()) | |
232 expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointFo
rEdge(h4, h1).cartesianPoint2d()); | |
233 | |
234 return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin)
); | |
235 } | |
236 | |
237 FloatQuad CCMathUtil::mapQuad(const WebTransformationMatrix& transform, const Fl
oatQuad& q, bool& clipped) | |
238 { | |
239 if (transform.isIdentityOrTranslation()) { | |
240 FloatQuad mappedQuad(q); | |
241 mappedQuad.move(static_cast<float>(transform.m41()), static_cast<float>(
transform.m42())); | |
242 clipped = false; | |
243 return mappedQuad; | |
244 } | |
245 | |
246 HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, q.p1()); | |
247 HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, q.p2()); | |
248 HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, q.p3()); | |
249 HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, q.p4()); | |
250 | |
251 clipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped
() || h4.shouldBeClipped(); | |
252 | |
253 // Result will be invalid if clipped == true. But, compute it anyway just in
case, to emulate existing behavior. | |
254 return FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianP
oint2d(), h4.cartesianPoint2d()); | |
255 } | |
256 | |
257 FloatPoint CCMathUtil::mapPoint(const WebTransformationMatrix& transform, const
FloatPoint& p, bool& clipped) | |
258 { | |
259 HomogeneousCoordinate h = mapHomogeneousPoint(transform, p); | |
260 | |
261 if (h.w > 0) { | |
262 clipped = false; | |
263 return h.cartesianPoint2d(); | |
264 } | |
265 | |
266 // The cartesian coordinates will be invalid after dividing by w. | |
267 clipped = true; | |
268 | |
269 // Avoid dividing by w if w == 0. | |
270 if (!h.w) | |
271 return FloatPoint(); | |
272 | |
273 // This return value will be invalid because clipped == true, but (1) users
of this | |
274 // code should be ignoring the return value when clipped == true anyway, and
(2) this | |
275 // behavior is more consistent with existing behavior of WebKit transforms i
f the user | |
276 // really does not ignore the return value. | |
277 return h.cartesianPoint2d(); | |
278 } | |
279 | |
280 FloatPoint3D CCMathUtil::mapPoint(const WebTransformationMatrix& transform, cons
t FloatPoint3D& p, bool& clipped) | |
281 { | |
282 HomogeneousCoordinate h = mapHomogeneousPoint(transform, p); | |
283 | |
284 if (h.w > 0) { | |
285 clipped = false; | |
286 return h.cartesianPoint3d(); | |
287 } | |
288 | |
289 // The cartesian coordinates will be invalid after dividing by w. | |
290 clipped = true; | |
291 | |
292 // Avoid dividing by w if w == 0. | |
293 if (!h.w) | |
294 return FloatPoint3D(); | |
295 | |
296 // This return value will be invalid because clipped == true, but (1) users
of this | |
297 // code should be ignoring the return value when clipped == true anyway, and
(2) this | |
298 // behavior is more consistent with existing behavior of WebKit transforms i
f the user | |
299 // really does not ignore the return value. | |
300 return h.cartesianPoint3d(); | |
301 } | |
302 | |
303 FloatQuad CCMathUtil::projectQuad(const WebTransformationMatrix& transform, cons
t FloatQuad& q, bool& clipped) | |
304 { | |
305 FloatQuad projectedQuad; | |
306 bool clippedPoint; | |
307 projectedQuad.setP1(projectPoint(transform, q.p1(), clippedPoint)); | |
308 clipped = clippedPoint; | |
309 projectedQuad.setP2(projectPoint(transform, q.p2(), clippedPoint)); | |
310 clipped |= clippedPoint; | |
311 projectedQuad.setP3(projectPoint(transform, q.p3(), clippedPoint)); | |
312 clipped |= clippedPoint; | |
313 projectedQuad.setP4(projectPoint(transform, q.p4(), clippedPoint)); | |
314 clipped |= clippedPoint; | |
315 | |
316 return projectedQuad; | |
317 } | |
318 | |
319 FloatPoint CCMathUtil::projectPoint(const WebTransformationMatrix& transform, co
nst FloatPoint& p, bool& clipped) | |
320 { | |
321 HomogeneousCoordinate h = projectHomogeneousPoint(transform, p); | |
322 | |
323 if (h.w > 0) { | |
324 // The cartesian coordinates will be valid in this case. | |
325 clipped = false; | |
326 return h.cartesianPoint2d(); | |
327 } | |
328 | |
329 // The cartesian coordinates will be invalid after dividing by w. | |
330 clipped = true; | |
331 | |
332 // Avoid dividing by w if w == 0. | |
333 if (!h.w) | |
334 return FloatPoint(); | |
335 | |
336 // This return value will be invalid because clipped == true, but (1) users
of this | |
337 // code should be ignoring the return value when clipped == true anyway, and
(2) this | |
338 // behavior is more consistent with existing behavior of WebKit transforms i
f the user | |
339 // really does not ignore the return value. | |
340 return h.cartesianPoint2d(); | |
341 } | |
342 | |
343 void CCMathUtil::flattenTransformTo2d(WebTransformationMatrix& transform) | |
344 { | |
345 // Set both the 3rd row and 3rd column to (0, 0, 1, 0). | |
346 // | |
347 // One useful interpretation of doing this operation: | |
348 // - For x and y values, the new transform behaves effectively like an orth
ographic | |
349 // projection was added to the matrix sequence. | |
350 // - For z values, the new transform overrides any effect that the transfor
m had on | |
351 // z, and instead it preserves the z value for any points that are transf
ormed. | |
352 // - Because of linearity of transforms, this flattened transform also pres
erves the | |
353 // effect that any subsequent (post-multiplied) transforms would have on
z values. | |
354 // | |
355 transform.setM13(0); | |
356 transform.setM23(0); | |
357 transform.setM31(0); | |
358 transform.setM32(0); | |
359 transform.setM33(1); | |
360 transform.setM34(0); | |
361 transform.setM43(0); | |
362 } | |
363 | |
364 float CCMathUtil::smallestAngleBetweenVectors(const FloatSize& v1, const FloatSi
ze& v2) | |
365 { | |
366 float dotProduct = (v1.width() * v2.width() + v1.height() * v2.height()) / (
v1.diagonalLength() * v2.diagonalLength()); | |
367 // Clamp to compensate for rounding errors. | |
368 dotProduct = std::max(-1.f, std::min(1.f, dotProduct)); | |
369 return rad2deg(acosf(dotProduct)); | |
370 } | |
371 | |
372 FloatSize CCMathUtil::projectVector(const FloatSize& source, const FloatSize& de
stination) | |
373 { | |
374 float sourceDotDestination = source.width() * destination.width() + source.h
eight() * destination.height(); | |
375 float projectedLength = sourceDotDestination / destination.diagonalLengthSqu
ared(); | |
376 return FloatSize(projectedLength * destination.width(), projectedLength * de
stination.height()); | |
377 } | |
378 | |
379 } // namespace cc | |
OLD | NEW |