Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorOmar Emara <mail@OmarEmara.dev>2022-05-06 12:22:10 +0300
committerOmar Emara <mail@OmarEmara.dev>2022-05-06 12:22:10 +0300
commiteac403b6e1533a96cf6fbefda5065ae1931176a4 (patch)
tree5de0f3ac80b575cc8007e1b91a1107a1fd123adb /source
parent908976b09a733e750dc54c3f329f2a0c70e5b057 (diff)
BLI: Add float3x3
This patch adds a float3x3 class that represents a 3x3 matrix. The class can be used to represent a 2D affine transformation stored in a 3x3 matrix in column major order. The class provides various constructors and processing methods, which utilizes the existing mat3 utilities in BLI. Corresponding tests were also added. This is needed by the upcoming viewport compositor to represent domain transformations. Reviewed By: fclem Differential Revision: https://developer.blender.org/D14687
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenlib/BLI_float3x3.hh192
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/tests/BLI_float3x3_test.cc119
3 files changed, 313 insertions, 0 deletions
diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh
new file mode 100644
index 00000000000..62478556d9b
--- /dev/null
+++ b/source/blender/blenlib/BLI_float3x3.hh
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cmath>
+#include <cstdint>
+
+#include "BLI_assert.h"
+#include "BLI_math_base.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.h"
+
+namespace blender {
+
+struct float3x3 {
+ /* A 3x3 matrix in column major order. */
+ float values[3][3];
+
+ float3x3() = default;
+
+ float3x3(const float *matrix)
+ {
+ memcpy(values, matrix, sizeof(float) * 3 * 3);
+ }
+
+ float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0]))
+ {
+ }
+
+ static float3x3 zero()
+ {
+ float3x3 result;
+ zero_m3(result.values);
+ return result;
+ }
+
+ static float3x3 identity()
+ {
+ float3x3 result;
+ unit_m3(result.values);
+ return result;
+ }
+
+ static float3x3 from_translation(const float2 translation)
+ {
+ float3x3 result = identity();
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ return result;
+ }
+
+ static float3x3 from_rotation(float rotation)
+ {
+ float3x3 result = zero();
+ const float cosine = std::cos(rotation);
+ const float sine = std::sin(rotation);
+ result.values[0][0] = cosine;
+ result.values[0][1] = sine;
+ result.values[1][0] = -sine;
+ result.values[1][1] = cosine;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ static float3x3 from_translation_rotation_scale(const float2 translation,
+ float rotation,
+ const float2 scale)
+ {
+ float3x3 result;
+ const float cosine = std::cos(rotation);
+ const float sine = std::sin(rotation);
+ result.values[0][0] = scale.x * cosine;
+ result.values[0][1] = scale.x * sine;
+ result.values[0][2] = 0.0f;
+ result.values[1][0] = scale.y * -sine;
+ result.values[1][1] = scale.y * cosine;
+ result.values[1][2] = 0.0f;
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ static float3x3 from_normalized_axes(const float2 translation,
+ const float2 horizontal,
+ const float2 vertical)
+ {
+ BLI_ASSERT_UNIT_V2(horizontal);
+ BLI_ASSERT_UNIT_V2(vertical);
+
+ float3x3 result;
+ result.values[0][0] = horizontal.x;
+ result.values[0][1] = horizontal.y;
+ result.values[0][2] = 0.0f;
+ result.values[1][0] = vertical.x;
+ result.values[1][1] = vertical.y;
+ result.values[1][2] = 0.0f;
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ /* Construct a transformation that is pivoted around the given origin point. So for instance,
+ * from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f))
+ * will construct a transformation representing a 90 degree rotation around the point (0, 2). */
+ static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin)
+ {
+ return from_translation(origin) * transformation * from_translation(-origin);
+ }
+
+ operator float *()
+ {
+ return &values[0][0];
+ }
+
+ operator const float *() const
+ {
+ return &values[0][0];
+ }
+
+ float *operator[](const int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 3);
+ return &values[index][0];
+ }
+
+ const float *operator[](const int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 3);
+ return &values[index][0];
+ }
+
+ using c_style_float3x3 = float[3][3];
+ c_style_float3x3 &ptr()
+ {
+ return values;
+ }
+
+ const c_style_float3x3 &ptr() const
+ {
+ return values;
+ }
+
+ friend float3x3 operator*(const float3x3 &a, const float3x3 &b)
+ {
+ float3x3 result;
+ mul_m3_m3m3(result.values, a.values, b.values);
+ return result;
+ }
+
+ void operator*=(const float3x3 &other)
+ {
+ mul_m3_m3_post(values, other.values);
+ }
+
+ friend float2 operator*(const float3x3 &transformation, const float2 &vector)
+ {
+ float2 result;
+ mul_v2_m3v2(result, transformation.values, vector);
+ return result;
+ }
+
+ friend float2 operator*(const float3x3 &transformation, const float (*vector)[2])
+ {
+ return transformation * float2(vector);
+ }
+
+ float3x3 transposed() const
+ {
+ float3x3 result;
+ transpose_m3_m3(result.values, values);
+ return result;
+ }
+
+ float3x3 inverted() const
+ {
+ float3x3 result;
+ invert_m3_m3(result.values, values);
+ return result;
+ }
+
+ friend bool operator==(const float3x3 &a, const float3x3 &b)
+ {
+ return equals_m3m3(a.values, b.values);
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 4e76ae3e855..109230ebfa7 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -199,6 +199,7 @@ set(SRC
BLI_fileops.hh
BLI_fileops_types.h
BLI_filereader.h
+ BLI_float3x3.hh
BLI_float4x4.hh
BLI_fnmatch.h
BLI_function_ref.hh
@@ -431,6 +432,7 @@ if(WITH_GTESTS)
tests/BLI_edgehash_test.cc
tests/BLI_expr_pylike_eval_test.cc
tests/BLI_fileops_test.cc
+ tests/BLI_float3x3_test.cc
tests/BLI_function_ref_test.cc
tests/BLI_generic_array_test.cc
tests/BLI_generic_span_test.cc
diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc
new file mode 100644
index 00000000000..d22993ee69e
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
+namespace blender::tests {
+
+TEST(float3x3, Identity)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::identity();
+ float2 result = transformation * point;
+ EXPECT_EQ(result, point);
+}
+
+TEST(float3x3, Translation)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f));
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 6.0f);
+ EXPECT_FLOAT_EQ(result[1], 5.0f);
+}
+
+TEST(float3x3, Rotation)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_rotation(M_PI_2);
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], -2.0f);
+ EXPECT_FLOAT_EQ(result[1], 1.0f);
+}
+
+TEST(float3x3, TranslationRotationScale)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_translation_rotation_scale(
+ float2(1.0f, 3.0f), M_PI_2, float2(2.0f, 3.0f));
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], -5.0f);
+ EXPECT_FLOAT_EQ(result[1], 5.0f);
+}
+
+TEST(float3x3, NormalizedAxes)
+{
+ float2 point(1.0f, 2.0f);
+
+ /* The horizontal is aligned with (1, 1) and vertical is aligned with (-1, 1), in other words, a
+ * Pi / 4 rotation. */
+ float value = std::sqrt(2.0f) / 2.0f;
+ float3x3 transformation = float3x3::from_normalized_axes(
+ float2(1.0f, 3.0f), float2(value), float2(-value, value));
+ float2 result = transformation * point;
+
+ float3x3 expected_transformation = float3x3::from_translation_rotation_scale(
+ float2(1.0f, 3.0f), M_PI_4, float2(1.0f));
+ float2 expected = expected_transformation * point;
+
+ EXPECT_FLOAT_EQ(result[0], expected[0]);
+ EXPECT_FLOAT_EQ(result[1], expected[1]);
+}
+
+TEST(float3x3, PostTransformationMultiplication)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f));
+ float3x3 rotation = float3x3::from_rotation(M_PI_2);
+ float3x3 transformation = translation * rotation;
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 3.0f);
+ EXPECT_FLOAT_EQ(result[1], 4.0f);
+}
+
+TEST(float3x3, PreTransformationMultiplication)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f));
+ float3x3 rotation = float3x3::from_rotation(M_PI_2);
+ float3x3 transformation = rotation * translation;
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], -5.0f);
+ EXPECT_FLOAT_EQ(result[1], 6.0f);
+}
+
+TEST(float3x3, TransformationMultiplicationAssignment)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f));
+ transformation *= float3x3::from_rotation(M_PI_2);
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 3.0f);
+ EXPECT_FLOAT_EQ(result[1], 4.0f);
+}
+
+TEST(float3x3, Inverted)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_translation_rotation_scale(
+ float2(1.0f, 3.0f), M_PI_4, float2(1.0f));
+ transformation *= transformation.inverted();
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 1.0f);
+ EXPECT_FLOAT_EQ(result[1], 2.0f);
+}
+
+TEST(float3x3, Origin)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 rotation = float3x3::from_rotation(M_PI_2);
+ float3x3 transformation = float3x3::from_origin_transformation(rotation, float2(0.0f, 2.0f));
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 0.0f);
+ EXPECT_FLOAT_EQ(result[1], 3.0f);
+}
+
+} // namespace blender::tests