ノードン的なものを作るドン!その001


画像1

必要なこと

・(InputOutputValue) : ノード同士が単一の、あるいは複数の、及び組み合わされたパラメータをやり取りする。
・(InputPort) : ノードが受け取ったパラメータはノードによって解釈され、変更され、修正される。
・(Node) : ノードはパラメータを保持したり反映したり、あるいはGUI(Graphigs User Interface)や図形などの表現により表示したりするし、ユーザーの操作に対して応答したりすらする。また、しなかったりもする。
・(OutputPort) : 他のノードやユーザーの操作の影響により、ノードの保持するパラメータや他のノードなどから集められたパラメータはまとめられ、解釈され、組み合わせられて、最終的に出力される。その値はまた次のノードに渡る。

解釈だの組み合わせだのとはなにか

例えば3つほど並んだパラメータ(x,y,z)なるものがあるとして、我々はそれを(R,G,B)の色と解釈することもできれば3次のベクトルと解釈することもできるし、((x,y),r)なる2次のベクトルと半径からなる円と解釈することも可能である。つまり勝手にすることができる。

そうした時、適当に受け渡されるパラメータをベクトルだの行列だのテンソルだのそれらの配列だのリストだのと組み合わせる方法と、
組み合わされたパラメータを最終的にどのように利用するのかのやり方(例えば(R,G,B)は(H,S,B)にもなりうるし、((x,y),r)は円にもなれば正方形にもなる)。こういうのがここでいう解釈だの組み合わせだのである。


ノードンがやり取りする値の一例。
全て使う気もないのでちゃんと作ってはいない。また、意味が同じものもある。足らなければまた足す。つど足す。

プログラム的なパラメータの組み合わせなどというものは考えれば考えるほど無限に湧いてくるため、適当に書き下すと

bool,u_int,int,float,double
Vector1D(x),Vector2D(x,y),Vector3D(x,y,z),Vector4D(x,y,z,w),VectorN(x...)
double[], double[,], double[,,],double[,,,]
Vector1D[],Vector2D[],Vector3D[],Vector4D[]
List<double>,List<List<double>>
List<Vector1D>,List<Vector2D>,List<Vector3D>,List<Vector4D>
Matrix(m,n,object)

この中から個人的に良く使うもの、環境的(processing的)に使わざるを得ないものを見繕うと
Index(u_int),int, float,
PVector
List<PVector>,List<List<PVector>>

くらいであろうことから下書き程度にコードにすると


interface IOValue
{
 void set(IOValue value);
}

class int1 implements IOValue
{
 int x;
 int1(int x_){x=x_;}
 
 void set(IOValue value)
 {
   if(!(value instanceof int1)){return;}
   this.x=((int1)value).x;
 }
}

//Vector2D
class int2 implements IOValue
{
 int x;int y;
 int2(int x_, int y_){x=x_;y=y_;}  
 
 void set(IOValue value)
 {
   if(!(value instanceof int2)){return;}
   this.x=((int2)value).x;
   this.y=((int2)value).y;
 }  
}

//Vector3D
class int3 implements IOValue
{
 int x;int y;int z;
 int3(int x_, int y_, int z_){x=x_;y=y_;z=z_;}    
 
 void set(IOValue value)
 {
   if(!(value instanceof int3)){return;}
   this.x=((int3)value).x;
   this.y=((int3)value).y;
   this.z=((int3)value).z;
 }    
}

//Vector4D
class int4 implements IOValue
{
 int x;int y;int z;int w;
 int4(int x_, int y_, int z_, int w_){x=x_;y=y_;z=z_;w=w_;}   
 
 void set(IOValue value)
 {
   if(!(value instanceof int4)){return;}
   this.x=((int4)value).x;
   this.y=((int4)value).y;
   this.z=((int4)value).z;
   this.w=((int4)value).w; 
 }     
}

class float1 implements IOValue
{
 float x;
 float1(float x_){x=x_;}  
 void set(IOValue value)
 {
   if(!(value instanceof float1)){return;}
   this.x=((float1)value).x;
 }  
}

//Vector2D
class float2 implements IOValue
{
 float x;float y;
 float2(float x_, float y_){x=x_;y=y_;}   
 void set(IOValue value)
 {
   if(!(value instanceof float2)){return;}
   this.x=((float2)value).x;
   this.y=((float2)value).y;
 }  
}

//Vector3D
class float3 implements IOValue
{
 float x;float y;float z;
 float3(float x_, float y_, float z_){x=x_;y=y_;z=z_;}    
 void set(IOValue value)
 {
   if(!(value instanceof float3)){return;}
   this.x=((float3)value).x;
   this.y=((float3)value).y;
   this.z=((float3)value).z;
 }  
}

//Vector4D
class float4 implements IOValue
{
 float x;float y;float z;float w;
 float4(float x_, float y_, float z_, float w_){x=x_;y=y_;z=z_;w=w_;}  
 void set(IOValue value)
 {
   if(!(value instanceof float4)){return;}
   this.x=((float4)value).x;
   this.y=((float4)value).y;
   this.z=((float4)value).z;
   this.w=((float4)value).w;    
 }      
}

//Matrix2*2
class float22 implements IOValue
{
 void set(IOValue value){}
}

//Matrix3*3
class float33 implements IOValue
{
   void set(IOValue value){}
}

//Matrix4*4
class float44 implements IOValue
{
   void set(IOValue value){}
}

//List<Vector2>
class Vector2N implements IOValue
{
   void set(IOValue value){}
}

//List<Vector3>
class Vector3N implements IOValue
{
   void set(IOValue value){}
}

//List<PVector>
class VectorPN implements IOValue
{
 ArrayList<PVector>values;
 VectorPN(PVector... values_)
 {
   values = new ArrayList<PVector>(Arrays.asList(values_));
 }
 void set(IOValue value)
 {
   if(!(value instanceof VectorPN)){return;}
   this.values=((VectorPN)value).values;
 }   
}


ノードンはユーザーと応答できて、かつ自身を表示する能力を備えるといういみでGUIであることが想定される。

また、パラメータをノードン同士でやり取りするという意味でNodeとしての能力を備える。

interface INodeGUI extends INode, IGUIObject{}
class NodeObject implements INodeGUI
{
 void draw(){}//IGUIObject
 
 boolean Hit(float x, float y){return false;}//IGUIObject
 
 int HitIndex(float x, float y){return -1;}//INode  
 IOPort GetPort(int index){return null;}//INode
 PVector GetPortPos(int index){return null;}//INode
 int GetPortIndex(IOPort port){return -1;}//INode
 
 void Translate(PVector dv){}//IGUIObject
 
 void mousePressed(){}//IGUIObject
 void mouseReleased(){}//IGUIObject
 void mouseDragged(){}  //IGUIObject
}


このなかでGUIとしての能力は以下の感じである。

void draw(){}//描画する能力
boolean Hit(float x, float y){return false;}//クリックしたかどうかの判定
void Translate(PVector dv){}//GUIの移動
void mousePressed(){}//processingのマウスイベント
void mouseReleased(){}//processingのマウスイベント
void mouseDragged(){} //processingのマウスイベント

ノードとしての能力は

interface INode
{
 int HitIndex(float x, float y);  
 IOPort GetPort(int index);
 PVector GetPortPos(int index);
 int GetPortIndex(IOPort port);
}

これらの機能は必須とは限らないし、ここで所持するべきかも定かではない。

とりあえずでおいてある機能である。

また、GUIがNodeを所持(has)した方がしっくりくる人もいるであろう。
ObjectなるクラスがGUIとNodeをhasしたいという人もいるであろう。好きにして良い。

class NodeObject implements IGUIObject
{
 INode Node;

 void draw(){}//IGUIObject
 
 boolean Hit(float x, float y){return false;}//IGUIObject 
 void Translate(PVector dv){}//IGUIObject 
 void mousePressed(){}//IGUIObject
 void mouseReleased(){}//IGUIObject
 void mouseDragged(){}  //IGUIObject
}
class NodeObject implements IObject
{
 INode Node;
 IGUI GUI;
}


Inputしかしないノードン、Outputしかしないノードン、InputOutputするノードンに分けることを試みる。それが本当に必要であるかはまだ分からない。

abstract class IONodeBase extends NodeObject 
    implements HasInputPorts, HasOutputPorts, IPullable
{
 void Pull()
 {
   for(IInputPort p : this.GetInputPorts())
   {
     if(p == null){continue;}
     if(!(p instanceof IPullable)){continue;}      
     ((IPullable)p).Pull();
   }    
 }     
}

abstract class OutputNodeBase extends NodeObject implements HasOutputPorts
{
   
}

abstract class InputNodeBase extends NodeObject implements HasInputPorts, IPullable
{
 void Pull()
 {
   for(IInputPort p : this.GetInputPorts())
   {
     if(p == null){continue;}
     if(!(p instanceof IPullable)){continue;}      
     ((IPullable)p).Pull();
   }    
 }      
}


具体的なノードンを考える。1つ目はスライダーバーを動かしてfloatを出力のみするノードン。

画像2

2つ目はスライダーバーからの出力を受けることを想定した円。

画像3





//単一のOutputPortを持つ
class SliderNode extends OutputNodeBase
{
 PVector pos;
 float w = 100;
 float h = 100; 
 
 //this GUI内部のHitエリア判定
 int current_index = -1; 
 
 String value_name = "Value Name";
 float min = 0;
 float max = 100;  
 float GetRange(){return max-min;}
 float GetValue(){return GetRange()*t;}
 void SetValue(float value)
 {
   t = value/GetRange();
   if(t<0){t=0;}else if(1<t){t=1;}
 }
 float t = 0;
   
 PVector GetNamePos(){return GetClientPosAbs();}  
 void drawName()
 {
   fill(0);
   text(value_name,GetNamePos().x,GetNamePos().y);
   fill(DefaultFillColor);
 }
 PVector GetValuePos(){return new PVector(pos.x, GetFooter2TopAbs());}
 void drawValue()
 {
   fill(0);
   text("Value : "+str(GetValue()),GetValuePos().x,GetValuePos().y);
   fill(DefaultFillColor);
 }  
 
 PVector GetMinPos(){return new PVector(pos.x, GetFooter2BottomAbs());}
 void drawMin()
 {
   fill(0);
   text("Min : "+str(min),GetMinPos().x,GetMinPos().y);
   fill(DefaultFillColor);
 }  
 PVector GetMaxPos(){return new PVector(pos.x, GetFooter1BottomAbs());}
 void drawMax()
 {
   fill(0);
   text("Max : "+str(max),GetMaxPos().x,GetMaxPos().y);
   fill(DefaultFillColor);
 }  
 
   
   
 //float1 Output;
 IOutputPort OutputPort;
 ArrayList<IOutputPort> GetOutputPorts(){return new ArrayList<IOutputPort>(Arrays.asList(this.OutputPort));}  
 void port_setup(){OutputPort = new float1OutputPort(this, new SliderOutputMethod());}  
     
 float port_w = 10;
 float port_h = 10;  
 int port_index = 200;
 PVector GetPortPos(){return new PVector(pos.x+w-port_w, pos.y+header_h);}
 boolean HitPort(float x, float y){return(GetPortPos().x<x&&x<GetPortPos().x+port_w&&GetPortPos().y<y&&y<GetPortPos().y+port_h);}
 void drawPort(){rect(GetPortPos().x, GetPortPos().y, port_w, port_h);}
 
 //Header
 float header_h = 10;  
 int header_index = 1;
 boolean HitHeader(float x, float y){if(pos.x<x&&x<pos.x+w&&GetHeaderTopAbs()<y&&y<GetHeaderBottomAbs()){return true;}return false;}
 float GetHeaderTopAbs(){return pos.y;}  
 float GetHeaderBottomAbs(){return pos.y+header_h;}
 
 //Footer1
 float footer1_h = 10;//下から上へ
 int footer1_index = 2;
 boolean HitFooter1(float x, float y){if(pos.x<x&&x<pos.x+w&&GetFooter1TopAbs()<y&&y<GetFooter1BottomAbs()){return true;}return false;}
 float GetFooter1TopAbs(){return pos.y+h-footer1_h;}
 float GetFooter1BottomAbs(){return pos.y+h;}
 
 //Footer2
 float footer2_h = 10;//下から上へ,2つめ
 int footer2_index = 3;
 boolean HitFooter2(float x, float y){if(pos.x<x&&x<pos.x+w&&GetFooter2TopAbs()<y&&y<GetFooter2BottomAbs()){return true;}return false;}
 float GetFooter2TopAbs(){return pos.y+h-footer1_h-footer2_h;}
 float GetFooter2BottomAbs(){return pos.y+h-footer1_h;}
 
 //ClientArea
 PVector GetClientPosAbs(){return new PVector(pos.x,pos.y+header_h);}
 float GetClientAreaH(){return this.h-header_h-footer1_h;}
 

   
 int slider_button1_index = 100;
 float slider_button_width_ratio = 0.1;//slider_lvとの比
 float slider_button_height_ratio = 0.2;
 
 //Slider
 PVector GetSliderSVAbs(){return GetClientPosAbs().add(new PVector(0,GetClientAreaH()/2));}
 PVector GetSliderEVAbs(){return GetClientPosAbs().add(new PVector(w,GetClientAreaH()/2));}
 PVector GetSliderLV(){return GetSliderEVAbs().sub(GetSliderSVAbs());}
 PVector GetSliderPos(){return GetSliderSVAbs().add(GetSliderLV().mult(t));}
 PVector GetSliderLTV(){return new PVector(GetSliderPos().x-GetSliderButtonWidth()/2,GetSliderPos().y-GetSliderButtonHeight()/2);}  
 
 float GetSliderButtonWidth(){return GetSliderLV().mag()*slider_button_width_ratio;}
 float GetSliderButtonHeight(){return GetSliderLV().mag()*slider_button_height_ratio;}
 
 boolean HitSliderButton(float x, float y)
 {
   if(GetSliderLTV().x<x&&x<GetSliderLTV().x+GetSliderButtonWidth())
   {
     if(GetSliderLTV().y<y&&y<GetSliderLTV().y+GetSliderButtonHeight())
     {
       return true;
     }
   }
   return false;
 }
 
 void drawButton()
 {
   rectMode(CENTER);
   rect(GetSliderPos().x, GetSliderPos().y, GetSliderButtonWidth(), GetSliderButtonHeight());
   rectMode(CORNER);
 }

 
 
 //コンストラクタ
 SliderNode(String name_, float x_, float y_, float min_, float max_, float value_)
 {
   value_name = name_;    
   this.pos = new PVector(x_,y_);
   min=min_;max=max_;
   SetValue(value_);
   
   port_setup();
 }
 SliderNode(float x, float y)
 {
   this.pos = new PVector(x,y);    
   port_setup();
 }
 SliderNode(PVector v)
 {
   this.pos.set(v);
   port_setup();
 }
 
 @Override
 void draw()
 {
   rect(pos.x,pos.y,w,h);
   drawName();
   drawValue();
   drawMin();
   drawMax();
   
   //header, footer
   line(pos.x,GetHeaderBottomAbs(),pos.x+w,GetHeaderBottomAbs());
   line(pos.x,GetFooter1TopAbs(),pos.x+w,GetFooter1TopAbs());
   line(pos.x,GetFooter2TopAbs(),pos.x+w,GetFooter2TopAbs());    
   line(GetSliderSVAbs().x,GetSliderSVAbs().y,GetSliderEVAbs().x,GetSliderEVAbs().y);
   
   drawButton();
   drawPort();    
 }
   
 @Override
 boolean Hit(float x, float y)
 {
   if(pos.x<x&&x<pos.x+w&&pos.y<y&&y<pos.y+h)
   {
     if(HitHeader(x,y)){current_index=header_index;}
     if(HitFooter1(x,y)){current_index=footer1_index;}
     if(HitSliderButton(x,y)){current_index=slider_button1_index;}
     if(HitPort(x,y)){current_index=port_index;}
     return true;
   }
   return false;
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(HitHeader(x,y)){return header_index;}
   if(HitFooter1(x,y)){return footer1_index;}
   if(HitSliderButton(x,y)){return slider_button1_index;}    
   if(HitPort(x,y)){return port_index;}
   return -1;
 }  
 
 @Override
 IOPort GetPort(int index)
 {
   if(index==port_index){return OutputPort;}
   return null;
 }
 
 @Override
 PVector GetPortPos(int index)
 {
   if(index==port_index){return GetPortPos();}
   return null;    
 }
 
 @Override
 int GetPortIndex(IOPort port)
 {
   if(OutputPort==port){return port_index;}
   return -1;
 }
   
 @Override
 void mouseDragged()
 {
   if(current_index<0){return;}
   if(current_index==header_index){this.pos.add(dv);}
   
   //if(current_index==footer_index){this.pos.add(dv);}    
   if(current_index==slider_button1_index)
   {
     t+=dv.x/GetSliderLV().mag();    
     if(t<0){t=0;}else if(1<t){t=1;}
   }
   
   if(current_index==port_index)
   {
     line(GetPortPos().x,GetPortPos().y,mouseX,mouseY);
   }
 }
 
 @Override
 void mouseReleased()
 {
   current_index = -1;
   
   for(IGUIObject gui : guis)
   {
     if(!(gui instanceof INode)){continue;}
     
     INode node = (INode)gui;
     
     int index = node.HitIndex(mouseX,mouseY);
     IOPort port = node.GetPort(index);
     if(port!=null)
     {
       //portに接触した
       //portの接続
       Connect(this.OutputPort, port);
     }
   }
 }  
}





class CircleNode extends IONodeBase
{     
 void Input(int index, IOValue value)
 {
   if(index==port0_index){this.InputPort0.Input(value);}
   if(index==port1_index){this.InputPort1.Input(value);}    
 }
 
 void port_setup()
 {
   InputPort0 = new float2InputPort(this, this.Center);
   InputPort1 = new float1InputPort(this, this.Radius);
 }  
 
 ArrayList<IInputPort> GetInputPorts(){return new ArrayList<IInputPort>(Arrays.asList(InputPort0,InputPort1));}
 ArrayList<IOutputPort> GetOutputPorts(){return null;}
 
 //Port
 IInputPort InputPort0;
 IInputPort InputPort1;  
 
 //値
 float2 Center;
 float1 Radius;
 
 float port_w = 10;
 float port_h = 10;

 int port0_index = 200;//center  
 int port1_index = 201;//radius
 
 PVector GetPort1Pos()
 {
   return new PVector(Center.x, Center.y);
 }
 
 boolean HitPort(float x, float y){return(GetPort1Pos().x<x&&x<GetPort1Pos().x+port_w&&GetPort1Pos().y<y&&y<GetPort1Pos().y+port_h);}
 void drawPort()
 {    
   rect(GetPort1Pos().x, GetPort1Pos().y, port_w, port_h);    
 }    
 
 //コンストラクタ
 CircleNode(float x, float y, float r)
 {
   this.Center = new float2(x,y);
   this.Radius = new float1(r);
   port_setup();
 }
 
 @Override
 void draw()
 {
   circle(this.Center.x,Center.y,Radius.x);    
   drawPort();
   
   if(this.InputPort1 instanceof HasPairPort)
   {
     HasPairPort inputport = ((HasPairPort)InputPort1);
     if(inputport.GetPairPort()!=null)
     {
         int index = inputport.GetPairPort().GetNode().GetPortIndex(inputport.GetPairPort());
         PVector pos = inputport.GetPairPort().GetNode().GetPortPos(index);
         
         line(pos.x,pos.y,GetPort1Pos().x,GetPort1Pos().y); 
     }
   }
 }
 
 @Override
 boolean Hit(float x, float y)
 {
   return HitPort(x,y);
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(HitPort(x,y)){return port1_index;}
   return -1;
 }
 
 @Override
 IOPort GetPort(int index)
 {
   if(index==port1_index)
   {
     return InputPort1;
   }
   return null;
 }

 @Override
 PVector GetPortPos(int index)
 {
   if(index==port1_index){return GetPort1Pos();}
   return null;
 }
 @Override
 int GetPortIndex(IOPort port)
 {
   if(InputPort0==port){return port0_index;}
   if(InputPort1==port){return port1_index;}
   return -1;
 }
 
}


長いが、ほとんどはGUIの機能である。processingのイベントから始まり、各ノードンの対応するイベントに伝わる。ノードンをクリックしたらそのノードンのIDを記録し、現在操作中のノードンであると認識する。ノードンのパーツをクリックしたら、ノードン内部のIDでパーツに対する操作であると認識する。


ノード同士のパラメータ伝達機能の中枢はPortクラスとする。このクラスは各ノードに1つないし複数所持される。それはInput用であったりOutput用であったりする。


interface HasInputPorts
{
 ArrayList<IInputPort> GetInputPorts();
}

interface HasOutputPorts
{
 ArrayList<IOutputPort> GetOutputPorts(); 
}

interface IOPort
{
 INode GetNode();
}

interface IOutputPort extends IOPort
{
 IOValue Output();
}

interface IInputPort extends IOPort
{
 void Input(IOValue value);
}

NodeはPortを所持するし、PortはIOValueを受けたり出力したりする関数を持つ。IOValueの本体はNodeに所持されるが、InputPortは入力IOValueを編集ないし修正して、指定された(Nodeの所持する)標的IOValueに格納する。

class InputPortBase implements IInputPort,IPullable,HasPairPort
{  
 INode GetNode(){return Node;}
 //Portを保持するノード
 INode Node;
 
 //値
 IOValue Target;
 
 void SetPairPort(IOPort output_port)
 {
   if(output_port instanceof IOutputPort)
   {
     this.PairOutputPort=(IOutputPort)output_port;
   }
 }
 
 IOPort GetPairPort()
 {
   return PairOutputPort; 
 }
 
 //thisに接続しているPort, 辿る用
 IOutputPort PairOutputPort;
 //IOutputPort GetOutputPort(){return OutputPort;}
 void Pull()
 {
   if(this.PairOutputPort!=null)
   {
      if(this.PairOutputPort instanceof IPullable)
     {
       ((IPullable)this.PairOutputPort).Pull();
     }      
     this.Input(PairOutputPort.Output());
   }    
 }
 
 //パラメータコピー
 void Input(IOValue value)
 {
   if(Target!=null)
   {
     Target.set(value);
   }    
 }  
}

InputPortの本体はInput関数であり、Defaultでは入力IOValueの値をそのまんまコピー。

このへんは適当なIOValueを受け入れて、関数内で振り分けてもいいし修正してもいいが、ここでは例えば指定したクラス以外は取り合わないような作りにしている。

class float2InputPort extends InputPortBase
{
 //float2 
 //IOValue target;

 //コンストラクタ(参照とりこみ)
 float2InputPort(INode node, IOValue target_)
 {    
   if(!(target_ instanceof float2)){return;}   
   this.Target=(float2)target_;
   
   this.Node = node;
 }
}

class float1InputPort extends InputPortBase
{
 float1InputPort(INode node, IOValue target_)
 {
   if(!(target_ instanceof float1)){return;}   
   this.Target=(float1)target_;    
   
   this.Node = node;
 }
}

OutputPortの方には出力時にそれなりに出力値を編集できる関数を所持、使用できるようにした。このやり方はInputPortでも同じように用いても良い。




abstract class OutputPortBase implements IOutputPort,IPullable
{
 INode GetNode(){return Node;}
 //Portを保持するノード
 INode Node;
 
 IOutputMethod OutputMethod;
 
 //全てのInputPort統合->Node->OutputPort
 void Pull()
 {
   if(this.Node instanceof IPullable)
   {
     ((IPullable)this.Node).Pull();
   }
 }   
}

class float1OutputPort extends OutputPortBase
{
 float1OutputPort(INode node, IOutputMethod output_method)
 {
   this.Node = node;
   this.OutputMethod = output_method;
 }
 
 IOValue Output(){return this.OutputMethod.Output(this.Node);}
}

いわゆるデリゲートや関数ポインタに相当する。

interface IOutputMethod
{
 IOValue Output(INode node);
}

class SliderOutputMethod implements IOutputMethod
{
 IOValue Output(INode node)
 {
   if(!(node instanceof SliderNode)){return null;}
   SliderNode sn = (SliderNode)node;
   return new float1(sn.GetValue());
 } 
}


機能は作ったので実際に値が反映されるようにする。その実現方法はいくらでもあろうが、

その実現方法はいくらでもあろうが、

ここではPullなんちゃら系の関数がそれを担っている。あるノードンないしI/Oポートの現在の値が、他のノードンないしI/Oポートからの出力によって構築されている場合、Pull関数の実現により現在の値が更新、反映される。

InputPortのPullは、自身が対応するOutputPortを呼出し、Pull可能ならPullしてから、その出力値をInputしてNodeに格納する。

NodeのPullは全てのInputPortのPullを実行する。

OutputPortのPullはNodeのPullを実行する。

すなわち知りたい値から始まり、その値を計算するために必要な値を逆順に辿っていく形である。

データ構造はPortの繋ぎ方に依存し、値の評価の順番はPortの格納方法なりPull関数の内容による。

つづく。

コピペ用コード


import java.util.Arrays;

float DefaultFillColor = 255;
float DefaultStrokeColor = 0;
float DefaultStrokeWeight = 1;

CircleNode cn;
SliderNode pn;
void setup()
{
 size(500,500);  
 
 pn = new SliderNode(100,100);  
 cn = new CircleNode(250,250,100);
 
 this.guis.add(pn);
 this.guis.add(cn);
}

void draw()
{
 background(255);
 smooth();
 
 //noFill();

 cn.Pull();
 pn.draw();
 cn.draw();
}

ArrayList<IGUIObject> guis = new ArrayList<IGUIObject>(); 

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


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

void mouseDragged()
{
 if(current==null){return;}
 if(prev!=null){pprev = prev.copy();}
 
 prev = current.copy();
 current.x = mouseX;
 current.y = mouseY;  
 dv = PVector.sub(current,prev);
   
 if(0<=current_gui_index)
 {
     IGUIObject gui = this.guis.get(current_gui_index); 
     gui.mouseDragged(); 
 }
}

void mouseReleased()
{
 
 if(0<=current_gui_index)
 {
     IGUIObject gui = this.guis.get(current_gui_index); 
     gui.mouseReleased();
 }
 
 pprev = null;
 prev = null;
 current = null;  
 current_gui_index=-1;  
}


//Event
interface HasEventInterface{IEventInterface GetEventInterface();}
interface IEventInterface extends IClickable,IDraggable,IMouseMoved,IKeyInputtable, IHit, IOverlap, ITranslate, IMove{}
interface IClickable{void mousePressed(); void mouseReleased();}
interface IDraggable{void mouseDragged();}
interface IMouseMoved{void mouseMoved();}
interface IKeyInputtable{void keyPressed(); void keyReleased();}

interface IUniversalObject{}

interface IDrawable{void draw();}
interface IDrawableXY{void draw(float x, float y);}
interface IDrawablePG{void draw(PGraphics pg);}

interface IHit{boolean Hit(float x, float y);}
interface IOverlap{boolean IsOverlap(float x, float y);}
interface IMove{void Move(float x, float  y);}
interface ITranslate{void Translate(PVector dv);}

interface IGUIObject extends 
 IUniversalObject, IDrawable, IHit, ITranslate, IClickable, IDraggable{}//GUI系統


interface INode
{
 int HitIndex(float x, float y);  
 IOPort GetPort(int index);
 PVector GetPortPos(int index);
 int GetPortIndex(IOPort port);
}

interface INodeGUI extends INode, IGUIObject{}

class NodeObject implements INodeGUI
{
 void draw(){}//IGUIObject
 
 boolean Hit(float x, float y){return false;}//IGUIObject
 
 int HitIndex(float x, float y){return -1;}//INode  
 IOPort GetPort(int index){return null;}//INode
 PVector GetPortPos(int index){return null;}//INode
 int GetPortIndex(IOPort port){return -1;}//INode
 
 void Translate(PVector dv){}//IGUIObject
 
 void mousePressed(){}//IGUIObject
 void mouseReleased(){}//IGUIObject
 void mouseDragged(){}  //IGUIObject
}

abstract class IONodeBase extends NodeObject implements HasInputPorts, HasOutputPorts, IPullable
{
 void Pull()
 {
   for(IInputPort p : this.GetInputPorts())
   {
     if(p == null){continue;}
     if(!(p instanceof IPullable)){continue;}      
     ((IPullable)p).Pull();
   }    
 }     
}

abstract class OutputNodeBase extends NodeObject implements HasOutputPorts
{
   
}

abstract class InputNodeBase extends NodeObject implements HasInputPorts, IPullable
{
 void Pull()
 {
   for(IInputPort p : this.GetInputPorts())
   {
     if(p == null){continue;}
     if(!(p instanceof IPullable)){continue;}      
     ((IPullable)p).Pull();
   }    
 }      
}

interface IOValue
{
 void set(IOValue value);
}

class int1 implements IOValue
{
 int x;
 int1(int x_){x=x_;}
 
 void set(IOValue value)
 {
   if(!(value instanceof int1)){return;}
   this.x=((int1)value).x;
 }
}

//Vector2D
class int2 implements IOValue
{
 int x;int y;
 int2(int x_, int y_){x=x_;y=y_;}  
 
 void set(IOValue value)
 {
   if(!(value instanceof int2)){return;}
   this.x=((int2)value).x;
   this.y=((int2)value).y;
 }  
}

//Vector3D
class int3 implements IOValue
{
 int x;int y;int z;
 int3(int x_, int y_, int z_){x=x_;y=y_;z=z_;}    
 
 void set(IOValue value)
 {
   if(!(value instanceof int3)){return;}
   this.x=((int3)value).x;
   this.y=((int3)value).y;
   this.z=((int3)value).z;
 }    
}

//Vector4D
class int4 implements IOValue
{
 int x;int y;int z;int w;
 int4(int x_, int y_, int z_, int w_){x=x_;y=y_;z=z_;w=w_;}   
 
 void set(IOValue value)
 {
   if(!(value instanceof int4)){return;}
   this.x=((int4)value).x;
   this.y=((int4)value).y;
   this.z=((int4)value).z;
   this.w=((int4)value).w; 
 }     
}

class float1 implements IOValue
{
 float x;
 float1(float x_){x=x_;}  
 void set(IOValue value)
 {
   if(!(value instanceof float1)){return;}
   this.x=((float1)value).x;
 }  
}

//Vector2D
class float2 implements IOValue
{
 float x;float y;
 float2(float x_, float y_){x=x_;y=y_;}   
 void set(IOValue value)
 {
   if(!(value instanceof float2)){return;}
   this.x=((float2)value).x;
   this.y=((float2)value).y;
 }  
}

//Vector3D
class float3 implements IOValue
{
 float x;float y;float z;
 float3(float x_, float y_, float z_){x=x_;y=y_;z=z_;}    
 void set(IOValue value)
 {
   if(!(value instanceof float3)){return;}
   this.x=((float3)value).x;
   this.y=((float3)value).y;
   this.z=((float3)value).z;
 }  
}

//Vector4D
class float4 implements IOValue
{
 float x;float y;float z;float w;
 float4(float x_, float y_, float z_, float w_){x=x_;y=y_;z=z_;w=w_;}  
 void set(IOValue value)
 {
   if(!(value instanceof float4)){return;}
   this.x=((float4)value).x;
   this.y=((float4)value).y;
   this.z=((float4)value).z;
   this.w=((float4)value).w;    
 }      
}

//Matrix2*2
class float22 implements IOValue
{
 void set(IOValue value){}
}

//Matrix3*3
class float33 implements IOValue
{
   void set(IOValue value){}
}

//Matrix4*4
class float44 implements IOValue
{
   void set(IOValue value){}
}

//List<Vector2>
class Vector2N implements IOValue
{
   void set(IOValue value){}
}

//List<Vector3>
class Vector3N implements IOValue
{
   void set(IOValue value){}
}

//List<PVector>
class VectorPN implements IOValue
{
 ArrayList<PVector>values;
 VectorPN(PVector... values_)
 {
   values = new ArrayList<PVector>(Arrays.asList(values_));
 }
 void set(IOValue value)
 {
   if(!(value instanceof VectorPN)){return;}
   this.values=((VectorPN)value).values;
 }   
}


void Connect(IOPort p1, IOPort p2)
{
 if(p1 instanceof IInputPort&&p2 instanceof IOutputPort)
 {
   Connect((IInputPort)p1,(IOutputPort)p2);
 }
 if(p1 instanceof IOutputPort&&p2 instanceof IInputPort)
 {
   Connect((IInputPort)p2,(IOutputPort)p1);
 }
}

void Connect(IInputPort input, IOutputPort output)
{
 if(input instanceof HasPairPort)
 {
   ((HasPairPort)input).SetPairPort(output);
 }
 if(output instanceof HasPairPort)
 {
   ((HasPairPort)output).SetPairPort(input);
 }
}

interface HasInputPorts
{
 ArrayList<IInputPort> GetInputPorts();
}

interface HasOutputPorts
{
 ArrayList<IOutputPort> GetOutputPorts(); 
}

interface IOPort
{
 INode GetNode();
}

interface IOutputPort extends IOPort
{
 IOValue Output();
}

interface IInputPort extends IOPort
{
 void Input(IOValue value);
}

//接続先出力ポートから値を取り込むことができる
interface IPullable
{
 //thisに接続しているPort, 辿る用
 //IOutputPort GetOutputPort();  
 void Pull();
}

//入力としてPVectorを受ける
class PVectorInputPort
{
 
}

interface HasPairPort
{
 void SetPairPort(IOPort port);
 IOPort GetPairPort();
}

class InputPortBase implements IInputPort,IPullable,HasPairPort
{  
 INode GetNode(){return Node;}
 //Portを保持するノード
 INode Node;
 
 //値
 IOValue Target;
 
 void SetPairPort(IOPort output_port)
 {
   if(output_port instanceof IOutputPort)
   {
     this.PairOutputPort=(IOutputPort)output_port;
   }
 }
 
 IOPort GetPairPort()
 {
   return PairOutputPort; 
 }
 
 //thisに接続しているPort, 辿る用
 IOutputPort PairOutputPort;
 //IOutputPort GetOutputPort(){return OutputPort;}
 void Pull()
 {
   if(this.PairOutputPort!=null)
   {
     if(this.PairOutputPort instanceof IPullable)
     {
       ((IPullable)this.PairOutputPort).Pull();
     }
     this.Input(PairOutputPort.Output());
   }    
 }
 
 //パラメータコピー
 void Input(IOValue value)
 {
   if(Target!=null)
   {
     Target.set(value);
   }    
 }  
}

class float2InputPort extends InputPortBase
{
 //float2 
 //IOValue target;

 //コンストラクタ(参照とりこみ)
 float2InputPort(INode node, IOValue target_)
 {    
   if(!(target_ instanceof float2)){return;}   
   this.Target=(float2)target_;
   
   this.Node = node;
 }
}

class float1InputPort extends InputPortBase
{
 float1InputPort(INode node, IOValue target_)
 {
   if(!(target_ instanceof float1)){return;}   
   this.Target=(float1)target_;    
   
   this.Node = node;
 }
}

abstract class OutputPortBase implements IOutputPort,IPullable
{
 INode GetNode(){return Node;}
 //Portを保持するノード
 INode Node;
 
 IOutputMethod OutputMethod;
 
 //全てのInputPort統合->Node->OutputPort
 void Pull()
 {
   if(this.Node instanceof IPullable)
   {
     ((IPullable)this.Node).Pull();
   }
 }  
}

class float1OutputPort extends OutputPortBase
{
 float1OutputPort(INode node, IOutputMethod output_method)
 {
   this.Node = node;
   this.OutputMethod = output_method;
 }
 
 IOValue Output(){return this.OutputMethod.Output(this.Node);}
}

interface IOutputMethod
{
 IOValue Output(INode node);
}

class SliderOutputMethod implements IOutputMethod
{
 IOValue Output(INode node)
 {
   if(!(node instanceof SliderNode)){return null;}
   SliderNode sn = (SliderNode)node;
   return new float1(sn.GetValue());
 }
 
}

//単一のOutputPortを持つ
class SliderNode extends OutputNodeBase
{
 PVector pos;
 float w = 100;
 float h = 100; 
 
 //this GUI内部のHitエリア判定
 int current_index = -1; 
 
 String value_name = "Value Name";
 float min = 0;
 float max = 100;  
 float GetRange(){return max-min;}
 float GetValue(){return GetRange()*t;}
 void SetValue(float value)
 {
   t = value/GetRange();
   if(t<0){t=0;}else if(1<t){t=1;}
 }
 float t = 0;
   
 PVector GetNamePos(){return GetClientPosAbs();}  
 void drawName()
 {
   fill(0);
   text(value_name,GetNamePos().x,GetNamePos().y);
   fill(DefaultFillColor);
 }
 PVector GetValuePos(){return new PVector(pos.x, GetFooter2TopAbs());}
 void drawValue()
 {
   fill(0);
   text("Value : "+str(GetValue()),GetValuePos().x,GetValuePos().y);
   fill(DefaultFillColor);
 }  
 
 PVector GetMinPos(){return new PVector(pos.x, GetFooter2BottomAbs());}
 void drawMin()
 {
   fill(0);
   text("Min : "+str(min),GetMinPos().x,GetMinPos().y);
   fill(DefaultFillColor);
 }  
 PVector GetMaxPos(){return new PVector(pos.x, GetFooter1BottomAbs());}
 void drawMax()
 {
   fill(0);
   text("Max : "+str(max),GetMaxPos().x,GetMaxPos().y);
   fill(DefaultFillColor);
 }  
 
   
   
 //float1 Output;
 IOutputPort OutputPort;
 ArrayList<IOutputPort> GetOutputPorts(){return new ArrayList<IOutputPort>(Arrays.asList(this.OutputPort));}  
 void port_setup(){OutputPort = new float1OutputPort(this, new SliderOutputMethod());}  
     
 float port_w = 10;
 float port_h = 10;  
 int port_index = 200;
 PVector GetPortPos(){return new PVector(pos.x+w-port_w, pos.y+header_h);}
 boolean HitPort(float x, float y){return(GetPortPos().x<x&&x<GetPortPos().x+port_w&&GetPortPos().y<y&&y<GetPortPos().y+port_h);}
 void drawPort(){rect(GetPortPos().x, GetPortPos().y, port_w, port_h);}
 
 //Header
 float header_h = 10;  
 int header_index = 1;
 boolean HitHeader(float x, float y){if(pos.x<x&&x<pos.x+w&&GetHeaderTopAbs()<y&&y<GetHeaderBottomAbs()){return true;}return false;}
 float GetHeaderTopAbs(){return pos.y;}  
 float GetHeaderBottomAbs(){return pos.y+header_h;}
 
 //Footer1
 float footer1_h = 10;//下から上へ
 int footer1_index = 2;
 boolean HitFooter1(float x, float y){if(pos.x<x&&x<pos.x+w&&GetFooter1TopAbs()<y&&y<GetFooter1BottomAbs()){return true;}return false;}
 float GetFooter1TopAbs(){return pos.y+h-footer1_h;}
 float GetFooter1BottomAbs(){return pos.y+h;}
 
 //Footer2
 float footer2_h = 10;//下から上へ,2つめ
 int footer2_index = 3;
 boolean HitFooter2(float x, float y){if(pos.x<x&&x<pos.x+w&&GetFooter2TopAbs()<y&&y<GetFooter2BottomAbs()){return true;}return false;}
 float GetFooter2TopAbs(){return pos.y+h-footer1_h-footer2_h;}
 float GetFooter2BottomAbs(){return pos.y+h-footer1_h;}
 
 //ClientArea
 PVector GetClientPosAbs(){return new PVector(pos.x,pos.y+header_h);}
 float GetClientAreaH(){return this.h-header_h-footer1_h;}
 

   
 int slider_button1_index = 100;
 float slider_button_width_ratio = 0.1;//slider_lvとの比
 float slider_button_height_ratio = 0.2;
 
 //Slider
 PVector GetSliderSVAbs(){return GetClientPosAbs().add(new PVector(0,GetClientAreaH()/2));}
 PVector GetSliderEVAbs(){return GetClientPosAbs().add(new PVector(w,GetClientAreaH()/2));}
 PVector GetSliderLV(){return GetSliderEVAbs().sub(GetSliderSVAbs());}
 PVector GetSliderPos(){return GetSliderSVAbs().add(GetSliderLV().mult(t));}
 PVector GetSliderLTV(){return new PVector(GetSliderPos().x-GetSliderButtonWidth()/2,GetSliderPos().y-GetSliderButtonHeight()/2);}  
 
 float GetSliderButtonWidth(){return GetSliderLV().mag()*slider_button_width_ratio;}
 float GetSliderButtonHeight(){return GetSliderLV().mag()*slider_button_height_ratio;}
 
 boolean HitSliderButton(float x, float y)
 {
   if(GetSliderLTV().x<x&&x<GetSliderLTV().x+GetSliderButtonWidth())
   {
     if(GetSliderLTV().y<y&&y<GetSliderLTV().y+GetSliderButtonHeight())
     {
       return true;
     }
   }
   return false;
 }
 
 void drawButton()
 {
   rectMode(CENTER);
   rect(GetSliderPos().x, GetSliderPos().y, GetSliderButtonWidth(), GetSliderButtonHeight());
   rectMode(CORNER);
 }

 
 
 //コンストラクタ
 SliderNode(String name_, float x_, float y_, float min_, float max_, float value_)
 {
   value_name = name_;    
   this.pos = new PVector(x_,y_);
   min=min_;max=max_;
   SetValue(value_);
   
   port_setup();
 }
 SliderNode(float x, float y)
 {
   this.pos = new PVector(x,y);    
   port_setup();
 }
 SliderNode(PVector v)
 {
   this.pos.set(v);
   port_setup();
 }
 
 @Override
 void draw()
 {
   rect(pos.x,pos.y,w,h);
   drawName();
   drawValue();
   drawMin();
   drawMax();
   
   //header, footer
   line(pos.x,GetHeaderBottomAbs(),pos.x+w,GetHeaderBottomAbs());
   line(pos.x,GetFooter1TopAbs(),pos.x+w,GetFooter1TopAbs());
   line(pos.x,GetFooter2TopAbs(),pos.x+w,GetFooter2TopAbs());    
   line(GetSliderSVAbs().x,GetSliderSVAbs().y,GetSliderEVAbs().x,GetSliderEVAbs().y);
   
   drawButton();
   drawPort();    
 }
   
 @Override
 boolean Hit(float x, float y)
 {
   if(pos.x<x&&x<pos.x+w&&pos.y<y&&y<pos.y+h)
   {
     if(HitHeader(x,y)){current_index=header_index;}
     if(HitFooter1(x,y)){current_index=footer1_index;}
     if(HitSliderButton(x,y)){current_index=slider_button1_index;}
     if(HitPort(x,y)){current_index=port_index;}
     return true;
   }
   return false;
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(HitHeader(x,y)){return header_index;}
   if(HitFooter1(x,y)){return footer1_index;}
   if(HitSliderButton(x,y)){return slider_button1_index;}    
   if(HitPort(x,y)){return port_index;}
   return -1;
 }  
 
 @Override
 IOPort GetPort(int index)
 {
   if(index==port_index){return OutputPort;}
   return null;
 }
 
 @Override
 PVector GetPortPos(int index)
 {
   if(index==port_index){return GetPortPos();}
   return null;    
 }
 
 @Override
 int GetPortIndex(IOPort port)
 {
   if(OutputPort==port){return port_index;}
   return -1;
 }
   
 @Override
 void mouseDragged()
 {
   if(current_index<0){return;}
   if(current_index==header_index){this.pos.add(dv);}
   
   //if(current_index==footer_index){this.pos.add(dv);}    
   if(current_index==slider_button1_index)
   {
     t+=dv.x/GetSliderLV().mag();    
     if(t<0){t=0;}else if(1<t){t=1;}
   }
   
   if(current_index==port_index)
   {
     line(GetPortPos().x,GetPortPos().y,mouseX,mouseY);
   }
 }
 
 @Override
 void mouseReleased()
 {
   current_index = -1;
   
   for(IGUIObject gui : guis)
   {
     if(!(gui instanceof INode)){continue;}
     
     INode node = (INode)gui;
     
     int index = node.HitIndex(mouseX,mouseY);
     IOPort port = node.GetPort(index);
     if(port!=null)
     {
       //portに接触した
       //portの接続
       Connect(this.OutputPort, port);
     }
   }
 }  
}


class CircleNode extends IONodeBase
{     
 void Input(int index, IOValue value)
 {
   if(index==port0_index){this.InputPort0.Input(value);}
   if(index==port1_index){this.InputPort1.Input(value);}    
 }
 
 void port_setup()
 {
   InputPort0 = new float2InputPort(this, this.Center);
   InputPort1 = new float1InputPort(this, this.Radius);
 }  
 
 ArrayList<IInputPort> GetInputPorts(){return new ArrayList<IInputPort>(Arrays.asList(InputPort0,InputPort1));}
 ArrayList<IOutputPort> GetOutputPorts(){return null;}
 
 //Port
 IInputPort InputPort0;
 IInputPort InputPort1;  
 
 //値
 float2 Center;
 float1 Radius;
 
 float port_w = 10;
 float port_h = 10;

 int port0_index = 200;//center  
 int port1_index = 201;//radius
 
 PVector GetPort1Pos()
 {
   return new PVector(Center.x, Center.y);
 }
 
 boolean HitPort(float x, float y){return(GetPort1Pos().x<x&&x<GetPort1Pos().x+port_w&&GetPort1Pos().y<y&&y<GetPort1Pos().y+port_h);}
 void drawPort()
 {    
   rect(GetPort1Pos().x, GetPort1Pos().y, port_w, port_h);    
 }    
 
 //コンストラクタ
 CircleNode(float x, float y, float r)
 {
   this.Center = new float2(x,y);
   this.Radius = new float1(r);
   port_setup();
 }
 
 @Override
 void draw()
 {
   circle(this.Center.x,Center.y,Radius.x);    
   drawPort();
   
   if(this.InputPort1 instanceof HasPairPort)
   {
     HasPairPort inputport = ((HasPairPort)InputPort1);
     if(inputport.GetPairPort()!=null)
     {
         int index = inputport.GetPairPort().GetNode().GetPortIndex(inputport.GetPairPort());
         PVector pos = inputport.GetPairPort().GetNode().GetPortPos(index);
         
         line(pos.x,pos.y,GetPort1Pos().x,GetPort1Pos().y); 
     }
   }
 }
 
 @Override
 boolean Hit(float x, float y)
 {
   return HitPort(x,y);
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(HitPort(x,y)){return port1_index;}
   return -1;
 }
 
 @Override
 IOPort GetPort(int index)
 {
   if(index==port1_index)
   {
     return InputPort1;
   }
   return null;
 }

 @Override
 PVector GetPortPos(int index)
 {
   if(index==port1_index){return GetPort1Pos();}
   return null;
 }
 @Override
 int GetPortIndex(IOPort port)
 {
   if(InputPort0==port){return port0_index;}
   if(InputPort1==port){return port1_index;}
   return -1;
 }
 
}





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