Sign in to follow this  
c4c0d3m0n

Implementation issue - how to store multiple forces

Recommended Posts

c4c0d3m0n    100
Hello all. I'm developing a new particle system. I have 2 classes, one is ParticleSystem and one is Particle. This is how far I've gotten:
///
/// psystem.h
///
///


#ifndef PSYSTEM_H
#define PSYSTEM_H

#include "coords.h"
#include <vector>
#include <SDL.h> // For SDL_Rect



class ParticleSystem;
class Particle;
struct BooleanForce;



/// ////////////// ///
/// ParticleSystem ///
/// ////////////// ///

class ParticleSystem
{
public:

    ParticleSystem( const SDL_Rect& universe );
    //~ParticleSystem();


    void addParticle( const Particle& ptcl );

    void addForce( const Vector* force );
    void removeForce( const Vector* force );
    void turnOffForce( const Vector* force );
    void turnOnForce( const Vector* force );

private:

    std::vector<Particle> system;
    SDL_Rect universe;
    std::vector<BooleanForce> forces;

};



/// //////// ///
/// Particle ///
/// //////// ///

class Particle
{
public:

    Particle( float x, float y, float z = 0.0, float weight = 1.0 );
    //~Particle();

    friend class ParticleSystem;


    void applyForce( const Vector& force, float dt );

private:

    Vector pos;
    Vector vel;

    float weight;

};



/// //////////// ///
/// BooleanForce ///
/// //////////// ///

struct BooleanForce
{
public:

    BooleanForce( Vector* force );

    Vector* force;
    bool active;

};



#endif // PSYSTEM_H



I'm having trouble figuring out a way to implement the forces in the ParticleSystem. I want to have a std::vector with BooleanForce's (so I can turn them on/off without removing them/reading them). I thought of making a vector full of pointers to BooleanForce's, and then give the user the ability to add, remove, switch on/off the forces by comparing a pointer to a force and then compare it to all existing pointers. Is this a good way to implement what I am trying to archieve? Is it even possible to compare wether or not two pointers point to the same address? /Edit: Here is the current source for the forces, hopefully it explains better what I am trying to archieve:
void ParticleSystem::addForce( const Vector* force )
{
    if( force != NULL )
      forces.push_back( BooleanForce( force ) );
}

void ParticleSystem::removeForce( const Vector* force )
{
    std::vector<BooleanForce>::iterator iter;

    for( iter = forces.begin(); iter != forces.end(); iter++ ) {
        if( iter->force == force ) {
            forces.erase( iter );
            iter--;
        }
    }
}

void ParticleSystem::turnOffForce( const Vector* force )
{
    std::vector<BooleanForce>::iterator iter;

    for( iter = forces.begin(); iter != forces.end(); iter++ ) {
        if( iter->force == force ) {
            iter->active = false;
        }
    }
}

void ParticleSystem::turnOnForce( const Vector* force )
{
    std::vector<BooleanForce>::iterator iter;

    for( iter = forces.begin(); iter != forces.end(); iter++ ) {
        if( iter->force == force ) {
            iter->active = true;
        }
    }
}

Share this post


Link to post
Share on other sites
jpetrie    13157
Do you want to be able to switch off a boolean force at some point in the code and have all particles using that force see the change (and thus, no longer accumulate that force)? If so, a vector of pointers (or some kind of smart pointer) is an option. Be aware of lifetime and ownership issues (that's why smart pointers are good), but iteration over all forces in the vector and comparison between them isn't necessary.

But if you don't want that referential behavior, a vector of BooleanForce objects (no pointer) is sufficient.

From you code, it looks like the force vector is wholly contained in the particle system and you'll never want to share a force with another system (this seems like the sane option to me anyway), so I'd suggest you don't need pointers at all here.

You should refer to your forces by some kind of identifier or index to enable/disable them more likely. Not their vector component.

Share this post


Link to post
Share on other sites
c4c0d3m0n    100
Quote:
Do you want to be able to switch off a boolean force at some point in the code and have all particles using that force see the change (and thus, no longer accumulate that force)? If so, a vector of pointers (or some kind of smart pointer) is an option. Be aware of lifetime and ownership issues (that's why smart pointers are good), but iteration over all forces in the vector and comparison between them isn't necessary.

I want to be able to turn off forces in a system without removing them, so I can turn them on later again. What are these things "lifetime and ownership" you talk of? I've never had any issues concerning them I don't think.

Quote:
But if you don't want that referential behavior, a vector of BooleanForce objects (no pointer) is sufficient.

Yes, I figured this out shortly after posting and edited my code. Thanks though

Quote:
From you code, it looks like the force vector is wholly contained in the particle system and you'll never want to share a force with another system (this seems like the sane option to me anyway), so I'd suggest you don't need pointers at all here.

The Particle class does not contain any vectors. Here is the code to the function you are talking about:
void Particle::applyForce( const Vector& force, float dt )
{
// F = m a
// a = F/m
// v = a t
// v = F t 1/m

vel += force * dt / weight;
}


Quote:
You should refer to your forces by some kind of identifier or index to enable/disable them more likely. Not their vector component.


Would I do this through a Linked List? I've never used them, only heard about them and read the Wikipedia article on them.

Share this post


Link to post
Share on other sites
jpetrie    13157
Quote:

I want to be able to turn off forces in a system without removing them, so I can turn them on later again. What are these things "lifetime and ownership" you talk of? I've never had any issues concerning them I don't think.

If you've ever used raw pointers, then:
(1) you've dealt with lifetime and ownership, or
(2) you have a memory leak or undefined behavior.

"Lifetime and ownership" refers to keeping track of who 'owns' a raw pointer and is responsible for its deletion. For example, if you had pointers to the forces used on a particle in each particle object, it would be an error for the particle destructor to delete those pointers -- any pointer shared between more than one particle would be deleted twice. The ownership of the pointers should be in something else, perhaps the particle system object.

Quote:

Would I do this through a Linked List? I've never used them, only heard about them and read the Wikipedia article on them.

I mean you should not use the vector (floating point x, y, z) aspect of the force object to test for equality. Your BooleanForce objects have a pointer to a vector in them, which you use to test for equality. This is not exactly desirable, for a number of reasons(*). It would be better if you gave your forces some kind of non-pointer ID (such as an int) and used that as the identity test. You have less unnecessary raw pointers floating around in your system as a result.

In your system, it appears the only way I can ever remove a force or disable a force, or look up a force, is if I already have that force. Because the lookup requires testing the force's 'force' Vector pointer against the pointer supplied to the function.

(*) In addition to the above, it implies a referential semantic when you probably only need a value semantic, and it does look like you have any kind of ownership management of the Vector pointer holding the force. Who allocates it? Who deletes it? This smacks of memory leak or undefined behavior waiting to happen, and if nothing else presents a clunky interface because the pointer does not appear to be necessary.

Share this post


Link to post
Share on other sites
c4c0d3m0n    100
The force vectors are owned by the user. He gives a pointer to a force vector in ParticleSystem::addForce(). The pointer is then given to a BooleanForce object. Whenever I do an action on a force (remove it, turn it off/on) the user needs to resupply the address of the force. Then, the function iterates through the vector and removes all forces with that address.

The problem with this implementation is that the user needs to keep his forces and manage them somehow, for if he removes the force from its address, he's going to run into segfaults/undefined behaviour.

Creating a private static int in ParticleSystem is a good idea for the ID. I guess I should return the id when using ParticleSystem::addForce().

How do I create a static int for my class? I tried, but I'm failing with a multiple definition error

class ParticleSystem
{
public:

ParticleSystem( const SDL_Rect& universe );
//~ParticleSystem();


void addParticle( const Particle& ptcl );

int addForce( const Vector& force );
void removeForce( int id );
void turnOffForce( int id );
void turnOnForce( int id );

private:

std::vector<Particle> system;
SDL_Rect universe;
std::vector<BooleanForce> forces;

static int max_force_id;

};

int ParticleSystem::max_force_id = 0;

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by c4c0d3m0n
The force vectors are owned by the user. He gives a pointer to a force vector in ParticleSystem::addForce(). The pointer is then given to a BooleanForce object. Whenever I do an action on a force (remove it, turn it off/on) the user needs to resupply the address of the force. Then, the function iterates through the vector and removes all forces with that address.

The problem with this implementation is that the user needs to keep his forces and manage them somehow, for if he removes the force from its address, he's going to run into segfaults/undefined behaviour.


That is exactly what jpetrie is talking about with "lifetime and ownership". However, this indirection does give the user the flexibility to change the direction of the force without talking to the particle system. Whether or not you want that is up to you. If you don't, then it probably makes more sense to let the BooleanForce objects make copies of the Vectors, and come up with a better way of referring to the forces.

Although I think it would be simpler to just store the force vectors, and instead of tracking which forces are "active" with flags, keep separate containers of "active" and "inactive" forces. This simplifies the code for removing a force, too.

These are my suggestions:


class ParticleSystem {
typedef int force_id;
force_id last_fid; // static-ness is actually probably undesirable here
std::map<force_id, Vector> forces[2]; // 0 = active set, 1 = inactive set
std::vector<Particle> system;
Vector total_force;
SDL_Rect universe;

void recalculate_force() {
total_force = Vector();
for (std::map<force_id, Vector>::iterator it = forces[0].begin(); it != forces[0].end(); ++it) {
total_force += it->second;
}
}

public:
ParticleSystem(const SDL_Rect& universe): universe(universe), last_id(0) {}

void addParticle(const Particle& p) {
system.push_back(p);
}

force_id addForce(const Vector& force) {
forces[0][last_fid] = force;
recalculate_force();
return last_fid++;
}

void removeForce(force_id which) {
forces[0].erase(which);
forces[1].erase(which);
recalculate_force();
}

void enableForce(force_id which, bool active) {
typedef std::map<force_id, Vector> map;
map to_remove_from = forces[active ? 1 : 0];
map::iterator to_remove = to_remove_from.find(which);
if (to_remove != to_remove_from.end()) {
forces[active ? 0 : 1][which] = to_remove->second;
to_remove_from.erase(to_remove);
}
recalculateForce();
}

void turnOnForce(force_id which) { enableForce(which, true); }
void turnOffForce(force_id which) { enableForce(which, false); }
};





Quote:

How do I create a static int for my class? I tried, but I'm failing with a multiple definition error


It's a defined bit of storage (as opposed to just a type), so it has to go into a source file.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this