1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-06-22 21:23:18 +00:00
hyperrogue/netgen.cpp
Arthur O'Dwyer dae6b9e3d3 Consistently use "space" for the "pop this menu" option in all submenus.
The "pop this menu" option's *text* is still highly variable among all
the different menus, which could make it hard to navigate, but at least
if you're using the keyboard it's now easy to "go back".

Plus, the rogueviz menu incorrectly reported "(v) exit menu" when actually
`v` was already in use by an earlier option. This is now fixed.
2017-10-30 18:48:14 -07:00

710 lines
18 KiB
C++

// HyperRogue paper model generator
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#if CAP_MODEL
namespace netgen {
// We need a two-dimensional vector class for this.
struct vec {
double x, y;
vec(double _x, double _y) : x(_x), y(_y) { }
vec() : x(0), y(0) {}
};
vec& operator += (vec& a, const vec b) { a.x += b.x; a.y += b.y; return a; }
vec& operator -= (vec& a, const vec b) { a.x -= b.x; a.y -= b.y; return a; }
// coordinatewise multiplication and division
vec& operator *= (vec& a, const vec b) { a.x *= b.x; a.y *= b.y; return a; }
vec& operator *= (vec& a, double scalar) { a.x *= scalar; a.y *= scalar; return a; }
vec& operator /= (vec& a, const vec b) { a.x /= b.x; a.y /= b.y; return a; }
vec& operator /= (vec& a, double scalar) { a.x /= scalar; a.y /= scalar; return a; }
vec operator + (vec a, const vec b) { return a+=b; }
vec operator - (vec a, const vec b) { return a-=b; }
vec operator * (vec a, const vec b) { return a*=b; }
vec operator / (vec a, const vec b) { return a/=b; }
vec operator * (vec a, double scalar) { return a*=scalar; }
vec operator * (double scalar, vec a) { return a*=scalar; }
vec operator / (vec a, double scalar) { return a/=scalar; }
vec operator / (double scalar, vec a) { return a/=scalar; }
vec ang(double f) { return vec(cos(f), sin(f)); }
double norm(vec v) { return v.x*v.x+v.y*v.y; }
// the parameters.
bool loaded;
int SCALE, PX, PY, BASE, SX, SY, CELLS, fontsize, created;
double el;
#define MAXCELLS 1000
// All the datatables stored in the net files.
int ct[MAXCELLS];
double vx[MAXCELLS][16];
vec center[MAXCELLS];
double rot[MAXCELLS];
int glued[MAXCELLS];
int nei[MAXCELLS][7];
// auxiliary data
double raylen[MAXCELLS];
double edgist[MAXCELLS];
char patek[MAXCELLS][7];
// data generated by HyperRogue
hyperpoint hcenter[MAXCELLS][8];
// Functions handling the data.
//==============================
// Use HyperRogue to generate the data (ct, vx, nei).
int mode = 0;
void buildVertexInfo(cell *c, transmatrix V) {
if(mode == 1)
for(int ii=0; ii<CELLS; ii++) if(dcal[ii] == c) {
hcenter[ii][7] = V * C0;
if(c->type == 7) {
for(int i=0; i<c->type; i++) {
int hdir = displaydir(c, i) + 6;
transmatrix V2 = V * spin(hdir * M_PI / 42) * xpush(hexf);
hcenter[ii][i] = V2 * C0;
}
}
if(c->type == 6) {
for(int i=0; i<c->type; i++) {
int hdir = displaydir(c, i);
transmatrix V2 =
V * spin(hdir * M_PI / 42) * xpush(crossf) * spin(M_PI*8/7) * xpush(hexf);
hcenter[ii][i] = V2 * C0;
}
}
}
}
void dataFromHR() {
mode = 1;
drawthemap();
mode = 0;
for(int i=0; i<CELLS; i++) {
ct[i] = dcal[i]->type;
for(int k=0; k<8; k++)
vx[i][2*k] = hcenter[i][k][0],
vx[i][2*k+1] = hcenter[i][k][1];
for(int k=0; k<ct[i]; k++) nei[i][k] = -1;
for(int j=0; j<CELLS; j++) {
cell *c1 = dcal[i];
cell *c2 = dcal[j];
for(int k=0; k<c1->type; k++) if(c1->mov[k] == c2)
nei[i][k] = j;
}
}
for(int i=0; i<CELLS; i++) {
center[i] = vec(SX/2, SY/2);
rot[i] = 0;
glued[i] = -1;
for(int e=0; e<ct[i]; e++)
if(nei[i][e] < i && nei[i][e] != -1 && (glued[i] == -1 || nei[i][e] < glued[i])) {
glued[i] = nei[i][e];
}
}
}
void loadData() {
FILE *f = fopen("papermodeldata.txt", "rt");
if(!f) return;
int err = fscanf(f, "%d %d %d %d %d %d %d %lf %d\n\n",
&CELLS, &SX, &SY, &PX, &PY, &SCALE, &BASE, &el, &created);
if(err != 9) { fclose(f); return; }
loaded = true;
if(!created) { fclose(f); return; }
for(int i=0; i<CELLS; i++) err = fscanf(f, "%d", &ct[i]);
for(int i=0; i<CELLS; i++) for(int j=0; j<16; j++)
err = fscanf(f, "%lf" ,&vx[i][j]);
for(int i=0; i<CELLS; i++)
for(int j=0; j<7; j++) nei[i][j] = -1;
while(true) {
int a, b, c;
err = fscanf(f, "%d%d%d", &a, &b, &c);
if(a < 0) break;
else nei[a][c] = b;
}
for(int i=0; i<CELLS; i++) {
double dx, dy, dr;
int g;
err = fscanf(f, "%lf%lf%lf%d\n", &dx, &dy, &dr, &g);
center[i] = vec(dx, dy);
rot[i] = dr;
glued[i] = g;
}
fclose(f);
}
void saveData() {
// global parameters
FILE *f = fopen("papermodeldata2.txt", "wt");
if(!f) {
addMessage("Could not save the paper model data");
return;
}
fprintf(f, "%d %d %d %d %d %d %d %lf %d\n\n", CELLS, SX, SY, PX, PY, SCALE, BASE, el, created);
// net parameters: cell types
for(int i=0; i<CELLS; i++)
fprintf(f, "%d ", ct[i]);
fprintf(f, "\n");
// net parameters: hcenters
for(int i=0; i<CELLS; i++) {
for(int k=0; k<16; k++)
fprintf(f, "%9.6lf ", vx[i][k]);
fprintf(f, "\n");
}
fprintf(f, "\n\n");
// create netgen
for(int i=0; i<CELLS; i++) for(int j=0; j<CELLS; j++) {
for(int k=0; k<ct[i]; k++) if(nei[i][k] == j)
fprintf(f, "%d %d %d ", i, j, k);
}
fprintf(f, "-1 -1 -1\n\n");
// graphics
for(int i=0; i<CELLS; i++)
fprintf(f, "%12.7lf %12.7lf %10.7lf %d\n",
center[i].x, center[i].y, rot[i], glued[i]
);
fclose(f);
}
// Simple graphical functions
//============================
void blackline(vec v1, vec v2, int col = 0x000000FF) {
#if CAP_SDLGFX==1
aalineColor(s, int(v1.x), int(v1.y), int(v2.x), int(v2.y), col);
#endif
}
void drawtriangle(vec v1, vec v2, vec v3, int col) {
#if CAP_SDLGFX==1
polyx[0] = int(v1.x);
polyx[1] = int(v2.x);
polyx[2] = int(v3.x);
polyy[0] = int(v1.y);
polyy[1] = int(v2.y);
polyy[2] = int(v3.y);
filledPolygonColorI(s, polyx, polyy, 3, col);
#endif
}
void blackcircle(vec v, int r, int col = 0x000000FF) {
#if CAP_SDLGFX
aacircleColor(s, int(v.x), int(v.y), r, col);
#endif
}
void blacktext(vec v, char c) {
char str[2]; str[0] = c; str[1] = 0;
int tsize = int(el * 12/27);
displaystr(int(v.x), int(v.y), 0, tsize, str, 0, 8);
}
hyperpoint hvec(int i, int e) {
return hpxy(vx[i][2*e], vx[i][2*e+1]);
}
bool wellspread(double d1, double d2, double d3, int &co) {
int id1 = int(d1);
int id2 = int(d2);
int id3 = int(d3);
co = min(min(id1,id2),id3);
return (id1 <= co+1 && id2 <= co+1 && id3 <= co+1);
}
SDL_Surface *net, *hqsurface;
int& hqpixel(hyperpoint h) {
int hx, hy, hs;
getcoord0(h, hx, hy, hs);
return qpixel(hqsurface, hx, hy);
}
void copyhypertriangle(
vec g1, vec g2, vec g3,
hyperpoint h1, hyperpoint h2, hyperpoint h3) {
int ix, iy;
if(wellspread(g1.x,g2.x,g3.x,ix) && wellspread(g1.y,g2.y,g3.y,iy))
qpixel(net,ix,iy) = hqpixel(h1);
else {
vec g4 = (g2+g3)/2;
vec g5 = (g3+g1)/2;
vec g6 = (g1+g2)/2;
hyperpoint h4 = mid(h2,h3);
hyperpoint h5 = mid(h3,h1);
hyperpoint h6 = mid(h1,h2);
copyhypertriangle(g1,g5,g6, h1,h5,h6);
copyhypertriangle(g5,g3,g4, h5,h3,h4);
copyhypertriangle(g6,g4,g2, h6,h4,h2);
copyhypertriangle(g4,g6,g5, h4,h6,h5);
}
}
void setRaylen() {
for(int i=0; i<CELLS; i++) {
raylen[i] = el / sin(M_PI / ct[i]);
edgist[i] = raylen[i] * cos(M_PI / ct[i]);
}
}
// draw the model
void createPapermodel() {
#if !CAP_SDLGFX
addMessage(XLAT("High quality shots not available on this platform"));
return;
#endif
loadData();
SDL_Surface *sav = s;
s = hqsurface = SDL_CreateRGBSurface(SDL_SWSURFACE,BASE,BASE,32,0,0,0,0);
videopar vid2 = vid;
vid.xres = vid.yres = 2000; vid.scale = 0.99; vid.usingGL = false;
int sch = cheater; cheater = 0;
calcparam();
mode = 2;
darken = 0;
SDL_FillRect(s, NULL, 0);
drawfullmap();
mode = 0;
/* for(int i=0; i<CELLS; i++) {
int t = ct[i];
for(int e=0; e<t; e++)
drawline(hvec(i,e), hvec(i,(e+1)%t), 0x80808080);
for(int e=0; e<7; e++)
drawline(hvec(i,e), hvec(i,7), 0x80808080);
} */
s = net = SDL_CreateRGBSurface(SDL_SWSURFACE,SX*SCALE,SY*SCALE,32,0,0,0,0);
SDL_FillRect(net, NULL, 0xFFFFFF);
int pateks = 0;
int zeroi = nei[0][0];
int zeroe = 0;
for(int e=0; e<6; e++) if(nei[zeroi][e] == 0) zeroe = e;
el *= SCALE;
setRaylen();
for(int faza=0; faza<2; faza++) for(int i=0; i<CELLS; i++) {
int t = ct[i];
printf("faza %d cell %d\n", faza, i);
for(int e=0; e<t; e++) {
vec v1 = center[i] * SCALE + raylen[i] * ang(rot[i] + 2*M_PI*e/t);
vec v2 = center[i] * SCALE + raylen[i] * ang(rot[i] + 2*M_PI*(e+1)/t);
vec v3 = (v1+v2)/2;
if(faza == 1) blackline(v1, v2);
int ofs = t == 7 ? 0 : 5;
// 0,2,0 ~ 2,0,0
if(0) if((i==0 && e == 0) || (i == zeroi && e == zeroe)) {
for(int ofs=0; ofs<t; ofs++) {
printf("OFS %d: %s", ofs, display(hvec(i, (e+ofs)%t)));
printf(" %s\n", display(hvec(i, (e+1+ofs)%t)));
}
}
if(faza == 0) copyhypertriangle(
center[i] * SCALE, v1, v2,
hvec(i,7), hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t)
);
if(faza == 1)
if(nei[i][e] != -1 && nei[i][e] != glued[i] && glued[nei[i][e]] != i) {
vec vd = v2-v1;
swap(vd.x, vd.y); vd.x = -vd.x;
double factor = -sqrt(3)/6;
vd.x *= factor;
vd.y *= factor;
vec v4 = v3 + vd;
vec v5 = v3 + vd/2;
if(!patek[i][e]) {
int i2 = nei[i][e];
for(int e2=0; e2<ct[nei[i][e]]; e2++) if(nei[i2][e2] == i)
patek[i][e] = patek[i2][e2] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
"!@#$%^&*+=~:;<>?/|\"., [{(\\]})" [(pateks++) % 85];
}
int col = 0xFFFFFFFF;
int p = patek[i][e];
col -= 0x8000 * (p&1); p /= 2;
col -= 0x800000 * (p&1); p /= 2;
col -= 0x80000000 * (p&1); p /= 2;
col -= 0x4000 * (p&1); p /= 2;
col -= 0x400000 * (p&1); p /= 2;
col -= 0x40000000 * (p&1); p /= 2;
col -= 0x2000 * (p&1); p /= 2;
col -= 0x200000 * (p&1); p /= 2;
col -= 0x20000000 * (p&1); p /= 2;
drawtriangle(v1,v2,v4, col);
blacktext(v5, patek[i][e]);
blackline(v1, v4);
blackline(v2, v4);
}
}
}
printf("pateks = %d\n", pateks);
IMAGESAVE(net, "papermodel-all" IMAGEEXT);
IMAGESAVE(hqsurface, "papermodel-source" IMAGEEXT);
int qx = SX*SCALE/PX;
int qy = SY*SCALE/PY;
SDL_Surface *quarter = SDL_CreateRGBSurface(SDL_SWSURFACE,qx,qy,32,0,0,0,0);
for(int iy=0; iy<PY; iy++)
for(int ix=0; ix<PX; ix++) {
for(int y=0; y<qy; y++) for(int x=0; x<qx; x++)
qpixel(quarter,x,y) = qpixel(net, x+qx*ix, y+qy*iy);
char buf[64];
sprintf(buf, "papermodel-page%d%d" IMAGEEXT, iy, ix);
IMAGESAVE(quarter, buf);
}
SDL_FreeSurface(net);
SDL_FreeSurface(hqsurface);
SDL_FreeSurface(quarter);
s = sav; vid = vid2; cheater = sch;
}
vec mousepos, rel;
int bei = 0, bee = 0, whichcell = 0;
double cedist;
bool dragging = false;
int glueroot(int i) {
if(glued[i] == -1) return i;
return glueroot(glued[i]);
}
void clicked(int x, int y, int b) {
mousepos = vec(x, y);
if(b == 1)
rel = center[glueroot(whichcell)] - mousepos,
dragging = true;
if(b == 17)
dragging = false;
if(b == 32 && dragging)
center[glueroot(whichcell)] = rel + mousepos;
}
void applyGlue(int i) {
int j = glued[i];
int it = ct[i];
int jt = ct[j];
int ie = 0, je = 0;
for(int e=0; e<it; e++) if(nei[i][e] == j) ie = e;
for(int e=0; e<jt; e++) if(nei[j][e] == i) je = e;
rot[i] = rot[j] + 2*M_PI*(je+.5)/jt - 2*M_PI*(ie+.5)/it + M_PI;
center[i] =
center[j] +
(edgist[i]+edgist[j]) * ang(rot[j] + 2*M_PI*(je+.5)/jt);
}
void displaynets() {
SDL_LockSurface(s);
setRaylen();
for(int uy=SY-1; uy>=0; uy--)
for(int ux=SX-1; ux>=0; ux--) {
qpixel(s, ux, uy) = 0;
}
for(int y=1; y<PY; y++)
blackline(vec(0,SY*y/PY), vec(SX,SY*y/PY), 0x404080FF);
for(int x=1; x<PX; x++)
blackline(vec(SX*x/PX,0), vec(SX*x/PX,SY), 0x404080FF);
for(int i=0; i<CELLS; i++) {
if(norm(center[i]-mousepos) < norm(center[whichcell]-mousepos))
whichcell = i;
int t = ct[i];
if(i == whichcell)
blackcircle(center[i], 10, 0x40FF40FF);
if(i == bei || i == nei[bei][bee])
blackcircle(center[i], 5, 0x40FF40FF);
if(glued[i] == -1)
blackcircle(center[i], 7, 0xFF4040FF);
if(glued[i] != -1)
applyGlue(i);
for(int e=0; e<t; e++) {
vec v1 = center[i] + raylen[i] * ang(rot[i] + 2*M_PI*e/t);
vec v2 = center[i] + raylen[i] * ang(rot[i] + 2*M_PI*(e+1)/t);
vec v3 = (v1+v2)/2;
if(nei[i][e] >= 0 && !dragging) {
if(norm(v3-mousepos) < cedist) bei = i, bee = e;
if(i == bei && e == bee) cedist = norm(v3-mousepos);
}
int col =
i == bei && e == bee ? 0x40FF40FF:
i == nei[bei][bee] && nei[i][e] == bei ? 0x40FF40FF :
nei[i][e] == glued[i] ? 0x303030FF :
glued[nei[i][e]] == i ? 0x303030FF :
nei[i][e] >= 0 ? 0xC0C0C0FF :
0x808080FF;
blackline(v1, v2, col);
if(nei[i][e] != -1 && nei[i][e] != glued[i] && glued[nei[i][e]] != i) {
vec vd = v2-v1;
swap(vd.x, vd.y); vd.x = -vd.x;
double factor = -sqrt(3)/6;
vd.x *= factor; vd.y *= factor;
vec v4 = v3 + vd;
blackline(v1, v4, 0xFFC0C0C0);
blackline(v2, v4, 0xFFC0C0C0);
}
}
}
SDL_UnlockSurface(s);
SDL_UpdateRect(s, 0, 0, 0, 0);
}
double rs, rz;
void addglue() {
int i = bei;
int j = nei[bei][bee];
if(glued[i] == j)
glued[i] = -1;
else if(glued[j] == i)
glued[j] = -1;
else if(glueroot(i) == glueroot(j))
;
else if(glued[j] == -1)
glued[j] = i;
}
int nti;
void smooth() {
int ti = SDL_GetTicks();
rot[whichcell] += rs * (nti - ti) / 1000.0;
el += rz * (nti - ti) / 1000.0;
nti = ti;
}
void netgen_loop() {
nti = SDL_GetTicks();
while(true) {
smooth();
displaynets();
SDL_Event event;
while(SDL_PollEvent(&event)) switch (event.type) {
case SDL_QUIT:
exit(1);
return;
case SDL_MOUSEBUTTONDOWN: {
clicked(event.button.x, event.button.y, event.button.button);
break;
}
case SDL_MOUSEBUTTONUP: {
clicked(event.button.x, event.button.y, 16+event.button.button);
break;
}
case SDL_MOUSEMOTION: {
clicked(event.motion.x, event.motion.y, 32);
break;
}
case SDL_KEYDOWN: {
int key = event.key.keysym.sym;
int uni = event.key.keysym.unicode;
if(uni == 'q' || key == SDLK_ESCAPE || key == SDLK_F10)
return;
if(key == SDLK_PAGEUP) rs = 3;
if(key == SDLK_PAGEDOWN) rs = -3;
if(uni == 'z') rz = 1;
if(uni == 'x') rz = -1;
if(uni == 'g') addglue();
break;
}
case SDL_KEYUP: {
rs = 0;
rz = 0;
break;
}
}
}
}
void designNet() {
s = SDL_SetVideoMode(SX, SY, 32, 0);
netgen_loop();
saveData();
setvideomode();
}
void show() {
cmode = sm::SIDE;
gamescreen(0);
if(true) {
for(int i=0; i<CELLS; i++) {
int t = ct[i];
int ofs = t == 7 ? 0 : 5;
for(int e=0; e<t; e++) {
int col =
nei[i][e] == glued[i] && glued[i] >= 0 ? 0x303030 :
nei[i][e] >= 0 && glued[nei[i][e]] == i ? 0x303030 :
nei[i][e] >= 0 ? 0x808080 :
0xC0C0C0;
prettyline(hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t), (col << 8) + 0xFF, 3);
}
}
}
if(mode != 2) {
dialog::init("paper model creator");
dialog::addItem(XLAT("synchronize net and map"), 's');
dialog::addItem(XLAT("display the scope"), 't');
dialog::addItem(XLAT("create the model"), 'c');
dialog::addItem(XLAT("back to HyperRogue"), ' ');
dialog::addItem(XLAT("design the net"), 'd');
dialog::display();
}
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(!loaded) {
loadData();
if(!loaded) {
addMessage(XLAT("Failed to load the file 'papermodeldata.txt'"));
popScreen();
return;
}
if(!created) {
View = Id;
if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
dataFromHR();
designNet();
created = 1;
return;
}
}
if(mode == 2 && uni != 0) {
mode = 0;
return;
}
if(uni == 's') {
View = Id;
if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
}
else if(uni == 'c') {
createPapermodel();
addMessage(XLAT("The paper model created as papermodel-*.bmp"));
}
else if(uni == 'd') designNet();
else if(uni == 't') mode = 2;
else if(doexiton(sym, uni))
popScreen();
};
}
}
#endif