Compare commits

..

No commits in common. "main" and "initially-provided-code" have entirely different histories.

9 changed files with 524 additions and 195 deletions

1
.gitignore vendored
View File

@ -6,4 +6,3 @@
# Data files
*.csv
*.o

View File

@ -4,18 +4,28 @@
#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) {
void DLASystem::Update() {
if (lastParticleIsActive == 1)
moveLastParticle();
} else if (numParticles < endNum) {
else if (numParticles < endNum) {
addParticleOnAddCircle();
setParticleActive();
} else {
this->running = false;
}
if (lastParticleIsActive == 0 || slowNotFast == 1)
glutPostRedisplay(); //Tell GLUT that the display has changed
}
@ -31,7 +41,7 @@ void DLASystem::clearParticles() {
// remove any existing particles and setup initial condition
void DLASystem::Reset() {
// stop running
this->running = false;
running = 0;
clearParticles();
@ -46,11 +56,16 @@ void DLASystem::Reset() {
// setup initial condition and parameters
addCircle = 10;
killCircle = 2.0 * addCircle;
killCircle = 2.0*addCircle;
clusterRadius = 0.0;
// add a single particle at the origin
double pos[] = {0.0, 0.0};
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
@ -58,29 +73,31 @@ void DLASystem::Reset() {
// 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;
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)];
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) {
this->running = false;
cerr << "STOP" << endl;
pauseRunning();
cout << "STOP" << endl;
glutPostRedisplay(); // update display
return 1;
} else return 0;
}
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);
Particle * p = new Particle(pos);
// push_back means "add this to the end of the list"
particleList.push_back(p);
numParticles++;
@ -100,7 +117,7 @@ void DLASystem::addParticleOnAddCircle() {
if (readGrid(pos) == 0)
addParticle(pos);
else
cerr << "FAIL " << pos[0] << " " << pos[1] << endl;
cout << "FAIL " << pos[0] << " " << pos[1] << endl;
}
// send back the position of a neighbour of a given grid cell
@ -127,6 +144,19 @@ void DLASystem::setPosNeighbour(double setpos[], double pos[], int val) {
}
}
// 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[]) {
@ -143,6 +173,7 @@ void DLASystem::updateClusterRadius(double pos[]) {
if (addCircle < check) {
addCircle = check;
killCircle = killRatio * addCircle;
updateViewSize();
}
checkStop();
}
@ -158,12 +189,14 @@ void DLASystem::moveLastParticle() {
setPosNeighbour(newpos, lastP->pos, rr);
if (distanceFromOrigin(newpos) > killCircle) {
//cerr << "#deleting particle" << endl;
//cout << "#deleting particle" << endl;
setGrid(lastP->pos, 0);
particleList.pop_back(); // remove particle from particleList
numParticles--;
setParticleInactive();
} else if (readGrid(newpos) == 0) {
}
// 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];
@ -172,56 +205,68 @@ void DLASystem::moveLastParticle() {
// check if we stick
if (checkStick()) {
//cerr << "stick" << endl;
//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 and the particle will stick probabilistically.
if (readGrid(checkpos) == 1 && rgen.random01() < stickProbability) {
return 1;
// if the neighbour is occupied...
if (readGrid(checkpos) == 1)
result = 1;
}
}
return 0;
return result;
}
// constructor
DLASystem::DLASystem(const int maxParticles, const string &csvPath, const double stickProbability) {
cerr << "creating system, gridSize " << gridSize << endl;
DLASystem::DLASystem(Window *set_win) {
cout << "creating system, gridSize " << gridSize << endl;
win = set_win;
numParticles = 0;
endNum = maxParticles;
this->stickProbability = stickProbability;
endNum = 1000;
// allocate memory for the grid, remember to free the memory in destructor
grid = new int *[gridSize];
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
csv.open(csvPath);
// 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...
cerr << "deleting system" << endl;
cout << "deleting system" << endl;
// delete the particles
clearParticles();
// delete the grid
@ -229,16 +274,46 @@ DLASystem::~DLASystem() {
delete[] grid[i];
delete[] grid;
if (csv.is_open()) {
csv.flush();
csv.close();
}
if (logfile.is_open())
logfile.close();
}
void DLASystem::exportData() {
csv << "x,y" << endl;
for (auto particle: particleList) {
csv << particle->pos[0] << "," << particle->pos[1] << endl;
// 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);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <GLUT/glut.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
@ -10,6 +11,7 @@
#include <string>
#include <sstream>
#include "Window.h"
#include "Particle.h"
#include "rnd.h"
@ -20,15 +22,12 @@ 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;
/*
* The probability that a particle will stick at a given site when adjacent.
* */
double stickProbability;
// delete particles and clear the particle list
void clearParticles();
@ -42,11 +41,15 @@ class DLASystem {
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;
// CSV ouput
ofstream csv;
// output file (not used at the moment)
ofstream logfile;
// number of particles at which the simulation will stop
// (the value is set in constructor)
@ -62,16 +65,22 @@ class DLASystem {
// update the system: if there is an active particle then move it,
// else create a new particle (on the adding circle)
void update();
void Update();
// is the simulation running
bool running;
// 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
explicit DLASystem(int maxParticles, const string &csvPath, double stickProbability);
DLASystem(Window *set_win);
// destructor
~DLASystem();
@ -84,6 +93,25 @@ class DLASystem {
// 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[] );
@ -118,5 +146,9 @@ class DLASystem {
// currently it sticks whenever it touches another particle
int checkStick();
void exportData();
// 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); }
};

View File

@ -10,11 +10,11 @@
CXX = clang++
CXXFLAGS = -Wall -Wextra -g -O0 --std=c++20
CXXFLAGS = -Wall -Wextra -g -O0
IFLAGS = -I/usr/local/include -I/usr/include
LFLAGS = -L/usr/local/lib -lm
LFLAGS = -L/usr/local/lib -lm -framework OpenGL -framework GLUT
# ------------------------------------------
# FOR GENERIC MAKEFILE:

View File

@ -1,4 +0,0 @@
# DLA Model
This currently contains The Initially Provided Code for the DLA model which will be used as the source of truth for
further alterations once verified.

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[]);
};

View File

@ -1,29 +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) {
if (argc != 5) {
cerr << "Usage " << argv[0] << " <seed> <maxParticles> <stickProbability> <csvPath>" << endl;
return 1;
}
// turn on glut
glutInit(&argc, argv);
int seed = std::stoi(argv[1]);
int maxParticles = std::stoi(argv[2]);
double stickProbability = std::stod(argv[3]);
string csvPath = argv[4];
std::cerr << "Setting seed " << seed << endl;
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
auto *sys = new DLASystem(maxParticles, csvPath, stickProbability);
sys = new DLASystem(win);
// this is the seed for the random numbers
int seed = 6;
cout << "setting seed " << seed << endl;
sys->setSeed(seed);
sys->running = true;
while (sys->running) {
sys->update();
}
// print the "help" message to the console
drawFuncs::introMessage();
sys->exportData();
// 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);"