javascript - ¿Cómo implementar un solucionador de restricciones para la geometría 2D?
algorithm geometry (1)
Tengo un conjunto de piezas metálicas deslizantes que están limitadas a los ejes xey de la siguiente manera:
Necesitaría maximizar la distancia horizontal entre todas las piezas restringidas por el mismo control deslizante y la distancia vertical entre las piezas deslizantes y los controles deslizantes en sí. ¿Cómo se puede resolver esto?
Cualquier consejo y sugerencia que pueda conducir a una solución para este problema sería muy apreciado
Primero examiné algunas bibliotecas muy poderosas como cassowary y jsLPSolver, pero tuve algunos problemas para comprender el algoritmo central y cómo se verifica la viabilidad de la restricción y cómo se clasifican las posibles soluciones.
¿Cómo podría implementarse en JavaScript un trozo (simple) para un solucionador de restricciones geométricas 2D para problemas como este anterior?
EDITAR:
Tengo los siguientes datos de entrada:
maxW = 300, maxH = 320
Las piezas se definen de la siguiente manera (no obligatorio, se aceptan todas las soluciones):
slidingPiece = [pX, pY, width, height, anchorPoint, loopDistance];
Trataré de explicar lo que quiero decir en "maximizar".
Espaciado horizontal:
a0-b1, b1-b2, b2-b4, b4-b5 y b5-maxX serán iguales, es decir, max X dividido por el mayor número de piezas de intersección vertical + 1 (5). b1-b3 y b3-b5 serán determinados por el espacio restante disponible.
Espaciado vertical:
b1-a3, a3-a4 y a0-b5 serán lo mismo. Idealmente, a0-b3, b3-b4, a2-b2, b4-a3 y b2-a4 también serán el mismo valor. Maximizar a1-b4 y b3-a2 es lo mismo que maximizar b3-b4. Lo mismo se aplica a a2-b2 y b4-a3: la distancia b2-b4 será entonces el valor negativo máximo.
Por lo tanto, necesito maximizar la distancia entre cada pieza deslizante y su restricción Y por encima o por debajo.
La representación geométrica bidimensional de este problema muestra que el espacio horizontal depende de la distancia vertical de los anclajes (debido a la intersección vertical de las piezas ancladas), que a su vez depende de la posición horizontal de las piezas en sí. Piense por ejemplo, b2 es algo más corto arriba. En este caso, b1 y b2 ya no se cruzan y se convertirían en el mismo valor de x, es decir, max X dividido por 4.
En algunos otros casos, por ejemplo, b2 es mucho más largo en la parte anterior y cruzará el ancla a2, entonces estará espaciado a a1. Esta es la razón porque habrá un conjunto de soluciones, algunas factibles y otras no, porque, por ejemplo, la restricción global máxima Y se rompería.
Intentaría un enfoque de campo similar a this .
-
Cada control deslizante retraerá todos los controles deslizantes
con una fuerza escalada por la distancia ^ 2, como todos ellos tendrían la misma carga eléctrica de polaridad o resortes unidos entre sí.
-
Además de eso, agregue fricción escalada por la velocidad
realmente no importa si el aire
v^2
o el líquidov^3
-
implementar restricciones cinemáticas
para el deslizamiento horizontal y vertical solo debería ser realmente fácil.
-
Haga simulación física y espere hasta que converja al estado estable
v=~0
si golpeas min / max local, agita todo un poco o arregla todo al azar e inténtalo de nuevo. También puede hacer esto para obtener otra solución.
[Edit4] Ejemplo de solucionador C ++
-
estructuras / clases para representar el sistema deslizante
Para facilitar el código posterior, no admitiré bucles cerrados o doble anclaje. Es por eso que el control deslizante i1 (más a la derecha) no está anclado a nada (solo proporcionará un campo de fuerza). Terminé con esta definición de control deslizante:
mira la fuente de la
class _slider
para más información. -
hacer
Dash-dash significa control deslizante fijo. Los plateados son horizontales, aqua significa vertical y el amarillo se selecciona con el mouse. Puede ser más tarde en rojo significará algún tipo de error / atascado o algo con fines de depuración. Para los solucionadores de campos de fuerza, a veces agrego la intensidad del campo como escala rojo-azul, pero no estoy seguro de si la implementaré aquí o no.
Para mantener esto simple, no implementaré las funciones de zoom / panorámica ya que sus dimensiones son convenientes para el renderizado directo sin transformaciones.
-
implementar la configuración inicial
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();
Donde
ia
es el índice padre eib
es el índice hijo (la clase del control deslizante en sí contieneib
como padre pero eso sería confuso para init ya que necesitaría vincular al elemento que aún no existe para que la transformaciónib
se maneje ensys.add
función).sys
es una clase que contiene todo ysys.add
solo agrega un nuevo control deslizante y devuelve su índice contando desde cero. Elx,y
es la posición relativa al padre.Para facilitar la cantidad de codificación, esta configuración no debe entrar en conflicto con las restricciones. La descripción general de esta configuración está en la viñeta anterior.
Tenga en cuenta que el orden de los controles deslizantes debe dejarse de izquierda a derecha para los controles deslizantes verticales y de arriba a abajo para garantizar la funcionalidad de restricción correcta.
-
interacción con el ratón
solo movimiento deslizante simple para depurar y ajustar los valores iniciales de configuración. Y o manejo de casos atascados. Debe manejar los eventos del mouse, seleccione el control deslizante más cercano si aún no está editando. Y si se presiona el botón del mouse, mueva el control deslizante seleccionado a la posición del mouse ...
-
restricción física / interacción
Simplifico esto un poco, así que acabo de crear una función de predicado que se llama para el control deslizante especificado y devuelve si él o alguno de sus elementos secundarios / de anclaje está en conflicto con las restricciones definidas. Esto es mucho más fácil de codificar y depurar que actualizar la posición para que coincida con la restricción real.
El uso es entonces un poco más de código. Primera posición real de la tienda para el control deslizante actualizado. Luego actualice el control deslizante a la nueva posición / estado. Después de eso, si no se cumplen las restricciones, detenga las velocidades reales del deslizador y restaure su posición original.
Será un poco más lento, pero soy demasiado vago para codificar el actualizador de restricciones completo (ese código podría ser realmente complejo ...).
Reconozco 2 interacciones paralelas y perpendiculares. El paralelo es sencillo. Pero lo perpendicular es la interacción entre el borde del control deslizante y los controles deslizantes perpendiculares cercanos, sin incluir los controles deslizantes que ya se cruzan (a, b anclados o simplemente cruzados) durante el estado inicial. Así que creé una lista de controles deslizantes de intersección (
ic
) al inicio que se ignorará para esta interacción. -
simulacion fisica
La física simple de Newton - D''Alembert para velocidades no relativistas servirá. Solo en cada iteración establezca el
ax,ay
aceleraciónax,ay
a la fuerza del campo y las fricciones. -
solucionador de campo
Este es un conjunto de reglas / ecuaciones para establecer aceleraciones de simulación para que cada control deslizante converja a la solución. Terminé con una fuerza de retracción electrostática
F = -Q/r^2
y una amortiguación lineal de la velocidad. También se han implementado limitadores de velocidad y aceleración absoluta para evitar problemas numéricos.Para aumentar el tiempo y la estabilidad de la solución, agregué modos de control de precisión donde la carga eléctrica está disminuyendo cuando la velocidad máxima general de los controles deslizantes está disminuyendo.
Aquí el código completo de la clase C ++ / VCL para esto:
//---------------------------------------------------------------------------
//--- 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
//---------------------------------------------------------------------------
Puede ignorar las cosas de VCL, es solo API para la interacción con mi ventana de aplicación y renderizado.
El solucionador en sí no necesita nada de él.
Usé mi
List<T>
plantillas de matriz lineal dinámica
List<T>
así que aquí algunas explicaciones:
-
List<double> xxx;
es lo mismo quedouble xxx[];
-
xxx.add(5);
agrega5
al final de la lista -
Elemento de matriz de acceso
xxx[7]
(seguro) -
xxx.dat[7]
matriz de accesoxxx.dat[7]
(acceso directo inseguro pero rápido) -
xxx.num
es el tamaño real utilizado de la matriz -
xxx.reset()
borra la matriz y establecexxx.num=0
-
xxx.allocate(100)
preasigna espacio para100
elementos
El uso es simple después del inicio correcto de la viñeta # 3 como este:
sys.solve(true);
for (;;)
{
sys.solve();
sys.update(0.040); // just time step
if (sys.mode==4) break; // stop if solution found or stuck
}
En lugar de por ciclo, llamo a esto en temporizador y vuelvo a dibujar la ventana para que vea la animación:
La agitación se debe a una frecuencia de muestreo de captura de GIF no uniforme (omitiendo algunos fotogramas de la simulación de forma irregular).
Puedes jugar con las constantes de
vel,acc
límites de
vel,acc
, coeficiente de amortiguación y el control de modo
if
s para cambiar el comportamiento.
Si implementa también el controlador del mouse, puede mover los controles deslizantes con el botón izquierdo del mouse para que pueda salir de los casos atascados ...
Aquí está la demostración independiente de Win32 (compilada con BDS2006 C ++ ).
- Demo haga clic en descarga lenta debajo del botón grande magenta, ingrese el código alfanumérico de 4 letras para iniciar la descarga, no es necesario registrarse.
Para obtener más información sobre cómo funciona el cálculo de Force de solucionador, consulte el QA relacionado / de seguimiento
- Diseño dirigido forzado para formas rectangulares restringidas