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 |