210 lines
9.2 KiB
C
210 lines
9.2 KiB
C
#include "reading.h"
|
|
#include "i18n.h"
|
|
|
|
#include <math.h>
|
|
|
|
void reading_generate(const char *tarot_seed, const BirthData *birth, Gender gender,
|
|
time_t utc_moment, DailyReading *out) {
|
|
out->utc_moment = utc_moment;
|
|
out->gender = gender;
|
|
astro_compute_natal_chart(birth, &out->natal);
|
|
astro_compute_daily_transits(utc_moment, &out->natal, &out->transits);
|
|
tarot_draw_celtic_cross(tarot_seed, &out->spread);
|
|
}
|
|
|
|
/* Everything below uses fprintf, which Pebble's SDK blocks at compile
|
|
* time (see pebble_warn_unsupported_functions.h) - not compiled at all
|
|
* for the watch, which only ever calls reading_generate() above. */
|
|
#ifndef PBL_SDK_3
|
|
|
|
/* UI-label keys ("ui.*") are looked up here rather than in i18n.h/.c
|
|
* because they're specific to these two renderers, not part of the
|
|
* portable astro/tarot vocabulary. */
|
|
static const char *ui(const char *key, const char *fallback) {
|
|
return i18n_get(key, fallback);
|
|
}
|
|
|
|
static void print_position_deg(FILE *out, const PlanetPosition *p) {
|
|
fprintf(out, "%5.1f%s %-11s (%s %d)", p->degree_in_sign, "\xc2\xb0",
|
|
astro_sign_name(p->sign), ui("ui.house", "House"), p->house);
|
|
}
|
|
|
|
void reading_print_text(const DailyReading *r, FILE *out) {
|
|
fprintf(out, "===== %s =====\n", ui("ui.natal_chart", "Natal Chart"));
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
fprintf(out, "%-8s ", astro_body_name((Body)b));
|
|
print_position_deg(out, &r->natal.bodies[b]);
|
|
fprintf(out, "\n");
|
|
}
|
|
fprintf(out, "%s %5.1f%s %s\n", ui("ui.ascendant", "Ascendant"),
|
|
fmod(r->natal.ascendant_longitude, 30.0), "\xc2\xb0",
|
|
astro_sign_name(r->natal.houses[0]));
|
|
fprintf(out, "\n");
|
|
|
|
fprintf(out, "===== %s =====\n", ui("ui.todays_sky", "Today's Sky"));
|
|
fprintf(out, "%s %s\n", ui("ui.moon_phase", "Moon phase:"),
|
|
astro_moon_phase_name(r->transits.moon_phase));
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
fprintf(out, "%-8s ", astro_body_name((Body)b));
|
|
print_position_deg(out, &r->transits.bodies[b]);
|
|
fprintf(out, "\n");
|
|
}
|
|
fprintf(out, "\n");
|
|
|
|
fprintf(out, "===== %s =====\n", ui("ui.todays_aspects", "Today's Aspects to Natal Chart"));
|
|
if (r->transits.aspect_count == 0) {
|
|
fprintf(out, "%s\n", ui("ui.no_aspects", "(none within orb)"));
|
|
}
|
|
for (int i = 0; i < r->transits.aspect_count; i++) {
|
|
const Aspect *a = &r->transits.aspects[i];
|
|
fprintf(out, "%s %s %s %s %s (%s %.1f%s)\n", ui("ui.transiting", "Transiting"),
|
|
astro_body_name(a->transiting_planet), astro_aspect_name(a->type),
|
|
ui("ui.natal", "natal"), astro_body_name(a->natal_planet),
|
|
ui("ui.orb", "orb"), a->orb, "\xc2\xb0");
|
|
}
|
|
fprintf(out, "\n");
|
|
|
|
fprintf(out, "===== %s =====\n", ui("ui.celtic_cross", "Celtic Cross"));
|
|
for (int i = 0; i < TAROT_SPREAD_SIZE; i++) {
|
|
const TarotDraw *draw = &r->spread.positions[i];
|
|
fprintf(out, "%-16s %s%s%s\n", tarot_position_name((CelticCrossPosition)i, r->gender),
|
|
tarot_card_name(draw->card), draw->reversed ? " " : "",
|
|
draw->reversed ? ui("ui.reversed", "(Reversed)") : "");
|
|
fprintf(out, " %s\n", tarot_position_description((CelticCrossPosition)i, r->gender));
|
|
fprintf(out, " %s\n", tarot_card_meaning(draw->card, draw->reversed));
|
|
}
|
|
}
|
|
|
|
static void print_body_row_html(FILE *out, const char *label, const PlanetPosition *p) {
|
|
fprintf(out, "<tr><td>%s</td><td>%.1f° %s</td><td>%s %d</td></tr>\n", label,
|
|
p->degree_in_sign, astro_sign_name(p->sign), ui("ui.house", "House"), p->house);
|
|
}
|
|
|
|
void reading_print_html(const DailyReading *r, FILE *out) {
|
|
fprintf(out,
|
|
"<!doctype html>\n<html><head><meta charset=\"utf-8\">\n"
|
|
"<title>%s</title>\n"
|
|
"<style>\n"
|
|
"body { font-family: sans-serif; max-width: 900px; margin: 2em auto; }\n"
|
|
"table { border-collapse: collapse; margin-bottom: 1.5em; }\n"
|
|
"td { padding: 2px 10px 2px 0; }\n"
|
|
"h1, h2 { border-bottom: 1px solid #ccc; }\n"
|
|
".spread { display: flex; flex-wrap: wrap; gap: 1.5em; }\n"
|
|
".card { width: 160px; }\n"
|
|
".card img { width: 140px; display: block; }\n"
|
|
".card img.reversed { transform: rotate(180deg); }\n"
|
|
".card .position { font-weight: bold; }\n"
|
|
".card .name { font-style: italic; }\n"
|
|
"</style></head><body>\n",
|
|
ui("ui.page_title", "Deck in a Dash - Daily Reading"));
|
|
|
|
fprintf(out, "<h1>%s</h1>\n", ui("ui.daily_reading", "Daily Reading"));
|
|
|
|
fprintf(out, "<h2>%s</h2>\n<table>\n", ui("ui.natal_chart", "Natal Chart"));
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
print_body_row_html(out, astro_body_name((Body)b), &r->natal.bodies[b]);
|
|
}
|
|
fprintf(out, "<tr><td>%s</td><td>%.1f° %s</td></tr>\n", ui("ui.ascendant", "Ascendant"),
|
|
fmod(r->natal.ascendant_longitude, 30.0), astro_sign_name(r->natal.houses[0]));
|
|
fprintf(out, "</table>\n");
|
|
|
|
fprintf(out, "<h2>%s</h2>\n<p>%s %s</p>\n<table>\n", ui("ui.todays_sky", "Today's Sky"),
|
|
ui("ui.moon_phase", "Moon phase:"), astro_moon_phase_name(r->transits.moon_phase));
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
print_body_row_html(out, astro_body_name((Body)b), &r->transits.bodies[b]);
|
|
}
|
|
fprintf(out, "</table>\n");
|
|
|
|
fprintf(out, "<h2>%s</h2>\n<table>\n", ui("ui.todays_aspects", "Today's Aspects to Natal Chart"));
|
|
for (int i = 0; i < r->transits.aspect_count; i++) {
|
|
const Aspect *a = &r->transits.aspects[i];
|
|
fprintf(out, "<tr><td>%s %s</td><td>%s</td><td>%s %s</td><td>%s %.1f°</td></tr>\n",
|
|
ui("ui.transiting", "Transiting"), astro_body_name(a->transiting_planet),
|
|
astro_aspect_name(a->type), ui("ui.natal", "natal"),
|
|
astro_body_name(a->natal_planet), ui("ui.orb", "orb"), a->orb);
|
|
}
|
|
fprintf(out, "</table>\n");
|
|
|
|
fprintf(out, "<h2>%s</h2>\n<div class=\"spread\">\n", ui("ui.celtic_cross", "Celtic Cross"));
|
|
for (int i = 0; i < TAROT_SPREAD_SIZE; i++) {
|
|
const TarotDraw *draw = &r->spread.positions[i];
|
|
fprintf(out,
|
|
"<div class=\"card\">\n"
|
|
" <div class=\"position\">%s</div>\n"
|
|
/* Expects card art at img/<file> next to the HTML file itself -
|
|
* `make images` copies res/img/ there alongside the binary in
|
|
* dist/. */
|
|
" <img class=\"%s\" src=\"img/%s\" alt=\"%s\">\n"
|
|
" <div class=\"name\">%s%s%s</div>\n"
|
|
" <div class=\"desc\">%s</div>\n"
|
|
" <div class=\"meaning\">%s</div>\n"
|
|
"</div>\n",
|
|
tarot_position_name((CelticCrossPosition)i, r->gender),
|
|
draw->reversed ? "reversed" : "",
|
|
tarot_card_image_file(draw->card), tarot_card_name(draw->card),
|
|
tarot_card_name(draw->card), draw->reversed ? " " : "",
|
|
draw->reversed ? ui("ui.reversed", "(Reversed)") : "",
|
|
tarot_position_description((CelticCrossPosition)i, r->gender),
|
|
tarot_card_meaning(draw->card, draw->reversed));
|
|
}
|
|
fprintf(out, "</div>\n</body></html>\n");
|
|
}
|
|
|
|
static void print_body_position_json(FILE *out, const char *indent, Body body,
|
|
const PlanetPosition *p) {
|
|
fprintf(out,
|
|
"%s{\"body\": \"%s\", \"sign\": \"%s\", \"degree_in_sign\": %.4f, "
|
|
"\"ecliptic_longitude\": %.4f, \"house\": %d}",
|
|
indent, astro_body_slug(body), astro_sign_slug(p->sign),
|
|
p->degree_in_sign, p->ecliptic_longitude, p->house);
|
|
}
|
|
|
|
void reading_print_json(const DailyReading *r, FILE *out) {
|
|
fprintf(out, "{\n");
|
|
|
|
struct tm *utc = gmtime(&r->utc_moment);
|
|
fprintf(out, " \"date\": \"%04d-%02d-%02d\",\n", utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday);
|
|
fprintf(out, " \"gender\": \"%s\",\n", tarot_gender_slug(r->gender));
|
|
|
|
fprintf(out, " \"natal\": {\n \"bodies\": [\n");
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
print_body_position_json(out, " ", (Body)b, &r->natal.bodies[b]);
|
|
fprintf(out, "%s\n", b + 1 < NUM_BODIES ? "," : "");
|
|
}
|
|
fprintf(out, " ],\n \"ascendant_longitude\": %.4f,\n \"houses\": [",
|
|
r->natal.ascendant_longitude);
|
|
for (int h = 0; h < 12; h++) {
|
|
fprintf(out, "\"%s\"%s", astro_sign_slug(r->natal.houses[h]), h + 1 < 12 ? ", " : "");
|
|
}
|
|
fprintf(out, "]\n },\n");
|
|
|
|
fprintf(out, " \"transits\": {\n \"bodies\": [\n");
|
|
for (int b = 0; b < NUM_BODIES; b++) {
|
|
print_body_position_json(out, " ", (Body)b, &r->transits.bodies[b]);
|
|
fprintf(out, "%s\n", b + 1 < NUM_BODIES ? "," : "");
|
|
}
|
|
fprintf(out, " ],\n \"moon_phase\": \"%s\",\n \"aspects\": [\n",
|
|
astro_moon_phase_slug(r->transits.moon_phase));
|
|
for (int i = 0; i < r->transits.aspect_count; i++) {
|
|
const Aspect *a = &r->transits.aspects[i];
|
|
fprintf(out,
|
|
" {\"transiting_planet\": \"%s\", \"natal_planet\": \"%s\", "
|
|
"\"type\": \"%s\", \"orb\": %.4f}%s\n",
|
|
astro_body_slug(a->transiting_planet), astro_body_slug(a->natal_planet),
|
|
astro_aspect_slug(a->type), a->orb,
|
|
i + 1 < r->transits.aspect_count ? "," : "");
|
|
}
|
|
fprintf(out, " ]\n },\n");
|
|
|
|
fprintf(out, " \"spread\": {\n \"positions\": [\n");
|
|
for (int i = 0; i < TAROT_SPREAD_SIZE; i++) {
|
|
const TarotDraw *draw = &r->spread.positions[i];
|
|
fprintf(out, " {\"position\": \"%s\", \"card\": \"%s\", \"reversed\": %s}%s\n",
|
|
tarot_position_slug((CelticCrossPosition)i), tarot_card_slug(draw->card),
|
|
draw->reversed ? "true" : "false", i + 1 < TAROT_SPREAD_SIZE ? "," : "");
|
|
}
|
|
fprintf(out, " ]\n }\n}\n");
|
|
}
|
|
|
|
#endif /* !PBL_SDK_3 */
|