检测鼠标是否以圆周方式移动
Detect if mouse move in circle way
我尝试实现鼠标移动跟踪,当鼠标以圆形或矩形方式移动时显示特定消息。
bool IsWithinCircle(int centerX, int centerY, int mouseX, int mouseY, double radius)
{
int diffX = centerX - mouseX;
int diffY = centerY - mouseY;
return (diffX * diffX + diffY * diffY) <= radius * radius;
}
我使用此功能使用鼠标定位来检测圆形。
还有其他检测鼠标移动的方法吗?
您能提供一些示例代码或 link 吗?
您可能希望将鼠标移动作为一系列线段进行跟踪,并使用这些线段创建一个近似圆。如果圆心保持相对一致,则用户正在绕圈移动。
他们的关键是任何两个连续的线段都近似于一个圆弧。从圆心到这些线段(法线是垂直线)的法线形成穿过圆心的线。当你有两条或更多条法线时,你可以计算出圆心。如果当用户四处移动鼠标时该中心保持相对一致,并且围绕圆心的那些法线的角度顺时针或逆时针进行,那么您就有了一个圆。
举个例子:
- 用户从
100,0
移动到 104,2
再到 106,6
,给你 2 行
- 直线的斜率为Δy/Δx。法线的斜率为-Δx/Δy
- 直线 1 的法线从
102,1
开始,斜率为 -1/2
(atan2(-1,2)
产生 -26°
)
- 第 2 行的法线从
105,4
开始,斜率为 -2/1
(atan2(-2,1)
产生 -63°
)
- 这些线相交于
99,7
- 如果所有后续线段的中心都为 99±t,7±t,其中 t 是一些公差,则您有一个连续的弧
- 当用户连续通过一个圆的度数(或弧度)时,他们就完成了一个圆
需要考虑的一些事项:
- 由于鼠标移动事件通常会触发很多次,导致许多相同的值或彼此相邻的值,您必须等到有足够的移动来计算合理的法线 - 您必须实验看看什么对你有用
- 公差还取决于您的实验表明什么对您有效
下面的示例代码...这将跟踪连续的鼠标移动以确定是否绘制了一个圆。每次触发 MouseMove 事件时,都会将光标 Point
添加到集合中,并启动计时器来评估集合并重置。
在评估集合时,它会首先确定中心Point
,然后它会确保所有点都在距离中心一定距离内(在下图中的绿色区域内)并且所有象限中都有点(完整 圆)。
使用简单的 WPF window:
<Window x:Class="Shaping.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas x:Name="ShapeCanvas" Background="Transparent" />
<Ellipse Width="100" Height="100" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
后面还有一些乱七八糟的代码:
public partial class MainWindow : Window
{
List<Point> points;
Timer reset;
public MainWindow()
{
InitializeComponent();
points = new List<Point>();
reset = new Timer { AutoReset = false, Interval = 500 };
reset.Elapsed += (s, e) =>
{
App.Current.Dispatcher.BeginInvoke((Action)(() =>
{
ShapeCanvas.Children.Clear();
}));
if (points.Count > 10)
{
double? max_x = null;
double? min_x = null;
double? max_y = null;
double? min_y = null;
// evaulate points to determine center
foreach (var point in points)
{
max_x = Math.Max(max_x ?? point.X, point.X);
min_x = Math.Min(min_x ?? point.X, point.X);
max_y = Math.Max(max_y ?? point.Y, point.Y);
min_y = Math.Min(min_y ?? point.Y, point.Y);
}
var center = new Point((((double)max_x - (double)min_x) / 2) + (double)min_x, (((double)max_y - (double)min_y) / 2) + (double)min_y);
var count_r = 0;
var sum_r = 0d;
var all_r = new List<double>();
var quadrands = new int[4];
// evaluate points again to determine quadrant and to get all distances and average distance
foreach (var point in points)
{
// distance
var r = Math.Sqrt(Math.Pow(point.X - center.X, 2) + Math.Pow(point.Y - center.Y, 2));
sum_r += r;
count_r += 1;
all_r.Add(r);
// quadrand
quadrands[(point.Y > center.Y ? 1 : 0) + (point.X > center.X ? 2 : 0)] += 1;
}
var r_avg = sum_r / count_r;
if (all_r.Count(r => Math.Abs(r - r_avg) < r_avg * .3) >= count_r * .8 && quadrands.All(q => q > 1))
{
// you made a circle
App.Current.Dispatcher.BeginInvoke((Action)(() =>
{
ShapeCanvas.Children.Clear();
ShapeCanvas.Children.Add(new Ellipse() { Fill = new SolidColorBrush(Colors.Red), Width = 15, Height = 15 });
reset.Start();
}));
}
}
points.Clear();
};
ShapeCanvas.MouseMove += (s, e) =>
{
points.Add(e.GetPosition((Canvas)s));
reset.Stop();
reset.Start();
};
}
}
关于 quadrands.All(q=> q > 1)
的注意事项——我最初是在检查所有象限 quadrants.All(q=> Math.Abs(q - avg_quad) < avg_quad * .3
之间的 even 分布,但这只有在绘制圆圈时才有效以恒定速度(或者如果根据速度调整计数;速度由连续点之间的距离确定)
.3
和 .8
只是我发现用来描述 roughly a circle
的数字——翻译为:至少 80% 的点落在平均距离的 +-15% 范围内从中心开始。
我尝试实现鼠标移动跟踪,当鼠标以圆形或矩形方式移动时显示特定消息。
bool IsWithinCircle(int centerX, int centerY, int mouseX, int mouseY, double radius)
{
int diffX = centerX - mouseX;
int diffY = centerY - mouseY;
return (diffX * diffX + diffY * diffY) <= radius * radius;
}
我使用此功能使用鼠标定位来检测圆形。
还有其他检测鼠标移动的方法吗?
您能提供一些示例代码或 link 吗?
您可能希望将鼠标移动作为一系列线段进行跟踪,并使用这些线段创建一个近似圆。如果圆心保持相对一致,则用户正在绕圈移动。
他们的关键是任何两个连续的线段都近似于一个圆弧。从圆心到这些线段(法线是垂直线)的法线形成穿过圆心的线。当你有两条或更多条法线时,你可以计算出圆心。如果当用户四处移动鼠标时该中心保持相对一致,并且围绕圆心的那些法线的角度顺时针或逆时针进行,那么您就有了一个圆。
举个例子:
- 用户从
100,0
移动到104,2
再到106,6
,给你 2 行 - 直线的斜率为Δy/Δx。法线的斜率为-Δx/Δy
- 直线 1 的法线从
102,1
开始,斜率为-1/2
(atan2(-1,2)
产生-26°
) - 第 2 行的法线从
105,4
开始,斜率为-2/1
(atan2(-2,1)
产生-63°
) - 这些线相交于
99,7
- 如果所有后续线段的中心都为 99±t,7±t,其中 t 是一些公差,则您有一个连续的弧
- 当用户连续通过一个圆的度数(或弧度)时,他们就完成了一个圆
需要考虑的一些事项:
- 由于鼠标移动事件通常会触发很多次,导致许多相同的值或彼此相邻的值,您必须等到有足够的移动来计算合理的法线 - 您必须实验看看什么对你有用
- 公差还取决于您的实验表明什么对您有效
下面的示例代码...这将跟踪连续的鼠标移动以确定是否绘制了一个圆。每次触发 MouseMove 事件时,都会将光标 Point
添加到集合中,并启动计时器来评估集合并重置。
在评估集合时,它会首先确定中心Point
,然后它会确保所有点都在距离中心一定距离内(在下图中的绿色区域内)并且所有象限中都有点(完整 圆)。
使用简单的 WPF window:
<Window x:Class="Shaping.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas x:Name="ShapeCanvas" Background="Transparent" />
<Ellipse Width="100" Height="100" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
后面还有一些乱七八糟的代码:
public partial class MainWindow : Window
{
List<Point> points;
Timer reset;
public MainWindow()
{
InitializeComponent();
points = new List<Point>();
reset = new Timer { AutoReset = false, Interval = 500 };
reset.Elapsed += (s, e) =>
{
App.Current.Dispatcher.BeginInvoke((Action)(() =>
{
ShapeCanvas.Children.Clear();
}));
if (points.Count > 10)
{
double? max_x = null;
double? min_x = null;
double? max_y = null;
double? min_y = null;
// evaulate points to determine center
foreach (var point in points)
{
max_x = Math.Max(max_x ?? point.X, point.X);
min_x = Math.Min(min_x ?? point.X, point.X);
max_y = Math.Max(max_y ?? point.Y, point.Y);
min_y = Math.Min(min_y ?? point.Y, point.Y);
}
var center = new Point((((double)max_x - (double)min_x) / 2) + (double)min_x, (((double)max_y - (double)min_y) / 2) + (double)min_y);
var count_r = 0;
var sum_r = 0d;
var all_r = new List<double>();
var quadrands = new int[4];
// evaluate points again to determine quadrant and to get all distances and average distance
foreach (var point in points)
{
// distance
var r = Math.Sqrt(Math.Pow(point.X - center.X, 2) + Math.Pow(point.Y - center.Y, 2));
sum_r += r;
count_r += 1;
all_r.Add(r);
// quadrand
quadrands[(point.Y > center.Y ? 1 : 0) + (point.X > center.X ? 2 : 0)] += 1;
}
var r_avg = sum_r / count_r;
if (all_r.Count(r => Math.Abs(r - r_avg) < r_avg * .3) >= count_r * .8 && quadrands.All(q => q > 1))
{
// you made a circle
App.Current.Dispatcher.BeginInvoke((Action)(() =>
{
ShapeCanvas.Children.Clear();
ShapeCanvas.Children.Add(new Ellipse() { Fill = new SolidColorBrush(Colors.Red), Width = 15, Height = 15 });
reset.Start();
}));
}
}
points.Clear();
};
ShapeCanvas.MouseMove += (s, e) =>
{
points.Add(e.GetPosition((Canvas)s));
reset.Stop();
reset.Start();
};
}
}
关于 quadrands.All(q=> q > 1)
的注意事项——我最初是在检查所有象限 quadrants.All(q=> Math.Abs(q - avg_quad) < avg_quad * .3
之间的 even 分布,但这只有在绘制圆圈时才有效以恒定速度(或者如果根据速度调整计数;速度由连续点之间的距离确定)
.3
和 .8
只是我发现用来描述 roughly a circle
的数字——翻译为:至少 80% 的点落在平均距离的 +-15% 范围内从中心开始。