I was watching a YouTube article about Quantum Spin and, during the introduction, the author mentioned that a vector dot product was a measure of how similar two vectors are. I had never thought of it that way before so I thought it might be fun to visualize it. Here is the result.
Start a Visual Studio 2022 C# Framework WPF project and call it DotProduct.
We will create an 800 x 800 canvas with axes drawn through the middle. We will add two lines that represent vectors and display their dot product in the middle of the canvas. We will then allow the user to grab the end of either of the vectors and move it around, updating the dot product as they do so.
We will start with the code behind which will need a class called cVector.
Is Tracking is true if the end of the line representing the vector is following the mouse. The other end is always anchored at the origin (which is point 400, 400 on the canvas).
The X and Y coordinates hold the location of the movable end of the line in canvas coordinates.
Color is the color of the line and there is a constructor that can easily initialize a new vector. We need to UI to update when X or Y are changed so the class must implement INotifyPropertyChanged.
private bool _IsTracking = false;
get { return _IsTracking; }
get { return _X; }
get { return _Y; }
get { return _Color; }
IsTracking = false;
if (!Object.Equals(storage, value))
storage = value;
if (PropertyChanged != null)
}
}
private List<cVector> _Vectors = new List<cVector> { new cVector(200,200, Colors.Red), new cVector(600,200, Colors.Blue)};
get { return _Vectors; }
get { return _DotProduct; }
InitializeComponent();
UpdateDotProduct(Vectors[0], Vectors[1]);
}
DotProduct = (int)((v1.X - 400) * (v2.X - 400) + (v1.Y - 400) * (v2.Y - 400));
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:DotProduct"
mc:Ignorable="d" ResizeMode="NoResize"
Title="Dot Product" Height="800" Width="800"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Background="Black">
<Canvas Background="Transparent">
<Line X1="400" X2="400" Y1="0" Y2="800" Stroke="LightGray"/>
<Line X1="0" X2="800" Y1="400" Y2="400" Stroke="LightGray"/>
<Line X1="400" Y1="400" X2="{Binding Vectors[0].X}" Y2="{Binding Vectors[0].Y}" Stroke="{Binding Vectors[0].Color}">
<Line.Style>
<Style TargetType="Line">
<Setter Property="StrokeThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Vectors[0].IsTracking}" Value="True">
<Setter Property="StrokeThickness" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<Line X1="400" Y1="400" X2="{Binding Vectors[1].X}" Y2="{Binding Vectors[1].Y}" Stroke="{Binding Vectors[1].Color}">
<Line.Style>
<Style TargetType="Line">
<Setter Property="StrokeThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Vectors[1].IsTracking}" Value="True">
<Setter Property="StrokeThickness" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<TextBlock Canvas.Top="390" Canvas.Left="350" Height="20" Background="Black" Foreground="White" Padding="2"
Text="{Binding DotProduct, StringFormat=Dot Product {0}}"/>
</Canvas>
</Window>
If we run the application now we can see these controls, but there is no interaction. We will need to add event handlers for MouseDown, MouseUp, and MouseMove.
Add these event handler definitions to the Canvas tag so that it looks like this.
<Canvas MouseDown="Canvas_MouseDown" MouseUp="Canvas_MouseUp" MouseMove="Canvas_MouseMove" Background="Transparent">
We want to grab the end of a vector when the user clicks near it. As the user moves the mouse the end of the vector will move with it. To do this well, we need to know how far from the mouse the end of the vector needs to be. Add two private members to the MainWindow class to hold the vertical and horizontal offset from the mouse pointer to the end of the vector.
Then add the event handlers to the MainWindow class.
MouseDown iterates through the list of vectors and finds the first one whose end is close to the mouse (if any). The vector is flagged as tracking and MouseMove events will update it.
MouseUp sets all the vectors as not tracking.
MouseMove finds each vector that is tracking (should be zero or one) and moves the end of the vector relative to the current mouse position, recalculating Dot Product at the same time.
Point p = Mouse.GetPosition((Canvas)sender);
foreach (cVector v in Vectors)
OffsetX = (decimal)p.X - v.X;
v.IsTracking = true;
}
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
Vectors.ForEach(v => v.IsTracking = false);
private void Canvas_MouseMove(object sender, MouseEventArgs e)
Point p = Mouse.GetPosition((Canvas)sender);
foreach (cVector v in Vectors)
if (v.IsTracking)
v.X = (decimal)p.X - OffsetX;
}
}
}
No comments:
Post a Comment