mirror of
https://github.com/google/pebble.git
synced 2025-07-08 15:50:26 -04:00
80 lines
3.1 KiB
C
80 lines
3.1 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 "system/passert.h"
|
|
#include "util/math.h"
|
|
|
|
static uint16_t prv_triangle_side(uint16_t hypotenuse, uint16_t side) {
|
|
// third side of triangle based on pythagorean theorem
|
|
return integer_sqrt(ABS(((uint32_t)hypotenuse * hypotenuse) - ((uint32_t)side * side)));
|
|
}
|
|
|
|
T_STATIC GRangeHorizontal perimeter_for_circle(GRangeVertical vertical_range, GPoint center,
|
|
int32_t radius) {
|
|
radius = MAX(0, radius);
|
|
int32_t height = 0;
|
|
int32_t width = 0;
|
|
|
|
const int32_t top = center.y - radius;
|
|
const int32_t bottom = center.y + radius;
|
|
|
|
int32_t range_start = vertical_range.origin_y;
|
|
int32_t range_end = vertical_range.origin_y + vertical_range.size_h;
|
|
|
|
// Check if both top and bottom are outside but not surrounding the perimeter
|
|
if ((range_start < top && range_end < top) ||
|
|
(range_start > bottom && range_end > bottom)) {
|
|
return (GRangeHorizontal){0, 0};
|
|
}
|
|
|
|
range_start = CLIP(range_start, top, bottom);
|
|
range_end = CLIP(range_end, top, bottom);
|
|
|
|
// height of triangle from center to range start
|
|
height = ABS(center.y - range_start);
|
|
const int32_t start_width = prv_triangle_side(radius, height);
|
|
|
|
// height of triangle from center to range end
|
|
height = ABS(center.y - range_end);
|
|
const int32_t end_width = prv_triangle_side(radius, height);
|
|
|
|
width = MIN(start_width, end_width);
|
|
|
|
return (GRangeHorizontal){.origin_x = center.x - width, .size_w = width * 2};
|
|
}
|
|
|
|
T_STATIC GRangeHorizontal perimeter_for_display_round(const GPerimeter *perimeter,
|
|
const GSize *ctx_size,
|
|
GRangeVertical vertical_range,
|
|
uint16_t inset) {
|
|
const GRect frame = (GRect) { GPointZero, *ctx_size };
|
|
const GPoint center = grect_center_point(&frame);
|
|
const int32_t radius = grect_shortest_side(frame) / 2 - inset;
|
|
return perimeter_for_circle(vertical_range, center, radius);
|
|
}
|
|
|
|
T_STATIC GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter,
|
|
const GSize *ctx_size,
|
|
GRangeVertical vertical_range,
|
|
uint16_t inset) {
|
|
return (GRangeHorizontal){.origin_x = inset, .size_w = MAX(0, ctx_size->w - 2 * inset)};
|
|
}
|
|
|
|
const GPerimeter * const g_perimeter_for_display = &(const GPerimeter) {
|
|
.callback = PBL_IF_RECT_ELSE(perimeter_for_display_rect, perimeter_for_display_round),
|
|
};
|