WPF InkCanvas测量角度

目的:测量图片上某部分的角度。

  • 效果图

  • xaml代码
<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;
            }
        }
    }
}
  • ViewModel.cs代码
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");
            }
        }
    }
}

补充说明:鼠标左键按下确定初始点,鼠标左键按下并移动绘制第一条线;鼠标右键按下确定第二条线的终点并绘制夹角弧线;鼠标右键按下并移动绘制第二条线并绘制夹角弧线;角度大小可以用余弦定理或者向量的点乘;夹角方向用向量的叉乘来确定。

猜你喜欢

转载自blog.csdn.net/u012366767/article/details/81270198
WPF
今日推荐