// Simulation code for Inferring collective behavior from a fossilized fish shoal // 10/29/2018 updated by N.Mizumoto // The simulation program was implemented in Microsoft Visual Studio C++ 2017 // libraries #include "stdafx.h" #include #include // cout; cin #include // to_string #include // writing #include using namespace std; // Random sampling #include //std::random_device rd; //std::mt19937 mt(rd()); //std::uniform_real_distribution rnd(0.000, 1.000); #include std::uniform_real_distribution distribution(0.0, 1.0); std::random_device seed_gen; std::mt19937 engine(seed_gen()); auto rnd = [&] { return distribution(engine); }; #define pi 3.1415926535 #define nipi 3.1415926535*2 // random angle double dir; double allocate() { dir = rnd()*nipi; return(dir); } // PBC // modification for PBC double aPBC(double Loc, double Length) { if (Loc >= Length) { Loc -= Length; } else if (Loc < 0) { Loc += Length; } return(Loc); } ///*// opencv #include #include #include using namespace cv; //*/ // Parameters #define n 257 #define L 200 #define zor 1 // zone of replusion #define zoo 16 // zone of orientation #define st 10000 // * 0.1 second #define RotateLimit 30*pi/180 #define maxspeed 7 #define minspeed 3 #define sd 0.2 // rad (0-0.2) #define size 5 // Variables vector Cx(n), Cy(n); // C: position vector vector C2x(n), C2y(n); // C: position vector vector Vx(n), Vy(n); // V: unit direction vector vector Dx(n), Dy(n); // D: desired direction vector vector Vangle(n); vector Dangle(n); vector n1(n), Dr1x(n), Dr1y(n); vector n2(n), Do2x(n), Do2y(n); double AllDis[n][n], Rx[n][n], Ry[n][n], Pangle[n][n]; int i, j, step, nothing, z, iter; double xdis, ydis, nearangle, tangle, RotateDirection, r, speed; int main() { // Output first std::string FileName; int OK, plot; cout << "Simulations for Boids" << endl; cout << "Plot?" << endl; cin >> plot; cout << "Please enter the number of replications" << endl; cin >> iter; cout << "The values of parameters are as follow:" << endl; cout << "Number of individuals: " << n << endl; cout << "Field Size: " << L << endl; cout << "Repulsion radius: " << zor << endl; cout << "Orientation/Attraction radius: " << zoo << endl; cout << "Turining rate: " << RotateLimit << endl; cout << "Max Speed: " << maxspeed << endl; cout << "Min Speed: " << minspeed << endl; cout << "Error: " << sd << endl; cout << "Timesteps: " << st << endl; cout << "Are you OK?" << endl; cout << "Yes: 1, No: 2" << endl; cin >> OK; if (OK == 2) { return 1; } // parameter output FileName = "res_boid_sim"; FileName += "_parameters"; FileName += ".csv"; std::ofstream ofs(FileName); ofs << "NumOfIndividuals, FieldSize, Repulsion, Orientation/Attraction, FieldOfPerception, TurningRate, MaxSpeed, MinSpeed, Error, Seconds" << std::endl; ofs << n << "," << L << "," << zor << "," << zoo << "," << << RotateLimit << ","; ofs << maxspeed << "," << minspeed << "," << sd << "," << st << std::endl; ofs.close(); ofs.clear(); // replication for (z = 0; z < iter; z++) { // initialization for (i = 0; i maxspeed) { speed = maxspeed; } Cx[i] = Cx[i] + Vx[i] * speed * 0.1; Cy[i] = Cy[i] + Vy[i] * speed * 0.1; Cx[i] = aPBC(Cx[i], L); Cy[i] = aPBC(Cy[i], L); C2x[i] = Cx[i] - size * Vx[i]; C2y[i] = Cy[i] - size * Vy[i]; } // image output if (plot>0) { Mat Img(Size(L, L), CV_8UC3, Scalar(255, 255, 255)); namedWindow("search image", cv::WINDOW_AUTOSIZE); for (i = 0; i abs(L - abs(Cx[i] - Cx[j]))) { if (Cx[i] > Cx[j]) { Rx[i][j] = (Cx[j] + L) - Cx[i]; } else { Rx[i][j] = (Cx[j] - L) - Cx[i]; } } else { Rx[i][j] = Cx[j] - Cx[i]; } if (abs(Cy[i] - Cy[j]) > abs(L - abs(Cy[i] - Cy[j]))) { if (Cy[i] > Cy[j]) { Ry[i][j] = (Cy[j] + L) - Cy[i]; } else { Ry[i][j] = (Cy[j] - L) - Cy[i]; } } else { Ry[i][j] = Cy[j] - Cy[i]; } AllDis[i][j] = sqrt((Rx[i][j] * Rx[i][j]) + (Ry[i][j] * Ry[i][j])); Pangle[i][j] = atan(Ry[i][j] / Rx[i][j]); if (Rx[i][j] < 0) { Pangle[i][j] += pi; } if (Pangle[i][j] < 0) { Pangle[i][j] += nipi; } } else { Rx[i][j] = -Rx[j][i]; Ry[i][j] = -Ry[j][i]; AllDis[i][j] = AllDis[j][i]; if (Pangle[j][i] < pi) { Pangle[i][j] = Pangle[j][i] + pi; } else { Pangle[i][j] = Pangle[j][i] - pi; } } } } // rule 1: replusion for (i = 0; i 0) { Dx[i] = Dr1x[i]; Dy[i] = Dr1y[i]; } else { if (n2[i] > 0) { Dx[i] = Do2x[i]; Dy[i] = Do2y[i]; } else { Dx[i] = Vx[i]; Dy[i] = Vy[i]; } } //// turning limitation // After the above process, individuals turn towards the direction vector D by the turning rate Thita. // If the angle between V and D is less than Thita * tau, V = D. // "atan" returns -pi/2 ~ pi/2 → xが負なら+piで補正。最終的に負なら+2piで補正。これで 0~2piの範囲になるはず。 Dangle[i] = atan(Dy[i] / Dx[i]); if (Dx[i] < 0) { Dangle[i] += pi; } if (Dangle[i] < 0) { Dangle[i] += nipi; } // error Dangle[i] += WrappedNormal(sd); // rotating direction is positive +1 or negative -1? if (Dangle[i] > Vangle[i]) { if (Dangle[i] - Vangle[i] < pi) { RotateDirection = Dangle[i] - Vangle[i]; } else if (Dangle[i] - Vangle[i] > pi) { RotateDirection = Dangle[i] - Vangle[i] - nipi; } else if (Dangle[i] - Vangle[i] == pi) { if (rnd() > 0.5) { RotateDirection = pi; } else { RotateDirection = -pi; } } } else { if (Dangle[i] - Vangle[i] > -pi) { RotateDirection = Dangle[i] - Vangle[i]; } else if (Dangle[i] - Vangle[i] < -pi) { RotateDirection = Dangle[i] - Vangle[i] + nipi; } else { if (rnd() > 0.5) { RotateDirection = pi; } else { RotateDirection = -pi; } } } // limitation if (abs(RotateDirection) > RotateLimit) { RotateDirection = RotateDirection / abs(RotateDirection) * RotateLimit; } // timestep = 0.1 second RotateDirection *= 0.1; Vangle[i] += RotateDirection; if (Vangle[i] < 0) { Vangle[i] += nipi; } if (Vangle[i] > nipi) { Vangle[i] -= nipi; } //// move Vx[i] = cos(Vangle[i]); Vy[i] = sin(Vangle[i]); } } // results output FileName = "res_boid_sim"; FileName += "_rep"; FileName += std::to_string(1 + z); FileName += ".csv"; ofs.open(FileName); ofs << "id, x, y, Angle" << endl; for (i = 0; i