[英]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.