OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) | 2 * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) |
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. |
4 * | 4 * |
5 * This library is free software; you can redistribute it and/or | 5 * This library is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU Library General Public | 6 * modify it under the terms of the GNU Library General Public |
7 * License as published by the Free Software Foundation; either | 7 * License as published by the Free Software Foundation; either |
8 * version 2 of the License, or (at your option) any later version. | 8 * version 2 of the License, or (at your option) any later version. |
9 * | 9 * |
10 * This library is distributed in the hope that it will be useful, | 10 * This library is distributed in the hope that it will be useful, |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 * Library General Public License for more details. | 13 * Library General Public License for more details. |
14 * | 14 * |
15 * You should have received a copy of the GNU Library General Public License | 15 * You should have received a copy of the GNU Library General Public License |
16 * along with this library; see the file COPYING.LIB. If not, write to | 16 * along with this library; see the file COPYING.LIB. If not, write to |
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 * Boston, MA 02110-1301, USA. | 18 * Boston, MA 02110-1301, USA. |
19 * | 19 * |
20 */ | 20 */ |
21 | 21 |
22 #include "config.h" | 22 #include "config.h" |
23 #include "platform/transforms/TransformOperations.h" | 23 #include "platform/transforms/TransformOperations.h" |
24 | 24 |
25 #include "platform/animation/AnimationUtilities.h" | |
26 #include "platform/geometry/FloatBox.h" | |
25 #include "platform/transforms/IdentityTransformOperation.h" | 27 #include "platform/transforms/IdentityTransformOperation.h" |
26 #include "platform/transforms/InterpolatedTransformOperation.h" | 28 #include "platform/transforms/InterpolatedTransformOperation.h" |
29 #include "platform/transforms/RotateTransformOperation.h" | |
27 #include <algorithm> | 30 #include <algorithm> |
28 | 31 |
29 using namespace std; | 32 using namespace std; |
30 | 33 |
31 namespace WebCore { | 34 namespace WebCore { |
32 | 35 |
33 TransformOperations::TransformOperations(bool makeIdentity) | 36 TransformOperations::TransformOperations(bool makeIdentity) |
34 { | 37 { |
35 if (makeIdentity) | 38 if (makeIdentity) |
36 m_operations.append(IdentityTransformOperation::create()); | 39 m_operations.append(IdentityTransformOperation::create()); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
102 if (from == *this || (!from.size() && !size())) | 105 if (from == *this || (!from.size() && !size())) |
103 return *this; | 106 return *this; |
104 | 107 |
105 // If either list is empty, use blendByMatchingOperations which has special logic for this case. | 108 // If either list is empty, use blendByMatchingOperations which has special logic for this case. |
106 if (!from.size() || !size() || from.operationsMatch(*this)) | 109 if (!from.size() || !size() || from.operationsMatch(*this)) |
107 return blendByMatchingOperations(from, progress); | 110 return blendByMatchingOperations(from, progress); |
108 | 111 |
109 return blendByUsingMatrixInterpolation(from, progress); | 112 return blendByUsingMatrixInterpolation(from, progress); |
110 } | 113 } |
111 | 114 |
115 static void findCandidatesInPlane(double px, double py, double nz, double* candi dates, int* numCandidates) | |
116 { | |
117 // The angle that this point is rotated with respect to the plane nz | |
118 double phi = atan2(px, py); | |
119 | |
120 *numCandidates = 4; | |
121 candidates[0] = phi; // The element at 0deg (maximum x) | |
122 | |
123 for (int i = 1; i < *numCandidates; ++i) | |
124 candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg | |
125 if (nz < 0.f) { | |
126 for (int i = 0; i < *numCandidates; ++i) | |
127 candidates[i] *= -1; | |
128 } | |
129 } | |
130 | |
131 // This method returns the bounding box that contains the starting point, | |
132 // the ending point, and any of the extrema (in each dimension) found across | |
Ian Vollick
2014/06/17 15:54:56
nit: extra space.
awoloszyn
2014/06/17 20:06:02
Done.
| |
133 // the circle described by the arc. These are then filtered to points that | |
134 // actually reside on the arc. | |
135 static void boundingBoxForArc(const FloatPoint3D& point, const RotateTransformOp eration& fromTransform, const RotateTransformOperation& toTransform, double minP rogress, double maxProgress, FloatBox& box) | |
136 { | |
137 double candidates[6]; | |
138 int numCandidates = 0; | |
139 | |
140 FloatPoint3D axis(fromTransform.x(), fromTransform.y(), fromTransform.z()); | |
Ian Vollick
2014/06/17 15:54:56
fromTransform.axis()
awoloszyn
2014/06/17 20:06:02
Done.
| |
141 double fromDegrees = fromTransform.angle(); | |
142 double toDegrees = toTransform.angle(); | |
143 | |
144 if (axis.dot(toTransform.axis()) < 0) | |
145 toDegrees *= -1; | |
146 | |
147 fromDegrees = blend(fromDegrees, toTransform.angle(), minProgress); | |
148 toDegrees = blend(toDegrees, fromTransform.angle(), 1.0 - maxProgress); | |
149 if (fromDegrees > toDegrees) | |
150 std::swap(fromDegrees, toDegrees); | |
151 | |
152 TransformationMatrix fromMatrix; | |
153 TransformationMatrix toMatrix; | |
154 fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), fromDegrees); | |
155 toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), t oDegrees); | |
156 | |
157 FloatPoint3D fromPoint = fromMatrix.mapPoint(point); | |
158 FloatPoint3D toPoint = toMatrix.mapPoint(point); | |
159 | |
160 if (box.isEmpty()) | |
161 box.setOrigin(fromPoint); | |
162 else | |
163 box.expandTo(fromPoint); | |
164 | |
165 box.expandTo(toPoint); | |
166 | |
167 switch (fromTransform.type()) { | |
168 case TransformOperation::RotateX: | |
169 findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidate s, &numCandidates); | |
170 break; | |
171 case TransformOperation::RotateY: | |
172 findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidate s, &numCandidates); | |
173 break; | |
174 case TransformOperation::RotateZ: | |
175 findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidate s, &numCandidates); | |
176 break; | |
177 default: | |
178 { | |
179 FloatPoint3D normal = axis; | |
180 if (normal.isZero()) | |
181 return; | |
182 normal.normalize(); | |
183 FloatPoint3D origin; | |
184 FloatPoint3D toPoint = point - origin; | |
185 FloatPoint3D center = origin + normal*toPoint.dot(normal); | |
Ian Vollick
2014/06/17 15:54:56
http://dev.chromium.org/blink/coding-style says sp
awoloszyn
2014/06/17 20:06:02
Done.
| |
186 FloatPoint3D v1 = point - center; | |
187 if (v1.isZero()) | |
188 return; | |
189 | |
190 v1.normalize(); | |
191 FloatPoint3D v2 = normal.cross(v1); | |
192 // v1 is the basis vector in the direction of the point. | |
193 // i.e. with a rotation of 0, v1 is our +x vector. | |
194 // v2 is a perpenticular basis vector of our plane (+y). | |
195 | |
196 // Take the parametric equation of a circle. | |
197 // (x = r*cos(t); y = r*sin(t); | |
198 // We can treat that as a circle on the plane v1xv2 | |
199 // From that we get the parametric equations for a circle on the | |
200 // plane in 3d space of | |
201 // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx | |
202 // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy | |
203 // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz | |
204 // taking the derivative of (x, y, z) and solving for 0 gives us our | |
205 // maximum/minimum x, y, z values | |
206 // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 | |
207 // tan(t) = v2.x/v1.x | |
208 // t = atan2(v2.x, v1.x) + n*M_PI; | |
209 | |
210 candidates[0] = atan2(v2.x(), v1.x()); | |
211 candidates[1] = candidates[0] + M_PI; | |
212 candidates[2] = atan2(v2.y(), v1.y()); | |
213 candidates[3] = candidates[2] + M_PI; | |
214 candidates[4] = atan2(v2.z(), v1.z()); | |
215 candidates[5] = candidates[4] + M_PI; | |
216 numCandidates = 6; | |
217 } | |
218 break; | |
219 } | |
220 | |
221 double minRadians = deg2rad(fromDegrees); | |
222 double maxRadians = deg2rad(toDegrees); | |
223 // Once we have the candidates, we now filter them down to ones that | |
224 // actually live on the arc, rather than the entire circle. | |
225 for (int i = 0; i < numCandidates; ++i) { | |
226 double radians = candidates[i]; | |
227 | |
228 while (radians < minRadians) | |
229 radians += 2.0 * M_PI; | |
230 while (radians > maxRadians) | |
231 radians -= 2.0 * M_PI; | |
232 if (radians < minRadians) | |
233 continue; | |
234 | |
235 TransformationMatrix rotation; | |
236 rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians)); | |
237 box.expandTo(rotation.mapPoint(point)); | |
238 } | |
239 } | |
240 | |
241 bool TransformOperations::blendedBoundsForBox(const FloatBox& box, const Transfo rmOperations& from, const double& minProgress, const double& maxProgress, FloatB ox* bounds) const | |
242 { | |
243 | |
244 int fromSize = from.operations().size(); | |
245 int toSize = operations().size(); | |
246 int size = max(fromSize, toSize); | |
247 | |
248 *bounds = box; | |
249 for (int i = size - 1; i >= 0; i--) { | |
250 RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operati ons()[i] : static_cast<TransformOperation*>(0); | |
Ian Vollick
2014/06/17 15:54:56
Can you use nullptr rather than static_cast<Transf
awoloszyn
2014/06/17 20:06:02
Done.
| |
251 RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i]: static_cast<TransformOperation*>(0); | |
Ian Vollick
2014/06/17 15:54:56
nit: space before colon
awoloszyn
2014/06/17 20:06:02
Done.
| |
252 if (fromOperation && fromOperation->type() == TransformOperation::None) | |
253 fromOperation = static_cast<TransformOperation*>(0); | |
254 | |
255 if (toOperation && toOperation->type() == TransformOperation::None) | |
256 toOperation = static_cast<TransformOperation*>(0); | |
257 | |
258 TransformOperation::OperationType interpolationType = toOperation ? toOp eration->type() : | |
259 fromOperation ? fromOperation->type() : | |
260 TransformOperation::None; | |
261 if (fromOperation && toOperation && !fromOperation->canInterpolateWith(* toOperation.get())) | |
Ian Vollick
2014/06/17 15:54:56
This seems fishy. Why do we not return false here?
awoloszyn
2014/06/17 20:06:02
Thanks for the catch. This should almost certainly
| |
262 continue; | |
263 | |
264 switch (interpolationType) { | |
265 case TransformOperation::Identity: | |
266 bounds->expandTo(box); | |
267 continue; | |
268 case TransformOperation::Translate: | |
269 case TransformOperation::TranslateX: | |
270 case TransformOperation::TranslateY: | |
271 case TransformOperation::TranslateZ: | |
272 case TransformOperation::Translate3D: | |
273 case TransformOperation::Scale: | |
274 case TransformOperation::ScaleX: | |
275 case TransformOperation::ScaleY: | |
276 case TransformOperation::ScaleZ: | |
277 case TransformOperation::Scale3D: | |
278 case TransformOperation::Skew: | |
279 case TransformOperation::SkewX: | |
280 case TransformOperation::SkewY: | |
281 case TransformOperation::Perspective: | |
282 { | |
283 RefPtr<TransformOperation> fromTransform; | |
284 RefPtr<TransformOperation> toTransform; | |
285 if (!toOperation) { | |
286 fromTransform = fromOperation->blend(toOperation.get(), 1-mi nProgress, false); | |
287 toTransform = fromOperation->blend(toOperation.get(), 1-maxP rogress, false); | |
288 } else { | |
289 fromTransform = toOperation->blend(fromOperation.get(), minP rogress, false); | |
290 toTransform = toOperation->blend(fromOperation.get(), maxPro gress, false); | |
291 } | |
292 if (!fromTransform || !toTransform) | |
293 continue; | |
294 TransformationMatrix fromMatrix; | |
295 TransformationMatrix toMatrix; | |
296 fromTransform->apply(fromMatrix, FloatSize()); | |
297 toTransform->apply(toMatrix, FloatSize()); | |
298 FloatBox fromBox = *bounds; | |
299 FloatBox toBox = *bounds; | |
300 fromMatrix.transformBox(fromBox); | |
301 toMatrix.transformBox(toBox); | |
302 *bounds = fromBox; | |
303 bounds->expandTo(toBox); | |
304 continue; | |
305 } | |
306 case TransformOperation::Rotate: // This is also RotateZ | |
307 case TransformOperation::Rotate3D: | |
308 case TransformOperation::RotateX: | |
309 case TransformOperation::RotateY: | |
310 { | |
311 RefPtr<RotateTransformOperation> identityRotation; | |
312 const RotateTransformOperation* fromRotation = nullptr; | |
313 const RotateTransformOperation* toRotation = nullptr; | |
314 if (fromOperation) { | |
315 fromRotation = static_cast<const RotateTransformOperation*>( fromOperation.get()); | |
316 if (fromRotation->hasZeroAxis()) | |
317 fromRotation = nullptr; | |
318 } | |
319 | |
320 if (toOperation) { | |
321 toRotation = static_cast<const RotateTransformOperation*>(to Operation.get()); | |
322 if (toRotation->hasZeroAxis()) | |
323 toRotation = nullptr; | |
324 } | |
325 | |
326 double fromAngle; | |
327 double toAngle; | |
328 FloatPoint3D axis; | |
329 if (!RotateTransformOperation::shareSameAxis(fromRotation, toRot ation, &axis, &fromAngle, &toAngle)) { | |
330 return(false); | |
331 } | |
332 | |
333 if (!fromRotation) { | |
334 identityRotation = RotateTransformOperation::create(axis.x() , axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->ty pe()); | |
335 fromRotation = identityRotation.get(); | |
336 } | |
337 | |
338 if (!toRotation) { | |
339 if (!identityRotation) | |
340 identityRotation = RotateTransformOperation::create(axis .x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation ->type()); | |
341 toRotation = identityRotation.get(); | |
342 } | |
343 | |
344 FloatBox fromBox = *bounds; | |
345 bool first = true; | |
346 for (size_t i = 0; i < 2; ++i) { | |
347 for (size_t j = 0; j < 2; ++j) { | |
348 for (size_t k = 0; k < 2; ++k) { | |
349 FloatBox boundsForArc; | |
350 FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBo x.z()); | |
351 corner += FloatPoint3D(i * fromBox.width(), | |
352 j * fromBox.height(), | |
353 k * fromBox.depth()); | |
354 boundingBoxForArc(corner, *fromRotation, *toRotation , minProgress, maxProgress, boundsForArc); | |
355 if (first) { | |
356 *bounds = boundsForArc; | |
357 first = false; | |
358 } else { | |
359 bounds->expandTo(boundsForArc); | |
360 } | |
361 } | |
362 } | |
363 } | |
364 } | |
365 continue; | |
366 case TransformOperation::None: | |
367 continue; | |
368 case TransformOperation::Matrix: | |
369 case TransformOperation::Matrix3D: | |
370 case TransformOperation::Interpolated: | |
371 return(false); | |
372 } | |
373 } | |
374 | |
375 return true; | |
376 } | |
Ian Vollick
2014/06/17 15:54:56
Nit: extra ws
awoloszyn
2014/06/17 20:06:02
Done.
| |
377 | |
378 | |
112 TransformOperations TransformOperations::add(const TransformOperations& addend) const | 379 TransformOperations TransformOperations::add(const TransformOperations& addend) const |
113 { | 380 { |
114 TransformOperations result; | 381 TransformOperations result; |
115 result.m_operations = operations(); | 382 result.m_operations = operations(); |
116 result.m_operations.appendVector(addend.operations()); | 383 result.m_operations.appendVector(addend.operations()); |
117 return result; | 384 return result; |
118 } | 385 } |
119 | 386 |
120 } // namespace WebCore | 387 } // namespace WebCore |
OLD | NEW |