Coin & Dice Toss & Geomantic Divination Watch Faces (#235)
* init * advanced latlon setting * simple functionality done * lat lon high precision fwd bwd * edit toggle * added readme for branch * DD DMS conversion & cleanup * DD to OLC conversion * olc encoding & decoding * OLC implementation * swapped bools for modes, code cleanup * place name editor * updated button logic, fixed display * load and save places in state array * todo list * simplified OLC functions * geohash conversion functions * geohash display & digit functions * todo * finished geohash implementation * code display function, defaults, bugfixes * read/write file/reg logic * long light in DATA to cancel * write to registry * todo * read & write backup register * file read/write * todo * new more concise button logic, optimizations * todo * renamed & cleaned up, fixed button logic * documentation * documentation * LAP mode for all coordinate screens * faster and more precise geohash algorithm * updated description * updated docu * simple place face * bugfixes, updated documentation * init * meh * added public functions for OLC and Geohash * randonauting face * fix * display fix * cleanup * bugfixes * bugfix * added place * fixed TRNG call * fixed declaration conflict * modulo bias filter * simplified things, chance RNG selection * fixed button logic, better menus * cleanup * documentation * docu fixes * init * basic functions * all needed static functions done * progress * coins and dice done * progress * place update * divination faces functionality done * better divine_bit * figure numbers and names * captions optional * coin animation * dice animation & optimizations * animation * changed names, documented * bugfix * cleanup * reset config --------- Co-authored-by: joeycastillo <joeycastillo@utexas.edu>
This commit is contained in:
356
movement/watch_faces/complication/geomancy_face.c
Normal file
356
movement/watch_faces/complication/geomancy_face.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Tobias Raayoni Last / @randogoth
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "toss_up_face.h"
|
||||
#include "geomancy_face.h"
|
||||
|
||||
// CONSTANTS //////////////////////////////////////////////////////////////////
|
||||
|
||||
// The Bagua 八卦 Trigrams encoded as 3bit tribbles, represented as binary integer
|
||||
static const uint32_t bagua = 0b00000101001110010111011100000000;
|
||||
|
||||
// The King Wen Sequence 文王卦序 of the I Ching 易經 Hexagrams 卦 encoded as an array
|
||||
// of decimal integers in the order of two combined Trigram tribbles from 0b000000 to
|
||||
// 0b111111
|
||||
static const uint8_t wen_order[] = {
|
||||
1, 22, 7, 19, 15, 34, 44, 11,
|
||||
14, 51, 38, 52, 61, 55, 30, 32,
|
||||
6, 3, 28, 58, 39, 63, 46, 5,
|
||||
45, 17, 47, 56, 31, 49, 27, 43,
|
||||
23, 26, 2, 41, 50, 20, 16, 24,
|
||||
35, 21, 62, 36, 54, 29, 48, 12,
|
||||
18, 40, 59, 60, 53, 37, 57, 9,
|
||||
10, 25, 4, 8, 33, 13, 42, 0
|
||||
};
|
||||
|
||||
// The geomantic figures encoded as 4 bit nibbles, represented as hexadecimal integer
|
||||
static const uint64_t geomantic = 0x4ABF39D25E76C180;
|
||||
|
||||
// Abbreviations of the Names of the Geomantic Figures in the order of the 4 bit nibbles
|
||||
// from 0b0000 to 0b1111
|
||||
static const char figures[16][2] = {
|
||||
"VI" /* Via */, "Hd" /* Head of the Dragon */, "PA" /* Puella */, "GF" /* Greater Fortune*/,
|
||||
"PR" /* Puer */, "AQ" /* Acquisitio */, "CA" /* Carcer */, "TR" /* Tristitia */,
|
||||
"Td" /* Tail of the Dragon */, "CO" /* Conjunctio */, "AM" /* Amissio */, "AL" /* Albus */,
|
||||
"LF" /* Lesser Fortune */, "RU" /* Rubeus */, "LA" /* Laetitia */, "PO" /* Populus */
|
||||
};
|
||||
|
||||
// DECLARATIONS ///////////////////////////////////////////////////////////////
|
||||
|
||||
static void geomancy_face_display();
|
||||
static nibble_t _geomancy_pick_figure();
|
||||
static tribble_t _iching_pick_trigram();
|
||||
static uint8_t _iching_form_hexagram();
|
||||
static void _geomancy_display(nibble_t code);
|
||||
static void _display_hexagram(uint8_t hexagram, char* str);
|
||||
static void _fix_broken_line(uint8_t hexagram);
|
||||
static void _throw_animation(geomancy_state_t *state);
|
||||
|
||||
// WATCH FACE FUNCTIONS ///////////////////////////////////////////////////////
|
||||
|
||||
void geomancy_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) settings;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(geomancy_state_t));
|
||||
memset(*context_ptr, 0, sizeof(geomancy_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
void geomancy_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
||||
|
||||
bool geomancy_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
geomancy_state_t *state = (geomancy_state_t *)context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
state->animate = false;
|
||||
state->animation = 0;
|
||||
watch_display_string(" IChing", 0);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
if ( state->animate ) {
|
||||
state->animation = (state->animation + 1) % 39;
|
||||
geomancy_face_display(state);
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
if ( state->animate ) break;
|
||||
if ( state->mode <= 1 ) state->mode = 2;
|
||||
else if ( state->mode >= 2 ) state->mode = 0;
|
||||
geomancy_face_display(state);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
if ( state->animate ) break;
|
||||
switch ( state->mode ) {
|
||||
case 0:
|
||||
state->mode++;
|
||||
case 1:
|
||||
state->animate = true;
|
||||
state->i_ching_hexagram = _iching_form_hexagram();
|
||||
break;
|
||||
case 2:
|
||||
state->mode++;
|
||||
case 3:
|
||||
state->animate = true;
|
||||
state->geomantic_figure = _geomancy_pick_figure().bits;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
geomancy_face_display(state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
if ( state->animate ) break;
|
||||
state->caption = !state->caption;
|
||||
watch_display_string(" ", 0);
|
||||
geomancy_face_display(state);
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void geomancy_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
||||
|
||||
// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
|
||||
/** @brief display handler */
|
||||
static void geomancy_face_display(geomancy_state_t *state) {
|
||||
char token[7] = {0};
|
||||
nibble_t figure = *((nibble_t*) &state->geomantic_figure);
|
||||
switch ( state->mode ) {
|
||||
case 0:
|
||||
watch_display_string(" IChing", 0);
|
||||
break;
|
||||
case 1:
|
||||
_throw_animation(state);
|
||||
if ( !state->animate ) {
|
||||
_display_hexagram(state->i_ching_hexagram, token);
|
||||
watch_display_string(token, 4);
|
||||
_fix_broken_line(state->i_ching_hexagram);
|
||||
if (state->caption) {
|
||||
sprintf(token, "%2d", wen_order[state->i_ching_hexagram] + 1);
|
||||
watch_display_string(token, 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
watch_display_string(" GeomCy", 0);
|
||||
break;
|
||||
case 3:
|
||||
_throw_animation(state);
|
||||
if ( !state->animate ) {
|
||||
if ( state->caption ) {
|
||||
sprintf(token, "%c%c", figures[state->geomantic_figure][0], figures[state->geomantic_figure][1]);
|
||||
watch_display_string(token, 0);
|
||||
}
|
||||
_geomancy_display(figure);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief screen clearing animation between castings */
|
||||
static void _throw_animation(geomancy_state_t *state) {
|
||||
movement_request_tick_frequency(16);
|
||||
switch ( state->animation ) {
|
||||
case 0:
|
||||
watch_set_pixel(0, 22);
|
||||
break;
|
||||
case 1:
|
||||
watch_set_pixel(2, 22);
|
||||
watch_set_pixel(2, 23);
|
||||
watch_clear_pixel(0, 22);
|
||||
break;
|
||||
case 2:
|
||||
watch_set_pixel(1, 22);
|
||||
watch_set_pixel(0, 23);
|
||||
break;
|
||||
case 3:
|
||||
watch_set_pixel(2, 0);
|
||||
watch_set_pixel(1, 0);
|
||||
watch_set_pixel(2, 21);
|
||||
watch_set_pixel(1, 21);
|
||||
watch_clear_pixel(2, 22);
|
||||
watch_clear_pixel(1, 22);
|
||||
watch_clear_pixel(2, 23);
|
||||
watch_clear_pixel(0, 23);
|
||||
watch_clear_pixel(1, 23);
|
||||
break;
|
||||
case 4:
|
||||
watch_set_pixel(1, 17);
|
||||
watch_set_pixel(0, 20);
|
||||
watch_set_pixel(2, 10);
|
||||
watch_set_pixel(0, 1);
|
||||
break;
|
||||
case 5:
|
||||
watch_clear_pixel(2, 21);
|
||||
watch_clear_pixel(1, 21);
|
||||
watch_clear_pixel(2, 0);
|
||||
watch_clear_pixel(1, 0);
|
||||
watch_clear_pixel(1, 20);
|
||||
watch_clear_pixel(2, 20);
|
||||
watch_clear_pixel(0, 21);
|
||||
watch_clear_pixel(1, 1);
|
||||
watch_clear_pixel(0, 0);
|
||||
watch_clear_pixel(2, 1);
|
||||
watch_set_pixel(2, 19);
|
||||
watch_set_pixel(0, 19);
|
||||
watch_set_pixel(1, 2);
|
||||
watch_set_pixel(0, 2);
|
||||
break;
|
||||
case 6:
|
||||
watch_clear_pixel(1, 17);
|
||||
watch_clear_pixel(0, 20);
|
||||
watch_clear_pixel(2, 10);
|
||||
watch_clear_pixel(0, 1);
|
||||
watch_set_pixel(2, 18);
|
||||
watch_set_pixel(0, 18);
|
||||
watch_set_pixel(2, 3);
|
||||
watch_set_pixel(0, 4);
|
||||
break;
|
||||
case 7:
|
||||
watch_clear_pixel(2, 19);
|
||||
watch_clear_pixel(0, 19);
|
||||
watch_clear_pixel(1, 18);
|
||||
watch_clear_pixel(1, 19);
|
||||
watch_clear_pixel(1, 2);
|
||||
watch_clear_pixel(0, 2);
|
||||
watch_clear_pixel(1, 3);
|
||||
watch_clear_pixel(0, 3);
|
||||
watch_clear_pixel(2, 2);
|
||||
watch_set_pixel(1, 4);
|
||||
watch_set_pixel(0, 5);
|
||||
break;
|
||||
case 8:
|
||||
watch_clear_pixel(2, 18);
|
||||
watch_clear_pixel(0, 18);
|
||||
watch_clear_pixel(2, 3);
|
||||
watch_clear_pixel(0, 4);
|
||||
watch_set_pixel(2, 5);
|
||||
watch_set_pixel(1, 6);
|
||||
break;
|
||||
case 9:
|
||||
watch_clear_pixel(1, 4);
|
||||
watch_clear_pixel(0, 5);
|
||||
watch_clear_pixel(1, 5);
|
||||
watch_clear_pixel(2, 4);
|
||||
watch_clear_pixel(0, 6);
|
||||
break;
|
||||
case 10:
|
||||
watch_clear_pixel(2, 5);
|
||||
watch_clear_pixel(1, 6);
|
||||
break;
|
||||
case 11:
|
||||
state->animate = false;
|
||||
state->animation = 0;
|
||||
movement_request_tick_frequency(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// I CHING FUNCTIONS //////////////////////////////////////////////////////////
|
||||
|
||||
/** @brief form a trigram from three random bit picks
|
||||
*/
|
||||
static tribble_t _iching_pick_trigram() {
|
||||
uint8_t index = (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
|
||||
tribble_t trigram = {(bagua >> (3 * index)) & 0b111};
|
||||
return trigram;
|
||||
}
|
||||
|
||||
/** @brief form a hexagram from two trigrams
|
||||
*/
|
||||
static uint8_t _iching_form_hexagram() {
|
||||
tribble_t inner = _iching_pick_trigram();
|
||||
tribble_t outer = _iching_pick_trigram();
|
||||
uint8_t hexagram = (inner.bits << 3) | outer.bits;
|
||||
return hexagram;
|
||||
}
|
||||
|
||||
/** @brief display hexagram
|
||||
* @details | for unbroken lines and Ξ for broken lines, left of display is bottom
|
||||
*/
|
||||
static void _display_hexagram(uint8_t hexagram, char* str) {
|
||||
str[6] = '\0'; // Null-terminate the string
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
if (hexagram & (1 << (5 - i))) {
|
||||
str[i] = '1';
|
||||
} else {
|
||||
str[i] = '=';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief when Ξ digits show as = then manually add a line on top
|
||||
*/
|
||||
static void _fix_broken_line(uint8_t hexagram) {
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
if (!(hexagram & (1 << (5 - i)))) {
|
||||
if ( i == 1 ) watch_set_pixel(2, 20);
|
||||
if ( i == 3 ) watch_set_pixel(2, 1);
|
||||
if ( i == 4 ) watch_set_pixel(2, 2);
|
||||
if ( i == 5 ) watch_set_pixel(2, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GEOMANCY FUNCTIONS /////////////////////////////////////////////////////////
|
||||
|
||||
/** @brief choose a geomantic figure from four random bits
|
||||
* @details 0 represents · and 1 represents : counting from the bottom
|
||||
*/
|
||||
static nibble_t _geomancy_pick_figure() {
|
||||
uint8_t index = (divine_bit() << 3) | (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
|
||||
nibble_t figure = {(geomantic >> (4 * (15 - index))) & 0xF};
|
||||
return figure;
|
||||
}
|
||||
|
||||
/** @brief display the geomantic figure, left of display is bottom
|
||||
*/
|
||||
static void _geomancy_display(nibble_t code) {
|
||||
// draw geomantic figures
|
||||
bool row1 = (code.bits >> 3) & 1;
|
||||
bool row2 = (code.bits >> 2) & 1;
|
||||
bool row3 = (code.bits >> 1) & 1;
|
||||
bool row4 = code.bits & 1;
|
||||
|
||||
if ( row1 ) watch_set_pixel(1, 18); else watch_set_pixel(1, 19);
|
||||
if ( row2 ) { watch_set_pixel(2, 20); watch_set_pixel(0, 21);} else watch_set_pixel(1, 20);
|
||||
if ( row3 ) watch_set_pixel(0, 22); else watch_set_pixel(1, 23);
|
||||
if ( row4 ) { watch_set_pixel(2, 1); watch_set_pixel(0, 0);} else watch_set_pixel(1, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user