#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;
}