目的:测量图片上某部分的角度。
<Window x:Class="WPF_InkCanvas.Angle_InkCanvas"
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:WPF_InkCanvas"
mc:Ignorable="d"
Title="Angle_InkCanvas" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Image Name="imgMeasure" HorizontalAlignment="Center" Stretch="Uniform"/>
<InkCanvas Name="inkCanvasMeasure" EditingMode="None" Background="Transparent" Strokes="{Binding InkStrokes, Mode=TwoWay}" HorizontalAlignment="Center"
Width="{Binding ElementName=imgMeasure, Path=ActualWidth}" Height="{Binding ElementName=imgMeasure, Path=ActualHeight}"
MouseDown="InkCanvasMeasure_MouseDown" MouseMove="InkCanvasMeasure_MouseMove">
<Label Content="{Binding MeaInfo}" Background="Transparent" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10"
FontSize="18" Foreground="Red" IsHitTestVisible="False"/>
</InkCanvas>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="OpenFile" Margin="5" HorizontalAlignment="Left" FontSize="20" Click="OpenFile_Click"/>
</StackPanel>
</Grid>
</Window>
using Microsoft.Win32;
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.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WPF_InkCanvas
{
/// <summary>
/// Angle_InkCanvas.xaml 的交互逻辑
/// </summary>
public partial class Angle_InkCanvas : Window
{
private ViewModel viewModel;
System.Windows.Point iniP;
System.Windows.Point line1P;
System.Windows.Point line2P;
public Angle_InkCanvas()
{
InitializeComponent();
DrawingAttributes drawingAttributes = new DrawingAttributes
{
Color = Colors.Red,
Width = 2,
Height = 2,
StylusTip = StylusTip.Rectangle,
//FitToCurve = true,
IsHighlighter = false,
IgnorePressure = true,
};
inkCanvasMeasure.DefaultDrawingAttributes = drawingAttributes;
viewModel = new ViewModel
{
MeaInfo = "测试······",
InkStrokes = new StrokeCollection(),
};
DataContext = viewModel;
}
private void DrawAxiesCircle()
{
// Draw Line
List<System.Windows.Point> pointList = new List<System.Windows.Point>
{
new System.Windows.Point(iniP.X, iniP.Y),
new System.Windows.Point(line2P.X, line2P.Y),
};
StylusPointCollection point = new StylusPointCollection(pointList);
Stroke stroke = new Stroke(point)
{
DrawingAttributes = inkCanvasMeasure.DefaultDrawingAttributes.Clone(),
};
inkCanvasMeasure.Strokes.Add(stroke);
// Calculate angle
double a = Math.Sqrt((iniP.X - line1P.X) * (iniP.X - line1P.X) + (iniP.Y - line1P.Y) * (iniP.Y - line1P.Y));
double b = Math.Sqrt((iniP.X - line2P.X) * (iniP.X - line2P.X) + (iniP.Y - line2P.Y) * (iniP.Y - line2P.Y));
double c = Math.Sqrt((line1P.X - line2P.X) * (line1P.X - line2P.X) + (line1P.Y - line2P.Y) * (line1P.Y - line2P.Y));
double cTheta = (a * a + b * b - c * c) / (2 * a * b);
double theta = Math.Acos(cTheta) * 180 / Math.PI;
// Draw Circle
double r = 25;
double rMax = a;
if (rMax > b)
{
rMax = b;
}
if (r > 0.5 * rMax)
{
r = 0.5 * rMax;
}
double theta0 = Math.Atan((iniP.Y - line1P.Y) / (line1P.X - iniP.X + 1e-10)) * 180 / Math.PI;
pointList = new List<System.Windows.Point>();
//double cos_ab = ((line1P.Y - iniP.Y) * (line2P.Y - iniP.Y) + (line1P.X - iniP.X) * (line2P.X - iniP.X)) / (a * b);
double sin_ab = ((line1P.X - iniP.X) * (line2P.Y - iniP.Y) - (line1P.Y - iniP.Y) * (line2P.X - iniP.X)) / (a * b);
if (sin_ab <= 0)
{
for (double delta = 0; delta <= theta; delta++)
{
double th = delta + theta0;
pointList.Add(new System.Windows.Point(iniP.X + r * Math.Cos(th * Math.PI / 180), iniP.Y - r * Math.Sin(th * Math.PI / 180)));
}
}
else
{
for (double delta = -theta; delta <= 0; delta++)
{
double th = delta + theta0;
pointList.Add(new System.Windows.Point(iniP.X + r * Math.Cos(th * Math.PI / 180), iniP.Y - r * Math.Sin(th * Math.PI / 180)));
}
}
point = new StylusPointCollection(pointList);
stroke = new Stroke(point)
{
DrawingAttributes = inkCanvasMeasure.DefaultDrawingAttributes.Clone(),
};
inkCanvasMeasure.Strokes.Add(stroke);
viewModel.MeaInfo = "Current angle: " + string.Format("{0:F}", theta) + "°.";
}
private void InkCanvasMeasure_MouseDown(object sender, MouseButtonEventArgs e)
{
if (imgMeasure.Source == null)
{
return;
}
if (e.LeftButton == MouseButtonState.Pressed)
{
inkCanvasMeasure.Strokes.Clear();
viewModel.MeaInfo = "";
iniP = e.GetPosition(inkCanvasMeasure);
}
else if (e.RightButton == MouseButtonState.Pressed)
{
if (inkCanvasMeasure.Strokes.Count == 0)
{
return;
}
while (inkCanvasMeasure.Strokes.Count > 1)
{
inkCanvasMeasure.Strokes.RemoveAt(1);
}
line2P = e.GetPosition(inkCanvasMeasure);
DrawAxiesCircle();
}
}
private void InkCanvasMeasure_MouseMove(object sender, MouseEventArgs e)
{
if (imgMeasure.Source == null)
{
return;
}
if (e.LeftButton == MouseButtonState.Pressed)
{
// Measure Angle
inkCanvasMeasure.Strokes.Clear();
viewModel.MeaInfo = "";
line1P = e.GetPosition(inkCanvasMeasure);
if (line1P.X < iniP.X)
{
return;
}
// Draw Line
List<System.Windows.Point> pointList = new List<System.Windows.Point>
{
new System.Windows.Point(iniP.X, iniP.Y),
new System.Windows.Point(line1P.X, line1P.Y),
};
StylusPointCollection point = new StylusPointCollection(pointList);
Stroke stroke = new Stroke(point)
{
DrawingAttributes = inkCanvasMeasure.DefaultDrawingAttributes.Clone(),
};
inkCanvasMeasure.Strokes.Insert(0, stroke);
}
else if (e.RightButton == MouseButtonState.Pressed)
{
if (inkCanvasMeasure.Strokes.Count == 0)
{
return;
}
while (inkCanvasMeasure.Strokes.Count > 1)
{
inkCanvasMeasure.Strokes.RemoveAt(1);
}
line2P = e.GetPosition(inkCanvasMeasure);
DrawAxiesCircle();
}
}
private void OpenFile_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openDialog = new OpenFileDialog
{
Filter = "Image Files (*.jpg)|*.jpg|Image Files (*.png)|*.png|Image Files (*.bmp)|*.bmp",
Title = "Open Image File"
};
if (openDialog.ShowDialog() == true)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(openDialog.FileName, UriKind.RelativeOrAbsolute);
image.EndInit();
imgMeasure.Source = image;
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Ink;
namespace WPF_InkCanvas
{
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string meaInfo;
public string MeaInfo
{
get => meaInfo;
set
{
meaInfo = value;
OnPropertyChanged("MeaInfo");
}
}
private StrokeCollection inkStrokes;
public StrokeCollection InkStrokes
{
get { return inkStrokes; }
set
{
inkStrokes = value;
OnPropertyChanged("InkStrokes");
}
}
}
}
补充说明:鼠标左键按下确定初始点,鼠标左键按下并移动绘制第一条线;鼠标右键按下确定第二条线的终点并绘制夹角弧线;鼠标右键按下并移动绘制第二条线并绘制夹角弧线;角度大小可以用余弦定理或者向量的点乘;夹角方向用向量的叉乘来确定。