Blame onefile/history-dates.c

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