|
|
261920 |
|
|
|
261920 |
#include <helianthus.h></helianthus.h>
|
|
|
261920 |
#include <stdio.h></stdio.h>
|
|
|
261920 |
#include <string.h></string.h>
|
|
|
261920 |
#include <ctype.h></ctype.h>
|
|
|
261920 |
#include <math.h></math.h>
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
#define MAXREC 1000
|
|
|
261920 |
#define MARGINX 30.0
|
|
|
261920 |
#define MARGINY 15.0
|
|
|
261920 |
#define MARGINLINE0 30.0
|
|
|
261920 |
#define MARGINLINE1 40.0
|
|
|
261920 |
#define BASELINE 22.0
|
|
|
261920 |
#define PADDING 15.0
|
|
|
261920 |
#define SEPARATOR 15.0
|
|
|
261920 |
#define TEXTWIDTH 300.0
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[128];
|
|
|
261920 |
double w;
|
|
|
261920 |
double h;
|
|
|
261920 |
} Date;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[1024];
|
|
|
261920 |
double w;
|
|
|
261920 |
double h;
|
|
|
261920 |
} Text;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
char text[128];
|
|
|
261920 |
int index;
|
|
|
261920 |
int begin;
|
|
|
261920 |
int end;
|
|
|
261920 |
double w;
|
|
|
261920 |
double y0;
|
|
|
261920 |
double y1;
|
|
|
261920 |
} Part;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
double x, w;
|
|
|
261920 |
} Column;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
Date date;
|
|
|
261920 |
Text text;
|
|
|
261920 |
Part part;
|
|
|
261920 |
} BaseRecord;
|
|
|
261920 |
|
|
|
261920 |
typedef struct {
|
|
|
261920 |
Date *date;
|
|
|
261920 |
Text *text;
|
|
|
261920 |
Part *part;
|
|
|
261920 |
double y;
|
|
|
261920 |
} Record;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
BaseRecord recordsSrc[MAXREC];
|
|
|
261920 |
Record records[MAXREC];
|
|
|
261920 |
Part *parts[MAXREC];
|
|
|
261920 |
Date *dates[MAXREC];
|
|
|
261920 |
Column columns[MAXREC];
|
|
|
261920 |
int partCount;
|
|
|
261920 |
int dateCount;
|
|
|
261920 |
int recCount;
|
|
|
261920 |
int colCount;
|
|
|
261920 |
double datesWidth;
|
|
|
261920 |
double fullWidth, fullHeight;
|
|
|
261920 |
|
|
|
261920 |
double dx, dy;
|
|
|
261920 |
double mx, my;
|
|
|
261920 |
double zm = 1;
|
|
|
261920 |
|
|
|
261920 |
int curRec;
|
|
|
261920 |
double curFade;
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void updateLayout();
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
double calcWidth(char *b, char *e) {
|
|
|
261920 |
char c = *e;
|
|
|
261920 |
*e = 0;
|
|
|
261920 |
TextLayout tl = createTextLayout(b);
|
|
|
261920 |
*e = c;
|
|
|
261920 |
double w = textLayoutGetWidth(tl);
|
|
|
261920 |
textLayoutDestroy(tl);
|
|
|
261920 |
return w;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
void wordWrap(char *buf, double maxWidth) {
|
|
|
261920 |
int len = strlen(buf);
|
|
|
261920 |
if (calcWidth(buf, buf + len) <= maxWidth) return;
|
|
|
261920 |
|
|
|
261920 |
char *last = NULL;
|
|
|
261920 |
for(char *p = buf + len - 1; p > buf; --p) {
|
|
|
261920 |
if (isspace(*p)) {
|
|
|
261920 |
last = p;
|
|
|
261920 |
if (calcWidth(buf, p) < maxWidth)
|
|
|
261920 |
{ *p = '\n'; wordWrap(p+1, maxWidth); return; }
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
if (!last) return;
|
|
|
261920 |
|
|
|
261920 |
*last = '\n';
|
|
|
261920 |
wordWrap(last+1, maxWidth);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void load(const char *filename) {
|
|
|
261920 |
curRec = 0;
|
|
|
261920 |
curFade = 0;
|
|
|
261920 |
recCount = 0;
|
|
|
261920 |
partCount = 0;
|
|
|
261920 |
dateCount = 0;
|
|
|
261920 |
colCount = 0;
|
|
|
261920 |
datesWidth = 0;
|
|
|
261920 |
memset(recordsSrc, 0, sizeof(recordsSrc));
|
|
|
261920 |
memset(records, 0, sizeof(records));
|
|
|
261920 |
memset(parts, 0, sizeof(parts));
|
|
|
261920 |
memset(dates, 0, sizeof(dates));
|
|
|
261920 |
memset(columns, 0, sizeof(columns));
|
|
|
261920 |
|
|
|
261920 |
FILE *f = fopen(filename, "r");
|
|
|
261920 |
if (!f) {
|
|
|
261920 |
printf("cannot open file for read: %s\n", filename);
|
|
|
261920 |
return;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
int mode = 0;
|
|
|
261920 |
BaseRecord *rs = recordsSrc;
|
|
|
261920 |
char *p = rs->date.text;
|
|
|
261920 |
char *e = p + sizeof(rs->date.text);
|
|
|
261920 |
while(1) {
|
|
|
261920 |
int c = fgetc(f);
|
|
|
261920 |
if (c <= 0 || c >= 256) break;
|
|
|
261920 |
if (c == '\t') {
|
|
|
261920 |
if (mode == 0) {
|
|
|
261920 |
mode = 1; p = rs->text.text; e = p + sizeof(rs->text.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 1) {
|
|
|
261920 |
mode = 2; p = rs->part.text; e = p + sizeof(rs->part.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (mode == 2) {
|
|
|
261920 |
mode = 3;
|
|
|
261920 |
}
|
|
|
261920 |
} else
|
|
|
261920 |
if (c == '\n') {
|
|
|
261920 |
if (rs->date.text[0] || rs->text.text[0] || rs->part.text[0]) {
|
|
|
261920 |
++recCount; ++rs;
|
|
|
261920 |
if (recCount >= MAXREC) break;
|
|
|
261920 |
}
|
|
|
261920 |
mode = 0; p = rs->date.text; e = p + sizeof(rs->date.text);
|
|
|
261920 |
} else
|
|
|
261920 |
if (c != '\r' && p+1 < e) {
|
|
|
261920 |
*p++ = c;
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
fclose(f);
|
|
|
261920 |
|
|
|
261920 |
if (recCount < MAXREC)
|
|
|
261920 |
if (rs->date.text[0] || rs->text.text[0] || rs->part.text[0])
|
|
|
261920 |
++recCount;
|
|
|
261920 |
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
BaseRecord *rs = &recordsSrc[i];
|
|
|
261920 |
wordWrap(rs->text.text, TEXTWIDTH);
|
|
|
261920 |
|
|
|
261920 |
for(int j = 0; j <= i; ++j) {
|
|
|
261920 |
BaseRecord *rrs = &recordsSrc[j];
|
|
|
261920 |
if (!r->date && !strcmp(rs->date.text, rrs->date.text)) r->date = &rrs->date;
|
|
|
261920 |
if (!r->text && !strcmp(rs->text.text, rrs->text.text)) r->text = &rrs->text;
|
|
|
261920 |
if (!r->part && !strcmp(rs->part.text, rrs->part.text)) r->part = &rrs->part;
|
|
|
261920 |
}
|
|
|
261920 |
if (r->part == &rs->part)
|
|
|
261920 |
parts[partCount++] = r->part;
|
|
|
261920 |
if (r->date == &rs->date)
|
|
|
261920 |
dates[dateCount++] = r->date;
|
|
|
261920 |
|
|
|
261920 |
TextLayout tl = createTextLayout(r->text->text);
|
|
|
261920 |
r->text->w = textLayoutGetWidth(tl);
|
|
|
261920 |
r->text->h = textLayoutGetHeight(tl);
|
|
|
261920 |
textLayoutDestroy(tl);
|
|
|
261920 |
|
|
|
261920 |
tl = createTextLayout(r->date->text);
|
|
|
261920 |
r->date->w = textLayoutGetWidth(tl);
|
|
|
261920 |
r->date->h = textLayoutGetHeight(tl);
|
|
|
261920 |
textLayoutDestroy(tl);
|
|
|
261920 |
|
|
|
261920 |
if (r->part->w < r->text->w)
|
|
|
261920 |
r->part->w = r->text->w;
|
|
|
261920 |
if (datesWidth < r->date->w)
|
|
|
261920 |
datesWidth = r->date->w;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
updateLayout();
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void writeUnwrapped(const char *buf, FILE *f) {
|
|
|
261920 |
while(*buf) {
|
|
|
261920 |
fputc(*buf == '\n' ? ' ' : *buf, f);
|
|
|
261920 |
++buf;
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void save(const char *filename) {
|
|
|
261920 |
FILE *f = fopen(filename, "w");
|
|
|
261920 |
if (!f) {
|
|
|
261920 |
printf("cannot open file for write: %s\n", filename);
|
|
|
261920 |
return;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
writeUnwrapped(r->date->text, f); fputc('\t', f);
|
|
|
261920 |
writeUnwrapped(r->text->text, f); fputc('\t', f);
|
|
|
261920 |
writeUnwrapped(r->part->text, f); fputc('\n', f);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
fclose(f);
|
|
|
261920 |
printf("saved into: %s\n", filename);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void updateLayout() {
|
|
|
261920 |
colCount = 0;
|
|
|
261920 |
for(int i = 0; i < partCount; ++i) {
|
|
|
261920 |
parts[i]->index = -1;
|
|
|
261920 |
columns[i].w = 0;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
double y = MARGINY + PADDING;
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
Part *p = r->part;
|
|
|
261920 |
if (p->index < 0) {
|
|
|
261920 |
int idx = 0;
|
|
|
261920 |
while(p->text[idx] == '~') ++idx;
|
|
|
261920 |
while(1) {
|
|
|
261920 |
int found = TRUE;
|
|
|
261920 |
for(int j = i+1; j < recCount; ++j)
|
|
|
261920 |
if (records[j].part->index == idx)
|
|
|
261920 |
found = FALSE;
|
|
|
261920 |
if (found) break;
|
|
|
261920 |
++idx;
|
|
|
261920 |
}
|
|
|
261920 |
p->index = idx;
|
|
|
261920 |
p->begin = i;
|
|
|
261920 |
p->y0 = y;
|
|
|
261920 |
|
|
|
261920 |
if (colCount <= idx) colCount = idx + 1;
|
|
|
261920 |
if (columns[idx].w < p->w) columns[idx].w = p->w;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
if (i > 0) {
|
|
|
261920 |
if (p == records[i-1].part) {
|
|
|
261920 |
y += SEPARATOR;
|
|
|
261920 |
} else {
|
|
|
261920 |
for(int j = i-1; j >= 0; --j) {
|
|
|
261920 |
if (records[j].part->index == p->index) {
|
|
|
261920 |
double py = records[j].part->y1 + 2*PADDING + MARGINY;
|
|
|
261920 |
if (y < py) y = py;
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
r->y = y;
|
|
|
261920 |
p->end = i + 1;
|
|
|
261920 |
if (p->begin == i) p->y0 = r->y;
|
|
|
261920 |
p->y1 = y + r->text->h;
|
|
|
261920 |
|
|
|
261920 |
y = p->y1;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
double x = MARGINX + PADDING + datesWidth + PADDING + MARGINLINE0 + MARGINLINE1 + MARGINX;
|
|
|
261920 |
for(int i = 0; i < colCount; ++i) {
|
|
|
261920 |
columns[i].x = x;
|
|
|
261920 |
x += columns[i].w + 2*PADDING + MARGINX;
|
|
|
261920 |
}
|
|
|
261920 |
fullWidth = x;
|
|
|
261920 |
fullHeight = y + PADDING + MARGINY;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void drawRect(double x, double y, double w, double h) {
|
|
|
261920 |
noFill();
|
|
|
261920 |
stroke(COLOR_WHITE);
|
|
|
261920 |
strokeWidth(4);
|
|
|
261920 |
rectRounded( x - PADDING, y - PADDING, w + 2*PADDING, h + 2*PADDING, PADDING );
|
|
|
261920 |
fill(colorByRGBA(1, 1, 1, 0.75));
|
|
|
261920 |
stroke(COLOR_BLUE);
|
|
|
261920 |
strokeWidth(2);
|
|
|
261920 |
rectRounded( x - PADDING, y - PADDING, w + 2*PADDING, h + 2*PADDING, PADDING );
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void drawRecords() {
|
|
|
261920 |
saveState();
|
|
|
261920 |
|
|
|
261920 |
strokeWidth(2);
|
|
|
261920 |
stroke(COLOR_BLUE);
|
|
|
261920 |
|
|
|
261920 |
double mr = PADDING/4.0;
|
|
|
261920 |
|
|
|
261920 |
// lines
|
|
|
261920 |
double xl = MARGINX + PADDING + datesWidth + PADDING + MARGINLINE0;
|
|
|
261920 |
double xd = xl - MARGINLINE0;
|
|
|
261920 |
double h = 0;
|
|
|
261920 |
if (recCount > 0)
|
|
|
261920 |
h = records[recCount-1].y + records[recCount-1].text->h + MARGINY + PADDING;
|
|
|
261920 |
line(xl, 0, xl, h);
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
double x = columns[ r->part->index ].x;
|
|
|
261920 |
double y = r->y + BASELINE;
|
|
|
261920 |
line(xd, y, x, y);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
// text rects
|
|
|
261920 |
for(int i = 0; i < partCount; ++i) {
|
|
|
261920 |
Part *p = parts[i];
|
|
|
261920 |
Column *c = &columns[p->index];
|
|
|
261920 |
drawRect(c->x + PADDING, p->y0, p->w, p->y1 - p->y0);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
// circles
|
|
|
261920 |
fill(COLOR_WHITE);
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
double x = columns[ r->part->index ].x;
|
|
|
261920 |
double y = r->y + BASELINE;
|
|
|
261920 |
double mrr = i == curRec ? mr + curFade*mr : mr;
|
|
|
261920 |
circle(xl, y, mrr);
|
|
|
261920 |
circle(x, y, mrr);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
// texts
|
|
|
261920 |
noFill();
|
|
|
261920 |
stroke(COLOR_BLACK);
|
|
|
261920 |
for(int i = 0; i < recCount; ++i) {
|
|
|
261920 |
Record *r = &records[i];
|
|
|
261920 |
Part *p = r->part;
|
|
|
261920 |
Date *d = r->date;
|
|
|
261920 |
double x = columns[ p->index ].x + PADDING;
|
|
|
261920 |
double y = r->y;
|
|
|
261920 |
text(x, y, r->text->text);
|
|
|
261920 |
text(xd - PADDING - d->w, y, d->text);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
restoreState();
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void saveImage(const char *filename) {
|
|
|
261920 |
double s = 1;
|
|
|
261920 |
Framebuffer fb = createFramebuffer((int)(fullWidth*s) + 1, (int)(fullHeight*s) + 1);
|
|
|
261920 |
|
|
|
261920 |
saveState();
|
|
|
261920 |
target(fb);
|
|
|
261920 |
clear();
|
|
|
261920 |
zoom(s);
|
|
|
261920 |
drawRecords();
|
|
|
261920 |
if (viewportSave(filename))
|
|
|
261920 |
printf("image saved into: %s\n", filename);
|
|
|
261920 |
restoreState();
|
|
|
261920 |
|
|
|
261920 |
framebufferDestroy(fb);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void init() {
|
|
|
261920 |
if (fileExists("data/output/history/ancient-dates.txt"))
|
|
|
261920 |
load("data/output/history/ancient-dates.txt");
|
|
|
261920 |
else
|
|
|
261920 |
load("data/history/ancient-dates.txt");
|
|
|
261920 |
|
|
|
261920 |
dx = -fullWidth/2;
|
|
|
261920 |
dy = -450;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
void draw() {
|
|
|
261920 |
double w = windowGetWidth();
|
|
|
261920 |
double h = windowGetHeight();
|
|
|
261920 |
|
|
|
261920 |
double pmx = mx, pmy = my;
|
|
|
261920 |
mx = mouseX(), my = mouseY();
|
|
|
261920 |
|
|
|
261920 |
if (mouseDown("left")) {
|
|
|
261920 |
dx += (mx - pmx)/zm;
|
|
|
261920 |
dy += (my - pmy)/zm;
|
|
|
261920 |
} else
|
|
|
261920 |
if (mouseDown("right")) {
|
|
|
261920 |
zm *= pow(2.0, -(my - pmy)/500.0);
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
if (keyEventGetCount(KEYEVENT_KEY_DOWN))
|
|
|
261920 |
curFade = 1;
|
|
|
261920 |
|
|
|
261920 |
if (keyWentDown("up") && curRec > 0) {
|
|
|
261920 |
if (keyDown("any ctrl")) {
|
|
|
261920 |
Record r;
|
|
|
261920 |
memcpy(&r, &records[curRec], sizeof(r));
|
|
|
261920 |
memcpy(&records[curRec], &records[curRec-1], sizeof(r));
|
|
|
261920 |
memcpy(&records[curRec-1], &r, sizeof(r));
|
|
|
261920 |
updateLayout();
|
|
|
261920 |
}
|
|
|
261920 |
--curRec;
|
|
|
261920 |
} else
|
|
|
261920 |
if (keyWentDown("down") && curRec < recCount-1) {
|
|
|
261920 |
if (keyDown("any ctrl")) {
|
|
|
261920 |
Record r;
|
|
|
261920 |
memcpy(&r, &records[curRec], sizeof(r));
|
|
|
261920 |
memcpy(&records[curRec], &records[curRec+1], sizeof(r));
|
|
|
261920 |
memcpy(&records[curRec+1], &r, sizeof(r));
|
|
|
261920 |
updateLayout();
|
|
|
261920 |
}
|
|
|
261920 |
++curRec;
|
|
|
261920 |
} else
|
|
|
261920 |
if (keyWentDown("s")) {
|
|
|
261920 |
save("data/output/history/ancient-dates.txt");
|
|
|
261920 |
} else
|
|
|
261920 |
if (keyWentDown("i")) {
|
|
|
261920 |
curFade = 0;
|
|
|
261920 |
saveImage("data/output/history/ancient-dates.png");
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
saveState();
|
|
|
261920 |
translate(w/2, h/2);
|
|
|
261920 |
zoom(zm);
|
|
|
261920 |
translate(dx, dy);
|
|
|
261920 |
|
|
|
261920 |
drawRecords();
|
|
|
261920 |
restoreState();
|
|
|
261920 |
|
|
|
261920 |
curFade -= windowGetFrameTime()*2;
|
|
|
261920 |
if (curFade < 0) curFade = 0;
|
|
|
261920 |
}
|
|
|
261920 |
|
|
|
261920 |
|
|
|
261920 |
int main() {
|
|
|
261920 |
windowSetResizable(TRUE);
|
|
|
261920 |
windowSetVariableFrameRate();
|
|
|
261920 |
windowSetInit(&init);
|
|
|
261920 |
windowSetDraw(&draw);
|
|
|
261920 |
windowRun();
|
|
|
261920 |
return 0;
|
|
|
261920 |
}
|
|
|
261920 |
|