簡體   English   中英

旋轉形狀以跟隨wpf中的光標

[英]Rotate shapes to follow cursor in wpf

我得到了大部分工作。 然而,問題出現在我的數學計算中。 我希望箭頭(用戶控件)旋轉指向光標,因為它在wpf中的'click'上拖動畫布。 我已經弄清楚如何以弧度計算角度。 但是,當我應用該值時,它似乎沒有按預期工作。

當前

在此輸入圖像描述

目標

在此輸入圖像描述 在此輸入圖像描述

重點關注的主要代碼片段是......

private double Angle(Point origin, Point target)
        {
            //Calculate the distance from the square to the mouse's X and Y position
            var radians = Math.Atan2(origin.Y - target.Y, origin.X - target.X);
            var degrees = radians * (180 / Math.PI) - 90;

            Console.WriteLine(target + "--" + origin + "--" + degrees);
            return degrees;
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {                                     
            var canvas = (Canvas)sender;

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (_followMouse)
                {
                    // Get Cursor Position
                    Point _targetPoint = e.GetPosition(this);

                    // Follow mouse
                    foreach (UIElement element in canvas.Children)
                    {                        
                        Arrow arrow = (Arrow)element;

                        // example 1
                        double x = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
                        double y = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
                        Point _originPoint = new Point(x,y);

                        arrow.rotateTransform.Angle = Angle(_originPoint, _targetPoint);         
                    }
                }
            }
        }

以下整個項目代碼......

MainWindow.xaml.cs

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;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        bool _followMouse;

        private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            _followMouse = true;
        }
        private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _followMouse = false;
        }

        private double Angle(Point origin, Point target)
        {
            //Calculate the distance from the square to the mouse's X and Y position
            var radians = Math.Atan2(origin.Y - target.Y, origin.X - target.X);
            var degrees = radians * (180 / Math.PI) - 90;

            Console.WriteLine(target + "--" + origin + "--" + degrees);
            return degrees;
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {                                     
            var canvas = (Canvas)sender;

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (_followMouse)
                {
                    // Get Cursor Position
                    Point _targetPoint = e.GetPosition(this);

                    // Follow mouse
                    foreach (UIElement element in canvas.Children)
                    {                        
                        Arrow arrow = (Arrow)element;

                        // example 1
                        double x = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
                        double y = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
                        Point _originPoint = new Point(x,y);

                        arrow.rotateTransform.Angle = Angle(_originPoint, _targetPoint);         
                    }
                }
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication1.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:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300"
        WindowStartupLocation="CenterScreen">

    <Canvas
        MouseDown="Canvas_MouseDown" 
        MouseUp="Canvas_MouseUp"
        MouseMove="Canvas_MouseMove"
        Background="LightBlue">
        <local:Arrow Canvas.Left="158" Canvas.Top="43"/>
        <local:Arrow Canvas.Left="38" Canvas.Top="108"/>
        <local:Arrow Canvas.Left="158" Canvas.Top="170"/>
        <local:Arrow Canvas.Left="78" Canvas.Top="158"/>
        <local:Arrow Canvas.Left="196" Canvas.Top="108"/>
        <local:Arrow Canvas.Left="78" Canvas.Top="53"/>
    </Canvas>

</Window>

Arrow.xaml

<UserControl x:Class="WpfApplication1.Arrow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApplication1"
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="50">
    <Grid>
        <Path Data="M 0 4 L 4 0 L 8 4 Z" RenderTransformOrigin="0.5,0.5"
              Width="50"
              Height="50"
              Stretch="Uniform"
              Fill="Red">
            <Path.RenderTransform>
                <RotateTransform x:Name="rotateTransform"/>
            </Path.RenderTransform>

        </Path>
    </Grid>
</UserControl>

這個問題基本上歸結為找到兩個向量之間的角度,矢量為0角(即(0,-1))和從箭頭到點擊點的向量。

數學是相當直接的,acos(DotProduct(v1,v2)),因為這是一個非常關鍵的等式,有很多資源可以解釋為什么( http://www.wikihow.com/Find-the-Angle-Between-Two - 矢量 )。 請注意,這會給出矢量之間的無符號角度。 要弄清楚這個符號,你應該設置一個等同於vectorTo的DotProduct符號的符號和一個與0向量正交的向量。

用普通(呃)術語來說,想象一下旋轉應用於時鍾的臂,其中0角是12(並由向量(0,-1)表示)。 如果以正角度旋轉,則結果將具有正X值,因為手臂的尖端將位於時鍾中心的右側(3個時鍾將為(0,1))。 向左旋轉將給出負值(9點鍾將是(0,-1)。因此,如果你知道你想要的結果在右邊結束(因為你的X值是正的),那么你知道角度你周圍的旋轉應該是正的,反之亦然。這實際上是上面數學的簡化視覺表示。

Anywho - 代碼。

private double Angle(Point origin, Point target)
{

    // Get the vector from origin->point
    Vector vecTo = target - origin;

    // Normalize the vector
    vecTo.Normalize();

    // 0-angle is pointing straight up, aligned with (0, -1). 
    // The equation for the angle between 2 vectors is acos(Dot(v1, v1))
    // Our DotProduct is trivial, as know v1 is (0, -1).  This exands
    // 0 * v2.X + -1 * v2.Y
    double dotAngle = -vecTo.Y;
    double angle = Math.Acos(dotAngle);

    // Convert to rad
    angle = angle * 180 / Math.PI;
    // ACos will always return a positive number, but because Cos is
    // symmetric around 0 a -ve number is also valid,  Figure out which
    // is correct by taking the Dot vs (1, 0).  If result is positive,
    // then vecTo point in the same general direction as (1, 0), and
    // the angle returned should also be positive.  I've skipped
    // all the actual math, but thats the idea.
    if (vecTo.X > 0)
        return angle;
    else
        return -angle;
}

另一個問題是箭頭的X和Y坐標是相反的,它應該是:

double y = Canvas.GetTop(arrow) + arrow.ActualWidth / 2.0;
double x = Canvas.GetLeft(arrow) + arrow.ActualHeight / 2.0;
Point _originPoint = new Point(x,y);

每當移動到新的圖形包時,總是需要確定哪個方向是正確的。 如果找不到文檔,只需在某處放置一個斷點,然后單擊左上角和右下角,看看哪些坐標是吐出的。

在:var angleInRadians = Math.Atan2(yDiff,xDiff)* 180.0 / Math.PI;

Math.Atan2已經以弧度為單位返回向量,因此不需要“* 180.0 / Math.PI”部分。 如果您需要將度數重命名為angle而不是angleInRadian。

我不能回答為什么會這樣,但我開始假設上下左邊是不正確的:你想得到頂部中心(箭頭的點)。 此外,x應該是左邊,y應該是頂部。 然后添加一半寬度使x成為中心而不是左上角。

double x = Canvas.GetLeft(arrow);
double y = Canvas.GetTop(arrow);
x += (.5*arrow.ActualWidth);

這讓我們接近......接下來的部分是我不確定為什么會起作用,但似乎有效。 也許精通數學的人可以提供幫助。 向角度添加90度:

arrow.rotateTransform.Angle = (angleInRadians+90);

我偶然發現了這個因為看起來你正在計算的角度是不正確的......通過分析迭代的第一個箭頭的頂部和左邊的值,我得出結論它是最頂層的。 假設,點擊左上角區域應該將其旋轉大約270度,但我得到的值更接近180.所以,我添加了90,它似乎適用於任何光標位置,雖然我說我不是確定為什么。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM