2015-10-25 23 views
8

私は、すべてのノードがその子へのポインタのリストを(再帰的に)保持するツリーデータ構造を実装しました。放射状ツリーレイアウトアルゴリズム

私はツリーを視覚化するために(x、y)座標を計算しようとしています。 私はこの記事を経て:

for (int i = 0; i < GetDepth()+1; i++) 
{ 
    if (i == 0) 
    { 
     GetNodesInDepth(i).at(0)->SetXRadial(MIDDLE(m_nWidth)); 
     GetNodesInDepth(i).at(0)->SetYRadial(MIDDLE(m_nHeight)); 
     continue; 
    } 

    double dNodesInDepth = GetNodesInDepth(i).size(); 
    double dAngleSpace = 2 * PI/dNodesInDepth; 

    for (int j = 0; j < dNodesInDepth; j++) 
    { 
     Node * pCurrentNode = GetNodesInDepth(i).at(j); 

     pCurrentNode->SetXRadial((SPACING * i) * qCos(j * dAngleSpace) + MIDDLE(m_nWidth)); 
     pCurrentNode->SetYRadial((SPACING * i) * qSin(j * dAngleSpace) + MIDDLE(m_nHeight)); 
     pCurrentNode->m_dAngle = dAngleSpace * j; 

     if (pCurrentNode->IsParent()) 
     { 
     //..(I'm stuck here)..// 
     } 
    } 
} 

を私は:これは私がこれまでに書かれたものである。すなわち、私は、どのように最初のレベルの過去GESTに把握することはできません

http://gbook.org/projects/RadialTreeGraph.pdf

カット限界を計算する方法がわからない、などの二等分線 が、これは私の引き出しは、これまでにやったことです:

This image shows that two edges of the graph intersect

これは明らかに2番目(0ベース)のレベルから探しているものではありません。

私が探しているものを得るために必要なすべての情報にアクセスできます。

+2

あなたのグラフで(明確にするために)、間違って配置されたのは30ノードだけですか?この場合、 –

+0

、はい。 –

+1

あなたが並べ替えを与えたリンクには、あなたがしようとしているもののコードがあります。それはかなり面倒ですが、限界と二等分線をさまざまなレベルで計算しています。コードの理解に問題がありますか? –

答えて

6

恐らくあなたは今自分でそれを理解しました。ない場合は、ここで

double dNodesInDepth = GetNodesInDepth(i).size(); 
double dAngleSpace = 2 * PI/dNodesInDepth; 

あなたはそのレベルで2つのノードだけ、dNodesInDepth = 2があるとして、あなたの第二のレベルでPI(180 degreees)に角度スペースを設定しています。そのため、ノード20を描画した後、ノード30は180度離れている。その方法は、その角度が小さくなるため、非常に密度の高いツリーの場合は問題ありません。しかし、あなたの場合、角度をできるだけ親の角度に近づけたいと思っています。したがって、レベル2以上のノードの親の角度を取得し、子どもを広げて角度空間がsibilingAngle = min(dAngleSpace, PI/10)になるようにすることをお勧めします。したがって、最初の子供は親の正確な角度を持ち、残りの子供はお互いからsibilingAngleです。あなたはそのアイデアを得て、おそらくより良い方法で来るでしょう。私はminを使用しています。そのレベルであまりにも多くのノードを持っている場合は、ノードを互いに近づけたいと思っています。

リンク先の記事には、Figure 2 – Tangent and bisector limits for directoriesで示されている解決策が使用されています。あなたが子供の絶対角度を決定するのではなく、親に対する場合は、その方法は非常に多くの操作を行おうとまさにん単純/クリーナーソリューションを持つことができるので、私はずっとその方法が好きではありません。

更新:

次のコードは、このイメージを生成します。

enter image description here

私はあなたが簡単に子ノードをセンタリングする方法を見つけ出すとなどができると思います

#include <cairo/cairo.h> 
#include <math.h> 
#include <vector> 

using namespace std; 

class Node { 
public: 
    Node() { 
     parent = 0; 
     angle = 0; 
     angleRange = 2*M_PI; 
     depth = 0; 
    } 
    void addChildren(int n) { 
     for (int i=0; i<n; i++) { 
      Node* c = new Node; 
      c->parent = this; 
      c->depth = depth+1; 
      children.push_back(c); 
     } 
    } 
    vector<Node*> children; 
    float angle; 
    float angleRange; 
    Node* parent; 
    int depth; 
    int x, y; 
}; 

void rotate(float x, float y, float angle, float& nx, float& ny) { 
    nx = x * cos(angle) - y * sin(angle); 
    ny = x * sin(angle) + y * cos(angle); 
} 
void draw(Node* root, cairo_t *cr) { 
    if (root->parent == 0) { 
     root->x = root->y = 300; 
     cairo_arc(cr, root->x, root->y, 3, 0, 2 * M_PI); 
    } 

    int n = root->children.size(); 
    for (int i=0; i<root->children.size(); i++) { 
     root->children[i]->angle = root->angle + root->angleRange/n * i; 
     root->children[i]->angleRange = root->angleRange/n; 

     float x, y; 
     rotate(40 * root->children[i]->depth, 0, root->children[i]->angle, x, y); 
     root->children[i]->x = 300+x; 
     root->children[i]->y = 300+y; 

     cairo_move_to(cr, root->x, root->y); 
     cairo_line_to(cr, root->children[i]->x, root->children[i]->y); 
     cairo_set_source_rgb(cr, 0, 0, 0); 
     cairo_stroke(cr); 

     cairo_arc(cr, 300+x, 300+y, 3, 0, 2 * M_PI); 
     cairo_set_source_rgb(cr, 1, 1, 1); 
     cairo_stroke_preserve(cr); 
     cairo_set_source_rgb(cr, 0, 0, 0); 
     cairo_fill(cr); 

     draw(root->children[i], cr); 
    } 
} 

int main(void) { 
    Node root; 
    root.addChildren(4); 
    root.children[0]->addChildren(3); 
    root.children[0]->children[0]->addChildren(2); 
    root.children[1]->addChildren(5); 
    root.children[2]->addChildren(5); 
    root.children[2]->children[1]->addChildren(2); 
    root.children[2]->children[1]->children[1]->addChildren(2); 

    cairo_surface_t *surface; 
    cairo_t *cr; 

    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 600, 600); 
    cr = cairo_create(surface); 

    cairo_rectangle(cr, 0.0, 0.0, 600, 600); 
    cairo_set_source_rgb(cr, 1, 1, 1); 
    cairo_fill(cr); 

    cairo_set_line_width(cr, 2); 

    for (int i=0; i<6; i++) { 
     cairo_arc(cr, 300, 300, 40*i, 0, 2 * M_PI); 
     cairo_set_source_rgb(cr, .5, .5, .5); 
     cairo_stroke(cr); 
    } 

    draw(&root, cr); 

    cairo_surface_write_to_png(surface, "image.png"); 

    cairo_destroy(cr); 
    cairo_surface_destroy(surface); 

    return 0; 
} 

アップデート2: ちょうどここに、あなたのためにそれを容易にするためには、ノードをセンタリングする方法である:

以上人口のツリー表示

enter image description here

for (int i=0; i<root->children.size(); i++) { 
    float centerAdjust = 0; 
    if (root->parent != 0) { 
     centerAdjust = (-root->angleRange + root->angleRange/n)/2; 
    } 
    root->children[i]->angle = root->angle + root->angleRange/n * i + centerAdjust; 
    root->children[i]->angleRange = root->angleRange/n; 

enter image description here

+0

私はあなたが提案したものを試しました。私はどのように各レベルの角度を定義するかは確かではありません。すべてのレベルのすべての角度空間を均等にレベル円に分割したもの –

+0

ok、更新情報を参照 – fireant

+0

ellaboratedソリューションをありがとう。私はカイロの部分を使わずにコードを書こうとしましたが(x、y座標だけが必要です)、同じX軸にすべてを入れました –

1

ここでは、t動作するはず、彼は記事(注:私はあなたのプログラムの他の部分を持っていないので、私はそれをコンパイルしませんでした):

void Tree::CalculateAngles() 
{ 
    // IsEmpty() returns true if the tree is empty, false otherwise 
    if (!IsEmpty()) 
    { 
     Node* pRoot = GetNodesInDepth(0).at(0); 
     pRoot->SetAngle(0); 
     // Relative to the current angle 
     pRoot->SetTangentLimit(PI); 
     // Absolute limit 
     pRoot->SetLowerBisector(-PI); 
     pRoot->SetHigherBisector(PI); 
    } 
    for (int depth = 1; depth < GetDepth() + 1; depth++) 
    { 
     double dDepth = (double)depth; 
     // The last non-leaf node in of the current depth (i.e. node with children) 
     Node* pPreviousNonleafNode = NULL; 
     // The first non-leaf node 
     Node* pFirstNonleafNode = NULL; 
     // The parent of the previous node 
     Node* pPreviousParent = NULL; 
     int indexInCurrentParent = 0; 
     double dTangentLimt = acos(dDepth/(dDepth + 1.0)); 
     for (int i = 0; i < GetNodesInDepth(depth).size(); i++) 
     { 
      Node* pCurrentNode = GetNodesInDepth(depth).at(i); 
      Node* pParent = pCurrentNode->GetParent(); 
      if (pParent != pPreviousParent) 
      { 
       pPreviousParent = pParent; 
       indexInCurrentParent = 0; 
      } 
      // (GetMaxChildAngle() - GetMinChildAngle())/GetChildCount() 
      double angleSpace = pParent->GetAngleSpace(); 
      pCurrentNode->SetAngle(angleSpace * (indexInCurrentParent + 0.5)); 
      pCurrentNode->SetTangentLimit(dTangentLimt); 
      if (pCurrentNode->IsParent()) 
      { 
       if (!pPreviousNonleafNode) 
       { 
        pFirstNonleafNode = pCurrentNode; 
       } 
       else 
       { 
        double dBisector = (pPreviousNonleafNode->GetAngle() + pCurrentNode->GetAngle())/2.0; 
        pPreviousNonleafNode->SetHigherBisector(dBisector); 
        pCurrentNode->SetLowerBisector(dBisector); 
       } 
       pPreviousNonleafNode = pCurrentNode; 
      } 
      indexInCurrentParent++; 
     } 
     if (pPreviousNonleafNode && pFirstNonleafNode) 
     { 
      if (pPreviousNonleafNode == pFirstNonleafNode) 
      { 
       double dAngle = pFirstNonleafNode->GetAngle(); 
       pFirstNonleafNode->SetLowerBisector(dAngle - PI); 
       pFirstNonleafNode->SetHigherBisector(dAngle + PI); 
      } 
      else 
      { 
       double dBisector = PI + (pPreviousNonleafNode->GetAngle() + pFirstNonleafNode->GetAngle())/2.0; 
       pFirstNonleafNode->SetLowerBisector(dBisector); 
       pPreviousNonleafNode->SetHigherBisector(dBisector); 
      } 
     } 
    } 
} 

void Tree::CalculatePositions() 
{ 
    for (int depth = 0; depth < GetDepth() + 1; depth++) 
    { 
     double redius = SPACING * depth; 
     for (int i = 0; i < GetNodesInDepth(depth).size(); i++) 
     { 
      Node* pCurrentNode = GetNodesInDepth(depth).at(i); 
      double angle = pCurrentNode->GetAngle(); 
      pCurrentNode->SetXRadial(redius * qCos(angle) + MIDDLE(m_nWidth)); 
      pCurrentNode->SetYRadial(redius * qSin(angle) + MIDDLE(m_nHeight)); 
     } 
    } 
} 

void Tree::CalculateLayout() 
{ 
    CalculateAngles(); 
    CalculatePositions(); 
} 

double Node::GetAngleSpace() 
{ 
    return (GetMaxChildAngle() - GetMinChildAngle())/GetChildCount(); 
} 

注:あなたがする必要はありませんので、私はあなたのコードのスタイルを模倣しようとしましたプログラムの他の部分と一致するようにリファクタリングします。

P.S.あなたがバグを見つけたら、私のコメントに私に連絡してください - 私は私の答えを編集します。

+0

私は努力を感謝します。私は明日これを試し、あなたがどんな問題についても投稿してくれるでしょう。 –

+0

実際には、次の行に問題があります。double angleSpace = pParent-> GetAngleSpace();このメンバーフィールド(angleSpace)はどこでも変更されずにアクセスされるだけであることは私には奇妙に見えます。 また、線:double mid_x = MIDDLE(m_nWidth)など、 m_nWidthは画面のレイアウトであり、定数です(ツリー宣言では無視されます)。あなたはそこで何を達成しようとしていますか? –

+0

ゲッターには対応するフィールドとセッターが必要です。この関数は、角度範囲と子ノードの数に基づいて角度空間を計算することになっています(コメント参照)。 mid_xについて:それは本当に何かを改善するものではありませんが、どちらか悪いことではありません。 –

関連する問題