Added source code.

This commit is contained in:
2018-02-06 01:24:46 +02:00
parent 1d9f2990c8
commit 8b28da5b80
367 changed files with 22964 additions and 0 deletions

View File

@ -0,0 +1,101 @@
<Window x:Class="GraphingCalculator.EvaluateWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Evaluate expression" Height="251" Width="471" WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow" SnapsToDevicePixels="True"
KeyUp="Window_KeyUp">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/GroupBoxStyle.xaml" />
<ResourceDictionary Source="/Styles/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint=".5,1">
<GradientStop Color="#EEE" Offset="0" />
<GradientStop Color="#BBB" Offset="1" />
</LinearGradientBrush>
</Window.Background>
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="5"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="0" Background="Transparent"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<GroupBox Header="Input expression" Margin="0,0,-3,0">
<TextBox Name="inputExpression" TextWrapping="Wrap"/>
</GroupBox>
<GroupBox Grid.Column="2" Header="Variables" Margin="-3,0,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox Name="listVars" Grid.ColumnSpan="100" Margin="0,0,0,2">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" Click="contextEdit_Click"/>
<MenuItem Header="Delete" Click="contextDelete_Click" />
<MenuItem Header="Clear list" Click="contextClear_Click" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center">Name:</TextBlock>
<TextBox Name="inputVarName" Grid.Row="1" Grid.Column="1" />
<TextBlock Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" Margin="3,0,0,0">Value:</TextBlock>
<TextBox Name="inputVarValue" Grid.Row="1" Grid.Column="3" />
<Button VerticalAlignment="Center" Name="buttonAdd"
Grid.Row="1" Grid.Column="4" Margin="3,0,0,0"
Click="buttonAdd_Click">Add</Button>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Grid.ColumnSpan="3" Header="Result">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Name="outputResult" Grid.ColumnSpan="2" IsReadOnly="True" Background="White"
Margin="0,0,0,2"/>
<Button Name="buttonClose" Grid.Row="1" Grid.Column="1" Width="80" Margin="2"
HorizontalAlignment="Left" Click="buttonClose_Click">Close</Button>
<Button Name="buttonEval" Grid.Row="1" Width="80" Margin="2"
HorizontalAlignment="Right" Click="buttonEval_Click">Evaluate</Button>
</Grid>
</GroupBox>
</Grid>
</Window>

View File

@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
namespace GraphingCalculator
{
/// <summary>
/// Interaction logic for EvaluateWindow.xaml
/// </summary>
public partial class EvaluateWindow : Window
{
private Dictionary<string, double> variables = new Dictionary<string, double>();
#region Properties
public string InputExpression
{
get { return inputExpression.Text; }
set { inputExpression.Text = value; }
}
#endregion
#region Constructors
public EvaluateWindow()
{
InitializeComponent();
}
public EvaluateWindow(string txt)
{
InitializeComponent();
InputExpression = txt;
// Convenience: evaluate already
buttonEval_Click(this, new RoutedEventArgs());
}
#endregion
#region User input
private void buttonAdd_Click(object sender, RoutedEventArgs e)
{
string errorMessage = "";
double val = 0;
outputResult.Clear();
// Verify boxes are not empty
if (inputVarName.Text == "" || inputVarValue.Text == "")
errorMessage = "Name or value cannot be empty!";
// Get number
if (errorMessage == "" && !double.TryParse(inputVarValue.Text, out val))
{
try {
Expression expr = new Expression(inputVarValue.Text);
val = expr.Evaluate();
}
catch { errorMessage = "Value must be a number!"; }
}
// Verify name is unique
if (errorMessage == "" && variables.ContainsKey(inputVarName.Text))
{
var res = MessageBox.Show("A variable with the same name already exists. Replace it with the new value?", "Warning", MessageBoxButton.YesNo);
if (res != MessageBoxResult.Yes) errorMessage = "A variable with the same name already exists!";
}
// Verify name doesn't contain forbidden characters
if (errorMessage == "")
{
bool ok = char.IsLetter (inputVarName.Text.First());
foreach (var i in inputVarName.Text)
if (!char.IsLetterOrDigit(i)) ok = false;
if (!ok) errorMessage = "Forbidden variable name, it can only contain letters or digits!";
}
// Add variable
if (errorMessage == "")
{
variables[inputVarName.Text] = val;
// Remove if it exists already
for (int i = 0; i < listVars.Items.Count; i++)
{
string str = listVars.Items[i] as string;
if (str != null && str.StartsWith(inputVarName.Text + " = ")) listVars.Items.RemoveAt(i);
}
// Add variable
listVars.Items.Add(inputVarName.Text + " = " + inputVarValue.Text);
// Clear text boxes
inputVarName.Clear();
inputVarValue.Clear();
}
else
{
outputResult.Foreground = Brushes.DarkRed;
outputResult.Text = "Error adding variable: " + errorMessage;
}
}
private void buttonEval_Click(object sender, RoutedEventArgs e)
{
// Create expression
Expression expression = new Expression(inputExpression.Text);
double result = 0;
// Add variables
foreach (var i in variables)
expression.Variables.Add(i.Key, i.Value);
// Try to evaluate
try {
result = expression.Evaluate();
}
catch (Exception ex) {
outputResult.Text = "Error evaluating: " + ex.Message;
outputResult.Foreground = Brushes.DarkRed;
#region Log
Log.LogEvent("Failed to evaluate expression '{0}'. Message: {1}", inputExpression.Text, ex.Message);
Log.LogEvent("> Stack trace: {0}", ex.StackTrace);
if (listVars.Items.Count > 0) Log.LogEvent("> Variables: ");
foreach (var i in listVars.Items)
Log.LogEvent(">> {0}", i.ToString());
#endregion
return;
}
// Output results
outputResult.Text = Math.Round(result, 15).ToString();
outputResult.Foreground = Brushes.Black;
#region Log
// Log what happened here
Log.LogEvent("Evaluated expression '{0}' result={1}", inputExpression.Text, outputResult.Text);
if (listVars.Items.Count > 0) Log.LogEvent("> WHERE: ");
foreach (var i in listVars.Items)
Log.LogEvent(">> {0}", i.ToString());
#endregion
}
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void contextEdit_Click(object sender, RoutedEventArgs e)
{
// Make sure we have 1 selected item
if (listVars.SelectedItems.Count != 1) return;
// Get key
var item = listVars.SelectedItem as string;
if (item == null) return;
string key = item.Substring(0, item.IndexOf(" = "));
// Place in input boxes
inputVarName.Text = key;
inputVarValue.Text = variables[key].ToString();
// Remove item
listVars.Items.Remove(listVars.SelectedItem);
variables.Remove(key);
}
private void contextDelete_Click(object sender, RoutedEventArgs e)
{
// Make sure we have 1 selected item
if (listVars.SelectedItems.Count != 1) return;
// Get key
var item = listVars.SelectedItem as string;
if (item == null) return;
string key = item.Substring(0, item.IndexOf(" = "));
// Remove
listVars.Items.Remove(listVars.SelectedItem);
variables.Remove(key);
}
private void contextClear_Click(object sender, RoutedEventArgs e)
{
listVars.Items.Clear();
variables.Clear();
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
e.Handled = true;
if (e.Key == Key.Escape) buttonClose_Click(this, new RoutedEventArgs());
else if (e.Key == Key.Enter) buttonEval_Click(this, new RoutedEventArgs());
else if (e.Key == Key.Insert) buttonAdd_Click(this, new RoutedEventArgs());
else e.Handled = false;
}
#endregion
}
}

View File

@ -0,0 +1,137 @@
<Canvas x:Class="GraphingCalculator.GraphingCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="234" d:DesignWidth="348"
ClipToBounds="True"
SizeChanged="Canvas_SizeChanged"
MouseWheel="Canvas_MouseWheel"
>
<Canvas.Resources>
<Style TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Name="grid">
<Border Name="border" CornerRadius="4" Visibility="Collapsed"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderBrush="Orange"
BorderThickness="1">
<Border.Background>
<RadialGradientBrush Center=".5, .5" GradientOrigin=".5,1" RadiusX="1" RadiusY=".7">
<GradientStop Color="#FFBF6A" Offset="0" />
<GradientStop Color="#FFF6E2" Offset="1" />
</RadialGradientBrush>
</Border.Background>
</Border>
<Border Name="blackness" CornerRadius="4" Background="Brown" Opacity="0" />
<ContentPresenter VerticalAlignment="Center" Margin="3"
HorizontalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="blackness" Property="Opacity" Value=".3" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="RepeatButton">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Grid Name="grid">
<Border Name="border" CornerRadius="4" Visibility="Collapsed"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderBrush="Orange"
BorderThickness="1">
<Border.Background>
<RadialGradientBrush Center=".5, .5" GradientOrigin=".5,1" RadiusX="1" RadiusY=".7">
<GradientStop Color="#FFBF6A" Offset="0" />
<GradientStop Color="#FFF6E2" Offset="1" />
</RadialGradientBrush>
</Border.Background>
</Border>
<Border Name="blackness" CornerRadius="4" Background="Brown" Opacity="0" />
<ContentPresenter VerticalAlignment="Center" Margin="3"
HorizontalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="blackness" Property="Opacity" Value=".3" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<RepeatButton Name="buttonUp" Canvas.Right="25" Canvas.Top="5"
Width="20" Height="20" Click="buttonUp_Click">
<Path Height="12" Stretch="Fill" Fill="Gray"
Data="F1 M 190.792,238.149L 182.688,252.184L 198.895,252.184L 190.792,238.149 Z "/>
</RepeatButton>
<RepeatButton Name="buttonLeft" Canvas.Right="45" Canvas.Top="25"
Width="20" Height="20" Click="buttonLeft_Click" >
<Path Width="12" Stretch="Fill" Fill="Gray" Data="F1 M 204.774,246.729L 218.809,254.833L 218.809,238.626L 204.774,246.729 Z "/>
</RepeatButton>
<Button Name="buttonReset"
Canvas.Right="25" Canvas.Top="25"
Width="20" Height="20"
Click="buttonReset_Click"
ToolTip="Reset" >
<Ellipse Width="12" Height="12" Fill="Gray"/>
</Button>
<RepeatButton Name="buttonRight"
Canvas.Right="5" Canvas.Top="25"
Width="20" Height="20" Click="buttonRight_Click" >
<Path Width="12" Stretch="Fill" Fill="Gray" Data="F1 M 214.722,227.667L 200.686,219.563L 200.686,235.77L 214.722,227.667 Z "/>
</RepeatButton>
<RepeatButton Name="buttonBottom" Canvas.Right="25" Canvas.Top="45"
Width="20" Height="20" Click="buttonBottom_Click" >
<Path Height="12" Stretch="Fill" Fill="Gray" Data="F1 M 188.333,235.024L 196.437,220.988L 180.23,220.988L 188.333,235.024 Z "/>
</RepeatButton>
<!-- Zoom buttons -->
<RepeatButton Name="buttonZoomIn" Canvas.Right="5" Canvas.Bottom="5"
Width="25" Height="20" Click="buttonZoomIn_Click">
<Path Width="12" Height="12" Stretch="Fill" Fill="Gray" Data="M 139.292,225.542L 147.292,225.542L 147.292,233.542L 155.292,233.542L 155.292,241.542L 147.292,241.542L 147.292,249.542L 139.292,249.542L 139.292,241.542L 131.292,241.542L 131.292,233.542L 139.292,233.542L 139.292,225.542 Z "/>
</RepeatButton>
<RepeatButton Name="buttonZoomOut" Canvas.Right="30" Canvas.Bottom="5"
Width="25" Height="20" Click="buttonZoomOut_Click">
<Rectangle Width="12" Height="4.32" Stretch="Fill" Fill="Gray"/>
</RepeatButton>
</Canvas>

View File

@ -0,0 +1,466 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 GraphingCalculator
{
/// <summary>
/// Interaction logic for GraphingCanvas.xaml
/// </summary>
public partial class GraphingCanvas : Canvas
{
#region Settings
private double PlotPrecision
{
get { return GraphingCalculator.Properties.Settings.Default.PlotPrecision; }
set { GraphingCalculator.Properties.Settings.Default.PlotPrecision = value; }
}
private double GridDensity
{
get { return GraphingCalculator.Properties.Settings.Default.GridDensity; }
set { GraphingCalculator.Properties.Settings.Default.GridDensity = value; }
}
private int RoundDoublesGraph
{
get { return GraphingCalculator.Properties.Settings.Default.RoundDoublesGraph; }
set { GraphingCalculator.Properties.Settings.Default.RoundDoublesGraph = value; }
}
private double NavigationSens
{
get { return GraphingCalculator.Properties.Settings.Default.NavigationSensitivity; }
set { GraphingCalculator.Properties.Settings.Default.NavigationSensitivity = value; }
}
private double ZoomSens
{
get { return GraphingCalculator.Properties.Settings.Default.ZoomSensitivity; }
set { GraphingCalculator.Properties.Settings.Default.ZoomSensitivity = value; }
}
#endregion
#region Constants
private Point specialSeparator = new Point(double.MaxValue, double.MaxValue);
private Point specialVisibleTrue = new Point(0, double.MaxValue);
private Point specialVisibleFalse = new Point(0, double.MinValue);
#endregion
#region Expressions
private List<VisualExpression> expressions = new List<VisualExpression>();
public List<VisualExpression> Expressions { get { return expressions; } }
/// <summary>
/// Adds an expression to be plotted
/// </summary>
public void AddExpression(VisualExpression ex)
{
this.Expressions.Add(ex);
EvaluateExpression(ex);
Redraw();
#region Log
Log.LogEvent("Plotted expression '{0}'", ex.ExpressionString);
#endregion
}
public void SetExpressionVisibility(int index, bool visibility)
{
if (Expressions[index].IsVisible == visibility) return;
if (queuedPoints.Count != 0) {
int i, ci;
for (i = 0, ci = -1; i < queuedPoints.Count && ci < index; i++)
if (queuedPoints[i] == specialSeparator) ci++;
queuedPoints[i] = (visibility) ? specialVisibleTrue : specialVisibleFalse;
}
Expressions[index].IsVisible = visibility;
Redraw();
}
#endregion
#region Canvas and screen bounds
public Bounds CanvasBounds { get; set; }
public Bounds ScreenBounds { get; set; }
public double CoordXToCanvas(double x)
{
double scrX = x - ScreenBounds.Left;
return CanvasBounds.Left + (CanvasBounds.Width * (scrX / ScreenBounds.Width));
}
public double CoordXToScreen(double x)
{
double canX = x - CanvasBounds.Left;
return ScreenBounds.Left + (ScreenBounds.Width * (canX / CanvasBounds.Width));
}
public double CoordYToCanvas(double y)
{
double scrY = y - ScreenBounds.Bottom;
return CanvasBounds.Bottom + (CanvasBounds.Height * (scrY / ScreenBounds.Height));
}
public double CoordYToScreen(double y)
{
double canY = y - CanvasBounds.Bottom;
return ScreenBounds.Bottom + (ScreenBounds.Height * (canY / CanvasBounds.Height));
}
public Point CoordToCanvas(double x, double y)
{
return new Point(CoordXToCanvas(x), CoordYToCanvas(y));
}
public Point CoordToCanvas(Point p)
{
return new Point(CoordXToCanvas(p.X), CoordYToCanvas(p.Y));
}
public Point CoordToScreen(double x, double y)
{
return new Point(CoordXToScreen(x), CoordYToScreen(y));
}
public Point CoordToScreen(Point p)
{
return new Point(CoordXToScreen(p.X), CoordYToScreen(p.Y));
}
public void SetCanvasBounds(Bounds b)
{
if (b.Width <= 0 || b.Height <= 0) return;
CanvasBounds.Top = b.Top;
CanvasBounds.Bottom = b.Bottom;
CanvasBounds.Left = b.Left;
CanvasBounds.Right = b.Right;
EvaluateExpressions();
Redraw();
}
#endregion
#region Constructor
public GraphingCanvas()
{
InitializeComponent();
CanvasBounds = new Bounds(-5, 5, 5, -5);
ScreenBounds = new Bounds(0, 0, this.ActualWidth, this.ActualHeight);
}
#endregion
#region UI Events
private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
ScreenBounds.Right = e.NewSize.Width;
ScreenBounds.Bottom = e.NewSize.Height;
Redraw();
}
private void buttonReset_Click(object sender, RoutedEventArgs e)
{
CanvasBounds.Left = -5;
CanvasBounds.Right = 5;
CanvasBounds.Bottom = -5;
CanvasBounds.Top = 5;
EvaluateExpressions();
Redraw();
}
private void buttonUp_Click(object sender, RoutedEventArgs e)
{
double h = CanvasBounds.Height * NavigationSens;
CanvasBounds.Top += h;
CanvasBounds.Bottom += h;
EvaluateExpressions();
Redraw();
}
private void buttonLeft_Click(object sender, RoutedEventArgs e)
{
double w = CanvasBounds.Width * NavigationSens;
CanvasBounds.Left -= w;
CanvasBounds.Right -= w;
EvaluateExpressions();
Redraw();
}
private void buttonRight_Click(object sender, RoutedEventArgs e)
{
double w = CanvasBounds.Width * NavigationSens;
CanvasBounds.Left += w;
CanvasBounds.Right += w;
EvaluateExpressions();
Redraw();
}
private void buttonBottom_Click(object sender, RoutedEventArgs e)
{
double h = CanvasBounds.Height * NavigationSens;
CanvasBounds.Top -= h;
CanvasBounds.Bottom -= h;
EvaluateExpressions();
Redraw();
}
private void buttonZoomIn_Click(object sender, RoutedEventArgs e)
{
double new_w = CanvasBounds.Width / ZoomSens;
double new_h = CanvasBounds.Height / ZoomSens;
double diff_w = (CanvasBounds.Width - new_w) / 2 ;
double diff_h = (CanvasBounds.Height - new_h) / 2;
CanvasBounds.Left += diff_w;
CanvasBounds.Right -= diff_w;
CanvasBounds.Bottom += diff_h;
CanvasBounds.Top -= diff_h;
EvaluateExpressions();
Redraw();
}
private void buttonZoomOut_Click(object sender, RoutedEventArgs e)
{
double new_w = CanvasBounds.Width * ZoomSens;
double new_h = CanvasBounds.Height * ZoomSens;
double diff_w = (CanvasBounds.Width - new_w) / 2;
double diff_h = (CanvasBounds.Height - new_h) / 2;
CanvasBounds.Left += diff_w;
CanvasBounds.Right -= diff_w;
CanvasBounds.Bottom += diff_h;
CanvasBounds.Top -= diff_h;
EvaluateExpressions();
Redraw();
}
#endregion
#region Expression evaluation
List<Point> queuedPoints = new List<Point>();
public void EvaluateExpression(VisualExpression expr)
{
queuedPoints.Add(specialSeparator);
queuedPoints.Add((expr.IsVisible) ? specialVisibleTrue : specialVisibleFalse);
for (double x = CanvasBounds.Left; x <= CanvasBounds.Right; x += CanvasBounds.Width / PlotPrecision)
{
expr.Variables["x"] = expr.Variables["X"] = x;
queuedPoints.Add(new Point(x, expr.Evaluate()));
}
}
public void EvaluateExpressions()
{
queuedPoints.Clear();
foreach (var expr in Expressions)
EvaluateExpression(expr);
}
#endregion
#region Rendering
public void Redraw()
{
this.InvalidateVisual();
}
private void RenderDouble(DrawingContext dc, double n, Point pos, bool center)
{
FormattedText text = new FormattedText(Math.Round(n, RoundDoublesGraph).ToString(),
new System.Globalization.CultureInfo("en-US"), FlowDirection.LeftToRight,
new Typeface("Arial"), 8.0, Brushes.Black);
if (center) dc.DrawText(text, new Point(pos.X - (text.Width / 2), pos.Y + 10));
else dc.DrawText(text, new Point(pos.X + 2, pos.Y));
}
private void RenderGridVertical(DrawingContext dc, Pen pen, double from, double to, double interv)
{
if (from > to) interv *= -1;
// Draw vertical grid
for (double i = from; (from < to && i < to) || (from > to && i > to); i += interv)
{
Point text = CoordToScreen(i, 0);
Point f = CoordToScreen(i, CanvasBounds.Bottom);
Point t = CoordToScreen(i, CanvasBounds.Top);
dc.DrawLine(pen, f, t);
RenderDouble(dc, i, text, true);
}
}
private void RenderGridHorizontal(DrawingContext dc, Pen pen, double from, double to, double interv)
{
if (from > to) interv *= -1;
// Draw vertical grid
for (double i = from; (from < to && i < to) || (from > to && i > to); i += interv)
{
Point text = CoordToScreen(0, i);
Point f = CoordToScreen(CanvasBounds.Left, i);
Point t = CoordToScreen(CanvasBounds.Right, i);
dc.DrawLine(pen, f, t);
RenderDouble(dc, i, text, false);
}
}
private void RenderGrid(DrawingContext dc)
{
Pen pen = new Pen(new SolidColorBrush(Color.FromRgb(0xa6, 0xdd, 0xe2)), 1);
pen.DashStyle = DashStyles.Dash;
double w_div = Math.Truncate(this.ActualWidth / GridDensity);
double h_div = Math.Truncate(this.ActualHeight / GridDensity);
if (Convert.ToInt32(w_div) % 2 == 1) w_div += 1;
if (Convert.ToInt32(h_div) % 2 == 1) h_div += 1;
double w_int = CanvasBounds.Width / w_div;
double h_int = CanvasBounds.Height / h_div;
RenderGridVertical(dc, pen, 0, CanvasBounds.Right, w_int);
RenderGridVertical(dc, pen, 0, CanvasBounds.Left, w_int);
RenderGridHorizontal(dc, pen, 0, CanvasBounds.Top, h_int);
RenderGridHorizontal(dc, pen, 0, CanvasBounds.Bottom, h_int);
}
private void RenderAxis(DrawingContext dc)
{
Point dst;
Pen lines = new Pen(Brushes.Coral, 1.5);
// Draw X axis
dst = CoordToScreen(CanvasBounds.Right, 0);
dc.DrawLine(lines, CoordToScreen(CanvasBounds.Left, 0), dst);
dc.DrawLine(lines, dst, new Point(dst.X - 5, dst.Y + 2));
dc.DrawLine(lines, dst, new Point(dst.X - 5, dst.Y - 2));
// Draw Y axis
dst = CoordToScreen(0, CanvasBounds.Top);
dc.DrawLine(lines, CoordToScreen(0, CanvasBounds.Bottom), dst);
dc.DrawLine(lines, dst, new Point(dst.X - 2, dst.Y + 5));
dc.DrawLine(lines, dst, new Point(dst.X + 2, dst.Y + 5));
}
private void RenderFunctions(DrawingContext dc)
{
if (Expressions.Count == 0) return;
int exprIndex = -1;
bool visible = true;
Pen pen = new Pen();
for (int i = 0; i < queuedPoints.Count - 1; i++)
{
if (queuedPoints[i] == specialSeparator)
{
exprIndex++; i++;
pen = new Pen(new SolidColorBrush(Expressions[exprIndex].Color), Expressions[exprIndex].Thickness);
visible = (queuedPoints[i] == specialVisibleTrue);
}
else if (visible && queuedPoints[i + 1] != specialSeparator)
{
Point src = CoordToScreen(queuedPoints[i]);
Point dst = CoordToScreen(queuedPoints[i + 1]);
if (ScreenBounds.IsInBounds(src) || ScreenBounds.IsInBounds(dst))
dc.DrawLine(pen, src, dst);
}
}
}
protected override void OnRender(DrawingContext dc)
{
// Reduce garbage collector intrusions
var latency = System.Runtime.GCSettings.LatencyMode;
System.Runtime.GCSettings.LatencyMode = System.Runtime.GCLatencyMode.LowLatency;
// Draw grid
RenderGrid(dc);
RenderAxis(dc);
// Render functions
RenderFunctions(dc);
// Base render
base.OnRender(dc);
// Restore previous garbage collector setting
System.Runtime.GCSettings.LatencyMode = latency;
}
#endregion
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0) buttonZoomIn_Click(this, new RoutedEventArgs());
else buttonZoomOut_Click(this, new RoutedEventArgs());
}
#region Navigation control
public void PeformMouseWheelChange(MouseWheelEventArgs e)
{
Canvas_MouseWheel(this, e);
}
public void PerformMoveLeft()
{
buttonLeft_Click(this, new RoutedEventArgs());
}
public void PerformMoveRight()
{
buttonRight_Click(this, new RoutedEventArgs());
}
public void PerformMoveUp()
{
buttonUp_Click(this, new RoutedEventArgs());
}
public void PerformMoveDown()
{
buttonBottom_Click(this, new RoutedEventArgs());
}
public void PerformReset()
{
buttonReset_Click(this, new RoutedEventArgs());
}
public void PerformZoomIn()
{
buttonZoomIn_Click(this, new RoutedEventArgs());
}
public void PerformZoomOut()
{
buttonZoomOut_Click(this, new RoutedEventArgs());
}
#endregion
}
}

View File

@ -0,0 +1,80 @@
<Window x:Class="GraphingCalculator.Controls.IntegralWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Integral" Height="331" Width="261" WindowStyle="ToolWindow" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/ButtonStyle.xaml" />
<ResourceDictionary Source="/Styles/RadioButtonStyle.xaml" />
<ResourceDictionary Source="/Styles/GroupBoxStyle.xaml" />
<ResourceDictionary Source="/Styles/WarningGroupBoxStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint=".5,1">
<GradientStop Color="#EEE" Offset="0" />
<GradientStop Color="#999" Offset="1" />
</LinearGradientBrush>
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Style="{StaticResource WarningGroupBox}"
Grid.Row="0" Grid.ColumnSpan="2" Margin="8,5,8,2"
HorizontalContentAlignment="Stretch"
Header="Warning" TextBlock.TextAlignment="Center" >
<TextBlock TextWrapping="Wrap">This feature is experimental, so the given results may not fully be accurate.</TextBlock>
</GroupBox>
<GroupBox Header="Expression" Grid.Row="1">
<TextBox Name="inputExpression" TextWrapping="Wrap" />
</GroupBox>
<GroupBox Header="Interval" Grid.Row="2">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="32" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center">From:</TextBlock>
<TextBox Name="inputIntervalBeg" Grid.Column="1" VerticalAlignment="Center">-1</TextBox>
<TextBlock Grid.Column="3" VerticalAlignment="Center">To:</TextBlock>
<TextBox Name="inputIntervalEnd" Grid.Column="4" VerticalAlignment="Center">1</TextBox>
</Grid>
</StackPanel>
</GroupBox>
<GroupBox Header="Result" Grid.Row="3">
<TextBox Name="outputResult" IsReadOnly="True" Background="White"/>
</GroupBox>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Name="buttonCalculate" Grid.Column="0" HorizontalAlignment="Right" Width="80" Click="buttonCalculate_Click">Calculate</Button>
<Button Name="buttonClose" Grid.Column="1" HorizontalAlignment="Left" Width="80" Click="buttonClose_Click">Close</Button>
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
namespace GraphingCalculator.Controls
{
/// <summary>
/// Interaction logic for LimitWindow.xaml
/// </summary>
public partial class IntegralWindow : Window
{
#region Properties
public string ExpressionString
{
get { return inputExpression.Text; }
set { inputExpression.Text = value; }
}
#endregion
#region Constructors
public IntegralWindow()
{
InitializeComponent();
ExpressionString = "";
}
public IntegralWindow(string expression)
{
InitializeComponent();
ExpressionString = expression;
}
#endregion
#region Other routines
private void Result (string result, bool error = false)
{
outputResult.Text = result;
if (error) outputResult.Foreground = Brushes.DarkRed;
else outputResult.Foreground = Brushes.Black;
}
#endregion
#region Button handlers
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void buttonCalculate_Click(object sender, RoutedEventArgs e)
{
double val_beg, val_end, integr_res;
Expression beg, end, expr;
beg = new Expression(inputIntervalBeg.Text);
end = new Expression(inputIntervalEnd.Text);
expr = new Expression(inputExpression.Text);
// Try to evaluate interval ends
try { val_beg = beg.Evaluate(); }
catch { Result("Failed to evaluate interval start.", true); return; }
try { val_end = end.Evaluate(); }
catch { Result("Failed to evaluate interval end.", true); return; }
// Try to calculate the integral
try { integr_res = Integrator.Integrate(expr, val_beg, val_end, "x"); }
catch (Exception ex)
{
Result("Failed to evaluate expression. Message: " + ex.Message, true);
Log.LogEvent("Failed to integrate expression: {0}", expr.ExpressionString);
Log.LogEvent("> Interval: [{0}, {1}] = [{2}, {3}]", beg.ExpressionString, end.ExpressionString, val_beg, val_end);
Log.LogEvent("> Message: {0}", ex.Message);
Log.LogEvent("> Stack trace: {0}", ex.StackTrace);
return;
}
// Show result
Result(Math.Round(integr_res, 14).ToString());
Log.LogEvent("Integrated expression: {0}", expr.ExpressionString);
Log.LogEvent("> Interval: [{0}, {1}] = [{2}, {3}]", beg.ExpressionString, end.ExpressionString, val_beg, val_end);
Log.LogEvent("> Result: {0}", integr_res);
}
#endregion
}
}

View File

@ -0,0 +1,44 @@
<Window x:Class="GraphingCalculator.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Log" Height="379" Width="543" WindowStartupLocation="CenterScreen" ShowInTaskbar="True">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ListBox Name="listEntries" Grid.Row="0" Grid.ColumnSpan="10"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="buttonCopy" Grid.Row="1" Grid.Column="1" Width="60" Click="buttonCopy_Click">Copy</Button>
<Button Name="buttonSave" Grid.Row="1" Grid.Column="2" Width="60" Click="buttonSave_Click">Save...</Button>
<Button Name="buttonClear" Grid.Row="1" Grid.Column="3" Width="60" Click="buttonClear_Click">Clear</Button>
<Button Name="buttonRefresh" Grid.Row="1" Grid.Column="0" Width="60" Click="buttonRefresh_Click">Refresh</Button>
<Button Name="buttonClose" Grid.Row="1" Grid.Column="5" Width="60" Click="buttonClose_Click">Close</Button>
</Grid>
</Window>

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
namespace GraphingCalculator
{
/// <summary>
/// Interaction logic for LogWindow.xaml
/// </summary>
public partial class LogWindow : Window
{
public LogWindow()
{
InitializeComponent();
buttonRefresh_Click(this, new RoutedEventArgs());
}
private void buttonCopy_Click(object sender, RoutedEventArgs e)
{
if (listEntries.SelectedIndex != -1)
Clipboard.SetText(listEntries.SelectedItem.ToString());
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
listEntries.Items.Clear();
Log.Entries.Clear();
}
private void buttonRefresh_Click(object sender, RoutedEventArgs e)
{
listEntries.Items.Clear();
foreach (var i in Log.Entries)
listEntries.Items.Add(i);
}
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
dialog.Title = "Save log...";
dialog.Filter = "Text file|*.txt|Log file|*.log|All files|*.*";
bool? res = dialog.ShowDialog();
if (!res.HasValue && !res.Value) return;
try
{
System.IO.File.WriteAllLines(dialog.FileName, Log.Entries.ToArray());
}
catch (Exception ex)
{
Log.LogEvent("Failed to save log to file {0}: {1}", dialog.FileName, ex.Message);
MessageBox.Show("Error: " + ex.Message, "Failed to save log file!");
}
}
}
}

View File

@ -0,0 +1,165 @@
<Window x:Class="GraphingCalculator.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Settings" Height="320" Width="562"
ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/GroupBoxStyle.xaml" />
<ResourceDictionary Source="/Styles/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint=".5,1">
<GradientStop Color="#EEE" Offset="0" />
<GradientStop Color="#BBB" Offset="1" />
</LinearGradientBrush>
</Window.Background>
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GroupBox Header="Graph precision" Grid.Row="0">
<StackPanel>
<Slider Name="sliderGraphPrecision"
Minimum="50" Maximum="2000" TickFrequency="150"
SmallChange="10" LargeChange="50"
TickPlacement="BottomRight"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock>Minimum</TextBlock>
<TextBlock Grid.Column="2">Maximum</TextBlock>
</Grid>
<TextBlock TextWrapping="Wrap" FontSize="11"
Foreground="#444" Margin="2,4,2,2">
A higher precision means that drawing takes longer to render, making the application slower.</TextBlock>
</StackPanel>
</GroupBox>
<GroupBox Header="Grid density" Grid.Column="1" Grid.Row="0">
<StackPanel>
<Slider Name="sliderGridDensity"
Minimum="10" Maximum="100" TickFrequency="5"
SmallChange="2" LargeChange="5"
TickPlacement="BottomRight"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock>Higher</TextBlock>
<TextBlock Grid.Column="2">Lower</TextBlock>
</Grid>
<TextBlock TextWrapping="Wrap" FontSize="11"
Foreground="#444" Margin="2,4,2,2">
A higher density means that more grid lines are drawn.</TextBlock>
</StackPanel>
</GroupBox>
<GroupBox Header="Grid numbers precision" Grid.Row="2" Grid.RowSpan="2">
<StackPanel>
<Slider Name="sliderDoublePrecision"
Minimum="0" Maximum="15" TickFrequency="1"
SmallChange="1" LargeChange="1" ValueChanged="sliderDoublePrecision_ValueChanged"
TickPlacement="BottomRight" IsSnapToTickEnabled="True"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock>0</TextBlock>
<TextBlock Name="outputDoublePrecision" Grid.Column="1" HorizontalAlignment="Center">? decimals</TextBlock>
<TextBlock Grid.Column="2">15</TextBlock>
</Grid>
<TextBlock TextWrapping="Wrap" FontSize="11"
Foreground="#444" Margin="2,4,2,2">
A higher precision means that more decimals appear in the graph. If the value is too high, values may overlap each other.</TextBlock>
</StackPanel>
</GroupBox>
<GroupBox Header="Navigation sensitivity"
Grid.Column="1" Grid.Row="2">
<StackPanel>
<Slider Name="sliderNavigationSensitivity"
Minimum=".01" Maximum="1.5" TickFrequency=".1"
SmallChange=".01" LargeChange=".05" ValueChanged="sliderNavigationSensitivity_ValueChanged"
TickPlacement="BottomRight"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock>1%</TextBlock>
<TextBlock Name="outputNavigationSensitivity" Grid.Column="1" HorizontalAlignment="Center">?% of screen</TextBlock>
<TextBlock Grid.Column="2">150%</TextBlock>
</Grid>
</StackPanel>
</GroupBox>
<GroupBox Header="Zoom sensitivity"
Grid.Column="1" Grid.Row="3">
<StackPanel>
<Slider Name="sliderZoomSensitivity"
Minimum="1.01" Maximum="2.5" TickFrequency=".1"
SmallChange=".01" LargeChange=".05" ValueChanged="sliderZoomSensitivity_ValueChanged"
TickPlacement="BottomRight"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock>1%</TextBlock>
<TextBlock Name="outputZoomSensitivity" Grid.Column="1" HorizontalAlignment="Center">?% of screen</TextBlock>
<TextBlock Grid.Column="2">150%</TextBlock>
</Grid>
</StackPanel>
</GroupBox>
<Grid Grid.Row="5" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Name="buttonDefaults" Width="80" Click="buttonDefaults_Click">Defaults</Button>
<Button Name="buttonCancel" Width="80" Grid.Column="2" Click="buttonCancel_Click">Cancel</Button>
<Button Name="buttonAccept" Width="80" Grid.Column="3" Click="buttonAccept_Click">Accept</Button>
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
namespace GraphingCalculator
{
/// <summary>
/// Interaction logic for SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow : Window
{
public SettingsWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(SettingsWindow_Loaded);
}
void SettingsWindow_Loaded(object sender, RoutedEventArgs e)
{
Update();
}
private void Update()
{
sliderGraphPrecision.Value = GraphingCalculator.Properties.Settings.Default.PlotPrecision;
sliderGridDensity.Value = GraphingCalculator.Properties.Settings.Default.GridDensity;
sliderDoublePrecision.Value = GraphingCalculator.Properties.Settings.Default.RoundDoublesGraph;
sliderNavigationSensitivity.Value = GraphingCalculator.Properties.Settings.Default.NavigationSensitivity;
sliderZoomSensitivity.Value = GraphingCalculator.Properties.Settings.Default.ZoomSensitivity;
}
private void Save()
{
GraphingCalculator.Properties.Settings.Default.PlotPrecision = sliderGraphPrecision.Value;
GraphingCalculator.Properties.Settings.Default.GridDensity = sliderGridDensity.Value;
GraphingCalculator.Properties.Settings.Default.RoundDoublesGraph = Convert.ToInt32(sliderDoublePrecision.Value);
GraphingCalculator.Properties.Settings.Default.NavigationSensitivity = sliderNavigationSensitivity.Value;
GraphingCalculator.Properties.Settings.Default.ZoomSensitivity = sliderZoomSensitivity.Value;
}
private void buttonDefaults_Click(object sender, RoutedEventArgs e)
{
sliderGraphPrecision.Value = 600;
sliderGridDensity.Value = 41;
sliderDoublePrecision.Value = 4;
sliderNavigationSensitivity.Value = 0.1;
sliderZoomSensitivity.Value = 1.075;
}
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void buttonAccept_Click(object sender, RoutedEventArgs e)
{
Save();
this.Close();
}
private void sliderDoublePrecision_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (outputDoublePrecision != null)
outputDoublePrecision.Text = e.NewValue.ToString() + " decimals";
}
private void sliderNavigationSensitivity_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (outputNavigationSensitivity != null)
outputNavigationSensitivity.Text = Math.Round(e.NewValue * 100, 0).ToString() + "% of screen";
}
private void sliderZoomSensitivity_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (outputZoomSensitivity != null)
outputZoomSensitivity.Text = Math.Round((e.NewValue-1) * 100, 0).ToString() + "% of screen";
}
}
}