Blob Blame History Raw

#include "puzzle.h"



unsigned int chunkCalcEdges(Chunk *c) {
  unsigned int edges = 0;
  if (c->c   <= 0           || c->pz->chunks[c->r][c->c-1].parent != c->parent) edges |= PF_BORDER_L;
  if (c->c+1 >= c->pz->cols || c->pz->chunks[c->r][c->c+1].parent != c->parent) edges |= PF_BORDER_R;
  if (c->r   <= 0           || c->pz->chunks[c->r-1][c->c].parent != c->parent) edges |= PF_BORDER_T;
  if (c->r+1 >= c->pz->rows || c->pz->chunks[c->r+1][c->c].parent != c->parent) edges |= PF_BORDER_B;
  return edges;
}


void chunkDrawBase(Chunk *c, unsigned int edges) {
  int levels = 4;
  Vec gp = chunkGroupPos(c);
  
  saveState();
  noFill();
  strokeWidth( mind(c->pz->cs.x, c->pz->cs.y)*0.025 );
  translate(-gp.x, -gp.y);
  if (edges & PF_BORDER_T) lineDraw(c->pz->hlines[c->r + 0] + (c->c + 0)*LN_SEGS,  1, levels);
  if (edges & PF_BORDER_R) lineDraw(c->pz->vlines[c->c + 1] + (c->r + 0)*LN_SEGS,  1, levels);
  if (edges & PF_BORDER_B) lineDraw(c->pz->hlines[c->r + 1] + (c->c + 1)*LN_SEGS, -1, levels);
  if (edges & PF_BORDER_L) lineDraw(c->pz->vlines[c->c + 0] + (c->r + 1)*LN_SEGS, -1, levels);
  restoreState();

  saveState();
  translate(-gp.x, -gp.y);
  /*strokeWidth( mind(c->pz->cs.x, c->pz->cs.y)*0.1 );
  stroke(COLOR_WHITE);
  strokeTexture(c->pz->image, 0, 0, c->pz->cols*c->pz->cs.x, c->pz->rows*c->pz->cs.x, FALSE);
  if (edges & PF_BORDER_T)    lineDraw(c->pz->hlines[c->r + 0] + (c->c + 0)*LN_SEGS,  1, levels);
  if (edges & PF_BORDER_R)  lineDraw(c->pz->vlines[c->c + 1] + (c->r + 0)*LN_SEGS,  1, levels);
  if (edges & PF_BORDER_B) lineDraw(c->pz->hlines[c->r + 1] + (c->c + 1)*LN_SEGS, -1, levels);
  if (edges & PF_BORDER_L)   lineDraw(c->pz->vlines[c->c + 0] + (c->r + 1)*LN_SEGS, -1, levels);*/
  noStroke();
  fill(COLOR_WHITE);
  fillTexture(c->pz->image, 0, 0, c->pz->cols*c->pz->cs.x, c->pz->rows*c->pz->cs.y, FALSE);
  linePut(c->pz->hlines[c->r + 0] + (c->c + 0)*LN_SEGS,  1, levels);
  linePut(c->pz->vlines[c->c + 1] + (c->r + 0)*LN_SEGS,  1, levels);
  linePut(c->pz->hlines[c->r + 1] + (c->c + 1)*LN_SEGS, -1, levels);
  linePut(c->pz->vlines[c->c + 0] + (c->r + 1)*LN_SEGS, -1, levels);
  closePath();
  restoreState();
}


void chunkDraw(Chunk *c) {
  saveState();
  Vec p = chunkWorldPos(c);
  translate(p.x, p.y);
  rotate(vangled(c->parent->dx));
  if (c->t.tex && c->t.ts > 1e-5 && (c->flags & PF_RENDERED)) {
    translate(-c->t.s/2, -c->t.s/2);
    noStroke();
    double k = c->t.s/c->t.ts;
    fillTexture(c->t.tex, -c->t.tp.x*k, -c->t.tp.y*k, k, k, FALSE);
    rect(0, 0, c->t.s, c->t.s);
  } else {
    chunkDrawBase(c, chunkCalcEdges(c));
  }
  restoreState();
}


void chunkDrawDebug(Chunk *c) {
  saveState();
  Vec p = chunkWorldPos(c);
  translate(p.x, p.y);
  rotate(vangled(c->parent->dx));
  stroke(randColorPtr(c->parent));
  Vec hs = vdiv(c->pz->cs, 2);
  if (c->flags & PF_HANDLE_L) line(-hs.x, -hs.y, -hs.x,  hs.y);
  if (c->flags & PF_HANDLE_R) line( hs.x, -hs.y,  hs.x,  hs.y);
  if (c->flags & PF_HANDLE_T) line(-hs.x, -hs.y,  hs.x, -hs.y);
  if (c->flags & PF_HANDLE_B) line(-hs.x,  hs.y,  hs.x,  hs.y);
  restoreState();
}


void chunkRender(Chunk *c) {
  if (!c->t.fb || !(c->t.ts > 1e-5)) return;

  unsigned int edges = PF_RENDERED | chunkCalcEdges(c);
  if ((c->flags & PF_RENDERING) == edges) return;
  
  saveState();
  target(c->t.fb);
  resetStateEx(STATE_TRANSFORM);

  int ix = (int)round(c->t.tp.x*TM_SIZE);
  int iy = (int)round(c->t.tp.y*TM_SIZE);
  int s = (int)round(c->t.ts*TM_SIZE);
  background(COLOR_TRANSPARENT);
  glEnable(GL_SCISSOR_TEST);
  glScissor(ix, iy, s, s);
  clear();
  glScissor(0, 0, TM_SIZE, TM_SIZE);
  glDisable(GL_SCISSOR_TEST);

  double k = c->t.s/c->t.ts;
  zoom(TM_SIZE/k);
  translate(c->t.tp.x*k + c->t.s/2, c->t.tp.y*k + c->t.s/2);
  cliprect(-c->t.s/2, -c->t.s/2, c->t.s, c->t.s);
  chunkDrawBase(c, edges);
  c->flags |= edges;

  restoreState();
}

#include <assert.h>
void chunkMerge(Chunk *a, Chunk *b) {
  if (a->parent == b->parent) return;
  Puzzle *pz = a->pz;
  PhGroup *pa = a->parent, *pb = b->parent;
  phGroupMerge(pa, pb);
  
  for(int r = 0; r < pz->rows; ++r)
  for(int c = 0; c < pz->cols; ++c) {
    Chunk *chunk = &pz->chunks[r][c];
    if (chunk->parent == pa || chunk->parent == pb) chunk->flags &= ~PF_HANDLES;
    if (chunk->parent != pb) continue;
    chunk->parent = pa;
    chunkRender(chunk);
    if (c   > 0       ) chunkRender(&pz->chunks[r][c-1]);
    if (c+1 < pz->cols) chunkRender(&pz->chunks[r][c+1]);
    if (r   > 0       ) chunkRender(&pz->chunks[r-1][c]);
    if (r+1 < pz->rows) chunkRender(&pz->chunks[r+1][c]);
  }
  
  int c0 = pz->cols-1, c1 = 0;
  for(int r = 0; r < pz->rows; ++r) {
    Chunk *first = NULL, *last = NULL;
    for(int c = 0; c < pz->cols; ++c) {
      Chunk *chunk = &pz->chunks[r][c];
      if (chunk->parent != pa) continue;
      if (c <= c0) { first = chunk; c0 = c; }
      if (c >= c1) { last  = chunk; c1 = c; }
    }
    if (first) first->flags |= PF_HANDLE_L;
    if (last)  last->flags  |= PF_HANDLE_R;
  }

  c0 = pz->cols-1, c1 = 0;
  for(int r = pz->rows-1; r >= 0; --r) {
    Chunk *first = NULL, *last = NULL;
    for(int c = 0; c < pz->cols; ++c) {
      Chunk *chunk = &pz->chunks[r][c];
      if (chunk->parent != pa) continue;
      if (c <= c0) { first = chunk; c0 = c; }
      if (c >= c1) { last  = chunk; c1 = c; }
    }
    if (first) first->flags |= PF_HANDLE_L;
    if (last)  last->flags  |= PF_HANDLE_R;
  }
  
  int r0 = pz->rows-1, r1 = 0;
  for(int c = 0; c < pz->cols; ++c) {
    Chunk *first = NULL, *last = NULL;
    for(int r = 0; r < pz->rows; ++r) {
      Chunk *chunk = &pz->chunks[r][c];
      if (chunk->parent != pa) continue;
      if (r <= r0) { first = chunk; r0 = r; }
      if (r >= r1) { last  = chunk; r1 = r; }
    }
    if (first) first->flags |= PF_HANDLE_T;
    if (last)  last->flags  |= PF_HANDLE_B;
  }

  r0 = pz->rows-1, r1 = 0;
  for(int c = pz->cols-1; c >= 0; --c) {
    Chunk *first = NULL, *last = NULL;
    for(int r = 0; r < pz->rows; ++r) {
      Chunk *chunk = &pz->chunks[r][c];
      if (chunk->parent != pa) continue;
      if (r <= r0) { first = chunk; r0 = r; }
      if (r >= r1) { last  = chunk; r1 = r; }
    }
    if (first) first->flags |= PF_HANDLE_T;
    if (last)  last->flags  |= PF_HANDLE_B;
  }
}


void chunkTryMerge(Chunk *a, int dr, int dc) {
  int r = a->r + dr;
  int c = a->c + dc;
  Puzzle *pz = a->pz;
  if (r < 0 || c < 0 || r >= pz->rows || c >= pz->cols) return;
  
  Chunk *b = &pz->chunks[r][c];
  if (a->parent == b->parent) return;
  
  Vec gp = vadd( chunkGroupPos(a), vdiv(vec(dc,dr), 2));
  Vec pa = phGroupTrans(a->parent, gp);
  Vec pb = phGroupTrans(b->parent, gp);
  double d2 = vdist2(pb, pa);

  if (d2 < pz->cs.x*pz->cs.y/16)
    chunkMerge(a, b);
}


Flags chunkCheckPoint(Chunk *c, Vec p) {
  Vec hs = vdiv(c->pz->cs, 2);
  double hbs = mind(hs.x, hs.y);
  Vec hs0 = vsub(hs, vecxy(hbs*0.25));
  Vec hs1 = vadd(hs, vecxy(hbs*0.50));
  Vec gp = vsub(phGroupUntrans(c->parent, p), chunkGroupPos(c));
  int l = c->flags & (gp.x < 0 ? PF_HANDLE_L : PF_HANDLE_R);
  int t = c->flags & (gp.y < 0 ? PF_HANDLE_T : PF_HANDLE_B);
  gp = vabs(gp);

  int inx = gp.x < hs0.x;
  int iny = gp.y < hs0.y;
  if (!(gp.x < hs1.x) || !(gp.y < hs1.y)) return 0;
  if (inx && iny) return PCP_BODY;
  
  Flags res = 0;
  if (gp.x < hs.x && gp.y < hs.y) res |= PCP_BODY;
  double r = hs1.x - hs.x;
  if ( (l && !inx && gp.y < hs.y)
    || (t && !iny && gp.x < hs.x)
    || (l && t && !inx && !iny && vdist2(gp, hs) < r*r) ) res |= PCP_EDGE;
  return res;
}