Как установить эллипс под случайным углом и направлением в холсте с помощью WPF

#c# #wpf

#c# #wpf

Вопрос:

Привет, я новичок в C # WPF и хочу установить эллипс под любым углом и направлением. К сожалению, я не нашел решения для этого. Мое простое решение не учитывает углы, которые я вычисляю. Я использую Canvas.SetTop, Canvas.setRight… и т.д., чтобы установить этот угол, но я хотел бы установить его с помощью моего вычисленного угла.

Вот мой XAML-код:

 <Window x:Class="someApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:someApp"
        mc:Ignorable="d" Background="LightBlue"
        Title="app" Height="450" Width="800">
    <Grid>
        <Canvas Name="enviroinment" Background="WhiteSmoke" HorizontalAlignment="Left" Height="297" Margin="120,88,0,0" VerticalAlignment="Top" Width="639">
            <Button Content="Start/Stop" Canvas.Left="-97" Canvas.Top="24" Width="75" Click="Button_Click"/>
        </Canvas>
    </Grid>
</Window>
  

Вот мой основной класс:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace someApp
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer timer = new DispatcherTimer();
        Random rand = new Random();
        List<Ellipse> People = new List<Ellipse>();
        double SPEED = 1.5;
        double _angle = 90;

        public MainWindow()
        {
            InitializeComponent();
            timer.Stop();
            timer.Interval = TimeSpan.FromSeconds(0.05);
            timer.Tick  = movePeople;
            timer.IsEnabled = true;
            timer.Start();
        }

        private void createPeople()
        {
            for(int i= 0; i < 4; i  )
            {
                Ellipse person = new Ellipse();
                person.Width = 10;
                person.Height = 10;
                person.Fill = Brushes.Gray;
                //Canvas.SetLeft(person, rand.Next(0, (int)environment.ActualWidth));
                //Canvas.SetTop(person, rand.Next(0,(int)environment.ActualHeight));
                People.Add(person);
            }
        }

        private void setDirection()
        {
            for (int i = 0; i < People.Count; i  )
            {
                _angle = rand.Next(0, 90);
                SPEED = 0.5   rand.NextDouble();
                double radians = Math.PI * _angle / 180.0;

                double initPosX = rand.Next(0, (int)environment.ActualWidth);
                double initPosY = rand.Next(0, (int)environment.ActualHeight);

                double x = initPosX   Math.Sin(radians) * SPEED;
                double y = initPosY    Math.Cos(radians) * SPEED;
               
                if (i == 0)
                {
                    Canvas.SetLeft(People[i], x);
                    Canvas.SetTop(People[i], y);
                }
                if(i == 1)
                {
                    Canvas.SetLeft(People[i], x);
                    Canvas.SetBottom(People[i], y);
                }
                if (i == 2)
                {
                    Canvas.SetRight(People[i], x);
                    Canvas.SetTop(People[i], y);
                }
                if (i == 3)
                {
                    Canvas.SetRight(People[i], x);
                    Canvas.SetBottom(People[i], y);
                }
                environment.Children.Add(People[i]);
            }
        }

        private void movePeople(object sender, EventArgs e)
        {
            for (int i = 0; i < People.Count; i  )
            {
                
                if (i == 0)
                {
                    double x = Canvas.GetLeft(People[i]);
                    double y = Canvas.GetTop(People[i]);

                    x  = SPEED;
                    y  = SPEED;

                    Canvas.SetLeft(People[i], x);
                    Canvas.SetTop(People[i], y);
                }
                if (i == 1)
                {
                    double x = Canvas.GetLeft(People[i]);
                    double y = Canvas.GetBottom(People[i]);

                    x  = SPEED;
                    y  = SPEED;

                    Canvas.SetLeft(People[i], x);
                    Canvas.SetBottom(People[i], y);
                }
                if (i == 2)
                {
                    double x = Canvas.GetRight(People[i]);
                    double y = Canvas.GetTop(People[i]);

                    x  = SPEED;
                    y  = SPEED;

                    Canvas.SetRight(People[i], x);
                    Canvas.SetTop(People[i], y);
                }
                if (i == 3)
                {
                    double x = Canvas.GetRight(People[i]);
                    double y = Canvas.GetBottom(People[i]);

                    x  = SPEED;
                    y  = SPEED;

                    Canvas.SetRight(People[i], x);
                    Canvas.SetBottom(People[i], y);
                }
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (timer.IsEnabled)
            {
                timer.Stop();
                People.Clear();
            }
            else
            {
                createPeople();
                setDirection();
                timer.Start();

            }
        }
    }
}
  

Заранее спасибо!!

Комментарии:

1. Пожалуйста, предоставьте четкое описание того, что вы пытаетесь сделать. Какова цель этого угла? Какова общая идея вашего кода? Есть четыре эллипса, расположенных в случайных положениях, которые вы, по-видимому, хотите перемещать циклически. Но что именно означает » установить эллипс под любым углом и направлением «?

2. То, что я хочу сделать, это моделирование людей, движущихся в прямоугольнике. На это ссылаются как на эллипсы, которые при запуске перемещаются в любом случайном направлении. Мой код работает только под углом 45 градусов в 4 разных направлениях. Это очень ограничено, мне нужно переместить мой эллипс на 20, 10 или другие градусы.

3. И вам, конечно же, нужно перемещать каждого человека в другом направлении, а не всех в одном и том же, верно?

4. да, пожалуйста, это похоже на это моделирование: (направления меняются только после столкновения) washingtonpost.com/gdpr-consent /…

Ответ №1:

Прежде всего, вы должны сделать это с самого начала и написать приложение, которое реализует архитектурный шаблон MVVM.

Был бы класс, который моделирует движущийся объект с положением и скоростью. Класс реализует интерфейс INotifyPropertyChanged, и его свойства запускают событие PropertyChanged при изменении их значения.

 public class Particle : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Point position;
    public Point Position
    {
        get { return position; }
        set
        {
            position = value;
            NotifyPropertyChanged(nameof(Position));
        }
    }

    private Vector velocity;
    public Vector Velocity
    {
        get { return velocity; }
        set
        {
            velocity = value;
            NotifyPropertyChanged(nameof(Velocity));
        }
    }

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  

Основной класс модели представления будет содержать коллекцию частиц в свойстве ObservableCollection и реализовывать их поведение при движении, заданное s = v * dt .

 public class ViewModel
{
    public ObservableCollection<Particle> Particles { get; }
        = new ObservableCollection<Particle>();

    private DateTime lastUpdate = DateTime.Now;

    public void MoveParticles()
    {
        var now = DateTime.Now;
        var dt = (now - lastUpdate).TotalSeconds;

        foreach (var particle in Particles)
        {
            particle.Position  = particle.Velocity * dt;
        }

        lastUpdate = now;
    }
}
  

Чтобы визуализировать эти частицы, вы должны использовать ItemsControl с холстом в качестве ItemsPanel . Его ItemContainerStyle позаботится о настройке холста.Слева и холст.Верхние свойства в соответствии с положением частиц, а ItemTemplate будет определять внешний вид:

 <ItemsControl ItemsSource="{Binding Particles}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Position.X}"/>
            <Setter Property="Canvas.Top" Value="{Binding Position.Y}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Path Fill="Red">
                <Path.Data>
                    <EllipseGeometry RadiusX="5" RadiusY="5"/>
                </Path.Data>
            </Path>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
  

Чтобы склеить представление и модель представления вместе, назначьте экземпляр класса ViewModel DataContext представления. Запустите таймер для циклического обновления модели представления, что автоматически также обновит представление.

 public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel();
    DataContext = vm;

    vm.Particles.Add(new Particle
    {
        Position = new Point(200, 100),
        Velocity = new Vector(10, 10)
    });

    vm.Particles.Add(new Particle
    {
        Position = new Point(200, 200),
        Velocity = new Vector(10, -10)
    });

    var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };

    timer.Tick  = (s, e) => vm.MoveParticles();
    timer.Start();
}
  

Теперь в классе view model будет реализовано необязательное обнаружение столкновений. Это просто изменило бы векторы скорости тех частиц, которые находятся ближе, чем заданное минимальное расстояние, где расстояние между двумя частицами равно

 var distance = (particle1.Position - particle2.Position).Length;