javascript algorithm geometry integer-programming

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 .

  1. 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í.

  2. Además de eso, agregue fricción escalada por la velocidad

    realmente no importa si el aire v^2 o el líquido v^3

  3. implementar restricciones cinemáticas

    para el deslizamiento horizontal y vertical solo debería ser realmente fácil.

  4. 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 ++

  1. 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.

  2. 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.

  3. 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 e ib es el índice hijo (la clase del control deslizante en sí contiene ib 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ón ib se maneje en sys.add función). sys es una clase que contiene todo y sys.add solo agrega un nuevo control deslizante y devuelve su índice contando desde cero. El x,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.

  4. 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 ...

  5. 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.

  6. 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ón ax,ay a la fuerza del campo y las fricciones.

  7. 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 que double xxx[];
  • xxx.add(5); agrega 5 al final de la lista
  • Elemento de matriz de acceso xxx[7] (seguro)
  • xxx.dat[7] matriz de acceso xxx.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 establece xxx.num=0
  • xxx.allocate(100) preasigna espacio para 100 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