codepad
[
create a new paste
]
login
|
about
Language:
C
C++
D
Haskell
Lua
OCaml
PHP
Perl
Plain Text
Python
Ruby
Scheme
Tcl
/* * Ball Collision, simulates collision between several * freely moving rigid balls. * Uses FreeGLUT for graphics. * Press SPACE or ENTER to generate new circles, ESC or Q to quit. */ #include <cstdlib> // for rand/srand #include "GL/freeglut.h" // normally should be <> #include <ctime> // for clock #include <iostream> // logging to console #include <cmath> // for hypot // // simple definitions /// 2D vector struct Vec2 { double x, y; Vec2(double nx = 0.0, double ny = 0.0) : x(nx), y(ny) { } Vec2& operator+=(const Vec2 &other) { x += other.x; y += other.y; return *this; } Vec2& operator*=(const double coef) { x *= coef; y *= coef; return *this; } double length() const { return hypot(x, y); } }; inline Vec2 operator+(const Vec2 &a, const Vec2 &b) { return Vec2(a.x + b.x, a.y + b.y); } inline Vec2 operator-(const Vec2 &a, const Vec2 &b) { return Vec2(a.x - b.x, a.y - b.y); } inline Vec2 operator*(double coef, const Vec2 &v) { return Vec2(coef * v.x, coef * v.y); } inline double operator*(const Vec2 &a, const Vec2 &b) { return a.x * b.x + a.y * b.y; } std::ostream& operator<<(std::ostream &os, const Vec2 &v) { return os << '(' << v.x << ", " << v.y << ')'; } /// RGBA color struct Color { float red, green, blue, alpha; Color(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f) : red(r), green(g), blue(b), alpha(a) { } void select() const { glColor4f(red, green, blue, alpha); } }; /// rigid ball struct Ball { Vec2 p; ///< position Vec2 v; ///< velocity Vec2 a; ///< acceleration double m; ///< mass double r; ///< radius Color color; Ball ( const Vec2 &pos = Vec2(), const Vec2 &vel = Vec2(), double mass = 1.0, double rad = 10.0, const Color &col = Color(1.0f, 1.0f) // yellow by default ) : p(pos), v(vel), m(mass), r(rad), color(col), a() { } /// OpenGL code to bring Ball to the frame buffer is here void paint() const { glLoadIdentity(); glTranslated(p.x, p.y, 0.0); glScaled(r, r, r); color.select(); glCallList(1); } /// calculate dynamics void move(double time_step) { const Vec2 ta = time_step * a; p += time_step * (v + 0.5 * ta); v += ta; a = Vec2(); } }; std::ostream& operator<<(std::ostream &os, const Ball &c) { return os << "position: " << c.p << "\nvelocity: " << c.v << "\nmass: " << c.m << "\nradius: " << c.r << std::endl; } // // constants and current (global) state // /// amount of bodies const size_t N = 20; /// relaxation coefficient (should fall into [0, 1]) const double k = 0.9; /// central force constant const double G = 50000.0; /// actual bodies Ball C[N]; /// last frame time moment std::clock_t t; /// window width and height float width = 800.0f, height = 600.0f; // // aux functions // /// returns a random number from [minv, maxv) inline double random(double minv, double maxv) { static const double anti_rand_max = 1.0 / double(RAND_MAX); return minv + (maxv - minv) * std::rand() * anti_rand_max; } /// generates randomly moving circles going to collide somewhere void newBalls() { std::cout << "\nGenerating new circles:\n"; // target collision point const Vec2 cp(random(0.1 * width, 0.9 * width), random(0.1 * height, 0.9 * height)); std::cout << "Collision point: " << cp << "\n"; for (size_t i = 0; i < N; ++i) { Ball &b = C[i]; // setup C[i] fields b.p = Vec2(random(0.0, width), random(0.0, height)); // position b.v = 0.25 * (cp - b.p); // 4 seconds before it may reach the collision point b.m = random(0.1, 10.0); // mass b.r = random(4.0, 20.0); // radius b.color.red = static_cast<float>(random(0.0, 1.0)); b.color.green = static_cast<float>(random(0.0, 1.0)); b.color.blue = static_cast<float>(random(0.0, 1.0)); std::cout << i << ")\n" << b; } } /// gravity-like force, pulling to the screen center inline void applyCentralForce(Ball &ball) { Vec2 r(0.5 * width - ball.p.x, 0.5 * height - ball.p.y); const double dist = (std::max)(r.length(), 1.0); r *= G / (dist * dist * dist); ball.a += r; } // // GLUT callbacks // /// redraw our scene void repaint() { glClear(GL_COLOR_BUFFER_BIT); // paint all balls for (size_t i = 0; i < N; ++i) C[i].paint(); // finish and present our frame to the user glFinish(); glutSwapBuffers(); } /// calculate the next frame void frameMove() { static const double anti_clocks_per_sec = 1.0 / double(CLOCKS_PER_SEC); // check time std::clock_t t1 = std::clock(); if(t1 != t) { const double h = anti_clocks_per_sec * (t1 - t); t = t1; // move for (size_t i = 0; i < N; ++i) { applyCentralForce(C[i]); C[i].move(h); } // check for intersection for (size_t i = 0; i < N; ++i) { for (size_t j = i + 1; j < N; ++j) { const Vec2 dp = C[i].p - C[j].p; const double dplen = dp.length(); const double delta = dplen - (C[i].r + C[j].r); if (delta < 0.0) // are overlapping? { const Vec2 r = 1.0 / dplen * dp; if ( (C[i].v - C[j].v) * r < 0.0) // are approaching? { // collision detected -- calculate new velocities const Vec2 u(-r.y, r.x); // old velocities in (r, u) basis const double v1r = C[i].v * r, v2r = C[j].v * r, v1u = C[i].v * u, v2u = C[j].v * u, // new velocities v1n = k / (C[i].m + C[j].m) * ((C[i].m - C[j].m) * v1r + 2.0 * C[j].m * v2r), v2n = k / (C[i].m + C[j].m) * (2.0 * C[i].m * v1r + (C[j].m - C[i].m) * v2r); // calculate resulting velocities in original coordinates C[i].v = v1n * r + v1u * u; C[j].v = v2n * r + v2u * u; } } } } // check if all disks flew out of viewport bool new_round = true; for (size_t i = 0; new_round && i < N; ++i) { new_round = new_round && (C[i].p.x < -C[i].r || C[i].p.y < -C[i].r || C[i].p.x > width + C[i].r || C[i].p.y > height + C[i].r); } if (new_round) newBalls(); // say GLUT that our frame has changed glutPostRedisplay(); } } /// work out a keypress void keyPressed(unsigned char key, int x, int y) { switch (key) { case 27: // ESC ASCII code case 'q': case 'Q': // if ASCII fails ;) std::cout << "Exiting\n"; exit(0); // quit case ' ': case '\r': case '\n': newBalls(); default: ; // ignore it, suppress compiler's possible warning } } void reshape(int w, int h) { width = static_cast<float>(w); height = static_cast<float>(h); // setup projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, w, 0.0, h, -100.0, 100.0); glMatrixMode(GL_MODELVIEW); // setup viewport glViewport(0, 0, w, h); } // // main // int main(int argc, char **argv) { // initialization glutInit(&argc, argv); std::srand(static_cast<unsigned>(std::time(nullptr))); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_MULTISAMPLE); glutInitWindowPosition(100, 100); glutInitWindowSize(width, height); glutCreateWindow("Circle collision simulation"); // setup some OpenGL state glClearColor(0.0, 0.0, 0.0, 1.0); // default one (black), just to show glClearColor glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); // create a disk image glNewList(1, GL_COMPILE); glutSolidSphere(1.0, 16, 4); glEndList(); // register callbacks glutDisplayFunc(repaint); glutKeyboardFunc(keyPressed); glutReshapeFunc(reshape); glutIdleFunc(frameMove); // run std::cout << "Starting...\n" << "k = " << k << "\n"; newBalls(); t = std::clock(); glutMainLoop(); }
Private
[
?
]
Run code
Submit