diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2190dee
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,335 @@
+# custom
+packages/
+
+
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
diff --git a/LaserEye.sln b/LaserEye.sln
new file mode 100644
index 0000000..2c22d05
--- /dev/null
+++ b/LaserEye.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.33529.622
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaserEye", "LaserEye\LaserEye.csproj", "{2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {26BC88E5-3B54-47A6-BC47-3C8EC8989E83}
+ EndGlobalSection
+EndGlobal
diff --git a/LaserEye/App.config b/LaserEye/App.config
new file mode 100644
index 0000000..56efbc7
--- /dev/null
+++ b/LaserEye/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LaserEye/App.xaml b/LaserEye/App.xaml
new file mode 100644
index 0000000..d939268
--- /dev/null
+++ b/LaserEye/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/LaserEye/App.xaml.cs b/LaserEye/App.xaml.cs
new file mode 100644
index 0000000..93f6e74
--- /dev/null
+++ b/LaserEye/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace LaserEye
+{
+ ///
+ /// App.xaml 的交互逻辑
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/LaserEye/LaserEye.csproj b/LaserEye/LaserEye.csproj
new file mode 100644
index 0000000..2aa2677
--- /dev/null
+++ b/LaserEye/LaserEye.csproj
@@ -0,0 +1,98 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2182AE0B-3983-45A4-BBF4-CC5F7036C6C4}
+ WinExe
+ LaserEye
+ LaserEye
+ v4.7.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LaserEye/MainWindow.xaml b/LaserEye/MainWindow.xaml
new file mode 100644
index 0000000..fc481f1
--- /dev/null
+++ b/LaserEye/MainWindow.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LaserEye/MainWindow.xaml.cs b/LaserEye/MainWindow.xaml.cs
new file mode 100644
index 0000000..69b498a
--- /dev/null
+++ b/LaserEye/MainWindow.xaml.cs
@@ -0,0 +1,279 @@
+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 LaserEye
+{
+ ///
+ /// MainWindow.xaml 的交互逻辑
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void SelectButtonClick(object sender, RoutedEventArgs e)
+ {
+ // select image file
+ var dlg = new Microsoft.Win32.OpenFileDialog();
+ dlg.DefaultExt = ".jpg";
+ dlg.Filter = "Image Files (*.jpg, *.png, *.bmp)|*.jpg;*.png;*.bmp";
+ bool? result = dlg.ShowDialog();
+ if (result == true)
+ {
+ // display image
+ tipsText.Content = "图片选择成功";
+ string filename = dlg.FileName;
+ filePathText.Text = filename;
+ SetImage(filename);
+ }
+ }
+
+ private void FilePathTextChanged(object sender, TextChangedEventArgs e)
+ {
+ // if stack trace shows SelectButtonClick, then it is a valid image file path, do not trigger this event again
+ var st = new System.Diagnostics.StackTrace();
+ var sfs = st.GetFrames();
+ foreach (var sf in sfs)
+ {
+ if (sf.GetMethod().Name == "SelectButtonClick")
+ {
+ filePathText.Foreground = Brushes.Black;
+ return;
+ }
+ }
+
+ // or validate the file path
+ string filename = filePathText.Text;
+ if (!System.IO.File.Exists(filename))
+ {
+ filePathText.Foreground = Brushes.Red;
+ }
+ else
+ {
+ tipsText.Content = "图片选择成功";
+ filePathText.Foreground = Brushes.Black;
+ SetImage(filename);
+ }
+ }
+
+ private void UnitScaleTextChanged(object sender, TextChangedEventArgs e)
+ {
+ // validate the unit scale
+ if (!double.TryParse(unitScaleText.Text, out var scale))
+ {
+ unitScaleText.Foreground = Brushes.Red;
+ }
+ else
+ {
+ tipsText.Content = "单位距离设置成功";
+ unitScaleText.Foreground = Brushes.Black;
+ coordSys.UnitScale = scale;
+ Recalculate();
+ }
+ }
+
+ private void SetImage(string file)
+ {
+ var bitmap = new BitmapImage(new Uri(file));
+ img.Source = bitmap;
+ coordSys.Pos1 = coordSys.Pos2 = null;
+ measurePoint1 = measurePoint2 = null;
+
+ Recalculate();
+ Redraw();
+ }
+
+ CoordSys coordSys = new CoordSys();
+
+ private void cnv_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (img.Source == null)
+ {
+ tipsText.Content = "请先选择图片";
+ return;
+ }
+
+ // draw a point on the canvas
+ var point = e.GetPosition(img);
+ var pos = point.ToPixPoint(img.RenderSize, new Size(img.Source.Width, img.Source.Height));
+ if (pos == null) return;
+
+ if ((coordSys.Pos1 == null) == (coordSys.Pos2 == null)) // both null or both not null
+ {
+ tipsText.Content = "基准坐标1设置成功";
+ coordSys.Pos1 = pos;
+ coordSys.Pos2 = null;
+ }
+ else if (coordSys.Pos2 == null)
+ {
+ tipsText.Content = "基准坐标2设置成功";
+ coordSys.Pos2 = pos;
+ }
+
+ Recalculate();
+ Redraw();
+ }
+
+ Point? measurePoint1 = null;
+ Point? measurePoint2 = null;
+
+ private void cnv_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (img.Source == null)
+ {
+ tipsText.Content = "请先选择图片";
+ return;
+ }
+
+ if (coordSys.Pos1 == null || coordSys.Pos2 == null)
+ {
+ tipsText.Content = "请先右键选择两个基准坐标";
+ return;
+ }
+
+ var point = e.GetPosition(img);
+ var pos = point.ToPixPoint(img.RenderSize, new Size(img.Source.Width, img.Source.Height));
+ if (pos == null) return;
+
+ if ((measurePoint1 == null) == (measurePoint2 == null)) // both null or both not null
+ {
+ measurePoint1 = pos;
+ measurePoint2 = null;
+ tipsText.Content = "测量坐标1设置成功";
+ }
+ else
+ {
+ measurePoint2 = pos;
+ tipsText.Content = "测量坐标2设置成功";
+ }
+
+ Recalculate();
+ Redraw();
+ }
+
+ private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ Redraw();
+ }
+
+ private void Recalculate()
+ {
+ if (coordSys.Pos1 == null ||
+ coordSys.Pos2 == null ||
+ measurePoint1 == null ||
+ measurePoint2 == null)
+ {
+ if (distanceText != null) distanceText.Content = "";
+ return;
+ }
+ var distance = coordSys.Measure(measurePoint1.Value, measurePoint2.Value);
+ distanceText.Content = distance.ToString("F2");
+ tipsText.Content = "计算成功";
+ }
+
+ private void Redraw()
+ {
+ // redraw the points on the canvas
+ void drawPoint(Point? pos, Brush color)
+ {
+ if (pos == null) return;
+ var point = pos.Value.ToRenderPoint(img.RenderSize, new Size(img.Source.Width, img.Source.Height));
+ if (point == null) return;
+ var ellipse = new Ellipse();
+ ellipse.Width = 5;
+ ellipse.Height = 5;
+ ellipse.Fill = color;
+ ellipse.Stroke = color;
+ ellipse.StrokeThickness = 1;
+ // get relative position of img in cnv
+ var imgPos = img.TranslatePoint(new Point(0, 0), cnv);
+ Canvas.SetLeft(ellipse, imgPos.X + point.Value.X - ellipse.Width / 2);
+ Canvas.SetTop(ellipse, imgPos.Y + point.Value.Y - ellipse.Height / 2);
+ cnv.Children.Add(ellipse);
+ }
+
+ cnv.Children.Clear();
+ drawPoint(coordSys.Pos1, Brushes.Red);
+ drawPoint(coordSys.Pos2, Brushes.Red);
+ if (measurePoint1 == null || measurePoint2 == null)
+ {
+ drawPoint(measurePoint1, Brushes.Yellow);
+ drawPoint(measurePoint2, Brushes.Yellow);
+ }
+ else
+ {
+ // draw the line
+ var point1 = measurePoint1.Value.ToRenderPoint(img.RenderSize, new Size(img.Source.Width, img.Source.Height));
+ if (point1 == null) return;
+ var point2 = measurePoint2.Value.ToRenderPoint(img.RenderSize, new Size(img.Source.Width, img.Source.Height));
+ if (point2 == null) return;
+
+ var line = new Line();
+ line.Stroke = Brushes.Yellow;
+ line.StrokeThickness = 2;
+ line.X1 = point1.Value.X;
+ line.Y1 = point1.Value.Y;
+ line.X2 = point2.Value.X;
+ line.Y2 = point2.Value.Y;
+ // get relative position of img in cnv
+ var imgPos = img.TranslatePoint(new Point(0, 0), cnv);
+ Canvas.SetLeft(line, imgPos.X);
+ Canvas.SetTop(line, imgPos.Y);
+ cnv.Children.Add(line);
+ }
+ }
+ }
+
+ public static class CoordExtension
+ {
+ public static Point? ToPixPoint(this Point point, Size renderSize, Size imageSize)
+ {
+ if (point.X < 0 || point.Y < 0 || point.X > renderSize.Width || point.Y > renderSize.Height) return null;
+ double x = point.X / renderSize.Width * imageSize.Width;
+ double y = point.Y / renderSize.Height * imageSize.Height;
+ return new Point(x, y);
+ }
+
+ public static Point? ToRenderPoint(this Point point, Size renderSize, Size imageSize)
+ {
+ if (point.X < 0 || point.Y < 0 || point.X > imageSize.Width || point.Y > imageSize.Height) return null;
+ double x = point.X / imageSize.Width * renderSize.Width;
+ double y = point.Y / imageSize.Height * renderSize.Height;
+ return new Point(x, y);
+ }
+ }
+
+ public class CoordSys
+ {
+ public Point? Pos1 { get; set; }
+ public Point? Pos2 { get; set; }
+
+ public double UnitScale { get; set; } = 1.0;
+
+ public static double Distance(Point p1, Point p2)
+ {
+ return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
+ }
+
+ public double Measure(Point p1, Point p2)
+ {
+ double unitDistance = Distance(Pos1.Value, Pos2.Value);
+ double distance = Distance(p1, p2);
+ return distance / unitDistance * UnitScale;
+ }
+ }
+}
diff --git a/LaserEye/Properties/AssemblyInfo.cs b/LaserEye/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3377685
--- /dev/null
+++ b/LaserEye/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("LaserEye")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("LaserEye")]
+[assembly: AssemblyCopyright("Copyright © xaxys 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+//若要开始生成可本地化的应用程序,请设置
+//.csproj 文件中的 CultureYouAreCodingWith
+//例如,如果您在源文件中使用的是美国英语,
+//使用的是美国英语,请将 设置为 en-US。 然后取消
+//对以下 NeutralResourceLanguage 特性的注释。 更新
+//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //主题特定资源词典所处位置
+ //(未在页面中找到资源时使用,
+ //或应用程序资源字典中找到时使用)
+ ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
+ //(未在页面中找到资源时使用,
+ //、应用程序或任何主题专用资源字典中找到时使用)
+)]
+
+
+// 程序集的版本信息由下列四个值组成:
+//
+// 主版本
+// 次版本
+// 生成号
+// 修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/LaserEye/Properties/Resources.Designer.cs b/LaserEye/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..e0d443f
--- /dev/null
+++ b/LaserEye/Properties/Resources.Designer.cs
@@ -0,0 +1,70 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本: 4.0.30319.42000
+//
+// 对此文件的更改可能导致不正确的行为,如果
+// 重新生成代码,则所做更改将丢失。
+//
+//------------------------------------------------------------------------------
+
+
+namespace LaserEye.Properties
+{
+ ///
+ /// 强类型资源类,用于查找本地化字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// 返回此类使用的缓存 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LaserEye.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/LaserEye/Properties/Resources.resx b/LaserEye/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/LaserEye/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/LaserEye/Properties/Settings.Designer.cs b/LaserEye/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..b65a130
--- /dev/null
+++ b/LaserEye/Properties/Settings.Designer.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+namespace LaserEye.Properties
+{
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/LaserEye/Properties/Settings.settings b/LaserEye/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/LaserEye/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..62c4449
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# LaserEye
+
+A simple measurement helper for laser distance measurement devices.
+
+## Screenshot
+
+![image](assets/image.png)
+
+## Principle
+
+We use two parallel laser beams that cast two dots on the target.
+Since the distance between the dots is known, when the distance between the dots on the target is measured, the distance between the device and the target can be calculated using a simple affine transformation (仿射变换).
+
+Of course, this calculation ignored the affection of distortion.
+
+## Usage
+
+I think it is simple enough that you can figure it out yourself by just clicking around.
diff --git a/assets/image.png b/assets/image.png
new file mode 100644
index 0000000..40ac21e
Binary files /dev/null and b/assets/image.png differ