gpt4 book ai didi

javascript - 如何实现二维几何的约束求解器?

转载 作者:数据小太阳 更新时间:2023-10-29 03:54:51 26 4
gpt4 key购买 nike

我有一组金属滑动件,它们按以下方式约束到x和y轴:

sliding pieces

我需要最大化受同一滑块约束的所有零件之间的水平距离,以及滑块与滑块本身之间的垂直距离。如何解决呢?

任何可以解决该问题的建议都将不胜感激。

我首先看了一些非常强大的库,例如cassowary和jsLPSolver,但是我在理解核心算法以及如何检查约束的可行性以及如何对可能的解决方案进行排名时遇到了一些麻烦。

如何在JavaScript中为二维几何约束求解器实现一个(简单)存根,以解决上述问题?

编辑:

我有以下输入数据:

maxW = 300, maxH = 320

各个部分的定义如下(不是强制性的,每个解决方案都可以接受):
slidingPiece = [pX, pY, width, height, anchorPoint, loopDistance];

我将尝试解释“最大化”下的含义。

水平间距:

a0-b1,b1-b2,b2-b4,b4-b5和b5-maxX将是相同的,即最大X除以最大垂直相交件数+ 1(5)。
然后,b1-b3和b3-b5将由可用的剩余空间确定。

垂直间距:

b1-a3,a3-a4和a0-b5相同。理想情况下,a0-b3,b3-b4,a2-b2,b4-a3和b2-a4也是相同的值。最大化a1-b4和b3-a2与最大化b3-b4相同。同样适用于a2-b2和b4-a3:距离b2-b4将是最大负值。

因此,我需要最大程度地延长每个滑动块与他在Y约束之上或之下的最近距离。

此问题的二维几何表示表明,水平间距取决于 anchor 的垂直距离(由于 anchor 定件的垂直交点),而距离又取决于件本身的水平位置。例如,想想b2在上面短一些。在这种情况下,b1和b2不再相交并且将变为相同的x值,即max X除以4。

在其他一些情况下,例如b2在上面的部分中更长-会越过 anchor a2,则它应与a1隔开。这是因为会有一组解决方案,一些可行,而另一些则没有,例如,将破坏全局最大Y约束。

最佳答案

我会尝试类似于this的现场方法。

  • 每个滑块将收回所有滑块

    力按距离^ 2缩放,就像它们都将在彼此之间具有相同极性的电荷或 Spring 一样。
  • 最重要的是添加摩擦力,它由速度缩放

    空气v^2或液体v^3
  • 并不重要
  • 实现运动学约束

    对于仅水平滑动和垂直滑动,应该非常容易。
  • 做物理模拟,等到收敛到稳定状态v=~0

    如果按本地最小/最大值,请摇动整个对象,或随机安排整个对象,然后重试。您也可以这样做以获得其他解决方案。

  • [Edit4] C++求解器示例
  • 表示滑块系统的结构/类

    为了简化以后的代码,我将不支持闭环或双 anchor 。这就是为什么i1滑块(最右边)没有固定到任何东西(只会提供力场)的原因。
    我最终得到了以下滑块定义:

    slider def

    查看class _slider的源以获取更多信息。
  • 渲染

    短划线表示固定的滑块。银色表示水平,浅绿色表示垂直,鼠标选择黄色。可能稍后会亮起红色表示某种错误/卡住或某种出于调试目的的东西。对于力场求解器,我有时将场强添加为红蓝色标度,但不确定是否在此实现。

    为简单起见,我将不实现缩放/平移功能,因为您的尺寸便于直接渲染而无需变换。

    initial positions
  • 实现初始设置

    sliders sys;
    int i0,i1,a0,a1,a2,a3,a4,b1,b2,b3,b4,b5;
    sys.slider_beg();//ia,ib, x, y, a0, a1, b0, b1,_horizontal
    i0=sys.slider_add(-1,-1, 25.0, 25.0, -5.0, 405.0, 0.0, 0.0, 0);
    a0=sys.slider_add(i0,-1, 0.0, 0.0, 0.0, 400.0, 0.0, 0.0, 1);
    a1=sys.slider_add(i0,-1, 0.0,100.0, 0.0, 400.0, 0.0, 0.0, 1);
    a2=sys.slider_add(i0,-1, 0.0,200.0, 0.0, 400.0, 0.0, 0.0, 1);
    a3=sys.slider_add(i0,-1, 0.0,300.0, 0.0, 400.0, 0.0, 0.0, 1);
    a4=sys.slider_add(i0,-1, 0.0,400.0, 0.0, 400.0, 0.0, 0.0, 1);
    b1=sys.slider_add(a0,a2, 20.0, 0.0, 0.0, 125.0, 125.0, 250.0, 0);
    b2=sys.slider_add(a3,-1, 40.0, 0.0, -70.0, 30.0, 0.0, 0.0, 0);
    b3=sys.slider_add(a1,-1, 60.0, 0.0, -70.0, 30.0, 0.0, 0.0, 0);
    b4=sys.slider_add(a2,-1, 80.0, 0.0, -30.0, 70.0, 0.0, 0.0, 0);
    b5=sys.slider_add(a3,a1,100.0, 0.0,-125.0, 0.0,-125.0,-250.0, 0);
    i1=sys.slider_add(-1,-1,425.0, 25.0, -5.0, 405.0, 0.0, 0.0, 0);
    sys.slider_end();

    其中ia是父级索引​​,而ib是子级索引(滑块类本身将ib保留为父级索引,但是由于您需要链接到尚不存在的项,因此初始化时会感到困惑,因此ib转换在sys.add函数中进行处理) 。 sys是保存整个内容的类,sys.add只是向其中添加新的滑块,并返回从零开始计数的索引。 x,y是相对于父级的相对位置。

    为了减轻编码量,此设置不得与约束冲突。此设置的概述在先前的项目符号中。

    请注意,垂直滑块的顺序必须从左到右,水平滑块的顺序从上到下,以确保正确的约束功能。
  • 鼠标交互

    只需简单的滑块移动即可调试和调整初始设置值。和或处理卡住的情况。您需要处理鼠标事件,如果尚未编辑,请选择最近的滑块。如果按下鼠标按钮,将选定的滑块移动到鼠标位置...
  • body 约束/互动

    我对此进行了简化,因此我刚刚创建了一个谓词函数,该谓词函数用于指定的滑块,并且如果它或它的任何子代/ anchor 定与定义的约束冲突,它将返回。这样就更容易编码和调试,然后更新位置以匹配实际约束。

    用法则是更多代码。首先存储更新滑块的实际位置。然后将滑块更新到新位置/状态。此后,如果不满足约束条件,则停止实际的滑块速度并恢复其原始位置。

    这会慢一些,但我懒得编写完整的约束更新程序(该代码可能会变得非常复杂...)。

    我认识到2个平行和垂直的互动。平行是直接的。但是,垂直线是滑块边缘与其附近的垂直滑块之间的相互作用,不包括在初始状态下已经相交的滑块(a,b anchor 定或刚好相交)。因此,我在开始时创建了一个相交滑块(ic)的列表,该交互将被忽略。
  • 物理模拟

    简单的Newton - D'Alembert physics for non relativistic speeds就可以了。只需在每次迭代中将加速度ax,ay设置为磁场强度和摩擦即可。
  • 字段求解器

    这是一组规则/等式,用于设置每个滑块的仿真加速度以收敛到解。最后我得到了静电回缩力F = -Q/r^2和线性衰减速度。还实现了绝对速度和加速度限制器,以避免数值问题。

    为了增加求解时间和稳定性,我添加了精确的控制模式,当滑块的总最大速度降低时,电荷会降低。

  • 这里是完整的 C++ / VCL 类代码:

    //---------------------------------------------------------------------------
    //--- Sliders solver ver: 1.01 ----------------------------------------------
    //---------------------------------------------------------------------------
    #ifndef _sliders_h
    #define _sliders_h
    //---------------------------------------------------------------------------
    #include <math.h>
    #include "list.h" // linear dynamic array template List<T> similar to std::vector
    //---------------------------------------------------------------------------
    const double _slider_w = 3.00; // [px] slider half width (for rendering)
    const double _slider_gap = 4.00; // [px] min gap between sliders (for colisions)
    const double _acc_limit= 100.00; // [px/s^2]
    const double _vel_limit= 100.00; // [px/s]
    const double _friction = 0.90; // [-]
    const double _charge =250000.00; // [px^3/s^2]
    //---------------------------------------------------------------------------
    class _slider // one slider (helper class)
    {
    public:
    // properties
    double x,y; // actual relative pos
    bool _horizontal; // orientation
    double a0,a1; // slider vertexes 0 is anchor point
    double b0,b1; // anchor zone for another slider
    int ia; // -1 for fixed or index of parrent slider
    int ib; // -1 or index of parrent slider
    // computed
    List<int> ic; // list of slider indexes to ignore for perpendicular constraints
    double a,b; // force field affected part
    double X,Y; // actual absolute position
    double vx,vy,ax,ay; // actual relative vel,acc
    // temp
    int flag; // temp flag for simulation
    double x0,x1; // temp variables for solver
    // constructors (can ignore this)
    _slider() {}
    _slider(_slider& a) { *this=a; }
    ~_slider() {}
    _slider* operator = (const _slider *a) { *this=*a; return this; }
    //_slider* operator = (const _slider &a) { ...copy... return this; }
    };
    //---------------------------------------------------------------------------
    class sliders // whole slider system main class
    {
    public:
    List<_slider> slider; // list of sliders

    double vel_max; // max abs velocity of sliders for solver precision control
    double charge; // actual charge of sliders for solve()
    int mode; // actual solution precision control mode

    // constructors (can ignore this)
    sliders();
    sliders(sliders& a) { *this=a; }
    ~sliders() {}
    sliders* operator = (const sliders *a) { *this=*a; return this; }
    //sliders* operator = (const sliders &a) { ...copy... return this; }

    // VCL window API variables (can ignore this)
    double mx0,my0,mx1,my1; // last and actual mouse position
    TShiftState sh0,sh1; // last and actual mouse buttons and control keys state
    int sel;

    // API (this is important stuff)
    void slider_beg(){ slider.num=0; } // clear slider list
    int slider_add(int ia,int ib,double x,double y,double a0,double a1,double b0,double b1,bool _h); // add slider to list
    void slider_end(); // compute slider parameters
    bool constraints(int ix); // return true if constraints hit
    void positions(); // recompute absolute positions
    void update(double dt); // update physics simulation with time step dt [sec]
    void solve(bool _init=false); // set sliders accelerations to solve this
    void stop(); // stop all movements
    // VCL window API for interaction with GUI (can ignore this)
    void mouse(int x,int y,TShiftState sh);
    void draw(TCanvas *scr);
    };
    //---------------------------------------------------------------------------
    sliders::sliders()
    {
    mx0=0.0; my0=0.0;
    mx1=0.0; my1=0.0;
    sel=-1;
    }
    //---------------------------------------------------------------------------
    int sliders::slider_add(int ia,int ib,double x,double y,double a0,double a1,double b0,double b1,bool _h)
    {
    _slider s; double q;
    if (a0>a1) { q=a0; a0=a1; a1=q; }
    if (b0>b1) { q=b0; b0=b1; b1=q; }
    s.x=x; s.vx=0.0; s.ax=0.0;
    s.y=y; s.vy=0.0; s.ay=0.0;
    s.ia=ia; s.a0=a0; s.a1=a1;
    s.ib=-1; s.b0=b0; s.b1=b1;
    s.ic.num=0;
    if ((ib>=0)&&(ib<slider.num)) slider[ib].ib=slider.num;
    s._horizontal=_h;
    s.a=a0; // min
    if (s.a>a1) s.a=a1;
    if (s.a>b0) s.a=b0;
    if (s.a>b1) s.a=b1;
    s.b=a0; // max
    if (s.b<a1) s.b=a1;
    if (s.b<b0) s.b=b0;
    if (s.b<b1) s.b=b1;
    slider.add(s);
    return slider.num-1;
    }
    //---------------------------------------------------------------------------
    void sliders::slider_end()
    {
    int i,j;
    double a0,a1,b0,b1,x0,x1,w=_slider_gap;
    _slider *si,*sj;
    positions();
    // detect intersecting sliders and add them to propriet ic ignore list
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    for (sj=si+1 ,j=i+1;j<slider.num;j++,sj++)
    if (si->_horizontal!=sj->_horizontal)
    {
    if (si->_horizontal)
    {
    a0=si->X+si->a; a1=sj->X-w;
    b0=si->X+si->b; b1=sj->X+w;
    x0=si->Y; x1=sj->Y;
    }
    else{
    a0=si->Y+si->a; a1=sj->Y-w;
    b0=si->Y+si->b; b1=sj->Y+w;
    x0=si->X; x1=sj->X;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    if ((x0>x1+sj->a-w)&&(x0<x1+sj->b+w))
    {
    si->ic.add(j);
    sj->ic.add(i);
    }
    }
    }
    //---------------------------------------------------------------------------
    bool sliders::constraints(int ix)
    {
    int i,j;
    double a0,a1,b0,b1,x0,x1,x,w=_slider_gap;
    _slider *si,*sj,*sa,*sb,*s;
    s=slider.dat+ix;
    // check parallel neighbors overlapp
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    if ((i!=ix)&&(si->_horizontal==s->_horizontal))
    {
    if (s->_horizontal)
    {
    a0=s->X+s->a; a1=si->X+si->a;
    b0=s->X+s->b; b1=si->X+si->b;
    x0=s->Y; x1=si->Y;
    }
    else{
    a0=s->Y+s->a; a1=si->Y+si->a;
    b0=s->Y+s->b; b1=si->Y+si->b;
    x0=s->X; x1=si->X;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    {
    if ((i<ix)&&(x0<x1+w)) return true;
    if ((i>ix)&&(x0>x1-w)) return true;
    }
    }
    // check perpendicular neighbors overlapp
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    if ((i!=ix)&&(si->_horizontal!=s->_horizontal))
    {
    // skip ignored sliders for this
    for (j=0;j<s->ic.num;j++)
    if (s->ic[j]==i) { j=-1; break; }
    if (j<0) continue;
    if (s->_horizontal)
    {
    a0=s->X+s->a; a1=si->X-w;
    b0=s->X+s->b; b1=si->X+w;
    x0=s->Y; x1=si->Y;
    }
    else{
    a0=s->Y+s->a; a1=si->Y-w;
    b0=s->Y+s->b; b1=si->Y+w;
    x0=s->X; x1=si->X;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    if ((x0>x1+si->a-w)&&(x0<x1+si->b+w))
    return true;
    }
    // conflict a anchor area of parent?
    if (s->ia>=0)
    {
    si=slider.dat+s->ia;
    if (s->_horizontal)
    {
    x0=si->Y+si->a0;
    x1=si->Y+si->a1;
    x=s->Y;
    }
    else{
    x0=si->X+si->a0;
    x1=si->X+si->a1;
    x=s->X;
    }
    if (x<x0+w) return true;
    if (x>x1-w) return true;
    }
    // conflict b anchor area of parent?
    if (s->ib>=0)
    {
    si=slider.dat+s->ib;
    if (si->_horizontal)
    {
    x0=si->X+si->b0;
    x1=si->X+si->b1;
    x=s->X;
    }
    else{
    x0=si->Y+si->b0;
    x1=si->Y+si->b1;
    x=s->Y;
    }
    if (x<x0+w) return true;
    if (x>x1-w) return true;
    }
    // conflict b anchor area with childs?
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    if ((i!=ix)&&(si->ib==ix))
    {
    if (s->_horizontal)
    {
    x0=s->X+s->b0;
    x1=s->X+s->b1;
    x=si->X;
    }
    else{
    x0=s->Y+s->b0;
    x1=s->Y+s->b1;
    x=si->Y;
    }
    if (x<x0+w) return true;
    if (x>x1-w) return true;
    }

    // check childs too
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    if ((i!=ix)&&(si->ia==ix))
    if (constraints(i)) return true;
    return false;
    }
    //---------------------------------------------------------------------------
    void sliders::positions()
    {
    int i,e;
    _slider *si,*sa;
    // set flag = uncomputed
    for (si=slider.dat,i=0;i<slider.num;i++,si++) si->flag=0;
    // iterate until all sliders are computed
    for (e=1;e;)
    for (e=0,si=slider.dat,i=0;i<slider.num;i++,si++)
    if (!si->flag)
    {
    // fixed
    if (si->ia<0)
    {
    si->X=si->x;
    si->Y=si->y;
    si->flag=1;
    continue;
    }
    // a anchored
    sa=slider.dat+si->ia;
    if (sa->flag)
    {
    si->X=sa->X+si->x;
    si->Y=sa->Y+si->y;
    si->flag=1;
    continue;
    }
    e=1; // not finished yet
    }
    }
    //---------------------------------------------------------------------------
    void sliders::update(double dt)
    {
    int i;
    _slider *si,*sa;
    double x,X;
    // D'Lamnbert integration
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    if (si->_horizontal)
    {
    x=si->y; si->vy+=si->ay*dt; // vel = Integral(acc*dt)
    si->vy*=_friction; // friction k*vel
    X=si->Y; si->y +=si->vy*dt; // pos = Integral(vel*dt)
    positions(); // recompute childs
    if ((si->ia<0)||(constraints(i))) // if fixed or constraint hit (stop and restore original position)
    {
    si->vy=0.0;
    si->y =x;
    si->Y =X;
    positions(); // recompute childs
    }
    }
    else{
    x=si->x; si->vx+=si->ax*dt; // vel = Integral(acc*dt)
    si->vx*=_friction; // friction k*vel
    X=si->X; si->x +=si->vx*dt; // pos = Integral(vel*dt)
    positions(); // recompute childs
    if ((si->ia<0)||(constraints(i))) // if fixed or constraint hit (stop and restore original position)
    {
    si->vx=0.0;
    si->x =x;
    si->X =X;
    positions(); // recompute childs
    }
    }
    }
    //---------------------------------------------------------------------------
    void sliders::solve(bool _init)
    {
    int i,j,k;
    double a0,a1,b0,b1,x0,x1;
    _slider *si,*sj,*sa;
    // init solution
    if (_init)
    {
    mode=0;
    charge=_charge;
    }
    // clear accelerations and compute actual max velocity
    vel_max=0.0;
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    {
    si->ax=0.0;
    si->ay=0.0;
    x0=fabs(si->vx); if (vel_max<x0) vel_max=x0;
    x0=fabs(si->vy); if (vel_max<x0) vel_max=x0;
    }
    // precision control of solver
    if ((mode==0)&&(vel_max>25.0)) { mode++; } // wait until speed raises
    if ((mode==1)&&(vel_max<10.0)) { mode++; charge*=0.10; } // scale down forces to lower jitter
    if ((mode==2)&&(vel_max< 1.0)) { mode++; charge*=0.10; } // scale down forces to lower jitter
    if ((mode==3)&&(vel_max< 0.1)) { mode++; charge =0.00; stop(); } // solution found
    // set x0 as 1D vector to closest parallel neighbor before and x1 after
    for (si=slider.dat,i=0;i<slider.num;i++,si++) { si->x0=0.0; si->x1=0.0; }
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    for (sj=si+1 ,j=i+1;j<slider.num;j++,sj++)
    if (si->_horizontal==sj->_horizontal)
    {
    // longer side interaction
    if (si->_horizontal)
    {
    a0=si->X+si->a; a1=sj->X+sj->a;
    b0=si->X+si->b; b1=sj->X+sj->b;
    x0=si->Y; x1=sj->Y;
    }
    else{
    a0=si->Y+si->a; a1=sj->Y+sj->a;
    b0=si->Y+si->b; b1=sj->Y+sj->b;
    x0=si->X; x1=sj->X;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    {
    x0=x1-x0;
    if ((si->ia>=0)&&(x0<0.0)&&((fabs(si->x0)<_slider_gap)||(fabs(si->x0)>fabs(x0)))) si->x0=-x0;
    if ((si->ia>=0)&&(x0>0.0)&&((fabs(si->x1)<_slider_gap)||(fabs(si->x1)>fabs(x0)))) si->x1=-x0;
    if ((sj->ia>=0)&&(x0<0.0)&&((fabs(sj->x0)<_slider_gap)||(fabs(sj->x0)>fabs(x0)))) sj->x0=+x0;
    if ((sj->ia>=0)&&(x0>0.0)&&((fabs(sj->x1)<_slider_gap)||(fabs(sj->x1)>fabs(x0)))) sj->x1=+x0;
    }
    // shorter side interaction
    if (si->_horizontal)
    {
    a0=si->Y-_slider_gap; a1=sj->Y+_slider_gap;
    b0=si->Y+_slider_gap; b1=sj->Y+_slider_gap;
    x0=si->X; x1=sj->X;
    }
    else{
    a0=si->X-_slider_gap; a1=sj->X+_slider_gap;
    b0=si->X+_slider_gap; b1=sj->X+_slider_gap;
    x0=si->Y; x1=sj->Y;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    {
    if (x0<x1) { x0+=si->b; x1+=sj->a; }
    else { x0+=si->a; x1+=sj->b; }
    x0=x1-x0;
    if (si->ia>=0)
    {
    sa=slider.dat+si->ia;
    if ((sa->ia>=0)&&(x0<0.0)&&((fabs(sa->x0)<_slider_gap)||(fabs(sa->x0)>fabs(x0)))) sa->x0=-x0;
    if ((sa->ia>=0)&&(x0>0.0)&&((fabs(sa->x1)<_slider_gap)||(fabs(sa->x1)>fabs(x0)))) sa->x1=-x0;
    }
    if (sj->ia>=0)
    {
    sa=slider.dat+sj->ia;
    if ((sa->ia>=0)&&(x0<0.0)&&((fabs(sa->x0)<_slider_gap)||(fabs(sa->x0)>fabs(x0)))) sa->x0=+x0;
    if ((sa->ia>=0)&&(x0>0.0)&&((fabs(sa->x1)<_slider_gap)||(fabs(sa->x1)>fabs(x0)))) sa->x1=+x0;
    }
    }
    }
    // set x0 as 1D vector to closest perpendicular neighbor before and x1 after
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    for (sj=si+1 ,j=i+1;j<slider.num;j++,sj++)
    if (si->_horizontal!=sj->_horizontal)
    {
    // skip ignored sliders for this
    for (k=0;k<si->ic.num;k++)
    if (si->ic[k]==j) { k=-1; break; }
    if (k<0) continue;
    if (si->_horizontal)
    {
    a0=si->X+si->a; a1=sj->X-_slider_w;
    b0=si->X+si->b; b1=sj->X+_slider_w;
    x0=si->Y;
    }
    else{
    a0=si->Y+si->a; a1=sj->Y-_slider_w;
    b0=si->Y+si->b; b1=sj->Y+_slider_w;
    x0=si->X;
    }
    if (((a0<=b1)&&(b0>=a1))||((a1<=b0)&&(b1>=a0)))
    {
    if (si->_horizontal)
    {
    a1=sj->Y+sj->a;
    b1=sj->Y+sj->b;
    }
    else{
    a1=sj->X+sj->a;
    b1=sj->X+sj->b;
    }
    a1-=x0; b1-=x0;
    if (fabs(a1)<fabs(b1)) x0=-a1; else x0=-b1;
    if ((si->ia>=0)&&(x0<0.0)&&((fabs(si->x0)<_slider_gap)||(fabs(si->x0)>fabs(x0)))) si->x0=+x0;
    if ((si->ia>=0)&&(x0>0.0)&&((fabs(si->x1)<_slider_gap)||(fabs(si->x1)>fabs(x0)))) si->x1=+x0;
    if (sj->ia<0) continue;
    sa=slider.dat+sj->ia;
    if ((sa->ia>=0)&&(x0<0.0)&&((fabs(sa->x0)<_slider_gap)||(fabs(sa->x0)>fabs(x0)))) sa->x0=-x0;
    if ((sa->ia>=0)&&(x0>0.0)&&((fabs(sa->x1)<_slider_gap)||(fabs(sa->x1)>fabs(x0)))) sa->x1=-x0;
    }
    }
    // convert x0,x1 distances to acceleration
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    {
    // driving force F = ~ Q / r^2
    if (fabs(si->x0)>1e-10) x0=charge/(si->x0*si->x0); else x0=0.0; if (si->x0<0.0) x0=-x0;
    if (fabs(si->x1)>1e-10) x1=charge/(si->x1*si->x1); else x1=0.0; if (si->x1<0.0) x1=-x1;
    a0=x0+x1;
    // limit acc
    if (a0<-_acc_limit) a0=-_acc_limit;
    if (a0>+_acc_limit) a0=+_acc_limit;
    // store parallel acc to correct axis
    if (si->_horizontal) si->ay=a0;
    else si->ax=a0;
    // limit vel (+/- one iteration overlap)
    if (si->_horizontal) x0=si->vy;
    else x0=si->vx;
    if (x0<-_vel_limit) x0=-_vel_limit;
    if (x0>+_vel_limit) x0=+_vel_limit;
    if (si->_horizontal) si->vy=x0;
    else si->vx=x0;
    }
    }
    //---------------------------------------------------------------------------
    void sliders::stop()
    {
    int i;
    _slider *si;
    for (si=slider.dat,i=0;i<slider.num;i++,si++)
    {
    si->vx=0.0;
    si->vy=0.0;
    si->ax=0.0;
    si->ay=0.0;
    }
    }
    //---------------------------------------------------------------------------
    void sliders::mouse(int x,int y,TShiftState sh)
    {
    int i,q0,q1;
    double d,dd;
    _slider *si;
    // update mouse state
    mx0=mx1; my0=my1; sh0=sh1;
    mx1=x; my1=y; sh1=sh;
    // slider movement with left mouse button
    q0=sh0.Contains(ssLeft);
    q1=sh1.Contains(ssLeft);
    if ((sel>=0)&&(q1))
    {
    si=slider.dat+sel;
    // stop simulation for selected slider
    si->vx=0.0;
    si->vy=0.0;
    si->ax=0.0;
    si->ay=0.0;
    // use mouse position instead
    if (si->ia>=0)
    {
    if (si->_horizontal){ d=si->y; dd=si->Y; si->y+=my1-si->Y; si->Y=my1; si->vy=0.0; si->ay=0.0; positions(); if (constraints(sel)) { si->y=d; si->Y=dd; positions(); }}
    else { d=si->x; dd=si->X; si->x+=mx1-si->X; si->X=mx1; si->vx=0.0; si->ax=0.0; positions(); if (constraints(sel)) { si->x=d; si->X=dd; positions(); }}
    }
    }
    // select slider (if not left mouse button used)
    if (!q1)
    for (sel=-1,d=_slider_w+1.0,si=slider.dat,i=0;i<slider.num;i++,si++)
    {
    dd=_slider_w+1.0;
    if (si->_horizontal){ if ((mx1>=si->X+si->a)&&(mx1<=si->X+si->b)) dd=fabs(my1-si->Y); }
    else { if ((my1>=si->Y+si->a)&&(my1<=si->Y+si->b)) dd=fabs(mx1-si->X); }
    if ((dd<d)&&(dd<=_slider_w)) { sel=i; d=dd; }
    }
    }
    //---------------------------------------------------------------------------
    void sliders::draw(TCanvas *scr)
    {
    int i,j,n;
    double w=_slider_w,r,x,y,a0,a1;
    AnsiString txt;
    _slider *s;
    scr->Brush->Style=bsClear;
    #define _line(aa,bb) \
    if (s->_horizontal) \
    { \
    scr->MoveTo(s->X+aa,s->Y); \
    scr->LineTo(s->X+bb,s->Y); \
    } \
    else{ \
    scr->MoveTo(s->X,s->Y+aa); \
    scr->LineTo(s->X,s->Y+bb); \
    }
    scr->Pen->Color=clSilver;
    scr->Font->Color=clWhite;
    scr->TextOutA(40,40,AnsiString().sprintf("mode %i",mode));
    scr->TextOutA(40,60,AnsiString().sprintf("vel: %.3lf [px/s]",vel_max));
    scr->TextOutA(40,80,AnsiString().sprintf(" Q: %.3lf [px^3/s^2]",charge));
    scr->Font->Color=clYellow;
    for (s=slider.dat,i=0;i<slider.num;i++,s++)
    {
    if (s->_horizontal) scr->Pen->Color=clSilver;
    else scr->Pen->Color=clAqua;
    if (i==sel)
    {
    scr->Pen->Color=clYellow;
    txt=AnsiString().sprintf(" ix:%i ia:%i ib:%i ic:",sel,s->ia,s->ib);
    for (j=0;j<=s->ic.num;j++) txt+=AnsiString().sprintf(" %i",s->ic[j]);
    scr->TextOutA(40,100,txt);
    scr->TextOutA(40,120,AnsiString().sprintf("pos: %.1lf %.1lf [px]",s->X,s->Y));
    scr->TextOutA(40,140,AnsiString().sprintf("vel: %.3lf %.3lf [px/s]",s->vx,s->vy));
    scr->TextOutA(40,160,AnsiString().sprintf("acc: %.3lf %.3lf [px/s^2]",s->ax,s->ay));
    scr->Pen->Color=clYellow;
    }
    if (s->ia<0) scr->Pen->Style=psDash;
    else scr->Pen->Style=psSolid;
    // a anchor loop
    x=s->X;
    y=s->Y;
    if (s->ia>=0) scr->Ellipse(x-w,y-w,x+w,y+w);
    // b anchor loop
    r=0.5*fabs(s->b1-s->b0);
    if (s->_horizontal)
    {
    x=s->X+0.5*(s->b0+s->b1);
    y=s->Y;
    scr->RoundRect(x-r,y-w,x+r,y+w,w,w);
    }
    else{
    x=s->X;
    y=s->Y+0.5*(s->b0+s->b1);
    scr->RoundRect(x-w,y-r,x+w,y+r,w,w);
    }
    // a line cutted by a anchor loop
    a0=s->a0; a1=s->a1;
    if ((s->ia>=0)&&(a0<=+w)&&(a1>=-w))
    {
    if (a0<-w) _line(s->a0,-w);
    if (a1>+w) _line( w,s->a1);
    }
    else _line(s->a0,s->a1);
    }
    scr->Font->Color=clDkGray;
    scr->Pen->Style=psSolid;
    scr->Brush->Style=bsSolid;
    #undef _line
    }
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------

    您可以忽略VCL内容,它只是与我的App窗口和呈现进行交互的API。求解器本身不需要任何东西。我使用了动态线性数组模板 List<T>,因此这里提供了一些解释:
  • List<double> xxx;double xxx[];相同
  • xxx.add(5);5添加到列表的末尾
  • xxx[7]访问数组元素(安全)
  • xxx.dat[7]访问数组元素(不安全但快速直接访问)
  • xxx.num是数组
  • 的实际使用大小
  • xxx.reset()清除数组并设置xxx.num=0
  • xxx.allocate(100)100项预分配空间

  • 从bullet #3 正确初始化后,用法很简单,如下所示:
    sys.solve(true);
    for (;;)
    {
    sys.solve();
    sys.update(0.040); // just time step
    if (sys.mode==4) break; // stop if solution found or stuck
    }

    我不是在循环中调用计时器,而是重新绘制窗口,以便看到动画:

    animation

    断断续续是由于不均匀的 GIF 抓取采样率(不规则地跳过模拟中的某些帧)。

    您可以使用 vel,acc限制,阻尼系数和模式控制 if的常数来更改行为。如果您还实现了鼠标处理程序,则可以使用鼠标左键移动滑块,这样就可以摆脱卡住的情况。

    这里是一个独立的Win32演示(与 BDS2006 C++ 一起编译)。
  • Demo单击大洋红色按钮下方的慢速下载,输入4个字母的字母数字代码以开始下载,无需注册。

  • 有关求解器力计算如何工作的更多信息,请参见相关/后续质量检查:
  • Force directed layout for constrained rectangular shapes
  • 关于javascript - 如何实现二维几何的约束求解器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40817359/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com