Commit 06daa005 authored by bbguimaraes's avatar bbguimaraes
Browse files

sms

parent ad069ccf
......@@ -8,6 +8,7 @@ for details/demos/screenshots.
- mem_manager: best-fit memory allocator simluation.
- nqueens: n-queens solver and graphical display.
- parser: a top-down parser for a made-up programming language.
- sms: spring-mass system simulation library/GUI.
- sockets: an implementation of the game Jungle ("Dou Shou Qi", in Chinese)
with graphics and network communication.
- solar: graphical simulation of the solar system with embedded internet
......
Spring-Mass System
==================
A library to simulate systems of masses and springs attached to them. Also
includes a graphical (opengl) program for testing.
Building and running
--------------------
$ qmake
$ make
$ (cd sms && LD_LIBRARY_PATH=../libsms ./sms)
#ifndef MASS_H
#define MASS_H
#include <set>
#include "sms/vector.h"
class Spring;
class Mass {
Vector m_position;
Vector m_speed;
double m_mass;
bool m_fixed;
Vector m_force;
std::set<Spring *> m_springs;
public:
Mass() :
m_position(Vector()),
m_speed(0.0f),
m_mass(1.0f),
m_fixed(false),
m_force(0.0f),
m_springs(std::set<Spring *>()) {}
Mass(
Vector position,
Vector speed,
float mass,
bool fixed,
Vector force,
const std::set<Spring *> springs) :
m_position(position),
m_speed(speed),
m_mass(mass),
m_fixed(fixed),
m_force(force),
m_springs(springs) {}
void set_position(const Vector & p) {this->m_position = p;}
void set_speed(const Vector & speed) {this->m_speed = speed;}
void set_mass(double mass) {this->m_mass = mass;}
void set_fixed(bool fixed) {this->m_fixed = fixed;}
void set_force(const Vector & force) {this->m_force = force;}
void set_springs(const std::set<Spring *> & s) {this->m_springs = s;}
Vector position() const {return m_position; }
Vector speed() const {return m_speed; }
double mass() const {return m_mass; }
bool fixed() const {return m_fixed; }
Vector force() const {return m_force; }
std::set<Spring *> * springs() {return &m_springs;}
const std::set<Spring *> * springs() const {return &m_springs;}
};
#endif // MASS_H
#ifndef SPRING_H
#define SPRING_H
#include "sms/vector.h"
class Mass;
class Spring {
double m_k;
double m_eq_length;
Vector m_distance;
Vector m_force;
Mass * m_mass0;
Mass * m_mass1;
public:
Spring() : m_mass0(nullptr), m_mass1(nullptr) {}
void set_k(double k) {this->m_k = k;}
void set_eq_length(double l) {this->m_eq_length = l;}
void set_distance(const Vector & d) {this->m_distance = d;}
void set_force(const Vector & f) {this->m_force = f;}
void set_mass0(Mass * m) {this->m_mass0 = m;}
void set_mass1(Mass * m) {this->m_mass1 = m;}
double k() const {return this->m_k;}
double eq_length() const {return this->m_eq_length;}
Vector distance() const {return this->m_distance;}
Vector force() const {return this->m_force;}
Mass * mass0() {return this->m_mass0;}
Mass * mass1() {return this->m_mass1;}
void update_force();
};
#endif // SPRING_H
#ifndef SPRING_MASS_SYSTEM_H
#define SPRING_MASS_SYSTEM_H
#include <vector>
#include "sms/vector.h"
class Mass;
class Spring;
class SpringMassSystem {
static const float DAMPING;
std::vector<Mass *> m_masses;
std::vector<Spring *> m_springs;
Vector m_gravity;
public:
SpringMassSystem() : m_gravity(Vector(0.0f, -9.8f, 0.0f)) {}
void set_masses(const std::vector<Mass *> & m)
{this->m_masses = m;}
void set_springs(const std::vector<Spring *> & s)
{this->m_springs = s;}
void set_gravity(const Vector & g) {this->m_gravity = g;}
const std::vector<Mass *> & masses() const {return this->m_masses;}
const std::vector<Spring *> & springs() const {return this->m_springs;}
Vector gravity() const {return this->m_gravity;}
static void link(Mass * mass0, Mass * mass1, Spring * spring);
void update(float interval);
};
#endif // SPRING_MASS_SYSTEM_H
#ifndef VECTOR_H
#define VECTOR_H
class Vector {
float m_x, m_y, m_z;
public:
Vector(float x = 0.0f, float y = 0.0f, float z = 0.0f);
float x() const;
float y() const;
float z() const;
float magnitude() const;
Vector normalized() const;
Vector scaled(float n) const;
};
Vector operator+(const Vector & lhs, const Vector & rhs);
Vector operator-(const Vector & lhs, const Vector & rhs);
Vector operator*(float lhs, const Vector & rhs);
Vector operator*(const Vector & lhs, float rhs);
Vector operator/(const Vector & lhs, float rhs);
Vector operator/(float lhs, const Vector & rhs);
#endif // VECTOR_H
TEMPLATE = lib
CONFIG *= c++11
QT =
TARGET = sms
HEADERS = include/sms/*.h
SOURCES = src/*.cpp
INCLUDEPATH = include
OBJECTS_DIR = obj
#include "sms/spring.h"
#include "sms/mass.h"
void Spring::update_force() {
this->m_distance = this->mass1()->position() - this->mass0()->position();
float displacement = this->distance().magnitude() - this->eq_length();
// Hooke's law: F = kX
float force = this->k() * displacement;
// TODO remove normalization?
// can the force be derived from the original value of distance?
this->m_force = this->distance().normalized().scaled(-force);
}
#include "sms/springmasssystem.h"
#include "sms/vector.h"
#include "sms/mass.h"
#include "sms/spring.h"
/*static*/
const float SpringMassSystem::DAMPING = 0.95f;
/*static*/
void SpringMassSystem::link(Mass * mass0, Mass * mass1, Spring * spring) {
mass0->springs()->insert(spring);
mass1->springs()->insert(spring);
spring->set_mass0(mass0);
spring->set_mass1(mass1);
}
void SpringMassSystem::update(float interval) {
for(auto mass : this->masses()) {
if(mass->fixed())
continue;
Vector total_force = mass->force() + (this->m_gravity * mass->mass());
for(auto spring : *mass->springs())
total_force = total_force + (mass == spring->mass1()
? spring->force()
: spring->force() * -1.0f);
Vector speed = mass->speed() + total_force / mass->mass();
speed = speed * this->DAMPING;
mass->set_speed(speed);
mass->set_position(mass->position() + speed * interval);
}
for(auto spring : this->springs())
spring->update_force();
}
#include "sms/vector.h"
#include <cmath>
Vector::Vector(float x, float y, float z)
: m_x(x), m_y(y), m_z(z) {}
float Vector::x() const {
return this->m_x;
}
float Vector::y() const {
return this->m_y;
}
float Vector::z() const {
return this->m_z;
}
float Vector::magnitude() const {
return std::sqrt(
this->x() * this->x()
+ this->y() * this->y()
+ this->z() * this->z());
}
Vector Vector::normalized() const {
auto m = this->magnitude();
return Vector(
this->x() / m,
this->y() / m,
this->z() / m);
}
Vector Vector::scaled(float n) const {
return Vector(
this->x() * n,
this->y() * n,
this->z() * n);
}
Vector operator+(const Vector & lhs, const Vector & rhs) {
return Vector(
lhs.x() + rhs.x(),
lhs.y() + rhs.y(),
lhs.z() + rhs.z());
}
Vector operator-(const Vector & lhs, const Vector & rhs) {
return Vector(
lhs.x() - rhs.x(),
lhs.y() - rhs.y(),
lhs.z() - rhs.z());
}
Vector operator*(const Vector & lhs, float rhs) {
return Vector(
lhs.x() * rhs,
lhs.y() * rhs,
lhs.z() * rhs);
}
Vector operator*(float lhs, const Vector & rhs) {
return Vector(
lhs * rhs.x(),
lhs * rhs.y(),
lhs * rhs.z());
}
Vector operator/(const Vector & lhs, float rhs) {
return Vector(
lhs.x() / rhs,
lhs.y() / rhs,
lhs.z() / rhs);
}
Vector operator/(float lhs, const Vector & rhs) {
return Vector(
lhs / rhs.x(),
lhs / rhs.y(),
lhs / rhs.z());
}
TEMPLATE = subdirs
SUBDIRS = libsms sms
#ifndef CAMERA_H
#define CAMERA_H
#include "sms/vector.h"
class Camera {
Vector m_center;
Vector m_rotation;
float m_distance;
public:
Camera() : m_distance(0.0f) {}
Vector center() const {return this->m_center;}
Vector rotation() const {return this->m_rotation;}
float distance() const {return this->m_distance;}
void set_distance(float d) {this->m_distance = d;}
void change_distance(float d) {this->m_distance += d;}
void move(Vector m) {this->m_center = this->m_center + m;}
void rotate(Vector r) {this->m_rotation = this->m_rotation + r;}
};
#endif // CAMERA_H
#ifndef DISPLAY_H
#define DISPLAY_H
#include <vector>
#include <QGLWidget>
#include <QTime>
#include "sms/vector.h"
#include "camera.h"
class GLUquadric;
class Mass;
class SpringMassSystem;
class Display : public QGLWidget {
Q_OBJECT
public:
Display(QWidget * parent = nullptr);
~Display();
float update_rate() const {return this->m_update_rate;}
Camera * camera() {return &this->m_camera;}
bool paused() const {return this->m_paused;}
bool textured() const {return this->m_textured;}
void set_systems(const std::vector<SpringMassSystem *> & v)
{this->m_systems = v;}
void set_update_rate(float r) {this->m_update_rate = r;}
void set_texture(GLuint t) {this->m_texture = t;}
void set_paused(bool p) {this->m_paused = p;}
void set_draw_grid(bool d) {this->m_draw_grid = d;}
void set_grid_size(int s) {this->m_grid_size = s;}
void set_draw_axes(bool d) {this->m_draw_axes = d;}
void set_draw_masses(bool d) {this->m_draw_masses = d;}
void set_textured(bool t) {this->m_textured = t;}
public slots:
void update_systems();
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
virtual void keyPressEvent(QKeyEvent * event);
virtual void keyReleaseEvent(QKeyEvent * event);
virtual void mousePressEvent(QMouseEvent * event);
virtual void mouseReleaseEvent(QMouseEvent * event);
virtual void mouseMoveEvent(QMouseEvent * event);
virtual void wheelEvent(QWheelEvent * event);
private:
void set_camera();
void select(Vector click);
void draw_grid();
void draw_axes();
void draw_axis(float height);
void draw_circle(float radius);
void draw_system(SpringMassSystem * system);
void draw_springs_non_textured(SpringMassSystem * system);
void draw_springs_textured(SpringMassSystem * system);
void draw_masses(SpringMassSystem * system, GLenum mode);
void draw_hud();
void draw_number(unsigned int n);
void update_fps();
std::vector<SpringMassSystem *> m_systems;
Mass * m_selected;
float m_update_rate;
Camera m_camera;
GLuint m_texture;
GLUquadric * m_quadric;
QTime m_last_frame;
unsigned int m_frame_count;
unsigned int m_fps;
bool m_ctrl_key_down;
Vector m_click_position;
Vector m_last_position;
bool m_paused;
bool m_draw_grid;
bool m_draw_axes;
bool m_draw_masses;
bool m_textured;
int m_grid_size;
};
#endif // DISPLAY_H
#ifndef SMSCREATOR_H
#define SMSCREATOR_H
#include <string>
#include <istream>
#include "sms/vector.h"
class SpringMassSystem;
class SMSCreator {
public:
static void read_from_file(
SpringMassSystem * system, std::istream * input);
static void create_string(
SpringMassSystem * system,
Vector begin, Vector end,
unsigned int n_masses, float total_mass, float k);
static void create_flag(
SpringMassSystem * system,
Vector begin, Vector end,
unsigned int x_masses, unsigned int y_masses,
float total_mass, float k);
static void create_crossed_flag(
SpringMassSystem * system,
Vector begin, Vector end,
unsigned int x_masses, unsigned int y_masses,
float total_mass, float k);
};
#endif // SMSCREATOR_H
TEMPLATE = app
CONFIG *= c++11
QT += opengl
OBJECTS_DIR = obj
MOC_DIR = moc
INCLUDEPATH += ../libsms/include include
DEPENDPATH += include ../lib/include
LIBS += -L../libsms -lsms -lGLU
HEADERS = include/*.h
SOURCES = src/*.cpp
#include "display.h"
#include <cassert>
#include <cmath>
#include <GL/glu.h>
#include <QWheelEvent>
#include "sms/springmasssystem.h"
#include "sms/mass.h"
#include "sms/spring.h"
Display::Display(QWidget * parent) :
QGLWidget(parent),
m_selected(nullptr),
m_update_rate(1.0f),
m_texture(0),
m_quadric(nullptr),
m_last_frame(QTime::currentTime()),
m_ctrl_key_down(false),
m_paused(false),
m_draw_grid(false),
m_draw_axes(false),
m_draw_masses(true),
m_textured(false),
m_grid_size(100) {
this->setFocusPolicy(Qt::StrongFocus);
}
Display::~Display() {
gluDeleteQuadric(this->m_quadric);
}
/*virtual*/
void Display::initializeGL() {
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClearDepth(1.0);
}
/*virtual*/
void Display::resizeGL(int w, int h) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, w, h);
gluPerspective(60.0, static_cast<double>(w) / h, 0.1, 1000.0);
}
/*virtual*/
void Display::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
this->set_camera();
if(this->m_draw_grid)
this->draw_grid();
if(this->m_draw_axes)
this->draw_axes();
for(auto x : this->m_systems)
this->draw_system(x);
this->update_fps();
this->draw_hud();
}
void Display::set_camera() {
glTranslatef(0.0f, 0.0f, -this->m_camera.distance());
auto p = this->m_camera.center();
glTranslatef(p.x(), p.y(), p.z());
auto r = this->m_camera.rotation();
glRotatef(-r.x(), 1.0, 0.0, 0.0);
glRotatef(-r.y(), 0.0, 1.0, 0.0);
glRotatef(-r.z(), 0.0, 0.0, 1.0);
}
void Display::select(Vector click) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
const GLsizei BUFSIZE = 512;
GLuint select_buffer[BUFSIZE];
glSelectBuffer(BUFSIZE, select_buffer);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(
static_cast<GLdouble>(click.x()),
static_cast<GLdouble>(viewport[3]) - click.y(),
5.0, 5.0,
viewport);
gluPerspective(
60.0,
static_cast<double>(viewport[2]) / viewport[3],
0.1,
1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
this->set_camera();
Mass * selected = nullptr;
for(auto system : this->m_systems) {
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
this->draw_masses(system, GL_SELECT);
if(glRenderMode(GL_RENDER) != 0) {
selected = system->masses()[select_buffer[3]];
break;
}
}
glMatrixMode(GL_PROJECTION);
glPopMatrix();
this->m_selected = selected;
}