首页 文章

在c中向粒子添加力列表

提问于
浏览
0

我对c有点新鲜,如果这是一个愚蠢的问题,那就很抱歉 .

我有一个表示粒子系统中的粒子的结构 . 与位置,速度和质量等标准物质一起,我想给它一个力列表,这样每个力都是一个函数,我将它传递给粒子,并根据粒子的当前状态(或不是),此函数返回一个力矢量 . 理想情况下,我会总结每个这样的力矢量的结果,以获得净力,然后我可以用它来计算下一个滴答的粒子的速度 .

这就是我希望我的粒子看起来像

struct particle {
    double mass;

    // position
    double x, y, z;

    // velocity
    double dx, dy, dz;


    std::list<function> forces;
};

现在我的问题是:我是否可以在不实现实现计算力的函数的通用强制基类的情况下执行此操作?有没有办法可以指定具有相同呼叫签名的功能列表?

2 回答

  • 0

    你可以使用std :: function . 像这样的东西:

    // define a type for the function
    typedef std::function< void(particle const&, float *fxfyfz) > forcer_function;
    std::vector< forcer_function > forces;
    

    现在,关于表现的一些话 . 由于这是你正在谈论的粒子,我假设你有相当多的粒子(例如至少几百个) . 所以我假设你有兴趣让这段代码运行速度适中 .

    因此,首先,由于缓存属性不正确,不建议使用std :: list作为容器 . 它会使你的代码慢得多 . 因此,使用std :: vector .

    其次,将力列表添加为粒子结构的成员是不常见的 . 你真的想要每粒子使用不同的力吗?通常,存在<5个力和> 100-1000个粒子 . 如果你可以对所有粒子使用相同的力集合,那么将力从粒子类中移出会给你带来收益 . 例如,

    struct particle
    {
        double mass;
        // position
        double x, y, z;
        // velocity
        double dx, dy, dz;
    };
    struct particle_container
    {
        std::vector< particle > particles;
        std::vector< forcer_function > forces;
    
        void update();
    };
    
    void particle_container::update()
    {
        for(particle &p : particles) {
            double rx, ry, rz;
            rx = ry = rz = 0.0;
            for(forcer_function fn : forces) {
                double f[3];
                fn(p, &f[0]);
                rx += f[0];
                ry += f[1];
                rz += f[2];
            }
            // integrate resulting force, etc
            // ...
        }
    }
    

    如果OTOH你真的想要使用每粒子力,你仍然可以使用我在上面概述的方法,通过将粒子与相同的力集合分组到不同的容器对象中 . 然后你可以重用上面的所有内容,再添加一个类就可以解决它:

    struct particle_groups
    {
        std::vector< particle_container > groups;
        void update();
    };
    void particle_groups::update()
    {
        for(auto &g : groups) {
            g.update();
        }
    };
    

    如果你真的,真的,不想要分组,那么至少要考虑是否有一种方法可以使用粒子成员归零非活动力 . 然后你仍然可以使用上面的方法 . 例如,像这样:

    struct particle
    {
        double mass;
        // position
        double x, y, z;
        // velocity
        double dx, dy, dz;
    
        // is gravity active? either 1.0 or 0.0
        double grav;
        // is player interaction active? either 1.0 or 0.0
        double player;
        // etc... for all possible forces
    };
    

    然后将粒子的重力成员乘以例如你的重力,并根据particle.grav的值是1.0还是0.0,有效地关闭或重新开启该粒子的重力 .

    最后,std :: function很慢 . 您可以混合使用上述两种方法并使用单一功能 . 像这样:

    struct particle
    {
        double mass;
        // position
        double x, y, z;
        // velocity
        double dx, dy, dz;
    };
    struct force_settings
    {
        double grav;
        double attractor;
        double player;
        //etc...
    };
    struct particle_container
    {
        // no need to keep pointers to functions
        force_settings forces;
        std::vector< particle > particles;
    
        void update();
        void compute_forces(particle const& p, double *rf) const
        {
            // zero resulting force
            rf[0] = rf[1] = rf[2] = 0.0;
            // compute gravity, (assume y axis)
            rf[1] += forces.grav * 9.8; // will be either 9.8 or 0.0
            // compute attractor
            double ax = p.x - attractor.x;
            double ay = p.y - attractor.y;
            double az = p.z - attractor.z;
            rf[0] += forces.attraction * ax*ax;
            rf[1] += forces.attraction * ay*ay;
            rf[2] += forces.attraction * az*az;
            // etc... more forces here
       }
    };
    
    void particle_container::update()
    {
        for(particle &p : particles) {
            double rf[3];
            compute_forces(p, &rf);
            // integrate, etc...
        }
    }
    
  • 0

    如果可以保证所有函数都具有相同的方法签名,则可以使用模板化的function [cppreference.com]类 . 我已修改您的示例以说明如何使用它 .

    #include <functional>
    #include <list>
    #include <cmath>
    
    using namespace std;
    
    struct particle 
    {
        double mass;
    
        // position
        double x, y, z;
    
        // velocity
        double dx, dy, dz;
    
        // A list of forces that take a particle and return a double
        // The space between the two > symbols is needed in pre-c++11 compilers.
        list<function<double(const particle&)> > forces;
    };
    
    // An example function to calculate the force due to gravity.
    double gravity(const particle& p)
    {
        return p.mass * -9.8;
    }
    
    // Making something up for air resistance
    double resistance(const particle& p)
    {
        return 0.1 * sqrt(p.dx * p.dx + p.dy * p.dy + p.dz * p.dz);
    }
    
    int main()
    {
        particle p;
        p.mass = 10;
        p.x = 0;
        p.y = 100;
        p.z = 0;
    
        p.dx = 0;
        p.dy = 0;
        p.dz = 0;
        p.forces.push_back(gravity);
        p.forces.push_back(resistance);
    }
    

    如果你可能想要返回类型中的更多信息而不仅仅是一个double,但这应该是一个很好的起点 . 此外,如果您有一个兼容c 11的编译器,您可能还需要查找lambda functions,以便可以在同一行中创建这些函数 .

相关问题