/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" #include "applib/graphics/gtransform.h" #include "util/trig.h" #include "util/math_fixed.h" #include "clar.h" #include #include // Helper Functions //////////////////////////////////// // Stubs //////////////////////////////////// #include "stubs_app_state.h" #include "stubs_applib_resource.h" #include "stubs_compiled_with_legacy2_sdk.h" #include "stubs_heap.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_pebble_tasks.h" #include "stubs_print.h" #include "stubs_resources.h" #include "stubs_serial.h" #include "stubs_syscalls.h" #include "stubs_ui_window.h" #include "stubs_unobstructed_area.h" // Tests //////////////////////////////////// ///////////////////////////////// /// Generic matrix tests ///////////////////////////////// void test_graphics_gtransform_${BIT_DEPTH_NAME}__types_gtransformnumber(void) { GTransform t_c; // matrix to compare against GTransformNumber tn; int32_t test_num; tn = GTransformNumberFromNumber(1); test_num = (int32_t)((float)1 * (1 << FIXED_S32_16_PRECISION)); cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0)); tn = GTransformNumberFromNumber(3.5); test_num = (int32_t)((float)3.5 * (1 << FIXED_S32_16_PRECISION)); cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0)); tn = GTransformNumberFromNumber(-2); test_num = (int32_t)((float)-2 * (1 << FIXED_S32_16_PRECISION)); cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0)); tn = GTransformNumberFromNumber(-3.5); test_num = (int32_t)((float)-3.5 * (1 << FIXED_S32_16_PRECISION)); cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0)); t_c = GTransform(GTransformNumberFromNumber(1), GTransformNumberFromNumber(2), GTransformNumberFromNumber(3), GTransformNumberFromNumber(4), GTransformNumberFromNumber(5), GTransformNumberFromNumber(6)); int32_t test_array[6] = {1 * (1 << FIXED_S32_16_PRECISION), 2 * (1 << FIXED_S32_16_PRECISION), 3 * (1 << FIXED_S32_16_PRECISION), 4 * (1 << FIXED_S32_16_PRECISION), 5 * (1 << FIXED_S32_16_PRECISION), 6 * (1 << FIXED_S32_16_PRECISION)}; cl_assert((memcmp(&t_c, &test_array, sizeof(GTransform)) == 0)); cl_assert(gtransform_is_equal(&t_c, (const GTransform*)&test_array)); t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6); cl_assert(gtransform_is_equal(&t_c, (const GTransform*)&test_array)); // Test to make sure implemented rotation calculation is correct int32_t angle = DEG_TO_TRIGANGLE(45); int32_t cosine = cos_lookup(angle); GTransformNumber num = GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO); GTransformNumber num2 = (GTransformNumber) { .raw_value = (int32_t)(((int64_t)cosine * GTransformNumberOne.raw_value) / TRIG_MAX_RATIO) }; cl_assert(num.raw_value == num2.raw_value); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__types_precise(void) { GPointPrecise pointP = GPointPreciseFromGPoint(GPoint(2, 5)); GPointPrecise pointP_c = GPointPrecise((2 % GPOINT_PRECISE_MAX) << GPOINT_PRECISE_PRECISION, (5 % GPOINT_PRECISE_MAX) << GPOINT_PRECISE_PRECISION); cl_assert(gpointprecise_equal(&pointP, &pointP_c)); GVectorPrecise vectorP = GVectorPreciseFromGVector(GVector(2, 5)); GVectorPrecise vectorP_c = GVectorPrecise((2 % GVECTOR_PRECISE_MAX) << GVECTOR_PRECISE_PRECISION, (5 % GVECTOR_PRECISE_MAX) << GVECTOR_PRECISE_PRECISION); cl_assert(gvectorprecise_equal(&vectorP, &vectorP_c)); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__init(void) { GTransform t; GTransform t_c; // matrix to compare against // Test Identity Matrix t = GTransformIdentity(); t_c = GTransformFromNumbers(1, 0, 0, 1, 0, 0); cl_assert(gtransform_is_equal(&t, &t_c)); cl_assert(gtransform_is_identity(&t)); cl_assert(gtransform_is_identity(&t_c)); // Test Scale Matrix t = GTransformScale(GTransformNumberFromNumber(2), GTransformNumberFromNumber(5)); t_c = GTransformFromNumbers(2, 0, 0, 5, 0, 0); cl_assert(gtransform_is_equal(&t, &t_c)); cl_assert(gtransform_is_only_scale(&t)); cl_assert(gtransform_is_only_scale(&t_c)); t_c = GTransformScaleFromNumber(2, 5); cl_assert(gtransform_is_equal(&t, &t_c)); // Test Translation Matrix t = GTransformTranslation(GTransformNumberFromNumber(2), GTransformNumberFromNumber(5)); t_c = GTransformFromNumbers(1, 0, 0, 1, 2, 5); cl_assert(gtransform_is_equal(&t, &t_c)); cl_assert(gtransform_is_only_translation(&t)); cl_assert(gtransform_is_only_translation(&t_c)); t_c = GTransformTranslationFromNumber(2, 5); cl_assert(gtransform_is_equal(&t, &t_c)); // Test Rotation Matrix int32_t angle = DEG_TO_TRIGANGLE(45); t = GTransformRotation(angle); int32_t cosine = cos_lookup(angle); int32_t sine = sin_lookup(angle); t_c = GTransform(GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO), GTransformNumberFromNumber(-(float)sine / TRIG_MAX_RATIO), GTransformNumberFromNumber((float)sine / TRIG_MAX_RATIO), GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO), GTransformNumberZero, GTransformNumberZero); cl_assert(gtransform_is_equal(&t, &t_c)); angle = DEG_TO_TRIGANGLE(46); cosine = cos_lookup(angle); sine = sin_lookup(angle); t_c = GTransform(GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO), GTransformNumberFromNumber(-(float)sine / TRIG_MAX_RATIO), GTransformNumberFromNumber((float)sine / TRIG_MAX_RATIO), GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO), GTransformNumberZero, GTransformNumberZero); cl_assert(!gtransform_is_equal(&t, &t_c)); t = GTransformRotation(0); // Should return identity if angle == 0 cl_assert(gtransform_is_identity(&t)); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__concat(void) { GTransform t_new; GTransform t1; GTransform t2; GTransform t_c; // matrix to compare against // Test identity concatenation t1 = GTransformIdentity(); t2 = GTransformIdentity(); t_c = GTransformIdentity(); gtransform_concat(&t_new, &t1, &t2); cl_assert(gtransform_is_equal(&t_new, &t_c)); // Test identity concatenation with non-identity t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t2 = GTransformIdentity(); t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6); gtransform_concat(&t_new, &t1, &t2); cl_assert(gtransform_is_equal(&t_new, &t_c)); // Test pointer re-use gtransform_concat(&t2, &t1, &t2); cl_assert(gtransform_is_equal(&t2, &t_c)); // Test non-identity concatenation with identity t1 = GTransformIdentity(); t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6); gtransform_concat(&t_new, &t1, &t2); cl_assert(gtransform_is_equal(&t_new, &t_c)); // Test pointer re-use gtransform_concat(&t1, &t1, &t2); cl_assert(gtransform_is_equal(&t1, &t_c)); // Test concatenation of two non-identity matrices t1 = GTransformFromNumbers(3, 5, 7, 11, 13, 17); t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t_c = GTransformFromNumbers(18, 26, 40, 58, 69, 100); gtransform_concat(&t_new, &t1, &t2); cl_assert(gtransform_is_equal(&t_new, &t_c)); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__scale(void) { GTransform t_new; GTransform t1; GTransform t2; GTransform t_c; // matrix to compare against // Test scaling t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t_c = GTransformFromNumbers(10, 20, 600, 800, 5, 6); gtransform_scale(&t_new, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200)); cl_assert(gtransform_is_equal(&t_new, &t_c)); cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed gtransform_scale_number(&t_new, &t1, 10, 200); cl_assert(gtransform_is_equal(&t_new, &t_c)); cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed // Test pointer re-use gtransform_scale(&t1, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200)); cl_assert(gtransform_is_equal(&t1, &t_c)); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__translation(void) { GTransform t_new; GTransform t1; GTransform t2; GTransform t_c; // matrix to compare against // Test translation t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6); t_c = GTransformFromNumbers(1, 2, 3, 4, 615, 826); gtransform_translate(&t_new, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200)); cl_assert(gtransform_is_equal(&t_new, &t_c)); cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed gtransform_translate_number(&t_new, &t1, 10, 200); cl_assert(gtransform_is_equal(&t_new, &t_c)); cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed // Test pointer re-use gtransform_translate(&t1, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200)); cl_assert(gtransform_is_equal(&t1, &t_c)); } void test_graphics_gtransform_${BIT_DEPTH_NAME}__rotation(void) { GTransform t_new; GTransform t1; GTransform t2; GTransform t_c; // matrix to compare against // Test rotation t1 = GTransformFromNumbers(10, 10, 10, 10, 10, 10); t2 = GTransformFromNumbers(10, 10, 10, 10, 10, 10); // Initialize a, b, c, and d based on the expected result // a = b = 10*cos(45) - 10*sin(45) // c = d = 10*sin(45) + 10*cos(45) t_c = GTransform(GTransformNumberFromNumber(0), GTransformNumberFromNumber(0), (Fixed_S32_16){ .raw_value = (int32_t)(923960) }, (Fixed_S32_16){ .raw_value = (int32_t)(923960) }, GTransformNumberFromNumber(10), GTransformNumberFromNumber(10)); gtransform_rotate(&t_new, &t1, DEG_TO_TRIGANGLE(45)); cl_assert(gtransform_is_equal(&t_new, &t_c)); cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed // Test pointer re-use gtransform_rotate(&t1, &t1, DEG_TO_TRIGANGLE(45)); cl_assert(gtransform_is_equal(&t1, &t_c)); }