在 C++ 中向粒子添加力列表
Adding a list of forces to a particle in c++
我对 C++ 有点陌生,如果这是一个愚蠢的问题,我深表歉意。
我有一个表示粒子系统中粒子的结构。除了位置、速度和质量等标准内容外,我还想给它一个力列表,这样每个力都是一个函数,我将粒子传递给它,并基于粒子的当前状态(或不), 这个函数 returns 一个力向量。理想情况下,我会总结每个这样的力矢量的结果以获得净力,然后我可以用它来计算下一个刻度的粒子速度。
这就是我希望粒子看起来的样子
struct particle {
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
std::list<function> forces;
};
现在我的问题是:我可以在不实现通用力基础 class 的情况下执行此操作吗?它实现了计算力的函数?有没有一种方法可以指定具有相同调用签名的函数列表?
如果你能保证所有的函数都有相同的方法签名,那么你就可以使用模板化的function[cppreference.com] class。我修改了您的示例以说明如何使用它。
#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);
}
如果您要处理三维力,您可能需要 return 类型的更多信息,而不仅仅是双精度,但这应该是一个很好的起点。此外,如果您有兼容 C++11 的编译器,您可能还需要查找 lambda functions 以便可以在同一行中创建函数。
您可以使用 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 个粒子。如果您可以对所有粒子使用相同的力集合,那么将力从粒子中移出 class 会给您带来收益。例如,
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
// ...
}
}
如果你真的想使用每粒子力,你仍然可以使用我上面概述的方法,将具有相同力集合的粒子分组到不同的容器对象中。然后你就可以重复使用上面的所有东西,再加一个 class 就可以解决:
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...
}
}
我对 C++ 有点陌生,如果这是一个愚蠢的问题,我深表歉意。
我有一个表示粒子系统中粒子的结构。除了位置、速度和质量等标准内容外,我还想给它一个力列表,这样每个力都是一个函数,我将粒子传递给它,并基于粒子的当前状态(或不), 这个函数 returns 一个力向量。理想情况下,我会总结每个这样的力矢量的结果以获得净力,然后我可以用它来计算下一个刻度的粒子速度。
这就是我希望粒子看起来的样子
struct particle {
double mass;
// position
double x, y, z;
// velocity
double dx, dy, dz;
std::list<function> forces;
};
现在我的问题是:我可以在不实现通用力基础 class 的情况下执行此操作吗?它实现了计算力的函数?有没有一种方法可以指定具有相同调用签名的函数列表?
如果你能保证所有的函数都有相同的方法签名,那么你就可以使用模板化的function[cppreference.com] class。我修改了您的示例以说明如何使用它。
#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);
}
如果您要处理三维力,您可能需要 return 类型的更多信息,而不仅仅是双精度,但这应该是一个很好的起点。此外,如果您有兼容 C++11 的编译器,您可能还需要查找 lambda functions 以便可以在同一行中创建函数。
您可以使用 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 个粒子。如果您可以对所有粒子使用相同的力集合,那么将力从粒子中移出 class 会给您带来收益。例如,
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
// ...
}
}
如果你真的想使用每粒子力,你仍然可以使用我上面概述的方法,将具有相同力集合的粒子分组到不同的容器对象中。然后你就可以重复使用上面的所有东西,再加一个 class 就可以解决:
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...
}
}