From 0729fd40a4da4da44a37f82c5d6289f261ea237c Mon Sep 17 00:00:00 2001 From: Recep Aslantas Date: Sun, 31 Mar 2019 18:50:52 +0300 Subject: [PATCH] implement vec2 --- CREDITS | 4 + include/cglm/call/vec2.h | 13 + include/cglm/cglm.h | 1 + include/cglm/common.h | 1 + include/cglm/vec2-ext.h | 189 +++++++++++++++ include/cglm/vec2.h | 499 +++++++++++++++++++++++++++++++++++++++ test/src/test_common.c | 6 + test/src/test_common.h | 3 + test/src/test_main.c | 5 +- test/src/test_tests.h | 4 + test/src/test_vec2.c | 13 + win/cglm.vcxproj | 1 + win/cglm.vcxproj.filters | 5 +- 13 files changed, 742 insertions(+), 2 deletions(-) create mode 100644 include/cglm/call/vec2.h create mode 100644 include/cglm/vec2-ext.h create mode 100644 include/cglm/vec2.h create mode 100644 test/src/test_vec2.c diff --git a/CREDITS b/CREDITS index 7272ddc..a159edf 100644 --- a/CREDITS +++ b/CREDITS @@ -61,3 +61,7 @@ https://forums.khronos.org/showthread.php/10264-Animations-in-1-4-1-release-note https://forums.khronos.org/showthread.php/10644-Animation-Bezier-interpolation https://forums.khronos.org/showthread.php/10387-2D-Tangents-in-Bezier-Splines?p=34164&viewfull=1#post34164 https://forums.khronos.org/showthread.php/10651-Animation-TCB-Spline-Interpolation-in-COLLADA?highlight=bezier + +12. vec2 cross product +http://allenchou.net/2013/07/cross-product-of-2d-vectors/ + diff --git a/include/cglm/call/vec2.h b/include/cglm/call/vec2.h new file mode 100644 index 0000000..acf7f41 --- /dev/null +++ b/include/cglm/call/vec2.h @@ -0,0 +1,13 @@ +// +// vec2.h +// glm +// +// Created by Recep Aslantas on 3/31/19. +// Copyright © 2019 Recep Aslantas. All rights reserved. +// + +#ifndef vec2_h +#define vec2_h + + +#endif /* vec2_h */ diff --git a/include/cglm/cglm.h b/include/cglm/cglm.h index 7c301bf..f4c203f 100644 --- a/include/cglm/cglm.h +++ b/include/cglm/cglm.h @@ -9,6 +9,7 @@ #define cglm_h #include "common.h" +#include "vec2.h" #include "vec3.h" #include "vec4.h" #include "mat4.h" diff --git a/include/cglm/common.h b/include/cglm/common.h index 42d5e67..c7c5eb8 100644 --- a/include/cglm/common.h +++ b/include/cglm/common.h @@ -14,6 +14,7 @@ #include #include #include +#include #if defined(_MSC_VER) # ifdef CGLM_DLL diff --git a/include/cglm/vec2-ext.h b/include/cglm/vec2-ext.h new file mode 100644 index 0000000..0b05aa4 --- /dev/null +++ b/include/cglm/vec2-ext.h @@ -0,0 +1,189 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +/* + Functions: + CGLM_INLINE void glm_vec2_broadcast(float val, vec2 d); + CGLM_INLINE bool glm_vec2_eq(vec2 v, float val); + CGLM_INLINE bool glm_vec2_eq_eps(vec2 v, float val); + CGLM_INLINE bool glm_vec2_eq_all(vec2 v); + CGLM_INLINE bool glm_vec2_eqv(vec2 a, vec2 b); + CGLM_INLINE bool glm_vec2_eqv_eps(vec2 a, vec2 b); + CGLM_INLINE float glm_vec2_max(vec2 v); + CGLM_INLINE float glm_vec2_min(vec2 v); + CGLM_INLINE bool glm_vec2_isnan(vec2 v); + CGLM_INLINE bool glm_vec2_isinf(vec2 v); + CGLM_INLINE bool glm_vec2_isvalid(vec2 v); + CGLM_INLINE void glm_vec2_sign(vec2 v, vec2 dest); + CGLM_INLINE void glm_vec2_sqrt(vec2 v, vec2 dest); + */ + +#ifndef cglm_vec2_ext_h +#define cglm_vec2_ext_h + +#include "common.h" +#include "util.h" + +/*! + * @brief fill a vector with specified value + * + * @param[in] val value + * @param[out] d dest + */ +CGLM_INLINE +void +glm_vec2_broadcast(float val, vec2 d) { + d[0] = d[1] = val; +} + +/*! + * @brief check if vector is equal to value (without epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +CGLM_INLINE +bool +glm_vec2_eq(vec2 v, float val) { + return v[0] == val && v[0] == v[1]; +} + +/*! + * @brief check if vector is equal to value (with epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +CGLM_INLINE +bool +glm_vec2_eq_eps(vec2 v, float val) { + return fabsf(v[0] - val) <= FLT_EPSILON + && fabsf(v[1] - val) <= FLT_EPSILON; +} + +/*! + * @brief check if vectors members are equal (without epsilon) + * + * @param[in] v vector + */ +CGLM_INLINE +bool +glm_vec2_eq_all(vec2 v) { + return v[0] == v[1]; +} + +/*! + * @brief check if vector is equal to another (without epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +CGLM_INLINE +bool +glm_vec2_eqv(vec2 a, vec2 b) { + return a[0] == b[0] && a[1] == b[1]; +} + +/*! + * @brief check if vector is equal to another (with epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +CGLM_INLINE +bool +glm_vec2_eqv_eps(vec2 a, vec2 b) { + return fabsf(a[0] - b[0]) <= FLT_EPSILON + && fabsf(a[1] - b[1]) <= FLT_EPSILON; +} + +/*! + * @brief max value of vector + * + * @param[in] v vector + */ +CGLM_INLINE +float +glm_vec2_max(vec2 v) { + return glm_max(v[0], v[1]); +} + +/*! + * @brief min value of vector + * + * @param[in] v vector + */ +CGLM_INLINE +float +glm_vec2_min(vec2 v) { + return glm_min(v[0], v[1]); +} + +/*! + * @brief check if all items are NaN (not a number) + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +CGLM_INLINE +bool +glm_vec2_isnan(vec2 v) { + return isnan(v[0]) || isnan(v[1]); +} + +/*! + * @brief check if all items are INFINITY + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +CGLM_INLINE +bool +glm_vec2_isinf(vec2 v) { + return isinf(v[0]) || isinf(v[1]); +} + +/*! + * @brief check if all items are valid number + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +CGLM_INLINE +bool +glm_vec2_isvalid(vec2 v) { + return !glm_vec2_isnan(v) && !glm_vec2_isinf(v); +} + +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +CGLM_INLINE +void +glm_vec2_sign(vec2 v, vec2 dest) { + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_sqrt(vec2 v, vec2 dest) { + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); +} + +#endif /* cglm_vec2_ext_h */ diff --git a/include/cglm/vec2.h b/include/cglm/vec2.h new file mode 100644 index 0000000..9bf8749 --- /dev/null +++ b/include/cglm/vec2.h @@ -0,0 +1,499 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +/* + Macros: + GLM_VEC2_ONE_INIT + GLM_VEC2_ZERO_INIT + GLM_VEC2_ONE + GLM_VEC2_ZERO + + Functions: + + */ + +#ifndef cglm_vec2_h +#define cglm_vec2_h + +#include "common.h" +#include "util.h" +#include "vec2-ext.h" + +#define GLM_VEC2_ONE_INIT {1.0f, 1.0f} +#define GLM_VEC2_ZERO_INIT {0.0f, 0.0f} + +#define GLM_VEC2_ONE ((vec2)GLM_VEC2_ONE_INIT) +#define GLM_VEC2_ZERO ((vec2)GLM_VEC2_ZERO_INIT) + +/*! + * @brief init vec2 using another vector + * + * @param[in] v a vector + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2(float * __restrict v, vec2 dest) { + dest[0] = v[0]; + dest[1] = v[1]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2_copy(vec2 a, vec2 dest) { + dest[0] = a[0]; + dest[1] = a[1]; +} + +/*! + * @brief make vector zero + * + * @param[in, out] v vector + */ +CGLM_INLINE +void +glm_vec2_zero(vec2 v) { + v[0] = v[1] = 0.0f; +} + +/*! + * @brief make vector one + * + * @param[in, out] v vector + */ +CGLM_INLINE +void +glm_vec2_one(vec2 v) { + v[0] = v[1] = 1.0f; +} + +/*! + * @brief vec2 dot product + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return dot product + */ +CGLM_INLINE +float +glm_vec2_dot(vec2 a, vec2 b) { + return a[0] * b[0] + a[1] * b[1]; +} + +/*! + * @brief vec2 cross product + * + * REF: http://allenchou.net/2013/07/cross-product-of-2d-vectors/ + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return Z component of cross product + */ +CGLM_INLINE +float +glm_vec2_cross(vec2 a, vec2 b) { + /* just calculate the z-component */ + return a[0] * b[1] - a[1] * b[0]; +} + +/*! + * @brief norm * norm (magnitude) of vec + * + * we can use this func instead of calling norm * norm, because it would call + * sqrtf fuction twice but with this func we can avoid func call, maybe this is + * not good name for this func + * + * @param[in] v vector + * + * @return norm * norm + */ +CGLM_INLINE +float +glm_vec2_norm2(vec2 v) { + return glm_vec2_dot(v, v); +} + +/*! + * @brief norm (magnitude) of vec2 + * + * @param[in] vec vector + * + * @return norm + */ +CGLM_INLINE +float +glm_vec2_norm(vec2 vec) { + return sqrtf(glm_vec2_norm2(vec)); +} + +/*! + * @brief add a vector to b vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_add(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; +} + +/*! + * @brief add scalar to v vector store result in dest (d = v + s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_adds(vec2 v, float s, vec2 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; +} + +/*! + * @brief subtract b vector from a vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_sub(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; +} + +/*! + * @brief subtract scalar from v vector store result in dest (d = v - s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_subs(vec2 v, float s, vec2 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; +} + +/*! + * @brief multiply two vector (component-wise multiplication) + * + * @param a v1 + * @param b v2 + * @param d v3 = (a[0] * b[0], a[1] * b[1], a[2] * b[2]) + */ +CGLM_INLINE +void +glm_vec2_mul(vec2 a, vec2 b, vec2 d) { + d[0] = a[0] * b[0]; + d[1] = a[1] * b[1]; +} + +/*! + * @brief multiply/scale vector with scalar: result = v * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_scale(vec2 v, float s, vec2 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; +} + +/*! + * @brief scale as vector specified: result = unit(v) * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +CGLM_INLINE +void +glm_vec2_scale_as(vec2 v, float s, vec2 dest) { + float norm; + norm = glm_vec2_norm(v); + + if (norm == 0.0f) { + glm_vec2_zero(dest); + return; + } + + glm_vec2_scale(v, s / norm, dest); +} + +/*! + * @brief div vector with another component-wise division: d = a / b + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest result = (a[0]/b[0], a[1]/b[1], a[2]/b[2]) + */ +CGLM_INLINE +void +glm_vec2_div(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; +} + +/*! + * @brief div vector with scalar: d = v / s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest result = (a[0]/s, a[1]/s, a[2]/s) + */ +CGLM_INLINE +void +glm_vec2_divs(vec2 v, float s, vec2 dest) { + dest[0] = v[0] / s; + dest[1] = v[1] / s; +} + +/*! + * @brief add two vectors and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +CGLM_INLINE +void +glm_vec2_addadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] + b[0]; + dest[1] += a[1] + b[1]; +} + +/*! + * @brief sub two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +CGLM_INLINE +void +glm_vec2_subadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] - b[0]; + dest[1] += a[1] - b[1]; +} + +/*! + * @brief mul two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a * b) + */ +CGLM_INLINE +void +glm_vec2_muladd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] * b[0]; + dest[1] += a[1] * b[1]; +} + +/*! + * @brief mul vector with scalar and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector + * @param[in] s scalar + * @param[out] dest dest += (a * b) + */ +CGLM_INLINE +void +glm_vec2_muladds(vec2 a, float s, vec2 dest) { + dest[0] += a[0] * s; + dest[1] += a[1] * s; +} + +/*! + * @brief add max of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += max(a, b) + */ +CGLM_INLINE +void +glm_vec2_maxadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += glm_max(a[0], b[0]); + dest[1] += glm_max(a[1], b[1]); +} + +/*! + * @brief add min of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += min(a, b) + */ +CGLM_INLINE +void +glm_vec2_minadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += glm_min(a[0], b[0]); + dest[1] += glm_min(a[1], b[1]); +} + +/*! + * @brief negate vector components and store result in dest + * + * @param[in] v vector + * @param[out] dest result vector + */ +CGLM_INLINE +void +glm_vec2_negate_to(vec2 v, vec2 dest) { + dest[0] = -v[0]; + dest[1] = -v[1]; +} + +/*! + * @brief negate vector components + * + * @param[in, out] v vector + */ +CGLM_INLINE +void +glm_vec2_negate(vec2 v) { + glm_vec2_negate_to(v, v); +} + +/*! + * @brief normalize vector and store result in same vec + * + * @param[in, out] v vector + */ +CGLM_INLINE +void +glm_vec2_normalize(vec2 v) { + float norm; + + norm = glm_vec2_norm(v); + + if (norm == 0.0f) { + v[0] = v[1] = v[2] = 0.0f; + return; + } + + glm_vec2_scale(v, 1.0f / norm, v); +} + +/*! + * @brief normalize vector to dest + * + * @param[in] vec source + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2_normalize_to(vec2 vec, vec2 dest) { + float norm; + + norm = glm_vec2_norm(vec); + + if (norm == 0.0f) { + glm_vec2_zero(dest); + return; + } + + glm_vec2_scale(vec, 1.0f / norm, dest); +} + +/*! + * @brief max values of vectors + * + * @param[in] v1 vector1 + * @param[in] v2 vector2 + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2_maxv(vec2 v1, vec2 v2, vec2 dest) { + dest[0] = glm_max(v1[0], v2[0]); + dest[1] = glm_max(v1[1], v2[1]); +} + +/*! + * @brief min values of vectors + * + * @param[in] v1 vector1 + * @param[in] v2 vector2 + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2_minv(vec2 v1, vec2 v2, vec2 dest) { + dest[0] = glm_min(v1[0], v2[0]); + dest[1] = glm_min(v1[1], v2[1]); +} + +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +CGLM_INLINE +void +glm_vec2_clamp(vec2 v, float minVal, float maxVal) { + v[0] = glm_clamp(v[0], minVal, maxVal); + v[1] = glm_clamp(v[1], minVal, maxVal); +} + +/*! + * @brief linear interpolation between two vector + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +CGLM_INLINE +void +glm_vec2_lerp(vec2 from, vec2 to, float t, vec2 dest) { + vec2 s, v; + + /* from + s * (to - from) */ + glm_vec2_broadcast(glm_clamp_zo(t), s); + glm_vec2_sub(to, from, v); + glm_vec2_mul(s, v, v); + glm_vec2_add(from, v, dest); +} + +#endif /* cglm_vec2_h */ diff --git a/test/src/test_common.c b/test/src/test_common.c index d41d3cb..3b78569 100644 --- a/test/src/test_common.c +++ b/test/src/test_common.c @@ -112,6 +112,12 @@ test_assert_eqf(float a, float b) { assert_true(fabsf(a - b) <= 0.000009); /* rounding errors */ } +void +test_assert_vec2_eq(vec2 v1, vec2 v2) { + assert_true(fabsf(v1[0] - v2[0]) <= 0.000009); /* rounding errors */ + assert_true(fabsf(v1[1] - v2[1]) <= 0.000009); +} + void test_assert_vec3_eq(vec3 v1, vec3 v2) { assert_true(fabsf(v1[0] - v2[0]) <= 0.000009); /* rounding errors */ diff --git a/test/src/test_common.h b/test/src/test_common.h index 8a16b0f..09c9891 100644 --- a/test/src/test_common.h +++ b/test/src/test_common.h @@ -40,6 +40,9 @@ test_assert_mat4_eq2(mat4 m1, mat4 m2, float eps); void test_assert_mat3_eq(mat3 m1, mat3 m2); +void +test_assert_vec2_eq(vec2 v1, vec2 v2); + void test_assert_vec3_eq(vec3 v1, vec3 v2); diff --git a/test/src/test_main.c b/test/src/test_main.c index 8ce1673..3bdfebb 100644 --- a/test/src/test_main.c +++ b/test/src/test_main.c @@ -41,7 +41,10 @@ main(int argc, const char * argv[]) { cmocka_unit_test(test_affine), /* bezier */ - cmocka_unit_test(test_bezier) + cmocka_unit_test(test_bezier), + + /* vec2 */ + cmocka_unit_test(test_vec2) }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/test/src/test_tests.h b/test/src/test_tests.h index 618cc9f..cf69346 100644 --- a/test/src/test_tests.h +++ b/test/src/test_tests.h @@ -43,4 +43,8 @@ test_affine(void **state); void test_bezier(void **state); +/* vec2 */ +void +test_vec2(void **state); + #endif /* test_tests_h */ diff --git a/test/src/test_vec2.c b/test/src/test_vec2.c new file mode 100644 index 0000000..26941e2 --- /dev/null +++ b/test/src/test_vec2.c @@ -0,0 +1,13 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#include "test_common.h" + +void +test_vec2(void **state) { + +} diff --git a/win/cglm.vcxproj b/win/cglm.vcxproj index 90e58a6..54cbfcf 100644 --- a/win/cglm.vcxproj +++ b/win/cglm.vcxproj @@ -88,6 +88,7 @@ + diff --git a/win/cglm.vcxproj.filters b/win/cglm.vcxproj.filters index dd66ee1..0b57eea 100644 --- a/win/cglm.vcxproj.filters +++ b/win/cglm.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -257,5 +257,8 @@ include\cglm\call + + include\cglm + \ No newline at end of file