Compare commits
5 Commits
f7c5610c95
...
master
Author | SHA1 | Date | |
---|---|---|---|
c618d22f0b
|
|||
585fdc3a7e
|
|||
4dae604485
|
|||
c4ef11f38e
|
|||
264f381306
|
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# swarmc
|
||||
|
||||
A [Particle Swarm
|
||||
Optimisation](https://en.wikipedia.org/wiki/Particle_swarm_optimization)
|
||||
visualiser in C++. Run with `swarm <iteration count>` and enjoy the pretty
|
||||
colors. Pressing `h` will print out a help menu.
|
||||
|
||||
Requires an ANSI terminal (emulator) with support for 24-bit colours to render.
|
||||
|
||||
The implemented showcase works for `R^2 -> R` functions, but the underlying code
|
||||
can work for much more, including for spaces other than real spaces (see the
|
||||
type parameters in `Agent` et al).
|
||||
|
||||
Supports dynamically changing Particle parameters.
|
||||
Although not implemented (yet!), the code supports arbitrary swarm topologies
|
@@ -40,19 +40,23 @@ struct Screen {
|
||||
Symbol &at(int x, int y);
|
||||
Symbol &at(float x, float y);
|
||||
|
||||
void resize(std::size_t n);
|
||||
void resize(std::size_t x, std::size_t y);
|
||||
|
||||
void move_to(float x, float y);
|
||||
|
||||
std::pair<float, float> screen_to_xy(int w, int h);
|
||||
std::pair<int, int> xy_to_screen(float x, float y);
|
||||
|
||||
Screen(std::size_t n) : buf(nullptr) { resize(n); }
|
||||
Screen(std::size_t x, std::size_t y) : buf(nullptr) { resize(x, y); }
|
||||
Screen(std::size_t x, std::size_t y) : buf(nullptr)
|
||||
{ resize(x, y); move_to(0, 0); }
|
||||
~Screen() { delete[] buf; }
|
||||
|
||||
std::vector<const vec<2>*> points;
|
||||
std::vector<std::pair<const vec<2>*,const vec<2>*>> vecs;
|
||||
char_shader_t shader;
|
||||
|
||||
bool draw_vecs = true;
|
||||
|
||||
private:
|
||||
Symbol *buf;
|
||||
Symbol _dummy;
|
||||
|
@@ -66,9 +66,9 @@ namespace ParamChange {
|
||||
float visc, float nostal, float peerp, int curr, int max) {
|
||||
float pct_done = (float)curr / (float)max;
|
||||
return {
|
||||
visc - (0.9f - 0.4f) * pct_done,
|
||||
nostal - (2.5f - 0.5f) * pct_done,
|
||||
peerp + (2.5f - 0.5f) * pct_done,
|
||||
visc - (visc - 0.4f) * pct_done,
|
||||
nostal - (nostal - 0.5f) * pct_done,
|
||||
peerp - (peerp - 2.5f) * pct_done,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -80,9 +80,9 @@ namespace ParamChange {
|
||||
float pct_done = (float)curr / (float)max;
|
||||
float scale_factor = (std::tanh(steepness * (pct_done - 0.5)) + 1) / 2;
|
||||
return {
|
||||
visc - (0.9f - 0.4f) * scale_factor,
|
||||
nostal - (2.5f - 0.5f) * scale_factor,
|
||||
peerp + (2.5f - 0.5f) * scale_factor,
|
||||
visc - (visc - 0.4f) * scale_factor,
|
||||
nostal - (nostal - 0.5f) * scale_factor,
|
||||
peerp - (peerp - 2.5f) * scale_factor,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -132,6 +132,7 @@ struct Particle : public Agent<vec<N>> {
|
||||
}
|
||||
|
||||
const vec<N> &get_position() const { return position; };
|
||||
const vec<N> &get_velocity() const { return velocity; };
|
||||
|
||||
private:
|
||||
A alg;
|
||||
|
50
src/main.cpp
50
src/main.cpp
@@ -8,6 +8,7 @@ static constexpr int kFPS = 60;
|
||||
static constexpr float kDT = 1.0 / kFPS;
|
||||
|
||||
float f(vec<2> x) {
|
||||
// return std::pow(x.x, 2) + std::pow(x.y, 2);
|
||||
return 50 * (std::pow(std::sin((x.x - 10)/2),2) + std::pow(std::sin(x.y/2),2)) +
|
||||
std::pow(std::abs(x.x - 3.141592653589), 1.2) + std::pow(std::abs(x.y), 1.2);
|
||||
}
|
||||
@@ -53,6 +54,10 @@ main(int argc, char **argv) {
|
||||
|
||||
for(const auto &p : swarm.get_particles()) {
|
||||
scr.points.push_back(&p.get_position());
|
||||
scr.vecs.push_back({
|
||||
&p.get_position(),
|
||||
&p.get_velocity(),
|
||||
});
|
||||
}
|
||||
|
||||
enter_noncanonical_mode();
|
||||
@@ -61,6 +66,7 @@ main(int argc, char **argv) {
|
||||
|
||||
bool pause = false;
|
||||
bool frame_step = false;
|
||||
bool auto_follow = false;
|
||||
|
||||
// initialize colorizer scale/translate
|
||||
scr.draw();
|
||||
@@ -75,6 +81,15 @@ main(int argc, char **argv) {
|
||||
255, 255, 255, x.x, x.y, y);
|
||||
};
|
||||
|
||||
auto center_screen = [&]() -> void {
|
||||
vec<2> pos = 0;
|
||||
for(const auto &p : swarm.get_particles()) {
|
||||
pos += p.get_position();
|
||||
}
|
||||
pos /= swarm.get_particles().size();
|
||||
scr.move_to(pos.x, pos.y);
|
||||
};
|
||||
|
||||
// We draw to the screen at a rate of `kFPS', but step()ing the swarm at
|
||||
// this rate would be far too fast to be interesting to look at. On the
|
||||
// other hand, step()ing once a second is too slow.
|
||||
@@ -98,6 +113,8 @@ main(int argc, char **argv) {
|
||||
printf("Current iteration: %d\nCurrent frame: %d\n", i * 4 / kFPS, i);
|
||||
}
|
||||
|
||||
if(auto_follow) center_screen();
|
||||
|
||||
if(frame_step) {
|
||||
pause = true;
|
||||
frame_step = false;
|
||||
@@ -111,6 +128,14 @@ main(int argc, char **argv) {
|
||||
case ' ':
|
||||
pause = !pause;
|
||||
break;
|
||||
case ',':
|
||||
for(int j = 0; j < kFPS/4; ++j) {
|
||||
swarm.move(kDT * 4);
|
||||
if(i % (kFPS/4) == (kFPS/4)-1)
|
||||
swarm.step();
|
||||
|
||||
++i;
|
||||
}
|
||||
case '.':
|
||||
pause = false;
|
||||
frame_step = true;
|
||||
@@ -166,11 +191,30 @@ main(int argc, char **argv) {
|
||||
update_and_draw();
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
scr.draw_vecs = !scr.draw_vecs;
|
||||
break;
|
||||
|
||||
case 'b': {
|
||||
const auto [_, b] = swarm.best();
|
||||
scr.move_to(b.x, b.y);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'c':
|
||||
center_screen();
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
auto_follow = !auto_follow;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
printf(" movement zoom coloring pause step \n"
|
||||
" W i I K SPC . \n"
|
||||
" ASD o O L \n"
|
||||
" quit: q \n");
|
||||
" W io IK LO SPC ., \n"
|
||||
" ASD \n"
|
||||
" [%c] draw (v)elocities quit: q \n",
|
||||
scr.draw_vecs ? 'x' : ' ');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -4,11 +4,6 @@
|
||||
|
||||
/* Screen */
|
||||
|
||||
void Screen::resize(std::size_t n) {
|
||||
w = n; h = 1;
|
||||
buf = (Symbol *)realloc(buf, n * sizeof(Symbol));
|
||||
}
|
||||
|
||||
void Screen::resize(std::size_t x, std::size_t y) {
|
||||
w = x; h = y;
|
||||
buf = (Symbol *)realloc(buf, x * y * sizeof(Symbol));
|
||||
@@ -41,6 +36,10 @@ std::pair<int, int> Screen::xy_to_screen(float x, float y) {
|
||||
};
|
||||
}
|
||||
|
||||
void Screen::move_to(float x, float y) {
|
||||
dx = x - w*sx/2;
|
||||
dy = y - h*sy/2;
|
||||
}
|
||||
|
||||
void Screen::clear() {
|
||||
static const Symbol s {' ', {0,0,0}};
|
||||
@@ -57,6 +56,32 @@ void Screen::draw() {
|
||||
}
|
||||
}
|
||||
|
||||
// draw lines for each velocity vector
|
||||
if(draw_vecs) {
|
||||
for(const auto &[start, vel] : vecs) {
|
||||
auto end = *start + (*vel) / 3;
|
||||
if(start->x == end.x) {
|
||||
// TODO: draw horizontal/vertical lines
|
||||
continue;
|
||||
}
|
||||
|
||||
auto a = (end.y - start->y) / (end.x - start->x);
|
||||
auto b = start->y - a * start->x;
|
||||
|
||||
for(int i = 0; i < w; ++i) {
|
||||
const auto [x, _] = screen_to_xy(i, 0);
|
||||
if(x < std::min(start->x, end.x)
|
||||
|| x > std::max(start->x, end.x)) continue;
|
||||
|
||||
auto y = a*x + b;
|
||||
at(x, y) = Symbol{
|
||||
.sym = '+',
|
||||
.color = { 0.75, 0, 0 },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write out a '#' wherever we have a point registered
|
||||
for(const auto &p : points) {
|
||||
at(p->x, p->y) = {.sym = '#', .color = 1};
|
||||
|
Reference in New Issue
Block a user