pebble/tests/fw/graphics/test_perimeter.c
2025-01-27 11:38:16 -08:00

118 lines
4.5 KiB
C

/*
* 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/perimeter.h"
#include "clar.h"
#include "util/trig.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
#ifndef M_PI
// M_PI doesn't exist in Linux
#define M_PI 3.14159265358979323846 /* pi */
#endif
#define DEG2RAD(a) (M_PI/180*(a))
// Stubs
////////////////////////////////////
#include "stubs_heap.h"
#include "stubs_passert.h"
#include "stubs_logging.h"
#include "stubs_app_state.h"
#include "stubs_compiled_with_legacy2_sdk.h"
#define BETWEEN(val, low, high) \
(val >= low && val <= high) ? true : false
// Tests
////////////////////////////////////
GRangeHorizontal perimeter_for_circle(GRangeVertical vertical_range, GPoint center, int32_t radius);
GRangeHorizontal perimeter_for_display_round(const GPerimeter *perimeter,
const GSize *ctx_size,
GRangeVertical vertical_range,
uint16_t inset);
GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter,
const GSize *ctx_size,
GRangeVertical vertical_range,
uint16_t inset);
void test_perimeter__perimeter_for_circle(void) {
GRect bounds = GRect(0,0,180,180);
GPoint center = grect_center_point(&bounds);
int16_t radius = bounds.size.w / 2;
// test robustness of perimeter_horizontal_range_for_circle
for (int y = 0; y < bounds.size.h; y++) {
GRangeHorizontal h_range = perimeter_for_circle(
(GRangeVertical) {.origin_y = y, .size_h = 0}, center, radius);
// internally we use integer_sqrt, which causes precision loss
// so we need to mirror some of the fixed point truncation here
int16_t height = 90 - y;
int16_t width = sqrt(radius * radius - height * height);
int16_t test_origin = 90 - width;
// Due to integer math and truncation, we need to test +-1
cl_assert(BETWEEN(h_range.origin_x, test_origin - 1, test_origin + 1));
cl_assert(BETWEEN(h_range.size_w, (width - 1) * 2, (width + 1) * 2));
}
}
#define cl_assert_equal_rangehorizontal(r1, r2) \
do { \
cl_assert_equal_i((r1).origin_x, (r2).origin_x); \
cl_assert_equal_i((r1).size_w, (r2).size_w); \
} while(0)
void test_perimeter__perimeter_for_display_rect(void) {
GPerimeter p = {
.callback = perimeter_for_display_rect,
};
const GSize ctx_size = GSize(DISP_COLS, DISP_ROWS);
GRangeVertical r = {.origin_y = 10, .size_h = 10};
GRangeHorizontal expected = (GRangeHorizontal){.origin_x = 0, .size_w = DISP_COLS};
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_rect(&p, &ctx_size, r, 0));
expected = (GRangeHorizontal){.origin_x = 5, .size_w = DISP_COLS - 10};
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_rect(&p, &ctx_size, r, 5));
cl_assert_equal_i(0, perimeter_for_display_rect(&p, &ctx_size, r, 500).size_w);
}
void test_perimeter__perimeter_for_display_round(void) {
GPerimeter p = {
.callback = perimeter_for_display_round,
};
GRangeVertical r = {.origin_y = 10, .size_h = 10};
const GSize ctx_size = GSize(DISP_COLS, DISP_ROWS);
const GRect disp = GRect(0, 0, DISP_COLS, DISP_ROWS);
GRangeHorizontal expected =
perimeter_for_circle(r, grect_center_point(&disp), grect_shortest_side(disp) / 2);
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_round(&p, &ctx_size, r, 0));
expected = perimeter_for_circle(r, grect_center_point(&disp), grect_shortest_side(disp) / 2 - 5);
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_round(&p, &ctx_size, r, 5));
cl_assert_equal_i(0, perimeter_for_display_round(&p, &ctx_size, r, 500).size_w);
}
void test_perimeter__g_perimeter_for_display(void) {
GPerimeterCallback expected = PBL_IF_RECT_ELSE(perimeter_for_display_rect,
perimeter_for_display_round);
cl_assert_equal_p(expected, g_perimeter_for_display->callback);
}