Chromium Code Reviews| 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/geometry/FloatBox.h" | |
| 25 #include "platform/transforms/IdentityTransformOperation.h" | 26 #include "platform/transforms/IdentityTransformOperation.h" |
| 26 #include "platform/transforms/InterpolatedTransformOperation.h" | 27 #include "platform/transforms/InterpolatedTransformOperation.h" |
| 28 #include "platform/transforms/RotateTransformOperation.h" | |
| 27 #include <algorithm> | 29 #include <algorithm> |
| 28 | 30 |
| 29 using namespace std; | 31 using namespace std; |
| 30 | 32 |
| 31 namespace WebCore { | 33 namespace WebCore { |
| 32 | 34 |
| 35 const double kAngleEpsilon = 1e-4; | |
| 36 | |
| 37 static inline void blendFloat(double& from, double to, double progress) | |
|
Ian Vollick
2014/06/16 15:23:37
Please switch this to the pre-existing blend fn yo
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 38 { | |
| 39 if (from != to) | |
| 40 from = from + (to - from) * progress; | |
| 41 } | |
| 42 | |
| 33 TransformOperations::TransformOperations(bool makeIdentity) | 43 TransformOperations::TransformOperations(bool makeIdentity) |
| 34 { | 44 { |
| 35 if (makeIdentity) | 45 if (makeIdentity) |
| 36 m_operations.append(IdentityTransformOperation::create()); | 46 m_operations.append(IdentityTransformOperation::create()); |
| 37 } | 47 } |
| 38 | 48 |
| 39 bool TransformOperations::operator==(const TransformOperations& o) const | 49 bool TransformOperations::operator==(const TransformOperations& o) const |
| 40 { | 50 { |
| 41 if (m_operations.size() != o.m_operations.size()) | 51 if (m_operations.size() != o.m_operations.size()) |
| 42 return false; | 52 return false; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 if (from == *this || (!from.size() && !size())) | 112 if (from == *this || (!from.size() && !size())) |
| 103 return *this; | 113 return *this; |
| 104 | 114 |
| 105 // If either list is empty, use blendByMatchingOperations which has special logic for this case. | 115 // If either list is empty, use blendByMatchingOperations which has special logic for this case. |
| 106 if (!from.size() || !size() || from.operationsMatch(*this)) | 116 if (!from.size() || !size() || from.operationsMatch(*this)) |
| 107 return blendByMatchingOperations(from, progress); | 117 return blendByMatchingOperations(from, progress); |
| 108 | 118 |
| 109 return blendByUsingMatrixInterpolation(from, progress); | 119 return blendByUsingMatrixInterpolation(from, progress); |
| 110 } | 120 } |
| 111 | 121 |
| 122 static void findCandidatesInPlane(double px, double py, double nz, double* candi dates, int* numCandidates) | |
| 123 { | |
| 124 // The angle that this point is rotated with respect to the plane nz | |
| 125 double phi = atan2(px, py); | |
| 126 | |
| 127 *numCandidates = 4; | |
| 128 candidates[0] = phi; // The element at 0deg (maximum x) | |
| 129 | |
| 130 for (int i = 1; i < *numCandidates; ++i) | |
| 131 candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg | |
| 132 if (nz < 0.f) { | |
| 133 for (int i = 0; i < *numCandidates; ++i) | |
| 134 candidates[i] *= -1; | |
| 135 } | |
| 136 } | |
| 137 | |
|
Ian Vollick
2014/06/16 15:23:37
A general comment here about how this method works
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 138 static void boundingBoxForArc(const FloatPoint3D& point, const RotateTransformOp eration& fromTransform, const RotateTransformOperation& toTransform, double minP rogress, double maxProgress, FloatBox& box) | |
| 139 { | |
| 140 double candidates[6]; | |
| 141 int numCandidates = 0; | |
| 142 | |
| 143 FloatPoint3D axis(fromTransform.x(), fromTransform.y(), fromTransform.z()); | |
| 144 double fromDegrees = fromTransform.angle(); | |
| 145 double toDegrees = toTransform.angle(); | |
| 146 | |
| 147 if (axis.dot(FloatPoint3D(toTransform.x(), toTransform.y(), toTransform.z()) ) < 0) { | |
|
Ian Vollick
2014/06/16 15:23:37
FloatPoint3D RotationTransformOperation::axis() wo
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 148 toDegrees *= -1; | |
|
Ian Vollick
2014/06/16 15:23:37
Nit: no braces on one-liners in blink. Here and el
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 149 } | |
| 150 | |
| 151 blendFloat(fromDegrees, toTransform.angle(), minProgress); | |
| 152 blendFloat(toDegrees, fromTransform.angle(), 1.0 - maxProgress); | |
| 153 if (fromDegrees > toDegrees) { | |
| 154 std::swap(fromDegrees, toDegrees); | |
| 155 } | |
| 156 | |
| 157 TransformationMatrix fromMatrix; | |
| 158 TransformationMatrix toMatrix; | |
| 159 fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), fromDegrees); | |
| 160 toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), t oDegrees); | |
| 161 | |
| 162 FloatPoint3D fromPoint = fromMatrix.mapPoint(point); | |
| 163 FloatPoint3D toPoint = toMatrix.mapPoint(point); | |
| 164 | |
| 165 if (box.isEmpty()) { | |
| 166 box.setOrigin(fromPoint); | |
| 167 } else { | |
| 168 box.expandTo(fromPoint); | |
| 169 } | |
| 170 | |
| 171 box.expandTo(toPoint); | |
| 172 | |
| 173 switch (fromTransform.type()) { | |
| 174 case TransformOperation::RotateX: | |
| 175 findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidate s, &numCandidates); | |
| 176 break; | |
| 177 case TransformOperation::RotateY: | |
| 178 findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidate s, &numCandidates); | |
| 179 break; | |
| 180 case TransformOperation::RotateZ: | |
| 181 findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidate s, &numCandidates); | |
| 182 break; | |
| 183 default: | |
| 184 { | |
| 185 FloatPoint3D normal = axis; | |
| 186 if (normal.isZero()) | |
| 187 return; | |
| 188 normal.normalize(); | |
| 189 FloatPoint3D origin; | |
| 190 FloatPoint3D toPoint = point - origin; | |
| 191 FloatPoint3D center = origin + normal*toPoint.dot(normal); | |
| 192 FloatPoint3D v1 = point - center; | |
| 193 if (v1.isZero()) | |
| 194 return; | |
| 195 | |
| 196 v1.normalize(); | |
| 197 FloatPoint3D v2 = normal.cross(v1); | |
| 198 // v1 is the basis vector in the direction of the point. | |
| 199 // i.e. with a rotation of 0, v1 is our +x vector. | |
| 200 // v2 is a perpenticular basis vector of our plane (+y). | |
| 201 | |
| 202 // Take the parametric equation of a circle. | |
| 203 // (x = r*cos(t); y = r*sin(t); | |
| 204 // We can treat that as a circle on the plane v1xv2 | |
| 205 // From that we get the parametric equations for a circle on the | |
| 206 // plane in 3d space of | |
| 207 // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx | |
| 208 // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy | |
| 209 // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz | |
| 210 // taking the derivative of (x, y, z) and solving for 0 gives us our | |
| 211 // maximum/minimum x, y, z values | |
| 212 // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 | |
| 213 // tan(t) = v2.x/v1.x | |
| 214 // t = atan2(v2.x, v1.x) + n*M_PI; | |
| 215 | |
| 216 candidates[0] = atan2(v2.x(), v1.x()); | |
| 217 candidates[1] = candidates[0] + M_PI; | |
| 218 candidates[2] = atan2(v2.y(), v1.y()); | |
| 219 candidates[3] = candidates[2] + M_PI; | |
| 220 candidates[4] = atan2(v2.z(), v1.z()); | |
| 221 candidates[5] = candidates[4] + M_PI; | |
| 222 numCandidates = 6; | |
| 223 } | |
| 224 break; | |
| 225 } | |
| 226 | |
|
Ian Vollick
2014/06/16 15:23:37
Pls add comment that explains that this is where w
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 227 double minRadians = deg2rad(fromDegrees); | |
| 228 double maxRadians = deg2rad(toDegrees); | |
| 229 for (int i = 0; i < numCandidates; ++i) { | |
| 230 double radians = candidates[i]; | |
| 231 | |
| 232 while (radians < minRadians) | |
| 233 radians += 2.0 * M_PI; | |
| 234 while (radians > maxRadians) | |
| 235 radians -= 2.0 * M_PI; | |
| 236 if (radians < minRadians) | |
| 237 continue; | |
| 238 | |
| 239 TransformationMatrix rotation; | |
| 240 rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians)); | |
| 241 box.expandTo(rotation.mapPoint(point)); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 static bool isEmptyAxis(const RotateTransformOperation& from) | |
|
Ian Vollick
2014/06/16 15:23:37
nuke.
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 246 { | |
| 247 double lengthSquared = from.x() * from.x() + from.y() * from.y() + from.z() * from.z(); | |
| 248 return (lengthSquared < kAngleEpsilon); | |
| 249 } | |
| 250 | |
| 251 static bool shareSameAxis(const RotateTransformOperation* from, const RotateTran sformOperation* to, double& x, double& y, double& z) | |
|
Ian Vollick
2014/06/16 15:23:37
ditto.
awoloszyn
2014/06/17 14:26:01
Done.
| |
| 252 { | |
| 253 if (!from && !to) { | |
| 254 x = 0.f; | |
| 255 y = 0.f; | |
| 256 z = 1.f; | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 if (from && to) { | |
| 261 if (!from->isSameType(*to)) { | |
| 262 return false; | |
| 263 } | |
| 264 } else { | |
| 265 if (from) { | |
| 266 x = from->x(); | |
| 267 y = from->y(); | |
| 268 z = from->z(); | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 x = to->x(); | |
| 273 y = to->y(); | |
| 274 z = to->z(); | |
| 275 return true; | |
| 276 } | |
| 277 | |
| 278 double fromSquared = from->x() * from->x() + from->y() * from->y() + from->z () * from->z(); | |
| 279 double toSquared = to->x() * to->x() + to->y() * to->y() + to->z() * to->z(); | |
| 280 | |
| 281 if (fromSquared <= kAngleEpsilon || toSquared <= kAngleEpsilon) { | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 double dot = from->x() * to->x() + from->y() * to->y() + from->z() * to->z() ; | |
| 286 double error = std::abs(1 - (dot * dot) / (fromSquared * toSquared)); | |
| 287 | |
| 288 x = from->x(); | |
| 289 y = from->y(); | |
| 290 z = from->z(); | |
| 291 | |
| 292 return (error < kAngleEpsilon); | |
| 293 } | |
| 294 | |
| 295 bool TransformOperations::blendedBoundsForBox(const FloatBox& box, const Transfo rmOperations& from, const double& minProgress, const double& maxProgress, FloatB ox* bounds) const | |
| 296 { | |
| 297 | |
| 298 int fromSize = from.operations().size(); | |
| 299 int toSize = operations().size(); | |
| 300 int size = max(fromSize, toSize); | |
| 301 | |
| 302 *bounds = box; | |
| 303 for (int i = size - 1; i >= 0; i--) { | |
| 304 RefPtr<TransformOperation> fromOperation = (i < fromSize)? from.operatio ns()[i]: static_cast<TransformOperation*>(0); | |
| 305 RefPtr<TransformOperation> toOperation = (i < toSize)? operations()[i]: static_cast<TransformOperation*>(0); | |
| 306 if (fromOperation && fromOperation->type() == TransformOperation::None) { | |
| 307 fromOperation = static_cast<TransformOperation*>(0); | |
| 308 } | |
| 309 if (toOperation && toOperation->type() == TransformOperation::None) { | |
| 310 toOperation = static_cast<TransformOperation*>(0); | |
| 311 } | |
| 312 TransformOperation::OperationType interpolationType = toOperation? toOpe ration->type(): | |
| 313 fromOperation? fromOperation->type(): | |
| 314 TransformOperation::None; | |
| 315 if (fromOperation && toOperation && !fromOperation->isCompatibleType(*to Operation.get())) { | |
| 316 continue; | |
| 317 } | |
| 318 | |
| 319 switch (interpolationType) { | |
| 320 case TransformOperation::Identity: | |
| 321 bounds->expandTo(box); | |
| 322 continue; | |
| 323 case TransformOperation::Translate: | |
| 324 case TransformOperation::TranslateX: | |
| 325 case TransformOperation::TranslateY: | |
| 326 case TransformOperation::TranslateZ: | |
| 327 case TransformOperation::Translate3D: | |
| 328 case TransformOperation::Scale: | |
| 329 case TransformOperation::ScaleX: | |
| 330 case TransformOperation::ScaleY: | |
| 331 case TransformOperation::ScaleZ: | |
| 332 case TransformOperation::Scale3D: | |
| 333 case TransformOperation::Skew: | |
| 334 case TransformOperation::SkewX: | |
| 335 case TransformOperation::SkewY: | |
| 336 case TransformOperation::Perspective: | |
| 337 { | |
| 338 RefPtr<TransformOperation> fromTransform; | |
| 339 RefPtr<TransformOperation> toTransform; | |
| 340 if (!toOperation) { | |
| 341 fromTransform = fromOperation->blend(toOperation.get(), 1-mi nProgress, false); | |
| 342 toTransform = fromOperation->blend(toOperation.get(), 1-maxP rogress, false); | |
| 343 } else { | |
| 344 fromTransform = toOperation->blend(fromOperation.get(), minP rogress, false); | |
| 345 toTransform = toOperation->blend(fromOperation.get(), maxPro gress, false); | |
| 346 } | |
| 347 if (!fromTransform || !toTransform) | |
| 348 continue; | |
| 349 TransformationMatrix fromMatrix; | |
| 350 TransformationMatrix toMatrix; | |
| 351 fromTransform->apply(fromMatrix, FloatSize()); | |
| 352 toTransform->apply(toMatrix, FloatSize()); | |
| 353 FloatBox fromBox = *bounds; | |
| 354 FloatBox toBox = *bounds; | |
| 355 fromMatrix.transformBox(fromBox); | |
| 356 toMatrix.transformBox(toBox); | |
| 357 *bounds = fromBox; | |
| 358 bounds->expandTo(toBox); | |
| 359 continue; | |
| 360 } | |
| 361 case TransformOperation::Rotate: // This is also RotateZ | |
| 362 case TransformOperation::Rotate3D: | |
| 363 case TransformOperation::RotateX: | |
| 364 case TransformOperation::RotateY: | |
| 365 { | |
| 366 RefPtr<RotateTransformOperation> identityRotation; | |
| 367 const RotateTransformOperation* fromRotation = nullptr; | |
| 368 const RotateTransformOperation* toRotation = nullptr; | |
| 369 if (fromOperation) { | |
| 370 fromRotation = static_cast<const RotateTransformOperation*>( fromOperation.get()); | |
| 371 if (isEmptyAxis(*fromRotation)) { | |
| 372 fromRotation = nullptr; | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 if (toOperation) { | |
| 377 toRotation = static_cast<const RotateTransformOperation*>(to Operation.get()); | |
| 378 if (isEmptyAxis(*toRotation)) { | |
| 379 toRotation = nullptr; | |
| 380 } | |
| 381 } | |
| 382 double x = 0; | |
| 383 double y = 0; | |
| 384 double z = 0; | |
| 385 if (!shareSameAxis(fromRotation, toRotation, x, y, z)) { | |
| 386 return(false); | |
| 387 } | |
| 388 | |
| 389 if (!fromRotation) { | |
| 390 identityRotation = RotateTransformOperation::create(x, y, z, 0, fromOperation? fromOperation->type(): toOperation->type()); | |
| 391 fromRotation = identityRotation.get(); | |
| 392 } | |
| 393 | |
| 394 if (!toRotation) { | |
| 395 if (!identityRotation) { | |
| 396 identityRotation = RotateTransformOperation::create(x, y , z, 0, fromOperation? fromOperation->type(): toOperation->type()); | |
| 397 } | |
| 398 toRotation = identityRotation.get(); | |
| 399 } | |
| 400 | |
| 401 FloatBox fromBox = *bounds; | |
| 402 bool first = true; | |
| 403 for (int i = 0; i < 8; i++) { | |
| 404 FloatBox boundsForArc; | |
| 405 FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBox.z()); | |
| 406 corner += FloatPoint3D(i & 1? fromBox.width() :0.f, | |
|
Ian Vollick
2014/06/16 15:23:38
nit: very inconsistent use of spaces around here.
awoloszyn
2014/06/17 14:26:01
I have no problems putting in the triply nested fo
| |
| 407 i & 2? fromBox.height():0.f, | |
| 408 i & 4? fromBox.depth() :0.f); | |
| 409 boundingBoxForArc(corner, *fromRotation, *toRotation, minPro gress, maxProgress, boundsForArc); | |
| 410 if (first) { | |
| 411 *bounds = boundsForArc; | |
| 412 first = false; | |
| 413 } else { | |
| 414 bounds->expandTo(boundsForArc); | |
| 415 } | |
| 416 } | |
| 417 } | |
| 418 continue; | |
| 419 case TransformOperation::None: | |
| 420 continue; | |
| 421 case TransformOperation::Matrix: | |
| 422 case TransformOperation::Matrix3D: | |
| 423 case TransformOperation::Interpolated: | |
| 424 return(false); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 return true; | |
| 429 } | |
| 430 | |
| 431 | |
| 112 TransformOperations TransformOperations::add(const TransformOperations& addend) const | 432 TransformOperations TransformOperations::add(const TransformOperations& addend) const |
| 113 { | 433 { |
| 114 TransformOperations result; | 434 TransformOperations result; |
| 115 result.m_operations = operations(); | 435 result.m_operations = operations(); |
| 116 result.m_operations.appendVector(addend.operations()); | 436 result.m_operations.appendVector(addend.operations()); |
| 117 return result; | 437 return result; |
| 118 } | 438 } |
| 119 | 439 |
| 120 } // namespace WebCore | 440 } // namespace WebCore |
| OLD | NEW |