home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 10
/
aminetcdnumber101996.iso
/
Aminet
/
misc
/
sci
/
StarCollapse.lha
/
grav.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-20
|
15KB
|
631 lines
//----------------------------------------------------------------------------
// 3-D Gravity Simulator © 1995 L. Vanhelsuwé
// --------------------- --------------------
//
// Model a "cloud" of masses interacting in 3 dimensions under the influence
// of gravitational attraction.
//
// History:
// --------
// 07-AUG-95: started this file from standard skeleton NEW.c file
// 08-AUG-95: seem to have the bare bones done: physics OK.
// 10-AUG-95: now generating data file with 3-D animation for 3D viewer
// 12-AUG-95: modified algorithm to do vector sum of forces. Code now CRAWLS.
// 20-AUG-95: rewrote bottleneck (calc_pull) in 68882 FPU assembler.
//
// BUGS:
// -----
//----------------------------------------------------------------------------
// some standard ANSI includes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// some Amiga specific includes
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/datetime.h>
#include <intuition/screens.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfx.h>
// some Amiga includes containing system call prototypes
#include <clib/alib_protos.h> // ANSI function prototypes
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
//-----------------------------------------------------------------------------
// Program Constants, Limits
// -------------------------
#define GRAVITY_CONST (100)
#define MASS_MASS (1)
#define STOP_BUTTON !(*((char*)0xBFE001) & 0x80)
#define DUMP_INFO if (options.audit) printf
#define CENTER_X (350)
#define CENTER_Y (250)
#define CLUSTER_RADIUS (250)
//-----------------------------------------------------------------------------
// User Types
// ----------
typedef ULONG Display;
typedef double num;
typedef struct Mass {
num mass;
num x,y,z;
num Vx,Vy,Vz;
} Mass;
typedef struct AnimFileHeader {
short startframe;
short numframes;
short framesize;
} AnimFileHeader;
struct RDAres { // array for ReadArgs()
ULONG frames; // number of animation frames wanted
ULONG masses; // number of masses taking part in simulation
ULONG delay; // optional delay to slow down animation
ULONG audit; // dump intermediate calculation results to stdout
ULONG nodisplay; // do not display simulation (for faster simulation)
} options;
//-----------------------------------------------------------------------------
// Function prototypes
// -------------------
void main (void);
void init_platform_specific (void);
void close_platform_specific (void);
Display open_display (void);
void close_display (Display d);
void erase_display (Display d);
void write_pixel (int x, int y);
BOOL init_simulation (void);
void simulate_gravity (void);
void display_cloud (void);
void dump_simulation_frame (void);
void calc_pull (int m, num*, num*, num*, num*);
void asm_calc_pull (int m, num*, num*, num*, num*);
void init_defaults (struct variable *varptr);
void print_frame_number (int fr);
void write_fileheader (void);
double random (int range);
//-----------------------------------------------------------------------------
// Global statics
// --------------
struct IntuitionBase *IntuitionBase; // for access to ActiveWindow
struct GfxBase *GfxBase;
struct Screen *sc;
struct RastPort *rp;
Mass *Cloud; // Mass Cloud[ CloudSize ];
#define NumFrames options.frames
#define AUDIT options.audit
#define NODISPLAY options.nodisplay
Display window;
int fh; // output animation file handle
short *buffer; // buffer[3*MAX_MASSES +1]; full data frame buffer
int frame;
ULONG CloudSize;
char GRAV_CLI_TEMPLATE[] = "FRAMES/K,MASSES/K,DELAY/K,AUDIT/S,NODISPLAY/S";
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void main (void) {
BOOL ok;
init_platform_specific();
CloudSize = options.masses;
ok = init_simulation();
if (! ok) {
close_platform_specific();
exit(10);
}
if (! NODISPLAY) {
window = open_display();
}
write_fileheader();
frame = NumFrames;
while ( frame-- != 0) {
if (STOP_BUTTON) break;
simulate_gravity();
dump_simulation_frame();
if ( ! NODISPLAY) display_cloud();
Delay(options.delay);
}
free(Cloud);
free(buffer);
if (! NODISPLAY) {
close_display( window );
}
close_platform_specific();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void write_fileheader (void) {
AnimFileHeader an;
an.startframe = 0; // start with frame #0
an.numframes = NumFrames; // max frame #
an.framesize = 2 + 6 * CloudSize; // size of a data frame
Write(fh, &an, sizeof(an)); // write animation file header
}
//-----------------------------------------------------------------------------
// Create starting model of cloud.
//-----------------------------------------------------------------------------
BOOL init_simulation (void) {
int m;
num x,y,z,R;
Cloud = malloc( CloudSize * sizeof(Mass));
if (! Cloud) {
printf("Failed to allocate Cloud data structure.\n");
return FALSE;
}
buffer = malloc( sizeof(short) * 3 * (CloudSize+1) );
if (! buffer) {
printf("Failed to allocate write buffer.\n");
return FALSE;
}
// Generate ball-shaped cluster of randomly distributed masses
for (m=0; m< CloudSize; m++) {
R = CLUSTER_RADIUS + 1; // ensure first while cond fails
while ( R > CLUSTER_RADIUS ) {
x = random(CLUSTER_RADIUS);
y = random(CLUSTER_RADIUS);
z = random(CLUSTER_RADIUS);
// printf("\nTrying %f,%f,%f \n", x,y,z);
R = sqrt(x*x + y*y + z*z);
// printf("Radius = %f\n", R);
}
Cloud[m].mass = 1.0 + (rand()%10) * MASS_MASS;
Cloud[m].x = x;
Cloud[m].y = y;
Cloud[m].z = z;
Cloud[m].Vx = 0;
Cloud[m].Vy = 0;
Cloud[m].Vz = 0;
DUMP_INFO ("Body %03d (x,y,z) = (%f,%f,%f)\n", m, x,y,z);
}
// printf("Initial Mass cloud generated.\n");
return TRUE;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
double random (int range) {
int throw;
throw = rand() % (2*range) - range;
return (double) throw;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void simulate_gravity (void) {
int m;
num F,Fx,Fy,Fz;
num V,Vx,Vy,Vz;
for (m=0; m < CloudSize; m++) {
// calc_pull (m, &F, &Fx, &Fy, &Fz);
asm_calc_pull (m, &F, &Fx, &Fy, &Fz);
V = F / Cloud[m].mass;
Vx = Vy = Vz = 0;
if (F != 0) {
Vx = V * Fx / F;
Vy = V * Fy / F;
Vz = V * Fz / F;
}
Cloud[m].Vx += Vx;
Cloud[m].Vy += Vy;
Cloud[m].Vz += Vz;
Cloud[m].x += Cloud[m].Vx;
Cloud[m].y += Cloud[m].Vy;
Cloud[m].z += Cloud[m].Vz;
}
}
//-----------------------------------------------------------------------------
//
// Calculate total force acting on 1 body by summing all VECTOR forces
// stemming from individual interactions with all other bodies.
//
//-----------------------------------------------------------------------------
void calc_pull (int star, num *F_ptr, num *Fx_ptr, num *Fy_ptr, num *Fz_ptr) {
int m;
num F,Fx,Fy,Fz;
num Fcouple; // force magnitude between 2 masses
num Fxcouple,Fycouple,Fzcouple; // force vector
num dx,dy,dz;
num R;
DUMP_INFO ("\nCalculating pull on body %03d\n", star);
// reset force accumulators
Fx = Fy = Fz = 0;
// forall bodies (except body under consideration)
for (m=0; m < CloudSize; m++) {
if (m == star) continue; // skip oneself...
// calc distance between bodies
dx = Cloud[m].x - Cloud[star].x;
dy = Cloud[m].y - Cloud[star].y;
dz = Cloud[m].z - Cloud[star].z;
R = dx*dx + dy*dy + dz*dz;
DUMP_INFO ("Squared distance %f (R=%f)\n", R, sqrt((double)R));
// If body collides with another, no force results
if (R == 0) continue;
Fcouple = GRAVITY_CONST * Cloud[star].mass * Cloud[m].mass / R;
DUMP_INFO ("Resulting intermediate force = %f\n", Fcouple);
R = sqrt(R);
if (R == 0) continue;
Fxcouple = Fcouple * dx/R;
Fycouple = Fcouple * dy/R;
Fzcouple = Fcouple * dz/R;
DUMP_INFO ("Intermed Fx = %03f Fy = %03f Fz = %03f\n", Fxcouple, Fycouple, Fzcouple);
Fx += Fxcouple;
Fy += Fycouple;
Fz += Fzcouple;
}
F = sqrt( Fx*Fx + Fy*Fy + Fz*Fz );
DUMP_INFO ("SUM Fx = %03f Fy = %03f Fz = %03f\n", Fx, Fy, Fz);
DUMP_INFO ("Resulting Force = %f\n", F);
*F_ptr = F;
*Fx_ptr = Fx;
*Fy_ptr = Fy;
*Fz_ptr = Fz;
}
//-----------------------------------------------------------------------------
//
//
//-----------------------------------------------------------------------------
void dump_simulation_frame (void) {
int m;
int x,y,z;
short *ptr;
ptr = buffer;
*ptr++ = CloudSize; // store number of vertices in frame
for (m=0; m < CloudSize; m++) {
x = Cloud[m].x + 0.5; // round correctly !!
y = Cloud[m].y + 0.5;
z = Cloud[m].z + 0.5;
*ptr++ = (short) x;
*ptr++ = (short) y;
*ptr++ = (short) z;
}
Write(fh, buffer, 2 + sizeof(short) * 3 * CloudSize);
}
//-----------------------------------------------------------------------------
//
//
//-----------------------------------------------------------------------------
void display_cloud (void) {
int m;
int x,y,z;
erase_display( window );
for (m=0; m < CloudSize; m++) {
x = Cloud[m].x + 0.5; // round correctly !!
y = Cloud[m].y + 0.5;
z = Cloud[m].z + 0.5;
write_pixel( CENTER_X+x , CENTER_Y+y );
}
print_frame_number(frame);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// The Variable structure defines program globals which can be accessed or
// changed via the command line and/or the AREXX interface.
struct variable {
char *name; // name of a global parameter
ULONG *address; // its address
ULONG init_val; // initial default value
ULONG min_val; // range minimum
ULONG max_val; // range maximum
};
#define PARAMETER (1)
struct variable varlist[] = {
// FLAGS/ATTR NAME ADDRESS DEFAULT MINIMUM MAXIMUM
// ---------- ----- ------- ------- ------- -------
{ "CLOUDSIZE" ,&options.masses,50 ,2 ,1000 },
{ "NUM_FRAMES" ,&NumFrames ,50 ,1 ,1000 },
{ "DELAY" ,&options.delay ,0 ,0 ,200 },
{ NULL ,NULL ,NULL ,0 ,0 }
};
//-----------------------------------------------------------------------------
void init_platform_specific (void) {
struct RDArgs *rdargs;
struct variable *var;
#ifndef DEBUG
if (! (rdargs = ReadArgs(GRAV_CLI_TEMPLATE, (ULONG*) &options, NULL))) {
printf("ERROR: ARGUMENTS INCORRECT.\n");
printf("Type Grav ? for a full syntax template.\n\n");
exit(100);
} // Free RDA only AFTER we've parsed ptrs in options pointing into RDA **!!
#endif
// Any non-initialized simulation globals get initialized now.
init_defaults(&varlist[0]); // others are intialized from CMD line.
// **!! NOW we can free RDA (AFTER we've parsed ptrs)
FreeArgs(rdargs);
var = &varlist[0];
while(var->name) {
if (*var->address > var->max_val) {
printf("%s has to be in the range [%d..%d] ! (%d > %d)\n",
var->name, var->min_val, var->max_val, *var->address, var->max_val);
exit(10);
} else
if (*var->address < var->min_val) {
printf("%s has to be in the range [%d..%d] ! (%d < %d)\n",
var->name, var->min_val, var->max_val, *var->address, var->min_val);
exit(10);
}
var++; // goto next var in list
}
IntuitionBase =
(struct IntuitionBase *) OpenLibrary("intuition.library",0);
GfxBase =
(struct GfxBase *) OpenLibrary("graphics.library",0);
fh = Output(); // get access to stdout (no need to Close() )
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void close_platform_specific (void) {
CloseLibrary( (struct Library *) IntuitionBase);
CloseLibrary( (struct Library *) GfxBase);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
Display open_display (void) {
sc = OpenScreenTags(NULL,
SA_LikeWorkbench, TRUE,
TAG_END);
rp = &sc->RastPort;
SetAPen(rp, 1);
return (Display) sc;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void close_display (Display d) {
CloseScreen( (struct Screen *) d);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void write_pixel (int x, int y) {
if (x<0 || x>600 || y<0 || y>500) return;
WritePixel(rp, x,y);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void erase_display (Display d) {
SetRast (rp, 0);
}
//----------------------------------------------------------------
// Set all non-initialized Simulation Global Parameters to a default value
// IF the user hasn't given a value himself.
//----------------------------------------------------------------
void init_defaults (struct variable *varptr) {
ULONG *var;
while (varptr->name) {
var = varptr->address;
if (*var) { // if RDA initialized a ptr to an arg,
*var = atoi((char*) *var); // convert ASCII arg to number
} else {
*var = varptr->init_val; // else use built-in default value.
}
varptr++; // goto next variable
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void print_frame_number (int fr) {
char fr_str[11];
sprintf(fr_str, "FRAME %04d", fr);
Move(rp, 40,40);
Text(rp, fr_str, 10);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------