自己交差を回避するランダムウォーク

※非整数ブラウン運動とは関係ありません。

線分の交差判定
この辺の詳細は最下部に歳の割には頭の賢いYoutuberへの動画リンクを張っとくため、ご覧召されよ。

注意すること
線分の端点の扱いをおろそかにすると、ずっとヒット判定だしたり、線分の繋ぎ目をすり抜けたりすることがある。

//線分は交差するか
public boolean IsIntersect(PVector sv0, PVector ev0, ArrayList<PVector> vertex)
{
 VectorLine2D line = new VectorLine2D(sv0,ev0);
 
 for(int i = 0; i<vertex.size()-1; i++)
 {
   PVector sv = vertex.get(i);
   PVector ev = vertex.get(i+1);
   
   if(IsOnIntersect(line,new VectorLine2D(sv,ev)))
   {
     return true;
   }
 }
 return false;
}

//自己交差判定用
//最後の要素を判定しない : vertex.size()-2
public boolean IsIntersect_1(PVector sv0, PVector ev0, ArrayList<PVector> vertex)
{
 VectorLine2D line = new VectorLine2D(sv0,ev0);
 
 for(int i = 0; i<vertex.size()-2; i++)
 {
   PVector sv = vertex.get(i);
   PVector ev = vertex.get(i+1);
   
   if(IsOnIntersect(line,new VectorLine2D(sv,ev)))
   {
     return true;
   }
 }
 return false;
}

//線分は交差するか,OnLineをとる
public boolean IsOnIntersect(VectorLine2D l1, VectorLine2D l2)
{
 if (IsOnStraddle(l1, l2))
 {
   if (IsOnStraddle(l2, l1))
   {
     return true;
   }
 }
 return false;
}

//線分は交差するか,OnLineをとらない
public boolean IsOffIntersect(VectorLine2D l1, VectorLine2D l2)
{
 if (IsStraddle(l1, l2))
 {
   if (IsStraddle(l2, l1))
   {
     return true;
   }
 }
 return false;
}

//線分が直線を跨ぐ(直線から線分を見る)
//お互いにお互いを見ると, 平行でない限り必ずどこかで跨ぐ
public boolean IsOnStraddle(VectorLine2D l1, VectorLine2D l2)
{
 PVector lv1 = l1.LV();
 PVector l1SVtol2SV = PVector.sub(l2.SV, l1.SV);
 PVector l1SVtol2EV = PVector.sub(l2.EV, l1.SV);

 //OnLine(外積=0)を入念にとる
 if ((int)Cross(lv1, l1SVtol2SV) == 0 || (int)Cross(lv1, l1SVtol2EV) == 0)
 {
   return true;
 }
 //片方がプラマイどちらかで、片方が外積ゼロだと反応しないケースがでる気がする(未確認)
 if (((int)Cross(lv1, l1SVtol2SV) ^ (int)Cross(lv1, l1SVtol2EV)) < 0)
 {
   return true;
 }
 return false;
}

//線分が直線を跨ぐ(直線から線分を見る)
//お互いにお互いを見ると, 平行でない限り必ずどこかで跨ぐ
public boolean IsStraddle(VectorLine2D l1, VectorLine2D l2)
{
 PVector lv1 = l1.LV();
 PVector l1SVtol2SV = PVector.sub(l2.SV, l1.SV);
 PVector l1SVtol2EV = PVector.sub(l2.EV, l1.SV);

 //OnLineはとらない
 if ((int)Cross(lv1, l1SVtol2SV) == 0 || (int)Cross(lv1, l1SVtol2EV) == 0)
 {
   return false;
 }
 //これだけだとOnLineをとってしまうことがある
 if (((int)Cross(lv1, l1SVtol2SV) ^ (int)Cross(lv1, l1SVtol2EV)) < 0)
 {
   return true;
 }
 return false;
}

ランダムウォークする本体。


class ArrowLine
{
 PVector origin;
 
 PVector pprev;
 PVector prev;
 PVector current;
 
 ArrayList<PVector> Rails = new ArrayList<PVector>();
 
 //コンストラクタ
 ArrowLine(float x, float y)
 {
   origin = new PVector(x,y);
   Rails.add(origin.copy());
   current = origin.copy();
 }
 
 void update()
 {
   pprev = prev;
   prev = current;
   current = CalcuNext();
   
   //絶対座標で返ってくる場合
   Rails.add(current);
   
   if(ResetCondition()){Reset();}
   if(StopCondition()){Stop();}
 }
 
 void draw()
 {   
   for(int i = 0; i<this.Rails.size()-1; i++)
   {
     PVector sv = this.Rails.get(i);
     PVector ev = this.Rails.get(i+1);

     line(sv.x, sv.y, ev.x, ev.y);
   }
   
   //beginShape();
   //for(int i = 0; i<this.Rails.size()-1; i++)
   //{
   //  //skip
   //  //if(!(i%3==0)){continue;}
     
   //  PVector v = this.Rails.get(i);
   //  curveVertex(v.x, v.y);
   //}
   //endShape();
 }
   
 PVector CalcuNext()
 {
   return RandLimitStep();
 }

 //可能な限り自己交差しないようにする
 int _count = 0;
 PVector RandLimitStep()
 {
   _count = 0;
   if(!(2<this.Rails.size()))
   {
     return PVector.add(current, new PVector(random(-10,10),random(-10,10)));      
   }
   
   while(true)
   {      
     PVector end_v = this.Rails.get(this.Rails.size()-1);      
     PVector new_v = PVector.add(new PVector(random(-10,10),random(-10,10)), current);
     
     //生成されたベクトルが短すぎる場合
     //if(new_v.mag()<10)
     //{
     //  continue;
     //}
     
     //生成されたベクトルが画面外
     if(new_v.x<0||width<new_v.x)
     {
       continue;
     }
     if(new_v.y<0||height<new_v.y)
     {
       continue;
     }
     
     //railsと新しく作った点は自己交差するか
     //rails末端を含むと常に交差判定を出すので含まないようにする
     if(!IsIntersect_1(end_v, new_v, this.Rails))
     {
       return new_v;
     }
     
     _count++;
     if(100<_count)
     {
       //rollback
       current = prev;
       prev = pprev;
       this.Rails.remove(this.Rails.size()-1);
       
       if(10000<_count)
       {
         //error        
         Reset();
       }
     }
   }
 }
 
 boolean StopCondition()
 {
   return false;
 }
 void Stop()
 {
 }
 boolean ResetCondition()
 {
   return false;    
 }
 void Reset()
 {
   this.Rails.clear();
   this.Rails.add(origin.copy());
 }
}



int window_width = 500;
int window_height = 500;

int ww;
int wh;

void settings()
{
 size(window_width, window_height);
}

PVector pprev;
PVector prev;
PVector current;
PVector dv = new PVector(0,0);
void mousePressed() 
{
 pprev = new PVector(mouseX,mouseY);
 prev = new PVector(mouseX,mouseY);
 current = new PVector(mouseX,mouseY);
}

void mouseDragged()
{          
 pprev = prev.copy();
 prev = current.copy();
 current.x = mouseX;
 current.y = mouseY;  
 dv = PVector.sub(current,prev);
}

void mouseReleased()
{   
 pprev = null;
 prev = null;
 current = null; 
}

void setup()
{
 ww = window_width;
 wh = window_height;
 
 //画面中央
 arrow1 = new ArrowLine(width/2,height/2);
}

ArrowLine arrow1;
void draw() 
{
 background(255);
 
 stroke(0);
 arrow1.update();
 arrow1.draw();
}


 

以下、年の割には頭の賢いYoutuber





この記事が気に入ったらサポートをしてみませんか?