|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Payments Humor Music |
Trajectory, C++ gravity simulationTraject.cpp was derived from the evolution demonstration program adapt.cpp, but it has a completely different purpose. It creates one agent (cell) and shoots it from the left side of the screen at a random velocity and random angle. The agent (a dot on the screen) then follows, at a continually changing speed, a parabolic trajectory similar to that of a real object under the influence of gravity. The key to the realistic effect is that the object's initial speed can be broken into two components which are treated separately:
The horizontal velocity remains constant. Only the vertical velocity is affected by the simulated "gravitation" which continuously exerts a downward pull. If the initial trajectory is upward, gravity slows the upward speed until it reaches zero. Then the object starts falling toward the bottom of the screen at an accelerating rate. The parabolic path is a natural result of the constant horizontal velocity and accelerating vertical velocity. That is, the path doesn't have to be explicitly programmed in. It is hardly a remarkable demonstration, except that it was so easily derived from adapt.cpp by ripping out most of the code and giving the agent a different motion behavior. On the other hand, the same effect can be achieved by a very simple program, and if I had set out to write this program from scratch using this type of program structure used by adapt.cpp, that would really have been overkill for what is needed here! It is an MSDOS console application written in Borland C++ 4.0. |
/* traject.cpp 6-26-97
Copyright (C)1995-1997 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
Version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Plots the trajectory of an object (dot) launched from the ground with a given
velocity and angle. Simulates effect of gravity. Based on adapt.cpp.
------
to do:
try launching a dot from a circle (planet) at screen center & plot orbits.
("Does God Play Dice?" has sections on gravity & pendulums, which may be
related. Says this calculation is very difficult.)
*/
#include <math.h>
#include <graphics.h>
#include <classlib\arrays.h>
#pragma hdrstop
#include "c:\bcs\my.h"
#include "c:\bcs\mylib.cpp"
/////////////////////////////////////////////////////////////////////////
// global variables
BOOL beepon = TRUE; // whether to use sound effects, test is in play()
BOOL delayon = FALSE; // whether to impose delay for better viewing
BOOL trailon = TRUE; // whether to show trail as a point moves
BOOL lookon = FALSE; // whether to show direction of sight
BOOL autoclear = TRUE; // whether to auto-clear screen every ## loops
Trigtable t; // so they only have to be calculated once
/////////////////////////////////////////////////////////////////////////
// entity, shown as a screen dot, that can move around, interact with others, etc.
class agent
{
public:
agent(); // constructor
~agent(); // destructor
BOOL operator == (const agent& other) const;
BOOL operator < (const agent& other) const;
double calcdistance(class agent* other); // distance from this to another agent
class agent* identifynearestneighbor();
class agent* move();
int run();
BOOL sense(); // update all sensors with current surroundings
int mapneighbors();
double x; // this cell's screen location
double y;
double heading; // direction of motion (and sight): 0=North=Up, 90=East
double velocity; // dies when 0
int color; // dot leaves a TRAIL in this color (the dot itself is always white)
};
/////////////////////////////////////////////////////////////////////////
// global array of agents. Must define here because some agent:: functions use it.
TIArrayAsVector<agent> ag(5,0,0);
//-----------------------------------------------------------------------
// constructor
agent::agent()
{
x = 0.; // starts at lower left corner
y = 479.;
heading = (double)(random(90)); // straight up to straight out
velocity = (double)(random(200)); // random starting velocity
color = LIGHTGREEN; // trail color
putpixel((int)x,(int)y,WHITE); // display on screen
}
//-----------------------------------------------------------------------
// destructor, removes from screen. OK if a dead cell's velocity is zero,
// but if residual velocity is ever allowed, it might be better to leave lying
// around for another cell to eat (scavenger).
agent::~agent()
{
putpixel((int)x,(int)y,BLACK);
}
//-----------------------------------------------------------------------
// == currently isn't used for anything, but there should be a way to make it useful.
BOOL agent::operator == (const agent& other) const
{
return(&other == this);
}
//-----------------------------------------------------------------------
// < Sorts agents by their Manhattan distance from (0,0). Not currently used.
BOOL agent::operator < (const agent& other) const
{
if((x+y) < (other.x + other.y))
return(TRUE);
return(FALSE);
}
//-----------------------------------------------------------------------
// computes distance between two cells
double agent::calcdistance(class agent* other)
{
double xdif = fabs(x - other->x);
double ydif = fabs(y - other->y);
return(sqrt((xdif * xdif) + (ydif * ydif)));
}
//-----------------------------------------------------------------------
// identify which agent is occupying a pixel & return pointer
// returns pointer, or ZERO if match not found.
// generally, you should know that the pixel IS occupied before calling, because this is slow.
// This is NOT a member function, because it's needed outside the class.
class agent* identify(int findx, int findy)
{
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
if(((int)(ag[i]->x) == findx) && ((int)(ag[i]->y) == findy))
return(ag[i]);
return(0);
}
//-----------------------------------------------------------------------
// move a cell along its heading until it hits the ground.
// Can only move 1 pixel at a time so it knows whether next step will hit something.
// returns pointer to the cell that blocked this one's path, if any,
// or ZERO if path was clear.
class agent* agent::move()
{
double xvelocity = velocity * cosine(heading);
double yvelocity = velocity * sine(heading);
int xi = (int)x;
int yi = (int)y;
// 1 loop = .1 second elapsed
do
{
delay(25);
putpixel((int)x,(int)y,trailon ? color : BLACK); // show or erase previous position
x += xvelocity * .1; // x velocity doesn't change
y -= yvelocity * .1; // y axis upside down
yvelocity -= 3.2; // change due to gravity, in ft/sec
if(y < -1000)
break;
while(x > 639.) // screen wraps around
x -= 639.;
if(y > 479.) // below bottom (landed)
y = 479.;
xi = (int)x;
yi = (int)y;
putpixel(0,yi,LIGHTRED); // show x & y positions on axes
putpixel(xi,479,LIGHTRED);
putpixel(xi,yi,WHITE); // show new position
}
while(y < 479.); // until it hits the ground
return(0);
/*
double targetx = x;
double targety = y;
// must get away from current pixel, or it'll be shown as "occupied" (by itself!)
while(((int)targetx == (int)x) && ((int)targety == (int)y))
{
targetx += t.sines[(int)heading];
targety -= t.cosines[(int)heading]; // screen's y axis is upside down
}
// Screen wraps around.
if((int)targetx >= 640) targetx -= 640.;
if((int)targetx <= -1) targetx += 640.;
if((int)targety >= 480) targety -= 480.;
if((int)targety <= -1) targety += 480.;
// if new location is occupied, can't move there
// This also causes the dots to stick when they hit, just like in DLA!
if(getpixel((int)targetx,(int)targety) == WHITE)
return(identify((int)targetx,(int)targety));
*/
} // end move
//-----------------------------------------------------------------------
// create a map of neighboring pixels
// each pixel encoded in 1 bit (excluding pixel occupied by itself).
// use if(mapneighbors()) to determine if *any* neighboring pixel is occupied.
// use if(mapneighbors() & 1) to determine if below right pixel is occupied.
// For use in DLA and in 2nd version of Life.
int agent::mapneighbors()
{
int xi = (int)x;
int yi = (int)y;
int map = 0;
// getpixel(offscreen) returns 0
if(getpixel(xi-1,yi-1) == WHITE) map |= 128; // above left
if(getpixel(xi ,yi-1) == WHITE) map |= 64; // above
if(getpixel(xi+1,yi-1) == WHITE) map |= 32; // above right
if(getpixel(xi-1,yi ) == WHITE) map |= 16; // to left
if(getpixel(xi+1,yi ) == WHITE) map |= 8; // to right
if(getpixel(xi-1,yi+1) == WHITE) map |= 4; // below left
if(getpixel(xi ,yi+1) == WHITE) map |= 2; // below
if(getpixel(xi+1,yi+1) == WHITE) map |= 1; // below right
return(map);
}
//-----------------------------------------------------------------------
// update all sensors with current surroundings
// determine if there is another cell in the current line of sight along our heading
// similar to move() except it just looks, doesn't go anywhere, and vision doesn't wrap
// beyond edge of screen.
// returns TRUE if another cell is in view, FALSE if not.
BOOL agent::sense()
{
return(TRUE);
// mapneighbors(); // construct a map of the neighborhood
/*
double targetx = x;
double targety = y;
while(1) // exits loop if an object sighted or vision ran off screen
{ // because it returns, this loop must be last thing sense() does.
double oldx = targetx;
double oldy = targety;
// must get away from current pixel, or it'll be shown as "occupied" (by itself!)
while(((int)targetx == (int)oldx) && ((int)targety == (int)oldy))
{
targetx += t.sines[(int)heading];
targety -= t.cosines[(int)heading]; // screen's y axis is upside down
}
// our "vision" ran off screen, so we're through looking.
if(((int)targetx >= 640) || ((int)targetx <= -1) ||
((int)targety >= 480) || ((int)targety <= -1))
return(FALSE);
// if new location is occupied, it is the object we're looking at.
if(getpixel((int)targetx,(int)targety) == WHITE)
{
inview = identify((int)targetx,(int)targety);
if(inview)
return(TRUE);
printf("ERROR: Object was sighted that isn't in array.\n");
return(FALSE);
}
if(lookon) // didn't sight anything or run off screen: show where we're looking
putpixel((int)targetx,(int)targety,BLUE);
} // end while(1)
*/
} // end sense
//-----------------------------------------------------------------------
// find mass center of all points on screen. uses average, could use least squares
// This could be an agent:: member function.
void calccenterofmass(double* x, double* y) // variables to fill
{
double sumx, sumy;
sumx = sumy = 0.;
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
{
sumx += ag[i]->x;
sumy += ag[i]->y;
}
*x = sumx / (double)count;
*y = sumy / (double)count;
} // end calccenterofmass
//-----------------------------------------------------------------------
// returns pointer to nearest cell neighbor, or 0 if none
class agent* agent::identifynearestneighbor()
{
double distance;
double mindistance = 1e6;
int nearest = -1;
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
if(ag[i] != this) // don't find itself
{
distance = calcdistance(ag[i]);
if(distance < mindistance)
{
mindistance = distance;
nearest = i;
}
}
return((nearest == -1) ? 0 : ag[nearest]);
}
//-----------------------------------------------------------------------
int agent::run()
{
return(TRUE); // survived alive
} // end run
//-----------------------------------------------------------------------
// end class agent
/////////////////////////////////////////////////////////////////////////
// global functions
//-----------------------------------------------------------------------
// display a short program description
void intro()
{
}
//-----------------------------------------------------------------------
// show the help screen
void drawhelp()
{
cout << "\nThese commands are available while program is running:\n\n";
cout << "A AUTO-CLEAR screen between launches (toggle, now "
<< (autoclear ? "ON" : "OFF") << ")\n";
cout << "B BEEP (sound effects) (toggle)\n";
cout << "C CLEAR screen once\n";
cout << "D DELAY for better viewing (toggle)\n";
cout << "H HELP (show this screen)\n";
cout << "L LINE-of-sight display (toggle)\n";
cout << "P PAUSE (any key to resume)\n";
cout << "T TRAIL display (toggle)\n";
cout << "<ESC> Quit\n\n";
presskey();
}
//-----------------------------------------------------------------------
// redraw all the agents on the screen
void drawagents()
{
clearviewport();
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
putpixel((int)(ag[i]->x),(int)(ag[i]->y),WHITE);
}
//////////////////////////////////////////////////////////////////////////////
int main()
{
randomize();
int ch, grmode, passcount, passlimit = 1;
// intro();
// drawhelp();
grmode = initgraf();
while(1)
{
passcount = 0;
while(1)
{
//----------------------------------------------------------------------------
// user input section
if(kbhit())
{
ch = tolower(getch());
switch(ch)
{
case 27: // user quit
ag.Flush(TShouldDelete::Delete);
nosound(); // just in case
closegraph();
return(0);
case 'a': autoclear = !autoclear; break;
case 'b': beepon = !beepon; break; // toggle sound effects
case 'c': clearviewport(); break; // clear screen this pass
case 'd': delayon = !delayon; break; // toggle delay
case 'h': // help
restorecrtmode();
drawhelp();
setgraphmode(grmode);
break;
case 'l': lookon = !lookon; break; // toggle showing where cell is looking
case 'p': getch(); break; // user pause
case 't': trailon = !trailon; break; // toggle trail display
}
} // end if(kbhit())
//----------------------------------------------------------------------------
if(autoclear)
if(++passcount >= passlimit)
{
clearviewport();
passcount = 0;
}
ag.Flush(TShouldDelete::Delete);
ag.Add(new agent);
ag[0]->move(); // plot its course until it crashes
} // end inner while(1)
} // end outer while(1)
} // end main()
|
|
|
|
|
|
Copyright ©2010 Steven Whitney. Last modified Thu 10/21/2010 02:08:04 -0700. |
||