1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-23 21:07:17 +00:00
hyperrogue/rogueviz/sag/cells.cpp

697 lines
19 KiB
C++

// RogueViz -- SAG embedder: cell constructor
// Copyright (C) 2011-24 Zeno Rogue, see 'hyper.cpp' for details
#include "../rogueviz.h"
#ifdef LINUX
#include <sys/mman.h>
#endif
#include <fcntl.h>
namespace rogueviz {
namespace sag {
namespace cells {
bool angular = false;
using subcell = pair<cell*, int>;
/** all the SAG cells */
vector<subcell> sagcells;
/** sagcells[ids[c]]] == c */
map<subcell, int> ids;
/** if i in neighbors[j], sagcells[i] is a neighbor of sagcells[j] */
vector<vector<int>> neighbors;
vector<hyperpoint> sagsubcell_point;
vector<transmatrix> sagsubcell_inv;
/** matrix for every sagcell, not subdivided */
vector<transmatrix> cell_matrix;
/** point for every sagcell */
vector<hyperpoint> cellpoint;
/** precision of geometric distances */
int gdist_prec;
/** max edge for dijkstra */
int dijkstra_maxedge;
/** dijkstra with tile distances */
bool dijkstra_tile;
string distance_file;
ld pdist(hyperpoint hi, hyperpoint hj);
/** the maximum value in sagdist +1 */
int max_sag_dist;
/** new style cell request */
int cell_request;
/** the structure type used to hold a N*N table of distances */
struct sagdist_t {
using distance = unsigned short;
distance* tab;
void* tabmap;
int fd;
size_t N;
int format;
distance* begin() { return tab; }
distance* end() { return tab+N*N; }
sagdist_t() { tab = nullptr; fd = 0; format = 1; }
distance* operator [] (int y) { return tab + N * y; }
void init(int _N, distance val) {
clear();
N = _N;
tab = new distance[N*N];
for(size_t i=0; i<N*N; i++) tab[i] = val;
}
#ifdef LINUX
void map(string fname) {
clear();
fd = open(fname.c_str(), O_RDONLY | O_LARGEFILE);
if(fd == -1) throw hr_exception("open failed in map");
if(read(fd, &N, 8) < 8) throw hr_exception("file error");
tabmap = (distance*) mmap(nullptr, N*N*sizeof(distance)+8, PROT_READ, MAP_SHARED, fd, 0);
if(tabmap == MAP_FAILED) {
perror("mmap");
throw hr_exception("Mapping Failed\n");
}
tab = (distance*) (((char*)tabmap) + 8);
println(hlog, "test: ", test());
}
#endif
void load_no_map(string fname) {
println(hlog, "clearing old map");
clear();
#ifdef O_BINARY
fd = open(fname.c_str(), O_RDONLY | O_BINARY);
#else
fd = open(fname.c_str(), O_RDONLY);
#endif
println(hlog, "fd equals ", fd);
if(fd == -1) throw hr_exception("open failed in load_no_map");
if(read(fd, &N, 8) < 8) throw hr_exception("file error");
println(hlog, "read N = ", (int) N);
tab = new distance[N*N];
size_t fsize = N * N * sizeof(distance);
println(hlog, "allocated memory");
size_t offset = 0;
while(offset < fsize) {
ssize_t block = read(fd, ((char*)tab)+offset, fsize-offset);
#ifdef LINUX
println(hlog, "read ", hr::format("%zd/%zd", block, fsize-offset), "B");
#else
println(hlog, "read ", hr::format("%lld/%lld", block, fsize-offset), "B");
#endif
if(block <= 0) throw hr_exception("file error reading table");
offset += block;
}
println(hlog, "test: ", test());
::close(fd); fd = 0;
}
void load_old(string fname) {
vector<vector<distance>> old;
clear();
fhstream f(fname, "rb");
f.read(old);
init(isize(old), 0);
auto ptr = tab;
for(auto& row: old) for(auto val: row) *(ptr++) = val;
}
void load(string fname) {
if(format == 1) {
#ifdef LINUXX
map(fname);
#else
load_no_map(fname);
#endif
}
else if(format == 2) load_old(fname);
else throw hr_exception("sagdist format unknown");
}
vector<int> test() {
vector<int> ttab = {int(N)};
for(int a=0; a<4; a++) for(int b=0; b<4; b++) ttab.push_back((*this)[a][b]);
for(size_t a=N-4; a<N; a++) for(size_t b=N-4; b<N; b++) ttab.push_back((*this)[a][b]);
return ttab;
}
void save(string fname) {
fd = open(fname.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(write(fd, &N, 8) < 8) throw hr_exception("write error");
size_t size = N*N*sizeof(distance);
#ifdef LINUX
println(hlog, "size is ", hr::format("%zd", size));
#else
println(hlog, "size is ", hr::format("%lld", (long long) size));
#endif
char *p = (char*) tab;
while(size) {
size_t written = write(fd, p, size);
if(written <= 0) throw hr_exception("bad written");
p += written; size -= written;
}
println(hlog, "test: ", test());
::close(fd);
}
void clear() {
#ifdef LINUX
if(fd) { munmap(tabmap, N*N*sizeof(distance)+8); ::close(fd); }
else
#endif
delete[] tab;
tab = nullptr; fd = 0;
}
~sagdist_t() {
clear();
}
};
sagdist_t sagdist;
vector<hyperpoint> subcell_points;
/** currently implemented only for Solv and Nil! */
void generate_subcellpoints() {
start_game();
subcell_points.clear();
println(hlog, currentmap->get_cellshape(cwt.at).vertices_only);
ld mx = 1, my = 1, mz = 1;
if(sol) mx = my = mz = log(2);
for(int x=0; x<4; x++)
for(int y=0; y<4; y++) if((x&1) == (y&1))
for(int z=0; z<4; z++) if((x&1) == (z&1)) {
subcell_points.push_back(point31(mx * (x+.5-2)/4, my * (y+.5-2)/4, mz * (z+.5-2)/4));
}
println(hlog, subcell_points);
}
void ensure_subcell_points() {
if(isize(subcell_points) <= 1) subcell_points = { C0 };
}
void compute_dists() {
int N = isize(sagcells);
neighbors.clear();
neighbors.resize(N);
int Q = isize(subcell_points);
for(int b=0; b<Q; b++)
for(int i=0; i<N; i++)
for(cell *c1: adj_minefield_cells(sagcells[i].first))
if(ids.count({c1,b})) neighbors[i].push_back(ids[{c1,b}]);
for(int i=0; i<N; i++) for(int b=0; b<Q; b++) if(b != sagcells[i].second)
neighbors[i].push_back(ids[{sagcells[i].first, b}]);
const ld ERRORV = -17.3;
transmatrix unknown = Id; unknown[0][0] = ERRORV;
cell_matrix.clear();
cell_matrix.resize(N, unknown);
cellpoint.clear();
cellpoint.resize(N, C0);
vector<int> visited;
auto visit = [&] (int id, const transmatrix& T) {
if(cell_matrix[id][0][0] != ERRORV) return;
cell_matrix[id] = T;
visited.push_back(id);
};
if(N == 0) return;
visit(0, Id);
for(int i=0; i<isize(visited); i++) {
cell *c0 = sagcells[visited[i]].first;
const transmatrix& T0 = cell_matrix[visited[i]];
for(int d=0; d<c0->type; d++)
if(ids.count({c0->move(d), 0}))
visit(ids[{c0->move(d), 0}], T0 * currentmap->adj(c0, d));
for(int q=0; q<Q; q++)
cellpoint[visited[i]/Q*Q+q] = T0 * subcell_points[q];
}
if(distance_file != "") {
sagdist.load(distance_file);
}
else if(gdist_prec && dijkstra_maxedge) {
sagdist.init(N, N);
println(hlog, "Computing Dijkstra distances...");
vector<vector<pair<int, ld>>> dijkstra_edges(N);
for(int i=0; i<N; i++) {
celllister cl(sagcells[i].first, dijkstra_maxedge, 50000, nullptr);
for(auto c1: cl.lst) for(int q=0; q<Q; q++) if(c1 != sagcells[i].first || q != sagcells[i].second) if(ids.count({c1, q}))
dijkstra_edges[i].emplace_back(ids[{c1, q}], pdist(cellpoint[i], cellpoint[ids[{c1, q}]]));
if(i == 0) println(hlog, i, " has ", isize(dijkstra_edges[i]), " edges");
}
parallelize(N, [&] (int a, int b) {
vector<ld> distances(N);
for(int i=a; i<b; i++) {
if(i % 500 == 0) println(hlog, "computing dijkstra for ", i , "/", N);
for(int j=0; j<N; j++) distances[j] = HUGE_VAL;
std::priority_queue<pair<ld, int>> pq;
auto visit = [&] (int i, ld dist) {
if(distances[i] <= dist) return;
distances[i] = dist;
pq.emplace(-dist, i);
};
visit(i, 0);
while(!pq.empty()) {
ld d = -pq.top().first;
int at = pq.top().second;
pq.pop();
for(auto e: dijkstra_edges[at]) visit(e.first, d + e.second);
}
for(int j=0; j<N; j++) sagdist[i][j] = distances[j] * gdist_prec + .5;
}
return 0;
}
);
println(hlog, "N0 = ", neighbors[0]);
println(hlog, "N1 = ", neighbors[1]);
}
else if(gdist_prec) {
sagdist.init(N, N);
println(hlog, "Computing distances... (N=", N, ")");
ld mx = 1;
for(int i=0; i<N; i++)
for(int j=0; j<N; j++) {
ld d = pdist(cellpoint[i], cellpoint[j]);
sagdist[i][j] = (d + .5) * gdist_prec;
if(d > mx) {
println(hlog, kz(cellpoint[i]), kz(cellpoint[j]), " :: ", mx = d);
}
}
}
else {
println(hlog, "no gdist_prec");
sagdist.init(N, N);
for(int i=0; i<N; i++) {
auto sdi = sagdist[i];
vector<int> q;
auto visit = [&] (int j, int dist) { if(sdi[j] < N) return; sdi[j] = dist; q.push_back(j); };
visit(i, 0);
for(int j=0; j<isize(q); j++) for(int k: neighbors[q[j]]) visit(k, sdi[q[j]]+1);
}
}
max_sag_dist = 0;
for(auto x: sagdist) max_sag_dist = max<int>(max_sag_dist, x);
max_sag_dist++;
println(hlog, "max_sag_dist = ", max_sag_dist);
}
bool legacy;
/* legacy method */
void init_snake(int n) {
sagcells.clear();
ids.clear();
auto enlist = [&] (cellwalker cw) {
ids[{cw.at, 0}] = isize(sagcells);
sagcells.emplace_back(cw.at, 0);
};
cellwalker cw = cwt;
enlist(cw);
cw += wstep;
enlist(cw);
for(int i=2; i<n; i++) {
cw += wstep;
while(ids.count({cw.at, 0})) {
cw = cw + wstep + 1 + wstep;
}
enlist(cw); cw += 1;
}
}
void init_cells_to_all() {
ensure_subcell_points();
sagcells.clear();
for(auto c: currentmap->allcells()) for(int i=0; i<isize(subcell_points); i++) {
ids[{c, i}] = isize(sagcells);
sagcells.emplace_back(c, i);
}
}
void compute_creq_neighbors() {
int SN = isize(sagcells);
neighbors.resize(SN);
vector<int> mindist_for(SN, 30000);
for(int i=0; i<SN; i++) {
auto& m = mindist_for[i];
for(int j=0; j<SN; j++) if(j != i) m = min<int>(m, sagdist[i][j]);
}
for(int i=0; i<SN; i++)
for(int j=0; j<SN; j++) if(i != j && sagdist[i][j] < mindist_for[i] + mindist_for[j]) neighbors[i].push_back(j);
max_sag_dist = 0;
for(auto x: sagdist) max_sag_dist = max<int>(max_sag_dist, x);
max_sag_dist++;
println(hlog, neighbors[0]);
hlog.flush();
}
vector<vector<pair<ld, subcell>>> dijkstra_edges;
void find_cells() {
println(hlog, "cellcount = ", cellcount);
ensure_subcell_points();
struct qitem {
ld dist; subcell sc; transmatrix T;
bool operator < (const qitem& b) const { return dist > b.dist + 1e-6; }
};
std::priority_queue<qitem> pq;
auto visit = [&] (subcell sc, ld dist, const transmatrix& T) {
if(ids.count(sc)) return;
pq.emplace(qitem{dist, sc, T});
};
sagsubcell_point.clear();
sagsubcell_inv.clear();
int Q = isize(subcell_points);
visit(subcell{cwt.at,0}, 0, Id);
ld maxdist0 = 0;
for(int i=0;; i++) {
if(pq.empty()) { println(hlog, "no more"); break; }
auto p = pq.top();
pq.pop();
ld dist = p.dist;
auto sc = p.sc;
transmatrix T = p.T;
if(ids.count(sc)) { i--; continue; }
if(i == cell_request-1) maxdist0 = dist;
if(i >= cell_request && dist > maxdist0 + 1e-6) break;
sagcells.push_back(sc);
sagsubcell_point.push_back(T * subcell_points[sc.second]);
sagsubcell_inv.push_back(inverse(T));
ids[sc] = i;
println(hlog, "cell ", i, " is ", sc, " at ", sagsubcell_point.back(), " in distance ", dist);
if(dijkstra_maxedge) {
dijkstra_edges.emplace_back();
auto& de = dijkstra_edges.back();
set<cell*> vis;
vector<tuple<cell*, transmatrix, int>> q;
auto visit1 = [&] (cell *c, transmatrix T, int d) {
if(vis.count(c)) return;
vis.insert(c);
q.emplace_back(c, T, d);
};
visit1(sc.first, Id, 0);
for(int i1=0; i1 < isize(q); i1++) {
cell *c = get<0>(q[i1]);
transmatrix T1 = get<1>(q[i1]);
int dist1 = get<2>(q[i1]);
if(dist1 < dijkstra_maxedge) for(int j=0; j<c->type; j++) {
cell *c1 = c->cmove(j);
visit1(c1, T1 * currentmap->adj(c, j), dist1+1);
}
for(int q=0; q<Q; q++) {
subcell sc1 {c, q};
ld ndist = dijkstra_tile ? dist1 : pdist(subcell_points[sc.second], T1 * subcell_points[q]);
de.push_back({ndist, sc1});
visit(sc1, dist + ndist, T*T1);
}
}
}
else {
for(int j=0; j<sc.first->type; j++) for(int k=0; k<Q; k++) {
cell *c1 = sc.first->cmove(j);
transmatrix T1 = T * currentmap->adj(sc.first, j);
visit(subcell{c1, k}, pdist(C0, T1*C0), T1);
}
}
}
int SN = isize(sagcells);
println(hlog, "number of cells found: ", SN, " dijkstra_maxedge = ", dijkstra_maxedge);
all_disk_cells_sorted = {};
for(auto p: ids) if(all_disk_cells_sorted.empty() || p.first.first != all_disk_cells_sorted.back()) all_disk_cells_sorted.push_back(p.first.first);
for(cell *c: all_disk_cells_sorted) c->mpdist = 0, c->land = laCanvas, c->landparam = 0x101010, c->wall = waNone;
}
void init_cell_request() {
println(hlog, "generating on cell request");
find_cells();
if(isize(subcell_points) == 1) {
compute_dists();
return;
}
int SN = isize(sagcells);
sagdist.init(SN, 0);
if(!dijkstra_maxedge) {
println(hlog, "computing sagdist ...");
parallelize(SN, [&] (int a, int b) {
for(int i=a; i<b; i++) {
for(int j=0; j<SN; j++) {
ld dist = pdist(sagsubcell_point[i], sagsubcell_point[j]);
sagdist[i][j] = int(dist * gdist_prec + 0.5);
if(i < j && sagdist[i][j] == 0) println(hlog, "for ", tie(i,j), " pdist computed as ", dist);
}
}
return 0;
});
println(hlog, "... done");
}
else {
vector<vector<pair<ld, int>>> dijkstra_edges_2;
dijkstra_edges_2.resize(SN);
for(int i=0; i<SN; i++) for(auto p: dijkstra_edges[i]) if(ids.count(p.second)) dijkstra_edges_2[i].emplace_back(p.first, ids[p.second]);
parallelize(SN, [&] (int a, int b) {
vector<ld> distances(SN);
for(int i=a; i<b; i++) {
if(i % 500 == 0) println(hlog, "computing dijkstra for ", i , "/", SN);
for(int j=0; j<SN; j++) distances[j] = HUGE_VAL;
std::priority_queue<pair<ld, int>> pq;
auto visit = [&] (int i, ld dist) {
if(distances[i] <= dist) return;
distances[i] = dist;
pq.emplace(-dist, i);
};
visit(i, 0);
while(!pq.empty()) {
ld d = -pq.top().first;
int at = pq.top().second;
pq.pop();
for(auto e: dijkstra_edges_2[at]) {
// println(hlog, "move from ", at, " to ", e.first, " for ", d, "+", e.second);
visit(e.second, d + e.first);
}
}
for(int j=0; j<SN; j++) sagdist[i][j] = distances[j] * gdist_prec + .5;
}
return 0;
});
}
compute_creq_neighbors();
}
bool distance_only;
void init_cells() {
if(state & SS_CELLS) return;
sag::init();
state |= SS_CELLS;
if(cell_request) {
if(distance_file != "") {
println(hlog, "loading graph ", distance_file);
sagdist.load(distance_file);
if(distance_only) {
sagcells.resize(sagdist.N, subcell{nullptr, 0});
}
else {
find_cells();
}
compute_creq_neighbors();
return;
}
init_cell_request();
return;
}
else if(legacy) state |= SS_NEED_SNAKE;
else init_cells_to_all();
if(!cell_request) compute_dists();
}
void init_snake_if_needed() {
if(!(state & SS_NEED_SNAKE)) return;
state &=~ SS_NEED_SNAKE;
init_snake(2 * isize(vdata));
compute_dists();
}
ld pdist(hyperpoint hi, hyperpoint hj) {
if(sol && angular) {
return 10 * asinh(hypot_d(3, lie_log(shiftless(gpushxto0(hi) * hj))) / 10);
}
if(sol) return min(geo_dist(hi, hj), geo_dist(hj, hi));
if(mproduct && angular) {
auto di = product_decompose(hi);
auto dj = product_decompose(hj);
ld x = hdist(di.second, dj.second);
ld z = di.first - dj.first;
auto res = sqrt((x*x+z*z) * (x > 0 ? sinh(x) / x : 1));
return res;
}
return geo_dist(hi, hj);
};
void geo_stats() {
init_cells();
println(hlog, "counting sagdist, N=", int(sagdist.N), " max_sag_dist = ", max_sag_dist);
vector<short> sgdc(max_sag_dist, 0);
for(auto x: sagdist) sgdc[x]++;
println(hlog, "building sorted_sagdist");
vector<short> sorted_sagdist;
for(int i=0; i<max_sag_dist; i++) for(int j=0; j<sgdc[i]; j++) sorted_sagdist.push_back(i);
println(hlog, "computing min_max_nei");
int minnei = 500, maxnei = 0;
int SN = sagdist.N;
for(int i=0; i<SN; i++) for(int j: neighbors[i]) {
if(sagdist[i][j] < minnei) minnei = sagdist[i][j];
if(sagdist[i][j] > maxnei) maxnei = sagdist[i][j];
}
for(int i=0; i<3; i++) {
bool first = false;
#define out(x, y) if(i == 0) println(hlog, x, " = ", y); else if(first) print(hlog, ";"); first = true; if(i == 1) print(hlog, x); if(i == 2) print(hlog, y);
out("nodes", SN);
out("maxsagdist", max_sag_dist);
out("dim", (euclid && WDIM == 2 && euc::eu.user_axes[1][1] == 1) ? 1 : WDIM);
out("geometry", S3 >= OINF ? "tree" : hyperbolic ? "hyperbolic" : sphere ? "sphere" : euclid ? "euclid" : nil ? "nil" : sol ? "solv" : mproduct ? "product" : "other");
out("closed", max_sag_dist == isize(sagcells) ? 0 : closed_manifold ? 1 : 0);
out("angular", angular);
for(int p: {1, 10, 50}) { out(format("sagdist%02d", p), sorted_sagdist[(p * sorted_sagdist.size()) / 100]); }
out("minnei", minnei);
out("maxnei", maxnei);
out("neighbors", isize(neighbors[0]));
println(hlog);
#undef out
}
}
bool visualize_subcells_on = false;
bool visualize_subcells(cell *c, const shiftmatrix& V) {
if(!visualize_subcells_on) return false;
for(int i=0; i<isize(subcell_points); i++) {
auto p = at_or_null(ids, pair<cell*,int>{c, i});
if(!p) continue;
queuepolyat(V * rgpushxto0(subcell_points[i]), cgi.shSnowball, 0x80FF80FF, PPR::FLOORb);
if(sagsubcell_inv.size()) for(auto nei: neighbors[*p]) if(nei<*p) {
queueline(V * subcell_points[i], V * sagsubcell_inv[*p] * sagsubcell_point[nei], 0x8000FF, 3).prio = PPR::FLOORa;
}
}
return false;
}
int cell_read_args() {
#if CAP_COMMANDLINE
using namespace arg;
if(0) ;
else if(argis("-sag_gdist")) {
shift(); gdist_prec = argi();
}
else if(argis("-sag_gdist_dijkstra")) {
shift(); dijkstra_maxedge = argi(); dijkstra_tile = false;
}
else if(argis("-sag-dtile")) {
dijkstra_tile = true; dijkstra_maxedge = 1;
}
else if(argis("-sag_gdist_save")) {
init_cells();
shift();
sagdist.save(args());
}
else if(argis("-sag_gdist_load")) {
distance_only = false;
shift(); distance_file = args();
}
else if(argis("-sag-gdist_load1")) {
distance_only = true;
shift(); distance_file = args();
}
else if(argis("-sag-angular")) {
shift(); angular = argi();
}
else if(argis("-sag-geo-stats")) geo_stats();
else if(argis("-sag-creq")) {
shift(); cell_request = argi();
}
else if(argis("-sag-initcells")) {
init_cells();
}
else if(argis("-gen-subcellpoints")) {
generate_subcellpoints();
}
else if(argis("-subcellpoints-off")) {
subcell_points.clear();
}
/* to viz only subcellpoints */
else if(argis("-sag-clear")) {
shmup::monstersAt.clear();
}
else return 1;
#endif
return 0;
}
int ah = addHook(hooks_args, 100, cell_read_args);
}
}
}