我会尝试类似这个的领域方法。
每个滑块都会收回所有滑块
力度按距离的平方缩放,就像它们都具有相同极性的电荷或彼此之间连接着弹簧一样。
在此基础上添加随速度缩放的摩擦力
无论是空气中的v^2
还是液体中的v^3
都不重要。
实现运动学约束
对于仅水平和垂直滑动,这应该非常容易。
进行物理模拟,并等待其收敛到稳定状态v = ~ 0
如果碰到局部最小/最大,则将整个事物摇晃一下或随机排列整个事物并重试。您也可以这样做以获得另一个解决方案。
[Edit4] C++求解器示例
structures/classes to represent the slider system
To ease up later code I will not support closed loops or double anchoring. That is why the i1 slider (most right) is not anchored to anything (will just provide forcefield).
I ended up with this slider definition:
look at the source of class _slider
for more info.
render
Dash-dash means fixed slider. Silver ones are horizontal, aqua means vertical and yellow is selected by mouse. May be later on red will mean some kind of error/stuck or something for debug purposes. For force field solvers I sometimes add the field strength as red-blue scale but not sure if I will implement it here or not.
To keep this simple I will not implement zoom/pan functions as your dimensions are convenient for direct render without transforms.
![initial positions](https://istack.dev59.com/ePj5z.webp)
implement initial setup
sliders sys;
int i0,i1,a0,a1,a2,a3,a4,b1,b2,b3,b4,b5;
sys.slider_beg();
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();
Where ia
is parent index and ib
is child index (the slider class itself holds ib
as parent but that would be confusing to init as you would need to link to item that do not exist yet so the ib
transformation is handled in the sys.add
function). sys
is class holding the whole thing and sys.add
just add new slider to it and returns its index counting from zero. The x,y
is relative position to parent.
To ease up amount of coding this setup must not conflict the constraints. The overview of this setup is in previous bullet.
Beware the order of sliders must be left to right for vertical and top to bottom for horizontal sliders to ensure correct constraint functionality.
mouse interaction
just simple slider movement for debug and adjusting initial setup values. And or handling stuck cases. You need to handle mouse events, select closest slider if not editing already. And if mouse button is pressed move selected slider to mouse position...
physical constraint/interaction
I simplify this a bit so I just created a predicate function that is called for specified slider and it returns if it or any its child/anchor is in conflict with defined constraints. This is much more easy to code and debug then to update the position to match actual constraint.
Usage is then a bit more code. First store actual position for updated slider. Then update slider to new position/state. After that if constraints are not met stop actual slider speeds and restore its original position.
It will be a bit slower but I am too lazy to code the full constraint updater (that code could get really complex...).
I recognize 2 interactions parallel and perpendicular. The parallel is straight forward. But the perpendicular is interaction between edge of slider and perpendicular sliders near it not including the already intersecting sliders (a,b anchored or just crossing) during initial state. So I created a list of intersecting sliders (ic
) at start which will be ignored for this interaction.
physical simulation
Simple Newton - D'Alembert physics for non relativistic speeds will do. Just on each iteration set the accelerations ax,ay
to the field strength and frictions.
field solver
This is set of rules/equations to set simulation accelerations for each slider to converge to solution. I ended up with electrostatic retracting force F = -Q/r^2
and linear dampening of speed. Also have implemented absolute velocity and acceleration limiters to avoid numeric problems.
To boost solution time and stability I added precision control modes where the electric charge is lowering when overall max speed of sliders is decreasing.
这是完整的C++/VCL类代码:
#ifndef _sliders_h
#define _sliders_h
#include <math.h>
#include "list.h"
const double _slider_w = 3.00;
const double _slider_gap = 4.00;
const double _acc_limit= 100.00;
const double _vel_limit= 100.00;
const double _friction = 0.90;
const double _charge =250000.00;
class _slider
{
public:
double x,y;
bool _horizontal;
double a0,a1;
double b0,b1;
int ia;
int ib;
List<int> ic;
double a,b;
double X,Y;
double vx,vy,ax,ay;
int flag;
double x0,x1;
_slider() {}
_slider(_slider& a) { *this=a; }
~_slider() {}
_slider* operator = (const _slider *a) { *this=*a; return this; }
};
class sliders
{
public:
List<_slider> slider;
double vel_max;
double charge;
int mode;
sliders();
sliders(sliders& a) { *this=a; }
~sliders() {}
sliders* operator = (const sliders *a) { *this=*a; return this; }
double mx0,my0,mx1,my1;
TShiftState sh0,sh1;
int sel;
void slider_beg(){ slider.num=0; }
int slider_add(int ia,int ib,double x,double y,double a0,double a1,double b0,double b1,bool _h);
void slider_end();
bool constraints(int ix);
void positions();
void update(double dt);
void solve(bool _init=false);
void stop();
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;
if (s.a>a1) s.a=a1;
if (s.a>b0) s.a=b0;
if (s.a>b1) s.a=b1;
s.b=a0;
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();
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;
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;
}
}
for (si=slider.dat,i=0;i<slider.num;i++,si++)
if ((i!=ix)&&(si->_horizontal!=s->_horizontal))
{
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;
}
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;
}
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;
}
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;
}
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;
for (si=slider.dat,i=0;i<slider.num;i++,si++) si->flag=0;
for (e=1;e;)
for (e=0,si=slider.dat,i=0;i<slider.num;i++,si++)
if (!si->flag)
{
if (si->ia<0)
{
si->X=si->x;
si->Y=si->y;
si->flag=1;
continue;
}
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;
}
}
void sliders::update(double dt)
{
int i;
_slider *si,*sa;
double x,X;
for (si=slider.dat,i=0;i<slider.num;i++,si++)
if (si->_horizontal)
{
x=si->y; si->vy+=si->ay*dt;
si->vy*=_friction;
X=si->Y; si->y +=si->vy*dt;
positions();
if ((si->ia<0)||(constraints(i)))
{
si->vy=0.0;
si->y =x;
si->Y =X;
positions();
}
}
else{
x=si->x; si->vx+=si->ax*dt;
si->vx*=_friction;
X=si->X; si->x +=si->vx*dt;
positions();
if ((si->ia<0)||(constraints(i)))
{
si->vx=0.0;
si->x =x;
si->X =X;
positions();
}
}
}
void sliders::solve(bool _init)
{
int i,j,k;
double a0,a1,b0,b1,x0,x1;
_slider *si,*sj,*sa;
if (_init)
{
mode=0;
charge=_charge;
}
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;
}
if ((mode==0)&&(vel_max>25.0)) { mode++; }
if ((mode==1)&&(vel_max<10.0)) { mode++; charge*=0.10; }
if ((mode==2)&&(vel_max< 1.0)) { mode++; charge*=0.10; }
if ((mode==3)&&(vel_max< 0.1)) { mode++; charge =0.00; stop(); }
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)
{
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;
}
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;
}
}
}
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)
{
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;
}
}
for (si=slider.dat,i=0;i<slider.num;i++,si++)
{
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;
if (a0<-_acc_limit) a0=-_acc_limit;
if (a0>+_acc_limit) a0=+_acc_limit;
if (si->_horizontal) si->ay=a0;
else si->ax=a0;
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;
mx0=mx1; my0=my1; sh0=sh1;
mx1=x; my1=y; sh1=sh;
q0=sh0.Contains(ssLeft);
q1=sh1.Contains(ssLeft);
if ((sel>=0)&&(q1))
{
si=slider.dat+sel;
si->vx=0.0;
si->vy=0.0;
si->ax=0.0;
si->ay=0.0;
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(); }}
}
}
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;
x=s->X;
y=s->Y;
if (s->ia>=0) scr->Ellipse(x-w,y-w,x+w,y+w);
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);
}
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部分,它只是与我的应用程序窗口和渲染进行交互的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);
if (sys.mode==4) break;
}
我使用定时器调用此方法并重新绘制窗口,以便看到动画,而不是使用for循环:
![animation](https://istack.dev59.com/0ZNZh.gif)
卡顿是由于非统一的GIF采样率(不规则地跳过仿真中的一些帧)造成的。
您可以修改常量vel,acc
极限、阻尼系数和模式控制if
来改变行为。如果还实现了鼠标处理程序,那么您可以用左键移动滑块,这样就可以摆脱一些困境...
这里有一个独立的Win32演示(使用BDS2006 C++编译)。
- 演示 点击大洋红色按钮下面的“慢速下载”,输入4位字母数字代码开始下载,无需注册。
关于求解器力计算的更多信息,请参见相关/后续QA: