1

数週間のうちに、家族のパーティーのために小さな絵のパズルをコーディングしようとしています。

パズルは次のようになります

私のソリューションが非常に遅くて醜いことはわかっています..しかし、最初のステップとしては機能しています。

画像のサイズに応じて動的に多くの長方形を作成します (さまざまな画像にパズルを使用するため、これは重要です)。

次に、X 秒ごとに 1 つの長方形が非表示になります (不透明度のステップが 0 に変わります)。

問題は、アプリケーションで四角形の行の間にスペースがあることです。

StackPanel、WrapPanel、UniformGrid で試してみました。UniformGrid を使用すると、最高の結果が得られました。

最良の方法は、メモリ内の画像全体にグレーの画像を生成し、このメモリ画像からタイマーでいくつかの長方形を切り取ることです。しかし、私は不透明効果が好きなので、それは不可能ですか?

ここに私のXAMLがあります:

<Window x:Class="PicturePuzzle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="420"
        Loaded="WindowLoaded">
  <Grid>
    <Grid Height="300" VerticalAlignment="Top">
      <Image x:Name="imgPicture"
             Source="/PicturePuzzle;component/Images/Koala.jpg"
             Stretch="Uniform" />
      <UniformGrid x:Name="ugMask"
                   Width="{Binding ElementName=imgPicture,
                                   Path=ActualWidth}"
                   Height="{Binding ElementName=imgPicture,
                                    Path=ActualHeight}" />

    </Grid>

    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom">
      <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
        <Button x:Name="btnStart"
                Width="60"
                Margin="0,0,5,0"
                Click="BtnStartClick"
                Content="Start" />
        <Button x:Name="btnStop"
                Width="60"
                Click="BtnStopClick"
                Content="Stop"
                IsEnabled="False" />
        <Button x:Name="btnSolution"
                Margin="5,0,0,0"
                Click="BtnSolutionClick"
                Content="Lösung anzeigen" />
      </StackPanel>
      <Slider x:Name="slSpeed"
              IsDirectionReversed="True"
              Maximum="10"
              Minimum="1"
              ValueChanged="SlSpeedValueChanged"
              Value="3" />
    </StackPanel>
  </Grid>
</Window>

そして私のコードビハインド:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace PicturePuzzle
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow
  {
    public MainWindow()
    {
      _rectangles = new List<Rectangle>();

      InitializeComponent();

      _timer = new DispatcherTimer();
      _generated = new List<int>();
      _random = new Random();
    }

    private readonly List<Rectangle> _rectangles;
    private readonly List<int> _generated;
    private readonly DispatcherTimer _timer;
    private readonly Random _random;

    private void WindowLoaded(object sender, RoutedEventArgs e)
    {
      var size = imgPicture.RenderSize;

      var columns = Convert.ToInt32(Math.Ceiling(size.Width/20));
      var rows = Convert.ToInt32(Math.Ceiling(size.Height/20));

      ugMask.Columns = columns;
      ugMask.Rows = rows;

      for (int y = 0; y < rows; y++)
      {
        for (int x = 0; x < columns; x++)
        {
          var rectangle = new Rectangle {Width = 20, Height = 20, Fill = Brushes.Gray};

          _rectangles.Add(rectangle);

          ugMask.Children.Add(rectangle);
        }
      }
    }

    private void BtnStartClick(object sender, RoutedEventArgs e)
    {
      btnStart.IsEnabled = false;
      btnStop.IsEnabled = true;

      _generated.Clear();
      _rectangles.ForEach(a => a.Opacity = 1);

      _timer.Interval = TimeSpan.FromSeconds(slSpeed.Value);
      _timer.Tick += TimerTick;
      _timer.Start();
    }

    private void TimerTick(object sender, EventArgs e)
    {
      if (_generated.Count >= _rectangles.Count)
      {
        _timer.Stop();
        return;
      }

      var random = GetNext();

      var alphaTimer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(0.1)};

      alphaTimer.Tick += (o, args) =>
        {
          var rec = _rectangles[random];

          if (rec.Opacity <= 0)
          {
            alphaTimer.Stop();
            return;
          }

          rec.Opacity -= 0.1;
        };

      alphaTimer.Start();
    }

    private int GetNext()
    {
      int r;

      do
      {
        r = _random.Next(0, _rectangles.Count);
      } while (_generated.Contains(r));


      _generated.Add(r);

      return r;
    }

    private void BtnStopClick(object sender, RoutedEventArgs e)
    {
      btnStart.IsEnabled = true;
      btnStop.IsEnabled = false;

      _timer.Stop();
    }

    private void SlSpeedValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
      if (_timer != null)
      {
        _timer.Interval = TimeSpan.FromSeconds((slSpeed.Value / 10));
      }
    }

    private void BtnSolutionClick(object sender, RoutedEventArgs e)
    {
      btnStop.IsEnabled = false;
      btnStart.IsEnabled = true;

      _timer.Stop();

      _rectangles.ForEach(a => a.Opacity = 0);
    }
  }
}

そして、ここで生成された結果を見ることができます:

ピクチャーパズル

どんなヒントでも大歓迎です!ありがとう。

4

1 に答える 1

2

少しテストしましたが、実際には2つの問題があります。

まず、長方形がアンチエイリアス処理されます。(これは WPF のデフォルトです。)四角形のエッジを整列させるには、アンチエイリアシングをオフにする必要があります。長方形を作成した後、次の行を追加します。

rectangle.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);

第 2 に、koala.jpg の幅が 300 の高さに均一に引き伸ばされた後、20 の倍数でない場合、均一な間隔で配置された 20 x 20 の長方形の間にギャップが必要になります。結果のギャップは、行が丸められて int32 にキャストされるときの丸め誤差によるものです。

次の 2 つのオプションがあります。

画像を少しつぶしても構わない場合は、Grid 要素の幅を 20 の倍数に設定します。次に、Image タグの Stretch プロパティを「Fill」に設定します。これにより、長方形が正確に正方形になります。

長方形の幅を に設定することもできますsize.Width / columns 。これにより、正確に収まるように少し引き伸ばされます。適切な測定のために、高さを に設定することもできますsize.Height / rowssize.Widthとは浮動小数点数であるためsize.Height、この除算により、行と列が計算されるときの丸め誤差が修正されます。これにより、任意のサイズの画像が許可されますが、長方形は正確に正方形にはなりません。

于 2012-11-21T08:16:15.440 に答える