Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/transform_util.h" | 5 #include "ui/gfx/transform_util.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/logging.h" | |
| 9 #include "ui/gfx/point.h" | 10 #include "ui/gfx/point.h" |
| 11 #include "ui/gfx/point3_f.h" | |
| 12 #include "ui/gfx/rect_f.h" | |
| 10 | 13 |
| 11 namespace gfx { | 14 namespace gfx { |
| 12 | 15 |
| 13 namespace { | 16 namespace { |
| 14 | 17 |
| 18 const float NEAR_INT_ESPILON = 1e-10; | |
|
danakj
2013/09/10 20:29:51
kNearIntEpsilon
I will also note this is at least
avallee
2013/09/10 21:30:13
I'll unify with what's in transform.cc.
Ian Vollick
2013/09/11 12:19:48
This is a bummer, for sure. Is it possible to shar
| |
| 19 | |
| 20 bool NearInteger(float f) { | |
| 21 return (abs(f-round(f)) < NEAR_INT_ESPILON); | |
|
danakj
2013/09/10 20:29:51
std::abs
std::round
avallee
2013/09/10 21:30:13
Done.
Ian Vollick
2013/09/11 12:19:48
I think we want the relative error, not the absolu
| |
| 22 } | |
| 23 | |
| 15 double Length3(double v[3]) { | 24 double Length3(double v[3]) { |
| 16 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | 25 return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
| 17 } | 26 } |
| 18 | 27 |
| 19 void Scale3(double v[3], double scale) { | 28 void Scale3(double v[3], double scale) { |
| 20 for (int i = 0; i < 3; ++i) | 29 for (int i = 0; i < 3; ++i) |
| 21 v[i] *= scale; | 30 v[i] *= scale; |
| 22 } | 31 } |
| 23 | 32 |
| 24 template <int n> | 33 template <int n> |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 257 if (row[2][1] > row[1][2]) | 266 if (row[2][1] > row[1][2]) |
| 258 decomp->quaternion[0] = -decomp->quaternion[0]; | 267 decomp->quaternion[0] = -decomp->quaternion[0]; |
| 259 if (row[0][2] > row[2][0]) | 268 if (row[0][2] > row[2][0]) |
| 260 decomp->quaternion[1] = -decomp->quaternion[1]; | 269 decomp->quaternion[1] = -decomp->quaternion[1]; |
| 261 if (row[1][0] > row[0][1]) | 270 if (row[1][0] > row[0][1]) |
| 262 decomp->quaternion[2] = -decomp->quaternion[2]; | 271 decomp->quaternion[2] = -decomp->quaternion[2]; |
| 263 | 272 |
| 264 return true; | 273 return true; |
| 265 } | 274 } |
| 266 | 275 |
| 267 // Taken from http://www.w3.org/TR/css3-transforms/. | 276 SkMatrix44 QuaternionToRotationMatrix(const double (& quaternion)[4]) { |
| 268 Transform ComposeTransform(const DecomposedTransform& decomp) { | 277 double x = quaternion[0]; |
| 269 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | 278 double y = quaternion[1]; |
| 270 for (int i = 0; i < 4; i++) | 279 double z = quaternion[2]; |
| 271 matrix.setDouble(3, i, decomp.perspective[i]); | 280 double w = quaternion[3]; |
| 272 | |
| 273 matrix.preTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 274 SkDoubleToMScalar(decomp.translate[1]), | |
| 275 SkDoubleToMScalar(decomp.translate[2])); | |
| 276 | |
| 277 double x = decomp.quaternion[0]; | |
| 278 double y = decomp.quaternion[1]; | |
| 279 double z = decomp.quaternion[2]; | |
| 280 double w = decomp.quaternion[3]; | |
| 281 | 281 |
| 282 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); | 282 SkMatrix44 rotation_matrix(SkMatrix44::kUninitialized_Constructor); |
| 283 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), | 283 rotation_matrix.set3x3(1.0 - 2.0 * (y * y + z * z), |
| 284 2.0 * (x * y + z * w), | 284 2.0 * (x * y + z * w), |
| 285 2.0 * (x * z - y * w), | 285 2.0 * (x * z - y * w), |
| 286 2.0 * (x * y - z * w), | 286 2.0 * (x * y - z * w), |
| 287 1.0 - 2.0 * (x * x + z * z), | 287 1.0 - 2.0 * (x * x + z * z), |
| 288 2.0 * (y * z + x * w), | 288 2.0 * (y * z + x * w), |
| 289 2.0 * (x * z + y * w), | 289 2.0 * (x * z + y * w), |
| 290 2.0 * (y * z - x * w), | 290 2.0 * (y * z - x * w), |
| 291 1.0 - 2.0 * (x * x + y * y)); | 291 1.0 - 2.0 * (x * x + y * y)); |
| 292 | 292 |
| 293 matrix.preConcat(rotation_matrix); | 293 return rotation_matrix; |
| 294 } | |
| 294 | 295 |
| 296 void ApplyPerspectiveTranslation(SkMatrix44* matrix, | |
| 297 const DecomposedTransform& decomp) { | |
| 298 for (int i = 0; i < 4; i++) | |
| 299 matrix->setDouble(3, i, decomp.perspective[i]); | |
| 300 | |
| 301 matrix->preTranslate(SkDoubleToMScalar(decomp.translate[0]), | |
| 302 SkDoubleToMScalar(decomp.translate[1]), | |
| 303 SkDoubleToMScalar(decomp.translate[2])); | |
| 304 } | |
| 305 | |
| 306 void ApplySkewScale(SkMatrix44* matrix, | |
| 307 const DecomposedTransform& decomp) { | |
| 295 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); | 308 SkMatrix44 temp(SkMatrix44::kIdentity_Constructor); |
| 296 if (decomp.skew[2]) { | 309 if (decomp.skew[2]) { |
| 297 temp.setDouble(1, 2, decomp.skew[2]); | 310 temp.setDouble(1, 2, decomp.skew[2]); |
| 298 matrix.preConcat(temp); | 311 matrix->preConcat(temp); |
| 299 } | 312 } |
| 300 | 313 |
| 301 if (decomp.skew[1]) { | 314 if (decomp.skew[1]) { |
| 302 temp.setDouble(1, 2, 0); | 315 temp.setDouble(1, 2, 0); |
| 303 temp.setDouble(0, 2, decomp.skew[1]); | 316 temp.setDouble(0, 2, decomp.skew[1]); |
| 304 matrix.preConcat(temp); | 317 matrix->preConcat(temp); |
| 305 } | 318 } |
| 306 | 319 |
| 307 if (decomp.skew[0]) { | 320 if (decomp.skew[0]) { |
| 308 temp.setDouble(0, 2, 0); | 321 temp.setDouble(0, 2, 0); |
| 309 temp.setDouble(0, 1, decomp.skew[0]); | 322 temp.setDouble(0, 1, decomp.skew[0]); |
| 310 matrix.preConcat(temp); | 323 matrix->preConcat(temp); |
| 311 } | 324 } |
| 312 | 325 |
| 313 matrix.preScale(SkDoubleToMScalar(decomp.scale[0]), | 326 matrix->preScale(SkDoubleToMScalar(decomp.scale[0]), |
| 314 SkDoubleToMScalar(decomp.scale[1]), | 327 SkDoubleToMScalar(decomp.scale[1]), |
| 315 SkDoubleToMScalar(decomp.scale[2])); | 328 SkDoubleToMScalar(decomp.scale[2])); |
| 329 } | |
| 330 | |
| 331 // Taken from http://www.w3.org/TR/css3-transforms/. | |
| 332 Transform ComposeTransform(const DecomposedTransform& decomp) { | |
| 333 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 334 ApplyPerspectiveTranslation(&matrix, decomp); | |
| 335 | |
| 336 matrix.preConcat(QuaternionToRotationMatrix(decomp.quaternion)); | |
| 337 | |
| 338 ApplySkewScale(&matrix, decomp); | |
| 316 | 339 |
| 317 Transform to_return; | 340 Transform to_return; |
| 318 to_return.matrix() = matrix; | 341 to_return.matrix() = matrix; |
| 319 return to_return; | 342 return to_return; |
| 320 } | 343 } |
| 321 | 344 |
| 345 Transform ComposeWithRotation(const DecomposedTransform& decomp, | |
| 346 const SkMatrix44& rotation_matrix) { | |
| 347 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); | |
| 348 ApplyPerspectiveTranslation(&matrix, decomp); | |
| 349 matrix.preConcat(rotation_matrix); | |
| 350 | |
| 351 ApplySkewScale(&matrix, decomp); | |
| 352 | |
| 353 Transform to_return; | |
| 354 to_return.matrix() = matrix; | |
| 355 return to_return; | |
| 356 } | |
| 357 | |
| 358 bool CheckTansformPoint(const PointF& point, const Transform& transform_a, | |
| 359 const Transform& transform_b) { | |
| 360 Point3F point_a, point_b; | |
| 361 point_a = point_b = Point3F(point.x(), point.y(), 0.0); | |
|
danakj
2013/09/10 20:29:51
0.f
avallee
2013/09/10 21:30:13
Done.
| |
| 362 | |
| 363 // Can't use TransformRect here since it would give us the axis-aligned | |
| 364 // bounding rect of the 4 points in the initial rectable which is not what we | |
| 365 // want. | |
| 366 bool invertible = true; | |
|
danakj
2013/09/10 20:29:51
true || anything = true. did you mean false?
avallee
2013/09/10 21:30:13
Meant and_equals.
| |
| 367 invertible |= transform_a.TransformPointReverse(&point_a); | |
| 368 invertible |= transform_b.TransformPointReverse(&point_b); | |
| 369 DCHECK(invertible) << "Non-invertible transform, cannot snap."; | |
| 370 | |
| 371 if (!(NearInteger(point_b.x()) && NearInteger(point_b.y()))) { | |
| 372 // Integers should get mapped back into integer points. | |
| 373 return false; | |
| 374 } | |
| 375 | |
| 376 if ((point_b-point_a).Length() > 1.0) { | |
|
danakj
2013/09/10 20:29:51
spaces around operators
avallee
2013/09/10 21:30:13
Done.
| |
| 377 // The changed distance should not be more than 1 pixel. | |
| 378 return false; | |
| 379 } | |
| 380 return true; | |
| 381 } | |
| 382 | |
| 383 bool SnapRotation(DecomposedTransform* out, const DecomposedTransform& in, | |
| 384 const RectF& viewport) { | |
| 385 | |
| 386 // Create snapped rotation. | |
| 387 SkMatrix44 rotation_matrix = QuaternionToRotationMatrix(in.quaternion); | |
| 388 for (int i = 0; i < 3; ++i) { | |
| 389 for (int j = 0; j < 3; ++j) { | |
| 390 SkMScalar value = rotation_matrix.get(i, j); | |
| 391 // Snap values to -1, 0 or 1. | |
| 392 if (value < -0.5) { | |
|
danakj
2013/09/10 20:29:51
treat SkMScalar as a float. so compare to -0.5f. s
avallee
2013/09/10 21:30:13
uOn 2013/09/10 20:29:51, danakj wrote:
| |
| 393 value = -1.0; | |
| 394 } else if (value > 0.5) { | |
| 395 value = 1.0; | |
| 396 } else { | |
| 397 value = 0.0; | |
| 398 } | |
| 399 rotation_matrix.set(i, j, value); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 // Get full tranforms | |
| 404 Transform original = ComposeTransform(in); | |
| 405 Transform snapped = ComposeWithRotation(in, rotation_matrix); | |
| 406 | |
| 407 // Verify that viewport is not moved unnaturally. | |
| 408 | |
| 409 bool snappable = CheckTansformPoint(viewport.origin(), original, snapped) && | |
| 410 CheckTansformPoint(viewport.top_right(), original, snapped) && | |
| 411 CheckTansformPoint(viewport.bottom_left(), original, snapped) && | |
| 412 CheckTansformPoint(viewport.bottom_right(), original, snapped); | |
| 413 if (snappable) { | |
| 414 DecomposeTransform(out, snapped); | |
| 415 } | |
| 416 return snappable; | |
| 417 } | |
| 418 | |
| 322 } // namespace ui | 419 } // namespace ui |
| OLD | NEW |