diff --git a/LensSimulator.sln b/LensSimulator.sln
index 46f7768..deaf39f 100644
--- a/LensSimulator.sln
+++ b/LensSimulator.sln
@@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImplementationTest", "Imple
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LensSimulatorWindows", "LensSimulatorWindows\LensSimulatorWindows.csproj", "{ACDB5AA7-0C3D-4923-9843-E850C3807041}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LensSimulator-WinForms", "LensSimulator-WinForms\LensSimulator-WinForms.csproj", "{F16F2F39-B341-4591-8D1E-A0BAC484248E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LensSimulator-WinForms", "LensSimulator-WinForms\LensSimulator-WinForms.csproj", "{F16F2F39-B341-4591-8D1E-A0BAC484248E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LensSimulatorWPF", "LensSimulatorWPF\LensSimulatorWPF.csproj", "{0C57DE67-4CA1-4F8D-9D9F-9DF8FD1C87E1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,6 +35,10 @@ Global
{F16F2F39-B341-4591-8D1E-A0BAC484248E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F16F2F39-B341-4591-8D1E-A0BAC484248E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F16F2F39-B341-4591-8D1E-A0BAC484248E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C57DE67-4CA1-4F8D-9D9F-9DF8FD1C87E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C57DE67-4CA1-4F8D-9D9F-9DF8FD1C87E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C57DE67-4CA1-4F8D-9D9F-9DF8FD1C87E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C57DE67-4CA1-4F8D-9D9F-9DF8FD1C87E1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/LensSimulatorWPF/App.xaml b/LensSimulatorWPF/App.xaml
new file mode 100644
index 0000000..e13ffaa
--- /dev/null
+++ b/LensSimulatorWPF/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/LensSimulatorWPF/App.xaml.cs b/LensSimulatorWPF/App.xaml.cs
new file mode 100644
index 0000000..221bbce
--- /dev/null
+++ b/LensSimulatorWPF/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 LensSimulatorWPF
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/LensSimulatorWPF/AssemblyInfo.cs b/LensSimulatorWPF/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/LensSimulatorWPF/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/LensSimulatorWPF/LensSimulatorWPF.csproj b/LensSimulatorWPF/LensSimulatorWPF.csproj
new file mode 100644
index 0000000..d84276c
--- /dev/null
+++ b/LensSimulatorWPF/LensSimulatorWPF.csproj
@@ -0,0 +1,10 @@
+
+
+
+ WinExe
+ net7.0-windows
+ enable
+ true
+
+
+
diff --git a/LensSimulatorWPF/LensSimulatorWPF.csproj.user b/LensSimulatorWPF/LensSimulatorWPF.csproj.user
new file mode 100644
index 0000000..644b0a6
--- /dev/null
+++ b/LensSimulatorWPF/LensSimulatorWPF.csproj.user
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/LensSimulatorWPF/MainWindow.xaml b/LensSimulatorWPF/MainWindow.xaml
new file mode 100644
index 0000000..927a5a7
--- /dev/null
+++ b/LensSimulatorWPF/MainWindow.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LensSimulatorWPF/MainWindow.xaml.cs b/LensSimulatorWPF/MainWindow.xaml.cs
new file mode 100644
index 0000000..64b5999
--- /dev/null
+++ b/LensSimulatorWPF/MainWindow.xaml.cs
@@ -0,0 +1,144 @@
+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 LensSimulatorWPF
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private Point _lastDragPoint;
+ private Matrix _currentMatrix = Matrix.Identity;
+ private const double ScaleRate = 1.1;
+ private const double MinScale = 0.5;
+ private const double MaxScale = 10.0;
+
+ private Point lastZoomPos = new Point(0, 0);
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ DrawGrid();
+ this.MouseWheel += MainWindow_MouseWheel;
+ this.MouseLeftButtonDown += MainWindow_MouseLeftButtonDown;
+ this.MouseLeftButtonUp += MainWindow_MouseLeftButtonUp;
+ this.MouseMove += MainWindow_MouseMove;
+ }
+
+ private void MainWindow_MouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ var scale = e.Delta > 0 ? ScaleRate : 1 / ScaleRate;
+
+ // Clamp the scale in between the min and max scale
+ if (_currentMatrix.M11 * scale < MinScale || _currentMatrix.M11 * scale > MaxScale)
+ {
+ return;
+ }
+
+ // Get the mouse position in screen space (relative to canvas)
+ var screenMousePos = e.GetPosition(canvas);
+
+ // Convert the screen mouse position to the canvas space
+ var matrix = _currentMatrix;
+ matrix.Invert();
+
+ var targetPointInCanvas = matrix.Transform(screenMousePos);
+
+ lastZoomPos = targetPointInCanvas;
+
+ // Perform the scale operation at the target point
+ _currentMatrix.ScaleAtPrepend(scale, scale, canvas.ActualWidth/2, canvas.ActualHeight/ 2);
+
+ // Apply the transformation to the canvas
+ canvas.RenderTransform = new MatrixTransform(_currentMatrix);
+
+ DrawGrid();
+ }
+
+
+ private void DrawGrid()
+ {
+ canvas.Children.Clear();
+
+ double step = 50; // The M11 component of the matrix represents the scale in X
+ double width = canvas.ActualWidth;
+ double height = canvas.ActualHeight;
+
+ for (double i = step; i < width; i += step)
+ {
+ canvas.Children.Add(new Line
+ {
+ Stroke = Brushes.LightGray,
+ X1 = i,
+ Y1 = 0,
+ X2 = i,
+ Y2 = height
+ });
+ }
+
+ for (double i = step; i < height; i += step)
+ {
+ canvas.Children.Add(new Line
+ {
+ Stroke = Brushes.LightGray,
+ X1 = 0,
+ Y1 = i,
+ X2 = width,
+ Y2 = i
+ });
+ }
+
+ var ellipse = new Ellipse
+ {
+ Fill = Brushes.Red,
+ Width = 10,
+ Height = 10
+ };
+
+ Canvas.SetLeft(ellipse, lastZoomPos.X - ellipse.Width / 2);
+ Canvas.SetTop(ellipse, lastZoomPos.Y - ellipse.Height / 2);
+
+ canvas.Children.Add(ellipse);
+ }
+
+ private void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ var mousePos = e.GetPosition(this);
+ _lastDragPoint = new Point(mousePos.X - _currentMatrix.OffsetX, mousePos.Y - _currentMatrix.OffsetY);
+ Mouse.Capture(this);
+ }
+
+ private void MainWindow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ Mouse.Capture(null);
+ }
+
+ private void MainWindow_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ var posNow = e.GetPosition(this);
+
+ var deltaX = posNow.X - _lastDragPoint.X - _currentMatrix.OffsetX;
+ var deltaY = posNow.Y - _lastDragPoint.Y - _currentMatrix.OffsetY;
+
+ _currentMatrix.Translate(deltaX, deltaY);
+ canvas.RenderTransform = new MatrixTransform(_currentMatrix);
+ _lastDragPoint = new Point(posNow.X - _currentMatrix.OffsetX, posNow.Y - _currentMatrix.OffsetY);
+ }
+ }
+ }
+}
\ No newline at end of file