1/*
2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26#include "platform/transforms/TransformOperations.h"
27
28#include "platform/geometry/FloatBox.h"
29#include "platform/geometry/FloatBoxTestHelpers.h"
30#include "platform/transforms/IdentityTransformOperation.h"
31#include "platform/transforms/Matrix3DTransformOperation.h"
32#include "platform/transforms/MatrixTransformOperation.h"
33#include "platform/transforms/PerspectiveTransformOperation.h"
34#include "platform/transforms/RotateTransformOperation.h"
35#include "platform/transforms/ScaleTransformOperation.h"
36#include "platform/transforms/SkewTransformOperation.h"
37#include "platform/transforms/TranslateTransformOperation.h"
38
39#include <gtest/gtest.h>
40
41using namespace blink;
42namespace {
43
44static const TransformOperations identityOperations;
45
46static void EmpiricallyTestBounds(const TransformOperations& from,
47    const TransformOperations& to,
48    const double& minProgress,
49    const double& maxProgress)
50{
51    FloatBox box(200, 500, 100, 100, 300, 200);
52    FloatBox bounds;
53
54    EXPECT_TRUE(to.blendedBoundsForBox(box, from, minProgress, maxProgress, &bounds));
55    bool firstTime = true;
56
57    FloatBox empiricalBounds;
58    static const size_t numSteps = 10;
59    for (size_t step = 0; step < numSteps; ++step) {
60        float t = step / (numSteps - 1);
61        t = minProgress + (maxProgress - minProgress) * t;
62        TransformOperations operations = from.blend(to, t);
63        TransformationMatrix matrix;
64        operations.apply(FloatSize(0, 0), matrix);
65        FloatBox transformed = box;
66        matrix.transformBox(transformed);
67
68        if (firstTime)
69            empiricalBounds = transformed;
70        else
71            empiricalBounds.unionBounds(transformed);
72        firstTime = false;
73    }
74
75    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertContains, bounds, empiricalBounds);
76}
77
78TEST(TransformOperationsTest, AbsoluteAnimatedTranslatedBoundsTest)
79{
80    TransformOperations fromOps;
81    TransformOperations toOps;
82    fromOps.operations().append(TranslateTransformOperation::create(Length(-30, blink::Fixed), Length(20, blink::Fixed), 15, TransformOperation::Translate3D));
83    toOps.operations().append(TranslateTransformOperation::create(Length(10, blink::Fixed), Length(10, blink::Fixed), 200, TransformOperation::Translate3D));
84    FloatBox box(0, 0, 0, 10, 10, 10);
85    FloatBox bounds;
86
87    EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds));
88    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 20, 20, 210), bounds);
89
90    EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds));
91    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 20, 20, 210), bounds);
92
93    EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
94    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-30, 0, 0, 40, 30, 25), bounds);
95
96    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
97    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-30, 10, 15, 50, 20, 195), bounds);
98
99    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds));
100    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-50, 7.5, -77.5, 80, 27.5, 333.75), bounds);
101}
102
103TEST(TransformOperationsTest, EmpiricalAnimatedTranslatedBoundsTest)
104{
105    float testTransforms[][2][3] = {
106        { { 0, 0, 0 }, { 10, 10, 0 } } ,
107        { { -100, 202.5, -32.6 }, { 43.2, 56.1, 89.75 } },
108        { { 43.2, 56.1, 89.75 }, { -100, 202.5, -32.6 } }
109    };
110
111    // All progressions for animations start and end at 0, 1 respectively,
112    // we can go outside of these bounds, but will always at least contain
113    // [0,1].
114    float progress[][2] = {
115        { 0, 1 },
116        { -.25, 1.25 }
117    };
118
119    for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) {
120        for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
121            TransformOperations fromOps;
122            TransformOperations toOps;
123            fromOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][0][0], blink::Fixed), Length(testTransforms[i][0][1], blink::Fixed), testTransforms[i][0][2], TransformOperation::Translate3D));
124            toOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][1][0], blink::Fixed), Length(testTransforms[i][1][1], blink::Fixed), testTransforms[i][1][2], TransformOperation::Translate3D));
125            EmpiricallyTestBounds(fromOps, toOps, 0, 1);
126        }
127    }
128}
129
130TEST(TransformOperationsTest, AbsoluteAnimatedScaleBoundsTest)
131{
132    TransformOperations fromOps;
133    TransformOperations toOps;
134    fromOps.operations().append(ScaleTransformOperation::create(4, -3, TransformOperation::Scale));
135    toOps.operations().append(ScaleTransformOperation::create(5, 2, TransformOperation::Scale));
136
137    FloatBox box(0, 0, 0, 10, 10, 10);
138    FloatBox bounds;
139
140    EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds));
141    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 50, 20, 10), bounds);
142
143    EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds));
144    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 50, 20, 10), bounds);
145
146    EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
147    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -30, 0, 40, 40, 10), bounds);
148
149    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
150    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -30, 0, 50, 50, 10), bounds);
151
152    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds));
153    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -55, 0, 52.5, 87.5, 10), bounds);
154}
155
156TEST(TransformOperationsTest, EmpiricalAnimatedScaleBoundsTest)
157{
158
159    float testTransforms[][2][3] = {
160        { { 1, 1, 1 }, { 10, 10, -32}},
161        { { 1, 2, 5 }, { -1, -2, -4}},
162        { { 0, 0, 0 }, { 1, 2, 3}},
163        { { 0, 0, 0 }, { 0, 0, 0}}
164    };
165
166    // All progressions for animations start and end at 0, 1 respectively,
167    // we can go outside of these bounds, but will always at least contain
168    // [0,1].
169    float progress[][2] = {
170        { 0, 1 },
171        { -.25f, 1.25f }
172    };
173
174    for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) {
175        for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
176            TransformOperations fromOps;
177            TransformOperations toOps;
178            fromOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][0][0], blink::Fixed), Length(testTransforms[i][0][1], blink::Fixed), testTransforms[i][0][2], TransformOperation::Translate3D));
179            toOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][1][0], blink::Fixed), Length(testTransforms[i][1][1], blink::Fixed), testTransforms[i][1][2], TransformOperation::Translate3D));
180            EmpiricallyTestBounds(fromOps, toOps, 0, 1);
181        }
182    }
183}
184
185TEST(TransformOperationsTest, AbsoluteAnimatedRotationBounds)
186{
187    TransformOperations fromOps;
188    TransformOperations toOps;
189    fromOps.operations().append(RotateTransformOperation::create(0, TransformOperation::Rotate));
190    toOps.operations().append(RotateTransformOperation::create(360, TransformOperation::Rotate));
191    float sqrt2 = sqrt(2.0f);
192    FloatBox box(-sqrt2, -sqrt2, 0, sqrt2, sqrt2, 0);
193    FloatBox bounds;
194
195    // Since we're rotating 360 degrees, any box with dimensions between 0 and
196    // 2 * sqrt(2) should give the same result.
197    float sizes[] = { 0, 0.1f, sqrt2, 2*sqrt2 };
198    toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds);
199    for (size_t i = 0; i < WTF_ARRAY_LENGTH(sizes); ++i) {
200        box.setSize(FloatPoint3D(sizes[i], sizes[i], 0));
201
202        EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
203        EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-2, -2, 0, 4, 4, 0), bounds);
204    }
205}
206
207TEST(TransformOperationsTest, AbsoluteAnimatedExtremeRotationBounds)
208{
209    // If the normal is off-plane, we can have up to 6 exrema (min/max in each
210    // dimension between) the endpoints of the arg. This makes sure we are
211    // catching all 6.
212    TransformOperations fromOps;
213    TransformOperations toOps;
214    fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D));
215    toOps.operations().append(RotateTransformOperation::create(1, 1, 1, 390,  TransformOperation::Rotate3D));
216
217    FloatBox box(1, 0, 0, 0, 0, 0);
218    FloatBox bounds;
219    float min = -1 / 3.0f;
220    float max = 1;
221    float size = max - min;
222    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
223    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(min, min, min, size, size, size), bounds);
224}
225
226TEST(TransformOperationsTest, AbsoluteAnimatedAxisRotationBounds)
227{
228    // We can handle rotations about a single axis. If the axes are different,
229    // we revert to matrix interpolation for which inflated bounds cannot be
230    // computed.
231    TransformOperations fromOps;
232    TransformOperations toSame;
233    TransformOperations toOpposite;
234    TransformOperations toDifferent;
235    fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D));
236    toSame.operations().append(RotateTransformOperation::create(1, 1, 1, 390,  TransformOperation::Rotate3D));
237    toOpposite.operations().append(RotateTransformOperation::create(-1, -1, -1, 390,  TransformOperation::Rotate3D));
238    toDifferent.operations().append(RotateTransformOperation::create(1, 3, 1, 390,  TransformOperation::Rotate3D));
239
240    FloatBox box(1, 0, 0, 0, 0, 0);
241    FloatBox bounds;
242    EXPECT_TRUE(toSame.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
243    EXPECT_TRUE(toOpposite.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
244    EXPECT_FALSE(toDifferent.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
245}
246
247TEST(TransformOperationsTest, AbsoluteAnimatedOnAxisRotationBounds)
248{
249    // If we rotate a point that is on the axis of rotation, the box should not
250    // change at all.
251    TransformOperations fromOps;
252    TransformOperations toOps;
253    fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D));
254    toOps.operations().append(RotateTransformOperation::create(1, 1, 1, 390,  TransformOperation::Rotate3D));
255
256    FloatBox box(1, 1, 1, 0, 0, 0);
257    FloatBox bounds;
258
259    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
260    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, box, bounds);
261}
262
263// This would have been best as anonymous structs, but |WTF_ARRAY_LENGTH|
264// does not get along with anonymous structs once we support C++11
265// WTF_ARRAY_LENGTH will automatically support anonymous structs.
266
267struct ProblematicAxisTest {
268    double x;
269    double y;
270    double z;
271    FloatBox expected;
272};
273
274TEST(TransformOperationsTest, AbsoluteAnimatedProblematicAxisRotationBounds)
275{
276    // Zeros in the components of the axis osf rotation turned out to be tricky to
277    // deal with in practice. This function tests some potentially problematic
278    // axes to ensure sane behavior.
279
280    // Some common values used in the expected boxes.
281    float dim1 = 0.292893f;
282    float dim2 = sqrt(2.0f);
283    float dim3 = 2 * dim2;
284
285    ProblematicAxisTest tests[] = {
286        { 0, 0, 0, FloatBox(1, 1, 1, 0, 0, 0) },
287        { 1, 0, 0, FloatBox(1, -dim2, -dim2, 0, dim3, dim3) },
288        { 0, 1, 0, FloatBox(-dim2, 1, -dim2, dim3, 0, dim3) },
289        { 0, 0, 1, FloatBox(-dim2, -dim2, 1, dim3, dim3, 0) },
290        { 1, 1, 0, FloatBox(dim1, dim1, -1, dim2, dim2, 2) },
291        { 0, 1, 1, FloatBox(-1, dim1, dim1, 2, dim2, dim2) },
292        { 1, 0, 1, FloatBox(dim1, -1, dim1, dim2, 2, dim2) }
293    };
294
295    for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
296        float x = tests[i].x;
297        float y = tests[i].y;
298        float z = tests[i].z;
299        TransformOperations fromOps;
300        fromOps.operations().append(RotateTransformOperation::create(x, y, z, 0, TransformOperation::Rotate3D));
301        TransformOperations toOps;
302        toOps.operations().append(RotateTransformOperation::create(x, y, z, 360, TransformOperation::Rotate3D));
303        FloatBox box(1, 1, 1, 0, 0, 0);
304        FloatBox bounds;
305
306        EXPECT_TRUE(toOps.blendedBoundsForBox(
307            box, fromOps, 0, 1, &bounds));
308        EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, tests[i].expected, bounds);
309    }
310}
311
312
313TEST(TransformOperationsTest, BlendedBoundsForRotationEmpiricalTests)
314{
315    float axes[][3] = {
316        { 1, 1, 1 },
317        { -1, -1, -1 },
318        { -1, 2, 3 },
319        { 1, -2, 3 },
320        { 0, 0, 0 },
321        { 1, 0, 0 },
322        { 0, 1, 0 },
323        { 0, 0, 1 },
324        { 1, 1, 0 },
325        { 0, 1, 1 },
326        { 1, 0, 1 },
327        { -1, 0, 0 },
328        { 0, -1, 0 },
329        { 0, 0, -1 },
330        { -1, -1, 0 },
331        { 0, -1, -1 },
332        { -1, 0, -1 }
333    };
334
335    float angles[][2] = {
336        { 5, 100 },
337        { 10, 5 },
338        { 0, 360 },
339        { 20, 180 },
340        { -20, -180 },
341        { 180, -220 },
342        { 220, 320 },
343        { 1020, 1120 },
344        {-3200, 120 },
345        {-9000, -9050 }
346    };
347
348    float progress[][2] = {
349        { 0, 1 },
350        { -0.25f, 1.25f }
351    };
352
353    for (size_t i = 0; i < WTF_ARRAY_LENGTH(axes); ++i) {
354        for (size_t j = 0; j < WTF_ARRAY_LENGTH(angles); ++j) {
355            for (size_t k = 0; k < WTF_ARRAY_LENGTH(progress); ++k) {
356                float x = axes[i][0];
357                float y = axes[i][1];
358                float z = axes[i][2];
359
360                TransformOperations fromOps;
361                TransformOperations toOps;
362
363                fromOps.operations().append(RotateTransformOperation::create(x, y, z, angles[j][0], TransformOperation::Rotate3D));
364                toOps.operations().append(RotateTransformOperation::create(x, y, z, angles[j][1], TransformOperation::Rotate3D));
365                EmpiricallyTestBounds(fromOps, toOps, progress[k][0], progress[k][1]);
366            }
367        }
368    }
369}
370
371TEST(TransformOperationsTest, AbsoluteAnimatedPerspectiveBoundsTest)
372{
373    TransformOperations fromOps;
374    TransformOperations toOps;
375    fromOps.operations().append(PerspectiveTransformOperation::create(10));
376    toOps.operations().append(PerspectiveTransformOperation::create(30));
377    FloatBox box(0, 0, 0, 10, 10, 10);
378    FloatBox bounds;
379    toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds);
380    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 15, 15, 15), bounds);
381
382    fromOps.blendedBoundsForBox(box, toOps, -0.25, 1.25, &bounds);
383    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-40, -40, -40, 52, 52, 52), bounds);
384}
385
386TEST(TransformOperationsTest, EmpiricalAnimatedPerspectiveBoundsTest)
387{
388    float depths[][2] = {
389        { 600, 400 },
390        { 800, 1000 },
391        { 800, std::numeric_limits<float>::infinity() }
392    };
393
394    float progress[][2] = {
395        { 0, 1 },
396        {-0.1f, 1.1f }
397    };
398
399    for (size_t i = 0; i < WTF_ARRAY_LENGTH(depths); ++i) {
400        for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
401            TransformOperations fromOps;
402            TransformOperations toOps;
403
404            fromOps.operations().append(PerspectiveTransformOperation::create(depths[i][0]));
405            toOps.operations().append(PerspectiveTransformOperation::create(depths[i][1]));
406
407            EmpiricallyTestBounds(fromOps, toOps, progress[j][0], progress[j][1]);
408        }
409    }
410}
411
412
413TEST(TransformOperationsTest, AnimatedSkewBoundsTest)
414{
415    TransformOperations fromOps;
416    TransformOperations toOps;
417    fromOps.operations().append(SkewTransformOperation::create(-45, 0, TransformOperation::Skew));
418    toOps.operations().append(SkewTransformOperation::create(0, 45, TransformOperation::Skew));
419    FloatBox box(0, 0, 0, 10, 10, 10);
420    FloatBox bounds;
421
422    toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds);
423    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 10, 20, 10), bounds);
424
425    identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds);
426    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 10, 10), bounds);
427
428    toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds);
429    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 20, 10), bounds);
430
431    fromOps.blendedBoundsForBox(box, toOps, 0, 1, &bounds);
432    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 20, 10), bounds);
433}
434
435TEST(TransformOperationsTest, NonCommutativeRotations)
436{
437    TransformOperations fromOps;
438    fromOps.operations().append(RotateTransformOperation::create(1, 0, 0, 0, TransformOperation::Rotate3D));
439    fromOps.operations().append(RotateTransformOperation::create(0, 1, 0, 0, TransformOperation::Rotate3D));
440    TransformOperations toOps;
441    toOps.operations().append(RotateTransformOperation::create(1, 0, 0, 45, TransformOperation::Rotate3D));
442    toOps.operations().append(RotateTransformOperation::create(0, 1, 0, 135, TransformOperation::Rotate3D));
443
444    FloatBox box(0, 0, 0, 1, 1, 1);
445    FloatBox bounds;
446
447    double minProgress = 0;
448    double maxProgress = 1;
449    EXPECT_TRUE(toOps.blendedBoundsForBox(
450        box, fromOps, minProgress, maxProgress, &bounds));
451
452    TransformOperations operations = toOps.blend(fromOps, maxProgress);
453    TransformationMatrix blendedTransform;
454    operations.apply(FloatSize(0, 0), blendedTransform);
455
456    FloatPoint3D blendedPoint(0.9f, 0.9f, 0);
457    blendedPoint = blendedTransform.mapPoint(blendedPoint);
458    FloatBox expandedBounds = bounds;
459    expandedBounds.expandTo(blendedPoint);
460
461    ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, bounds, expandedBounds);
462}
463
464TEST(TransformOperationsTest, AbsoluteSequenceBoundsTest)
465{
466    TransformOperations fromOps;
467    TransformOperations toOps;
468
469    fromOps.operations().append(TranslateTransformOperation::create(Length(1, Fixed), Length(-5, Fixed), 1, TransformOperation::Translate3D));
470    fromOps.operations().append(ScaleTransformOperation::create(-1, 2, 3, TransformOperation::Scale3D));
471    fromOps.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(4, Fixed), -1, TransformOperation::Translate3D));
472
473    toOps.operations().append(TranslateTransformOperation::create(Length(13, Fixed), Length(-1, Fixed), 5, TransformOperation::Translate3D));
474    toOps.operations().append(ScaleTransformOperation::create(-3, -2, 5, TransformOperation::Scale3D));
475    toOps.operations().append(TranslateTransformOperation::create(Length(6, Fixed), Length(-2, Fixed), 3, TransformOperation::Translate3D));
476
477    FloatBox box(1, 2, 3, 4, 4, 4);
478    FloatBox bounds;
479
480    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.5, &bounds));
481    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-57, -59, -1, 76, 112, 80), bounds);
482
483    EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
484    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-32, -25, 7, 42, 44, 48), bounds);
485
486    EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds));
487    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-33, -13, 3, 57, 19, 52), bounds);
488
489    EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds));
490    EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-7, -3, 2, 15, 23, 20), bounds);
491}
492
493} // namespace
494