2012年12月8日土曜日

トーナメント表を作る


トーナメント表は、ツリー構造をしています。
なので、トーナメント表のデータは、XMLで記述することにします。

要素名はなんでもかまいません。
トーナメント表の性質として、ツリー構造をつぎのように制限します。

  • リーフのテキストノードにチーム名や個人名を記述する。
  • 中間ノードは、2つのノードをもつ。(二分岐)

描画するアルゴリズムは、2回、再帰的にノードをたどっています。
1回目は、SetLineAttributeメソッドで、リーフの深さと数をカウントし、帰りがけに中間ノードのy座標の位置を計算しています。
2回目は、DrawLineメソッドで、実際の線分を描画しています。

XMLファイルのサンプルです。

<?xml version="1.0" encoding="utf-8" ?>
<game>
  <game>
    <game>
      <team>悟空</team>
      <team>ベジータ</team>
    </game>
    <team>クリリン</team>
  </game>
  <game>
    <team>フリーザ</team>
    <team>ヤムチャ</team>
  </game>
</game>


このファイルを開くとこうなります。


MainWindow.xaml

<Window x:Class="TournamentTable.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="350" Width="525">
    <Window.CommandBindings>
        <CommandBinding Command="Open" Executed="OpenCommandHandler"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Menu Grid.Row="0">
            <MenuItem Command="Open" />
        </Menu>
        <Canvas Grid.Row="1" Name="canvas1" Margin="10">
            <Canvas.Resources>
                <Style TargetType="Line">
                    <Setter Property="Stroke" Value="Black"/>
                </Style>
            </Canvas.Resources>
        </Canvas>
    </Grid>
</Window>

MainWindow.xaml.cs

using Microsoft.Win32;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Xml;

namespace TournamentTable
{
    public partial class MainWindow Window
    {
        private int scale = 40;
        private double textWidth = 60;
        private int maxLevel;
        private int leafCount;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OpenCommandHandler(object sender, ExecutedRoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                XmlDocument xdoc = new XmlDocument();
                xdoc.Load(ofd.FileName);
                this.maxLevel = 0;
                this.leafCount = 0;
                SetLineAttribute(xdoc.DocumentElement, 0);
                this.canvas1.Children.Clear();
                DrawLine(xdoc.DocumentElement);
            }
        }

        private void DrawLine(XmlElement ele)
        {
            double level = double.Parse(ele.Attributes["level"].Value);
            double y = double.Parse(ele.Attributes["y"].Value) * scale;
            if (ele.ChildNodes.Count == 2)
            {
                XmlElement c1 = (XmlElement)ele.ChildNodes[0];
                XmlElement c2 = (XmlElement)ele.ChildNodes[1];
                DrawLine(c1);
                DrawLine(c2);

                Line cLine = new Line();
                cLine.X1 = textWidth + (maxLevel - level) * scale;
                cLine.Y1 = double.Parse(c1.Attributes["y"].Value) * scale;
                cLine.X2 = textWidth + (maxLevel - level) * scale;
                cLine.Y2 = double.Parse(c2.Attributes["y"].Value) * scale;
                this.canvas1.Children.Add(cLine);

                Line line = new Line();
                line.X1 = textWidth + (maxLevel - level) * scale;
                line.Y1 = y;
                line.X2 = textWidth + (maxLevel - level + 1) * scale;
                line.Y2 = y;
                this.canvas1.Children.Add(line);
            }
            else
            {
                Line line = new Line();
                line.X1 = textWidth + (maxLevel - level + 1) * scale;
                line.Y1 = y;
                line.X2 = textWidth;
                line.Y2 = y;
                this.canvas1.Children.Add(line);

                Border border = new Border();
                border.Height = scale;
                border.Width = textWidth;
                border.SetValue(Canvas.TopProperty, y - 0.5 * scale);
                TextBlock tb = new TextBlock(new Run(ele.InnerText));
                tb.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                border.Child = tb;
                this.canvas1.Children.Add(border);
            }
        }

        private void SetLineAttribute(XmlElement ele, int level)
        {
            maxLevel = System.Math.Max(maxLevel, level);
            if (ele.ChildNodes.Count == 2)
            {
                XmlElement c1 = (XmlElement)ele.ChildNodes[0];
                XmlElement c2 = (XmlElement)ele.ChildNodes[1];
                SetLineAttribute(c1, level + 1);
                SetLineAttribute(c2, level + 1);
                double y = (double.Parse(c1.Attributes["y"].Value) + double.Parse(c2.Attributes["y"].Value)) / 2.0;
                ele.SetAttribute("y", y.ToString());
            }
            else
            {
                leafCount++;
                ele.SetAttribute("y", leafCount.ToString());
            }
            ele.SetAttribute("level", level.ToString());
        }
    }
}

0 件のコメント:

コメントを投稿