Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / platform / transforms / TransformOperations.cpp
blob6b33c742448d8ac0501ceaa6e17f7a6567279ce8
1 /*
2 * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
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
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 #include "config.h"
23 #include "platform/transforms/TransformOperations.h"
25 #include "platform/animation/AnimationUtilities.h"
26 #include "platform/geometry/FloatBox.h"
27 #include "platform/transforms/IdentityTransformOperation.h"
28 #include "platform/transforms/InterpolatedTransformOperation.h"
29 #include "platform/transforms/RotateTransformOperation.h"
30 #include <algorithm>
32 namespace blink {
34 TransformOperations::TransformOperations(bool makeIdentity)
36 if (makeIdentity)
37 m_operations.append(IdentityTransformOperation::create());
40 bool TransformOperations::operator==(const TransformOperations& o) const
42 if (m_operations.size() != o.m_operations.size())
43 return false;
45 unsigned s = m_operations.size();
46 for (unsigned i = 0; i < s; i++) {
47 if (*m_operations[i] != *o.m_operations[i])
48 return false;
51 return true;
54 bool TransformOperations::operationsMatch(const TransformOperations& other) const
56 size_t numOperations = operations().size();
57 // If the sizes of the function lists don't match, the lists don't match
58 if (numOperations != other.operations().size())
59 return false;
61 // If the types of each function are not the same, the lists don't match
62 for (size_t i = 0; i < numOperations; ++i) {
63 if (!operations()[i]->isSameType(*other.operations()[i]))
64 return false;
66 return true;
69 TransformOperations TransformOperations::blendByMatchingOperations(const TransformOperations& from, const double& progress) const
71 TransformOperations result;
73 unsigned fromSize = from.operations().size();
74 unsigned toSize = operations().size();
75 unsigned size = std::max(fromSize, toSize);
76 for (unsigned i = 0; i < size; i++) {
77 RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : 0;
78 RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : 0;
79 RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : nullptr);
80 if (blendedOperation)
81 result.operations().append(blendedOperation);
82 else {
83 RefPtr<TransformOperation> identityOperation = IdentityTransformOperation::create();
84 if (progress > 0.5)
85 result.operations().append(toOperation ? toOperation : identityOperation);
86 else
87 result.operations().append(fromOperation ? fromOperation : identityOperation);
91 return result;
94 TransformOperations TransformOperations::blendByUsingMatrixInterpolation(const TransformOperations& from, double progress) const
96 TransformOperations result;
97 result.operations().append(InterpolatedTransformOperation::create(from, *this, progress));
98 return result;
101 TransformOperations TransformOperations::blend(const TransformOperations& from, double progress) const
103 if (from == *this || (!from.size() && !size()))
104 return *this;
106 // If either list is empty, use blendByMatchingOperations which has special logic for this case.
107 if (!from.size() || !size() || from.operationsMatch(*this))
108 return blendByMatchingOperations(from, progress);
110 return blendByUsingMatrixInterpolation(from, progress);
113 static void findCandidatesInPlane(double px, double py, double nz, double* candidates, int* numCandidates)
115 // The angle that this point is rotated with respect to the plane nz
116 double phi = atan2(px, py);
118 *numCandidates = 4;
119 candidates[0] = phi; // The element at 0deg (maximum x)
121 for (int i = 1; i < *numCandidates; ++i)
122 candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg
123 if (nz < 0.f) {
124 for (int i = 0; i < *numCandidates; ++i)
125 candidates[i] *= -1;
129 // This method returns the bounding box that contains the starting point,
130 // the ending point, and any of the extrema (in each dimension) found across
131 // the circle described by the arc. These are then filtered to points that
132 // actually reside on the arc.
133 static void boundingBoxForArc(const FloatPoint3D& point, const RotateTransformOperation& fromTransform, const RotateTransformOperation& toTransform, double minProgress, double maxProgress, FloatBox& box)
135 double candidates[6];
136 int numCandidates = 0;
138 FloatPoint3D axis(fromTransform.axis());
139 double fromDegrees = fromTransform.angle();
140 double toDegrees = toTransform.angle();
142 if (axis.dot(toTransform.axis()) < 0)
143 toDegrees *= -1;
145 fromDegrees = blend(fromDegrees, toTransform.angle(), minProgress);
146 toDegrees = blend(toDegrees, fromTransform.angle(), 1.0 - maxProgress);
147 if (fromDegrees > toDegrees)
148 std::swap(fromDegrees, toDegrees);
150 TransformationMatrix fromMatrix;
151 TransformationMatrix toMatrix;
152 fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), fromDegrees);
153 toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), toDegrees);
155 FloatPoint3D fromPoint = fromMatrix.mapPoint(point);
156 FloatPoint3D toPoint = toMatrix.mapPoint(point);
158 if (box.isEmpty())
159 box.setOrigin(fromPoint);
160 else
161 box.expandTo(fromPoint);
163 box.expandTo(toPoint);
165 switch (fromTransform.type()) {
166 case TransformOperation::RotateX:
167 findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidates, &numCandidates);
168 break;
169 case TransformOperation::RotateY:
170 findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidates, &numCandidates);
171 break;
172 case TransformOperation::RotateZ:
173 findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidates, &numCandidates);
174 break;
175 default:
177 FloatPoint3D normal = axis;
178 if (normal.isZero())
179 return;
180 normal.normalize();
181 FloatPoint3D origin;
182 FloatPoint3D toPoint = point - origin;
183 FloatPoint3D center = origin + normal * toPoint.dot(normal);
184 FloatPoint3D v1 = point - center;
185 if (v1.isZero())
186 return;
188 v1.normalize();
189 FloatPoint3D v2 = normal.cross(v1);
190 // v1 is the basis vector in the direction of the point.
191 // i.e. with a rotation of 0, v1 is our +x vector.
192 // v2 is a perpenticular basis vector of our plane (+y).
194 // Take the parametric equation of a circle.
195 // (x = r*cos(t); y = r*sin(t);
196 // We can treat that as a circle on the plane v1xv2
197 // From that we get the parametric equations for a circle on the
198 // plane in 3d space of
199 // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx
200 // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy
201 // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz
202 // taking the derivative of (x, y, z) and solving for 0 gives us our
203 // maximum/minimum x, y, z values
204 // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0
205 // tan(t) = v2.x/v1.x
206 // t = atan2(v2.x, v1.x) + n*M_PI;
208 candidates[0] = atan2(v2.x(), v1.x());
209 candidates[1] = candidates[0] + M_PI;
210 candidates[2] = atan2(v2.y(), v1.y());
211 candidates[3] = candidates[2] + M_PI;
212 candidates[4] = atan2(v2.z(), v1.z());
213 candidates[5] = candidates[4] + M_PI;
214 numCandidates = 6;
216 break;
219 double minRadians = deg2rad(fromDegrees);
220 double maxRadians = deg2rad(toDegrees);
221 // Once we have the candidates, we now filter them down to ones that
222 // actually live on the arc, rather than the entire circle.
223 for (int i = 0; i < numCandidates; ++i) {
224 double radians = candidates[i];
226 while (radians < minRadians)
227 radians += 2.0 * M_PI;
228 while (radians > maxRadians)
229 radians -= 2.0 * M_PI;
230 if (radians < minRadians)
231 continue;
233 TransformationMatrix rotation;
234 rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians));
235 box.expandTo(rotation.mapPoint(point));
239 bool TransformOperations::blendedBoundsForBox(const FloatBox& box, const TransformOperations& from, const double& minProgress, const double& maxProgress, FloatBox* bounds) const
242 int fromSize = from.operations().size();
243 int toSize = operations().size();
244 int size = std::max(fromSize, toSize);
246 *bounds = box;
247 for (int i = size - 1; i >= 0; i--) {
248 RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i] : nullptr;
249 RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i] : nullptr;
250 if (fromOperation && fromOperation->type() == TransformOperation::None)
251 fromOperation = nullptr;
253 if (toOperation && toOperation->type() == TransformOperation::None)
254 toOperation = nullptr;
256 TransformOperation::OperationType interpolationType = toOperation ? toOperation->type() :
257 fromOperation ? fromOperation->type() :
258 TransformOperation::None;
259 if (fromOperation && toOperation && !fromOperation->canBlendWith(*toOperation.get()))
260 return false;
262 switch (interpolationType) {
263 case TransformOperation::Identity:
264 bounds->expandTo(box);
265 continue;
266 case TransformOperation::Translate:
267 case TransformOperation::TranslateX:
268 case TransformOperation::TranslateY:
269 case TransformOperation::TranslateZ:
270 case TransformOperation::Translate3D:
271 case TransformOperation::Scale:
272 case TransformOperation::ScaleX:
273 case TransformOperation::ScaleY:
274 case TransformOperation::ScaleZ:
275 case TransformOperation::Scale3D:
276 case TransformOperation::Skew:
277 case TransformOperation::SkewX:
278 case TransformOperation::SkewY:
279 case TransformOperation::Perspective:
281 RefPtr<TransformOperation> fromTransform;
282 RefPtr<TransformOperation> toTransform;
283 if (!toOperation) {
284 fromTransform = fromOperation->blend(toOperation.get(), 1-minProgress, false);
285 toTransform = fromOperation->blend(toOperation.get(), 1-maxProgress, false);
286 } else {
287 fromTransform = toOperation->blend(fromOperation.get(), minProgress, false);
288 toTransform = toOperation->blend(fromOperation.get(), maxProgress, false);
290 if (!fromTransform || !toTransform)
291 continue;
292 TransformationMatrix fromMatrix;
293 TransformationMatrix toMatrix;
294 fromTransform->apply(fromMatrix, FloatSize());
295 toTransform->apply(toMatrix, FloatSize());
296 FloatBox fromBox = *bounds;
297 FloatBox toBox = *bounds;
298 fromMatrix.transformBox(fromBox);
299 toMatrix.transformBox(toBox);
300 *bounds = fromBox;
301 bounds->expandTo(toBox);
302 continue;
304 case TransformOperation::Rotate: // This is also RotateZ
305 case TransformOperation::Rotate3D:
306 case TransformOperation::RotateX:
307 case TransformOperation::RotateY:
309 RefPtr<RotateTransformOperation> identityRotation;
310 const RotateTransformOperation* fromRotation = nullptr;
311 const RotateTransformOperation* toRotation = nullptr;
312 if (fromOperation) {
313 fromRotation = static_cast<const RotateTransformOperation*>(fromOperation.get());
314 if (fromRotation->axis().isZero())
315 fromRotation = nullptr;
318 if (toOperation) {
319 toRotation = static_cast<const RotateTransformOperation*>(toOperation.get());
320 if (toRotation->axis().isZero())
321 toRotation = nullptr;
324 double fromAngle;
325 double toAngle;
326 FloatPoint3D axis;
327 if (!RotateTransformOperation::shareSameAxis(fromRotation, toRotation, &axis, &fromAngle, &toAngle)) {
328 return(false);
331 if (!fromRotation) {
332 identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type());
333 fromRotation = identityRotation.get();
336 if (!toRotation) {
337 if (!identityRotation)
338 identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type());
339 toRotation = identityRotation.get();
342 FloatBox fromBox = *bounds;
343 bool first = true;
344 for (size_t i = 0; i < 2; ++i) {
345 for (size_t j = 0; j < 2; ++j) {
346 for (size_t k = 0; k < 2; ++k) {
347 FloatBox boundsForArc;
348 FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBox.z());
349 corner += FloatPoint3D(i * fromBox.width(), j * fromBox.height(), k * fromBox.depth());
350 boundingBoxForArc(corner, *fromRotation, *toRotation, minProgress, maxProgress, boundsForArc);
351 if (first) {
352 *bounds = boundsForArc;
353 first = false;
354 } else {
355 bounds->expandTo(boundsForArc);
361 continue;
362 case TransformOperation::None:
363 continue;
364 case TransformOperation::Matrix:
365 case TransformOperation::Matrix3D:
366 case TransformOperation::Interpolated:
367 return(false);
371 return true;
374 TransformOperations TransformOperations::add(const TransformOperations& addend) const
376 TransformOperations result;
377 result.m_operations = operations();
378 result.m_operations.appendVector(addend.operations());
379 return result;
382 TransformOperations TransformOperations::zoom(double factor) const
384 TransformOperations result;
385 for (auto& transformOperation : m_operations)
386 result.m_operations.append(transformOperation->zoom(factor));
387 return result;
390 } // namespace blink