blob: 53034181015698c297158df947e9776fc35edc9c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/paper/paper_transform_stack.h"
#include <gtest/gtest.h>
#include "src/ui/lib/escher/geometry/plane_ops.h"
#include "src/ui/lib/escher/geometry/transform.h"
namespace {
using namespace escher;
TEST(PaperTransformStack, StackSize) {
PaperTransformStack stack;
EXPECT_EQ(0U, stack.size());
EXPECT_TRUE(stack.empty());
stack.PushScale(2.f);
stack.PushScale(3.f);
stack.PushScale(4.f);
EXPECT_EQ(3U, stack.size());
EXPECT_FALSE(stack.empty());
stack.Pop();
stack.Pop();
EXPECT_EQ(1U, stack.size());
EXPECT_FALSE(stack.empty());
stack.Pop();
EXPECT_EQ(0U, stack.size());
EXPECT_TRUE(stack.empty());
}
TEST(PaperTransformStack, TransformVector) {
PaperTransformStack stack;
stack.PushTranslation(vec3(3, 4, 5));
stack.PushScale(2);
// The vector is scaled first, then translated.
vec4 transformed = stack.Top().matrix * vec4(10, 10, 10, 1);
EXPECT_EQ(transformed, vec4(23, 24, 25, 1));
// The vector is translated first, then scaled.
stack.Clear();
stack.PushScale(2);
stack.PushTranslation(vec3(3, 4, 5));
transformed = stack.Top().matrix * vec4(10, 10, 10, 1);
EXPECT_EQ(transformed, vec4(26, 28, 30, 1));
}
void TestPushIdentity(PaperTransformStack* stack) {
PaperTransformStack::Item item = stack->Top();
size_t sz = stack->size();
stack->PushIdentity();
EXPECT_EQ(item.matrix, stack->Top().matrix);
EXPECT_EQ(item.clip_planes, stack->Top().clip_planes);
EXPECT_EQ(sz + 1, stack->size());
stack->Pop();
EXPECT_EQ(item.matrix, stack->Top().matrix);
EXPECT_EQ(item.clip_planes, stack->Top().clip_planes);
EXPECT_EQ(sz, stack->size());
}
TEST(PaperTransformStack, Transform) {
std::vector<Transform> transforms{
Transform(vec3(5, 7, 9), vec3(5, 5, 5), glm::angleAxis(2.1f, glm::normalize(vec3(1, 2, -5))),
vec3(2, 1, 2)),
Transform(vec3(-2, 7, 13), vec3(.5f, .5f, .5f),
glm::angleAxis(.9f, glm::normalize(vec3(3, -1, -2))), vec3(2, 1, 2)),
Transform(vec3(-2, -3, -5), vec3(.75f, .75f, .75f),
glm::angleAxis(.4f, glm::normalize(vec3(4, 1, -2))), vec3(2, 1, 2)),
};
PaperTransformStack stack;
stack.PushIdentity();
std::vector<plane3> clip_planes{plane3(glm::normalize(vec3(1, 0, 0)), 5.f),
plane3(glm::normalize(vec3(1, 1, 1)), -5.f)};
mat4 top_matrix;
for (auto& t : transforms) {
// Matrix which will be pushed onto the stack.
mat4 m = static_cast<mat4>(t);
// Generate the matrix that we expect to see on top of the stack after
// pushing |m|.
top_matrix = top_matrix * m;
// Generate the clip-planes that we expect to see on top of the stack after
// pushing |m|.
std::vector<plane3> top_clip_planes = stack.Top().clip_planes;
for (size_t i = 0; i < top_clip_planes.size(); ++i) {
top_clip_planes[i] = TransformPlane(m, top_clip_planes[i]);
}
stack.PushTransform(m);
EXPECT_EQ(stack.Top().clip_planes, top_clip_planes);
EXPECT_EQ(stack.Top().matrix, top_matrix);
// Add some additional clip-planes to the stack. They are not transformed
// until the next transform is pushed.
const size_t num_clip_planes = stack.Top().clip_planes.size();
stack.AddClipPlanes(clip_planes);
ASSERT_EQ(num_clip_planes + 2, stack.Top().clip_planes.size());
EXPECT_EQ(clip_planes[0], stack.Top().clip_planes[num_clip_planes]);
EXPECT_EQ(clip_planes[1], stack.Top().clip_planes[num_clip_planes + 1]);
TestPushIdentity(&stack);
}
}
void TestPlanesSimilar(const plane3& p1, const plane3& p2) {
vec4 diff = vec4(p1.dir(), p1.dist()) - vec4(p2.dir(), p2.dist());
EXPECT_NEAR(0.f, glm::dot(diff, diff), kEpsilon);
}
TEST(PaperTransformStack, Translation) {
PaperTransformStack stack1;
PaperTransformStack stack2;
std::vector<plane3> clip_planes{plane3(glm::normalize(vec3(1, 0, 0)), 5.f),
plane3(glm::normalize(vec3(1, 1, 1)), -5.f)};
stack1.PushIdentity();
stack1.AddClipPlanes(clip_planes);
stack2.PushIdentity();
stack2.AddClipPlanes(clip_planes);
std::vector<vec3> translations{vec3(10, 19, 31), vec3(-1, 3, 17), vec3(-17, -14, 13),
vec3(2, 4, 6)};
for (auto& t : translations) {
// Generate matrix that has the same effect as the translation.
mat4 m = glm::translate(mat4(), t);
stack1.PushTranslation(t);
stack2.PushTransform(m);
EXPECT_EQ(stack1.size(), stack2.size());
EXPECT_EQ(stack1.Top().matrix, stack2.Top().matrix);
TestPlanesSimilar(stack1.Top().clip_planes[0], stack2.Top().clip_planes[0]);
TestPlanesSimilar(stack1.Top().clip_planes[1], stack2.Top().clip_planes[1]);
}
}
TEST(PaperTransformStack, Scale) {
PaperTransformStack stack1;
PaperTransformStack stack2;
std::vector<plane3> clip_planes{plane3(glm::normalize(vec3(1, 0, 0)), 5.f),
plane3(glm::normalize(vec3(1, 1, 1)), -5.f)};
stack1.PushIdentity();
stack1.AddClipPlanes(clip_planes);
stack2.PushIdentity();
stack2.AddClipPlanes(clip_planes);
std::vector<float> scales{2, 5, 7, 9};
for (auto& s : scales) {
// Generate matrix that has the same effect as the translation.
mat4 m = glm::scale(mat4(), vec3(s, s, s));
stack1.PushScale(s);
stack2.PushTransform(m);
EXPECT_EQ(stack1.size(), stack2.size());
EXPECT_EQ(stack1.Top().matrix, stack2.Top().matrix);
TestPlanesSimilar(stack1.Top().clip_planes[0], stack2.Top().clip_planes[0]);
TestPlanesSimilar(stack1.Top().clip_planes[1], stack2.Top().clip_planes[1]);
}
}
} // namespace