見出し画像

ストライプ生成マッスィーン

processingでできてます。

続編

使い方。

画像3

画像2

画像3

コピペ用コード

controlP5はがんばって導入します。


import controlP5.*;

ControlP5 cp;
//BackgroundColor用
Slider BackR;
Slider BackG;
Slider BackB;

float default_stroke_weight = 1;
color default_stroke_color = color(0);
color default_fill_color = color(255);

ArrayList<IGUI> GUIs = new ArrayList<IGUI>();
Rect CaptureWindow;

void setup()
{  
 size(800,800);

 //#1
 PVector origin1 = new PVector(100,100);
 PVector lv1 = new PVector(0,1);
 float lv_length1 = 400;
 PVector offset1 = new PVector(1,0);
 float offset_length1 = 2;
 int count1 = 200;
 
 Stripe stripe1 = new Stripe(origin1,lv1,lv_length1,offset1,offset_length1,count1);
 StripeGUI stripegui1 = new StripeGUI(stripe1);
 this.GUIs.add(stripegui1);
 
 //#2
 PVector origin2 = new PVector(100,100);
 PVector lv2 = new PVector(1,0);
 float lv_length2 = 400;
 PVector offset2 = new PVector(0,1);
 float offset_length2 = 2;
 int count2 = 200;  
 
 Stripe stripe2 = new Stripe(origin2,lv2,lv_length2,offset2,offset_length2,count2);
 StripeGUI stripegui2 = new StripeGUI(stripe2);
 this.GUIs.add(stripegui2);
 
 //#
 this.CaptureWindow = new Rect(new PVector(100,100), 300,300);
 this.GUIs.add(CaptureWindow);
 
 MakeGUI();
}

void draw()
{
 color background_color = color(this.BackR.getValue(),this.BackG.getValue(),this.BackB.getValue());
 background(background_color);
 
 for(int i = 0; i<this.GUIs.size(); i++)
 {
   IGUI gui = this.GUIs.get(i);
   gui.Draw();
 }
}

PVector pprev;
PVector prev;
PVector current;
PVector dv = new PVector(0,0);
int current_index = -1;

void keyPressed()
{
  if(key == 's')
  {
    SaveImage();
  }
 for(int i = 0; i<this.GUIs.size(); i++)
 {    
   IGUI gui = GUIs.get(i);
   gui.keyPressed();
 }     
}

void keyReleased()
{
 
}

void mousePressed()
{
 pprev = new PVector(mouseX,mouseY);
 prev = new PVector(mouseX,mouseY);
 current = new PVector(mouseX,mouseY);
 
 //Hit
 for(int i = 0; i<this.GUIs.size(); i++)
 {    
   if(GUIs.get(i).Hit(current))
   {
     current_index = i;
     break;
   }    
 }    
 
 if(0<=current_index)
 {
   GUIs.get(current_index).mousePressed();
 }
}

void mouseDragged()
{
 pprev = prev.copy();
 prev = current.copy();
 current.x = mouseX;
 current.y = mouseY;  
 dv = PVector.sub(current,prev);
   
 if(0<=current_index)
 {
   IGUI gui = GUIs.get(current_index); 
   gui.mouseDragged();
 }
}

void mouseReleased()
{
 pprev = null;
 prev = null;
 current = null;  
   
 if(0<=current_index)
 {
   IGUI gui = GUIs.get(current_index); 
   gui.mouseReleased();
 }      
 current_index=-1;
}

//GUI
void MakeGUI()
{
 cp = new ControlP5(this);

 int slider_x = 10;
 int slider_y = 10;  
 int slider_w = 100;  
 int slider_h = 10;
 int slider_offset = 10;
 
 BackR = cp.addSlider("R")
    .setPosition(slider_x,slider_y)
    .setRange(0, 255)
    .setValue(255)//初期値
    .setSize(slider_w,slider_h)
    .setColorBackground(color(255)) //スライダー内背景色      
    .setColorCaptionLabel(color(0));//Labelの色
    
 slider_y+=slider_offset;     
 
 BackG = cp.addSlider("G")
    .setPosition(slider_x,slider_y)
    .setRange(0, 255)
    .setValue(255)//初期値
    .setSize(slider_w,slider_h)
    .setColorBackground(color(255)) //スライダー内背景色      
    .setColorCaptionLabel(color(0));//Labelの色  
    
 slider_y+=slider_offset;     
 
 BackB = cp.addSlider("B")
    .setPosition(slider_x,slider_y)
    .setRange(0, 255)
    .setValue(255)//初期値
    .setSize(slider_w,slider_h)
    .setColorBackground(color(255)) //スライダー内背景色      
    .setColorCaptionLabel(color(0));//Labelの色     
}

interface IGUI
{
 boolean Hit(PVector input_pos);
 void Draw();

 void mousePressed();
 void mouseReleased(); 
 void mouseDragged();  
 void keyPressed();
 void keyReleased();
}

//画像の保存範囲を決定するWindow
class Rect implements IGUI
{
 PVector LTV = new PVector(0,0);
 float Width;
 float Height;
 
 float CPRadius = 10;
 int cp_index = -1;
 PVector GetLTV(){return new PVector(LTV.x,LTV.y);}
 PVector GetRTV(){return new PVector(LTV.x+Width,LTV.y);}
 PVector GetRBV(){return new PVector(LTV.x+Width,LTV.y+Height);}
 PVector GetLBV(){return new PVector(LTV.x,LTV.y+Height);}
 
 boolean Hit(PVector input_pos)
 {
   if(PointInCircle(this.GetLTV(),this.CPRadius, input_pos)){cp_index=1; return true;}
   else if(PointInCircle(this.GetRBV(),this.CPRadius, input_pos)){cp_index=2; return true;}
   return false;
 }

 boolean is_view = true;
 void keyPressed()
 {
    if(key == 'v')
    {
      is_view = !is_view;
    }
 }
 void keyReleased(){}
 
 void Draw()
 {
   if(!is_view){return;}
   line(GetLTV().x,GetLTV().y,GetRTV().x,GetRTV().y);
   line(GetRTV().x,GetRTV().y,GetRBV().x,GetRBV().y);
   line(GetRBV().x,GetRBV().y,GetLBV().x,GetLBV().y);
   line(GetLBV().x,GetLBV().y,GetLTV().x,GetLTV().y);
   circle(GetLTV().x,GetLTV().y,CPRadius);
   circle(GetRBV().x,GetRBV().y,CPRadius);
 }

 void mousePressed()
 {
   if(PointInCircle(this.GetLTV(),this.CPRadius, current)){cp_index=1;}
   else if(PointInCircle(this.GetRBV(),this.CPRadius, current)){cp_index=2;}  
 }
 void mouseReleased(){cp_index=-1;}
 void mouseDragged()
 {
   if(this.cp_index==1)
   {
     this.LTV.add(dv);
   }
   else if(this.cp_index==2)
   {
     this.Width+=dv.x;
     this.Height+=dv.y;
   }
 }  

 //コンストラクタ
 Rect(PVector ltv_, float w_, float h_)
 {
   LTV.set(ltv_);
   Width=w_;
   Height=h_;
 }
}

void SaveImage()
{
   PImage img = createImage(width, height, RGB);
    
   loadPixels();
   img.pixels = pixels;
   img.updatePixels();
    
   img = img.get((int)CaptureWindow.LTV.x, (int)CaptureWindow.LTV.y, (int)CaptureWindow.Width, (int)CaptureWindow.Height);
   img.save("cap.png");  
}

//Stripe
class StripeGUI implements IGUI
{
 //コンストラクタ
 StripeGUI(Stripe ray)
 {
   this.Stripe = ray;
 }
 
 Stripe Stripe;
 
 //コントロールポイント共通の半径(この半径を基準にGUIに触れることができる)
 float CPRadius = 10;  
 //カラーGUI用の半径
 float ColorCircleRadius = 30;
   
 //GUI原点
 PVector Origin(){return this.Stripe.Origin;}
 //線の長さと向きを決定するベクトル
 PVector LV(){return this.Stripe.LV.normalize().copy().mult(this.Stripe.LVLength);}
 PVector LVPos(){return this.Origin().copy().add(this.LV());}
 
 //線の間隔を決定するベクトルに掛かる係数
 float OffsetMultiplier = 10;
 //線の間隔を決定するベクトル
 PVector Offset(){return this.Stripe.Offset.normalize().copy().mult(this.Stripe.OffsetLength*OffsetMultiplier);}
 PVector OffsetPos(){return this.Origin().copy().add(this.Offset());}
 
 //線の幅を決定する係数
 float GetStrokeWeight(){return StrokeWeightVector.mag()/ ColorCircleRadius;}
 //線の幅を決定する係数を生成するためのベクトル
 PVector StrokeWeightVector = new PVector(StrokeWeightVectorInitPos().x,StrokeWeightVectorInitPos().y);
 PVector StrokeWeightVectorPos(){return this.Origin().copy().add(this.StrokeWeightVector);}
 
 PVector StrokeWeightVectorInitPos()
 {
   PVector lv = new PVector(CircleGUIArrow().x,CircleGUIArrow().y);
   return RotRadian(lv, Math.PI);
 }
 
 //線のRGB
 //0-255
 float r = 0;
 float RRadian(){return map(r,0,255,0,(float)(2*Math.PI));}
 PVector RPos(){return this.Origin().copy().add(RotRadian(CircleGUIArrow(), RRadian()));}  
 void SetR(PVector pos){this.r=map(CalcuRadianOnCircleGUI(pos),0,(float)(2*Math.PI),0,255);}
 
 float g = 0;
 float GRadian(){return map(g,0,255,0,(float)(2*Math.PI));}
 PVector GPos(){return this.Origin().copy().add(RotRadian(CircleGUIArrow(), GRadian()));}
 void SetG(PVector pos){this.g=map(CalcuRadianOnCircleGUI(pos),0,(float)(2*Math.PI),0,255);}

 float b = 0;
 float BRadian(){return map(b,0,255,0,(float)(2*Math.PI));}
 PVector BPos(){return this.Origin().copy().add(RotRadian(CircleGUIArrow(), BRadian()));}  
 void SetB(PVector pos){this.b=map(CalcuRadianOnCircleGUI(pos),0,(float)(2*Math.PI),0,255);}
 
 //CircleGUI上の位置を決めたり、そこから値を取得する時に使うベクトル
 PVector CircleGUIArrow(){return new PVector(0,-ColorCircleRadius);}
 //この座標はCircleGUI上の0を示す
 PVector CircleGUIOrigin(){return this.Origin().copy().add(CircleGUIArrow());}
 //CircleGUI上の座標を入力するとradianで返る
 float CalcuRadianOnCircleGUI(PVector pos)
 {
   PVector lv = pos.copy().sub(this.Origin());
   float radian = atan2(lv.y,lv.x);
   radian += (float)(Math.PI/2);
   if(radian<0)
   {
     radian+=2*Math.PI;
   }    
   return radian;
 }
 
 //現在選択中のControl Point
 int cp_index = -1;
 boolean Hit(PVector input_pos)
 {
   if(PointInCircle(this.Origin(),this.CPRadius, input_pos)){cp_index=0; return true;}
   else if(PointInCircle(this.LVPos(),this.CPRadius, input_pos)){cp_index=1; return true;}
   else if(PointInCircle(this.OffsetPos(),this.CPRadius, input_pos)){cp_index=2; return true;}    
   
   //RGB
   else if(PointInCircle(this.RPos(),this.CPRadius, input_pos)){cp_index=3; return true;}
   else if(PointInCircle(this.GPos(),this.CPRadius, input_pos)){cp_index=4; return true;}
   else if(PointInCircle(this.BPos(),this.CPRadius, input_pos)){cp_index=5; return true;}
   else if(PointInCircle(this.StrokeWeightVectorPos(),this.CPRadius, input_pos)){cp_index=6; return true;}   
   return false;
 }
 
 void Draw()
 {    
   //線の色
   color current_color = color((int)r,(int)g,(int)b);
   
   strokeWeight(this.GetStrokeWeight());
   stroke(current_color);    
   this.Stripe.Draw();    
   stroke(default_stroke_color);
   strokeWeight(default_stroke_weight);
   
   //以下付属のGUI
   if(!is_view){return;}
   
   stroke(0,255,0);
   line(Origin().x,Origin().y,StrokeWeightVectorPos().x,StrokeWeightVectorPos().y);
   stroke(153,153,0);
   line(Origin().x,Origin().y,LVPos().x,LVPos().y);
   stroke(0,0,255);    
   line(Origin().x,Origin().y,OffsetPos().x,OffsetPos().y);
   stroke(default_stroke_color);
   
   //RGB
   circle(Origin().x,Origin().y,ColorCircleRadius*2);
   fill(255,0,0);
   circle(RPos().x,RPos().y,CPRadius);
   fill(0,255,0);
   circle(GPos().x,GPos().y,CPRadius);
   fill(0,0,255);
   circle(BPos().x,BPos().y,CPRadius);
   fill(default_fill_color);
   
   //StrokeWeight
   circle(StrokeWeightVectorPos().x,StrokeWeightVectorPos().y,CPRadius);
   
   //
   circle(Origin().x,Origin().y,CPRadius);
   circle(LVPos().x,LVPos().y,CPRadius);
   circle(OffsetPos().x,OffsetPos().y,CPRadius);
 }
 
 boolean is_view = true;
 void keyPressed()
 {
    if(key == 'v')
    {
      is_view = !is_view;
    }
 }
 void keyReleased(){}
 void mousePressed(){}
 void mouseReleased(){cp_index=-1;}
 void mouseDragged()
 {
   if(this.cp_index==0)
   {
     this.Stripe.Origin.add(dv);
   }
   else if(this.cp_index==1)
   {
     PVector lv = this.LVPos().sub(this.Origin()).add(dv);
     this.Stripe.LV.set(lv.copy().normalize());      
     this.Stripe.LVLength = lv.mag();
   }
   else if(this.cp_index==2)
   {
     PVector lv = this.OffsetPos().sub(this.Origin()).add(dv);
     this.Stripe.Offset.set(lv.copy().normalize());      
     this.Stripe.OffsetLength = lv.mag()/OffsetMultiplier;      
   }
   
   //RGB
   else if(this.cp_index==3)
   {
     PVector pos = NearestOnCircle(this.Origin(), this.ColorCircleRadius, current);
     SetR(pos);
   }
   else if(this.cp_index==4)
   {
     PVector pos = NearestOnCircle(this.Origin(), this.ColorCircleRadius, current);
     SetG(pos);      
   }
   else if(this.cp_index==5)
   {
     PVector pos = NearestOnCircle(this.Origin(), this.ColorCircleRadius, current);
     SetB(pos);      
   }
   
   //StrokeWeight
   else if(this.cp_index==6)
   {
     this.StrokeWeightVector.add(dv);   
   }    
 }    
}

class Stripe
{
 public PVector Origin = new PVector(0,0);
 public PVector LV = new PVector(0,1);
 public float LVLength = 100;
 public PVector Offset = new PVector(0,1);
 public float OffsetLength = 10;
 public int Count = 200; //Stripe反復回数
 
 void Draw()
 {
   DrawStripe(this.Origin,LV,LVLength,Offset,OffsetLength,Count);
 }
 
 //コンストラクタ
 Stripe(PVector origin, PVector lv, float lv_length, PVector offset, float offset_length, int count)
 {
   this.Origin=origin;
   this.LV=lv;
   this.LVLength=lv_length;
   this.Offset=offset;
   this.OffsetLength=offset_length;
   this.Count=count;
 }
}

void DrawStripe(PVector origin, PVector lv, float lv_length, PVector offset, float offset_length, int count)
{
 for(int i = 0; i<count; i++)
 {
   PVector sv = origin.copy().add(offset.copy().normalize().mult(offset_length*i));
   PVector ev = sv.copy().add(lv.copy().normalize().mult(lv_length));
   line(sv.x,sv.y,ev.x,ev.y);
 }
}

//以下ベクトル
PVector TurnL(PVector v)
{
 return new PVector(v.y,-v.x);
}
PVector TurnR(PVector v)
{
 return new PVector(-v.y,v.x);
}

float CalcuAngle180(PVector v1, PVector v2)
{
 return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
}

//x軸に対して
float CalcuAngle360(PVector v)
{
 return CalcuAngle360(new PVector(100,0),v);
}

float CalcuAngle360(PVector v1, PVector v2)
{
 float s = Sin(v1,v2);
 float c = Cos(v1,v2);  
 if(0<s)
 {
   return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);
 }
 else if(s<0)
 {
   return 2*PI-(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
 }
 else
 {    
   if(0<c){return 0;}//前    
   else{return PI;}//後      
 }
}

PVector vadd(PVector v1, PVector v2)
{
 return PVector.add(v1,v2);
}

void vadd(ArrayList<PVector> vs, PVector v2)
{
 for(PVector v : vs)
 {
    v.add(v2); 
 }
}

PVector vsub(PVector v1, PVector v2)
{
 return PVector.sub(v1,v2);
}
PVector vmult(PVector v, float value)
{
 return PVector.mult(v,value);
}
PVector vdiv(PVector v, float value)
{
 return PVector.div(v,value);
}
PVector vrot(PVector v, float radian)
{
 return RotRadian(v,radian);
}

//2つのベクトルのcos
float Cos(PVector v1, PVector v2)
{
   //v1を基準とする時
   //v2の向きが揃ってるなら1
   //v2がv1の左右に直交しているなら0
   //v2がv1の真反対を向いているなら-1 

   float ll = v1.mag() * v2.mag();

   //0除算を回避
   //この場合どちらかのベクトルがゼロベクトルなので計算自体が成り立たないはず
   if (ll == 0) { return 0; }

   return Dot(v1, v2) / ll;
}

float Sin(PVector v1, PVector v2)
{
   //0<sinの時v2は右側(ディスプレイ座標(Yが下に+))
   //0=sinの時v2は平行、直線上
   //sin<0の時v2は左側

   float ll = v1.mag() * v2.mag();

   //0除算を回避
   //この場合どちらかのベクトルがゼロベクトルなので計算自体が成り立たないはず
   if (ll == 0) { return 0; }

   return Cross(v1, v2) / ll;
}

//体積を求める
public float CalcuArea(ArrayList<PVector> vertex)
{
 float sum = 0;
 for(int i = 0; i<vertex.size()-1; i++)
 {
   PVector v1 = vertex.get(i);
   PVector v2 = vertex.get(i+1);
   float c = Cross(v1,v2);
   sum+=c;
 }
 return (Math.abs(sum))/2.0;
}

public PVector Nagate(PVector v)
{
 return new PVector(-v.x,-v.y);
}

// 内積
public float Dot(PVector v1, PVector v2)
{
   return (v1.x * v2.x + v1.y * v2.y);
}

//外積、疑似外積、疑スカラー、パープ内積
public float Cross(PVector v1, PVector v2)
{
   //+ならv2がv1の右側
   return (v1.x * v2.y - v1.y * v2.x);
}

//重心
public PVector CenterOfGravity(ArrayList<PVector> vertexes)
{
 PVector sum = new PVector(0,0);
 for(int i = 0; i<vertexes.size(); i++)
 {
   PVector v = vertexes.get(i);
   sum.add(v);
 }
 sum.div(vertexes.size());
 
 return sum;
}

public ArrayList<PVector> RotRadian(ArrayList<PVector> vertex, double radian)
{
 PVector cog = CenterOfGravity(vertex);
 return RotRadian(vertex, cog, radian);
}

public ArrayList<PVector> RotDegree(ArrayList<PVector> vertex, double degree)
{
 PVector cog = CenterOfGravity(vertex);
 return RotDegree(vertex, cog, degree);
}

public ArrayList<PVector> RotRadian(ArrayList<PVector> vertex, PVector center, double radian)
{    
   ArrayList<PVector> ret = new ArrayList<PVector>();
   
   for(int i = 0; i<vertex.size(); i++)
   {
     PVector v = vertex.get(i);
     PVector dv = PVector.sub(v,center);      
     dv = RotRadian(dv, radian);
     ret.add(PVector.add(center,dv));
   }    
   return ret;
}

public ArrayList<PVector> RotDegree(ArrayList<PVector> vertex, PVector center, double degree)
{
   if (degree == 0) { return vertex; }
   double radian = (degree * Math.PI) / 180;
   return RotRadian(vertex, center, radian);
}

public PVector RotRadian(PVector v, double radian)
{
   float x = v.x * cos((float)radian) - v.y * sin((float)radian);
   float y = v.x * sin((float)radian) + v.y * cos((float)radian);
   return new PVector(x,y);
}

public PVector RotDegree(PVector v, double degree)
{
   if (degree == 0) { return new PVector(v.x,v.y); }
   double radian = (degree * Math.PI) / 180;
   return RotRadian(v, radian);
}

public void RotRadianRef(ArrayList<PVector> vertex, PVector center, double radian)
{
   for(int i = 0; i<vertex.size(); i++)
   {
     PVector v = vertex.get(i);
     PVector dv = PVector.sub(v,center);      
     RotRadianRef(dv, radian);
     vertex.get(i).x = center.x+dv.x;
     vertex.get(i).y = center.y+dv.y;
   }    
}

public void RotDegreeRef(ArrayList<PVector> vertex, PVector center, double degree)
{
   if (degree == 0) { return ; }
   double radian = (degree * Math.PI) / 180;
   RotRadianRef(vertex, center, radian);
}


public void RotRadianRef(PVector v, double radian)
{
   float x = v.x * cos((float)radian) - v.y * sin((float)radian);
   float y = v.x * sin((float)radian) + v.y * cos((float)radian);
   v.x = x;
   v.y = y;
}

public void RotRadianRef(PVector v, PVector center, double radian)
{
   PVector dv = PVector.sub(v,center);      
   RotRadianRef(dv, radian);
   v.x = center.x+dv.x;
   v.y = center.y+dv.y;   
}

public void RotDegreeRef(PVector v, double degree)
{
   if (degree == 0) { return; }
   double radian = (degree * Math.PI) / 180;
   RotRadianRef(v, radian);
}


//Christer p40
//正射影ベクトル(2点u,vが与えられた場合)
public PVector Projection(PVector u, PVector v)
{
 //u:axis
 return PVector.mult(u,(PVector.dot(u, v) / PVector.dot(u, u)));
}

//正射影ベクトル(直線上に焼き付ける場合)
public PVector Projection(PVector sv, PVector ev, PVector v)
{
 PVector lv = PVector.sub(ev,sv);
 PVector toV = PVector.sub(v,sv);

 //このベクトルの先端を絶対座標として取得するためにはsvを足さなければならない
 return PVector.mult(lv,(PVector.dot(toV, lv) / PVector.dot(lv, lv)));
}

//垂直射影ベクトル(2点u,vが与えられた場合)
public PVector Perpendicular(PVector u, PVector v)
{
 //u:axis
 return PVector.sub(v, Projection(u, v));
}

//垂直射影ベクトル(直線上から射出する場合)
public PVector Perpendicular(PVector sv, PVector ev, PVector v)
{
 PVector lv = PVector.sub(ev,sv);
 PVector toV = PVector.sub(v,sv);

 //このベクトルの先端を絶対座標として取得するためにはsv+projを足さなければならない
 return PVector.sub(toV, Projection(lv, toV));
}

//uの垂直軸に対してvを鏡映変換
public PVector RefrectV(PVector u, PVector v)
{
   //v-2*parpendicular
   return PVector.sub(v,PVector.mult(Perpendicular(u,v),2));
}

public PVector RefrectV(PVector sv, PVector ev, PVector v)
{
   PVector lv = PVector.sub(ev,sv);
   PVector toV = PVector.sub(v,sv);  
   //v-2*parpendicular
   return PVector.sub(toV,PVector.mult(Perpendicular(lv,toV),2));
}

//uに対するvの鏡映変換
public PVector RefrectH(PVector u, PVector v)
{
   //2*parpendicular-v
   return PVector.sub(PVector.mult(Perpendicular(u,v),2), v);
}
public PVector RefrectH(PVector sv, PVector ev, PVector v)
{
   PVector lv = PVector.sub(ev,sv);
   PVector toV = PVector.sub(v,sv);  
   //2*parpendicular-v
   return PVector.sub(PVector.mult(Perpendicular(lv,toV),2), toV);
}

float Epsilon = 0.0001;
PVector GetLV(VectorLine2D line)
{
 PVector lv = PVector.sub(line.EV,line.SV);
 //線分が短すぎると計算が飛ぶ
 if(Math.abs(lv.x)<Epsilon){lv.x=0;}
 if(Math.abs(lv.y)<Epsilon){lv.y=0;}
 return lv;
}

class VectorLine2D
{
 PVector SV;
 PVector EV;
 
 //PVector LV()
 //{
 //  return PVector.sub(this.EV,this.SV);
 //}
 
 VectorLine2D(PVector sv, PVector ev)
 {
   this.SV = sv.copy();
   this.EV = ev.copy();
 }
 VectorLine2D(float sx, float sy, float ex, float ey)
 {
   this.SV = new PVector(sx,sy);
   this.EV = new PVector(ex,ey);
 }  
}

//複数線分の交点座標を取得
public ArrayList<PVector> GetIntersection(ArrayList<VectorLine2D> l1, ArrayList<VectorLine2D> l2)
{
 ArrayList<PVector> ret = new ArrayList<PVector>();

 for (int i = 0; i < l1.size(); i++)
 {
   ArrayList<PVector> vs = GetIntersection(l1.get(i), l2);
   if(vs!=null&&vs.size()<1)
   {
     ret.addAll(vs);
   }
 }
 return ret;
}

public ArrayList<PVector> GetIntersection(VectorLine2D l1, ArrayList<VectorLine2D> l2)
{
 ArrayList<PVector> ret = new ArrayList<PVector>();

 for (int i = 0; i < l2.size(); i++)
 {
   PVector v = GetIntersection(l1, l2.get(i));
   if(v!=null)
   {
     ret.add(v);
   }
 }
 return ret;
}

public PVector GetIntersection(PVector sv1, PVector ev1, PVector sv2, PVector ev2)
{
 return GetIntersection(new VectorLine2D(sv1,ev1), new VectorLine2D(sv2, ev2));
}

public PVector GetIntersection(VectorLine2D l1, VectorLine2D l2)
{
 if(IsIntersect(l1,l2))
 {
   PVector v = GetLV(l1);
   PVector w = GetLV(l2);
   PVector u = PVector.sub(l2.SV, l1.SV);

   float t1 = (float)(Cross(v, u)/ Cross(w, v));
   return PVector.add(l2.SV, PVector.mult(GetLV(l2), t1));
 }
 return null;
}

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

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

 //OnLineはとらない(false)
 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;
}

//Line
float Get_t(PVector sv, PVector ev, PVector v)
{
 PVector lv = PVector.sub(ev,sv);
 PVector toV = PVector.sub(v,sv);
 float t = Dot(toV,lv)/Dot(lv,lv);
 
 return t;
}

PVector Set_t(PVector sv, PVector ev, float t)
{
 PVector lv = PVector.sub(ev,sv);
 return lv.normalize().mult(t);
}

//区間を1とする
float Get_t_Liner(ArrayList<PVector> vertices, PVector v)
{
 int index = NearestOnSegmentsIndex(vertices,v);
 PVector sv = vertices.get(index);
 PVector ev = vertices.get(index+1);
 return index+Get_t(sv,ev,v);  
}

PVector Set_t_Liner(ArrayList<PVector> vertices, float t)
{
 int index = (int)t;
 PVector sv = vertices.get(index);
 PVector ev = vertices.get(index+1);  
 return Set_t(sv,ev,t-index);
}

//区間を1とする
float Get_t_LinerClose(ArrayList<PVector> vertices, PVector v)
{
 int index = NearestOnSegmentsIndexClose(vertices,v);
 PVector sv = vertices.get(index);
 PVector ev = vertices.get(index+1);
 return index+Get_t(sv,ev,v);  
}

PVector Set_t_LinerClose(ArrayList<PVector> vertices, float t)
{
 int index = (int)t;
 PVector sv = vertices.get(index);
 PVector ev = vertices.get(index+1%vertices.size());  
 return Set_t(sv,ev,t-index);  
}

float Get_radian(PVector cv, float radius, PVector v)
{
 PVector lv = PVector.sub(v,cv);
 double radian = Math.atan2(lv.y, lv.x);
 //radian += PI;
 //if(radian<0){radian=0;}
 //if(2*PI<radian){radian=2*PI;}
 return (float)radian;
}

PVector Set_radian(PVector cv, float radius, float radian)
{
 PVector v = new PVector(radius,0);
 RotRadianRef(v,radian);
 return PVector.add(cv,v);
}

//Christer p128
//直線に一番近い点の絶対座標:Projection先端の絶対座標に等しい
PVector NearestOnLine(PVector sv, PVector to, PVector v)
{
 //PVector perp = Perpendicular(sv,to,v);
 //perp = Nagate(perp);
 //PVector d = PVector.add(v,perp);
 
 PVector proj = Projection(sv,to,v);
 PVector d = PVector.add(sv,proj);
 
 return d;
}

//Christer p128
//半直線に一番近い点の絶対座標
PVector NearestOnRay(PVector sv, PVector to, PVector v)
{
PVector lv = PVector.sub(to,sv);
PVector toV = PVector.sub(v,sv);
float t = Dot(toV,lv)/Dot(lv,lv);
if(t<0){t=0;} 
return PVector.add(sv, lv.mult(t));
}

//Christer p128
//線分に一番近い点の絶対座標
PVector NearestOnSegment(PVector sv, PVector ev, PVector v)
{
PVector lv = PVector.sub(ev,sv);
PVector toV = PVector.sub(v,sv);
float t = Dot(toV,lv)/Dot(lv,lv);
if(t<0){t=0;}
if(1<t){t=1;}
return PVector.add(sv, lv.mult(t));
}

//折れ線
//閉じない
PVector NearestOnSegments(ArrayList<PVector> vectors, PVector v)
{
 float min_distance = Float.MAX_VALUE;
 PVector ret = vectors.get(0);
 for(int i = 0; i<vectors.size()-1; i++)
 {
   PVector v0 = vectors.get(i);
   PVector v1 = vectors.get(i+1);
   PVector near = NearestOnSegment(v0,v1,v);
   
   PVector n2v = PVector.sub(v, near);
   float d = n2v.mag();
   if( d<min_distance)
   {
     min_distance = d;
     ret = near;
   }
 }
 return ret;
}

int NearestOnSegmentsIndex(ArrayList<PVector> vectors, PVector v)
{
 float min_distance = Float.MAX_VALUE;
 int ret = 0;
 for(int i = 0; i<vectors.size()-1; i++)
 {
   PVector v0 = vectors.get(i);
   PVector v1 = vectors.get(i+1);
   PVector near = NearestOnSegment(v0,v1,v);    
   PVector n2v = PVector.sub(v, near);
   float d = n2v.mag();
   if( d<min_distance)
   {
     min_distance = d;
     ret = i;
   }
 }
 return ret;
}

//閉じる
PVector NearestOnSegmentsClose(ArrayList<PVector> vectors, PVector v)
{
 float min_distance = Float.MAX_VALUE;
 PVector ret = vectors.get(0);
 for(int i = 0; i<vectors.size(); i++)
 {
   PVector v0 = vectors.get(i);
   PVector v1 = vectors.get((i+1)%vectors.size());
   PVector near = NearestOnSegment(v0,v1,v);
   
   PVector n2v = PVector.sub(v, near);
   float d = n2v.mag();
   if( d<min_distance)
   {
     min_distance = d;
     ret = near;
   }
 }
 return ret;
}

//インデックスを返す
int NearestOnSegmentsIndexClose(ArrayList<PVector> vectors, PVector v)
{
 float min_distance = Float.MAX_VALUE;
 int ret = 0;
 for(int i = 0; i<vectors.size(); i++)
 {
   PVector v0 = vectors.get(i);
   PVector v1 = vectors.get((i+1)%vectors.size());
   PVector near = NearestOnSegment(v0,v1,v);
   
   PVector n2v = PVector.sub(v, near);
   float d = n2v.mag();
   if( d<min_distance)
   {
     min_distance = d;
     ret = i;
   }
 }
 return ret;
}

//領域に対して最も近い点
PVector NearestOnPolygon(ArrayList<PVector> vectors, PVector v)
{
 //多角形の内側ならそのまま
 //多角形の外にでたら最も近い外周上の1点を返す
 if(CrossingNumber(vectors,v)){return v;}
 else{return NearestOnSegmentsClose(vectors,v);}
}

//■円周
//円周上の最近接点
//■Circle Circumference
PVector NearestOnCircumference(PVector center, float radius, PVector v)
{
 PVector toV = PVector.sub(v,center);
 toV.normalize().mult(radius);
 return toV.add(center);
}

//領域に対して最も近い点
PVector NearestOnCircle(PVector center, float radius, PVector v)
{
 //多角形の内側ならそのまま
 //多角形の外にでたら最も近い外周上の1点を返す
 if(PointInCircle(center,radius,v)){return v;}
 else{return NearestOnCircumference(center,radius,v);}
}

//■矩形
//矩形外周上
//■Rect Circumference
PVector NearestOnCircumference(PVector ltv, float w, float h, PVector v)
{
 ArrayList<PVector> rect = new ArrayList<PVector>();
 rect.add(new PVector(ltv.x,  ltv.y));
 rect.add(new PVector(ltv.x+w,ltv.y));
 rect.add(new PVector(ltv.x+w,ltv.y+h));
 rect.add(new PVector(ltv.x,  ltv.y+h));

 PVector near = NearestOnSegmentsClose(rect,v);
 return near;
}

//領域に対して最も近い点
PVector NearestOnRect(PVector ltv, float w, float h, PVector v)
{
 //多角形の内側ならそのまま
 //多角形の外にでたら最も近い外周上の1点を返す
 if(PointInRect(ltv,w,h,v)){return v;}
 else{return NearestOnCircumference(ltv,w,h,v);}
}

//■Tetragon Circumference
PVector NearestOnCircumference(PVector ltv, PVector vx, PVector vy, PVector v)
{
 ArrayList<PVector> tetragon = new ArrayList<PVector>();
 tetragon.add(ltv.copy());
 tetragon.add(PVector.add(ltv,vx));
 tetragon.add(PVector.add(ltv,vx).add(vy));
 tetragon.add(PVector.add(ltv,vy));

 PVector near = NearestOnSegmentsClose(tetragon,v);
 return near;
}

//領域に対して最も近い点
PVector NearestOnTetragon(PVector ltv, PVector vx, PVector vy, PVector v)
{
 //多角形の内側ならそのまま
 //多角形の外にでたら最も近い外周上の1点を返す
 if(PointInTetragon(ltv,vx,vy,v)){return v;}
 else{return NearestOnCircumference(ltv,vx,vy,v);}
}

int HitIndex(ArrayList<PVector> vectors, float radius, PVector mouse_xy)
{
 int ret_index = -1;
 for(int i = 0; i<vectors.size(); i++)
 {
   PVector v = vectors.get(i);
   if(PointInCircle(v,radius,mouse_xy))
   {
     ret_index=i;
     return ret_index;
   }
 }  
 return ret_index;
}

//円周上を含まないPointIn
boolean PointInCircle(PVector center, float radius, PVector v)
{
 float dx = v.x-center.x;
 float dy = v.y-center.y;
 if(dx*dx+dy*dy<radius*radius){return true;}
 return false;
}

boolean PointInRect(PVector ltv, float w, float h, PVector v)
{
 if(ltv.x<v.x&&v.x<ltv.x+w)
 {
   if(ltv.y<v.y&&v.y<ltv.y+h)
   {
     return true;
   }
 }
 return false;
}

boolean PointInTetragon(PVector ltv, PVector vx, PVector vy, PVector v)
{
 ArrayList<PVector> tetragon = new ArrayList<PVector>();
 tetragon.add(ltv.copy());
 tetragon.add(PVector.add(ltv,vx));
 tetragon.add(PVector.add(ltv,vx).add(vy));
 tetragon.add(PVector.add(ltv,vy));
 
 return CrossingNumber(tetragon,v);
}


//凸のみPointInPolygonの部品
boolean PIP_IsRight(PVector sv, PVector ev, PVector p)
{
 PVector lv = ev.copy().sub(sv);
 PVector p_ = p.copy().sub(sv);  
 if(0<Cross(lv,p_)){return true;}
 return false;
}

//凸のみPointInPolygon
boolean PointInPolygon(ArrayList<PVector> vertex, PVector p)
{
 //最初の一回
 boolean is_right = PIP_IsRight(vertex.get(0),vertex.get(1),p);
 //中間
 for(int i = 1; i<vertex.size(); i++)
 {
   //向き変わった
   if(is_right!=PIP_IsRight(vertex.get(i),vertex.get((i+1)%vertex.size()),p))
   {
     return false;
   }    
 }
 return true;
}

boolean PointInPolygon_origin(ArrayList<PVector> vertex, PVector p)
{  
 //最初の一回
 PVector sv = vertex.get(0).copy();
 PVector ev = vertex.get(1).copy();
 PVector lv = ev.sub(sv);
 PVector p_ = p.copy().sub(sv);  
 //右である
 boolean is_right = false;
 if(0<Cross(lv,p_)){is_right=true;}
 
 //中間
 for(int i = 1; i<vertex.size()-1; i++)
 {
   sv = vertex.get(i).copy();
   ev = vertex.get(i+1).copy();
   lv = ev.sub(sv);
   p_ = p.copy().sub(sv);
   if(0<Cross(lv,p_))
   {
     //向き変わった
      if(is_right==false){return false;}
   }
   else
   {
     //向き変わった
     return false;
   }
 }
 
 //最後の一回
 sv = vertex.get(vertex.size()-1).copy();
 ev = vertex.get(0).copy();
 lv = ev.sub(sv);
 p_ = p.copy().sub(sv);
 if(0<Cross(lv,p_))
 {
   //向き変わった
    if(is_right==false){return false;}
 }
 else
 {
   //向き変わった
   return false;
 }  
 
 return true;
}


//参考:https://www.nttpc.co.jp/technology/number_algorithm.html
boolean CrossingNumber(ArrayList<PVector> vertex, PVector p)
{
 int count = 0;
 for(int i = 0; i < vertex.size(); i++)
 {
   PVector sv = vertex.get(i);
   PVector ev = vertex.get((i+1)%vertex.size());  
   //PVector lv = PVector.sub(ev,sv);
   //PVector toP = PVector.sub(p,sv);
   
   //ルール1,2
   //上向き(sv.y<ev.y 終点含まず、始点ならカウント候補)||下向き(sv.y>ev.y 始点含まず、終点ならカウント候補)
   //ルール3
   //sv.y==ev.yはカウントしない
   if((sv.y <= p.y && p.y < ev.y)||(sv.y > p.y && p.y >= ev.y))
   {
     //ルール4
     //辺は点pよりも絶対座標で右側にあり、辺上ではない
     //点pは辺よりも絶対座標で左側にあり、辺上ではない
     //点p.xと交点.xの比較
     float t = (p.y - sv.y) / (ev.y - sv.y);
     if(p.x < (sv.x + (t * (ev.x - sv.x))))
     {
         count++;
     }
     
     //交点が辺の左
     //外積計算は相対位置で左右を取るため成り立たない
     //上向きの時に入力点を左を取り、下向きの時は入力点を右を取らなければならない
     //というよりも外積計算で左右とったら凸多角形の内外判定と変わらない
     //if(0>Cross(lv,toP)){count++;}
     
   }//if
 }//for
 
 if(count%2==1){return true;}
 return false;
}//CrossingNumber



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