Posted: 03/06/07 02:53:36
larsivi wrote:
It would be nice if you could post it here, and we may adapt it for the example folder in the distribution :)
Ok, here is my first D program. I think it's fairly cool. Hopefully other D newcomers may find it useful.... Please let me know what you think....
/* Boids 2D v0.1
*
* I wrote this program as a means to familiarize myself with the D
* programming language and the Tango and Derelict libraries. Hopefully
* others will find it useful as a learning tool.
*
* The following sources were utilized for help on this project:
*
* 1. Conrad Parker's Boids Pseudocode
* http://www.vergenet.net/~conrad/boids/pseudocode.html
* 2. Stephen Chappell's Boids Python Code
* http://aspn.activestate.com/ASPN/Python/Cookbook/
*
* Stephen Chappell's Python code inspired me to write my own version in D.
* Although I utilized many of Stephen's program variables to tweak my Boid
* behavior, Conrad Parker's pseudocode served as a primary reference. OpenGL
* and SDL code gleaned from various tutorials.
*
* Many thanks to the Tango and Derelict projects, and to Walter Brightfor D!
*
* Author: Mason Green (mason.green@gmail.com)
*
* Future ideas: Animation, Obstacles, 3D?
*
*/
module boidGL;
import derelict.opengl.gl;
import derelict.opengl.glu;
import derelict.sdl.sdl;
import tango.math.Core; // Tango Rules!!
import tango.stdc.stringz;
import sky;
const int NUM_BOIDS = 30;
//Boid Boundaries
const int XMIN = 10;
const int XMAX = 950;
const int YMIN = 10;
const int YMAX = 790;
const int VLIM = 400; // Velocity limit
const int SLIM = 120; // Smooths the velocity step
const int WALL_FORCE = 30; // Wall Repulsion
const char[] WINDOW_TITLE = "Boids 2D v0.1";
//The screen attributes
const int SCREEN_WIDTH = 1000;
const int SCREEN_HEIGHT = 800;
const int SCREEN_BPP = 32;
// Circle variables
const float RAD2DEG = 0.0174532925;
const float ANGLE_STEP = PI/180.0;
const double RADIUS = 3;
bool running; // The main loop flag
//Module constructor.
static this(){
DerelictGL.load(); // Load Derelict libraries
DerelictGLU.load();
DerelictSDL.load();
if (SDL_Init(SDL_INIT_VIDEO) < 0)
throw new Exception("Failed to initialize SDL: " ~ getSDLError());
}
// Module destructor
static ~this(){
SDL_Quit();
}
// Main
void main(char[][] args){
bool fullScreen = false;
auto flock = new Sky(NUM_BOIDS, XMIN, XMAX, YMIN, YMAX, VLIM, WALL_FORCE, SLIM, RADIUS);
if (args.length > 1) fullScreen = args[1] == "-fullscreen";
createGLWindow(WINDOW_TITLE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, fullScreen);
initGL();
flock.initPosition();
running = true;
while (running){ // Main Program Loop
processEvents(); // User input
flock.moveBoids(); // Update Boids
drawFillCircle(flock); // Draw Boids
SDL_GL_SwapBuffers();
SDL_Delay(10); // Pause
}
}
// Process all the pending events.
void processEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYUP:
keyReleased(event.key.keysym.sym);
break;
case SDL_QUIT:
running = false;
break;
default:
break;
}
}
}
// Process a key released event.
void keyReleased(int key)
{
switch (key)
{
case SDLK_ESCAPE:
running = false;
break;
default:
break;
}
}
// Initialize OpenGL.
void initGL()
{
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
gluOrtho2D(0,SCREEN_WIDTH,0,SCREEN_HEIGHT); // Use 2d Coordinate system
glMatrixMode( GL_MODELVIEW );
glDisable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glLoadIdentity();
}
// Draw the Boids.
void drawFillCircle(Sky flock){
double vectorX1,vectorY1; // vector to a point on circle from its center
double vectorX0,vectorY0; // previous version of vectorX1,Y1;
double angle; // Angle in radians from circle start point.
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset The Current Modelview Matrix
glLoadIdentity();
glColor3f(1.0f, 0.0f, 0.0f);
foreach(Boid b; flock.boids){
double xctr = b.position.x;
double yctr = b.position.y;
glBegin(GL_TRIANGLES); // Tell OpenGL to draw a series of triangles
vectorX1 = xctr + RADIUS; // Start at the circle's rightmost point.
vectorY1 = yctr;
for(angle=ANGLE_STEP; // step through all other points on circle;
angle < 2.0*PI + ANGLE_STEP; angle+= ANGLE_STEP){ // (>2PI so that circle is always closed)
vectorX0 = vectorX1; // save previous point's position,
vectorY0 = vectorY1;
vectorX1= xctr + RADIUS*cos(angle); // find a new point on the circle,
vectorY1= yctr + RADIUS*sin(angle);
glVertex2d(xctr,yctr); // plot the points of a triangle (CCW order)
glVertex2d(vectorX0,vectorY0); // center-->old pt-->new pt.
glVertex2d(vectorX1,vectorY1);
}
glEnd(); // finished drawing triangles.
glLoadIdentity();
glFlush(); // Finish any pending drawing commands
}
}
// Initializes and opens the SDL window.
void createGLWindow(char[] title, int width, int height, int bits,
bool fullScreen){
// Set the OpenGL attributes
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Set the window title
SDL_WM_SetCaption(toUtf8z(title), null);
// Note the SDL_DOUBLEBUF flag is not required to enable double
// buffering when setting an OpenGL video mode.
// Double buffering is enabled or disabled using the
// SDL_GL_DOUBLEBUFFER attribute. (See above.)
int mode = SDL_OPENGL;
if (fullScreen) mode |= SDL_FULLSCREEN;
// Now open a SDL OpenGL window with the given parameters
if (SDL_SetVideoMode(width, height, bits, mode) is null){
throw new Exception("Failed to open OpenGL window: " ~ getSDLError());
}
}
/* Get the SDL error as a D string.
* Returns: A D string containing the current SDL error.
*/
char[] getSDLError()
{
return fromUtf8z(SDL_GetError());
}
/* Boids 2D v0.1
*
* I wrote this program as a means to familiarize myself with the D
* programming language and the Tango and Derelict libraries. Hopefully
* others will find it useful as a learning tool.
*
* The following sources were utilized for help on this project:
*
* 1. Conrad Parker's Boids Pseudocode
* http://www.vergenet.net/~conrad/boids/pseudocode.html
* 2. Stephen Chappell's Boids Python Code
* http://aspn.activestate.com/ASPN/Python/Cookbook/
*
* Stephen Chappell's Python code inspired me to write my own version in D.
* Although I utilized many of Stephen's program variables to tweak my Boid
* behavior, Conrad Parker's pseudocode served as a primary reference. OpenGL
* and SDL code gleaned from various tutorials.
*
* Many thanks to the Tango and Derelict projects, and to Walter Brightfor D!
*
* Author: Mason Green (mason.green@gmail.com)
*
* Future ideas: Animation, Obstacles, 3D?
*
*/
module sky;
import tango.math.Core; // Tango Rules!!
import tango.math.Random;
import tango.io.Stdout;
class Sky{
this(int NUM_BOIDS, uint XMIN, int XMAX, int YMIN, uint YMAX, int VLIM, int WALL_FORCE, int SLIM, double RADIUS){
numBoids = NUM_BOIDS;
wallForce = WALL_FORCE;
xMax = XMAX; xMin = XMIN;
yMax = YMAX; yMin = YMIN;
vLim = VLIM; sLim = SLIM;
radius = RADIUS;
v1 = new Vector; v2 = new Vector; v3 = new Vector;
boids.length = numBoids;
for(int i = 0; i < boids.length; i++)
boids[i] = new Boid();
}
public:
Boid[] boids;
void initPosition(){ // Start Boids in Random Positions
auto rand = new Random;
foreach(inout Boid b; boids){
b.position.x = rand.next(xMax);
b.position.y = rand.next(yMax);
}
}
void moveBoids(){
foreach(inout Boid b; boids){
bound(b); // Stay inside the screen
rule1(b); // Clumping
rule2(b); // Avoidance
rule3(b); // Schooling
b.velocity.add(v1,v2,v3); // Add Vectors
limitVelocity(b); // Limit Velocity
b.position.add(b.velocity, sLim); // Add speed vector to position
}
}
private:
Vector v1; Vector v2; Vector v3;
int xMax, xMin;
int yMax, yMin;
int vLim, sLim;
int numBoids;
int wallForce;
double radius;
void rule1(Boid x){ // Clumping
v1.x = v1.y = 0;
auto pc = new Vector;
foreach(Boid b; boids)
if(b != x) pc.add(b.position);
pc.centerMass(numBoids);
v1.move(pc, x.position, 7.5);
}
void rule2(Boid x){ // Avoidance
v2.x = v2.y = 0;
foreach(Boid b; boids)
if (b != x)
if((x.position.range(b.position))<radius*8)
v2.repell(b.position,x.position);
}
void rule3(Boid x){ // Schooling
v3.x = v3.y = 0;
auto pv = new Vector;
foreach(Boid b; boids)
if(b != x) pv.add(b.velocity);
pv.centerMass(numBoids);
v3.move(pv, x.velocity,8);
}
void bound(Boid b){ // Bound Position
if (b.position.x < xMin) b.velocity.x += wallForce;
else if (b.position.x > xMax) b.velocity.x -= wallForce;
if (b.position.y < yMin) b.velocity.y += wallForce;
else if (b.position.y > yMax) b.velocity.y -= wallForce;
}
void limitVelocity(inout Boid b){ // Limit Velocity
float mag;
b.velocity.magnitude(mag);
if(mag > vLim){
b.velocity.x = (b.velocity.x / mag) * vLim;
b.velocity.y = (b.velocity.y / mag) * vLim;
}
}
}
// Vector Class
class Vector{
public:
float x,y;
this(){x = 0; y = 0;}
void add(Vector v, int sLim){
x = x + v.x/(sLim); // sLim helps limit the velocity step
y = y + v.y/(sLim);
}
void add(Vector v){
x += v.x;
y += v.y;
}
void add(Vector v1, Vector v2, Vector v3){
x += v1.x + v2.x + v3.x;
y += v1.y + v2.y + v3.y;
}
void centerMass(int numBoids){
x = x / (numBoids-1);
y = y / (numBoids-1);
}
void move(Vector v1, Vector v2, float div){
x = (v1.x-v2.x)/div;
y = (v1.y-v2.y)/div;
}
float range(Vector v){
float d;
float x2,y2;
x2 = abs(x-v.x);
y2 = abs(y-v.y);
d = sqrt((x2*x2+y2*y2));
return(d);
}
void repell(Vector v1, Vector v2){
x = (x - (v1.x-v2.x))*2;
y = (y - (v1.y-v2.y))*2;
}
void magnitude(inout float mag){
mag = sqrt(x*x+y*y);
}
}
// Boid Class
class Boid{
public:
Vector position;
Vector velocity;
this(){
position = new Vector;
velocity = new Vector;
}
}
Any comments, complaints, or suggestions will be appreciated. Anyone interested in posting this in the tutorial section?