Limit triangle generator to right triangles
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
# Trigalarm
|
# Trigalarm
|
||||||
A habit-correcting alarm for heavy sleepers. Cannot be shut off until you describe a triangle.
|
A habit-correcting alarm for heavy sleepers. Cannot be shut off until you solve for theta in a right triangle.
|
||||||
|
|
||||||
## The Pitch
|
## The Pitch
|
||||||
### Simple
|
### Simple
|
||||||
The app only has one page. You can only set one alarm. There is no snooze, there is no weekly schedule, and there is no way to disable the alarm (you'll always have one set - every day - like it or not).
|
The app only has one page. You can only set one alarm. There is no snooze, there is no weekly schedule, and there is no way to disable the alarm (you'll always have one set - every day - like it or not).
|
||||||
### Persistent
|
### Persistent
|
||||||
The alarm cannot be stopped until you describe a randomized triangle. Even if you force-close the app, it will re-open itself until you correctly describe the triangle. You must identify the type of triangle and provide the equation to solve for θ.
|
The alarm cannot be stopped until you solve for theta in a randomized right triangle. Even if you force-close the app, it will re-open itself until you complete the equation.
|
||||||
|
|
||||||
## Why Trigonometry?
|
## Why Trigonometry?
|
||||||
Because it requires the perfect amount of thought to activate your brain in the morning. If the app gave you a more typical math problem, you could just mindlessly punch it into a calculator. Trigonometry still requires a base amount of thought ("Do I need sine, cosine, or tangent?") before a calculator is of any use.
|
Because it requires the perfect amount of thought to activate your brain in the morning. If the app gave you a more typical math problem, you could just mindlessly punch it into a calculator. Trigonometry still requires a base amount of thought ("Do I need sine, cosine, or tangent?") before a calculator is of any use.
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"characterRegex": "[?ntype:θ=acuobsrigh¯¹0123456789]",
|
"characterRegex": "[°ntype:θ=acuobsrigh¯¹0123456789]",
|
||||||
"file": "mansalva_regular.ttf",
|
"file": "mansalva_regular.ttf",
|
||||||
"name": "FONT_MANSALVA_REGULAR_22",
|
"name": "FONT_MANSALVA_REGULAR_22",
|
||||||
"type": "font"
|
"type": "font"
|
||||||
|
|||||||
+10
-7
@@ -36,7 +36,7 @@ static TextLayer *s_drop_txt_under_layer;
|
|||||||
static Layer *s_division_layer;
|
static Layer *s_division_layer;
|
||||||
static Layer *s_alarm_highlight_layer;
|
static Layer *s_alarm_highlight_layer;
|
||||||
static MenuLayer *s_drop_menu_layer;
|
static MenuLayer *s_drop_menu_layer;
|
||||||
static TriangleLayer *s_triangle_layer;
|
static RightTriangleLayer *s_triangle_layer;
|
||||||
static TextLayer *s_side_a_txt_layer;
|
static TextLayer *s_side_a_txt_layer;
|
||||||
static TextLayer *s_side_b_txt_layer;
|
static TextLayer *s_side_b_txt_layer;
|
||||||
static TextLayer *s_side_c_txt_layer;
|
static TextLayer *s_side_c_txt_layer;
|
||||||
@@ -535,32 +535,33 @@ static void alarm_window_load() {
|
|||||||
layer_add_child(window_layer, s_alarm_highlight_layer);
|
layer_add_child(window_layer, s_alarm_highlight_layer);
|
||||||
|
|
||||||
// triangle
|
// 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)), GColorBlack);
|
s_triangle_layer = right_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)), GColorBlack);
|
||||||
layer_add_child(window_layer, s_triangle_layer->layer);
|
layer_add_child(window_layer, s_triangle_layer->layer);
|
||||||
|
|
||||||
// triangle measurements text layers
|
// triangle measurements text layers
|
||||||
//// side_a
|
//// side_a
|
||||||
s_side_a_txt_layer = text_layer_create(GRect(0, s_bottom_bar_grect.origin.y - 27, PBL_DISPLAY_WIDTH, 23));
|
s_side_a_txt_layer = text_layer_create(GRect(0, s_bottom_bar_grect.origin.y - 25, PBL_DISPLAY_WIDTH, 23));
|
||||||
text_layer_set_background_color(s_side_a_txt_layer, GColorClear);
|
text_layer_set_background_color(s_side_a_txt_layer, GColorClear);
|
||||||
text_layer_set_font(s_side_a_txt_layer, s_font_mansalva_small);
|
text_layer_set_font(s_side_a_txt_layer, s_font_mansalva_small);
|
||||||
text_layer_set_text_alignment(s_side_a_txt_layer, GTextAlignmentCenter);
|
text_layer_set_text_alignment(s_side_a_txt_layer, GTextAlignmentCenter);
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_side_a_txt_layer));
|
layer_add_child(window_layer, text_layer_get_layer(s_side_a_txt_layer));
|
||||||
//// side_b
|
//// side_b
|
||||||
s_side_b_txt_layer = text_layer_create(GRect(-4, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23));
|
s_side_b_txt_layer = text_layer_create(GRect(-3, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23));
|
||||||
text_layer_set_background_color(s_side_b_txt_layer, GColorClear);
|
text_layer_set_background_color(s_side_b_txt_layer, GColorClear);
|
||||||
text_layer_set_font(s_side_b_txt_layer, s_font_mansalva_small);
|
text_layer_set_font(s_side_b_txt_layer, s_font_mansalva_small);
|
||||||
text_layer_set_text_alignment(s_side_b_txt_layer, GTextAlignmentRight);
|
text_layer_set_text_alignment(s_side_b_txt_layer, GTextAlignmentRight);
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_side_b_txt_layer));
|
layer_add_child(window_layer, text_layer_get_layer(s_side_b_txt_layer));
|
||||||
//// side_c
|
//// side_c
|
||||||
s_side_c_txt_layer = text_layer_create(GRect(4, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23));
|
s_side_c_txt_layer = text_layer_create(GRect(3, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23));
|
||||||
text_layer_set_background_color(s_side_c_txt_layer, GColorClear);
|
text_layer_set_background_color(s_side_c_txt_layer, GColorClear);
|
||||||
text_layer_set_font(s_side_c_txt_layer, s_font_mansalva_small);
|
text_layer_set_font(s_side_c_txt_layer, s_font_mansalva_small);
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_side_c_txt_layer));
|
layer_add_child(window_layer, text_layer_get_layer(s_side_c_txt_layer));
|
||||||
//// display 2 side lengths to the user
|
//// display 2 side lengths to the user
|
||||||
static char side_a_buffer[3], side_b_buffer[3], side_c_buffer[3];
|
static char side_a_buffer[8], side_b_buffer[8], side_c_buffer[8], angle_A_buffer[8], angle_B_buffer[8], angle_C_buffer[8];
|
||||||
uint8_t ignored_side = rand() % 3;
|
uint8_t ignored_side = rand() % 3;
|
||||||
switch (ignored_side) {
|
switch (ignored_side) {
|
||||||
case 0:
|
case 0:
|
||||||
|
APP_LOG(APP_LOG_LEVEL_DEBUG, "b,c");
|
||||||
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
||||||
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
||||||
s_options_over[0] = side_b_buffer;
|
s_options_over[0] = side_b_buffer;
|
||||||
@@ -569,6 +570,7 @@ static void alarm_window_load() {
|
|||||||
s_options_under[1] = side_c_buffer;
|
s_options_under[1] = side_c_buffer;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
APP_LOG(APP_LOG_LEVEL_DEBUG, "a,c");
|
||||||
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
||||||
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
||||||
s_options_over[0] = side_a_buffer;
|
s_options_over[0] = side_a_buffer;
|
||||||
@@ -577,6 +579,7 @@ static void alarm_window_load() {
|
|||||||
s_options_under[1] = side_c_buffer;
|
s_options_under[1] = side_c_buffer;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
APP_LOG(APP_LOG_LEVEL_DEBUG, "a,b");
|
||||||
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
||||||
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
||||||
s_options_over[0] = side_a_buffer;
|
s_options_over[0] = side_a_buffer;
|
||||||
@@ -611,7 +614,7 @@ static void alarm_window_unload() {
|
|||||||
text_layer_destroy(s_side_a_txt_layer);
|
text_layer_destroy(s_side_a_txt_layer);
|
||||||
text_layer_destroy(s_side_b_txt_layer);
|
text_layer_destroy(s_side_b_txt_layer);
|
||||||
text_layer_destroy(s_side_c_txt_layer);
|
text_layer_destroy(s_side_c_txt_layer);
|
||||||
triangle_layer_destroy(s_triangle_layer);
|
right_triangle_layer_destroy(s_triangle_layer);
|
||||||
layer_destroy(s_alarm_highlight_layer);
|
layer_destroy(s_alarm_highlight_layer);
|
||||||
layer_destroy(s_division_layer);
|
layer_destroy(s_division_layer);
|
||||||
text_layer_destroy(s_parenthesis_txt_layer);
|
text_layer_destroy(s_parenthesis_txt_layer);
|
||||||
|
|||||||
+26
-63
@@ -1,27 +1,23 @@
|
|||||||
#include "triangles.h"
|
#include "triangles.h"
|
||||||
#include <pebble.h>
|
#include <pebble.h>
|
||||||
|
|
||||||
static void triangle_layer_update_proc(Layer *layer, GContext *ctx) {
|
static void right_triangle_layer_update_proc(Layer *layer, GContext *ctx) {
|
||||||
// retrieve the TriangleLayer pointer stored in the layer's data slot
|
// retrieve the RightTriangleLayer pointer stored in the layer's data slot
|
||||||
TriangleLayer *tri = *(TriangleLayer **)layer_get_data(layer);
|
RightTriangleLayer *tri = *(RightTriangleLayer **)layer_get_data(layer);
|
||||||
if (!tri)
|
if (!tri)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GRect bounds = layer_get_bounds(layer);
|
GRect bounds = layer_get_bounds(layer);
|
||||||
graphics_context_set_stroke_color(ctx, tri->color);
|
graphics_context_set_stroke_color(ctx, tri->color);
|
||||||
|
|
||||||
// side_a is always the longest side (horizontal base)
|
// side a guaranteed longest
|
||||||
uint16_t base_side = tri->side_a;
|
uint16_t base_side = tri->side_a;
|
||||||
uint16_t left_side = tri->side_c; // C → A (side c connects vertices B and A)
|
|
||||||
uint8_t left_angle = tri->angle_B; // at vertex B
|
|
||||||
|
|
||||||
// calculate height and horizontal apex offset first
|
// compute height and apex offset from side lengths
|
||||||
int32_t sin_left = sin_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360);
|
uint16_t height = (uint16_t)(((uint32_t)tri->side_b * tri->side_c) / tri->side_a);
|
||||||
int32_t cos_left = cos_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360);
|
uint16_t apex_offset = (uint16_t)(((uint32_t)tri->side_c * tri->side_c) / tri->side_a);
|
||||||
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);
|
|
||||||
|
|
||||||
// center the triangle both horizontally and vertically
|
// center both horizontally and vertically
|
||||||
uint16_t padded_base = (base_side % 2 == 0) ? base_side : base_side + 1;
|
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 margin = (bounds.size.w / 2) - (padded_base / 2);
|
||||||
uint16_t start_x = margin + 1;
|
uint16_t start_x = margin + 1;
|
||||||
@@ -30,14 +26,14 @@ static void triangle_layer_update_proc(Layer *layer, GContext *ctx) {
|
|||||||
uint16_t apex_x = start_x + apex_offset;
|
uint16_t apex_x = start_x + apex_offset;
|
||||||
uint16_t apex_y = base_y - height;
|
uint16_t apex_y = base_y - height;
|
||||||
|
|
||||||
// draw the three sides
|
// 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(end_x, base_y));
|
||||||
graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(apex_x, apex_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));
|
graphics_draw_line(ctx, GPoint(end_x, base_y), GPoint(apex_x, apex_y));
|
||||||
}
|
}
|
||||||
|
|
||||||
TriangleLayer *triangle_layer_create(GRect frame, GColor color) {
|
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
||||||
TriangleLayer *tri = malloc(sizeof(TriangleLayer));
|
RightTriangleLayer *tri = malloc(sizeof(RightTriangleLayer));
|
||||||
if (!tri)
|
if (!tri)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -45,63 +41,30 @@ TriangleLayer *triangle_layer_create(GRect frame, GColor color) {
|
|||||||
tri->color = color;
|
tri->color = color;
|
||||||
|
|
||||||
// determine angles
|
// determine angles
|
||||||
uint8_t r1, r2;
|
tri->angle_A = 90;
|
||||||
do {
|
tri->angle_B = rand() % 86; // acute angle at left base vertex
|
||||||
r1 = 5 + (rand() % 166); // 5–170 (angle_A ≥ 5)
|
tri->angle_C = 90 - tri->angle_B; // acute angle at right base vertex
|
||||||
r2 = (r1 + 5) + (rand() % (171 - r1)); // r1+5 … 175 (angles B,C ≥ 5)
|
|
||||||
} while (r1 == 60 && r2 == 120); // ensure never equilateral
|
|
||||||
|
|
||||||
tri->angle_A = r1;
|
// choose hypotenuse length randomly
|
||||||
tri->angle_B = r2 - r1;
|
uint16_t min_hyp = (frame.size.w < frame.size.h) ? frame.size.w / 2 : frame.size.h / 2;
|
||||||
tri->angle_C = 180 - r2;
|
uint16_t max_hyp = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h;
|
||||||
|
tri->side_a = min_hyp + (rand() % (max_hyp - min_hyp + 1));
|
||||||
|
|
||||||
// determine side lengths (law of sines)
|
// legs: side_b = hyp × sin(B), side_c = hyp × sin(C) = hyp × cos(B)
|
||||||
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_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 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);
|
||||||
|
|
||||||
int32_t max_sin = sin_A;
|
// create layer with room for a pointer-sized data slot & store the RightTriangleLayer* there
|
||||||
if (sin_B > max_sin)
|
tri->layer = layer_create_with_data(frame, sizeof(RightTriangleLayer *));
|
||||||
max_sin = sin_B;
|
*(RightTriangleLayer **)layer_get_data(tri->layer) = tri;
|
||||||
if (sin_C > max_sin)
|
layer_set_update_proc(tri->layer, right_triangle_layer_update_proc);
|
||||||
max_sin = sin_C;
|
|
||||||
|
|
||||||
// longest side randomly chosen
|
|
||||||
uint16_t min_length_of_longest_side = (frame.size.w < frame.size.h) ? frame.size.w / 2 : frame.size.h / 2;
|
|
||||||
uint16_t max_length_of_longest_side = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h;
|
|
||||||
uint16_t largest_side = min_length_of_longest_side + (rand() % (max_length_of_longest_side - min_length_of_longest_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);
|
|
||||||
|
|
||||||
// ensure side_a is always longest
|
|
||||||
if (tri->side_b > tri->side_a) {
|
|
||||||
uint16_t tmp_s = tri->side_a;
|
|
||||||
tri->side_a = tri->side_b;
|
|
||||||
tri->side_b = tmp_s;
|
|
||||||
uint8_t tmp_a = tri->angle_A;
|
|
||||||
tri->angle_A = tri->angle_B;
|
|
||||||
tri->angle_B = tmp_a;
|
|
||||||
}
|
|
||||||
if (tri->side_c > tri->side_a) {
|
|
||||||
uint16_t tmp_s = tri->side_a;
|
|
||||||
tri->side_a = tri->side_c;
|
|
||||||
tri->side_c = tmp_s;
|
|
||||||
uint8_t tmp_a = tri->angle_A;
|
|
||||||
tri->angle_A = tri->angle_C;
|
|
||||||
tri->angle_C = tmp_a;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tri;
|
return tri;
|
||||||
}
|
}
|
||||||
|
|
||||||
void triangle_layer_destroy(TriangleLayer *tri) {
|
void right_triangle_layer_destroy(RightTriangleLayer *tri) {
|
||||||
if (!tri)
|
if (!tri)
|
||||||
return;
|
return;
|
||||||
layer_destroy(tri->layer);
|
layer_destroy(tri->layer);
|
||||||
|
|||||||
+3
-3
@@ -14,7 +14,7 @@ typedef struct {
|
|||||||
uint16_t side_a;
|
uint16_t side_a;
|
||||||
uint16_t side_b;
|
uint16_t side_b;
|
||||||
uint16_t side_c;
|
uint16_t side_c;
|
||||||
} __attribute__((__packed__)) TriangleLayer;
|
} __attribute__((__packed__)) RightTriangleLayer;
|
||||||
|
|
||||||
TriangleLayer *triangle_layer_create(GRect frame, GColor color);
|
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color);
|
||||||
void triangle_layer_destroy(TriangleLayer *tri);
|
void right_triangle_layer_destroy(RightTriangleLayer *tri);
|
||||||
|
|||||||
Reference in New Issue
Block a user