From 38af0bcdeacb61cbb744843df36211bf17bd4de1 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Thu, 6 Aug 2026 23:36:41 -0400 Subject: [PATCH] Initial triangle generation --- src/c/main.c | 8 +++- src/c/triangles.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++ src/c/triangles.h | 20 +++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/c/triangles.c create mode 100644 src/c/triangles.h diff --git a/src/c/main.c b/src/c/main.c index 41d6357..5d6f3a0 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -1,4 +1,4 @@ -#include "src/resource_ids.auto.h" +#include "triangles.h" #include #include @@ -36,6 +36,7 @@ static TextLayer *s_drop_txt_under_layer; static Layer *s_division_layer; static Layer *s_alarm_highlight_layer; static MenuLayer *s_drop_menu_layer; +static TriangleLayer *s_triangle_layer; // fonts //// setter @@ -546,11 +547,16 @@ static void alarm_window_load() { layer_set_update_proc(s_alarm_highlight_layer, alarm_highlight_layer_update_proc); layer_add_child(window_layer, s_alarm_highlight_layer); + // triangle + s_triangle_layer = triangle_layer_create(GRect(0, s_top_bar_grect.size.h + 1, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT - (s_top_bar_grect.size.h * 2))); + layer_add_child(window_layer, s_triangle_layer->layer); + schedule_fallback_alarm(); tick_timer_service_subscribe(SECOND_UNIT, alarm_seconds_handler); } static void alarm_window_unload() { + triangle_layer_destroy(s_triangle_layer); layer_destroy(s_alarm_highlight_layer); layer_destroy(s_division_layer); text_layer_destroy(s_parenthesis_txt_layer); diff --git a/src/c/triangles.c b/src/c/triangles.c new file mode 100644 index 0000000..197a96d --- /dev/null +++ b/src/c/triangles.c @@ -0,0 +1,108 @@ +#include "triangles.h" +#include + +static void triangle_layer_update_proc(Layer *layer, GContext *ctx) { + // retrieve the TriangleLayer pointer stored in the layer's data slot + TriangleLayer *tri = *(TriangleLayer **)layer_get_data(layer); + if (!tri) + return; + + GRect bounds = layer_get_bounds(layer); + graphics_context_set_stroke_color(ctx, tri->color); + + // pick the longest side as the horizontal base + uint16_t base_side, left_side; + uint8_t left_angle; + + if (tri->side_a >= tri->side_b && tri->side_a >= tri->side_c) { + base_side = tri->side_a; + left_side = tri->side_c; // C → A + left_angle = tri->angle_B; // at vertex B + } else if (tri->side_b >= tri->side_a && tri->side_b >= tri->side_c) { + base_side = tri->side_b; + left_side = tri->side_c; // C → B + left_angle = tri->angle_A; // at vertex A + } else { + base_side = tri->side_c; + left_side = tri->side_b; // B → C + left_angle = tri->angle_A; // at vertex A + } + + // draw the base, centered horizontally + // TODO center whole triangle vertically + uint16_t padded_base = (base_side % 2 == 0) ? base_side : base_side + 1; + uint16_t margin = (bounds.size.w / 2) - (padded_base / 2); + uint16_t start_x = margin + 1; + uint16_t end_x = margin + base_side; + uint16_t base_y = bounds.size.h - 5; // small bottom margin + graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(end_x, base_y)); + + // determine height and apex coordinates + int32_t sin_left = sin_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360); + int32_t cos_left = cos_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360); + uint16_t height = (uint16_t)(((uint32_t)left_side * (uint32_t)sin_left) / TRIG_MAX_RATIO); + uint16_t apex_offset = (uint16_t)(((uint32_t)left_side * (uint32_t)cos_left) / TRIG_MAX_RATIO); + uint16_t apex_x = start_x + apex_offset; + uint16_t apex_y = base_y - height; + + // draw the remaining sides + graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(apex_x, apex_y)); + graphics_draw_line(ctx, GPoint(end_x, base_y), GPoint(apex_x, apex_y)); +} + +TriangleLayer *triangle_layer_create(GRect frame) { + TriangleLayer *tri = malloc(sizeof(TriangleLayer)); + if (!tri) + return NULL; + + // determine angles + uint8_t r1, r2; + do { + r1 = 1 + (rand() % 179); // 1–178 + r2 = 1 + (rand() % 179); // 1–178 + if (r1 > r2) { + uint8_t tmp = r1; + r1 = r2; + r2 = tmp; + } + } while (r1 == 60 && r2 == 120); // ensure never equilateral + + tri->angle_A = r1; + tri->angle_B = r2 - r1; + tri->angle_C = 180 - r2; + + // determine side lengths (law of sines) + int32_t sin_A = sin_lookup(((int32_t)tri->angle_A * TRIG_MAX_ANGLE) / 360); + int32_t sin_B = sin_lookup(((int32_t)tri->angle_B * TRIG_MAX_ANGLE) / 360); + int32_t sin_C = sin_lookup(((int32_t)tri->angle_C * TRIG_MAX_ANGLE) / 360); + + int32_t max_sin = sin_A; + if (sin_B > max_sin) + max_sin = sin_B; + if (sin_C > max_sin) + max_sin = sin_C; + +// longest side randomly chosen +#define MIN_SIDE 10 + uint16_t max_side = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h; + max_side = max_side - 12; // basic margin + uint16_t largest_side = MIN_SIDE + (rand() % (max_side - MIN_SIDE + 1)); + + tri->side_a = (uint16_t)(((uint32_t)sin_A * largest_side) / (uint32_t)max_sin); + tri->side_b = (uint16_t)(((uint32_t)sin_B * largest_side) / (uint32_t)max_sin); + tri->side_c = (uint16_t)(((uint32_t)sin_C * largest_side) / (uint32_t)max_sin); + + // create layer with room for a pointer-sized data slot & store the TriangleLayer* there + tri->layer = layer_create_with_data(frame, sizeof(TriangleLayer *)); + *(TriangleLayer **)layer_get_data(tri->layer) = tri; + layer_set_update_proc(tri->layer, triangle_layer_update_proc); + + return tri; +} + +void triangle_layer_destroy(TriangleLayer *tri) { + if (!tri) + return; + layer_destroy(tri->layer); + free(tri); +} diff --git a/src/c/triangles.h b/src/c/triangles.h new file mode 100644 index 0000000..e028d49 --- /dev/null +++ b/src/c/triangles.h @@ -0,0 +1,20 @@ +#pragma once +#include + +typedef struct { + Layer *layer; + GColor color; + + // angles; none of these are shown to the user, but they are needed to make the triangle + uint8_t angle_A; + uint8_t angle_B; + uint8_t angle_C; + + // side lengths; two of these are shown to the user (up to 3 digits) + uint16_t side_a; + uint16_t side_b; + uint16_t side_c; +} __attribute__((__packed__)) TriangleLayer; + +TriangleLayer *triangle_layer_create(GRect frame); +void triangle_layer_destroy(TriangleLayer *tri);