Path generation for non-intersecting disc movement on a plane

后端 未结 4 1664
时光说笑
时光说笑 2020-12-03 14:16

What I\'m looking for

I have 300 or fewer discs of equal radius on a plane. At time 0 each disc is at a position. At time 1 each disc is at a potentially different

4条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-03 15:03

    Have played with this for fun a bit and here the result:

    Algorithm:

    1. process each disc
    2. set speed as constant*destination_vector
      • multiplicative constant a
      • and limit the speed to constant v afterwards
    3. test if new iterated position does not conflict any other disc
    4. if it does rotate the speed in one direction by some angle step ang
    5. loop until free direction found or full circle covered
    6. if no free direction found mark disc as stuck

      This is how it looks like for circle to inverse circle path:

      example1

      This is how it looks like for random to random path:

      example2

      stuck disc are yellow (none in these cases) and not moving discs are at destination already. This can also get stuck if there is no path like if disc already in destination circles another ones destination. To avoid that you need also change the colliding disc also ... You can play with the ang,a,v constants to make different appearance and also you could try random direction of angle rotation to avoid that swirling/twister movement

    Here the source code I used (C++):

    //---------------------------------------------------------------------------
    const int    discs =23;     // number of discs
    const double disc_r=5;      // disc radius
    const double disc_dd=4.0*disc_r*disc_r;
    struct _disc
        {
        double x,y,vx,vy;       // actual position
        double x1,y1;           // destination
        bool _stuck;            // is currently stuck?
        };
    _disc disc[discs];          // discs array
    //---------------------------------------------------------------------------
    void disc_generate0(double x,double y,double r)     // circle position to inverse circle destination
        {
        int i;
        _disc *p;
        double a,da;
        for (p=disc,a=0,da=2.0*M_PI/double(discs),i=0;ix =x+(r*cos(a));
            p->y =y+(r*sin(a));
            p->x1=x-(r*cos(a));
            p->y1=y-(r*sin(a));
            p->vx=0.0;
            p->vy=0.0;
            p->_stuck=false;
            }
        }
    //---------------------------------------------------------------------------
    void disc_generate1(double x,double y,double r)     // random position to random destination
        {
        int i,j;
        _disc *p,*q;
        double a,da;
        Randomize();
        for (p=disc,a=0,da=2.0*M_PI/double(discs),i=0;ix=x+(2.0*Random(r))-r;
                p->y=y+(2.0*Random(r))-r;
                for (q=disc,j=0;jx-p->x)*(q->x-p->x))+((q->y-p->y)*(q->y-p->y))x1=x+(2.0*Random(r))-r;
                p->y1=y+(2.0*Random(r))-r;
                for (q=disc,j=0;jx1-p->x1)*(q->x1-p->x1))+((q->y1-p->y1)*(q->y1-p->y1))vx=0.0;
            p->vy=0.0;
            p->_stuck=false;
            }
        }
    //---------------------------------------------------------------------------
    void disc_iterate(double dt)                    // iterate positions
        {
        int i,j,k;
        _disc *p,*q;
        double v=25.0,a=10.0,x,y;
        const double ang=10.0*M_PI/180.0,ca=cos(ang),sa=sin(ang);
        const int n=double(2.0*M_PI/ang);
        for (p=disc,i=0;ivx=a*(p->x1-p->x); if (p->vx>+v) p->vx=+v; if (p->vx<-v) p->vx=-v;
            p->vy=a*(p->y1-p->y); if (p->vy>+v) p->vy=+v; if (p->vy<-v) p->vy=-v;
            x=p->x; p->x+=(p->vx*dt);
            y=p->y; p->y+=(p->vy*dt);
            p->_stuck=false;
            for (k=0,q=disc,j=0;jx-p->x)*(q->x-p->x))+((q->y-p->y)*(q->y-p->y))=n) { p->x=x; p->y=y; p->_stuck=true; break; }
                p->x=+(p->vx*ca)+(p->vy*sa); p->vx=p->x;
                p->y=-(p->vx*sa)+(p->vy*ca); p->vy=p->y;
                p->x=x+(p->vx*dt);
                p->y=y+(p->vy*dt);
                j=-1; q=disc-1;
                }
            }
        }
    //---------------------------------------------------------------------------
    

    Usage is simple:

    1. call generate0/1 with center and radius of your plane where discs will be placed
    2. call iterate (dt is time elapsed in seconds)
    3. draw the scene

    if you want to change this to use t=<0,1>

    1. loop iterate until all disc at destination or timeout
    2. remember any change in speed for each disc in a list need the position or speed vector and time it occur
    3. after loop rescale the discs list all to the range of <0,1>
    4. render/animate the rescaled lists

    [Notes]

    My test is running in real time but I did not apply the <0,1> range and have not too many discs. So you need to test if this is fast enough for your setup.

    To speed up you can:

    • enlarge the angle step
    • test the collision after rotation against last collided disc and only when free test the rest...
    • segmentate the disc into (overlapping by radius) regions handle each region separately
    • also I think some field approach here could speed up things like create field map once in a while for better determine the obstacle avoidance direction

    [edit1] some tweaks to avoid infinite oscillations around obstacle

    For more discs some of them get stuck bouncing around already stopped disc. To avoid that just change the ang step direction once in a while this is the result:

    exampe3

    you can see the oscillating bouncing before finish

    this is the changed source:

    void disc_iterate(double dt)                    // iterate positions
        {
        int i,j,k;
        static int cnt=0;
        _disc *p,*q;
        double v=25.0,a=10.0,x,y;
        const double ang=10.0*M_PI/180.0,ca=cos(ang),sa=sin(ang);
        const int n=double(2.0*M_PI/ang);
        // process discs
        for (p=disc,i=0;ivx=a*(p->x1-p->x); if (p->vx>+v) p->vx=+v; if (p->vx<-v) p->vx=-v;
            p->vy=a*(p->y1-p->y); if (p->vy>+v) p->vy=+v; if (p->vy<-v) p->vy=-v;
            // stroe old and compute new position
            x=p->x; p->x+=(p->vx*dt);
            y=p->y; p->y+=(p->vy*dt);
            p->_stuck=false;
            // test if coliding
            for (k=0,q=disc,j=0;jx-p->x)*(q->x-p->x))+((q->y-p->y)*(q->y-p->y))=n) { p->x=x; p->y=y; p->_stuck=true; break; }   // if full circle covered? stop
                if (int(cnt&128))   // change the rotation direction every 128 iterations
                    {
                    // rotate +ang
                    p->x=+(p->vx*ca)+(p->vy*sa); p->vx=p->x;
                    p->y=-(p->vx*sa)+(p->vy*ca); p->vy=p->y;
                    }
                else{
                    //rotate -ang
                    p->x=+(p->vx*ca)-(p->vy*sa); p->vx=p->x;
                    p->y=+(p->vx*sa)+(p->vy*ca); p->vy=p->y;
                    }
                // update new position and test from the start again
                p->x=x+(p->vx*dt);
                p->y=y+(p->vy*dt);
                j=-1; q=disc-1;
                }
            }
        cnt++;
        }
    

提交回复
热议问题