83 lines
3.1 KiB
C
83 lines
3.1 KiB
C
#include "triangles.h"
|
||
#include <pebble.h>
|
||
|
||
static void right_triangle_layer_update_proc(Layer *layer, GContext *ctx) {
|
||
// retrieve the RightTriangleLayer pointer stored in the layer's data slot
|
||
RightTriangleLayer *tri = *(RightTriangleLayer **)layer_get_data(layer);
|
||
if (!tri)
|
||
return;
|
||
|
||
GRect bounds = layer_get_bounds(layer);
|
||
graphics_context_set_stroke_color(ctx, tri->color);
|
||
|
||
// side a guaranteed longest
|
||
uint16_t base_side = tri->side_a;
|
||
|
||
// compute height and apex offset from side lengths
|
||
uint16_t height = (uint16_t)(((uint32_t)tri->side_b * tri->side_c) / tri->side_a);
|
||
uint16_t apex_offset = (uint16_t)(((uint32_t)tri->side_c * tri->side_c) / tri->side_a);
|
||
|
||
// center both horizontally and 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 + height) / 2;
|
||
uint16_t apex_x = start_x + apex_offset;
|
||
uint16_t apex_y = base_y - height;
|
||
|
||
// draw sides
|
||
graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(end_x, base_y));
|
||
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));
|
||
|
||
// circle theta angle
|
||
graphics_context_set_stroke_color(ctx, GColorRed);
|
||
graphics_draw_circle(ctx, GPoint(tri->theta_angle_is_left ? start_x : end_x, base_y), 4);
|
||
}
|
||
|
||
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
||
RightTriangleLayer *tri = malloc(sizeof(RightTriangleLayer));
|
||
if (!tri)
|
||
return NULL;
|
||
|
||
// set color
|
||
tri->color = color;
|
||
|
||
// determine angles
|
||
tri->angle_A = 90; // right angle at apex
|
||
tri->theta_angle_is_left = rand() % 2;
|
||
|
||
uint16_t min_hyp = (frame.size.w < frame.size.h) ? frame.size.w / 2 : frame.size.h / 2;
|
||
uint16_t max_hyp = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h;
|
||
|
||
// regenerate until no two sides share a length
|
||
do {
|
||
tri->angle_B = 5 + rand() % 81; // acute angle at left base vertex
|
||
tri->angle_C = 90 - tri->angle_B; // acute angle at right base vertex
|
||
|
||
// choose hypotenuse length randomly
|
||
tri->side_a = min_hyp + (rand() % (max_hyp - min_hyp + 1));
|
||
|
||
// legs: side_b = hyp × sin(B), side_c = hyp × sin(C) = hyp × cos(B)
|
||
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);
|
||
tri->side_b = (uint16_t)(((uint32_t)sin_B * tri->side_a) / TRIG_MAX_RATIO);
|
||
tri->side_c = (uint16_t)(((uint32_t)sin_C * tri->side_a) / TRIG_MAX_RATIO);
|
||
} while (tri->side_a == tri->side_b || tri->side_a == tri->side_c || tri->side_b == tri->side_c);
|
||
|
||
// create layer with room for a pointer-sized data slot & store the RightTriangleLayer* there
|
||
tri->layer = layer_create_with_data(frame, sizeof(RightTriangleLayer *));
|
||
*(RightTriangleLayer **)layer_get_data(tri->layer) = tri;
|
||
layer_set_update_proc(tri->layer, right_triangle_layer_update_proc);
|
||
|
||
return tri;
|
||
}
|
||
|
||
void right_triangle_layer_destroy(RightTriangleLayer *tri) {
|
||
if (!tri)
|
||
return;
|
||
layer_destroy(tri->layer);
|
||
free(tri);
|
||
}
|