6

サンプル画像 hereのように、ノード図のようなツリーを作成しようとしています。次のコードがあります。

    private void DrawNode(Graphics g, Node<T> node, float xOffset, float yOffset)
    {
        if (node == null)
        {
            return;
        }

        Bitmap bmp = (from b in _nodeBitmaps where b.Node.Value.Equals(node.Value) select b.Bitmap).FirstOrDefault();

        if (bmp != null)
        {
            g.DrawImage(bmp, xOffset, yOffset);

            DrawNode(g, node.LeftNode, xOffset - 30 , yOffset + 20);
            DrawNode(g, node.RightNode, xOffset + 30, yOffset + 20);
        }
    }

私のコードはほとんど機能しています。私が抱えている問題は、一部のノードが重複していることです。上の図では、ノード 25 と 66 が重なっています。その理由は、数学的に左側のノードと右側のノードを等しいスペースに配置するため、親の右側のノードが隣接する親の左側のノードと重なるためです。この問題を解決するにはどうすればよいですか?

アップデート:

これは、dtb の提案の後に行ったコードの更新です。

            int nodeWidth = 0;
            int rightChildWidth = 0;

            if (node.IsLeafNode)
            {
                nodeWidth = bmp.Width + 50;
            }
            else
            {
                int leftChildWidth = 0;

                Bitmap bmpLeft = null;
                Bitmap bmpRight = null;

                if (node.LeftNode != null)
                {
                    bmpLeft =
                        (from b in _nodeBitmaps where b.Node.Value.Equals(node.LeftNode.Value) select b.Bitmap).
                            FirstOrDefault();
                    if (bmpLeft != null)
                        leftChildWidth = bmpLeft.Width;
                }
                if (node.RightNode != null)
                {
                    bmpRight =
                        (from b in _nodeBitmaps where b.Node.Value.Equals(node.RightNode.Value) select b.Bitmap).
                            FirstOrDefault();
                    if (bmpRight != null)
                        rightChildWidth = bmpRight.Width;
                }

                nodeWidth = leftChildWidth + 50 + rightChildWidth;
            }


            g.DrawImage(bmp, xOffset + (nodeWidth - bmp.Width) / 2, yOffset);

            if (node.LeftNode != null)
            {
                DrawNode(g, node.LeftNode, xOffset, yOffset + 20);
            }
            if (node.RightNode != null)
            {
                DrawNode(g, node.RightNode, xOffset + nodeWidth - rightChildWidth, yOffset + 20);
            }

このコードのスクリーンショットを次に示します。スクリーンショット

4

2 に答える 2

5

それぞれに幅を割り当てますnode:

  • 葉の幅は画像の幅ですw
  • ノードの幅は、左の子ノードの幅 + 定数d+ 右の子ノードの幅です。

       図

void CalculateWidth(Node<T> node)
{
    node.Width = 20;
    if (node.Left != null)
    {
        CalculateWidth(node.Left);
        node.Width += node.Left.Width;
    }
    if (node.Right != null)
    {
        CalculateWidth(node.Right);
        node.Width += node.Right.Width;
    }
    if (node.Width < bmp.Width)
    {
        node.Width = bmp.Width;
    }
}

ルート ノード と から始めてx = 0、幅の半分の位置に画像を描画します。オフセットはxです。次に、各子ノード
の位置を計算し、再帰します。x

void DrawNode(Graphics g, Node<T> node, double x, double y)
{
    g.DrawImage(x + (node.Width - bmp.Width) / 2, y, bmp);

    if (node.Left != null)
    {
        DrawNode(g, node.Left, x, y + 20);
    }
    if (node.Right != null)
    {
        DrawNode(g, node.Right, x + node.Width - node.Right.Width, y + 20);
    }
}

使用法:

CalculateWidth(root);

DrawNode(g, root, 0, 0);
于 2012-04-11T00:22:23.127 に答える
1

それらが重なり合うというのは正しいです。これは、ツリーをたどって xOffset に固定値を加算/減算しているためです。例の図では、実際には固定オフセットではなく、垂直位置に関して対数指数です。下に行くほど、オフセットを小さくする必要があります。

30 を に置き換えますA * Math.Log(yOffset)。ここで、A は適切に見えるまで微調整する必要があるスケーリング値です。

編集:またはそれは指数関数的ですか?私はこれをうまく視覚化することはできません。代わりに欲しくなるかもしれませんA * Math.Exp(-B * yOffset)。(負の値は重要です。これは、yOffset が大きいほど小さくなることを意味します。これが必要です。)

ABオフセットがどれだけ速く小さくなるかを制御しながら、マスターの線形スケーリング係数のようになります。

double A = some_number;
double B = some_other_number;
int offset = (int)(A * Math.Exp(-B * yOffset));
DrawNode(g, node.LeftNode, xOffset - offset , yOffset + 20);
DrawNode(g, node.RightNode, xOffset + offset, yOffset + 20);

アップデート:

double A = 75f;
double B = 0.05f;
int offset = (int)(A * Math.Exp(-B * (yOffset - 10)));
DrawNode(g, node.LeftNode, xOffset - offset, yOffset + 20);
DrawNode(g, node.RightNode, xOffset + offset, yOffset + 20);

呼び出し:

DrawNode(e.Graphics, head, this.ClientSize.Width / 2, 10f);

Expの- 10は重要です。これは、ヘッドの初期 yOffset です。以下を生成します。

正確なマージン/パディング制御が必要な場合は、必ず dtb の方法を使用してください。ただし、1 つの数式で 3 行を追加することは、得られる数学的なソリューションと同じくらいエレガントだと思います。

更新 2:

忘れていたもう 1 つのこと: base を使用してe = 2.7183いますが、2 に近いものが必要です。論理的には正確に 2 を使用しますが、ノードの幅がゼロでないため、2.1 のように少し大きいものが必要になる場合があります。を掛けることで基数を変更できBますMath.Log(new_base)

double B = 0.05f * Math.Log(2.1);

0.05f の値をどのように取得したかについても説明する必要があります。基本的にyOffset、ツリーのレベルごとに 20 ずつ増加します。yOffset頭のイニシャル (私の場合は 10)を差し引くと、最初の数yOffsetは 0、20、40、60 などになります。行ごとに x オフセットを半分にカットする必要があります。あれは、

2 ^ (-0B) = 1
2 ^ (-20B) = 0.5
2 ^ (-40B) = 0.25

明らかに、B は 1/20、つまり 0.05 である必要があります。Math.Log(2.1)次の関係から値を取得します。

base ^ exponent == e ^ (ln(base) * exponent)

したがって、ベース 2.1 では、次のようになります。

于 2012-04-10T23:58:21.920 に答える