Import of Initially Provided Code

This commit is contained in:
Joshua Coles 2023-03-01 20:54:06 +00:00
commit 60bd8bee4e
10 changed files with 862 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Final executable
/run
# IDE files
.idea
# Data files
*.csv

319
DLASystem.cpp Normal file
View File

@ -0,0 +1,319 @@
//
// DLASystem.cpp
//
#include "DLASystem.h"
// colors
namespace colours {
GLfloat blue[] = { 0.1, 0.3, 0.9, 1.0 }; // blue
GLfloat red[] = { 1.0, 0.2, 0.1, 0.2 }; // red
GLfloat green[] = { 0.3, 0.6, 0.3, 1.0 }; // green
GLfloat paleGrey[] = { 0.7, 0.7, 0.7, 1.0 }; // green
GLfloat darkGrey[] = { 0.2, 0.2, 0.2, 1.0 }; // green
}
// this function gets called every step,
// if there is an active particle then it gets moved,
// if not then add a particle
void DLASystem::Update() {
if (lastParticleIsActive == 1)
moveLastParticle();
else if (numParticles < endNum) {
addParticleOnAddCircle();
setParticleActive();
}
if (lastParticleIsActive == 0 || slowNotFast == 1)
glutPostRedisplay(); //Tell GLUT that the display has changed
}
void DLASystem::clearParticles() {
// delete particles and the particle list
for (int i = 0; i < numParticles; i++) {
delete particleList[i];
}
particleList.clear();
numParticles = 0;
}
// remove any existing particles and setup initial condition
void DLASystem::Reset() {
// stop running
running = 0;
clearParticles();
lastParticleIsActive = 0;
// set the grid to zero
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < gridSize; j++) {
grid[i][j] = 0;
}
}
// setup initial condition and parameters
addCircle = 10;
killCircle = 2.0*addCircle;
clusterRadius = 0.0;
// add a single particle at the origin
double pos[] = { 0.0, 0.0 };
addParticle(pos);
// set the view
int InitialViewSize = 40;
setViewSize(InitialViewSize);
}
// set the value of a grid cell for a particular position
// note the position has the initial particle at (0,0)
// but this corresponds to the middle of the grid array ie grid[ halfGrid ][ halfGrid ]
void DLASystem::setGrid(double pos[], int val) {
int halfGrid = gridSize / 2;
grid[(int)(pos[0] + halfGrid)][(int)(pos[1] + halfGrid)] = val;
}
// read the grid cell for a given position
int DLASystem::readGrid(double pos[]) {
int halfGrid = gridSize / 2;
return grid[(int)(pos[0] + halfGrid)][(int)(pos[1] + halfGrid)];
}
// check if the cluster is big enough and we should stop:
// to be safe, we need the killCircle to be at least 2 less than the edge of the grid
int DLASystem::checkStop() {
if (killCircle + 2 >= gridSize / 2) {
pauseRunning();
cout << "STOP" << endl;
glutPostRedisplay(); // update display
return 1;
}
else return 0;
}
// add a particle to the system at a specific position
void DLASystem::addParticle(double pos[]) {
// create a new particle
Particle * p = new Particle(pos);
// push_back means "add this to the end of the list"
particleList.push_back(p);
numParticles++;
// pos coordinates should be -gridSize/2 < x < gridSize/2
setGrid(pos, 1);
}
// add a particle to the system at a random position on the addCircle
// if we hit an occupied site then we do nothing except print a message
// (this should never happen)
void DLASystem::addParticleOnAddCircle() {
double pos[2];
double theta = rgen.random01() * 2 * M_PI;
pos[0] = ceil(addCircle * cos(theta));
pos[1] = ceil(addCircle * sin(theta));
if (readGrid(pos) == 0)
addParticle(pos);
else
cout << "FAIL " << pos[0] << " " << pos[1] << endl;
}
// send back the position of a neighbour of a given grid cell
// NOTE: there is no check that the neighbour is inside the grid,
// this has to be done separately...
void DLASystem::setPosNeighbour(double setpos[], double pos[], int val) {
switch (val) {
case 0:
setpos[0] = pos[0] + 1.0;
setpos[1] = pos[1];
break;
case 1:
setpos[0] = pos[0] - 1.0;
setpos[1] = pos[1];
break;
case 2:
setpos[0] = pos[0];
setpos[1] = pos[1] + 1.0;
break;
case 3:
setpos[0] = pos[0];
setpos[1] = pos[1] - 1.0;
break;
}
}
// if the view is smaller than the kill circle then increase the view area (zoom out)
void DLASystem::updateViewSize() {
double mult = 1.2;
if (viewSize < 2.0*killCircle) {
setViewSize(viewSize * mult);
}
}
// set the view to be the size of the add circle (ie zoom in on the cluster)
void DLASystem::viewAddCircle() {
setViewSize(2.0*addCircle); // factor of 2 is to go from radius to diameter
}
// when we add a particle to the cluster, we should update the cluster radius
// and the sizes of the addCircle and the killCircle
void DLASystem::updateClusterRadius(double pos[]) {
double rr = distanceFromOrigin(pos);
if (rr > clusterRadius) {
clusterRadius = rr;
// this is how big addCircle is supposed to be:
// either 20% more than cluster radius, or at least 5 bigger.
double check = clusterRadius * addRatio;
if (check < clusterRadius + 5)
check = clusterRadius + 5;
// if it is smaller then update everything...
if (addCircle < check) {
addCircle = check;
killCircle = killRatio * addCircle;
updateViewSize();
}
checkStop();
}
}
// make a random move of the last particle in the particleList
void DLASystem::moveLastParticle() {
int rr = rgen.randomInt(4); // pick a random number in the range 0-3, which direction do we hop?
double newpos[2];
Particle *lastP = particleList[numParticles - 1];
setPosNeighbour(newpos, lastP->pos, rr);
if (distanceFromOrigin(newpos) > killCircle) {
//cout << "#deleting particle" << endl;
setGrid(lastP->pos, 0);
particleList.pop_back(); // remove particle from particleList
numParticles--;
setParticleInactive();
}
// check if destination is empty
else if (readGrid(newpos) == 0) {
setGrid(lastP->pos, 0); // set the old grid site to empty
// update the position
particleList[numParticles - 1]->pos[0] = newpos[0];
particleList[numParticles - 1]->pos[1] = newpos[1];
setGrid(lastP->pos, 1); // set the new grid site to be occupied
// check if we stick
if (checkStick()) {
//cout << "stick" << endl;
setParticleInactive(); // make the particle inactive (stuck)
updateClusterRadius(lastP->pos); // update the cluster radius, addCircle, etc.
if (numParticles % 100 == 0 && logfile.is_open()) {
logfile << numParticles << " " << clusterRadius << endl;
}
}
}
else {
// if we get to here then we are trying to move to an occupied site
// (this should never happen as long as the sticking probability is 1.0)
cout << "reject " << rr << endl;
cout << lastP->pos[0] << " " << lastP->pos[1] << endl;
//cout << newpos[0] << " " << newpos[1] << " " << (int)newpos[0] << endl;
//printOccupied();
}
}
// check if the last particle should stick (to a neighbour)
int DLASystem::checkStick() {
Particle *lastP = particleList[numParticles - 1];
int result = 0;
// loop over neighbours
for (int i = 0; i < 4; i++) {
double checkpos[2];
setPosNeighbour(checkpos, lastP->pos, i);
// if the neighbour is occupied...
if (readGrid(checkpos) == 1)
result = 1;
}
return result;
}
// constructor
DLASystem::DLASystem(Window *set_win) {
cout << "creating system, gridSize " << gridSize << endl;
win = set_win;
numParticles = 0;
endNum = 1000;
// allocate memory for the grid, remember to free the memory in destructor
grid = new int*[gridSize];
for (int i = 0; i < gridSize; i++) {
grid[i] = new int[gridSize];
}
slowNotFast = 1;
// reset initial parameters
Reset();
addRatio = 1.2; // how much bigger the addCircle should be, compared to cluster radius
killRatio = 1.7; // how much bigger is the killCircle, compared to the addCircle
// this opens a logfile, if we want to...
//logfile.open("opfile.txt");
}
// destructor
DLASystem::~DLASystem() {
// strictly we should not print inside the destructor but never mind...
cout << "deleting system" << endl;
// delete the particles
clearParticles();
// delete the grid
for (int i = 0; i < gridSize; i++)
delete[] grid[i];
delete[] grid;
if (logfile.is_open())
logfile.close();
}
// this draws the system
void DLASystem::DrawSquares() {
// draw the particles
double halfSize = 0.5;
for (int p = 0; p < numParticles; p++) {
double *vec = particleList[p]->pos;
glPushMatrix();
if (p == numParticles - 1 && lastParticleIsActive == 1)
glColor4fv(colours::red);
else if (p == 0)
glColor4fv(colours::green);
else
glColor4fv(colours::blue);
glRectd(drawScale*(vec[0] - halfSize),
drawScale*(vec[1] - halfSize),
drawScale*(vec[0] + halfSize),
drawScale*(vec[1] + halfSize));
glPopMatrix();
}
// print some information (at top left)
// this ostringstream is a way to create a string with numbers and words (similar to cout << ... )
ostringstream str;
str << "num " << numParticles << " size " << clusterRadius;
// print the string
win->displayString(str, -0.9, 0.9, colours::red);
// if we are paused then print this (at bottom left)
if (running == 0) {
ostringstream pauseStr;
pauseStr << "paused";
win->displayString(pauseStr, -0.9, -0.9, colours::red);
}
}

154
DLASystem.h Normal file
View File

@ -0,0 +1,154 @@
#pragma once
#include <GLUT/glut.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
#include <random>
#include <string>
#include <sstream>
#include "Window.h"
#include "Particle.h"
#include "rnd.h"
using namespace std;
class DLASystem {
private:
// these are private variables and functions that the user will not see
Window *win; // window in which the system is running
// list of particles
vector<Particle*> particleList;
int numParticles;
// delete particles and clear the particle list
void clearParticles();
// size of cluster
double clusterRadius;
// these are related to the DLA algorithm
double addCircle;
double killCircle;
// size of grid
static const int gridSize = 1600;
int **grid; // this will be a 2d array that stores whether each site is occupied
// the window draws only part of the grid, viewSize controls how much...
double viewSize;
double drawScale;
// random number generator, class name is rnd, instance is rgen
rnd rgen;
// output file (not used at the moment)
ofstream logfile;
// number of particles at which the simulation will stop
// (the value is set in constructor)
int endNum;
// the values of these variables are set in the constructor
double addRatio; // how much bigger the addCircle should be, compared to cluster radius
double killRatio; // how much bigger is the killCircle, compared to the addCircle
public:
// these are public variables and functions
// update the system: if there is an active particle then move it,
// else create a new particle (on the adding circle)
void Update();
// draw particles as squares
void DrawSquares();
// is the simulation running (1) or paused (0) ?
int running;
// slowNotFast is +1 for slow running, 0 for fast
int slowNotFast;
// lastParticleIsActive is +1 if there is an active particle in the system, otherwise 0
int lastParticleIsActive;
// constructor
DLASystem(Window *set_win);
// destructor
~DLASystem();
// delete all particles and reset
void Reset();
// this sets the seed for the random numbers
void setSeed(int s) { rgen.setSeed(s); }
// check whether we should stop (eg the cluster has reached the edge of the grid)
int checkStop();
// stop/start the algorithm
void setRunning() { if ( checkStop()==0 ) running = 1; }
void pauseRunning() { running = 0; }
// set whether it runs fast or slow
void setSlow() { slowNotFast = 1; }
void setFast() { slowNotFast = 0; }
void setSuperFast() { slowNotFast = -1; }
// set which part of the grid is visible on the screen
// basically the screen shows co-ordinates -vv < x < vv
// where vv is the input value
void setViewSize(double vv) { viewSize = vv; drawScale = 2.0/viewSize; }
// if the killcircle is almost as big as the view then increase the view
void updateViewSize();
// set the view to be the approx size of the addCircle
void viewAddCircle();
// if pos is outside the cluster radius then set clusterRadius to be the distance to pos.
void updateClusterRadius( double pos[] );
// set and read grid entries associated with a given position
void setGrid(double pos[], int val);
int readGrid(double pos[]);
// return the distance of a given point from the origin
double distanceFromOrigin(double pos[]) {
return sqrt( pos[0]*pos[0] + pos[1]*pos[1] );
}
// set whether there is an active particle in the system or not
void setParticleActive() { lastParticleIsActive = 1; }
void setParticleInactive() { lastParticleIsActive = 0; }
// add a particle at pos
void addParticle(double pos[]);
// add a particle at a random point on the addCircle
void addParticleOnAddCircle();
// assign setpos to the position of a neighbour of pos
// which neighbour we look at is determined by val (=0,1,2,3)
void setPosNeighbour(double setpos[], double pos[], int val);
// this attempts to move the last particle in the List to a random neighbour
// if the neighbour is occupied then nothing happens
// the function also checks if the moving particle should stick.
void moveLastParticle();
// check whether the last particle should stick
// currently it sticks whenever it touches another particle
int checkStick();
// set the background colour for the window
// it would be better for an OOP philosophy to make these member functions for the Window class
// but we are being a bit lazy here
void setWinBackgroundWhite() { glClearColor(1.0, 1.0, 1.0, 1.0); }
void setWinBackgroundBlack() { glClearColor(0.0, 0.0, 0.0, 0.0); }
};

72
Makefile Normal file
View File

@ -0,0 +1,72 @@
# ====================================================================================== #
# From the Author #
# ====================================================================================== #
# ! The purpose of this Makefile is to build the DLASystem project
# ! This makefile was adapted to work with any cpp project on OSX
# ====================================================================================== #
# Variables of the Makefile #
# ====================================================================================== #
CXX = clang++
CXXFLAGS = -Wall -Wextra -g -O0
IFLAGS = -I/usr/local/include -I/usr/include
LFLAGS = -L/usr/local/lib -lm -framework OpenGL -framework GLUT
# ------------------------------------------
# FOR GENERIC MAKEFILE:
# 1 - Binary directory
# 2 - Source directory
# 3 - Executable name
# 4 - Sources names
# 5 - Dependencies names
# ------------------------------------------
BIN = .
SOURCE = .
EXEC = ./run
SOURCES = $(wildcard $(SOURCE)/*.cpp)
OBJECTS = $(SOURCES:.cpp=.o)
# ====================================================================================== #
# Targets of the Makefile #
# target_name : dependency #
# <tabulation> command #
# ====================================================================================== #
# ------------------------------------------
# ! - all : Compiles everything
# ! - help : Shows this help
# ! - clean : erases all object files *.o
# ! and all binary executables
# ------------------------------------------
all : $(BIN)/run
test: $(BIN)/hllc_test
help :
@grep -E "^# !" Makefile | sed -e 's/# !/ /g'
clean:
rm -f $(EXEC) $(OBJECTS)
# ------------------------------------------
# Executable
# ------------------------------------------
$(EXEC): $(OBJECTS)
$(CXX) $(OBJECTS) -o $(EXEC) $(IFLAGS) $(LFLAGS)
# ------------------------------------------
# Temorary files (*.o) (IFLAGS should be added here)
# ------------------------------------------
$(SOURCE)/%.o: $(SOURCE)/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@ $(IFLAGS) $(LFLAGS)

20
Particle.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
class Particle {
public:
static const int dim = 2; // we are in two dimensions
double *pos; // pointer to an array of size dim, to store the position
// default constructor
Particle() {
pos = new double[dim];
}
// constructor, with a specified initial position
Particle(double set_pos[]) {
pos = new double[dim];
for (int d=0;d<dim;d++)
pos[d]=set_pos[d];
}
// destructor
~Particle() { delete[] pos; }
};

26
Window.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "Window.h"
// constructor
Window::Window(int set_size[], string &set_title) {
size[0]=set_size[0]; size[1]=set_size[1];
title=set_title;
locateOnScreen();
glutInitWindowSize(size[0],size[1]);
glutInitWindowPosition(pos[0],pos[1]);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow(title.c_str());
// sets the background to black
glClearColor(0.0, 0.0, 0.0, 0.0);
}
// print a string at a given position, don't worry about how this works...
void Window::displayString( ostringstream &str, double x, double y, GLfloat col[]) {
string localString = str.str();
glColor4fv(col);
glRasterPos2d(x,y);
for (int i = 0; i < localString.length(); i++) {
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, localString[i]);
}
}

34
Window.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <GLUT/glut.h>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
class Window {
public:
string title;
int size[2];
int pos[2];
void locateOnScreen() {
// the fx sets where on the screen the window will appear
// (values should be between 0 and 1)
double fx[] = { 0.7,0.5 };
pos[0] = (glutGet(GLUT_SCREEN_WIDTH) - size[0]) * fx[0];
pos[1] = (glutGet(GLUT_SCREEN_HEIGHT) - size[1]) * fx[1];
}
// constructor, size is in pixels
Window(int set_size[], string &set_title);
// function which prints a string to the screen, at a given position, with a given color
// note position is "absolute", not easy to get two strings spaced one above each other like this
void displayString( ostringstream &str, double x, double y, GLfloat col[]);
};

169
mainDLA.cpp Normal file
View File

@ -0,0 +1,169 @@
#include <GLUT/glut.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <math.h>
#include <string>
#include "DLASystem.h"
#include "Window.h"
using namespace std;
// functions which are needed for openGL go into a namespace so that we can identify them
namespace drawFuncs {
void handleKeypress(unsigned char key, int x, int y);
void display(void);
void update(int val);
void introMessage();
}
// this is a global pointer, which is how we access the system itself
DLASystem *sys;
int main(int argc, char **argv) {
// turn on glut
glutInit(&argc, argv);
int window_size[] = { 480,480 };
string window_title("simple DLA simulation");
// create a window
Window *win = new Window(window_size,window_title);
// create the system
sys = new DLASystem(win);
// this is the seed for the random numbers
int seed = 6;
cout << "setting seed " << seed << endl;
sys->setSeed(seed);
// print the "help" message to the console
drawFuncs::introMessage();
// tell openGL how to redraw the screen and respond to the keyboard
glutDisplayFunc( drawFuncs::display );
glutKeyboardFunc( drawFuncs::handleKeypress );
// tell openGL to do its first update after waiting 10ms
int wait = 10;
int val = 0;
glutTimerFunc(wait, drawFuncs::update, val);
// start the openGL stuff
glutMainLoop();
return 0;
}
// this is just a help message
void drawFuncs::introMessage() {
cout << "Keys (while in graphics window):" << endl << " q or e to quit (or exit)" << endl;
cout << " h to print this message (help)" << endl;
cout << " u for a single update" << endl;
cout << " g to start running (go)" << endl;
cout << " p to pause running" << endl;
cout << " s to run in slow-mode" << endl;
cout << " f to run in fast-mode" << endl;
cout << " r to clear everything (reset)" << endl;
cout << " z to pause and zoom in" << endl;
cout << " w or b to change background colour to white or black" << endl;
}
// openGL function deals with the keyboard
void drawFuncs::handleKeypress(unsigned char key, int x, int y) {
switch (key) {
case 'h':
drawFuncs::introMessage();
break;
case 'q':
case 'e':
cout << "Exiting..." << endl;
// delete the system
delete sys;
exit(0);
break;
case 'p':
cout << "pause" << endl;
sys->pauseRunning();
break;
case 'g':
cout << "go" << endl;
sys->setRunning();
glutTimerFunc(0, drawFuncs::update, 0);
break;
case 's':
cout << "slow" << endl;
sys->setSlow();
break;
case 'w':
cout << "white" << endl;
sys->setWinBackgroundWhite();
break;
case 'b':
cout << "black" << endl;
sys->setWinBackgroundBlack();
break;
case 'f':
cout << "fast" << endl;
sys->setFast();
break;
case 'r':
cout << "reset" << endl;
sys->Reset();
break;
case 'z':
cout << "zoom" << endl;
sys->pauseRunning();
sys->viewAddCircle();
break;
case 'u':
cout << "upd" << endl;
sys->Update();
break;
}
// tell openGL to redraw the window
glutPostRedisplay();
}
// this function gets called whenever the algorithm should do its update
void drawFuncs::update(int val) {
int wait; // time to wait between updates (milliseconds)
if ( sys->running ) {
if ( sys->slowNotFast == 1)
wait = 10;
else
wait = 0;
sys->Update();
// tell openGL to call this funtion again after "wait" milliseconds
glutTimerFunc(wait, drawFuncs::update, 0);
}
}
// this function redraws the window when necessary
void drawFuncs::display() {
// Clear the window or more specifically the frame buffer...
// This happens by replacing all the contents of the frame
// buffer by the clear color (black in our case)
glClear(GL_COLOR_BUFFER_BIT);
// this puts the camera at the origin (not sure why) with (I think) z axis out of page and y axis up
// there is also the question of the GL perspective which is not set up in any clear way at the moment
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 1.0, /* camera position */
0.0, 0.0, -1.0, /* point to look at */
0.0, 1.0, 0.0); /* up direction */
//sys->DrawSpheres();
sys->DrawSquares();
// Swap contents of backward and forward frame buffers
glutSwapBuffers();
}

27
readme.txt Normal file
View File

@ -0,0 +1,27 @@
To run the code on a Mac through the command line:
1. Download Makefile from this section
2. Put Makefile in same directory as DLASystem.cpp (! Remove the .txt extension if present !)
3. Relative to MS Visual Studio code, these files include the following changes: DLASystem.h Window.h mainDLA.cpp
change #include <gl/freeglut.h>
to #include <GLUT/glut.h>
4. Open the terminal in the same folder as these files
5. Type "make" to build project
6. Run the program by typing "./run"
7. To edit your files, run gedit, save the code, and recompile with "make -B" before running the program again.
Thanks to Eliot Ayache for figuring this out!
PS For Coursework 2: In addition, there needs to be a change in Line 72 of MAIN_Source.cpp file from "glutLeaveMainLoop();" to "exit(1);"

33
rnd.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <random>
// ... don't worry how this all works
// ... member functions that you may want to use:
// random01() returns a random double between 0 and 1
// randomInt(max) returns a random int between 0 and max-1 (inclusive)
class rnd {
private:
// nuts and bolts.. should not need to touch this.
std::default_random_engine generator;
int genMax;
std::uniform_int_distribution<int> *intmax;
std::uniform_real_distribution<double> *real01;
public:
// constructor
rnd() {
genMax = 0x7fffffff;
//cout << "genMax is " << generator.max() << endl;
intmax = new std::uniform_int_distribution<int>(0, genMax);
real01 = new std::uniform_real_distribution<double>(0.0, 1.0);
}
// destructor
~rnd() { delete intmax; delete real01; }
// set the random seed
void setSeed(int seed) { generator.seed(seed); }
// member functions for generating random double in [0,1] and random integer in [0,max-1]
double random01() { return (*real01)(generator); }
int randomInt(int max) { return (*intmax)(generator) % max; }
};