ノードン的なものを作るドン!その002~それなりに構造化しておくドン(道なかば)~


前回

前回のものに以下のものをまぜこぜます。

GUIなるものはここでは
・表示できる(Graphics)
・マウス操作に反応できる(User Interface)
・移動できる
という能力があることを基本とします。

interface IGUIObject extends 
 IUniversalObject, 
 IDrawable,
 IHit, ITranslate, IClickable, IDraggable{}//GUI系統
 
interface IUniversalObject{} 
interface IDrawable{void draw();}
interface IHit{boolean Hit(float input_x, float input_y);}
interface ITranslate{void Translate(PVector dv);}
interface IClickable{void mousePressed(); void mouseReleased();}
interface IDraggable{void mouseDragged();}
 
class GUIObject 
 implements IGUIObject //IUniversalObject, IDrawable, IHit, ITranslate, IClickable, IDraggable
{      
 void mousePressed(){}
 void mouseDragged(){}
 void mouseReleased(){}
 
 void draw(){}    
 boolean Hit(float x, float y){return false;}  
 void Translate(PVector dv){}  
}

表示できる能力があるということは、なにがしかの幾何情報と、それを描画する手段が必要です。

幾何情報とは、2Dならば一つないし複数の2次ベクトルといくらかのパラメータの組。3Dならば一つないし複数の3次ベクトルといくらかのパラメータの組。などとみなせるでしょう。

描画する手段とは、根本的には環境によって用意された、ディスプレイに出力するためのプログラマに許された最終的なメソッドということになります。これはOSやAPI(DirectXやらOpenGL)に規定され、物理的にはグラフィックスカードなりボードなりが実現します。
あらかじめ用意された描画メソッドでは不足の場合は、プログラマがあらかじめ用意された描画メソッドを捕捉するなり組み立てるなりして追加します。

Dot(x,y)

もっとも単純な幾何情報はただ一つの座標からなる『点』でありましょう。この幾何を実現するためにはただ一つのベクトルを保持すれば事足りますが、これを描画したり、これに触れたりとなると話が変わります。

DrawPixel()などという描画メソッドは環境によっては用意されていたり用意されていなかったり、用意されていたとしても速度が遅かったり、それを使用するための下準備がとてもメンドくさかったりします。

DrawCircle()の半径1で代用せよなどといわれることもあります。

主流はディスプレイの総ピクセル数×1つのピクセルに掛かるデータ量からなるそれなりに大きな配列を用意ないし取得し、その内変更したいピクセルのみを狙い打って書き換え、配列をうまいことディスプレイに流し込むための仕組みにあとは任せる、というような流れでしょう。

一番近い記事はこれ

以上のことから、点というのは単純なくせに面倒くさいということが分かります。

また、バカ正直にDrawPixelないしDrawDotなるものを実装したとて、得られるのは些末な満足感と見ることも困難な出力のみで、取り立てて役に立つことも無く、GUIとしてGraphicsの性質をはなはだ欠いたものであると言わざるを得ません。

またUIとしての側面から見ても、たんなる点をマウス操作で1対1で捕捉するというのは、ディスプレイの解像度にもよりますが、昨今は甚だ困難で、それを逆手に取ったゲームでもつくるでなければ無意味です。

また、半径Rのパラメータを幾何オブジェクト側でなくマウス操作側、ツール側に持たせることで半径を持つ点という不可解な幾何形状は回避できますが、それをするとツール系統側に膨大なコードが必要となり、(最終的にはそうすべきであったとしても)めんどうです。

なのでここでは半径パラメータを追加することとします。これにより点は、幾何情報的には円と同義となります。

interface IDotGeometry extends IGeometry{PVector GetDot();}
interface HasCenter{PVector GetCenter(); void SetCenter(PVector center);}
interface HasRadius{float GetRadius(); void SetRadius(float value);}
interface ICircleGeometry extends HasCenter, HasRadius, IGeometry{}
interface HasOrigin{PVector GetOrigin();}

//Circleと中身同じ
class DotGUIBase 
 extends GUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "DotGUIBase";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 
 PVector GetOrigin(){return this.Pos;}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }
   
 @Override
 boolean Hit(float x, float y){return CircleHit(new CircleConverter(this),x,y);}  
 @Override
 void Translate(PVector dv){this.Pos.add(dv);}  
}

幾何情報や表示機能を分離するという方法も考えられます。例えば次のような書き方です。

class GUIObject
{
    IGeometry Geometry = new DotGeometry();
    IView View = new DotView();
    
    void draw()
    {
        View.View(Geometry);
    }
}

このようなやり方はたいへんに応用が効き、柔軟性に富みます。例えばこのやり方であるならば、『点』を表示するために円を用いても矩形を用いても良く、なんなら表示は円だがヒット判定は矩形であるなどということも可能です。

反面、一度も使わないような機能のためにうんざりするほどの初期設定をこなさねばならず、思いついたことを形にするために無駄な時間がかかり、苦痛を伴うこともしばしば。

なのでここではやはり、素直に半径パラメータを追加する方式を採用します。


Circle((x,y),r)

2Dの円は1つの2Dベクトルと1つの半径パラメータからなります。

描画メソッドは標準で用意されておるものだと思ってよく、その意味ではただの『点』よりもよほど単純な幾何形状であると言えなくもありません。

単純すぎるがゆえに、円専用の円にしか成り立たない関数やアルゴリズムが数多存在し、円のみが必要な単純な状況や、複雑な状況を円に近似して簡単に考える場合には強力ですが、他の幾何形状とひっくるめて扱おうとすると意外と邪魔だったりもします。

class CircleGUIBase
 extends GUIObject 
 implements ICircleGeometry, HasOrigin
{
 String ID = "CircleGUIBase";
 
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }     
 
 @Override
 boolean Hit(float x, float y){return CircleHit(this,x,y);}  
 @Override
 void Translate(PVector dv){this.Center.add(dv);}    
}

他の形状とひっくるめるなら近似円と使い分けます。近似円は死ぬ程頂点の増えた多角形です。この近似円は三角や四角と同じアルゴリズムに引っ掛けることができたりできなかったりします。

class ApproxCircleGUIBase 
 extends GUIObject 
 implements IApproxCircleGeometry, ICircleGeometry, HasOrigin, RetainedCircumference
{
 String ID = "ApproxCircleGUIBase";
 
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 float UnitRadian = 0.1;
 
 void UpdateCircumference()
 {
   this.Circumference=MakeCircleCircumference(this.Center,this.Radius,this.UnitRadian);
 }
 
 ArrayList<PVector> Circumference;
 ArrayList<PVector> GetCircumference()
 {
   if(this.Circumference==null||this.Circumference.size()<1)
   {
     this.Circumference=MakeCircleCircumference(this.Center,this.Radius,this.UnitRadian);
   }
   return this.Circumference;    
 }

 @Override
 void draw()
 {
   stroke(0);
   DrawLiner(GetCircumference());
   stroke(DefaultStrokeColor);    
 }   
 
 @Override
 boolean Hit(float x, float y)
 {
   return CircleHit(this,x,y);
 }
 
 @Override
 void Translate(PVector dv)
 {
   this.Center.add(dv);    
   if(this.Circumference!=null&&0<this.Circumference.size())
   {
     vadd(this.Circumference, dv);
   }
 }  
}

ArrayList<PVector> MakeCircleCircumference(PVector center, float radius, float unit_radian)
{
 return MakeCircleOpen(center, radius, unit_radian);
}
//最後閉じない
ArrayList<PVector> MakeCircleOpen(PVector circle_center, float radius, float unit_radian)
{
 float start_radian = 0;
 return MakeCircleOpen(circle_center,radius, start_radian, unit_radian);
}   
//最後閉じない
ArrayList<PVector> MakeCircleOpen(PVector circle_center, float radius, float start_radian, float unit_radian)
{
 ArrayList<PVector> ret = new ArrayList<PVector>();
 for(float radian = 0; radian<2*PI; radian+=unit_radian)
 {
   float nx = circle_center.x+radius*cos(start_radian+radian);
   float ny = circle_center.y+radius*sin(start_radian+radian);        
   ret.add(new PVector(nx,ny));
 } 
 return ret;
}  ​

LineSegment((x0,y0),(x1,y1))

線というのは本当にめんどくさい幾何形状です。

2次元上の点が2Dベクトル1つで表されるなら、単純に2Dベクトルをもう1つ追加すればそれは線となりましょう。
線を引くための描画メソッドは、円を描くための描画メソッドと同等程度に根本的なメソッドです。このメソッドが提供されていなければ2次元グラフィックスなどというものは成り立たないと言っても過言ではありません。

2つの2DベクトルとDrawLine()なる描画メソッドによってあらわされる幾何形状は、より正確には『線分』と呼び表されます。線分は、両端を有する線です。

両端を有するとは、端っこでちょん切れておるということで、両端方向にちょん切れずに無限に伸びるものを『直線』、片側方向に無限に伸びつつ、もう片側がちょん切れているものを『半直線(Ray)』と呼びます。

これらは数学的にはもちろんのこと、アルゴリズム的にもたいへんにしゃしゃってくるため、注意が必要です。

class LinerGUIBase 
 extends GUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "LinerGUIBase";
 
 PVector SV;
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 
 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return PVector.sub(EV,SV);}
    
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 } 
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }    
}

また、『点』の時はDrawPixelなるものを作成したとて、その出力はディスプレイ上のただ一つの点となるため、大変に効果の薄いものでした。しかし『線分』を描画するDrawLineははっきりと人間に認識できる類のものです。ゆえに『点』の時の半径のような、描画のための補助的なパラメータを用意する必要は、必ずしもありません。

ではUIに相当する部分、『線分』にマウス操作で触れる場合はどうでしょうか。これの操作のし易さはディスプレイの解像度に依存しますが、基本的にはイラっとするレベルの操作感でしょう。たまに触れるとかそんなレベルになると思われます。

それゆえ、幅パラメータの追加はたいへんに有効です。

点形状が半径パラメータを伴うことで円と同義となるように、線分形状に幅が加わると回転を伴う矩形となります。

class WideLinerGUIBase 
 extends GUIObject
 implements IWideSegmentGeometry, HasWide, HasOrigin, RetainedCircumference
{
 String ID = "WideLinerGUIBase";
 
 PVector SV;
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 
 PVector GetLV(){return PVector.sub(EV,SV);}
 
 PVector GetOrigin(){return this.SV;}
 
 float Wide = 10;
 float GetWide(){return this.Wide;}
   
 void UpdateCircumference()
 {
   this.Circumference=MakeWideLineCircumference(this.GetSV(),this.GetEV(),this.Wide);
 }
 
 ArrayList<PVector> Circumference;
 ArrayList<PVector> GetCircumference()
 {
   if(this.Circumference==null||this.Circumference.size()<1)
   {
     this.Circumference=MakeWideLineCircumference(this.SV,this.EV,this.Wide);
   }
   return this.Circumference;    
 }
 
 @Override
 void draw()
 {
   stroke(0);
   DrawLinerClose(this.GetCircumference());
   stroke(DefaultStrokeColor);
 }   
 
 @Override
 boolean Hit(float x, float y)
 {
   return CrossingNumber(this.GetCircumference(), new PVector(x,y));
 }
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
   
   if(this.Circumference!=null&&0<this.Circumference.size())
   {
     vadd(this.Circumference, dv);
   }    
 }    
}

では幅パラメータがマストで必要かというと、そうでもなく。線分というのはそれを動かすことを想定した場合
・始点のみを動かす(変形)
・終点のみを動かす(変形)
・始点終点を共に動かす(平行移動)
の3種の動作が可能です。

これの意味する所は、幅が無くとも端点に対する半径パラメータがあれば線分に触れる、操作することは可能ということで、

組み合わせ的には
・幅のない線分の描画のみ、操作不可
・幅のない線分を描画し、両端点で操作する
・幅のある線分を描画し、操作する
・幅のある線分を描画し、操作し、両端点でも操作できる
このようなGUIのパターンが考えられます。

Rect(x,y,w,h)

GUIとして最も使いやすいのは回転のない矩形です。なんなら一番単純です。

class RectGUIBase 
 extends GUIObject 
 implements IRectGeometry, HasOrigin
{  
 String ID = "RectGUIBase";
 
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}
   
 PVector GetOrigin(){return this.LTV;}

 ArrayList<PVector> GetCoordinates()
 {
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(new PVector(this.LTV.x, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y+Height));
   ret.add(new PVector(this.LTV.x, this.LTV.y+Height));
   return ret;    
 }
 
 @Override
 void draw(){rect(this.LTV.x,this.LTV.y,Width,Height);}   
 @Override
 boolean Hit(float x, float y){return RectHit(this,x,y);}  
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}  
}

親子構造

GUIに対して親子構造をいれこみます。その実現方法はいろいろありましょうが、ここで用いるのは1つの親GUIに対して複数の子GUIからなる多分木構造です。

 //親子構造
 IGUIObject Parent;
 ArrayList<IGUIObject> Children;

ループはしないように気を付けます。ループはしていないということを前提で各種のアルゴリズムを組んだ場合、ループをしているとバグりますしパソコンが壊れます。

ループとは、例えば以下のような行為です。

Children.add(Parent);


相対座標と絶対座標

座標の取り方はいろいろとありましょうが、ここでは子のGUIは親のGUIの原点を原点とすること標準とします。それゆえ、子のGUIのもつ幾何情報は、最終的に親のGUIの表示位置に応じて平行移動されます。

ディスプレイ的な原点や、プログラムの表示エリア的な原点は左上の方が(0,0)であると相場が決まっておりますが、
この場における幾何情報的な原点とは、任意に一点、親子構造の実現のために強引にでも決めるものです。

//点
PVector GetOrigin(){return this.Pos;}
//円
PVector GetOrigin(){return this.Center.copy();}
//線分
PVector GetOrigin(){return this.SV;}
//矩形
PVector GetOrigin(){return this.LTV;}

形状によってはふさわしい原点が無かったり、あるいはいっぱいあったりしますが、強引に決めます。

子の座標は親の原点を原点とするため、その絶対座標は

Parent.Origin.add(v);

となります。

絶対座標とは、描画領域における本来の座標。最終的にGUIを表示したい、表示すべき座標です。

木構造であるならば、あるGUIが自身の最終的な絶対座標を知りたい場合、自身の親を辿っていってその原点を全て足します。

簡単に書くと

 PVector GetOriginAbs()
 {
   PVector abs_pos = new PVector(0,0);  
   IGUIObject current = this; 
   while(current!=null)
   {
     abs_pos.add(current.Origin);           
     current = current.Parent;
   }  
   return abs_pos;
 }

難しく書くと

 PVector GetOriginAbs()
 {
   PVector abs_pos = new PVector(0,0);
   if(!(this instanceof HasOrigin)){return abs_pos;}
   
   HasOrigin current = (HasOrigin)this;
   
   while(current!=null)
   {
     if(!(current instanceof HasOrigin)){break;}
     abs_pos.add(((HasOrigin)current).GetOrigin());      
     
     if(!(current instanceof IRelativeGUI)){break;}      
     IRelativeGUI rerative = (IRelativeGUI)current;      
     IGUIObject parent = rerative.GetParentGUI();
     
     if(parent==null){break;}
     if(!(parent instanceof HasOrigin)){break;}
     current = (HasOrigin)parent;
     
   }  
   return abs_pos;
 }

先に書いた親子構造のないGUIを継承し、親子構造を付け加えます。継承を悪手とみなす人もいます。

例えば親子構造のないGUIは適当に作っただけでも点、線分、円、矩形とあり、当然楕円なり三角なりひし形なりと、増やそうと思ったらいくらでも増やせます。

困ったことに我々は親子構造のあるGUIを以下の要領で作ってしまいますので、このGUIを継承した点、線分、円、矩形が必要になってきます。

interface HasParentGUI
{
 IGUIObject GetParentGUI();
 void SetParentGUI(IGUIObject parent);
}
interface HasChildrenGUI{ArrayList<IGUIObject> GetChildrenGUI();}

interface IRelativeGUI 
 extends IGUIObject,HasParentGUI,HasChildrenGUI,
 IDrawableAbs,IDrawableOffset,IHitAbs,IHitOffset
{  
 int GetIndexInParent();//親のリスト内でのindex
 PVector GetOriginAbs();//絶対座標
}

class RelativeGUIObject 
 extends GUIObject
 implements IRelativeGUI, HasIndex
{
 //指定して与えられるindex
 int index;
 int GetIndex(){return this.index;}  
 void SetIndex(int index_){this.index=index_;}
 
 //親のリストにおけるthisのindex
 int GetIndexInParent()
 {
   if(this.Parent==null){return -1;}
   if(!(this.Parent instanceof HasChildrenGUI)){return -1;}    
   HasChildrenGUI hcg = (HasChildrenGUI)this.Parent;     
   return hcg.GetChildrenGUI().indexOf(this);    
 }
 
 PVector GetOriginAbs()
 {
   PVector abs_pos = new PVector(0,0);
   if(!(this instanceof HasOrigin)){return abs_pos;}
   
   HasOrigin current = (HasOrigin)this;
   
   while(current!=null)
   {
     if(!(current instanceof HasOrigin)){break;}
     abs_pos.add(((HasOrigin)current).GetOrigin());      
     
     if(!(current instanceof IRelativeGUI)){break;}      
     IRelativeGUI rerative = (IRelativeGUI)current;      
     IGUIObject parent = rerative.GetParentGUI();
     
     if(parent==null){break;}
     if(!(parent instanceof HasOrigin)){break;}
     current = (HasOrigin)parent;
     
   }  
   return abs_pos;
 }
 
 //親子構造
 IGUIObject Parent;
 IGUIObject GetParentGUI(){return this.Parent;}
 void SetParentGUI(IGUIObject parent_){this.Parent = parent_;}
   
 ArrayList<IGUIObject> Children = new ArrayList<IGUIObject>();
 ArrayList<IGUIObject> GetChildrenGUI(){return this.Children;}  
 
 //blank method
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y){return false;}//絶対座標でHit判定  
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y){return false;}  
 void drawAbs(float x, float y){};
 void drawOffset(float ox, float oy){};
 
}

こういうことがあるのでHasにしろ理論がでてきます。あるいは全部Interfaceにしてコピペの方がまし理論です。

interface IGUIObject()
{
    IGeometry Geometry;
    IView View;
    IHit Hit;
    IGUIObject Parent;
    ArrayList<IGUIObject> Children;
}

interface IObject()
{
    IGUI GUI;
    IObjectParent;
    ArrayList<IObject> Children;    
}

現状でも幾何情報系統、表示および操作を伴うGUI系統、親子構造が入り組んでおり、この上に我々はノードン系統を組み込まねばなりません。継承でやりくりしていると、記述せねばならないクラスが組み合わせの限り増えます。

こうした問題に言語的に対応しているのはC++の多重継承ですが、これはまったくバグり倒すので支持している人を見たことがありません。

RubyのmixinやC#のインターフェースのデフォルト実装などはある程度の解決を与えてはくれるでしょう。

我々は難しいことは考えるのはやめて、継承の道を突っ走ります。

RelativeDot

class RelativeDotGUIBase
 extends RelativeGUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "RelativeDotGUIBase";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 PVector GetOrigin(){return this.Pos.copy();}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }  
 @Override
 void draw(){drawAbs(GetOrigin().x,GetOrigin().y);} 
 @Override
 void drawOffset(float ox,float oy){drawAbs(GetOrigin().x+ox,GetOrigin().y+oy);}   
 
 //Absにまとめる
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return CircleHit(abs_x,abs_y,this.Radius,input_x,input_y);
 }  
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(this.Pos.x,this.Pos.y,input_x,input_y);}
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y){
   return HitAbs(this.Pos.x+offset_x,this.Pos.y+offset_y,input_x,input_y);}  
 
 @Override
 void Translate(PVector dv)
 {
   this.Pos.add(dv);
 }    
}

RelativeCircle

class RelativeCircleGUIBase
 extends RelativeGUIObject 
 implements ICircleGeometry, HasOrigin
{
 String ID = "RelativeCircleGUIBase";
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }    
 @Override
 void draw(){drawAbs(GetOrigin().x,GetOrigin().y);} 
 @Override
 void drawOffset(float ox,float oy){drawAbs(GetOrigin().x+ox,GetOrigin().y+oy);}   
 
 //Absにまとめる
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y){
   return CircleHit(abs_x,abs_y,this.Radius,input_x,input_y);}  
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(Center.x,Center.y,input_x,input_y);}  
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y){
   return HitAbs(Center.x+offset_x,Center.y+offset_y,input_x,input_y);}  
 
 @Override
 void Translate(PVector dv)
 {
   this.Center.add(dv);
 }     
}

RelativeLine

class RelativeLinerGUIBase 
 extends RelativeGUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "RelativeLinerGUIBase";
 
 PVector SV;//Origin
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  

 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return PVector.sub(EV,SV);}
 
 //Absにまとめがたい
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 } 
 @Override
 void drawAbs(float abs_x, float abs_y)
 {
   stroke(0);
   line(abs_x,abs_y,abs_x+GetLV().x,abs_y+GetLV().y);
   stroke(DefaultStrokeColor);
 } 
 @Override
 void drawOffset(float ox, float oy)
 {
   stroke(0);
   line(this.SV.x+ox,SV.y+oy,EV.x+ox,EV.y+oy);
   stroke(DefaultStrokeColor);
 }   
 
 //Hit無し
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }      
}

RelativeRect

class RelativeRectGUIBase 
 extends RelativeGUIObject
 implements IRectGeometry, HasOrigin, HasOriginAbs, HasCircumference, HasCircumferenceAbs
{    
 
 PVector GetOrigin(){return this.LTV;}
 
 ArrayList<PVector> GetCircumference()
 {
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(new PVector(this.LTV.x, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y+Height));
   ret.add(new PVector(this.LTV.x, this.LTV.y+Height));
   return ret;    
 }
 
 ArrayList<PVector> GetCircumferenceAbs()
 {
   PVector origin = this.GetOriginAbs();
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(origin.copy().add(new PVector(this.LTV.x, this.LTV.y)));
   ret.add(origin.copy().add(new PVector(this.LTV.x+Width, this.LTV.y)));
   ret.add(origin.copy().add(new PVector(this.LTV.x+Width, this.LTV.y+Height)));
   ret.add(origin.copy().add(new PVector(this.LTV.x, this.LTV.y+Height)));
   return ret;    
 }
 
 //親Originを原点とする
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
 @Override
 void draw()
 {
   this.drawAbs(this.LTV.x, this.LTV.y);
 }   
 @Override
 void drawOffset(float ox, float oy)
 {
   this.drawAbs(this.LTV.x+ox,this.LTV.y+oy);
 }
  
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }    
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(LTV.x,LTV.y,input_x,input_y);}  
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y)
 {
   return HitAbs(LTV.x+offset_x,LTV.y+offset_y,input_x,input_y);
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}    
}

Abs,Offset

DrawやHitにAbsなりOffsetなりが追加されております。これらは親子構造の導入によって座標があっちゃこっちゃするために導入されたものです。全て頭の中で計算ができるなら導入は不要です。導入することで余計に分かりにくくなる側面もあるにはあります。

最終的なAPIの描画関数に放り込むのは描画したい位置の絶対座標であり、Abs系のメソッドであると考えます。引数無しのDraw()メソッドは、自分の原点を内部で用いてDrawAbs()を呼び出すだけであり、

描画したい位置=そのGUIの原点の絶対座標

であることを前提とします。

DrawOffset()は入力引数によって、形状を示す全ての座標が平行移動されたものと考えます。すなわち入力引数は親の原点であり、親の原点と自分の原点を加算してDrawAbs()に放り込むのがDrawOffset()です。

引数無しのDraw()メソッドによって、意図したとおりの位置に描画されるのはそのオブジェクトがルート、すなわち木構造の最上段に位置し、自らの上部構造に親が存在しない場合のみです。

子であるオブジェクトを適切な位置に表示するためには子のDrawAbs()を呼び出す必要があり、DrawAbs()メソッドは作成したいGUIに応じて都度、適切に実装する必要があります。

DrawAbs()によってGUIが自身を描画する場合、そのGUIが親構造を有しないことが分かっているのなら、そのGUIは自身の幾何情報を好きに使って描画関数を設計することができます。そのGUIが親構造を持つ場合、GetOriginAbs()を用いて現在の原点の絶対座標を明らかにし、全ての自身の幾何情報を現在の絶対原点位置に平行移動してから描画する必要があります。

あるGUIが子のDraw関数を呼び出して子のGUIの表示位置を管理したい場合はDrawOffset()用いると比較的分かり易い可能性があります。

//親のdrawAbs
void drawAbs(abs_x, abs_y)
{
    child.drawOffset(abs_x,abs_y);
}
void draw()
{
    drawAbs(GetOriginAbs().x,GetOriginAbs().y)
}

これが意味する所は

child.drawOffset(GetOriginAbs().x,GetOriginAbs().y);

です。

Blank,BlankHit,Moval

BaseクラスはGUIなり親子構造だのに必要な関数をあらかじめ盛り込んでおいて、デフォルトで無意味な関数を実装。使う場合はオーバーライドして使うための手抜きクラスです。状況によってはデフォルトの無意味な関数すら邪魔な場合があります。

Blank,BlankHit,Movalはそのために作成された適当なクラスです。

Blankは幾何情報+Draw関数を所持。
BlankHitは幾何情報とDraw関数とHit関数を所持。
Movalは幾何情報,Draw,Hit,Translateを所持。Baseに等しい。

Blankは表示はしたいがユーザーに操作はされたくないGUIに使用。BlankHitは表示もしたいしスイッチ的な反応も欲しいが、勝手に動かされたくないGUIに使用。特に別のGUIのパーツとして使用したいような時。

class RelativeBlankRect 
   extends RelativeGUIObject
 implements IRectGeometry, HasOrigin, HasOriginAbs
{
 String ID = "RelativeBlankRect";
 
 PVector LTV;//Origin
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}
 
 PVector GetOrigin(){return this.LTV;}
 
 //コンストラクタ
 RelativeBlankRect(float x, float y, float w, float h)
 {
   //super(x,y,w,h);
   this.LTV = new PVector(x,y);
   this.Width=w;
   this.Height=h;      
 }  
 
 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
 @Override
 void draw()
 {
   this.drawAbs(this.LTV.x, this.LTV.y);
 }   
 @Override
 void drawOffset(float ox, float oy)
 {
   this.drawAbs(this.LTV.x+ox,this.LTV.y+oy);
 }  
}

class RelativeBlankHitRect 
   extends RelativeBlankRect
{
 
 //コンストラクタ
 RelativeBlankHitRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);     
 }   
 
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }    
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(LTV.x,LTV.y,input_x,input_y);}  
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y)
 {
   return HitAbs(LTV.x+offset_x,LTV.y+offset_y,input_x,input_y);
 }  
}

class RelativeMovalRect 
 extends RelativeBlankHitRect
{
 
 //コンストラクタ
 RelativeMovalRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);    
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);} 
 
}

それらをふまえて

以上を踏まえた上で死ぬ程おおざっぱに前回部分を書き直します。この部分はいまだ死ぬ程おおざっぱであるがゆえにおおいに適当です。

以下しばらく部品化されたGUIら。

HeaderGUIは自身が操作されると、それに伴って親のGUIをまるごと移動させるような機能を持つ。

class HeaderGUI 
 extends RelativeRectGUIBase
 implements HasParentGUI
{
 String ID = "HeaderGUI";
   
 String text = "";
 String GetText(){return this.text;}
 void SetText(String text_){this.text=text_;}
 
 //コンストラクタ
 HeaderGUI(IGUIObject parent_, int index_, PVector v, float w, float h)
 {
   this.index = index_;
   this.Parent=parent_;
   this.LTV = new PVector(v.x,v.y);
   this.Width = w;
   this.Height = h;    
 }
 
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);   
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
       
   fill(0);
   text(this.GetText(),abs_x,abs_y+this.Height);
   fill(DefaultFillColor);
 }

 //この能力はヘッダー特有
 //親を移動させる能力Parent Translate
 @Override
 void Translate(PVector dv)
 {
   if(Parent==null)
   {
     this.LTV.add(dv);
     return;
   }        
   this.Parent.Translate(dv);
 }    
}

class ParameterGUI 
 extends RelativeRectGUIBase 
 implements HasParentGUI
{
 String ID = "ParameterGUI";
 
 String text = "";
 String GetText(){return this.text;}
 void SetText(String text_){this.text=text_;}
 
 //コンストラクタ
 ParameterGUI(IGUIObject parent_, int index_, PVector v, float w, float h)
 {
   this.index = index_;
   this.Parent=parent_;
   this.LTV = new PVector(v.x,v.y);
   this.Width = w;
   this.Height = h;    
 }
 
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);   
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
       
   fill(0);
   text(this.GetText(),abs_x,abs_y+this.Height);
   fill(DefaultFillColor);
 } 
 
 void drawParam(float param)
 {
   fill(0);
   text(GetText()+" : "+str(param),GetOriginAbs().x, GetOriginAbs().y+this.Height);
   fill(DefaultFillColor);
 }    
}
class RectSlider 
 extends RelativeRectGUIBase
{
 
 float t = 0;
 
 RelativeBlankLine line;
 RelativeMovalRect button;
 
 float button_w_t=0.15;
 float button_h_t=1;  
 
 //Slider
 PVector GetSliderSVAbs(){return this.GetOriginAbs().add(line.SV);}
 PVector GetSliderEVAbs(){return this.GetOriginAbs().add(line.EV);}
 PVector GetSliderLV(){return line.GetLV();}  
 PVector GetSliderButtonPosAbs(){return GetSliderSVAbs().add(GetSliderLV().mult(t));}  
 PVector GetSliderButtonLTVAbs(){
   return new PVector(GetSliderButtonPosAbs().x-GetSliderButtonWidth()/2,GetSliderButtonPosAbs().y-GetSliderButtonHeight()/2);}  
 float GetSliderButtonWidth(){return this.Width*button_w_t;}
 float GetSliderButtonHeight(){return this.Height*button_h_t;}  
 
 void drawButton()
 {
   fill(255,0,0);
   this.button.drawAbs(GetSliderButtonLTVAbs().x,GetSliderButtonLTVAbs().y);
   fill(DefaultFillColor);
 }  
 
 
 //コンストラクタ
 RectSlider(IGUIObject parent, int index, float x, float y, float w, float h)
 {
   this.Parent = parent;
   this.SetIndex(index);
   
   this.LTV=new PVector(x,y);
   this.Width=w;
   this.Height=h;
   line = new RelativeBlankLine(0,h/2,w,h/2);//相対位置
   button = new RelativeMovalRect(0,0,w*button_w_t,h*button_h_t);//相対位置
   
   line.SetIndex(0);
   button.SetIndex(1);    
 }  
 //コンストラクタ
 RectSlider(float x, float y, float w, float h)
 {
   this.LTV=new PVector(x,y);
   this.Width=w;
   this.Height=h;
   line = new RelativeBlankLine(0,h/2,w,h/2);//相対位置
   button = new RelativeMovalRect(0,0,w*button_w_t,h*button_h_t);//相対位置
   
   line.SetIndex(0);
   button.SetIndex(1);    
 }  
 
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   this.line.drawOffset(abs_x,abs_y);
   drawButton();
   stroke(DefaultStrokeColor);
 }
 
 int current_index = -1;
 
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   if(this.button.HitOffset(abs_x,abs_y,input_x,input_y))
   {      
     return true;
   }    
   return false;
 }      
 
 @Override
 void mousePressed()
 {
   if(this.button.HitOffset(GetOriginAbs().x,GetOriginAbs().y,mouseX, mouseY))
   {
     current_index=this.button.GetIndex();
   }
 }  
 
 @Override
 void mouseDragged()
 {
   if(current_index==this.button.GetIndex())
   {
     t+=dv.x/GetSliderLV().mag();    
     if(t<0){t=0;}else if(1<t){t=1;}
     //相対座標
     this.button.LTV.set(GetSliderButtonLTVAbs().sub(this.GetOriginAbs()));      
   }
 }
 
 @Override
 void mouseReleased() {current_index =-1;}
}

Node系統まで合流した極めて悪質なクラス。なんらかの対策が求められる。

class RelativeNodeObject
 extends RelativeGUIObject
 implements INodeGUI, IRelativeGUI
{
 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
}

abstract class IONodeBase 
 extends RelativeNodeObject
 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 RelativeNodeObject
 implements HasOutputPorts
{
   
}

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

class OutputNodeRect
 extends OutputNodeBase 
 implements IRectGeometry, HasOrigin, HasOriginAbs, HasCircumference, HasCircumferenceAbs
{
 String ID = "OutputNodeRect";
 
 //float1 Output;
 IOutputPort OutputPort;
 ArrayList<IOutputPort> GetOutputPorts(){return new ArrayList<IOutputPort>(Arrays.asList(this.OutputPort));}   
 
 PVector GetOrigin(){return this.LTV;}
 
 ArrayList<PVector> GetCircumference()
 {
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(new PVector(this.LTV.x, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y+Height));
   ret.add(new PVector(this.LTV.x, this.LTV.y+Height));
   return ret;    
 }
 
 ArrayList<PVector> GetCircumferenceAbs()
 {
   PVector origin = this.GetOriginAbs();
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(origin.copy().add(new PVector(this.LTV.x, this.LTV.y)));
   ret.add(origin.copy().add(new PVector(this.LTV.x+Width, this.LTV.y)));
   ret.add(origin.copy().add(new PVector(this.LTV.x+Width, this.LTV.y+Height)));
   ret.add(origin.copy().add(new PVector(this.LTV.x, this.LTV.y+Height)));
   return ret;    
 }
 
 //親Originを原点とする
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
 @Override
 void draw()
 {
   this.drawAbs(this.LTV.x, this.LTV.y);
 }   
 @Override
 void drawOffset(float ox, float oy)
 {
   this.drawAbs(this.LTV.x+ox,this.LTV.y+oy);
 }
  
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }    
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(LTV.x,LTV.y,input_x,input_y);}  
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y)
 {
   return HitAbs(LTV.x+offset_x,LTV.y+offset_y,input_x,input_y);
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}     
 
}


class SliderNode 
 extends OutputNodeRect 
{  
 String ID = "SliderNode";
 
 //this GUI内部のHitエリア判定
 int current_index = -1; 
 
 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 GetValuePos(){return new PVector(LTV.x, this.footer2.GetOriginAbs().y);}
 void drawValue()
 {
   fill(0);
   text("Value : "+str(GetValue()),GetValuePos().x,GetValuePos().y);
   fill(DefaultFillColor);
 }     
   
 ////float1 Output;
 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(LTV.x+Width-port_w, LTV.y+this.header1.Height);}
 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);}
 
 float header_h = 10;
 float footer_h = 10;
 float slider_h = 10;
   
 //Header,Footer
 HeaderGUI header1;
 ParameterGUI footer1;
 ParameterGUI footer2;  
 RectSlider slider1;
 float slider_button_width_ratio = 0.1;//slider_lvとの比
 float slider_button_height_ratio = 0.2;  
 
 void children_setup(String value_name)
 {
   header1 = new HeaderGUI(this, 1, new PVector(0,0),Width,header_h); 
   header1.SetText(value_name);    
   footer1 = new ParameterGUI(this, 2, new PVector(0,Height-footer_h),this.Width,footer_h); 
   footer2 = new ParameterGUI(this, 3, new PVector(0,Height-footer_h-footer_h),this.Width,footer_h);    
   slider1 = new RectSlider(this, 4, 0,header_h,Width,slider_h);
 }
 
 //ClientArea
 PVector GetClientPosAbs(){return new PVector(LTV.x,LTV.y+this.header1.Height);}
 float GetClientAreaH(){return this.Height-this.header1.Height-this.footer1.Height;}      

 //コンストラクタ
 SliderNode(String name_, float x_, float y_, float min_, float max_, float value_)
 {
   //value_name = name_;    
   this.LTV = new PVector(x_,y_);
   this.Width=100;
   this.Height=100;
   
   min=min_;max=max_;
   SetValue(value_);    
   port_setup(); 
   children_setup(name_);
 }
 
 SliderNode(String name_, float x, float y)
 {
   this.LTV = new PVector(x,y);    
   this.Width=100;
   this.Height=100;
       
   port_setup();    
   children_setup(name_);
 }
 
 SliderNode(String name_, PVector v)
 {
   this.LTV.set(v);
   this.Width=100;
   this.Height=100;
       
   port_setup();    
   children_setup(name_);
 }
 
 @Override
 void drawAbs(float abs_x, float abs_y)
 {
   rect(abs_x,abs_y,Width,Height);
   drawValue();

   //header, footer
   this.header1.drawOffset(abs_x,abs_y);  
   this.footer1.drawOffset(abs_x,abs_y);
   this.footer1.drawParam(min);
   this.footer2.drawOffset(abs_x,abs_y);
   this.footer2.drawParam(max);       
   this.slider1.drawOffset(abs_x,abs_y);
   drawPort();    
 }
 
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   if(RectHit(abs_x,abs_y,Width,Height,input_x,input_y))
   {
     if(this.header1.HitOffset(abs_x,abs_y,input_x,input_y)){current_index=this.header1.GetIndex();}
     if(this.slider1.HitOffset(abs_x,abs_y,input_x,input_y))
     {
       current_index=this.slider1.GetIndex();
     }
  
     if(HitPort(input_x,input_y)){current_index=port_index;}
     return true;      
   }
   return false;  
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(this.header1.HitOffset(LTV.x,LTV.y,x,y)){return this.header1.GetIndex();}
   if(this.slider1.HitOffset(LTV.x,LTV.y,x,y)){return this.slider1.GetIndex();}
   
   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 mousePressed()
 {
   if(current_index<0){return;}
   
   if(current_index==this.slider1.GetIndex())
   {
     this.slider1.mousePressed();
   }
   
 }
 
 @Override
 void mouseDragged()
 {
   if(current_index<0){return;}
   if(current_index==this.header1.GetIndex()){this.header1.Translate(dv);}
   
   if(current_index==this.slider1.GetIndex())
   {
     this.slider1.mouseDragged();
     this.t=slider1.t;
   }
   
   if(current_index==port_index)
   {
     line(GetPortPos().x,GetPortPos().y,mouseX,mouseY);
   }
 }
 
 @Override
 void mouseReleased()
 {
   this.slider1.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);
     }
   }
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}  
}

Ver1.002


もはや完全にNode系統を継承主流に合流
GetOriginAbs()を重要視し、親子構造を持つGUIは必ずGetOrigin()を実装する
Abs,Offset系のdraw,HitメソッドをGetOriginAbs()を用いてBaseクラスで実装
通常、設定がうまくいっていれば各GUIはdraw(),Hit(x,y)を普通に用いればよい

//全て書き換えられる

  @Override
 void drawAbs(float abs_x, float abs_y)
 {
   rect(abs_x,abs_y,Width,Height);
   drawValue();

   //header, footer
   //this.header1.drawOffset(abs_x,abs_y);  
   //this.footer1.drawOffset(abs_x,abs_y);
   //this.footer2.drawOffset(abs_x,abs_y);
   //this.slider1.drawOffset(abs_x,abs_y);   
   //this.OutputPort.drawOffset(abs_x,abs_y);

   this.header1.draw();  
   this.footer1.draw();
   this.footer2.draw();
   this.slider1.draw(); 
   this.OutputPort.draw();
 }

■GUIObject implements IGUIObject
+親子構造の導入
■RelativeGUIObject extends GUIObject implements IRelativeGUI, HasIndex
+Node構造の導入
■NodeObject extends RelativeGUIObject
    implements INode, IRelativeGUI, HasInputPorts, HasOutputPorts, IPullable
+幾何構造の導入
■OutputNodeRect extends NodeObject
    implements IRectGeometry, HasOrigin, HasOriginAbs
■SliderNode extends OutputNodeRect



//案1
//Base:パラメータしかもたぬ(Geometryと同じ)
//Blank:+draw
//RelativeBase:親子構造を持つ

//Base:パラメータ+Event
//Blank:パラメータ+draw
//RelativeBase:親子構造を持つ


//Event


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

class ControlPoint
{
 float origin_r = 5;
 float r = 15;
 
 PVector origin = new PVector(0,0);
 PVector pos(){return this.origin.copy().add(offset);}
 
 PVector offset = new PVector(0,0);//gui操作時に変動するのはこっち
 
 //コンストラクタ
 ControlPoint(){}
 ControlPoint(float x, float y){origin.set(x,y);}
 ControlPoint(PVector origin_){origin.set(origin_);}
 ControlPoint(PVector origin_, PVector offset_)
 {
   origin.set(origin_);offset.set(offset_);
 }  
 
 void Draw()
 {
   circle(origin.x, origin.y, origin_r);    
   circle(pos().x, pos().y, r);        
   line(origin.x, origin.y, pos().x, pos().y);
 }
}

void mousePressed()
{
 if(mouseButton==RIGHT)
 {
   onfill = !onfill; //<>//
   int a = 0;
   int aa = a;
 }
 
 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;      
      this.guis.get(i).mousePressed();
   }
 }    
}

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_vertex_index=-1;
 current_gui_index=-1;  
}


//GUI

//GUI Base
class GUIObject 
 implements IGUIObject //IUniversalObject, IDrawable, IHit, ITranslate, IClickable, IDraggable
{      
 void mousePressed(){}
 void mouseDragged(){}
 void mouseReleased(){}
 
 void draw(){}    
 boolean Hit(float x, float y){return false;}  
 void Translate(PVector dv){}  
}

interface IRelativeGUI 
 extends IGUIObject,HasParentGUI,HasChildrenGUI,
 HasOrigin,IDrawableAbs,IDrawableOffset,IHitAbs,IHitOffset
{  
 int GetIndexInParent();//親のリスト内でのindex
 PVector GetOriginAbs();//絶対座標
}

class RelativeGUIObject 
 extends GUIObject
 implements IRelativeGUI, HasIndex
{
 
 
 //指定して与えられるindex
 int index;
 int GetIndex(){return this.index;}  
 void SetIndex(int index_){this.index=index_;}
 
 //親のリストにおけるthisのindex
 int GetIndexInParent()
 {
   if(this.Parent==null){return -1;}
   if(!(this.Parent instanceof HasChildrenGUI)){return -1;}    
   HasChildrenGUI hcg = (HasChildrenGUI)this.Parent;     
   return hcg.GetChildrenGUI().indexOf(this);    
 }
 
 PVector GetOrigin(){return null;}
 
 PVector GetOriginAbs()
 {
   PVector abs_pos = new PVector(0,0);
   if(!(this instanceof HasOrigin)){return abs_pos;}
   
   HasOrigin current = (HasOrigin)this;
   
   while(current!=null)
   {
     if(!(current instanceof HasOrigin)){break;}
     abs_pos.add(((HasOrigin)current).GetOrigin());      
     
     if(!(current instanceof IRelativeGUI)){break;}      
     IRelativeGUI rerative = (IRelativeGUI)current;      
     IGUIObject parent = rerative.GetParentGUI();
     
     if(parent==null){break;}
     if(!(parent instanceof HasOrigin)){break;}
     current = (HasOrigin)parent;
     
   }  
   return abs_pos;
 }
 
 //親子構造
 IGUIObject Parent;
 IGUIObject GetParentGUI(){return this.Parent;}
 void SetParentGUI(IGUIObject parent_){this.Parent = parent_;}
   
 ArrayList<IGUIObject> Children = new ArrayList<IGUIObject>();
 ArrayList<IGUIObject> GetChildrenGUI(){return this.Children;}  
 
 //blank method
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y){return false;}//絶対座標でHit判定  
 //boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y){return false;}  
 void drawAbs(float x, float y){};
 //void drawOffset(float ox, float oy){};
 
 @Override
 void draw(){drawAbs(GetOriginAbs().x,GetOriginAbs().y);} 
 @Override
 void drawOffset(float offset_x,float offset_y)
 {
   if(GetOrigin()!=null)
   {
     drawAbs(GetOrigin().x+offset_x,GetOrigin().y+offset_y);
   }
   else
   {
     drawAbs(offset_x,offset_y);
   }
 }
 @Override
 boolean Hit(float input_x, float input_y){return HitAbs(GetOriginAbs().x,GetOriginAbs().y,input_x,input_y);}
 @Override
 boolean HitOffset(float offset_x, float offset_y, float input_x, float input_y)
 {
   if(GetOrigin()!=null)
   {
     return HitAbs(GetOrigin().x+offset_x,GetOrigin().y+offset_y,input_x,input_y);
   }
   else
   {
     return HitAbs(offset_x,offset_y,input_x,input_y);
   }
 }    
}

//↑GUIBase, RelativeGUIBase

//↓RelativeDotGUI,DotGUI,RelativeBlankDot,BlankDot

//Circleと中身同じ
//幾何
//Draw,Hit,Translate
class RelativeDotGUIBase
 extends RelativeGUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "RelativeDotGUIBase";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 PVector GetOrigin(){return this.Pos.copy();}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }  

 //Absにまとめる
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return CircleHit(abs_x,abs_y,this.Radius,input_x,input_y);
 }  

 @Override
 void Translate(PVector dv)
 {
   this.Pos.add(dv);
 }    
}

//幾何
//Draw
//Hit無し,Translate無し
class RelativeBlankDot 
 extends RelativeGUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "RelativeBlankDot";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 PVector GetOrigin(){return this.Pos.copy();}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //コンストラクタ
 RelativeBlankDot(float x, float y, float r){this.Pos = new PVector(x,y);this.Radius = r;}
 RelativeBlankDot(float x, float y){this.Pos = new PVector(x,y);}
 RelativeBlankDot(PVector v, float r){this.Pos = v.copy();this.Radius = r;}  
 RelativeBlankDot(PVector v){this.Pos = v.copy();}   

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }  
}  

//Circleと中身同じ
class DotGUIBase 
 extends GUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "DotGUIBase";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 
 PVector GetOrigin(){return this.Pos.copy();}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }
   
 @Override
 boolean Hit(float x, float y){return CircleHit(new CircleConverter(this),x,y);}  
 @Override
 void Translate(PVector dv){this.Pos.add(dv);}  
}


 
//幾何
//Draw
//Hit無し,Translate無し
class BlankDot 
 extends GUIObject
 implements IDotGeometry, ICircleGeometry, HasRadius, HasOrigin
{
 String ID = "BlankDot";
 
 PVector Pos;
 PVector GetDot(){return this.Pos.copy();}
 PVector GetCenter(){return this.Pos.copy();}
 void SetCenter(PVector center){this.Pos.set(center);}
 PVector GetOrigin(){return this.Pos.copy();}
 
 float Radius = 10;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //コンストラクタ
 BlankDot(float x, float y, float r){this.Pos = new PVector(x,y);this.Radius = r;}
 BlankDot(float x, float y){this.Pos = new PVector(x,y);}
 BlankDot(PVector v, float r){this.Pos = v.copy();this.Radius = r;}  
 BlankDot(PVector v){this.Pos = v.copy();}  
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }  
}

interface ICircleConverter extends ICircleGeometry{}

class CircleConverter implements ICircleConverter
{
 IDotGeometry Dot;
 PVector GetCenter(){return Dot.GetDot();}
 void SetCenter(PVector center)
 {
   if(this.Dot instanceof HasCenter){((HasCenter)Dot).SetCenter(center);}
 }
 float GetRadius()
 {
   if(this.Dot instanceof HasRadius){return ((HasRadius)Dot).GetRadius();}
   return -1;
 }
 void SetRadius(float value)
 {
   if(this.Dot instanceof HasRadius)
   {
     ((HasRadius)Dot).SetRadius(value);
   }
 }
 
 //コンストラクタ
 CircleConverter(IDotGeometry dot){Dot = dot;}
}


//↑Dot


//↓Circle

class RelativeCircleGUIBase
 extends RelativeGUIObject 
 implements ICircleGeometry, HasOrigin
{
 String ID = "RelativeCircleGUIBase";
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }    
 
 //Absにまとめる
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y){
   return CircleHit(abs_x,abs_y,this.Radius,input_x,input_y);}  
 
 @Override
 void Translate(PVector dv)
 {
   this.Center.add(dv);
 }     
}

class CircleGUIBase
 extends GUIObject 
 implements ICircleGeometry, HasOrigin
{
 String ID = "CircleGUIBase";
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }     
 
 @Override
 boolean Hit(float x, float y){return CircleHit(this,x,y);}  
 @Override
 void Translate(PVector dv){this.Center.add(dv);}    
}


//Draw可能,Translate無し,全てのイベントハンドラが中身なし、Frameなんかを装着しなければ動かない
class RelativeBlankCircle 
 extends RelativeGUIObject 
 implements ICircleGeometry, HasOrigin
{
 String ID = "RelativeBlankCircle";
 
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //コンストラクタ
 RelativeBlankCircle(PVector center, float r_){this.Center = center.copy();this.Radius=r_;}      
 RelativeBlankCircle(PVector center){this.Center = center.copy();this.Radius=20;} 
 
 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   circle(abs_x,abs_y,this.Radius);
   stroke(DefaultStrokeColor);
 }    
}

//Draw可能,Translate無し,全てのイベントハンドラが中身なし、Frameなんかを装着しなければ動かない
class BlankCircle 
 extends GUIObject 
 implements ICircleGeometry, HasOrigin 
{
 String ID = "BlankCircle";
 
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 //コンストラクタ
 BlankCircle(PVector center, float r_){this.Center = center.copy();this.Radius=r_;}      
 BlankCircle(PVector center){this.Center = center.copy();this.Radius=20;}       
 
 @Override
 void draw()
 {
   stroke(0);
   circle(GetOrigin().x,GetOrigin().y,this.Radius);
   stroke(DefaultStrokeColor);
 }     
}


class ApproxCircleGUIBase 
 extends GUIObject 
 implements IApproxCircleGeometry, ICircleGeometry, HasOrigin, RetainedCircumference
{
 String ID = "ApproxCircleGUIBase";
 
 PVector Center;
 PVector GetCenter(){return this.Center.copy();}
 void SetCenter(PVector center){this.Center.set(center);}
 
 PVector GetOrigin(){return this.Center.copy();}
 
 float Radius;
 float GetRadius(){return this.Radius;}
 void SetRadius(float value){this.Radius = value;}
 
 float UnitRadian = 0.1;
 
 void UpdateCircumference()
 {
   this.Circumference=MakeCircleCircumference(this.Center,this.Radius,this.UnitRadian);
 }
 
 ArrayList<PVector> Circumference;
 ArrayList<PVector> GetCircumference()
 {
   if(this.Circumference==null||this.Circumference.size()<1)
   {
     this.Circumference=MakeCircleCircumference(this.Center,this.Radius,this.UnitRadian);
   }
   return this.Circumference;    
 }

 @Override
 void draw()
 {
   stroke(0);
   DrawLiner(GetCircumference());
   stroke(DefaultStrokeColor);    
 }   
 
 @Override
 boolean Hit(float x, float y)
 {
   return CircleHit(this,x,y);
 }
 
 @Override
 void Translate(PVector dv)
 {
   this.Center.add(dv);    
   if(this.Circumference!=null&&0<this.Circumference.size())
   {
     vadd(this.Circumference, dv);
   }
 }  
}

class LinerSliderGUIBase extends GUIObject
{
 ILineSegmentGeometry2D LinerArea;
 IRectGeometry Knob;
 
 
 int current_index = -1;
 int knob_index = 0;
 //int line_index = 1;
 //int line_sv_index = 2;
 //int line_ev_index = 3;
 
 PVector GetSliderSV(){return LinerArea.GetSV().copy();}
 PVector GetSliderEV(){return LinerArea.GetEV().copy();}
 PVector GetSliderLV(){return GetLV(LinerArea);}
 
 //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 GetValuePos(){return new PVector();}
 void drawValue()
 {
   fill(0);
   text("Value : "+str(GetValue()),GetValuePos().x,GetValuePos().y);
   fill(DefaultFillColor);
 }     
 
 boolean HitSliderButton(float x, float y)
 {
   return RectHit(this.Knob,x,y);
 }  
 
 LinerSliderGUIBase()
 {
   
 }
 
 @Override
 boolean Hit(float x, float y){return HitSliderButton(x,y);}
}

//Hit機能無し=>Geometryでいいか
//class PrincipleLinerGUIBase extends GUIObject implements ILineSegmentGeometry2D
//{
 
//}


//幾何
//Draw,Hit無し,Translate
class RelativeLinerGUIBase 
 extends RelativeGUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "RelativeLinerGUIBase";
 
 PVector SV;//Origin
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  

 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return PVector.sub(EV,SV);}
 
 //Absにまとめがたい
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 } 
 
 //Hit無し
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }      
}

//幾何
//Draw,Hit無し,Translate
class LinerGUIBase 
 extends GUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "LinerGUIBase";
 
 PVector SV;
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 //float point_radius = 10;
 
 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return PVector.sub(EV,SV);}
    
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 } 
 
 //標準でHit無し
 //@Override
 //boolean Hit(float x, float y)
 //{
 //  if(CircleHit(SV.x,SV.y,point_radius,x,y)){return true;}
 //  if(CircleHit(EV.x,EV.y,point_radius,x,y)){return true;}        
 //  return false;
 //}
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }    
}

//幾何
//Draw,Hit無し,Translate無し
class RelativeBlankLine   
 extends RelativeGUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "RelativeBlankLine";
 
 PVector SV;//Origin
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 
 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return this.EV.copy().sub(this.SV);}
 
 //コンストラクタ
 RelativeBlankLine(float x1, float y1, float x2, float y2){this.SV = new PVector(x1,y1);this.EV = new PVector(x2,y2);}  
 RelativeBlankLine(PVector sv, PVector ev){this.SV = sv.copy();this.EV = ev.copy();}
 
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 }    
 
 //Hit無し
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }    
}

//幾何
//Draw,Hit無し,Translate無し
class BlankLine   
 extends GUIObject
 implements ILineSegmentGeometry2D, HasOrigin
{
 String ID = "BlankLine";
 
 PVector SV;//Origin
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 
 PVector GetOrigin(){return this.SV;}
 
 PVector GetLV(){return this.EV.copy().sub(this.SV);}
 
 //コンストラクタ
 BlankLine(float x1, float y1, float x2, float y2){this.SV = new PVector(x1,y1);this.EV = new PVector(x2,y2);}  
 BlankLine(PVector sv, PVector ev){this.SV = sv.copy();this.EV = ev.copy();}
 
 @Override
 void draw()
 {
   stroke(0);
   line(this.SV.x,SV.y,EV.x,EV.y);
   stroke(DefaultStrokeColor);
 }          
 
 //Hit無し
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
 }    
}

//class TwoEndSegmentGUIBase 
//  extends GUIObject
//  implements ILineSegmentGeometry2D
//{
//  String ID = "LinerGUIBase";
 
//  PVector SV;
//  PVector GetSV(){return this.SV;}
//  PVector EV;
//  PVector GetEV(){return this.EV;}  
//  float point_radius = 10;
 
//  PVector GetLV(){return PVector.sub(EV,SV);}
 
//  int sv_index = 0;
//  int ev_index = 1;  
//  int current_index = -1;
 
//  @Override
//  void draw()
//  {
//    stroke(0);
//    circle(this.SV.x,SV.y,point_radius);
//    circle(this.EV.x,EV.y,point_radius);    
//    line(this.SV.x,SV.y,EV.x,EV.y);
//    stroke(DefaultStrokeColor);
//  }   
 
//  @Override
//  boolean Hit(float x, float y)
//  {
//    if(CircleHit(SV.x,SV.y,point_radius,x,y)){return true;}
//    if(CircleHit(EV.x,EV.y,point_radius,x,y)){return true;}        
//    return false;
//  }
 
//  @Override
//  void Translate(PVector dv)
//  {
//    this.SV.add(dv);
//    this.EV.add(dv);
//  }    
//}

class WideLinerGUIBase 
 extends GUIObject
 implements IWideSegmentGeometry, HasWide, HasOrigin, RetainedCircumference
{
 String ID = "WideLinerGUIBase";
 
 PVector SV;
 PVector GetSV(){return this.SV;}
 PVector EV;
 PVector GetEV(){return this.EV;}  
 
 PVector GetLV(){return PVector.sub(EV,SV);}
 
 PVector GetOrigin(){return this.SV;}
 
 float Wide = 10;
 float GetWide(){return this.Wide;}
   
 void UpdateCircumference()
 {
   this.Circumference=MakeWideLineCircumference(this.GetSV(),this.GetEV(),this.Wide);
 }
 
 ArrayList<PVector> Circumference;
 ArrayList<PVector> GetCircumference()
 {
   if(this.Circumference==null||this.Circumference.size()<1)
   {
     this.Circumference=MakeWideLineCircumference(this.SV,this.EV,this.Wide);
   }
   return this.Circumference;    
 }
 
 @Override
 void draw()
 {
   stroke(0);
   DrawLinerClose(this.GetCircumference());
   stroke(DefaultStrokeColor);
 }   
 
 @Override
 boolean Hit(float x, float y)
 {
   return CrossingNumber(this.GetCircumference(), new PVector(x,y));
 }
 
 @Override
 void Translate(PVector dv)
 {
   this.SV.add(dv);
   this.EV.add(dv);
   
   if(this.Circumference!=null&&0<this.Circumference.size())
   {
     vadd(this.Circumference, dv);
   }    
 }    
}


class RelativeRectGUIBase 
 extends RelativeGUIObject
 implements IRectGeometry, HasOrigin, HasOriginAbs//, HasCircumference, HasCircumferenceAbs
{ 
 //コンストラクタ
 RelativeRectGUIBase(){}
 RelativeRectGUIBase(float x, float y, float w, float h)
 {
   this.LTV = new PVector(x,y);
   this.Width = w;
   this.Height = h;
 }
 RelativeRectGUIBase(IGUIObject parent, int index, float x, float y, float w, float h)
 {
   this.Parent=parent;
   this.SetIndex(index);
   this.LTV = new PVector(x,y);
   this.Width = w;
   this.Height = h;    
 }  
 
 PVector GetOrigin(){return this.LTV;}
 
 //親Originを原点とする
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
  
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }    
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}    
}


//+幾何
class RectGUIBase 
 extends GUIObject 
 implements IRectGeometry, HasOrigin
{  
 RectGUIBase(float x, float y, float w, float h)
 {
   this.LTV = new PVector(x,y);
   this.Width = w;
   this.Height = h;
 }  
 
 String ID = "RectGUIBase";
 
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}
   
 PVector GetOrigin(){return this.LTV;}

 ArrayList<PVector> GetCoordinates()
 {
   ArrayList<PVector> ret = new ArrayList<PVector>();
   ret.add(new PVector(this.LTV.x, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y));
   ret.add(new PVector(this.LTV.x+Width, this.LTV.y+Height));
   ret.add(new PVector(this.LTV.x, this.LTV.y+Height));
   return ret;    
 }
 
 @Override
 void draw(){rect(this.LTV.x,this.LTV.y,Width,Height);}   
 @Override
 boolean Hit(float x, float y){return RectHit(this,x,y);}  
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}  
}


class RelativeBlankRect 
   extends RelativeGUIObject
 implements IRectGeometry, HasOrigin, HasOriginAbs
{
 String ID = "RelativeBlankRect";
 
 PVector LTV;//Origin
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}
 
 PVector GetOrigin(){return this.LTV;}
 
 //コンストラクタ
 RelativeBlankRect(float x, float y, float w, float h)
 {
   //super(x,y,w,h);
   this.LTV = new PVector(x,y);
   this.Width=w;
   this.Height=h;      
 }  
 
 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
}

class RelativeBlankHitRect 
   extends RelativeBlankRect
{
 
 //コンストラクタ
 RelativeBlankHitRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);     
 }   
 
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }    
}

class RelativeMovalRect 
 extends RelativeBlankHitRect
{
 
 //コンストラクタ
 RelativeMovalRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);    
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);} 
 
}


//Draw可能,Hit無し,Translate無し
class BlankRect 
 extends RectGUIBase
 implements IRectGeometry, HasOrigin
{
 String ID = "BlankRect";
 
 PVector LTV;//Origin
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}
 
 PVector GetOrigin(){return this.LTV;}
 
 BlankRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);     
 }
 
 @Override
 void draw(){rect(this.LTV.x,this.LTV.y,Width,Height);}  
 
}


//Draw可能,Translate無し,全てのイベントハンドラが中身なし、Frameなんかを装着しなければ動かない
class BlankPolygon 
 extends GUIObject 
 implements IPolylineGeometry
{
 String ID = "BlankPolygon";
 
 ArrayList<PVector> Coordinates = new ArrayList<PVector>();
 ArrayList<PVector> GetCoordinates(){return this.Coordinates;}
 
 //コンストラクタ
 BlankPolygon(PVector... vectors)
 {
   for(PVector v : vectors)
   {
     this.Coordinates.add(v.copy());
   }
 }
 
 BlankPolygon(ArrayList<PVector> vectors)
 {
   for(PVector v : vectors)
   {
     this.Coordinates.add(v.copy());
   }
 }
}


//+幾何情報
class PolygonGUIBase 
 extends GUIObject 
 implements IPolylineGeometry, HasOrigin
{  
 ArrayList<PVector> Coordinates = new ArrayList<PVector>();
 ArrayList<PVector> GetCoordinates()
 {
   return this.Coordinates;
 }
 
 PVector GetOrigin(){return this.Coordinates.get(0);}
   
 //void drawAbs(float x_,float y_)
 //{
 //  stroke(0);
 //  beginShape();
 //  for(PVector v : this.Coordinates)
 //  {
 //    vertex(x_+v.x,y_+v.y);
 //  }
 //  endShape(CLOSE);
 //}
 
 @Override
 void draw()
 {
   //this.draw(0,0);
   stroke(0);
   beginShape();
   for(PVector v : this.Coordinates)
   {
     vertex(v.x,v.y);
   }
   endShape(CLOSE);    
 } 
 
 @Override
 boolean Hit(float x, float y)
 {
   //return PointInPolygon(this.Coordinates, new PVector(x,y));
   return CrossingNumber(this.Coordinates, new PVector(x,y));
 }
 
 @Override
 void Translate(PVector dv)
 {
   for(PVector v : this.Coordinates){v.add(dv);}
 }  
}

class MovalDot extends BlankDot
{
 boolean pressed = false;  
 //コンストラクタ
 MovalDot(float x, float y, float r){super(x,y,r);}  
 MovalDot(float x, float y){super(x,y);}
 MovalDot(PVector v, float r){super(v,r);}
 MovalDot(PVector v){super(v);}     
 
 @Override
 void mousePressed(){ if(this.Hit(mouseX, mouseY)){pressed = true;}}  
 @Override
 void mouseDragged() {if(pressed){this.Translate(dv);}}
 @Override
 void mouseReleased() {pressed = false;}  
 //@Override
 //boolean Hit(float x, float y){return CircleHit(this,x,y);}  
 //@Override
 //void Translate(PVector dv){this.Pos.add(dv);}    
}

//動かすことができるLineGUI
//各種イベントを実装する
class MovalLine extends BlankLine
{
 boolean pressed = false;
 void mousePressed(){if(this.Hit(mouseX, mouseY)){pressed = true;}}  
 void mouseDragged(){if(pressed){this.Translate(dv);}}
 void mouseReleased(){pressed = false;}   
 
 //コンストラクタ
 MovalLine(float x1, float y1, float x2, float y2){super(x1,y1,x2,y2);}  
 MovalLine(PVector sv, PVector ev){super(sv,ev);}  
}

////動かすことができるPolygonGUI
////各種イベントを実装する
//class MovalPolygon extends BlankPolygon
//{
//  boolean pressed = false;
//  void mousePressed() {if(this.Hit(mouseX, mouseY)){pressed = true;}}  
//  void mouseDragged() {if(pressed){this.Translate(dv);} }
//  void mouseReleased() { pressed = false; }   
 
//  MovalPolygon(PVector... vectors)
//  {
//    super(vectors);  
//  }  

//  MovalPolygon(ArrayList<PVector> vectors)
//  {
//    super(vectors);  
//  }  
//}

//動かすことができるRectGUI
//他のGUIのつまみボタン的なものとして
//既にラベルと機能が被っている
class MovalRect extends BlankRect
{
 boolean pressed = false;
 void mousePressed() {if(this.Hit(mouseX, mouseY)){pressed = true;}}  
 void mouseDragged() {if(pressed){this.Translate(dv);} }
 void mouseReleased() { pressed = false; }   
 
 MovalRect(float x, float y, float w, float h)
 {
   super(x,y,w,h);  
 }  
}


//動かすことができるCircleGUI
//他のGUIのつまみボタン的なものとして
class MovalCircle extends CircleGUIBase
{
 boolean pressed = false;
 void mousePressed(){if(this.Hit(mouseX, mouseY)){pressed = true;}}  
 void mouseDragged(){if(pressed){this.Translate(dv);}}
 void mouseReleased(){ pressed = false; }  
 
 MovalCircle(float x, float y, float r_)
 {
   this.Center = new PVector(x,y);
   this.Radius=r_;      
 }    
 MovalCircle(PVector center, float r_)
 {
   this.Center = new PVector(center.x,center.y);
   this.Radius=r_;      
 }       
}

class MovalApproxCircle extends ApproxCircleGUIBase
{
 boolean pressed = false;
 void mousePressed() {if(this.Hit(mouseX, mouseY)){pressed = true;}}  
 void mouseDragged()
 {
   if(pressed)
   {
     this.Translate(dv);
     UpdateCircumference();
   } 
 }
 void mouseReleased() { pressed = false; }  
   
   //コンストラクタ
 MovalApproxCircle(PVector center, float radius, float unit_radian){
   this.Center = center.copy();this.Radius=radius;this.UnitRadian=unit_radian;}          
 MovalApproxCircle(PVector center, float radius){this.Center = center.copy();this.Radius=radius;}      
 MovalApproxCircle(float x, float y, float radius, float unit_radian){
   this.Center = new PVector(x,y);this.Radius=radius;this.UnitRadian=unit_radian;}        
 MovalApproxCircle(float x, float y, float radius){this.Center = new PVector(x,y);this.Radius=radius;}    
  

}



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

class NodeObject
 extends RelativeGUIObject
 implements INode, IRelativeGUI, HasInputPorts, HasOutputPorts, IPullable
{
 ArrayList<IOutputPort> GetOutputPorts(){return null;}
 ArrayList<IInputPort> GetInputPorts(){return null;}

 void Pull()
 {
   if(this.GetInputPorts()==null){return;}
   for(IInputPort port : this.GetInputPorts())
   {
     if(port == null){continue;}
     if(!(port instanceof IPullable)){continue;}      
     ((IPullable)port).Pull();
   }    
 }     
 
 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
}


class HeaderGUI 
 extends RelativeRectGUIBase
 implements HasParentGUI
{
 String ID = "HeaderGUI";
   
 String text = "";
 String GetText(){return this.text;}
 void SetText(String text_){this.text=text_;}
 
 //コンストラクタ
 HeaderGUI(IGUIObject parent_, int index_, PVector v, float w, float h)
 {
   this.index = index_;
   this.Parent=parent_;
   this.LTV = new PVector(v.x,v.y);
   this.Width = w;
   this.Height = h;    
 }
 
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);   
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
       
   fill(0);
   text(this.GetText(),abs_x,abs_y+this.Height);
   fill(DefaultFillColor);
 }

 //この能力はヘッダー特有
 //親を移動させる能力Parent Translate
 @Override
 void Translate(PVector dv)
 {
   if(Parent==null)
   {
     this.LTV.add(dv);
     return;
   }        
   this.Parent.Translate(dv);
 }    
}

class ParameterGUI 
 extends RelativeRectGUIBase 
 implements HasParentGUI
{
 float1 value;
 
 String ID = "ParameterGUI";
 
 String text = "";
 String GetText(){return this.text;}
 void SetText(String text_){this.text=text_;}
 
 //コンストラクタ
 ParameterGUI(IGUIObject parent_, int index_, float1 value_, PVector v, float w, float h)
 {
   this.index = index_;
   this.Parent=parent_;
   
   //参照ごと補足
   this.value=value_;
   
   this.LTV = new PVector(v.x,v.y);
   this.Width = w;
   this.Height = h;    
 }
 
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);   
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);        
   drawParam();
 } 

 void drawParam()
 {
   fill(0);
   text(GetText()+" : "+str(value.x),GetOriginAbs().x, GetOriginAbs().y+this.Height);
   fill(DefaultFillColor);
 } 
 
 //void drawParam(float param)
 //{
 //  fill(0);
 //  text(GetText()+" : "+str(param),GetOriginAbs().x, GetOriginAbs().y+this.Height);
 //  fill(DefaultFillColor);
 //}    
}

class OutputNodeRect
 extends NodeObject 
 implements IRectGeometry, HasOrigin, HasOriginAbs
{
 String ID = "OutputNodeRect";
 
 //float1 Output;
 OutputPort OutputPort;
 @Override
 ArrayList<IOutputPort> GetOutputPorts(){
   return new ArrayList<IOutputPort>(Arrays.asList(this.OutputPort));}   
 
 PVector GetOrigin(){return this.LTV;}
 
 //親Originを原点とする
 PVector LTV;
 PVector GetLTV(){return this.LTV;}  
 
 float Width;
 float GetWidth(){return this.Width;}  
 float Height;   
 float GetHeight(){return this.Height;}

 //Absにまとめる
 @Override
 void drawAbs(float abs_x,float abs_y)
 {
   stroke(0);
   rect(abs_x,abs_y,Width,Height);
   stroke(DefaultStrokeColor);
 }
  
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
   return RectHit(abs_x,abs_y,this.Width,this.Height,input_x,input_y);
 }
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}     
 
}

//単一のOutputPortを持つ
class SliderNode 
 extends OutputNodeRect 
 
{  
 String ID = "SliderNode";
 
 //this GUI内部のHitエリア判定
 int current_index = -1; 
 
 float1 min = new float1(0);
 float1 max = new float1(100);  
 float GetValue(int index)
 {
   if(index == this.slider1.GetIndex()){return this.slider1.GetValue();}
   return 0;
 }
 void SetValue(int index, float value)
 {
   if(index == this.slider1.GetIndex()){this.slider1.SetValue(value);}
 }
 
 PVector GetValuePos(){return new PVector(LTV.x, this.footer2.GetOriginAbs().y);}
 void drawValue()
 {
   fill(0);
   text("Value : "+str(slider1.GetValue()),GetValuePos().x,GetValuePos().y);
   fill(DefaultFillColor);
 }     
   
 float port_w = 10;
 float port_h = 10;  

 float header_h = 10;
 float footer_h = 10;
 float slider_h = 10;
     
 //Header,Footer
 HeaderGUI header1;
 ParameterGUI footer1;
 ParameterGUI footer2;  
 RectSlider slider1;
 
 void children_setup(String value_name)
 {
   header1 = new HeaderGUI(this, 1, new PVector(0,0),Width,header_h); 
   header1.SetText(value_name);    
   slider1 = new RectSlider(this, 2, 0,header_h,Width,slider_h);
   OutputPort = new float1OutputPort(this, this, 3, Width, header_h, port_w, port_h);
   OutputPort.SetOutputMethod(new SliderOutputMethod());
   
   footer1 = new ParameterGUI(this, 4, min, new PVector(0,Height-footer_h),this.Width,footer_h); 
   footer1.text="min";
   footer2 = new ParameterGUI(this, 5, max, new PVector(0,Height-footer_h-footer_h),this.Width,footer_h);
   footer2.text="max";    
 }
 
 //ClientArea
 PVector GetClientPosAbs(){return new PVector(LTV.x,LTV.y+this.header1.Height);}
 float GetClientAreaH(){return this.Height-this.header1.Height-this.footer1.Height;}      

 //コンストラクタ
 SliderNode(String name_, float x_, float y_, float min_, float max_, float value_)
 {
   //value_name = name_;    
   this.LTV = new PVector(x_,y_);
   this.Width=100;
   this.Height=100;
   
   min.x=min_;max.x=max_;
   //SetValue(value_);     
   children_setup(name_);
 }
 
 SliderNode(String name_, float x, float y)
 {
   this.LTV = new PVector(x,y);    
   this.Width=100;
   this.Height=100; 
   children_setup(name_);
 }
 
 @Override
 void drawAbs(float abs_x, float abs_y)
 {
   rect(abs_x,abs_y,Width,Height);
   drawValue();

   //header, footer
   //this.header1.drawOffset(abs_x,abs_y);  
   //this.footer1.drawOffset(abs_x,abs_y);
   //this.footer2.drawOffset(abs_x,abs_y);
   //this.slider1.drawOffset(abs_x,abs_y);   
   //this.OutputPort.drawOffset(abs_x,abs_y);

   this.header1.draw();  
   this.footer1.draw();
   this.footer2.draw();
   this.slider1.draw(); 
   this.OutputPort.draw();
 }
 
 @Override
 boolean HitAbs(float abs_x, float abs_y, float input_x, float input_y)
 {
     if(this.header1.HitOffset(abs_x,abs_y,input_x,input_y)){current_index=this.header1.GetIndex();}
     if(this.slider1.HitOffset(abs_x,abs_y,input_x,input_y))
     {
       current_index=this.slider1.GetIndex();
     } 
     if(this.OutputPort.HitOffset(abs_x,abs_y,input_x,input_y)){current_index=OutputPort.GetIndex();}
     return true; 
 }
 
 @Override
 int HitIndex(float x, float y)
 {
   if(this.header1.HitOffset(LTV.x,LTV.y,x,y)){return this.header1.GetIndex();}
   if(this.slider1.HitOffset(LTV.x,LTV.y,x,y)){return this.slider1.GetIndex();}    
   if(this.OutputPort.HitOffset(LTV.x,LTV.y,x,y)){OutputPort.GetIndex();}
   return -1;
 }  
 
 @Override
 IOPort GetPort(int index)
 {
   if(index==OutputPort.GetIndex()){return OutputPort;}
   return null;
 }
 
 @Override
 PVector GetPortPos(int index)
 {
   if(index==OutputPort.GetIndex()){return this.OutputPort.GetOriginAbs();}    
   return null;    
 }
 
 @Override
 int GetPortIndex(IOPort port)
 {
   if(OutputPort==port){return OutputPort.GetIndex();}
   return -1;
 }

 @Override
 void mousePressed()
 {
   if(current_index<0){return;}    
   if(current_index==this.slider1.GetIndex())
   {
     this.slider1.mousePressed();
   }  
 }
 
 @Override
 void mouseDragged()
 {
   if(current_index<0){return;}
   if(current_index==this.header1.GetIndex()){this.header1.Translate(dv);}
   
   if(current_index==this.slider1.GetIndex())
   {
     this.slider1.mouseDragged();
     //this.t=slider1.t;
   }
   
   if(current_index==OutputPort.GetIndex())
   {
     line(OutputPort.GetOriginAbs().x,OutputPort.GetOriginAbs().y,mouseX,mouseY);
   }    
 }
 
 @Override
 void mouseReleased()
 {
   this.slider1.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);
     }
   }
 }  
 
 @Override
 void Translate(PVector dv){this.LTV.add(dv);}  
}



















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