removed the MAX_EDGE limit

This commit is contained in:
Zeno Rogue 2020-01-18 16:03:32 +01:00
parent a6da5ded75
commit 2271a67506
24 changed files with 238 additions and 190 deletions

View File

@ -51,9 +51,11 @@ struct archimedean_tiling {
vector<ld> inradius, circumradius, alphas;
int matches[30][30];
int periods[30];
int tilegroup[30], groupoffset[30], tilegroups;
vector<vector<int>> matches;
vector<int> periods;
vector<int> tilegroup;
vector<int> groupoffset;
int tilegroups;
int errors;
string errormsg;
@ -130,18 +132,21 @@ void archimedean_tiling::make_match(int a, int i, int b, int j) {
periods[a] = periods[b] = gcd(matches[a][b] - (j-i), periods[a]);
}
/** mostly to protect the user from entering too large numbers */
const int MAX_EDGE_ARCM = FULL_EDGE;
void archimedean_tiling::prepare() {
euclidean_angle_sum = 0;
for(int f: faces) euclidean_angle_sum += (f-2.) / f;
for(int i: faces) if(i > MAX_EDGE) {
errormsg = XLAT("currently no more than %1 edges", its(MAX_EDGE));
for(int i: faces) if(i > MAX_EDGE_ARCM) {
errormsg = XLAT("currently no more than %1 edges", its(MAX_EDGE_ARCM));
errors++;
return;
}
if(isize(faces) > MAX_EDGE/2) {
errormsg = XLAT("currently no more than %1 faces in vertex", its(MAX_EDGE/2));
if(isize(faces) > MAX_EDGE_ARCM/2) {
errormsg = XLAT("currently no more than %1 faces in vertex", its(MAX_EDGE_ARCM/2));
errors++;
return;
}
@ -192,8 +197,13 @@ void archimedean_tiling::prepare() {
have_symmetry = false;
for(int i=0; i<N; i++) if(invert[i]) have_symmetry = true;
for(int i=0; i<M; i++) for(int j=0; j<M; j++) matches[i][j] = i==j ? 0 : -1;
matches.resize(M);
for(int i=0; i<M; i++) {
matches[i].resize(M);
for(int j=0; j<M; j++) matches[i][j] = i==j ? 0 : -1;
}
periods.resize(M);
for(int i=0; i<M; i++) periods[i] = i<2*N ? faces[i/2] : N;
for(int i=0; i<N; i++) {
@ -289,7 +299,9 @@ void archimedean_tiling::regroup() {
make_match(i, 0, k, matches[i][j] + matches[j][k]);
make_match(i, 0, k, matches[i][j] + matches[j][k] + gcd(periods[i], periods[j]));
}
for(int i=0; i<M; i++) tilegroup[i] = -1;
tilegroup.clear();
tilegroup.resize(M, -1);
groupoffset.resize(M);
tilegroups = 0;
for(int i=0; i<M; i+=(have_symmetry?1:2)) if(tilegroup[i] == -1) {
if(periods[i] < 0) periods[i] = -periods[i];
@ -335,11 +347,13 @@ void archimedean_tiling::compute_geometry() {
dynamicval<eGeometry> dv(geometry, gArchimedean);
/* compute the geometry */
inradius.resize(N);
inradius.resize(N+1); inradius[N] = 0;
circumradius.resize(N);
alphas.resize(N);
ld elmin = 0, elmax = hyperbolic ? 10 : sphere ? M_PI : 1;
/* inradius[N] is used in farcorner and nearcorner. Probably a bug */
if(real_faces == 2) {
/* standard methods fail for dihedra, but the answer is easy */
edgelength = 2 * M_PI / faces[0];
@ -777,19 +791,19 @@ void fixup_matrix(transmatrix& T, const transmatrix& X, ld step) {
}
pair<ld, ld>& archimedean_tiling::get_triangle(heptagon *h, int cid) {
return triangles[id_of(h)][(parent_index_of(h) + cid + MODFIXER) % neighbors_of(h)];
return triangles[id_of(h)][gmod(parent_index_of(h) + cid, neighbors_of(h))];
}
pair<int, int>& archimedean_tiling::get_adj(heptagon *h, int cid) {
return adjacent[id_of(h)][(parent_index_of(h) + cid + MODFIXER) % neighbors_of(h)];
return adjacent[id_of(h)][gmod(parent_index_of(h) + cid, neighbors_of(h))];
}
pair<int, int>& archimedean_tiling::get_adj(const pair<int, int>& p, int delta) {
return adjacent[p.first][(p.second + delta + MODFIXER) % isize(adjacent[p.first])];
return adjacent[p.first][gmod(p.second + delta, isize(adjacent[p.first]))];
}
pair<ld, ld>& archimedean_tiling::get_triangle(const pair<int, int>& p, int delta) {
return triangles[p.first][(p.second + delta + MODFIXER) % isize(adjacent[p.first])];
return triangles[p.first][gmod(p.second + delta, isize(adjacent[p.first]))];
}
transmatrix adjcell_matrix(heptagon *h, int d) {

View File

@ -841,9 +841,7 @@ EX bool buildBarrierNowall(cell *c, eLand l2, int forced_dir IS(NODIR)) {
bool warpv = warped_version(c->land, l2);
if(warpv && !arcm::in() && !pseudohept(c)) return false;
int ds[MAX_EDGE];
for(int i=0; i<c->type; i++) ds[i] = i;
for(int j=0; j<c->type; j++) swap(ds[j], ds[hrand(j+1)]);
vector<int> ds = hrandom_permutation(c->type);
for(int i=0; i<c->type; i++) {
int d = forced_dir != NODIR ? forced_dir : (valence()>3) ? (2+(i&1)) : ds[i];

View File

@ -298,32 +298,32 @@ EX void generateTreasureIsland(cell *c) {
beCIsland(c);
if(c->wall == waCTree) return;
}
cell* ctab[MAX_EDGE];
int qc = 0, qlo, qhi;
vector<cell*> ctab;
int qlo, qhi;
for(int i=0; i<c->type; i++) {
cell *c2 = createMov(c, i);
if(!eubinary) currentmap->generateAlts(c2->master);
if((eubinary || (c->master->alt && c2->master->alt)) && celldistAlt(c2) < celldistAlt(c)) {
ctab[qc++] = c2;
ctab.push_back(c2);
qlo = i; qhi = i;
while(true && qc < MAX_EDGE) {
while(true && isize(ctab) < c->type) {
qlo--;
c2 = c->cmodmove(qlo);
if(!eubinary && !c2->master->alt) break;
if(celldistAlt(c2) >= celldistAlt(c)) break;
ctab[qc++] = c2;
ctab.push_back(c2);
}
while(true && qc < MAX_EDGE) {
while(true && isize(ctab) < c->type) {
qhi++;
c2 = c->cmodmove(qhi);
if(!eubinary && !c2->master->alt) break;
if(celldistAlt(c2) >= celldistAlt(c)) break;
ctab[qc++] = c2;
ctab.push_back(c2);
}
break;
}
}
if(!qc) {
if(ctab.empty()) {
printf("NO QC\n"); c->wall = waSea;
for(int i=0; i<c->type; i++) printf("%d ", celldistAlt(c->move(i)));
printf("vs %d\n", celldistAlt(c));
@ -338,9 +338,9 @@ EX void generateTreasureIsland(cell *c) {
}
if(src && c2->wall == waCTree && (eubinary||c->master->alt) && celldistAlt(c) <= -10 && geometry != gRhombic3) {
bool end = true;
for(int i=0; i<qc; i++) {
generateTreasureIsland(ctab[i]);
if(ctab[i]->wall != waCTree)
for(cell *cc: ctab) {
generateTreasureIsland(cc);
if(cc->wall != waCTree)
end = false;
}
// printf("%p: end=%d, qc=%d, dist=%d\n", c, end, qc, celldistAlt(c));
@ -571,8 +571,8 @@ EX void buildEquidistant(cell *c) {
// if(qcv != 1) { printf("qcv = %d\n", qcv); exit(1); }
cell *c2 = c->move(sid);
int bsid = c->c.spin(sid);
for(int j=0; j<7; j++) {
int q = (bsid+j+42) % c2->type;
for(int j=0; j<c2->type; j++) {
int q = gmod(bsid+j, c2->type);
cell *c3 = c2->move(q);
if(coastval(c3, b) < mcv) {
cell *c4 = c2->cmodmove(bsid+1);
@ -581,7 +581,7 @@ EX void buildEquidistant(cell *c) {
mcv2 = coastval(c4, b);
break;
}
q = (bsid-j+MODFIXER) % c2->type;
q = gmod(bsid-j, c2->type);
c3 = c2->move(q);
if(coastval(c3, b) < mcv) {
cell *c4 = c2->cmodmove(bsid-1);
@ -774,14 +774,12 @@ EX void buildEquidistant(cell *c) {
}
EX cell *randomDown(cell *c) {
cell *tab[MAX_EDGE];
int q=0;
vector<cell*> tab;
for(int i=0; i<c->type; i++)
if(c->move(i) && coastval(c->move(i), laIvoryTower) < coastval(c, laIvoryTower))
tab[q++] = c->move(i);
if(!q) return NULL;
if(q==1) return tab[0];
return tab[hrand(q)];
tab.push_back(c->move(i));
if(isize(tab)==1) return tab[0];
return hrand_elt(tab, (cell*)nullptr);
}
EX int edgeDepth(cell *c) {

View File

@ -28,7 +28,7 @@ struct blizzardcell {
transmatrix *gm;
char wmap;
int inward, outward, ward;
int qty[MAX_EDGE];
vector<int> qty;
vector<snowball*> inorder, outorder;
int inid, outid;
~blizzardcell() { for(auto i: inorder) delete i; }
@ -77,6 +77,7 @@ EX void drawBlizzards() {
auto& bc = *bcells[i];
cell *c = bc.c;
bc.inward = bc.outward = 0;
bc.qty.resize(c->type);
for(int i=0; i<c->type; i++) {
int& qty = bc.qty[i];
qty = 0;

View File

@ -858,7 +858,10 @@ void celldrawer::draw_halfvine() {
queuepolyat(GDIM == 2 ? Vdepth : V2, cgi.shSemiFloor[0], darkena(vcol, fd, 0xFF), PPR::WALL3A);
{dynamicval<color_t> p(poly_outline, OUTLINE_TRANS); queuepolyat(V2 * spin(M_PI*2/3), cgi.shSemiFloorShadow, SHADOW_WALL, GDIM == 2 ? PPR::WALLSHADOW : PPR::TRANSPARENT_SHADOW); }
auto& side = queuepolyat(V2, cgi.shSemiFloorSide[SIDE_WALL], darkena(vcol, fd, 0xFF), PPR::WALL3A-2+away(V2));
if(GDIM == 3 && qfi.fshape) side.tinf = &floor_texture_vertices[shar.id];
if(GDIM == 3 && qfi.fshape) {
side.tinf = &floor_texture_vertices[shar.id];
ensure_vertex_number(*side.tinf, side.cnt);
}
if(cgi.validsidepar[SIDE_WALL]) forCellIdEx(c2, j, c) {
int dis = i-j;
@ -1570,6 +1573,7 @@ void celldrawer::draw_features_and_walls_3d() {
#endif
{
poly.tinf = &floor_texture_vertices[qfi.fshape->id];
ensure_vertex_number(*poly.tinf, poly.cnt);
poly.offset_texture = 0;
}
}
@ -1813,7 +1817,7 @@ void celldrawer::draw_cellstat() {
auto si = patterns::getpatterninfo0(c);
for(int i=(si.dir + MODFIXER) % si.symmetries; i<c->type; i += si.symmetries) {
for(int i= gmod(si.dir, si.symmetries); i<c->type; i += si.symmetries) {
queuepoly(V * ddspin(c, i) * (si.reflect?Mirror:Id), cgi.shAsymmetric, darkena(0x000000, 0, 0xC0));
si.dir += si.symmetries;
}

View File

@ -28,7 +28,7 @@ EX eMonster who_kills_me;
EX int lastkills;
EX bool legalmoves[MAX_EDGE+1];
EX vector<bool> legalmoves;
EX bool hasSafeOrb(cell *c) {
return
@ -402,11 +402,11 @@ EX void checkmove() {
// do not activate orbs!
for(int i=0; i<ittypes; i++) orbusedbak[i] = orbused[i];
for(int i=0; i<=MAX_EDGE; i++) legalmoves[i] = false;
legalmoves.clear(); legalmoves.resize(cwt.at->type+1, false);
canmove = haveRangedTarget();
items[itWarning]+=2;
if(movepcto(-1, 0, true)) canmove = legalmoves[MAX_EDGE] = true;
if(movepcto(-1, 0, true)) canmove = legalmoves[cwt.at->type] = true;
if(vid.mobilecompasssize || !canmove)
for(int i=0; i<cwt.at->type; i++)

View File

@ -64,7 +64,7 @@ EX namespace whirlwind {
qdirs = 0;
if(d == 0) return;
int qdf = 0, qdt = 0;
int cats[MAX_EDGE];
vector<int> cats(c->type);
for(int i=0; i<c->type; i++)
cats[i] = cat(createMov(c,i));
for(int i=0; i<c->type; i++)
@ -2923,11 +2923,10 @@ EX namespace kraken {
for(int i=0; i<isize(dcal); i++) {
cell *c = dcal[i];
if(c->monst == moKrakenH && !c->stuntime && !isWateryOrBoat(c)) {
int qdir = 0;
cell *ctab[MAX_EDGE];
forCellEx(c2, c) if(isWatery(c2)) ctab[qdir++] = c2;
hrandom_shuffle(ctab, qdir);
while(qdir--) trymove(ctab[qdir]);
vector<cell*> ctab;
forCellEx(c2, c) if(isWatery(c2)) ctab.push_back(c2);
hrandom_shuffle(ctab);
for(auto& cc: ctab) trymove(cc);
}
}
}
@ -4052,14 +4051,6 @@ EX namespace dungeon {
}
if(c->wparam) {
/* int q = 0;
cell* downs[MAX_EDGE];
forCellEx(c2, c) {
buildEquidistant(c2);
if(coastvalEdge(c2) > coastvalEdge(c)) downs[q++] = c2;
}
if(q) downs[hrand(q)]->wall = waLadder;
*/
cell *c2 =
WDIM == 3 ? random_child(c, coastvalEdge) :
c->wparam == 1 ? ts::add(c, 1, 2, coastvalEdge) :

View File

@ -83,7 +83,7 @@ EX movedir vectodir(hyperpoint P) {
ld binv = 99;
ld dirdist[MAX_EDGE];
vector<ld> dirdist(cwt.at->type);
for(int i=0; i<cwt.at->type; i++) {
transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin);
@ -139,7 +139,7 @@ EX void calcMousedest() {
cellwalker bcwt = cwt;
ld dists[MAX_EDGE];
vector<ld> dists(cwt.at->type);
transmatrix U = ggmatrix(cwt.at);

View File

@ -13,6 +13,7 @@ EX namespace crystal {
#if HDR
static const int MAXDIM = 7;
static const int MAX_EDGE_CRYSTAL = 2 * MAXDIM;
struct coord : public array<int, MAXDIM> {
coord operator + (coord b) { for(int i=0; i<MAXDIM; i++) b[i] += self[i]; return b; }
@ -279,7 +280,7 @@ struct crystal_structure {
if(count_bugs()) {
printf("bugs found\n");
}
if(dir > MAX_EDGE || dim > MAXDIM) {
if(dir > MAX_EDGE_CRYSTAL || dim > MAXDIM) {
printf("Dimension or directions exceeded -- I have generated it, but won't play");
exit(0);
}
@ -722,7 +723,7 @@ bool is_bi(crystal_structure& cs, coord co) {
return false;
}
array<array<int,2>, MAX_EDGE> distlimit_table = {{
array<array<int,2>, MAX_EDGE_CRYSTAL> distlimit_table = {{
{{SEE_ALL,SEE_ALL}}, {{SEE_ALL,SEE_ALL}}, {{SEE_ALL,SEE_ALL}}, {{SEE_ALL,SEE_ALL}}, {{15, 10}},
{{6, 4}}, {{5, 3}}, {{4, 3}}, {{4, 3}}, {{3, 2}}, {{3, 2}}, {{3, 2}}, {{3, 2}}, {{3, 2}}
}};
@ -1257,7 +1258,7 @@ EX void build_rugdata() {
const transmatrix& V = gp.second;
auto co = m->get_coord(c);
ldcoord vcoord[MAX_EDGE];
vector<ldcoord> vcoord(c->type);
for(int i=0; i<c->type; i++)
if(valence() == 4)
@ -1270,7 +1271,7 @@ EX void build_rugdata() {
v->flat = coord_to_flat(co);
v->valid = true;
rugpoint *p[MAX_EDGE];
rugpoint *p[MAX_EDGE_CRYSTAL];
for(int i=0; i<c->type; i++) {
p[i] = addRugpoint(V * get_corner_position(c, i), 0);
@ -1284,7 +1285,7 @@ EX void build_rugdata() {
else {
hyperpoint hco = coord_to_flat(co, 4);
hco[3] -= cut_level * rug::modelscale;
hyperpoint vco[MAX_EDGE];
vector<hyperpoint> vco(c->type);
for(int i=0; i<c->type; i++) {
vco[i] = coord_to_flat(vcoord[i], 4);
vco[i][3] -= cut_level * rug::modelscale;
@ -1325,8 +1326,7 @@ EX void set_crystal(int sides) {
static char buf[20];
sprintf(buf, "{%d,4}", sides);
ginf[gCrystal].tiling_name = buf;
if(sides < MAX_EDGE)
ginf[gCrystal].distlimit = distlimit_table[sides];
ginf[gCrystal].distlimit = distlimit_table[min(sides, MAX_EDGE_CRYSTAL-1)];
}
void test_crt() {

View File

@ -557,7 +557,7 @@ EX void moverefresh(bool turn IS(true)) {
c->monst = moReptile;
c->hitpoints = 3;
c->stuntime = 0;
int gooddirs[MAX_EDGE], qdirs = 0;
vector<int> gooddirs;
// in the peace mode, a reptile will
// prefer to walk on the ground, rather than the chasm
for(int i=0; i<c->type; i++) {
@ -565,9 +565,9 @@ EX void moverefresh(bool turn IS(true)) {
int i1 = (i+c->type-3) % c->type;
if(c->move(i0) && passable(c->move(i0), c, 0))
if(c->move(i1) && passable(c->move(i1), c, 0))
gooddirs[qdirs++] = i;
gooddirs.push_back(i);
}
if(qdirs) c->mondir = gooddirs[hrand(qdirs)];
c->mondir = hrand_elt(gooddirs, c->mondir);
playSound(c, "click");
}
}

View File

@ -79,7 +79,7 @@ void geometry_information::init_floorshapes() {
for(auto sh: all_escher_floorshapes) sh->id = ids++;
}
typedef pair<transmatrix, array<transmatrix, MAX_EDGE>> matrixitem;
typedef pair<transmatrix, vector<transmatrix>> matrixitem;
struct mesher {
eGeometry g;
@ -119,6 +119,7 @@ struct matrixlist {
matrixitem genitem(const transmatrix& m1, const transmatrix& m2, int nsym) {
matrixitem mi;
mi.first = m1;
mi.second.resize(nsym);
for(int i=0; i<nsym; i++)
mi.second[i] = spin(2*M_PI*i/nsym) * m2;
return mi;
@ -281,6 +282,7 @@ void geometry_information::bshape_regular(floorshape &fsh, int id, int sides, ld
}
for(int k=0; k<SIDEPARS; k++) {
fsh.gpside[k].resize(c->type);
for(int i=0; i<c->type; i++) {
sizeto(fsh.gpside[k][i], id);
bshape(fsh.gpside[k][i][id], PPR::LAKEWALL);
@ -435,7 +437,8 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
for(int i=0; i<=cor; i++)
hpcpush(mid_at(hpxy(0,0), cornerlist[i%cor], SHADMUL));
for(int k=0; k<SIDEPARS; k++)
for(int k=0; k<SIDEPARS; k++) {
fsh.gpside[k].resize(cor);
for(int cid=0; cid<cor; cid++) {
sizeto(fsh.gpside[k][cid], id);
bshape(fsh.gpside[k][cid][id], fsh.prio);
@ -443,6 +446,7 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
hpcpush(iddspin(c, cid) * cornerlist[(cid+1)%cor]);
chasmifyPoly(dlow_table[k], dhi_table[k], k);
}
}
}
for(auto pfsh: all_escher_floorshapes) {
@ -481,6 +485,7 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
m.n.sym = cor;
int v = sidir+siid;
for(auto& mvi: m.v) mvi.second.resize(cor);
for(int ii=0; ii<2; ii++) {
int i = 0;
@ -545,6 +550,9 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
texture_order([&] (ld x, ld y) { hpcpush(orthogonal_move(normalize(C0 + v1 * x + v2 * y), dfloor_table[k])); });
}
}
finishshape();
ensure_vertex_number(*last);
}
for(int co=0; co<2; co++) {
@ -574,12 +582,21 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
texture_order([&] (ld x, ld y) { hpcpush(orthogonal_move(normalize(C0 + v1 * x + v2 * y), top + h * (x+y))); });
}
}
finishshape();
ensure_vertex_number(*last);
}
for(int l=0; l<SIDEPARS; l++) {
for(auto& li: fsh.side[l]) li.tinf = &floor_texture_vertices[fsh.id];
for(int e=0; e<MAX_EDGE; e++)
for(auto& li: fsh.gpside[l][e]) li.tinf = &floor_texture_vertices[fsh.id];
for(auto& li: fsh.side[l]) {
li.tinf = &floor_texture_vertices[fsh.id];
ensure_vertex_number(li);
}
for(auto& gs: fsh.gpside[l])
for(auto& li: gs) {
li.tinf = &floor_texture_vertices[fsh.id];
ensure_vertex_number(li);
}
}
}
@ -592,7 +609,8 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
for(auto& li: fsh.levels[l]) li.tinf = &floor_texture_vertices[fsh.id];
fsh.side[l] = shFullFloor.side[l];
for(auto& li: fsh.side[l]) li.tinf = &floor_texture_vertices[fsh.id];
for(int e=0; e<MAX_EDGE; e++) {
fsh.gpside[l].resize(c->type);
for(int e=0; e<c->type; e++) {
fsh.gpside[l][e] = shFullFloor.gpside[l][e];
for(auto& li: fsh.gpside[l][e]) li.tinf = &floor_texture_vertices[fsh.id];
}
@ -1016,16 +1034,28 @@ void draw_shape_for_texture(floorshape* sh) {
}
// SL2 needs 6 times more
for(int a=0; a<MAX_EDGE*6; a++)
texture_order([&] (ld x, ld y) {
hyperpoint h = center + v1 * x + v2 * y;
hyperpoint inmodel;
applymodel(h, inmodel);
glvec2 v;
v[0] = (1 + inmodel[0] * vid.scale) / 2;
v[1] = (1 - inmodel[1] * vid.scale) / 2;
ftv.tvertices.push_back(glhr::makevertex(v[0], v[1], 0));
});
texture_order([&] (ld x, ld y) {
hyperpoint h = center + v1 * x + v2 * y;
hyperpoint inmodel;
applymodel(h, inmodel);
glvec2 v;
v[0] = (1 + inmodel[0] * vid.scale) / 2;
v[1] = (1 - inmodel[1] * vid.scale) / 2;
ftv.tvertices.push_back(glhr::makevertex(v[0], v[1], 0));
});
}
/** copy the texture vertices so that there are at least qty of them */
EX void ensure_vertex_number(basic_textureinfo& bti, int qty) {
int s = isize(bti.tvertices);
while(isize(bti.tvertices) <= qty) {
for(int i=0; i<s; i++) bti.tvertices.push_back(bti.tvertices[i]);
}
}
/** ensure_vertex_number for a hpcshape */
EX void ensure_vertex_number(hpcshape& sh) {
ensure_vertex_number(*sh.tinf, sh.e - sh.s);
}
const int FLOORTEXTURESIZE = 4096;

View File

@ -43,8 +43,21 @@ EX int hrand(int i) {
#if HDR
template<class T, class... U> T pick(T x, U... u) { std::initializer_list<T> i = {x,u...}; return *(i.begin() + hrand(1+sizeof...(u))); }
template<class T> void hrandom_shuffle(T* x, int n) { for(int k=1; k<n; k++) swap(x[k], x[hrand(k+1)]); }
template<class T> void hrandom_shuffle(T& container) { hrandom_shuffle(&container[0], isize(container)); }
template<class U> auto hrand_elt(U& container) -> decltype(container[0]) { return container[hrand(isize(container))]; }
template<class T, class U> T hrand_elt(U& container, T default_value) {
if(container.empty()) return default_value;
return container[hrand(isize(container))];
}
#endif
EX vector<int> hrandom_permutation(int qty) {
vector<int> res(qty);
for(int i=0; i<qty; i++) res[i] = i;
hrandom_shuffle(res);
return res;
}
/** Use \link hrngen \endlink to generate a floating point number between 0 and 1.
*/

View File

@ -63,7 +63,8 @@ struct floorshape {
int pstrength; // pattern strength in 3D
int fstrength; // frame strength in 3D
PPR prio;
vector<hpcshape> b, shadow, side[SIDEPARS], gpside[SIDEPARS][MAX_EDGE], levels[SIDEPARS], cone[2];
vector<hpcshape> b, shadow, side[SIDEPARS], levels[SIDEPARS], cone[2];
vector<vector<hpcshape>> gpside[SIDEPARS];
floorshape() { prio = PPR::FLOOR; pstrength = fstrength = 10; }
};
@ -112,8 +113,7 @@ struct geometry_information {
/** distance from heptagon center to heptagon vertex (either hexf or hcrossf) */
ld rhexf;
transmatrix heptmove[MAX_EDGE], hexmove[MAX_EDGE];
transmatrix invhexmove[MAX_EDGE];
vector<transmatrix> heptmove, hexmove, invhexmove;
int base_distlimit;
@ -381,7 +381,7 @@ hpcshape
/* Goldberg parameters */
#if CAP_GP
struct gpdata_t {
transmatrix Tf[MAX_EDGE][32][32][6];
vector<array<array<array<transmatrix, 6>, 32>, 32>> Tf;
transmatrix corners;
ld alpha;
int area;
@ -478,6 +478,10 @@ void geometry_information::prepare_basics() {
finish:
heptmove.resize(S7);
hexmove.resize(S7);
invhexmove.resize(S7);
for(int d=0; d<S7; d++)
heptmove[d] = spin(-d * ALPHA) * xpush(tessf) * spin(M_PI);

View File

@ -85,7 +85,7 @@ EX namespace gp {
transmatrix adjm;
};
EX int fixg6(int x) { return (x + MODFIXER) % SG6; }
EX int fixg6(int x) { return gmod(x, SG6); }
EX int get_code(const local_info& li) {
return
@ -137,7 +137,7 @@ EX namespace gp {
EX int pseudohept_val(cell *c) {
loc v = get_coord(c);
return (v.first - v.second + MODFIXER)%3;
return gmod(v.first - v.second, 3);
}
// mapping of the local equilateral triangle
@ -578,6 +578,7 @@ EX namespace gp {
loctoh_ort(param * loc(0,1)),
C03
));
cgi.gpdata->Tf.resize(S7);
for(int i=0; i<S7; i++) {
transmatrix T = dir_matrix(i);
for(int x=-16; x<16; x++)

View File

@ -3248,7 +3248,7 @@ EX bool placeSidewall(cell *c, int i, int sidepar, const transmatrix& V, color_t
if(NONSTDVAR || !standard_tiling()) {
#if CAP_ARCM
if(arcm::in() && !PURE)
i = (i + arcm::parent_index_of(c->master)/DUALMUL + MODFIXER) % c->type;
i = gmod(i + arcm::parent_index_of(c->master)/DUALMUL, c->type);
#endif
draw_shapevec(c, V2, qfi.fshape->gpside[sidepar][i], col, prio);
return false;

View File

@ -437,7 +437,7 @@ EX void drawStats() {
#if CAP_QUEUE
queuecircle(xmove, yb, rad, 0xFF0000FF);
queuecircle(xmove, yb, rad*SKIPFAC,
legalmoves[MAX_EDGE] ? 0xFF0000FF : 0xFF000080
legalmoves[cwt.at->type] ? 0xFF0000FF : 0xFF000080
);
#endif
#if CAP_SHAPES

View File

@ -110,7 +110,7 @@ struct gcell {
#define landparam_color LHU.landpar_color
#define fval LHU.fi.fieldval
#define MAX_EDGE 18
#define FULL_EDGE 120
template<class T> struct walker;
@ -125,10 +125,12 @@ template<class T> struct walker;
* and freed with tailored_free.
*/
int gmod(int i, int j);
template<class T> struct connection_table {
/** Table of moves. This is the maximum size, but tailored_alloc allocates less. */
T* move_table[MAX_EDGE + (MAX_EDGE + sizeof(char*) - 1) / sizeof(char*)];
T* move_table[FULL_EDGE + (FULL_EDGE + sizeof(char*) - 1) / sizeof(char*)];
unsigned char *spintable() { return (unsigned char*) (&move_table[full()->degree()]); }
@ -145,7 +147,7 @@ template<class T> struct connection_table {
/** on non-orientable surfaces, the d-th edge may be mirrored */
bool mirror(int d) { return spintable() [d] & 128; }
/** 'fix' the edge number d to get the actual index in [0, degree()) */
int fix(int d) { return (d + MODFIXER) % full()->degree(); }
int fix(int d) { return gmod(d, full()->degree()); }
/** T in the direction i */
T*& move(int i) { return move_table[i]; }
/** T in the direction i, modulo degree() */

View File

@ -86,7 +86,7 @@ EX namespace mapeditor {
c->hitpoints = c2->hitpoints;
if(c2->mondir != NODIR) {
auto si2 = patterns::getpatterninfo0(c2);
c->mondir = (c2->mondir - si2.dir + si.dir + MODFIXER) % c->type;
c->mondir = gmod(c2->mondir - si2.dir + si.dir, c->type);
// todo reflect
}
}
@ -434,7 +434,7 @@ namespace mapstream {
// printf("%p:%d,%d -> %p\n", c2, relspin[parent], dir, c);
// spinval becomes xspinval
rspin = (c2->c.spin(dir) - f.read_char() + MODFIXER) % (c->type - sub);
rspin = gmod(c2->c.spin(dir) - f.read_char(), c->type - sub);
if(GDIM == 3 && rspin && !hybri) {
println(hlog, "rspin in 3D");
throw hstream_exception();
@ -898,7 +898,7 @@ namespace mapeditor {
c->hitpoints = copywhat->hitpoints;
c->stuntime = copywhat->stuntime;
if(copywhat->mondir == NODIR) c->mondir = NODIR;
else c->mondir = ((where.first.mirrored == where.second.mirrored ? 1 : -1) * (copywhat->mondir - where.second.spin) + cdir + MODFIXER) % c->type;
else c->mondir = gmod((where.first.mirrored == where.second.mirrored ? 1 : -1) * (copywhat->mondir - where.second.spin) + cdir, c->type);
break;
}
checkUndo();

View File

@ -19,7 +19,7 @@ EX int sagephase = 0;
EX vector<cell*> targets;
/** monsters to move, ordered by the number of possible good moves */
vector<cell*> movesofgood[MAX_EDGE+1];
grow_vector<vector<cell*>> movesofgood;
EX vector<pair<cell*, int> > butterflies;
@ -538,11 +538,12 @@ EX int totalbulldistance(cell *c, int k) {
return tbd;
}
EX void determinizeBull(cell *c, int *posdir, int& nc) {
EX void determinizeBull(cell *c, vector<int>& posdir) {
// determinize the Angry Beast movement:
// use the previous PC's positions as the tiebreaker
int nc = isize(posdir);
for(int k=0; k<SHSIZE && nc>1; k++) {
int pts[MAX_EDGE];
vector<int> pts(nc);
for(int d=0; d<nc; d++) pts[d] = totalbulldistance(c->cmove(posdir[d]), k);
int bestpts = 1000;
@ -551,46 +552,46 @@ EX void determinizeBull(cell *c, int *posdir, int& nc) {
for(int d=0; d<nc; d++) if(pts[d] == bestpts) posdir[nc0++] = posdir[d];
nc = nc0;
}
posdir.resize(nc);
}
EX int determinizeBullPush(cellwalker bull) {
int nc = 2;
int dirs[2], positive;
vector<int> dirs(2);
int positive;
bull += wstep;
cell *c2 = bull.at;
if(!(c2->type & 1)) return 1; // irrelevant
int d = c2->type / 2;
bull += d; dirs[0] = positive = bull.spin;
bull -= 2*d; dirs[1] = bull.spin;
determinizeBull(c2, dirs, nc);
determinizeBull(c2, dirs);
if(dirs[0] == positive) return -1;
return 1;
}
int posdir[MAX_EDGE], nc;
vector<int> global_posdir;
EX int pickMoveDirection(cell *c, flagtype mf) {
int bestval = stayval(c, mf);
nc = 1; posdir[0] = -1;
global_posdir = {-1};
// printf("stayval [%p, %s]: %d\n", c, dnameof(c->monst), bestval);
for(int d=0; d<c->type; d++) {
cell *c2 = c->move(d);
int val = moveval(c, c2, d, mf);
// printf("[%d] %p: val=%5d pass=%d\n", d, c2, val, passable(c2,c,0));
if(val > bestval) nc = 0, bestval = val;
if(val == bestval) posdir[nc++] = d;
if(val > bestval) global_posdir.clear(), bestval = val;
if(val == bestval) global_posdir.push_back(d);
}
if(c->monst == moRagingBull)
determinizeBull(c, posdir, nc);
determinizeBull(c, global_posdir);
if(!nc) return -1;
return posdir[hrand(nc)];
return hrand_elt(global_posdir, -1);
}
EX int pickDownDirection(cell *c, flagtype mf) {
int downs[MAX_EDGE], qdowns = 0;
vector<int> downs;
int bestdif = -100;
forCellIdEx(c2, i, c) {
if(gravityLevelDiff(c2, c) < 0 && passable_for(c->monst, c2, c, P_MIRROR) &&
@ -602,12 +603,11 @@ EX int pickDownDirection(cell *c, flagtype mf) {
// printf("i=%d md=%d dif=%d\n", i, c->mondir, cdif);
if(c2->wall == waClosePlate || c->wall == waClosePlate)
cdif += 20;
if(cdif > bestdif) bestdif = cdif, qdowns = 0;
if(cdif == bestdif) downs[qdowns++] = i;
if(cdif > bestdif) bestdif = cdif, downs.clear();
if(cdif == bestdif) downs.push_back(i);
}
}
if(!qdowns) return -1;
return downs[hrand(qdowns)];
return hrand_elt(downs, -1);
}
// Angry Beast attack
@ -657,7 +657,7 @@ EX cell *moveNormal(cell *c, flagtype mf) {
int d;
if(c->stuntime) {
if(cellEdgeUnstable(c, MF_STUNNED)) d = pickDownDirection(c, mf), nc = 1, posdir[0] = d;
if(cellEdgeUnstable(c, MF_STUNNED)) d = pickDownDirection(c, mf), global_posdir = {d};
else return NULL;
}
else {
@ -706,8 +706,8 @@ EX cell *moveNormal(cell *c, flagtype mf) {
}
else {
bool attacking = false;
for(int i=0; i<nc; i++) {
cell *c2 = c->move(posdir[i]);
for(int dir: global_posdir) {
cell *c2 = c->move(dir);
if(isPlayerOn(c2)) {
killThePlayerAt(m, c2, 0);
@ -725,8 +725,8 @@ EX cell *moveNormal(cell *c, flagtype mf) {
}
}
if(!attacking) for(int i=0; i<nc; i++) {
movei mi(c, posdir[i]);
if(!attacking) for(int dir: global_posdir) {
movei mi(c, dir);
if(!c->monst) c->monst = m;
moveMonster(mi);
if(m == moRagingBull) beastAttack(mi.t, false, false);
@ -1152,13 +1152,12 @@ EX void groupmove(eMonster movtype, flagtype mf) {
for(int i=0; i<isize(gendfs); i++) {
cell *c = gendfs[i];
int dirtable[MAX_EDGE], qdirtable=0;
vector<int> dirtable;
forCellIdAll(c2,t,c) dirtable[qdirtable++] = t;
hrandom_shuffle(dirtable, qdirtable);
forCellIdAll(c2,t,c) dirtable.push_back(t);
hrandom_shuffle(dirtable);
while(qdirtable--) {
int t = dirtable[qdirtable];
for(auto& t: dirtable) {
groupmove2(movei(c, t).rev(),movtype,mf);
}
@ -1287,13 +1286,12 @@ EX void movehex(bool mounted, int colorpair) {
for(int i=0; i<isize(hexdfs); i++) {
cell *c = hexdfs[i];
int dirtable[MAX_EDGE], qdirtable=0;
vector<int> dirtable;
for(int t=0; t<c->type; t++) if(c->move(t) && inpair(c->move(t), colorpair))
dirtable[qdirtable++] = t;
dirtable.push_back(t);
hrandom_shuffle(dirtable, qdirtable);
while(qdirtable--) {
int t = dirtable[qdirtable];
hrandom_shuffle(dirtable);
for(auto& t: dirtable) {
hexvisit(c->move(t), c, t, mounted, colorpair);
}
}
@ -1306,13 +1304,11 @@ EX void movehex_rest(bool mounted) {
if(c->monst == moHexSnake) {
colorpair = snake_pair(c);
if(!goodmount(c, mounted)) continue;
int t[MAX_EDGE];
for(int i=0; i<c->type; i++) t[i] = i;
for(int j=1; j<c->type; j++) swap(t[j], t[hrand(j+1)]);
vector<int> dirtable = hrandom_permutation(c->type);
for(int u=0; u<c->type; u++) {
createMov(c, t[u]);
if(inpair(c->move(t[u]), colorpair))
hexvisit(c, c->move(t[u]), c->c.spin(t[u]), mounted, colorpair);
createMov(c, dirtable[u]);
if(inpair(c->move(dirtable[u]), colorpair))
hexvisit(c, c->move(dirtable[u]), c->c.spin(dirtable[u]), mounted, colorpair);
}
}
if(c->monst == moHexSnake) {
@ -1429,7 +1425,7 @@ EX void moveshadow() {
EX void moveghosts() {
if(invismove) return;
for(int d=0; d<=MAX_EDGE; d++) movesofgood[d].clear();
movesofgood.clear();
for(int i=0; i<isize(ghosts); i++) {
cell *c = ghosts[i];
@ -1444,19 +1440,18 @@ EX void moveghosts() {
if(ghostmove(c->monst, c->move(k), c) && !isPlayerOn(c->move(k)))
goodmoves++;
movesofgood[goodmoves].push_back(c);
movesofgood.grow(goodmoves).push_back(c);
}
}
for(int d=0; d<=MAX_EDGE; d++) for(int i=0; i<isize(movesofgood[d]); i++) {
cell *c = movesofgood[d][i];
for(auto& v: movesofgood) for(cell *c: v) {
if(c->stuntime) continue;
if(isPowerMonster(c) && !playerInPower()) continue;
if(isGhostMover(c->monst) && c->cpdist >= 1) {
int mdir[MAX_EDGE];
vector<int> mdir;
for(int j=0; j<c->type; j++)
if(c->move(j) && canAttack(c, c->monst, c->move(j), c->move(j)->monst, AF_GETPLAYER | AF_ONLY_FBUG)) {
@ -1466,12 +1461,11 @@ EX void moveghosts() {
goto nextghost;
}
int qmpos = 0;
for(int k=0; k<c->type; k++) if(c->move(k) && c->move(k)->cpdist < c->cpdist)
if(ghostmove(c->monst, c->move(k), c))
mdir[qmpos++] = k;
if(!qmpos) continue;
int d = mdir[hrand(qmpos)];
mdir.push_back(k);
if(mdir.empty()) continue;
int d = hrand_elt(mdir);
cell *c2 = c->move(d);
if(c2->monst == moTortoise && c2->stuntime > 1) {
addMessage(XLAT("%The1 scares %the2 a bit!", c->monst, c2->monst));
@ -1606,15 +1600,16 @@ EX void movegolems(flagtype flags) {
for(int i=0; i<ittypes; i++) recorduse[i] = orbused[i];
DEBB(DF_TURN, ("stayval"));
int bestv = stayvalue(m, c), bq = 0, bdirs[MAX_EDGE];
int bestv = stayvalue(m, c);
vector<int> bdirs;
DEBB(DF_TURN, ("moveval"));
for(int k=0; k<c->type; k++) if(c->move(k)) {
cell *c2 = c->move(k);
int val = movevalue(m, c, c2, flags);
if(val > bestv) bestv = val, bq = 0;
if(val == bestv) bdirs[bq++] = k;
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(k);
}
if(m == moTameBomberbird) {
@ -1622,8 +1617,8 @@ EX void movegolems(flagtype flags) {
if(c2 && !c2->monst) {
int val = movevalue(m, c, c2, flags);
// printf("val = %d bestv = %d\n",
if(val > bestv) bestv = val, bq = 0;
if(val == bestv) bdirs[bq++] = STRONGWIND;
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(STRONGWIND);
}
}
@ -1631,8 +1626,8 @@ EX void movegolems(flagtype flags) {
// printf("stayvalue = %d, result = %d, bq = %d\n", stayvalue(m,c), bestv, bq);
if(bq == 0) continue;
int dir = bdirs[hrand(bq)];
if(bdirs.empty()) continue;
int dir = hrand_elt(bdirs);
auto mi = movei(c, dir);
auto& c2 = mi.t;
if(c2->monst) {
@ -1882,16 +1877,16 @@ EX void consMove(cell *c, eMonster param) {
if(c2 && c2->pathdist < c->pathdist)
goodmoves++;
}
movesofgood[goodmoves].push_back(c);
movesofgood.grow(goodmoves).push_back(c);
}
else
movesofgood[0].push_back(c);
movesofgood.grow(0).push_back(c);
}
EX void moveNormals(eMonster param) {
pathdata pd(param);
for(int d=0; d<=MAX_EDGE; d++) movesofgood[d].clear();
movesofgood.clear();
for(int i=0; i<isize(pathqm); i++)
consMove(pathqm[i], param);
@ -1902,8 +1897,7 @@ EX void moveNormals(eMonster param) {
if(c->pathdist == PINFD) consMove(c, param);
}
for(int d=0; d<=MAX_EDGE; d++) for(int i=0; i<isize(movesofgood[d]); i++) {
cell *c = movesofgood[d][i];
for(auto& v: movesofgood) for(cell *c: v) {
if(minf[c->monst].mgroup == moYeti) {
moveNormal(c, MF_PATHDIST);
}

View File

@ -57,15 +57,15 @@ EX namespace netgen {
vec center[MAXCELLS];
double rot[MAXCELLS];
int glued[MAXCELLS];
int nei[MAXCELLS][MAX_EDGE];
vector<int> nei[MAXCELLS];
// auxiliary data
double raylen[MAXCELLS];
double edgist[MAXCELLS];
char patek[MAXCELLS][MAX_EDGE];
vector<char> patek[MAXCELLS];
// data generated by HyperRogue
hyperpoint hcenter[MAXCELLS][MAX_EDGE+1];
vector<hyperpoint> hcenter[MAXCELLS];
// Functions handling the data.
//==============================
@ -79,7 +79,8 @@ EX namespace netgen {
if(mode == 1)
for(int ii=0; ii<CELLS; ii++) if(dcal[ii] == c) {
hcenter[ii][MAX_EDGE] = V * C0;
hcenter[ii].resize(c->type+1);
hcenter[ii][c->type] = V * C0;
if(c->type == S7) {
for(int i=0; i<c->type; i++) {
@ -110,11 +111,12 @@ EX namespace netgen {
for(int i=0; i<CELLS; i++) {
ct[i] = dcal[i]->type;
for(int k=0; k<8; k++)
for(int k=0; k<=ct[i]; 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;
nei[i].clear();
nei[i].resize(ct[i], -1);
for(int j=0; j<CELLS; j++) {
cell *c1 = dcal[i];
@ -148,6 +150,8 @@ EX namespace netgen {
for(int i=0; i<CELLS; i++) scan(f, ct[i]);
for(int i=0; i<CELLS; i++) nei[i].resize(ct[i]);
for(int i=0; i<CELLS; i++) for(int j=0; j<16; j++) scan(f, vx[i][j]);
for(int i=0; i<CELLS; i++)
@ -343,6 +347,7 @@ EX namespace netgen {
SDL_FillRect(net, NULL, 0xFFFFFF);
int pateks = 0;
for(int i=0; i<CELLS; i++) patek[i].resize(ct[i]);
int zeroi = nei[0][0];
int zeroe = 0;

View File

@ -321,8 +321,7 @@ EX bool reflectingBarrierAt(cell *c) {
EX bool reflectingBarrierAt(cellwalker& c, int d) {
if(d >= 3) return true;
if(d <= -3) return true;
d = c.spin + d + MODFIXER;
d%=c.at->type;
d = gmod(c.spin + d, c.at->type);
if(!c.at->move(d)) return true;
return reflectingBarrierAt(c.at->move(d));
@ -1104,8 +1103,7 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
// nature
if(items[itOrbNature] && numplayers() == 1 && c->monst != moFriendlyIvy) {
int dirs[MAX_EDGE];
int qsides = 0;
vector<int> dirs;
forCellIdCM(cf, d, c)
if(cf->monst == moFriendlyIvy) {
@ -1118,11 +1116,11 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
if(strictlyAgainstGravity(c, cf, false, MF_IVY)) continue;
if(monstersnear(cwt.at, NULL, moPlayer, c, cwt.at)) continue;
}
dirs[qsides++] = d;
dirs.push_back(d);
}
if(qsides > 0) {
int di = dirs[hrand(qsides)];
int di = hrand_elt(dirs, -1);
if(di != -1) {
if(!isCheck(a)) growIvyTo(movei(c, di).rev());
return itOrbNature;
}

View File

@ -1278,8 +1278,8 @@ EX int pattern_threecolor(cell *c) {
#if CAP_GP
if(S3 == 3 && !(S7&1) && gp_threecolor() == 1 && c->master->c7 != c) {
auto li = gp::get_local_info(c);
int rel = (li.relative.first - li.relative.second + MODFIXER) % 3;
int par = (gp::param.first - gp::param.second + MODFIXER) % 3;
int rel = gmod(li.relative.first - li.relative.second, 3);
int par = gmod(gp::param.first - gp::param.second, 3);
if(rel == 0)
return pattern_threecolor(c->master->c7);
else if(rel == par)
@ -1335,7 +1335,7 @@ EX int pattern_threecolor(cell *c) {
#if CAP_GP
if(gp_threecolor() == 2) {
auto li = gp::get_local_info(c);
int sp = (MODFIXER + li.relative.first + 2 * li.relative.second) % 3;
int sp = gmod(li.relative.first + 2 * li.relative.second, 3);
if(sp != 0) {
if(li.last_dir & 1)
sp = 3 - sp;

View File

@ -12,25 +12,16 @@ namespace hr {
EX namespace quotientspace {
bool operator == (const code& c1, const code &c2) {
for(int i=0; i<=S7; i++) if(c1.c[i] != c2.c[i]) return false;
return true;
}
bool operator < (const code& c1, const code &c2) {
for(int i=0; i<=S7; i++) if(c1.c[i] != c2.c[i]) return c1.c[i] < c2.c[i];
return false;
}
int cod(heptagon *h) {
return zebra40(h->c7);
}
code get(heptspin hs) {
code res;
res.c[0] = cod(hs.at);
res.connections.resize(S7);
res.connections[0] = cod(hs.at);
for(int i=1; i<=S7; i++) {
res.c[i] = cod((hs + wstep).at);
res.connections[i] = cod((hs + wstep).at);
hs += 1;
}
return res;
@ -45,7 +36,11 @@ EX namespace quotientspace {
#if HDR
struct code {
int c[MAX_EDGE+1];
vector<int> connections;
bool operator == (const code &c2) const { return connections == c2.connections; }
bool operator < (const code &c2) const { return connections < c2.connections; }
};
struct hrmap_quotient : hrmap_standard {

View File

@ -609,7 +609,7 @@ EX void buildRug() {
rugpoint *v = p.second;
if(arcm::in() || (euclid && quotient)) {
rugpoint *p[MAX_EDGE+1];
vector<rugpoint*> p(c->type+1);
for(int j=0; j<c->type; j++) p[j] = findOrAddRugpoint(ggmatrix(c) * get_corner_position(c, j), v->dist);
for(int j=0; j<c->type; j++) addTriangle(v, p[j], p[(j+1) % c->type]);