From 6213d11d9399e0c20471b7234b8f36b044bace0c Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sat, 17 Nov 2018 20:25:47 +0100 Subject: [PATCH] rogueviz:: added flocking simulation --- rogueviz-flocking.cpp | 332 ++++++++++++++++++++++++++++++++++++++++++ rogueviz.cpp | 6 + rogueviz.h | 2 +- 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 rogueviz-flocking.cpp diff --git a/rogueviz-flocking.cpp b/rogueviz-flocking.cpp new file mode 100644 index 00000000..8f13c801 --- /dev/null +++ b/rogueviz-flocking.cpp @@ -0,0 +1,332 @@ +// flocking simulations +// Copyright (C) 2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details + +// example parameters: + +// flocking on a torus: +// -tpar 21,4 -geo 6 -flocking 10 -rvshape 3 + +// flocking on the Zebra quotient: +// -geo 4 -flocking 10 -rvshape 3 -zoom .9 + +// press 'o' when flocking active to change the parameters + +namespace hr { + hyperpoint nearcorner(cell *c, int i); + } + +namespace rogueviz { + +inline ld atan2(hyperpoint h) { + return std::atan2(h[1], h[0]); + } + +namespace flocking { + + int N; + + bool draw_lines = false; + + map> relmatrices; + + ld ini_speed = .5; + ld max_speed = 1; + + ld sep_factor = 1.5; + ld sep_range = .25; + + ld align_factor = 1; + ld align_range = .5; + + ld coh_factor = 1; + ld coh_range = .5; + + ld check_range = 2.5; + + vector > lines; + + void init() { + if(!bounded) { + addMessage("Flocking simulation needs a bounded space."); + return; + } + stop_game(); + rogueviz::init(); kind = kFlocking; + vdata.resize(N); + + const auto v = currentmap->allcells(); + + printf("computing relmatrices...\n"); + for(cell* c1: v) { + manual_celllister cl; + cl.add(c1); + for(int i=0; ivel = ini_speed; + } + + storeall(); + printf("done\n"); + } + + void simulate(int delta) { + ld d = delta / 1000.; + using namespace hyperpoint_vec; + int N = isize(vdata); + vector pats(N); + vector vels(N); + using shmup::monster; + + map> monsat; + + for(int i=0; ibase].push_back(m); + } + + lines.clear(); + + for(int i=0; ivel, 0, 0); + + transmatrix I = inverse(m->at); + + // if(i == 0) display(I); + + hyperpoint sep = hpxyz(0, 0, 0); + int sep_count = 0; + + hyperpoint align = hpxyz(0, 0, 0); + int align_count = 0; + + hyperpoint coh = hpxyz(0, 0, 0); + int coh_count = 0; + + m->findpat(); + + for(auto& p: relmatrices[m->base]) { + for(auto m2: monsat[p.first]) if(m != m2) { + ld vel2 = m2->vel; + transmatrix at2 = I * p.second * m2->at; + hyperpoint ac = tC0(at2); + ld di = hdist0(ac); + ld alpha = -atan2(ac); + + color_t col = 0; + + if(di < align_range) { + align += gpushxto0(ac) * at2 * hpxyz(vel2, 0, 0); + align_count++; + col = 0xFF00FF; + } + + if(di < check_range) { + coh += spin(alpha) * hpxyz(di, 0, 0); + coh_count++; + } + + if(di < sep_range) { + sep -= spin(alpha) * hpxyz(1 / di, 0, 0), sep_count++; + col = 0xFF0000FF; + } + + if(col && draw_lines) + lines.emplace_back(m->pat * C0, m->pat * at2 * C0, col); + } + } + + if(sep_count) velvec += sep * (d * sep_factor / sep_count); + if(align_count) velvec += align * (d * align_factor / align_count); + if(coh_count) velvec += coh * (d * coh_factor / coh_count); + + if(i == 0) { + printf("%s\n", display(velvec)); + // lines.emplace_back(gmatrix[m->base] * m->at * C0, gmatrix[m->base] * m->at * (C0 + velvec)); + // lines.emplace_back(gmatrix[m->base] * m->at * C0, gmatrix[m->base] * m->at * (C0 + sep / hypot2(sep))); + } + + vels[i] = hypot2(velvec); + ld alpha = -atan2(velvec); + + if(vels[i] > max_speed) { + velvec = velvec * (max_speed / vels[i]); + vels[i] = max_speed; + } + + pats[i] = m->pat * spin(alpha) * xpush(vels[i] * d); + } + for(int i=0; irebasePat(pats[i]); + m->vel = vels[i]; + } + shmup::fixStorage(); + } + + bool turn(int delta) { + if(!on) return false; + if(kind == kFlocking) simulate(delta), timetowait = 0; + return false; + // shmup::pc[0]->rebase(); + } + + #if CAP_COMMANDLINE + int readArgs() { + using namespace arg; + + // options before reading + if(0) ; + else if(argis("-flocking")) { + shift(); N = argi(); + init(); + } + else if(argis("-cohf")) { + shift(); coh_factor = argf(); + } + else if(argis("-alignf")) { + shift(); align_factor = argf(); + } + else if(argis("-sepf")) { + shift(); sep_factor = argf(); + } + else return 1; + return 0; + } + + void flock_marker() { + if(draw_lines) + for(auto p: lines) queueline(get<0>(p), get<1>(p), get<2>(p), 0); + } + + bool akh(int sym, int uni) { + if(uni == '7') { + for(int a=0; a<200; a++) { + drawthemap(); + for(int b=0; b<40; b++) turn(1); + char buf[2000]; + snprintf(buf, 1000, "animations/flock/flock%d-%03d.png", int(geometry), a); + saveHighQualityShot(buf); + } + } + else return false; + return true; + } + + void show() { + cmode = sm::SIDE; + gamescreen(0); + dialog::init(XLAT("flocking"), iinf[itPalace].color, 150, 0); + + dialog::addSelItem("initial speed", fts(ini_speed), 'i'); + dialog::add_action([]() { + dialog::editNumber(ini_speed, 0, 2, .1, .5, "", ""); + }); + + dialog::addSelItem("max speed", fts(max_speed), 'm'); + dialog::add_action([]() { + dialog::editNumber(max_speed, 0, 2, .1, .5, "", ""); + }); + + dialog::addSelItem("separation factor", fts(sep_factor), 's'); + dialog::add_action([]() { + dialog::editNumber(sep_factor, 0, 2, .1, 1.5, "", ""); + }); + + dialog::addSelItem("separation range", fts(sep_range), 'S'); + dialog::add_action([]() { + dialog::editNumber(sep_range, 0, 2, .1, .5, "", ""); + }); + + dialog::addSelItem("alignment factor", fts(align_factor), 'a'); + dialog::add_action([]() { + dialog::editNumber(align_factor, 0, 2, .1, 1.5, "", ""); + }); + + dialog::addSelItem("alignment range", fts(align_range), 'A'); + dialog::add_action([]() { + dialog::editNumber(align_range, 0, 2, .1, .5, "", ""); + }); + + dialog::addSelItem("cohesion factor", fts(coh_factor), 'c'); + dialog::add_action([]() { + dialog::editNumber(coh_factor, 0, 2, .1, 1.5, "", ""); + }); + + dialog::addSelItem("cohesion range", fts(coh_range), 'C'); + dialog::add_action([]() { + dialog::editNumber(coh_range, 0, 2, .1, .5, "", ""); + }); + + dialog::addSelItem("check range", fts(check_range), 't'); + dialog::add_action([]() { + ld radius = 0; + for(cell *c: currentmap->allcells()) + for(int i=0; idegree(); i++) { + hyperpoint h = nearcorner(c, i); + radius = max(radius, hdist0(h)); + } + dialog::editNumber(check_range, 0, 2, .1, .5, "", + "Value used in the algorithm: " + "only other boids in cells whose centers are at most 'check range' from the center of the current cell are considered. " + "Should be more than the other ranges by at least double the cell radius (in the current geometry, double the radius is " + fts(radius*2) + "); " + "but too large values slow the simulation down." + ); + }); + + dialog::addSelItem("number of boids", its(N), 'n'); + dialog::add_action([]() { + dialog::editNumber(N, 0, 1000, 1, 20, "", ""); + }); + + dialog::addSelItem("change geometry", XLAT(ginf[geometry].name), 'g'); + dialog::add_action(runGeometryExperiments); + + dialog::addBoolItem("draw forces", draw_lines, 'l'); + dialog::add_action([] () { draw_lines = !draw_lines; }); + + dialog::addBreak(100); + + dialog::addItem("restart", 'r'); + dialog::add_action(init); + + dialog::addBack(); + dialog::display(); + } + + named_functionality o_key() { + if(kind == kFlocking) return named_dialog("flocking", show); + return named_functionality(); + } + + auto hooks = + addHook(hooks_args, 100, readArgs) + + addHook(shmup::hooks_turn, 100, turn) + + addHook(hooks_frame, 100, flock_marker) + + addHook(hooks_handleKey, 120, akh) + + addHook(hooks_o_key, 80, o_key) + + 0; + #endif + + } + +} diff --git a/rogueviz.cpp b/rogueviz.cpp index 5502b6ad..bbfa30c2 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -27,6 +27,10 @@ #include "rogueviz.h" +namespace hr { +extern hpcshape shEagle; +} + namespace rogueviz { const transmatrix centralsym = {{{-1,0,0}, {0,-1,0}, {0,0,-1}}}; @@ -1128,6 +1132,7 @@ void queuedisk(const transmatrix& V, const colorpair& cp, bool legend, const str if(cp.shade == 's') queuepoly(V1, shDiskS, darken_a(cp.color2)); if(cp.shade == 'q') queuepoly(V1, shDiskSq, darken_a(cp.color2)); if(cp.shade == 'm') queuepoly(V1, shDiskM, darken_a(cp.color2)); + if(cp.shade == 'b') queuepoly(V1, shEagle, darken_a(cp.color2)); } unordered_map, int> drawn_edges; @@ -2285,3 +2290,4 @@ auto hooks = #include "rogueviz-graph.cpp" #include "rogueviz-fundamental.cpp" #include "rogueviz-sunflower.cpp" +#include "rogueviz-flocking.cpp" diff --git a/rogueviz.h b/rogueviz.h index 8222bc6a..83a34a68 100644 --- a/rogueviz.h +++ b/rogueviz.h @@ -3,7 +3,7 @@ namespace rogueviz { using namespace hr; - enum eVizkind { kNONE, kAnyGraph, kTree, kSpiral, kSAG, kCollatz, kFullNet, kKohonen }; + enum eVizkind { kNONE, kAnyGraph, kTree, kSpiral, kSAG, kCollatz, kFullNet, kKohonen, kFlocking }; extern eVizkind kind; extern bool on;