I have a class named Particle and i want to create a vector of particles. The problem is that my code is not drawing the objects stored in the vector. I tried different implementations, the first one was:
std::vector<Particle> particles(10);
and then i filled the vectors:
for(size_t i = 0; i < 10; ++i) particles.emplace_back(new Particle(false));
for drawing i used:
for(const auto& x: particles) window.draw(x);
the problem is that this is drawing just one object. Then i tried to create a class of particles container with a vector of shared_ptr:
std::vector<std::shared_ptr<Particle>> particle_container;
And the problem is the same, it just draws one particle, i don't know if theres something wrong with my implementation of the class particle or something wrong in the waay i use the draw function of sfml. This is my Particle class:
#include "SFML/Graphics.hpp"
#include <random>
#include <cmath>
#include <limits>
#include <memory>
using namespace sf;
class Particle : public sf::Drawable{
sf::CircleShape particle;
Vector2f position; //current position of the particle
Vector2f velocity; //velocity of the particle
double radius; //radius of each particle
double mass; //mass of the particle
int count; //number of collisions of each particle
bool sick; //if the particle is infected
Color color; //color of the particle
public:
//constructors
Particle(Vector2f _position, Vector2f _velocity, double _radius, double _mass, bool _sick);
Particle(bool sick);
}:
the Particle.cpp:
#include "Particle.h"
template<class T>
double Rand(T first, T last) {
std::random_device dev;
std::default_random_engine eng(dev());
std::uniform_real_distribution<double> dis(first, last);
return dis(eng);
}
Particle::Particle(Vector2f _position, Vector2f _velocity, double _radius, double _mass, bool _sick) {
position = _position;
velocity = _velocity;
radius = _radius;
mass = _mass;
sick = _sick;
if(sick) color = sf::Color::Green;
else color = sf::Color::Red;
//giving values to the particle
particle.setPosition(position);
particle.setRadius(radius);
particle.setFillColor(color);
}
Particle::Particle(bool sick) {
position.x = Rand(0.0, 550.0);
position.y = Rand(0.0, 550.0);
velocity.x = Rand(-0.005, 0.005);
velocity.y = Rand(-0.005, 0.005);
radius = 2.0;
mass = 0.5;
if(sick) color = sf::Color::Green;
else color = sf::Color::Red;
particle.setPosition(position);
particle.setRadius(radius);
particle.setFillColor(color);
}
And this is the particles_container
class particles_container : public sf::Drawable {
std::vector<std::shared_ptr<Particle>> particle_container;
//std::vector<Particle> particle_container;
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
public:
//default constructor
particles_container();
//constructor with a number of given particles
explicit particles_container(size_t size);
//default destructor
~particles_container() override;
//returns the container
std::vector<std::shared_ptr<Particle>> getContainer();
//another function to draw
void drawContainer(sf::RenderWindow &window);
};
the particles_container.cpp:
void particles_container::draw(RenderTarget &renderTarget, sf::RenderStates renderStates) const {
for (const auto & i : particle_container) {
renderTarget.draw(*i);
}
}
particles_container::particles_container(size_t size) {
if(size > 0){
for(size_t i = 0; i < size; ++i) particle_container.emplace_back(new Particle(false));
}
}
std::vector<std::shared_ptr<Particle>> particles_container::getContainer() {
return particle_container;
}
void particles_container::drawContainer(RenderWindow &window) {
for (auto & i : particle_container) {
window.draw(*i);
}
}
and finally this is my main.cpp:
#include "Particle.h"
#include "particles_container.h"
int main() {
sf::RenderWindow window(sf::VideoMode (600, 600), "PF");
std::vector<Particle> particles(10);
for(size_t i = 0; i < 10; ++i) particles.emplace_back(new Particle(false));
Particle p1(sf::Vector2f(45.0,45.0), sf::Vector2f(0.01,0.005), 2, 0.5, false);
particles_container parts(100);
Particle p3(true);
std::vector<std::shared_ptr<Particle> > my_vector(10);
for(size_t i = 0; i < 10; ++i) my_vector.emplace_back(new Particle(false));
while(window.isOpen()){
sf::Event event;
while(window.pollEvent(event)){
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
window.clear();
//parts.drawContainer(window);
//window.draw(parts);
for(const auto& x: particles) window.draw(x);
window.display();
}
}
this is the output:
output
Any help or advice would be greatly appreciated, thanks.
I am using:
SFML 2.5.1 and c++17
Your Rand function constructs a new random device every time, which has a high probability to always return the same number sequence. Move the construction of dev out of your Rand function.
Related
I am having 12 errors looping from:
Unexpected token(s) preceding ';'
missing type specifier - int assumed, Note: C++ does not support default - int
syntax error: missing ';' before '*
at Line 11 of Player.h
Here is the player.h code:
#pragma once
#include "GameObjects.h"
using namespace sf;
class Player : GameObject2D
{
public:
// Classes
GameSprite* playerSprite = new GameSprite("Textures/player.png", Vector2f(32.f, 32.f), IntRect(0, 0, 64, 64));
PlayerAnimationPlayer* anim = new PlayerAnimationPlayer(this, "idle", 0.5f, 0, 2, "", true);
// Sprite and animation variables
IntRect spriteFrames[3] = {
IntRect(0, 0, 64, 64),
IntRect(64, 0, 64, 64),
IntRect(128, 0, 64, 64),
};
// Movement variables
sf::Vector2f direction;
sf::Vector2f velocity;
const float speed = 300.f;
// Player functions
void inputDirection();
// Game Object functions
void update(float deltaTime) override;
void inputEvent(Event ev) override;
void draw(RenderWindow& target) override;
void free() override;
// 2D Functions
void setPos(float x, float y) override;
Vector2f moveObj(Vector2f direction, float speed, float deltaTime);
};
class PlayerAnimationPlayer : AnimationPlayer
{
public:
Player* parent;
PlayerAnimationPlayer(Player* newParent, std::string startAnimation,
float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart);
void animation(int index) override;
};
Here is the game object code (INCLUDING THE ANIMATION PLAYER):
#pragma once
#include <SFML\Graphics.hpp>
#include <map>
#include <iostream>
// BASE OBJECT OF ALL GAME OBJECTS
class GameObject
{
public:
virtual void update(float deltaTime) {}
virtual void inputEvent(sf::Event ev) {}
virtual void draw(sf::RenderWindow& target) {} // This draws all drawable objects
virtual void free() {} // This frees all pointer variables including the class itself
};
// GAMEOBJECT USED FOR 2D AND MOVEMENT
class GameObject2D : GameObject
{
public:
sf::Vector2f position;
sf::Vector2f getPos(); // Returns the position
virtual void setPos(float x, float y); // This moves all child classes including the class itself
virtual sf::Vector2f moveObj(sf::Vector2f direction, float speed, float deltaTime); // Moves all child classes by direction
};
// Class for sprites
class GameSprite
{
public:
sf::Sprite sprite;
sf::Texture texture;
GameSprite(std::string texturePath, sf::Vector2f offset, sf::IntRect visibleFrame);
void setSprite(std::string newTexturePath, sf::Vector2f offset = sf::Vector2f(0.f, 0.f), sf::IntRect visibleFrame = sf::IntRect(0, 0, -1, -1));
};
// THIS ANIMATES THE OBJECTS
class AnimationPlayer
{
public:
int maxIndex = 10;
sf::Clock clock;
std::string activeAnimation;
void updateTime(); // Updates the time
public:
bool isActive;
bool oneShot;
float speed = 1.f;
int index = 0;
AnimationPlayer(std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot = false, bool autoStart = false);
void start(std::string animation, int startIndex = 0); // Starts the animation player
void stop(); // Stops the animation player
virtual void animation(int index) {}
};
Here is the cpp file for animation player
#include "GameObjects.h"
// CONSTRUCTOR AND MAIN LOGIC
AnimationPlayer::AnimationPlayer(std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart)
{
activeAnimation = startAnimation;
speed = newSpeed;
index = startIndex;
maxIndex = newMaxIndex;
oneShot = isOneShot;
isActive = autoStart;
std::cout << "Constructor called" << std::endl;
if (oneShot)
start(startAnimation, startIndex);
}
// Updates the time
void AnimationPlayer::updateTime()
{
if (clock.getElapsedTime().asSeconds() > speed)
{
if (!isActive)
return;
if (index > maxIndex)
{
if (oneShot)
isActive = false;
else
index = 0;
}
else
{
index += 1;
}
animation(index);
}
}
/// STARTS AND STOP THE ANIMATION PLAYER
// Starts the animation player
void AnimationPlayer::start(std::string animation, int startIndex)
{
isActive = true;
index = startIndex;
}
// Stops the animation player
void AnimationPlayer::stop()
{
isActive = false;
}
Here is the code for the player animation player cpp
#include "Player.h"
PlayerAnimationPlayer::PlayerAnimationPlayer(Player* newParent, std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart) :
AnimationPlayer(startAnimation, newSpeed, startIndex, newMaxIndex, isOneShot, autoStart)
{
parent = newParent;
}
void PlayerAnimationPlayer::animation(int index)
{
parent->playerSprite->sprite.setTextureRect(parent->spriteFrames[index]);
}
Ty!
I'm trying to learn C++, and I'm making this little space-invader-like game to get better at it.
Currently everything works just fine but for some reason the performance of the game is kind of terrible considering how simple the graphics are (the game runs smoothly for a while and then stops for like half a second, constantly).
I'm pretty sure the lag is due to the amount of bullets I'm removing and creating from the vector containing them (I'm removing the bullets that go outside the screen because there's no point in updating them or rendering them). So how could I fix this?
I checked the game loop and the problem isn't there.
Code below
player.h:
#pragma once
#include <vector>
#include <iostream>
#include "entity.h"
#include "SFML/Graphics.hpp"
#include "vector.h"
class Player : public Entity {
private:
int f_lastshot = 0;
const double shoot_delay = 0.5;
const float bullet_speed = 1, speed = 0.75;
std::vector<Entity> bullets;
sf::Clock *c;
sf::Sprite bullet_sprite;
public:
Player(Vector &pos, int w, int h, sf::Sprite &spr);
~Player();
void init();
void update();
void render(Window &win);
void shoot();
};
player.cpp:
#include "player.h"
Player::Player(Vector &pos, int w, int h, sf::Sprite &spr) : Entity() {
this->pos = pos;
this->sprite = spr;
this->width = w;
this->height = h;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setScale(scale_x, scale_y);
}
void Player::update() {
if (sf::Mouse::isButtonPressed(sf::Mouse::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
if ((double)(f_lastshot / 60) > shoot_delay) // shoot only if enough time has passed
shoot();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) dir.setX(-speed); // move left
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) dir.setX(speed); // move right
else dir.setX(0); // stop if no button is being pressed
f_lastshot++;
pos = pos + dir; // add the direction to the position
for (Entity &e : bullets) {
e.update(); // update each bullet
if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
bullets.erase(bullets.begin());
}
}
void Player::render(Window &win) {
sprite.setPosition(pos.getX(), pos.getY()); // update the position of the sprite
win.render(sprite);
for (Entity &bullet : bullets)
bullet.render(win);
}
void Player::shoot() {
f_lastshot = 0;
bullets.push_back(Entity::Entity(Vector::Vector(pos.getX() + width / 2 - 8, pos.getY()), 16, 32, bullet_sprite, Vector::Vector(0,-bullet_speed)));
}
void Player::init() {
if (c == nullptr) c = new sf::Clock();
else c->restart();
dir.setX(0); dir.setY(0);
sf::Texture *bullet_texture = new sf::Texture();
if (!bullet_texture->loadFromFile("bullet.png"))
std::cerr << "Could not load bullet image" << std::endl;
bullet_sprite.setTexture(*bullet_texture);
}
Player::~Player() {
delete bullet_sprite.getTexture();
delete c;
}
entity.h:
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
#include "vector.h"
#include "window.h"
class Entity {
protected:
Vector pos;
sf::Sprite sprite;
int width, height;
Vector dir; // the direction
public:
Entity();
Entity(Vector &pos, int w, int h, sf::Sprite &spr);
Entity(Vector pos, int w, int h, sf::Sprite &spr);
Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir);
void update();
void render(Window &win);
bool collides(Entity &other) const;
public:
Vector getPos() const;
Vector getDirection() const;
sf::Sprite getSprite() const;
int getWidth() const;
int getHeight() const;
void setPos(Vector &pos);
void setWidth(int width);
void setHeight(int height);
void setSize(int width, int height);
void setSprite(sf::Sprite &sprite);
void setDirection(Vector dir);
};
entity.cpp:
#include "entity.h"
Entity::Entity() {}
Entity::Entity(Vector &pos, int w, int h, sf::Sprite &spr)
: pos(pos), width(w), height(h) {
this->sprite = spr;
float scale_x = (float)w/(float)spr.getTexture()->getSize().x;
float scale_y = (float)h/(float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
sprite.setScale(scale_x, scale_y);
}
Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr)
: pos(pos), width(w), height(h) {
this->sprite = spr;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
}
Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir)
: pos(pos), width(w), height(h) {
this->sprite = spr;
this->dir = dir;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
}
bool Entity::collides(Entity &other) const {
bool x_coll = (pos.getX() + width> other.getPos().getX() && pos.getX()< other.getPos().getX())||(pos.getX() < other.getPos().getX() + other.getWidth() && pos.getX() > other.getPos().getX());
bool y_coll = (pos.getY() + height > other.getPos().getY() && pos.getY() < other.getPos().getY()||(pos.getY() < other.getPos().getY() + other.getHeight() && pos.getY() > other.getPos().getY()));
return (x_coll && y_coll);
}
void Entity::render(Window &win) {
sprite.setPosition(pos.getX(), pos.getY());
win.render(sprite);
}
void Entity::update() {
pos = pos + dir;
}
Vector Entity::getPos() const { return pos; }
int Entity::getWidth() const { return width; }
int Entity::getHeight() const { return height; }
sf::Sprite Entity::getSprite() const { return sprite; }
Vector Entity::getDirection() const { return dir; }
void Entity::setPos(Vector &pos) { this->pos = pos; }
void Entity::setWidth(int width) { this->width = width; }
void Entity::setHeight(int height) { this->height = height; }
void Entity::setSprite(sf::Sprite &sprite) { this->sprite = sprite; }
void Entity::setDirection(Vector dir) { this->dir = dir; }
void Entity::setSize(int width, int height) {
this->width = width;
this->height = height;
}
To identify bottlenecks and other performance issues you should use a profiler. Only by looking the code it's hard if not impossible to find the correct places that need optimisation. But one problem is indeed the way you remove bullets:
for (Entity &e : bullets) {
e.update(); // update each bullet
if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
bullets.erase(bullets.begin());
}
There is a logic error. You check if the current bullet e is outside the screen, but then delete the first one anyway. This can't be right.
It's extremely inefficient to delete the first element of a std::vector, because all the following elements will have to be moved one place left. This happens for every bullet you remove!
It may cause undefined behavior. The reference says that erase
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
Changing the size of a vector while iterating over it is usually a bad idea.
To overcome those issues you could use the Erase-Remove-Idiom like this:
// Update as usual
for (Entity &e : bullets) {
e.update();
}
// Erase-Remove bullets that are out of screen
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
[](const Entity &e){ return e.getY() < 0;}), bullets.end());
I have some code that when compiled, runs an Asteroid Game. I want to make some changes. In place of sprites for the ship, I would like to use trianges. For a bullet, I'd like to use a small rectangle, and finally, a polygon for the asteroids. The code uses an Entity Master Class with a list. Can somebody please elaborate on how to make these changes?
#include <SFML/Graphics.hpp>
#include <time.h>
#include <list>
using namespace sf;
const int W = 1200;
const int H = 800;
float DEGTORAD = 0.017453f;
class Animation
{
public:
float Frame, speed;
Sprite sprite;
std::vector<IntRect> frames;
Animation(){}
Animation (Texture &t, int x, int y, int w, int h, int count, float Speed)
{
Frame = 0;
speed = Speed;
for (int i=0;i<count;i++)
frames.push_back( IntRect(x+i*w, y, w, h) );
sprite.setTexture(t);
sprite.setOrigin(w/2,h/2);
sprite.setTextureRect(frames[0]);
}
void update()
{
Frame += speed;
int n = frames.size();
if (Frame >= n) Frame -= n;
if (n>0) sprite.setTextureRect( frames[int(Frame)] );
}
bool isEnd()
{
return Frame+speed>=frames.size();
}
};
class Entity
{
public:
float x,y,dx,dy,R,angle;
bool life;
std::string name;
Animation anim;
Entity()
{
life=1;
}
void settings(Animation &a,int X,int Y,float Angle=0,int radius=1)
{
anim = a;
x=X; y=Y;
angle = Angle;
R = radius;
}
virtual void update(){};
void draw(RenderWindow &app)
{
anim.sprite.setPosition(x,y);
anim.sprite.setRotation(angle+90);
app.draw(anim.sprite);
CircleShape circle(R);
circle.setFillColor(Color(255,0,0,170));
circle.setPosition(x,y);
circle.setOrigin(R,R);
//app.draw(circle);
}
virtual ~Entity(){};
};
class asteroid: public Entity
{
public:
asteroid()
{
dx=rand()%8-4;
dy=rand()%8-4;
name="asteroid";
}
void update()
{
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
class bullet: public Entity
{
public:
bullet()
{
name="bullet";
}
void update()
{
dx=cos(angle*DEGTORAD)*6;
dy=sin(angle*DEGTORAD)*6;
// angle+=rand()%7-3; /*try this*/
x+=dx;
y+=dy;
if (x>W || x<0 || y>H || y<0) life=0;
}
};
class player: public Entity
{
public:
bool thrust;
player()
{
name="player";
}
void update()
{
if (thrust)
{ dx+=cos(angle*DEGTORAD)*0.2;
dy+=sin(angle*DEGTORAD)*0.2; }
else
{ dx*=0.99;
dy*=0.99; }
int maxSpeed=15;
float speed = sqrt(dx*dx+dy*dy);
if (speed>maxSpeed)
{ dx *= maxSpeed/speed;
dy *= maxSpeed/speed; }
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
bool isCollide(Entity *a,Entity *b)
{
return (b->x - a->x)*(b->x - a->x)+
(b->y - a->y)*(b->y - a->y)<
(a->R + b->R)*(a->R + b->R);
}
int main()
{
srand(time(0));
RenderWindow app(VideoMode(W, H), "Asteroids!");
app.setFramerateLimit(60);
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
t1.setSmooth(true);
t2.setSmooth(true);
Sprite background(t2);
Animation sExplosion(t3, 0,0,256,256, 48, 0.5);
Animation sRock(t4, 0,0,64,64, 16, 0.2);
Animation sRock_small(t6, 0,0,64,64, 16, 0.2);
Animation sBullet(t5, 0,0,32,64, 16, 0.8);
Animation sPlayer(t1, 40,0,40,40, 1, 0);
Animation sPlayer_go(t1, 40,40,40,40, 1, 0);
Animation sExplosion_ship(t7, 0,0,192,192, 64, 0.5);
std::list<Entity*> entities;
for(int i=0;i<15;i++)
{
asteroid *a = new asteroid();
a->settings(sRock, rand()%W, rand()%H, rand()%360, 25);
entities.push_back(a);
}
player *p = new player();
p->settings(sPlayer,200,200,0,20);
entities.push_back(p);
/////main loop/////
while (app.isOpen())
{
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::Closed)
app.close();
if (event.type == Event::KeyPressed)
if (event.key.code == Keyboard::Space)
{
bullet *b = new bullet();
b->settings(sBullet,p->x,p->y,p->angle,10);
entities.push_back(b);
}
}
if (Keyboard::isKeyPressed(Keyboard::Right)) p->angle+=3;
if (Keyboard::isKeyPressed(Keyboard::Left)) p->angle-=3;
if (Keyboard::isKeyPressed(Keyboard::Up)) p->thrust=true;
else p->thrust=false;
for(auto a:entities)
for(auto b:entities)
{
if (a->name=="asteroid" && b->name=="bullet")
if ( isCollide(a,b) )
{
a->life=false;
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion,a->x,a->y);
e->name="explosion";
entities.push_back(e);
for(int i=0;i<2;i++)
{
if (a->R==15) continue;
Entity *e = new asteroid();
e->settings(sRock_small,a->x,a->y,rand()%360,15);
entities.push_back(e);
}
}
if (a->name=="player" && b->name=="asteroid")
if ( isCollide(a,b) )
{
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion_ship,a->x,a->y);
e->name="explosion";
entities.push_back(e);
p->settings(sPlayer,W/2,H/2,0,20);
p->dx=0; p->dy=0;
}
}
if (p->thrust) p->anim = sPlayer_go;
else p->anim = sPlayer;
for(auto e:entities)
if (e->name=="explosion")
if (e->anim.isEnd()) e->life=0;
if (rand()%150==0)
{
asteroid *a = new asteroid();
a->settings(sRock, 0,rand()%H, rand()%360, 25);
entities.push_back(a);
}
for(auto i=entities.begin();i!=entities.end();)
{
Entity *e = *i;
e->update();
e->anim.update();
if (e->life==false) {i=entities.erase(i); delete e;}
else i++;
}
//////draw//////
app.draw(background);
for(auto i:entities) i->draw(app);
app.display();
}
return 0;
}
it is not really relevant question to be asked here,
but take a look at this part of code:
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
I'm trying to implement collision detection in sfml using the separating axis theorem but my function to get the min translation vector (getMtv()) is always returning that there is a collision (MTV::IsValid()). I was following a tutorial here
but I can't see where I went wrong.
#include <iostream>
#include <math.h>
#include <SFML/Graphics.hpp>
#include <gtest/gtest.h>
typedef sf::Vector2f Vector2;
typedef sf::Vector2f Axis;
typedef sf::Vector2f Projection;
typedef std::vector<Axis> AxesVec;
class MTV
{
private:
bool _valid;
Axis _axis;
float _overlap;
public:
MTV(Axis axis, float overlap, bool valid)
{
_valid = valid;
_overlap = overlap;
_axis = axis;
}
bool IsValid() const
{
return _valid;
}
};
Vector2 perpendicular(Vector2 v)
{
return Vector2(v.y, -v.x);
}
float dot(Vector2 vec1, Vector2 vec2)
{
return (vec1.x * vec2.x) + (vec1.y * vec2.y);
}
float magnitude(Vector2 v)
{
return std::sqrt(dot(v,v));
}
class Polygon : public sf::ConvexShape
{
public:
const AxesVec& GetAxes() const
{
return axes;
}
AxesVec axes;
void generateAxes()
{
for (int i = 0; i < getPointCount(); i++)
{
// get the current vertex
Vector2 p1 = getPoint(i); //shape.vertices[i];
// get the next vertex
Vector2 p2 = getPoint(i + 1 == getPointCount() ? 0 : i + 1);
// subtract the two to get the edge vector
Vector2 edge = p1 - p2;
// get either perpendicular vector
Vector2 normal = perpendicular(edge);
// the perp method is just (x, y) => (-y, x) or (y, -x)
axes.push_back(normal / magnitude(normal));
}
};
float cross(Vector2 vec1, Vector2 vec2)
{
return (vec1.x * vec2.y) - (vec1.y * vec2.x);
}
Vector2 project(Polygon p, Axis axis)
{
float min = dot(axis, p.getPoint(0)); //axis.dot(shape.vertices[0]);
float max = min;
for (int i = 1; i < p.getPointCount(); i++)
{
// NOTE: the axis must be normalized to get accurate projections
float prj = dot(axis, p.getPoint(i));//axis.dot(shape.vertices[i]);
if (prj < min)
{
min = prj;
}
else if (prj > max)
{
max = prj;
}
}
//Projection proj = new Projection(min, max);
return Projection(min, max);
}
class Collison
{
private:
Vector2 mtv;
Polygon a;
Polygon b;
};
bool overlap(Projection a, Projection b)
{
// x = min & y = max
return !(a.x > b.y || a.x > b.y);
}
float getOverlap(Projection a, Projection b)
{
// x = min & y = max
return (a.y < b.y) ? a.y - b.x : b.y - a.x;
}
MTV getMtv(Polygon a, Polygon b)
{
float overlapMax = std::numeric_limits<float>::infinity();// really large value;
float Overlap;
Axis smallest;// = null;
AxesVec axesA = a.GetAxes();
AxesVec axesB = b.GetAxes();
// loop over the axes1
for (auto&& axis : axesA) //for (int i = 0; i < axes1.length; i++)
{
//Axis axis = axes1[i];
// project both shapes onto the axis
Projection pA = project(a, axis);
Projection pB = project(b, axis);
// do the projections overlap?
if (!overlap(pA, pB)) //(!p1.overlap(p2))
{
// then we can guarantee that the shapes do not overlap
return MTV(smallest, 0, false);
}
else
{
// get the overlap
float o = getOverlap(pA, pB); //p1.getOverlap(p2);
// check for minimum
if (o < overlapMax)
{
// then set this one as the smallest
Overlap = o;
smallest = axis;
}
}
}
for (auto&& axis : axesB) //for (int i = 0; i < axes1.length; i++)
{
//Axis axis = axes1[i];
// project both shapes onto the axis
Projection pA = project(a, axis);
Projection pB = project(b, axis);
// do the projections overlap?
if (!overlap(pA, pB)) //(!p1.overlap(p2))
{
// then we can guarantee that the shapes do not overlap
return MTV(smallest, 0, false);
}
else
{
// get the overlap
double o = getOverlap(pA, pB); //p1.getOverlap(p2);
// check for minimum
if (o < overlapMax)
{
// then set this one as the smallest
Overlap = o;
smallest = axis;
}
}
}
//MTV mtv = new MTV(smallest, overlap);
// if we get here then we know that every axis had overlap on it
// so we can guarantee an intersection
return MTV(smallest, Overlap, true);
}
int main(int argc, char **argv)
{
Polygon polygon;
polygon.setPointCount(3);
polygon.setPoint(0, sf::Vector2f(500, 100));
polygon.setPoint(1, sf::Vector2f(250, 500));
polygon.setPoint(2, sf::Vector2f(750, 500));
polygon.setFillColor(sf::Color::Red);
polygon.generateAxes();
Polygon polygon2;
polygon2.setPointCount(3);
polygon2.setPoint(0, sf::Vector2f(100, 0));
polygon2.setPoint(1, sf::Vector2f(50, 150));
polygon2.setPoint(2, sf::Vector2f(150, 150));
polygon2.generateAxes();
//polygon2.setPoint(0, sf::Vector2f(100, 0));
//polygon2.setPoint(1, sf::Vector2f(500, 150));
//polygon2.setPoint(2, sf::Vector2f(250, 150));
polygon2.setFillColor(sf::Color::Green);
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
// run the program as long as the window is open
while (window.isOpen())
{
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
// clear the window with black color
window.clear(sf::Color::Black);
// draw everything here...
window.draw(polygon);
window.draw(polygon2);
std::cout << getMtv(polygon, polygon2).IsValid() << std::endl;
// end the current frame
window.display();
}
return 0;
}
This is my implementation of Sutherland-Hodgman Polygon Clipping Algorithm.
I am trying to clip a Positively Oriented and Convex polygon with a rectangular clipping area.
I am using BGI library from colorado university. I am using OS=Win7. Visual C++ 2008 Express.
This program isn't working correctly.
//Sutherland-Holdgman Line Clipping
#include "Line2d.h"
#include "Rectangle2d.h"
#include "Coordinates2d.h"
#include "Bits.h"
#include "Polygon2d.h"
#include <list>
typedef enum PointPosition
{
Left, Right
} PointPosition;
typedef enum LinePosition
{
CompletelyOut, CompletelyIn, ClippingCandidate, ClippingCandidateExtended
} LinePosition;
class ClippingPolygon2d
{
private:
Rectangle2d rectangle;
Polygon2d polygon;
public:
ClippingPolygon2d(){}
//ClippingPolygon2d(Rectangle2d & rectangle, Polygon2d & polygon): rectangle(rectangle), polygon(polygon){}
void SetCandidatePolygon(Polygon2d & pol)
{
polygon = pol;
}
void SetClippingPolygon(Rectangle2d & rect)
{
rectangle = rect;
}
public:
std::vector<Point2d> GetClippedPoints()
{
std::vector<Point2d> vertexList = polygon.GetVertices();
std::vector<Point2d> clipping = rectangle.GetVertices();
for (size_t i = 0; i < clipping.size(); i++)
{
//obtaining one vertex and the next one from the clipping region.
//Then, constructing a line from them.
Line2d clippingEdge(clipping[i], clipping[(i + 1) % clipping.size()]);
std::vector<Point2d> temp;
for (size_t j = 0; j < vertexList.size(); j++)
{
Point2d polygonEdgeStart = vertexList[j];
Point2d polygonEdgeEnd = vertexList[(j + 1) % vertexList.size()];
if (clippingEdge.onLeft(polygonEdgeStart))
{
if (clippingEdge.onLeft(polygonEdgeEnd))
{
// point on the left, just add to vertexList
temp.push_back(polygonEdgeEnd);
}
else //Right
{
// calculate intersection I and add it to vertexList
temp.push_back(clippingEdge.GetIntersection(Line2d(polygonEdgeStart, polygonEdgeEnd)));
}
}
else //Right
{
if (clippingEdge.onLeft(polygonEdgeEnd))
{
//calculate intersection I and add I and polygonEdgeEnd to vertexList
temp.push_back(clippingEdge.GetIntersection(Line2d(polygonEdgeStart, polygonEdgeEnd)));
temp.push_back(polygonEdgeEnd);
}
else //Right
{
// nothing to do: outside of the window
}
}
}
vertexList = temp;
}
return vertexList;
}
};
int main()
{
//////////////////////// Initialize ///////////////////////
Coordinates2d::ShowWindow("Sutherland-Hodgeman Line Clipping");
///////////////////////////////////////////////////////////////
Rectangle2d rectangle(Point2d(20, 20), Point2d(200, 140));
Polygon2d polygon;
polygon.Add(Point2d(30, 40));
polygon.Add(Point2d(110,40));
polygon.Add(Point2d(130,110));
polygon.Add(Point2d(70,150));
polygon.Add(Point2d(10,110));
ClippingPolygon2d clip;
clip.SetClippingPolygon(rectangle);
clip.SetCandidatePolygon(polygon);
std::vector<Point2d> clippedVerticesList = clip.GetClippedPoints();
Coordinates2d::Draw(polygon, Red);
Coordinates2d::Draw(rectangle, Magenta);
Coordinates2d::Draw(clippedVerticesList, Thick, Yellow);
//////////////////////// Draw /////////////////////////
Coordinates2d::Wait(); return 0;
///////////////////////////////////////////////////////////////
}
double Line2d :: orientationOf(Point2d & point)
{
return (end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x);
}
bool Line2d :: onRight(Point2d & point)
{
return orientationOf(point) < -E; //for precision reason
}
bool Line2d :: onLeft(Point2d & point)
{
return orientationOf(point) > E;//for precision reason
}
I found at least 2 problems in GetClippedPoints function:
you use if(i) - it's not right because i iterates on windows edges. I think that you suppose to use if(j).
supposed that for(size_t i=0 ; i<clippingRegionLines.size() ; i++) should transform (generate new) vertexList (initially vertexList==allPolygonVertexes) and use it in next step. (so after all iterations of this loop you will get clipped polygon). So my advice here: represent input polygon as list of vertexes instead of list of edges.
Also you need to be sure that std::vector<Line2d> clippingRegionLines = rectangle.GetLines(); returns segments oriented counter-clockwise.